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.pdfIf this is your first time using UniPDF, follow the getting started guide to create an API key and set up your development environment.