The Extensible Metadata Platform (XMP) logo inside a syringe.

Embed XMP sidecars into image files with embed_xmp

I wanted a program to inject Extensible Metadata Platform (XMP) sidecar (stand-alone) files into an image file. Image metadata is most useful when embedded into image files after all. I was surprised to learn that options for XMP editors were thin on the ground. I had to create a new program to get the job done.

Two existing programs come to mind: Exiv2 and ExifTool. Both tools interpret the sidecar file and write the recognize metadata back into the image files using their own distinct set of rules. This would be fine except when you want to include metadata from namespaces/vocabularies they don’t recognize or want to make sure the resulting metadata takes up as little space as possible.

Exiv2 writes XMP in a compacted form that isn’t compatible with the underlying RDF standard, the XMP standard, and isn’t supported by any popular image viewers or operating systems. ExifTool, on the other hand, produces overly verbose XMP markup that takes up more space than necessary. It can be configured to compact it somewhat and reduce optional whitespace. At my request, the author of ExifTool added even more options for compacting it further.

Both of these tools also have a few different image format-specific bugs and they don’t support all of the formats I need (including WebP and SVG).

I wanted a program that takes an XMP sidecar file without processing it and injects it as-is into the right place inside of image files. A third program called webpmux does exactly this. However, it only works for WebP images.

So I wrote a new program called embed_xmp to fulfill this need. It’s available as a Ruby gem/library and as a command-line interface (CLI). (Sorry if you were expecting a graphical user interface!) It supports writing XMP sidecars into JPEG, PNG, SVG, and WebP image files.

It’s not quite what I explained above, though. It doesn’t inject XMP data verbatim. It will wrap the XMP data in an xpacket XML processing instruction, as recommended by the XMP specification. This ensures the broadest client-compatibility. It also doesn’t preserve XML whitespace (unless overwritten by an xml:space instruction in the XMP file). Whitespace can bulk the file size more than it does in other contexts because XMP data isn’t compressed when embedded into files. Image files are usually not recompressed when served through a web server either.

The program won’t make other changes to the image file unless where required by the image format. The most substantial change is that basic WebP images are upgraded to the WebP Extended File Format (WEFF) when needed. WEFF may not render in legacy browsers. The WebP container specification only supports embedding XMP data into WEFF files.

You’ll need another program to author the XMP sidecar files — such as Corel AfterShot Pro, Adobe Photoshop or Lightroom, or Photo Mechanic — and can then use embed_xmp to insert that sidecar into an image file. You’ll probably want to use embed_xmp with some sort of content management system that holds image metadata, though.

You can install the embed_xmp CLI and library using RubyGems. You’ll need to install Ruby and RubyGems first, and then run the following command to download and install the latest version of embed_xmp:

gem install embed_xmp

The CLI tool requires three positional arguments:

embed_xmp sidecar.xmp input.png output.png

The file extension of the input file is used to determine the image format. The name of the output file is unimportant, but it will be output in the same format as the input image. No image format conversions will take place. The sidecar file must be in valid XML and it must not be wrapped in an xpacket XML processing instruction. There are no sanity-checks or format-validation.

The Ruby library has a few more options. The short and sweet usage instructions are as follows:

require "embed_xmp"

input_file = "/example.png"
# ::PNG, ::JPEG, ::SVG, or ::WebP
embedder = EmbedXMP::PNG.new(input_file)

sidecar_file = "/example.xmp"
# can also be supplied as a string
embedder.join_sidecar(sidecar_file)

output_file = "/example-xmp.png"
embedder.write(output_file)

You can find some more detailed usage in the documentation on RubyDoc. (You also installed a local copy of the documentation along with the gem.) Although not the intended use, it also includes methods for removing XMP data from image files.

The XMP specification requires that you ensure that legacy/other metadata formats — e.g. native metadata fields — are updated to be aligned with the XMP metadata. embed_xmp doesn’t do that. (It’s not aware of the contents of the metadata you want to insert.) You must ensure that you either update or remove conflicting metadata in other formats.

You can be notified of future updates by following the gem’s AppCast feed or by visiting its page on RubyGems.

I’ve not yet published the source code for this gem anywhere. However, you get a copy when installing the gem via RubyGems. It’s licensed under a BSD 3-Clause license. I’m evaluating distributed alternatives to GitHub and didn’t want to upload any more repositories there. I’ll update this article with links to the source code once I decide on something.