Modifying EXIF metadata with Go

KleinC.
4 min readDec 11, 2022

--

Photo by Reinhart Julian on Unsplash

Recently I have been working on a pet project that needs to work with EXIF metadata. Then I start looking for Go packages that handle EXIF data. Unexpectedly, there wasn’t much out there. Perhaps Go programmers don’t process images much. I found a few, and many of them are a wrap of the package I am going to introduce below.

Installation / Preparation

The package I am using is dsoprea/go-exif and a JPEG wrap by the same author, dsoprea/go-jpeg-image-structure. They take an image file and parse the EXIF header from it. You can then just read, modify and save back in.

First, we need to install the package via go get command.

$ go get github.com/dsoprea/go-exif/v3
$ go get github.com/dsoprea/go-jpeg-image-structure/v2

How to

Here I will only focus on the JPEG version. dsoprea provided other wrappers, e.g. HEIC, TIFF, PNG.

import (
"os"
"time"

exif "github.com/dsoprea/go-exif/v3"
exifcommon "github.com/dsoprea/go-exif/v3/common"
jis "github.com/dsoprea/go-jpeg-image-structure/v2"
log "github.com/dsoprea/go-logging"
pis "github.com/dsoprea/go-png-image-structure/v2"
"golang.org/x/text/encoding/unicode"
)

For demo, all returned errors will be suppressed, please do not ignore them in production.

First, we need to extract the media context from the image and then cast it into a segment list.

Then we need some IFD builders. Here we created 3 IFD builders for 3 groups of tags.

Next, we will update the metadata with the SetStandardWithName function.

The first parameter is the tag name, and the second is the value, of course. You can find a list of tag names with the tag group here.

Lastly, we need to set those updated metadata back to the segment list. Then write it out to the destination file.

That’s it, simple right? But it took up half of my day. Next, we will see what could be a trap.

Caution / Trap

The biggest trap is encoding. Unless you’re very familiar with EXIF and its encodings. You might never know that your code runs smoothly, but none of the characters shown you can read.

For example, the XPKeywords, the EXIF tag for tagging an image on Windows system, the main purpose I need an EXIF package. The value is encoded in UCS-2 encoding. But I didn’t see it documented anywhere. I just happen to see this when I’m picking a package by reading examples.

So, as you can see, what I have done is encode the new data with UTF-16, no BOM, little endian, and cast it into a byte list. This took me so long to notice that I need to cast. Actually, I didn’t notice even the little-endian part at first. Here comes the debug process.

Following is a code segment that I just copy from the official docs example.

As you can see, we can dump the EXIF data from the segment list. Then we print the metadata value with the tag “XPKeywords”.

Value: [74 00 61 00 67 00 20 00 31 00 3b 00 74 00 61 00 67 00 20 00 32 00 00 00]

Here is the dump of XPKeywords, a total of 12 words, the text version of it is “tag 1; tag 2”. The 5th word is 31 00, the 11th word is 32 00. Which is a little-endian encoded “1” and “2”.

Then, you need to cast it into a byte list. The new text is “tag 3;tag 4” Here is the dump of the cast version and the no-cast version.

// original
// Value: [74 00 61 00 67 00 20 00 31 00 3b 00 74 00 61 00 67 00 20 00 32 00 00 00]

_ = ifdIb.SetStandardWithName("XPKeywords", []byte(str)) // cast
// Value: [74 00 61 00 67 00 20 00 33 00 3b 00 74 00 61 00 67 00 20 00 34 00]

_ = ifdIb.SetStandardWithName("XPKeywords", str) // no cast
// Value: [74 00 61 00 67 00 20 00 33 00 3b 00 74 00 61 00 67 00 20 00 34 00 00]

You will notice there is an extra byte at the end. I don’t really sure what that is, but I believe that it is the \0 for string. There’s still some difference from the original version, the 2 trailing bytes of 00. It works fine without it, so I will leave it without.

That’s all. Hope you find this first before you got trapped for hours.

References

SegmentList.SetExif example

photo/tags package by frizinak

EXIF Tags

Exiv2 — Image metadata library and tools

--

--

KleinC.

The world is too wide to know every thing, but it’s a joy to know more then less.