mattiasw/ExifReader

Extensible tags

Opened this issue · 2 comments

Description

I'd like to suggest making the available tags configurable. From a user perspective, I'd imagine passing the tags to the read options, or exporting an ExifReader class that takes them as constructor parameters. Using custom tags would look like so:

Use ExifReader like it currently works (this ought to be the default):

import ExifReader from 'exifreader';

// The default export of the tags module would be an object of the default tags
import defaultTags from 'exifreader/tags';

ExifReader.read(data, {
  tags: defaultTags,
});

// or by omitting tags, which would result in a dynamic import of the defaultTags
ExifReader.read(data);

Use ExifReader with a subset of tags:

import ExifReader from 'exifreader';

// In addition to the default export, the tags module would hold separate exports
// for the individual tag families
import { exif, file, photoshop } from 'exifreader/tags';

ExifReader.read(data, {
  tags: { exif, file, photoshop },
});

Use ExifReader with a custom tag set (more on that below) in addition to the defaults:

import ExifReader from 'exifreader';
import defaultTags from 'exifreader/tags';
import { apple } from './custom-apple-tags-module';

ExifReader.read(data, {
  tags: { ...defaultTags, apple },
});

This would:

  • remove the need for custom builds altogether
  • add support for tree shaking, since the library could be build to avoid importing the tag sets automatically
  • allow using additional tag sets without upstream changes to the library required (but still possible).

To define custom tag sets, one would need an object implementing the below interface:

interface TagSet<T extends number | number[]> {
  read(dataView: DataView, offset: T): Promise<Record<string, Tag>>;

  findOffset(dataView: DataView): Promise<T | undefined>;
}

There may be more thinking required for generalising all existing tag extractors, but it should be possible.

custom-apple-tags-module.ts:

import { defineTagSet } from 'exifreader';

export const apple = defineTagSet({
  async read(dataView, offset) {
    // ...
  },

  async findOffset(dataView) {
    // ...
  },
});

(defineTagSet is just a helper function returning the object to make it easy to type the thing automatically)

Do you think this is feasible, or even an interesting direction to go into? I'm willing to contribute to this.
My motivation is that I need a few tags defined by Apple exifTool knows about, but ExifReader doesn't. Ideally, I'd like to just write a custom module to parse those tags, and if that works well, contribute them back to ExifReader; it would be cool to have an easy extension point. For my use case, I'd also like to use tree shaking, but the custom builds (while a neat solution) don't integrate well with my deployment target.

Hi,

It's definitely an interesting suggestion to look into. There already is a thread about it actually: #243

The customizability is not going to be as good as with the custom builds for a couple of reasons mentioned in the other issue but I think the plugin architecture is a much better solution. I should have some more time on my hands after the (European) summer and can have a more proper look at it then.

In the meantime it might be possible to add the Apple tags for you into the library directly (unless they are stored in a heavy Apple-specific way). I will have a look at the latest next week.

Thanks for the descriptive suggestion.

I seem to have spoken too soon. The Apple tags are stored in the maker notes. This makes them very suitable to rather be a custom module instead.

If you need these tags sooner rather than later I would suggest getting the MakerNotes tag using ExifReader and then write your own parser of that block of binary data (present in the value property of the tag). I don't know the specification of it but from a quick look at the byte codes for a file I have it seems to start with Apple iOS and then two null bytes (I assume the exact string will be different for different devices).