Get XML Metadata
Sometimes you want the XMP packet as XML rather than as a parsed model: to dump it,
diff it, hand it to another library, or read a property no Go model covers. The
catalog’s /Metadata entry is an ordinary stream, so decoding it gives you the
bytes.
Metadata streams are not limited to the catalog. Pages, images and other components can each carry one, reached the same way through their own dictionaries; the catalog is just where document-level metadata lives.
Doing it
metadata, ok := pdfReader.GetCatalogMetadata()
if !ok {
return errors.New("no metadata on the catalog")
}
metadataStream, ok := core.GetStream(metadata)
if !ok {
return fmt.Errorf("catalog metadata is not a stream: %T", metadata)
}
xmlMetadata, err := core.DecodeStream(metadataStream)
if err != nil {
return err
}
fmt.Printf("%s\n", xmlMetadata)core.DecodeStream applies whatever filters the stream declares. XMP packets are
usually stored uncompressed, but nothing requires that, so decode rather than
reading stream.Stream directly if you want the XML in every case.
Picking values out of the XML
XMP is RDF, and the properties you want sit as child elements of
rdf:RDF/rdf:Description. A small struct with encoding/xml is enough to collect
them without knowing the namespaces up front:
type xmpMetadata struct {
Descriptions []struct {
Tags []struct {
XMLName xml.Name
Value xml.CharData `xml:",innerxml"`
} `xml:",any"`
} `xml:"RDF>Description"`
}xml:",any" matches every child element regardless of name, and XMLName records
what it was. The example’s keyValMap flattens that into a map keyed by local
name.
Limitations
Flattening by local name loses the namespace, so two properties from different
namespaces that share a local name collide. It also flattens structure: an
rdf:Alt or rdf:Seq value arrives as raw inner XML, not as a list.
encoding/xml is strict where xmputil.LoadDocument is lenient. A packet with a
malformed property fails the whole decode here, whereas LoadDocument skips the
bad property. If you want typed access and tolerance of imperfect files, use
get XMP metadata instead.
Run the example
printXMLMetadataForPdf walks the trailer to the root catalog by hand with
GetTrailer and a local resolve helper, pulls /Metadata, decodes it, and prints
the property names and values sorted. That manual walk is what you would adapt to
reach a metadata stream on a page or an image; for the catalog,
GetCatalogMetadata does it in one call.
git clone https://github.com/unidoc/unipdf-examples.git
cd unipdf-examples/metadata
go run pdf_metadata_get_xml.go input1.pdf [input2.pdf] ...If this is your first time using UniPDF, follow the getting started guide to create an API key and set up your development environment.