Skip to content
Get XML Metadata

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.

View the full source
Last updated on