-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathgetExifTagTable.ts
More file actions
143 lines (126 loc) · 4.28 KB
/
getExifTagTable.ts
File metadata and controls
143 lines (126 loc) · 4.28 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
import { IFD_NAMES } from "../../constants.ts";
import type { Ifd } from "../../enums/ExifIfd.ts";
import type { SupportLevel } from "../../enums/ExifSupportLevel.ts";
import { ExifTagBiMap, type ExifTagValue } from "../../enums/ExifTag.ts";
import {
ExifTagGpsBiMap,
type ExifTagGpsValue,
} from "../../enums/ExifTagGps.ts";
import {
ExifTagUnifiedBiMap,
type ExifTagUnifiedValue,
type Tag,
} from "../../enums/ExifTagUnified.ts";
import {
EXIF_SENTINEL_TAG,
ExifTagInfo,
exifTagTableGetName,
exifTagTableGetTag,
} from "../ExifTag.ts";
type TagEntry = {
tagVal: number;
tag: Tag;
name: string;
title: string;
description: string;
esl: Record<Ifd, SupportLevel>;
};
type IndexedTagEntry = TagEntry & { index: number };
const DEFAULT_SUPPORT_LEVEL: Record<Ifd, SupportLevel> = {
IFD_0: "UNKNOWN",
IFD_1: "UNKNOWN",
EXIF: "UNKNOWN",
GPS: "UNKNOWN",
INTEROPERABILITY: "UNKNOWN",
};
/**
* Internally, `ExifTagTable[]` is an array of `TagEntry` structs that serve as
* the source of truth for all `ExifTag`-related functions. It is not exported
* in `<libexif/exif-tag.h>`, so it is replicated here.
*
* Since tags are not unique among entries (the value may have a different
* meaning in another IFD), duplicate entries have been replaced with their
* corresponding correct value.
*
* Note in the C library, the last entry is a sentinel value, hence, the table
* is length `exif_tag_table_count() - 1`.
*
* @see {@link https://github.com/libexif/libexif/blob/master/libexif/exif-tag.c#L55-L966}
*/
const getExifTagTable = (): TagEntry[] => {
const entryTagMap = new Map<number, IndexedTagEntry[]>();
// Map each entry's TagVal to a list of entries
for (let index = 0; index < EXIF_SENTINEL_TAG; index++) {
const tagVal = exifTagTableGetTag(index);
const name = exifTagTableGetName(index);
const tag =
ExifTagUnifiedBiMap.getKey(tagVal as ExifTagUnifiedValue) ?? null;
if (!tag) {
throw new Error(`Tag named ${name} does not exist`);
}
const esl = IFD_NAMES.reduce(
(acc, ifd) => {
acc[ifd] = ExifTagInfo.getSupportLevelInIfd(tag, ifd);
return acc;
},
{ ...DEFAULT_SUPPORT_LEVEL },
);
const tagEntry: IndexedTagEntry = {
index: index,
tagVal,
tag,
name,
title: ExifTagInfo.getTitle(tag),
description: ExifTagInfo.getDescription(tag),
esl,
};
entryTagMap.set(tagVal, [...(entryTagMap.get(tagVal) ?? []), tagEntry]);
}
return Array.from(entryTagMap).flatMap(([tagVal, tagEntries]) => {
const firstTagEntry = tagEntries.at(0);
if (firstTagEntry === undefined) {
throw new Error(
`An error occurred while building exifTagTable, tag value ${tagVal} does not have at least one TagEntry`,
);
}
if (tagEntries.length === 1) {
// No duplicate entries, return as-is
const { index: _, ...tagEntry } = firstTagEntry;
return tagEntry;
} else {
// Duplicate entries, return the entries within their corresponding IFDs
const { esl } = firstTagEntry; // Since they share the same tagVal, they will also share the same esl
const supportedIfds = IFD_NAMES.filter(
(ifd) => esl[ifd] === "MANDATORY" || esl[ifd] === "OPTIONAL",
);
if (supportedIfds.length !== tagEntries.length) {
throw new Error(
`Mismatch for tagVal ${tagVal}: expected ${tagEntries.length} IFDs, got ${supportedIfds.length}`,
);
}
return supportedIfds.map((ifd) => {
const tag =
ifd === "GPS" ?
ExifTagGpsBiMap.getKey(tagVal as ExifTagGpsValue)
: ExifTagBiMap.getKey(tagVal as ExifTagValue);
if (tag === undefined) {
throw new Error(
`Tag with value ${tagVal} does not exist in ifd ${ifd}`,
);
}
const supportLevel = { ...DEFAULT_SUPPORT_LEVEL };
// Only update the IFD this entry pertains to
supportLevel[ifd] = ExifTagInfo.getSupportLevelInIfd(tag, ifd);
return {
tagVal,
tag,
name: ExifTagInfo.getNameInIfd(tag, ifd),
title: ExifTagInfo.getTitleInIfd(tag, ifd),
description: ExifTagInfo.getDescriptionInIfd(tag, ifd),
esl: supportLevel,
};
});
}
});
};
export { getExifTagTable, type TagEntry };