Image Titler 1.9.0 Features EXIF Version Tagging

Image Titler 1.9.0 Features EXIF Version Tagging Featured Image

It’s official! The latest version of the image-titler is out, and it features EXIF version tagging. In this article, we’ll take a look at what that means and why it matters.

Table of Contents

What is EXIF Version Tagging

Much like the previous version tagging update in v1.6.0, I decided I wanted some way to store image-titler version information on the edited files themselves. As a result, I found a way to tag the version directly onto the photo using its metadata.

In particular, I am using the Exchangeable Image File Format (EXIF) data to tag images with version information. Unfortunately, not all images support EXIF data. That said, the files that do will look like this after editing:

The version information can be found under “Comments” on Windows 10.

In other words, the version of the software the generated that image can be found in the comment section. Apparently, tags are unique to Windows, so I didn’t bother using literal tags. That said, if you know of a better way to handle tagging, let me know.

Why EXIF Version Tagging?

The rationale behind EXIF version tagging is pretty simple: I wanted some way to track software versions even if file names change. In other words, if I end up changing the name of a file to handle name clashes, I can still be sure that the version information is retained in the metadata.

Also, part of me just likes the idea of tracing an image in the wild back to my software. If for some reason this tool takes off, it will be cool to be able to identify images that have been generated using my tool.

Of course, I think this feature could potentially be a privacy issue. As a result, some future version of this program will make the feature opt-in as a flag. In fact, I think even filename version tagging will need to be opt-in at some point.

How Does EXIF Version Tagging Work?

Funny you ask! I’m not really sure. Apparently, EXIF data has some special binary format, and there aren’t a ton of tools that help you modify it. As a result, I was stuck including another dependency in the project: piexif.

With the package installed, the rest looks like this:

def _add_version_to_exif(image: Image.Image, version: str) -> bytes:
    """
    Given an image and version, this function will place that vision in the EXIF data of the file.
    Currently, this function is limited to files that already have EXIF data. Naturally, not
    all files have EXIF data, so I'm not sure how useful this feature is. That said, it's
    a nice start!
    :param image: an image file
    :param version: the software version (e.g. 1.9.0)
    :return: the exif data as a byte string (empty string for images that didn't already have data)
    """
    if exif := image.info.get('exif'):
        exif_dict = piexif.load(exif)
        exif_dict['Exif'][piexif.ExifIFD.UserComment] = piexif.helper.UserComment.dump(f'image-titler-v{version}')
        return piexif.dump(exif_dict)
    else:
        return b""

Basically, this code extracts the EXIF data from the current file, if it exists. Then, it takes that data and loads the image-titler version into the user comments. Then, that EXIF data is returned as a byte string.

Later, that function is called when we save the file:

def save_copy(og_image: Image.Image, edited_image: Image.Image, title: str, output_path: str = None):
    """
    A helper function for saving a copy of the image.
    :param og_image: the original image
    :param edited_image: the edited image
    :param title: the title of the image
    :param output_path: the path to dump the picture
    :return: nothing
    """
    version: str = pkg_resources.require("image-titler")[0].version
    version = version.replace(".", SEPARATOR)
    storage_path = _generate_image_output_path(og_image.format, output_path, title, version)
    exif = _add_version_to_exif(og_image, version)
    edited_image.save(storage_path, subsampling=0, quality=100, exif=exif)

Now, every file that’s passed through this program has a chance to be updated with the version tag in the EXIF data.

Other Changes?

As always, new features using come with a host of bug fixes or quality of life updates. For example, v1.9.0 added a feature where testing dumps samples to the samples folder. This was convenient because the samples could quickly be linked in the samples README.

At the same time, I decided to start cleaning up the code by making certain functions “public” and others “private.” Of course, Python doesn’t support this access control directly in the language. However, it is understood in the community that functions that are preceded by underscores are “private.” Essentially, this means users shouldn’t use these functions as they’re basically implementation details and subject to change.

Now, if you take a look at the code, you’ll see several functions that I’ve deemed as “private.” For example, here’s a list:

  • _draw_rectangle()
  • _draw_text()
  • _draw_overlay()

In the future, I expect to maintain this sort of control over function access.

Plans for the Future?

At the time of writing, there have been two new versions of the software released. As a result, there are obviously tons of future plans. For example, our second major release is out in the wild, and it features a GUI. Shortly, I’m sure I’ll have a deeper update around that!

As for now, there’s a lot to still be done, but I’m planning to focus on writing for a bit. After all, I’m in the middle of my vacation, and I’d like to get enough articles in the pipeline to enjoy it.

In the meantime, check out some of these related updates:

Thanks again for sticking around! I appreciate it.

Jeremy Grifski

Jeremy grew up in a small town where he enjoyed playing soccer and video games, practicing taekwondo, and trading Pokémon cards. Once out of the nest, he pursued a Bachelors in Computer Engineering with a minor in Game Design. After college, he spent about two years writing software for a major engineering company. Today, he pursues a PhD in Computer Science in order to ultimately land a teaching gig. In his spare time, Jeremy enjoys spending time with his wife, playing Overwatch, practicing trombone, watching Penguins hockey, and traveling the world.

Recent Content