Skip to content
Get Custom XMP Metadata

Get Custom XMP Metadata

xmputil wraps a handful of namespaces directly: pdf:, xmpMM:, pdfaid:, pdfuaid:. Everything else in an XMP packet is reachable through Document.GetGoXmpDocument, which hands you the underlying github.com/unidoc/go-xmp/xmp.Document and with it every model the go-xmp library ships plus generic access by property path.

Note the import path. In UniPDF v5 the XMP library is a UniDoc fork, github.com/unidoc/go-xmp, not the original github.com/trimmer-io/go-xmp.

Doing it

xmpDoc, err := xmputil.LoadDocument(stream.Stream)
if err != nil {
    return err
}

goXmpDoc := xmpDoc.GetGoXmpDocument()

rights := xmprights.FindModel(goXmpDoc)
if rights == nil {
    return errors.New("no xmpRights namespace in the XMP document")
}
fmt.Println(rights.Certificate, rights.WebStatement)

Each go-xmp model package exports its own FindModel, returning nil when the packet has no node for that namespace. The models live under go-xmp/models: dc, xmp_base, xmp_rights, xmp_mm, exif, tiff, crs, id3 and more.

Changes you make through GetGoXmpDocument are changes to the same document, not to a copy, so writes made this way survive Marshal.

Namespaces with no model

For a private namespace nothing has a Go type for, the go-xmp document takes property paths as strings:

value, err := goXmpDoc.GetPath(xmp.NewPath("xmpRights", "WebStatement"))

ListPaths returns every property in the packet as a PathValueList, which is the quickest way to find out what a file actually carries before you write code against it. GetPath returns an error rather than an empty string when the namespace or property is absent, so treat “not found” as an ordinary outcome.

Limitations

Field types are go-xmp types, not Go primitives. xmp.AltString is a list of language alternatives, so UsageTerms iterates as Lang and Value pairs; xmp.StringArray is an ordered list; xmp.Bool and xmp.Date need Value() or a comparison against zero. Most of them have IsZero(), which is the check to use before iterating.

Reading a model requires that the packet declared the namespace. FindModel cannot distinguish a namespace that was absent from one whose properties were all dropped as malformed, since LoadDocument runs the decoder in lenient mode.

Run the example

The example reads the xmpRights: namespace and prints the certificate, owners, usage terms and web statement of whichever of those the file declares.

git clone https://github.com/unidoc/unipdf-examples.git
cd unipdf-examples/metadata
go run pdf_get_custom_xmp_metadata.go input.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