exiftool-vendored 36.1.0 → 37.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/CHANGELOG.md +60 -5
  2. package/CLAUDE.md +2 -1
  3. package/README.md +124 -35
  4. package/data/TagMetadata.json +10 -3
  5. package/dist/ExifTool.d.ts +15 -8
  6. package/dist/ExifTool.dispose.spec.js +3 -3
  7. package/dist/ExifTool.dispose.spec.js.map +1 -1
  8. package/dist/ExifTool.js +11 -9
  9. package/dist/ExifTool.js.map +1 -1
  10. package/dist/ExifToolOptions.d.ts +15 -2
  11. package/dist/ExifToolOptions.js.map +1 -1
  12. package/dist/ExifToolVendoredTags.d.ts +11 -1
  13. package/dist/ExifToolVendoredTags.js +1 -1
  14. package/dist/ExifToolVendoredTags.js.map +1 -1
  15. package/dist/ExiftoolPath.js +1 -1
  16. package/dist/ExiftoolPath.js.map +1 -1
  17. package/dist/InvalidUtf8Bytes.d.ts +20 -0
  18. package/dist/InvalidUtf8Bytes.js +106 -0
  19. package/dist/InvalidUtf8Bytes.js.map +1 -0
  20. package/dist/InvalidUtf8Bytes.spec.d.ts +1 -0
  21. package/dist/InvalidUtf8Bytes.spec.js +75 -0
  22. package/dist/InvalidUtf8Bytes.spec.js.map +1 -0
  23. package/dist/InvalidUtf8Xml.spec.d.ts +1 -0
  24. package/dist/InvalidUtf8Xml.spec.js +156 -0
  25. package/dist/InvalidUtf8Xml.spec.js.map +1 -0
  26. package/dist/JSON.d.ts +1 -0
  27. package/dist/JSON.js +6 -1
  28. package/dist/JSON.js.map +1 -1
  29. package/dist/RawTags.d.ts +9 -2
  30. package/dist/ReadRawTask.d.ts +1 -0
  31. package/dist/ReadRawTask.js +16 -3
  32. package/dist/ReadRawTask.js.map +1 -1
  33. package/dist/ReadRawTask.spec.js +191 -0
  34. package/dist/ReadRawTask.spec.js.map +1 -1
  35. package/dist/ReadTask.js +24 -2
  36. package/dist/ReadTask.js.map +1 -1
  37. package/dist/ReadTask.spec.js +52 -0
  38. package/dist/ReadTask.spec.js.map +1 -1
  39. package/dist/Tags.d.ts +56 -38
  40. package/dist/Tags.js +3 -3
  41. package/dist/Tags.js.map +1 -1
  42. package/dist/Tags.spec.js +8 -0
  43. package/dist/Tags.spec.js.map +1 -1
  44. package/dist/Utf8JsonFilter.d.ts +7 -0
  45. package/dist/Utf8JsonFilter.js +85 -0
  46. package/dist/Utf8JsonFilter.js.map +1 -0
  47. package/dist/update/mktags.js +19 -1
  48. package/dist/update/mktags.js.map +1 -1
  49. package/package.json +8 -11
@@ -288,6 +288,17 @@ export interface ExifToolOptions extends bc.BatchClusterOptions, bc.BatchProcess
288
288
  * library rejects any entry containing `\r`, `\n`, or `\0` as a
289
289
  * defense-in-depth measure, but provides no other sanitization.
290
290
  *
291
+ * JSON reads repair malformed UTF-8 with U+FFFD (`�`) independently of this
292
+ * array and preserve the original string bytes in
293
+ * `Tags.invalidUtf8Bytes`. If these arguments contain an explicit,
294
+ * non-empty ExifTool `-api Filter=...` option, that custom filter owns the
295
+ * complete output filtering pipeline and must perform any desired UTF-8
296
+ * repair and byte capture itself.
297
+ *
298
+ * The marker appears only in string values; to render it as the pre-v37 `?`,
299
+ * post-process with
300
+ * `typeof value === "string" ? value.replace(/\uFFFD/g, "?") : value`.
301
+ *
291
302
  * @default `["-fast"]`
292
303
  */
293
304
  readArgs: string[];
@@ -347,14 +358,16 @@ export interface ExifToolOptions extends bc.BatchClusterOptions, bc.BatchProcess
347
358
  keepUTCTime: boolean;
348
359
  /**
349
360
  * Timeout in milliseconds for synchronous disposal (using Symbol.dispose).
350
- * If graceful cleanup takes longer than this, forceful cleanup is attempted.
361
+ * If graceful cleanup takes longer than this, forceful cleanup is requested.
362
+ * This is not a hard completion deadline.
351
363
  *
352
364
  * @default 1000 (1 second)
353
365
  */
354
366
  disposalTimeoutMs?: number;
355
367
  /**
356
368
  * Timeout in milliseconds for asynchronous disposal (using Symbol.asyncDispose).
357
- * If graceful cleanup takes longer than this, forceful cleanup is attempted.
369
+ * If graceful cleanup takes longer than this, forceful cleanup is requested.
370
+ * This is not a hard completion deadline.
358
371
  *
359
372
  * @default 5000 (5 seconds)
360
373
  */
@@ -1 +1 @@
1
- {"version":3,"file":"ExifToolOptions.js","sourceRoot":"","sources":["../src/ExifToolOptions.ts"],"names":[],"mappings":";;;AAyZA,0DASC;AA/ZD,uCAA0D;AAG7C,QAAA,cAAc,GAAG,IAAA,iBAAO,EACnC,KAAK,EACL,QAAQ,EACR,QAAQ,CACsC,CAAC;AA+YjD,SAAgB,uBAAuB,CAIrC,OAAU;IACV,IAAI,OAAO,CAAC,aAAa,IAAI,IAAI,IAAI,OAAO,CAAC,mBAAmB,IAAI,IAAI,EAAE,CAAC;QACzE,OAAO,CAAC,aAAa,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC;IACtE,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC"}
1
+ {"version":3,"file":"ExifToolOptions.js","sourceRoot":"","sources":["../src/ExifToolOptions.ts"],"names":[],"mappings":";;;AAsaA,0DASC;AA5aD,uCAA0D;AAG7C,QAAA,cAAc,GAAG,IAAA,iBAAO,EACnC,KAAK,EACL,QAAQ,EACR,QAAQ,CACsC,CAAC;AA4ZjD,SAAgB,uBAAuB,CAIrC,OAAU;IACV,IAAI,OAAO,CAAC,aAAa,IAAI,IAAI,IAAI,OAAO,CAAC,mBAAmB,IAAI,IAAI,EAAE,CAAC;QACzE,OAAO,CAAC,aAAa,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC;IACtE,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC"}
@@ -1,9 +1,19 @@
1
1
  import { ErrorsAndWarnings } from "./ErrorsAndWarnings";
2
+ import { InvalidUtf8Bytes } from "./InvalidUtf8Bytes";
2
3
  import { StrEnum, StrEnumKeys } from "./StrEnum";
3
4
  /**
4
5
  * This tags are added to {@link Tags} from this library.
5
6
  */
6
7
  export interface ExifToolVendoredTags extends ErrorsAndWarnings {
8
+ /**
9
+ * Original bytes for metadata strings that contained malformed UTF-8.
10
+ *
11
+ * This sparse object mirrors the returned tag paths. It is absent when all
12
+ * strings were valid UTF-8 or when a custom ExifTool `Filter` handled them.
13
+ * Each damaged string is a `Uint8Array` leaf; narrow a dynamic lookup with
14
+ * `value instanceof Uint8Array` before decoding it.
15
+ */
16
+ invalidUtf8Bytes?: InvalidUtf8Bytes;
7
17
  /**
8
18
  * Either an offset, like `UTC-7`, or an actual IANA timezone, like
9
19
  * `America/Los_Angeles`.
@@ -28,5 +38,5 @@ export interface ExifToolVendoredTags extends ErrorsAndWarnings {
28
38
  */
29
39
  zoneSource?: string;
30
40
  }
31
- export declare const ExifToolVendoredTagNames: StrEnum<"zone" | "tz" | "tzSource" | "zoneSource" | "errors" | "warnings">;
41
+ export declare const ExifToolVendoredTagNames: StrEnum<"zone" | "invalidUtf8Bytes" | "tz" | "tzSource" | "zoneSource" | "errors" | "warnings">;
32
42
  export type ExifToolVendoredTagName = StrEnumKeys<typeof ExifToolVendoredTagNames>;
@@ -2,5 +2,5 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.ExifToolVendoredTagNames = void 0;
4
4
  const StrEnum_1 = require("./StrEnum");
5
- exports.ExifToolVendoredTagNames = (0, StrEnum_1.strEnum)("tz", "zone", "tzSource", "zoneSource", "errors", "warnings");
5
+ exports.ExifToolVendoredTagNames = (0, StrEnum_1.strEnum)("invalidUtf8Bytes", "tz", "zone", "tzSource", "zoneSource", "errors", "warnings");
6
6
  //# sourceMappingURL=ExifToolVendoredTags.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ExifToolVendoredTags.js","sourceRoot":"","sources":["../src/ExifToolVendoredTags.ts"],"names":[],"mappings":";;;AAGA,uCAA0D;AAkC7C,QAAA,wBAAwB,GAAG,IAAA,iBAAO,EAC7C,IAAI,EACJ,MAAM,EACN,UAAU,EACV,YAAY,EACZ,QAAQ,EACR,UAAU,CACmC,CAAC"}
1
+ {"version":3,"file":"ExifToolVendoredTags.js","sourceRoot":"","sources":["../src/ExifToolVendoredTags.ts"],"names":[],"mappings":";;;AAIA,uCAA0D;AA4C7C,QAAA,wBAAwB,GAAG,IAAA,iBAAO,EAC7C,kBAAkB,EAClB,IAAI,EACJ,MAAM,EACN,UAAU,EACV,YAAY,EACZ,QAAQ,EACR,UAAU,CACmC,CAAC"}
@@ -44,7 +44,7 @@ function vendorPackage() {
44
44
  async function tryImport({ prefix = "", logger, } = {}) {
45
45
  const id = prefix + vendorPackage();
46
46
  try {
47
- const module = await Promise.resolve(`${id}`).then(s => __importStar(require(s)));
47
+ const module = await import(id);
48
48
  return module.default ?? module;
49
49
  }
50
50
  catch (error) {
@@ -1 +1 @@
1
- {"version":3,"file":"ExiftoolPath.js","sourceRoot":"","sources":["../src/ExiftoolPath.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAmDA,oCAkDC;AApGD,6CAA+B;AAC/B,iDAAmC;AACnC,uCAAoC;AAEpC,mCAAgC;AAEhC,SAAS,aAAa;IACpB,OAAO,oBAAoB,GAAG,CAAC,IAAA,iBAAO,GAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;AAC3D,CAAC;AAED,KAAK,UAAU,SAAS,CAAC,EACvB,MAAM,GAAG,EAAE,EACX,MAAM,MACyC,EAAE;IACjD,MAAM,EAAE,GAAG,MAAM,GAAG,aAAa,EAAE,CAAC;IACpC,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,yBAAa,EAAE,uCAAC,CAAC;QAChC,OAAO,MAAM,CAAC,OAAO,IAAI,MAAM,CAAC;IAClC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,EAAE,IAAI,CAAC,EAAE,GAAG,cAAc,EAAE,KAAK,CAAC,CAAC;QACzC,OAAO;IACT,CAAC;AACH,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACI,KAAK,UAAU,YAAY,CAAC,MAAe;IAChD,MAAM,IAAI,GAAG,MAAM,SAAS,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC;IACrD,2EAA2E;IAC3E,iEAAiE;IAEjE,MAAM,gBAAgB,GAAG,IAAI;QAC3B,EAAE,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC;SACjB,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,KAAK,UAAU,CAAC,CAAC,CAAC,mBAAmB,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;SAC3D,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAEnB,uEAAuE;IACvE,yEAAyE;IACzE,IAAI,gBAAgB,IAAI,IAAI,IAAI,GAAG,CAAC,UAAU,CAAC,gBAAgB,CAAC,EAAE,CAAC;QACjE,OAAO,gBAAgB,CAAC;IAC1B,CAAC;IACD,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;QACzC,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,EAAE,IAAI,CAAC,8BAA8B,GAAG,aAAa,EAAE,CAAC,CAAC;IAE/D,kDAAkD;IAClD,MAAM,oBAAoB,GAAI,OAAsC;SACjE,aAAa,CAAC;IACjB,IAAI,oBAAoB,IAAI,IAAI,EAAE,CAAC;QACjC,MAAM,SAAS,GAAG,KAAK,CAAC,IAAI,CAC1B,oBAAoB,EACpB,aAAa,EAAE,EACf,KAAK,EACL,UAAU,GAAG,CAAC,IAAA,iBAAO,GAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CACvC,CAAC;QACF,IAAI,GAAG,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;YAC9B,OAAO,SAAS,CAAC;QACnB,CAAC;aAAM,CAAC;YACN,MAAM,EAAE,IAAI,CACV,4DAA4D;gBAC1D,SAAS,CACZ,CAAC;QACJ,CAAC;IACH,CAAC;IAED,kEAAkE;IAClE,MAAM,QAAQ,GAAG,MAAM,IAAA,aAAK,EAAC,UAAU,CAAC,CAAC;IACzC,IAAI,QAAQ,IAAI,IAAI,EAAE,CAAC;QACrB,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED,MAAM,IAAI,KAAK,CACb,oEAAoE,CACrE,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"ExiftoolPath.js","sourceRoot":"","sources":["../src/ExiftoolPath.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAmDA,oCAkDC;AApGD,6CAA+B;AAC/B,iDAAmC;AACnC,uCAAoC;AAEpC,mCAAgC;AAEhC,SAAS,aAAa;IACpB,OAAO,oBAAoB,GAAG,CAAC,IAAA,iBAAO,GAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;AAC3D,CAAC;AAED,KAAK,UAAU,SAAS,CAAC,EACvB,MAAM,GAAG,EAAE,EACX,MAAM,MACyC,EAAE;IACjD,MAAM,EAAE,GAAG,MAAM,GAAG,aAAa,EAAE,CAAC;IACpC,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,EAAE,CAAC,CAAC;QAChC,OAAO,MAAM,CAAC,OAAO,IAAI,MAAM,CAAC;IAClC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,EAAE,IAAI,CAAC,EAAE,GAAG,cAAc,EAAE,KAAK,CAAC,CAAC;QACzC,OAAO;IACT,CAAC;AACH,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACI,KAAK,UAAU,YAAY,CAAC,MAAe;IAChD,MAAM,IAAI,GAAG,MAAM,SAAS,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC;IACrD,2EAA2E;IAC3E,iEAAiE;IAEjE,MAAM,gBAAgB,GAAG,IAAI;QAC3B,EAAE,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC;SACjB,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,KAAK,UAAU,CAAC,CAAC,CAAC,mBAAmB,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;SAC3D,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAEnB,uEAAuE;IACvE,yEAAyE;IACzE,IAAI,gBAAgB,IAAI,IAAI,IAAI,GAAG,CAAC,UAAU,CAAC,gBAAgB,CAAC,EAAE,CAAC;QACjE,OAAO,gBAAgB,CAAC;IAC1B,CAAC;IACD,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;QACzC,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,EAAE,IAAI,CAAC,8BAA8B,GAAG,aAAa,EAAE,CAAC,CAAC;IAE/D,kDAAkD;IAClD,MAAM,oBAAoB,GAAI,OAAsC;SACjE,aAAa,CAAC;IACjB,IAAI,oBAAoB,IAAI,IAAI,EAAE,CAAC;QACjC,MAAM,SAAS,GAAG,KAAK,CAAC,IAAI,CAC1B,oBAAoB,EACpB,aAAa,EAAE,EACf,KAAK,EACL,UAAU,GAAG,CAAC,IAAA,iBAAO,GAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CACvC,CAAC;QACF,IAAI,GAAG,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;YAC9B,OAAO,SAAS,CAAC;QACnB,CAAC;aAAM,CAAC;YACN,MAAM,EAAE,IAAI,CACV,4DAA4D;gBAC1D,SAAS,CACZ,CAAC;QACJ,CAAC;IACH,CAAC;IAED,kEAAkE;IAClE,MAAM,QAAQ,GAAG,MAAM,IAAA,aAAK,EAAC,UAAU,CAAC,CAAC;IACzC,IAAI,QAAQ,IAAI,IAAI,EAAE,CAAC;QACrB,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED,MAAM,IAAI,KAAK,CACb,oEAAoE,CACrE,CAAC;AACJ,CAAC"}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Original bytes for metadata strings that contained malformed UTF-8.
3
+ *
4
+ * Keys mirror the returned tag value. List indexes are represented as numeric
5
+ * object keys, and branches without malformed strings are omitted. Each leaf
6
+ * is a `Uint8Array`; intermediate values are nested `InvalidUtf8Bytes` objects.
7
+ * Narrow a dynamic lookup with `value instanceof Uint8Array` before decoding
8
+ * it.
9
+ */
10
+ export interface InvalidUtf8Bytes {
11
+ [tagOrIndex: string]: Uint8Array | InvalidUtf8Bytes;
12
+ }
13
+ export declare const InvalidUtf8Marker = "__etvInvalidUtf8V1";
14
+ /** Restore byte arrays after callers serialize tags with `JSON.stringify()`. */
15
+ export declare function reviveInvalidUtf8Bytes(value: unknown): unknown;
16
+ /** Decode the private ExifTool filter wrapper before returning public tags. */
17
+ export declare function unwrapInvalidUtf8Tags(tags: Record<string, unknown>): {
18
+ tags: Record<string, unknown>;
19
+ invalidUtf8Bytes?: InvalidUtf8Bytes;
20
+ };
@@ -0,0 +1,106 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.InvalidUtf8Marker = void 0;
4
+ exports.reviveInvalidUtf8Bytes = reviveInvalidUtf8Bytes;
5
+ exports.unwrapInvalidUtf8Tags = unwrapInvalidUtf8Tags;
6
+ exports.InvalidUtf8Marker = "__etvInvalidUtf8V1";
7
+ const StringPrefix = "s:";
8
+ const Base64Prefix = "b64:";
9
+ function wireValue(value) {
10
+ if (value == null || typeof value !== "object" || Array.isArray(value)) {
11
+ return;
12
+ }
13
+ const outerEntries = Object.entries(value);
14
+ if (outerEntries.length !== 1 || outerEntries[0]?.[0] !== exports.InvalidUtf8Marker) {
15
+ return;
16
+ }
17
+ const payload = outerEntries[0][1];
18
+ if (payload == null ||
19
+ typeof payload !== "object" ||
20
+ Array.isArray(payload)) {
21
+ return;
22
+ }
23
+ const payloadEntries = Object.entries(payload);
24
+ if (payloadEntries.length !== 2 ||
25
+ typeof payload.replacement !==
26
+ "string" ||
27
+ typeof payload.rawBase64 !== "string") {
28
+ return;
29
+ }
30
+ const result = payload;
31
+ return result.replacement.startsWith(StringPrefix) &&
32
+ result.rawBase64.startsWith(Base64Prefix)
33
+ ? result
34
+ : undefined;
35
+ }
36
+ function unwrap(value) {
37
+ const wire = wireValue(value);
38
+ if (wire != null) {
39
+ return {
40
+ value: wire.replacement.slice(StringPrefix.length),
41
+ bytes: new Uint8Array(Buffer.from(wire.rawBase64.slice(Base64Prefix.length), "base64")),
42
+ };
43
+ }
44
+ if (value == null || typeof value !== "object")
45
+ return { value };
46
+ const result = value;
47
+ let bytes;
48
+ for (const [key, child] of Object.entries(value)) {
49
+ const unwrapped = unwrap(child);
50
+ if (unwrapped.value !== child) {
51
+ Object.defineProperty(result, key, {
52
+ configurable: true,
53
+ enumerable: true,
54
+ value: unwrapped.value,
55
+ writable: true,
56
+ });
57
+ }
58
+ if (unwrapped.bytes != null) {
59
+ bytes ??= {};
60
+ // Assignment would invoke Object.prototype.__proto__'s setter.
61
+ Object.defineProperty(bytes, key, {
62
+ configurable: true,
63
+ enumerable: true,
64
+ value: unwrapped.bytes,
65
+ writable: true,
66
+ });
67
+ }
68
+ }
69
+ return bytes == null ? { value: result } : { value: result, bytes };
70
+ }
71
+ /** Restore byte arrays after callers serialize tags with `JSON.stringify()`. */
72
+ function reviveInvalidUtf8Bytes(value) {
73
+ if (value == null || typeof value !== "object" || Array.isArray(value)) {
74
+ return value;
75
+ }
76
+ const entries = Object.entries(value);
77
+ if (entries.length > 0 &&
78
+ entries.every(([key, byte], index) => key === String(index) &&
79
+ typeof byte === "number" &&
80
+ Number.isInteger(byte) &&
81
+ byte >= 0 &&
82
+ byte <= 0xff)) {
83
+ return Uint8Array.from(entries.map(([, byte]) => byte));
84
+ }
85
+ for (const [key, child] of entries) {
86
+ const revived = reviveInvalidUtf8Bytes(child);
87
+ if (revived !== child) {
88
+ Object.defineProperty(value, key, {
89
+ configurable: true,
90
+ enumerable: true,
91
+ value: revived,
92
+ writable: true,
93
+ });
94
+ }
95
+ }
96
+ return value;
97
+ }
98
+ /** Decode the private ExifTool filter wrapper before returning public tags. */
99
+ function unwrapInvalidUtf8Tags(tags) {
100
+ const result = unwrap(tags);
101
+ const decodedTags = { tags: result.value };
102
+ return result.bytes == null || result.bytes instanceof Uint8Array
103
+ ? decodedTags
104
+ : { ...decodedTags, invalidUtf8Bytes: result.bytes };
105
+ }
106
+ //# sourceMappingURL=InvalidUtf8Bytes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"InvalidUtf8Bytes.js","sourceRoot":"","sources":["../src/InvalidUtf8Bytes.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,75 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const _chai_spec_1 = require("./_chai.spec");
4
+ const InvalidUtf8Bytes_1 = require("./InvalidUtf8Bytes");
5
+ const JSON_1 = require("./JSON");
6
+ function wrapped(replacement, bytes) {
7
+ return {
8
+ [InvalidUtf8Bytes_1.InvalidUtf8Marker]: {
9
+ replacement: "s:" + replacement,
10
+ rawBase64: "b64:" + Buffer.from(bytes).toString("base64"),
11
+ },
12
+ };
13
+ }
14
+ describe("unwrapInvalidUtf8Tags", () => {
15
+ it("unwraps nested object and list leaves into a sparse byte sidecar", () => {
16
+ const collision = {
17
+ [InvalidUtf8Bytes_1.InvalidUtf8Marker]: {
18
+ replacement: "missing the required prefix",
19
+ rawBase64: "b64:3Es=",
20
+ },
21
+ };
22
+ const decoded = (0, InvalidUtf8Bytes_1.unwrapInvalidUtf8Tags)({
23
+ Description: wrapped("G�teborg", [0x47, 0x9a, 0x74]),
24
+ Nested: {
25
+ Subjects: ["valid", wrapped("�K", [0xdc, 0x4b])],
26
+ },
27
+ Collision: collision,
28
+ });
29
+ (0, _chai_spec_1.expect)(decoded.tags).to.deep.equal({
30
+ Description: "G�teborg",
31
+ Nested: { Subjects: ["valid", "�K"] },
32
+ Collision: collision,
33
+ });
34
+ (0, _chai_spec_1.expect)(decoded.invalidUtf8Bytes).to.deep.equal({
35
+ Description: new Uint8Array([0x47, 0x9a, 0x74]),
36
+ Nested: {
37
+ Subjects: { 1: new Uint8Array([0xdc, 0x4b]) },
38
+ },
39
+ });
40
+ });
41
+ it("does not add a sidecar when no private wrapper is present", () => {
42
+ const tags = { Title: "Authored? 世界" };
43
+ const decoded = (0, InvalidUtf8Bytes_1.unwrapInvalidUtf8Tags)(tags);
44
+ (0, _chai_spec_1.expect)(decoded.tags).to.equal(tags);
45
+ (0, _chai_spec_1.expect)(decoded).to.deep.equal({
46
+ tags: { Title: "Authored? 世界" },
47
+ });
48
+ });
49
+ it("preserves wrapped bytes under an own __proto__ key", () => {
50
+ const input = JSON.parse(`{"__proto__":${JSON.stringify(wrapped("�K", [0xdc, 0x4b]))}}`);
51
+ const decoded = (0, InvalidUtf8Bytes_1.unwrapInvalidUtf8Tags)(input);
52
+ (0, _chai_spec_1.expect)(Object.hasOwn(decoded.tags, "__proto__")).to.equal(true);
53
+ (0, _chai_spec_1.expect)(decoded.tags.__proto__).to.equal("�K");
54
+ (0, _chai_spec_1.expect)(Object.hasOwn(decoded.invalidUtf8Bytes, "__proto__")).to.equal(true);
55
+ (0, _chai_spec_1.expect)(decoded.invalidUtf8Bytes?.__proto__).to.deep.equal(new Uint8Array([0xdc, 0x4b]));
56
+ });
57
+ it("preserves __proto__ as an ordinary metadata key", () => {
58
+ const input = JSON.parse('{"__proto__":"metadata"}');
59
+ const decoded = (0, InvalidUtf8Bytes_1.unwrapInvalidUtf8Tags)(input);
60
+ (0, _chai_spec_1.expect)(Object.hasOwn(decoded.tags, "__proto__")).to.equal(true);
61
+ (0, _chai_spec_1.expect)(decoded.tags.__proto__).to.equal("metadata");
62
+ (0, _chai_spec_1.expect)(Object.getPrototypeOf(decoded.tags)).to.equal(Object.prototype);
63
+ });
64
+ it("round-trips byte arrays through the public parseJSON helper", () => {
65
+ const value = {
66
+ City: "�K",
67
+ invalidUtf8Bytes: {
68
+ City: new Uint8Array([0xdc, 0x4b]),
69
+ Subject: { 1: new Uint8Array([0xdd, 0x4c]) },
70
+ },
71
+ };
72
+ (0, _chai_spec_1.expect)((0, JSON_1.parseJSON)(JSON.stringify(value))).to.deep.equal(value);
73
+ });
74
+ });
75
+ //# sourceMappingURL=InvalidUtf8Bytes.spec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"InvalidUtf8Bytes.spec.js","sourceRoot":"","sources":["../src/InvalidUtf8Bytes.spec.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,156 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const promises_1 = require("node:fs/promises");
4
+ const node_path_1 = require("node:path");
5
+ const ExifTool_1 = require("./ExifTool");
6
+ const _chai_spec_1 = require("./_chai.spec");
7
+ const InvalidMarker = "{{INVALID_UTF8}}";
8
+ const InvalidByte = 0xdc;
9
+ function withInvalidUtf8(template) {
10
+ const parts = template.split(InvalidMarker);
11
+ const chunks = [];
12
+ parts.forEach((part, index) => {
13
+ chunks.push(Buffer.from(part));
14
+ if (index < parts.length - 1)
15
+ chunks.push(Buffer.from([InvalidByte]));
16
+ });
17
+ return Buffer.concat(chunks);
18
+ }
19
+ function expectedBytes(before, after) {
20
+ return Uint8Array.from([
21
+ ...Buffer.from(before),
22
+ InvalidByte,
23
+ ...Buffer.from(after),
24
+ ]);
25
+ }
26
+ async function writeMalformedXml(template) {
27
+ const filename = (0, _chai_spec_1.tmpname)("malformed-utf8-") + ".xml";
28
+ await (0, promises_1.writeFile)(filename, withInvalidUtf8(template));
29
+ return filename;
30
+ }
31
+ async function writeMalformedXmpJpeg() {
32
+ const source = await (0, promises_1.readFile)((0, node_path_1.join)(_chai_spec_1.testDir, "with_faces.jpg"));
33
+ const needle = Buffer.from("<mwg-rs:Name>Alice Smith</mwg-rs:Name>");
34
+ const offset = source.indexOf(needle);
35
+ (0, _chai_spec_1.expect)(offset).to.be.greaterThan(-1);
36
+ const result = Buffer.from(source);
37
+ const damagedByte = offset + Buffer.byteLength("<mwg-rs:Name>A");
38
+ (0, _chai_spec_1.expect)(result[damagedByte]).to.equal("l".charCodeAt(0));
39
+ result[damagedByte] = InvalidByte;
40
+ const filename = (0, _chai_spec_1.tmpname)("malformed-utf8-struct-") + ".jpg";
41
+ await (0, promises_1.writeFile)(filename, result);
42
+ return filename;
43
+ }
44
+ async function writeXmpWithEmbeddedXmlPacket() {
45
+ const packet = withInvalidUtf8(`<x:xmpmeta xmlns:x="adobe:ns:meta/">
46
+ <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
47
+ <rdf:Description xmlns:dc="http://purl.org/dc/elements/1.1/">
48
+ <dc:description>packet A${InvalidMarker}ice</dc:description>
49
+ </rdf:Description>
50
+ </rdf:RDF>
51
+ </x:xmpmeta>`);
52
+ const outer = `<x:xmpmeta xmlns:x="adobe:ns:meta/">
53
+ <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
54
+ <rdf:Description
55
+ xmlns:ast="http://ns.nikon.com/asteroid/1.0/"
56
+ ast:XMLPackets="${packet.toString("base64")}"/>
57
+ </rdf:RDF>
58
+ </x:xmpmeta>`;
59
+ const filename = (0, _chai_spec_1.tmpname)("malformed-utf8-packet-") + ".xmp";
60
+ await (0, promises_1.writeFile)(filename, outer);
61
+ return { filename, packet };
62
+ }
63
+ describe("malformed UTF-8 in XML and nested structures", () => {
64
+ let exiftool;
65
+ before(() => (exiftool = new ExifTool_1.ExifTool()));
66
+ after(() => (0, _chai_spec_1.end)(exiftool));
67
+ const genericXml = `<?xml version="1.0" encoding="UTF-8"?>
68
+ <root attribute="attribute A${InvalidMarker}ice">
69
+ <nested>
70
+ <name>text &amp; A${InvalidMarker}ice</name>
71
+ <document><![CDATA[<doc>opaque A${InvalidMarker}ice</doc>]]></document>
72
+ </nested>
73
+ </root>`;
74
+ const expectedGenericValue = {
75
+ Attribute: "attribute A�ice",
76
+ Nested: {
77
+ Document: "<doc>opaque A�ice</doc>",
78
+ Name: "text & A�ice",
79
+ },
80
+ };
81
+ const expectedGenericBytes = {
82
+ Attribute: expectedBytes("attribute A", "ice"),
83
+ Nested: {
84
+ Document: expectedBytes("<doc>opaque A", "ice</doc>"),
85
+ Name: expectedBytes("text & A", "ice"),
86
+ },
87
+ };
88
+ it("captures nested generic XML values through read()", async () => {
89
+ const filename = await writeMalformedXml(genericXml);
90
+ const tags = await exiftool.read(filename, { struct: 1 });
91
+ (0, _chai_spec_1.expect)(tags.Root).to.deep.equal(expectedGenericValue);
92
+ (0, _chai_spec_1.expect)(tags.invalidUtf8Bytes?.Root).to.deep.equal(expectedGenericBytes);
93
+ });
94
+ it("captures nested generic XML values through readRaw()", async () => {
95
+ const filename = await writeMalformedXml(genericXml);
96
+ const tags = await exiftool.readRaw(filename, { readArgs: ["-struct"] });
97
+ (0, _chai_spec_1.expect)(tags.Root).to.deep.equal(expectedGenericValue);
98
+ (0, _chai_spec_1.expect)(tags.invalidUtf8Bytes?.Root).to.deep.equal(expectedGenericBytes);
99
+ });
100
+ for (const struct of [0, 1, 2]) {
101
+ it(`mirrors embedded XMP paths with struct=${struct}`, async () => {
102
+ const filename = await writeMalformedXmpJpeg();
103
+ const tags = await exiftool.read(filename, { struct });
104
+ const rawName = expectedBytes("A", "ice Smith");
105
+ if (struct !== 1) {
106
+ (0, _chai_spec_1.expect)(tags.RegionName).to.deep.equal([
107
+ "A�ice Smith",
108
+ "Bob Jones",
109
+ ]);
110
+ (0, _chai_spec_1.expect)(tags.invalidUtf8Bytes?.RegionName).to.deep.equal({
111
+ 0: rawName,
112
+ });
113
+ }
114
+ if (struct !== 0) {
115
+ (0, _chai_spec_1.expect)(tags.RegionInfo.RegionList[0].Name).to.equal("A�ice Smith");
116
+ (0, _chai_spec_1.expect)((tags.invalidUtf8Bytes?.RegionInfo).RegionList[0].Name).to.deep.equal(rawName);
117
+ }
118
+ });
119
+ }
120
+ it("parses embedded binary XML while preserving its binary value", async () => {
121
+ const { filename, packet } = await writeXmpWithEmbeddedXmlPacket();
122
+ const tags = await exiftool.readRaw(filename, {
123
+ readArgs: ["-G1", "-struct", "-XMLPackets", "-Description"],
124
+ });
125
+ (0, _chai_spec_1.expect)(tags["XMP-ast:XMLPackets"]).to.match(/^\(Binary data \d+ bytes, use -b option to extract\)$/);
126
+ (0, _chai_spec_1.expect)(tags["XMP-dc:Description"]).to.equal("packet A�ice");
127
+ const descriptionBytes = expectedBytes("packet A", "ice");
128
+ (0, _chai_spec_1.expect)(tags.invalidUtf8Bytes).to.deep.equal({
129
+ "MWG:Description": descriptionBytes,
130
+ "XMP-dc:Description": descriptionBytes,
131
+ });
132
+ const extracted = await exiftool.readRaw(filename, {
133
+ readArgs: ["-G1", "-b", "-XMLPackets"],
134
+ });
135
+ (0, _chai_spec_1.expect)(extracted["XMP-ast:XMLPackets"]).to.equal(packet.toString("base64"));
136
+ (0, _chai_spec_1.expect)(extracted.invalidUtf8Bytes).to.equal(undefined);
137
+ });
138
+ it("cannot capture malformed XML element or attribute-name bytes", async () => {
139
+ const filename = await writeMalformedXml(`<?xml version="1.0"?>
140
+ <root at${InvalidMarker}tribute="attribute value">
141
+ <na${InvalidMarker}me>element value</na${InvalidMarker}me>
142
+ <ok>fine</ok>
143
+ </root>`);
144
+ const tags = await exiftool.readRaw(filename, { readArgs: ["-struct"] });
145
+ // Value filters never receive names, and ExifTool's JSON writer renders
146
+ // these malformed generic-XML name bytes as "?". A separate non-JSON
147
+ // extraction path would be needed to preserve the original names.
148
+ (0, _chai_spec_1.expect)(tags.Root).to.deep.equal({
149
+ "At?tribute": "attribute value",
150
+ "Na?me": "element value",
151
+ Ok: "fine",
152
+ });
153
+ (0, _chai_spec_1.expect)(tags.invalidUtf8Bytes).to.equal(undefined);
154
+ });
155
+ });
156
+ //# sourceMappingURL=InvalidUtf8Xml.spec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"InvalidUtf8Xml.spec.js","sourceRoot":"","sources":["../src/InvalidUtf8Xml.spec.ts"],"names":[],"mappings":"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"}
package/dist/JSON.d.ts CHANGED
@@ -9,5 +9,6 @@ export type Literal = string | number | boolean | bigint | null;
9
9
  * - {@link ExifDateTime} is revived using {@link ExifDateTime.fromJSON}
10
10
  * - {@link ExifDate} is revived using {@link ExifDate.fromJSON}
11
11
  * - {@link ExifTime} is revived using {@link ExifTime.fromJSON}
12
+ * - `invalidUtf8Bytes` sidecars are revived as nested `Uint8Array` values
12
13
  */
13
14
  export declare function parseJSON(s: string): any;
package/dist/JSON.js CHANGED
@@ -5,6 +5,7 @@ const BinaryField_1 = require("./BinaryField");
5
5
  const ExifDate_1 = require("./ExifDate");
6
6
  const ExifDateTime_1 = require("./ExifDateTime");
7
7
  const ExifTime_1 = require("./ExifTime");
8
+ const InvalidUtf8Bytes_1 = require("./InvalidUtf8Bytes");
8
9
  const Revivers = {
9
10
  BinaryField: (ea) => BinaryField_1.BinaryField.fromJSON(ea),
10
11
  ExifDateTime: (ea) => ExifDateTime_1.ExifDateTime.fromJSON(ea),
@@ -18,15 +19,19 @@ const Revivers = {
18
19
  * - {@link ExifDateTime} is revived using {@link ExifDateTime.fromJSON}
19
20
  * - {@link ExifDate} is revived using {@link ExifDate.fromJSON}
20
21
  * - {@link ExifTime} is revived using {@link ExifTime.fromJSON}
22
+ * - `invalidUtf8Bytes` sidecars are revived as nested `Uint8Array` values
21
23
  */
22
24
  function parseJSON(s) {
23
- return JSON.parse(s, (_key, value) => {
25
+ return JSON.parse(s, (key, value) => {
24
26
  if (typeof value === "object" &&
25
27
  value !== null &&
26
28
  "_ctor" in value &&
27
29
  typeof value._ctor === "string") {
28
30
  return Revivers[value._ctor]?.(value) ?? value;
29
31
  }
32
+ if (key === "invalidUtf8Bytes") {
33
+ return (0, InvalidUtf8Bytes_1.reviveInvalidUtf8Bytes)(value);
34
+ }
30
35
  return value;
31
36
  });
32
37
  }
package/dist/JSON.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"JSON.js","sourceRoot":"","sources":["../src/JSON.ts"],"names":[],"mappings":";;AAoCA,8BAYC;AAhDD,+CAA4C;AAC5C,yCAAsC;AACtC,iDAA8C;AAC9C,yCAAsC;AAetC,MAAM,QAAQ,GAAmD;IAC/D,WAAW,EAAE,CAAC,EAAE,EAAE,EAAE,CAClB,yBAAW,CAAC,QAAQ,CAAC,EAAkD,CAAC;IAC1E,YAAY,EAAE,CAAC,EAAE,EAAE,EAAE,CACnB,2BAAY,CAAC,QAAQ,CAAC,EAAmD,CAAC;IAC5E,QAAQ,EAAE,CAAC,EAAE,EAAE,EAAE,CACf,mBAAQ,CAAC,QAAQ,CAAC,EAA+C,CAAC;IACpE,QAAQ,EAAE,CAAC,EAAE,EAAE,EAAE,CACf,mBAAQ,CAAC,QAAQ,CAAC,EAA+C,CAAC;CACrE,CAAC;AACF;;;;;;;GAOG;AACH,SAAgB,SAAS,CAAC,CAAS;IACjC,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,IAAI,EAAE,KAAW,EAAE,EAAE;QACzC,IACE,OAAO,KAAK,KAAK,QAAQ;YACzB,KAAK,KAAK,IAAI;YACd,OAAO,IAAI,KAAK;YAChB,OAAO,KAAK,CAAC,KAAK,KAAK,QAAQ,EAC/B,CAAC;YACD,OAAO,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC,KAAmB,CAAC,IAAI,KAAK,CAAC;QAC/D,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC,CAAC,CAAC;AACL,CAAC"}
1
+ {"version":3,"file":"JSON.js","sourceRoot":"","sources":["../src/JSON.ts"],"names":[],"mappings":";;AAsCA,8BAeC;AArDD,+CAA4C;AAC5C,yCAAsC;AACtC,iDAA8C;AAC9C,yCAAsC;AACtC,yDAA4D;AAe5D,MAAM,QAAQ,GAAmD;IAC/D,WAAW,EAAE,CAAC,EAAE,EAAE,EAAE,CAClB,yBAAW,CAAC,QAAQ,CAAC,EAAkD,CAAC;IAC1E,YAAY,EAAE,CAAC,EAAE,EAAE,EAAE,CACnB,2BAAY,CAAC,QAAQ,CAAC,EAAmD,CAAC;IAC5E,QAAQ,EAAE,CAAC,EAAE,EAAE,EAAE,CACf,mBAAQ,CAAC,QAAQ,CAAC,EAA+C,CAAC;IACpE,QAAQ,EAAE,CAAC,EAAE,EAAE,EAAE,CACf,mBAAQ,CAAC,QAAQ,CAAC,EAA+C,CAAC;CACrE,CAAC;AACF;;;;;;;;GAQG;AACH,SAAgB,SAAS,CAAC,CAAS;IACjC,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,GAAG,EAAE,KAAW,EAAE,EAAE;QACxC,IACE,OAAO,KAAK,KAAK,QAAQ;YACzB,KAAK,KAAK,IAAI;YACd,OAAO,IAAI,KAAK;YAChB,OAAO,KAAK,CAAC,KAAK,KAAK,QAAQ,EAC/B,CAAC;YACD,OAAO,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC,KAAmB,CAAC,IAAI,KAAK,CAAC;QAC/D,CAAC;QACD,IAAI,GAAG,KAAK,kBAAkB,EAAE,CAAC;YAC/B,OAAO,IAAA,yCAAsB,EAAC,KAAK,CAAC,CAAC;QACvC,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC,CAAC,CAAC;AACL,CAAC"}
package/dist/RawTags.d.ts CHANGED
@@ -1,8 +1,15 @@
1
1
  import { ErrorsAndWarnings } from "./ErrorsAndWarnings";
2
+ import { InvalidUtf8Bytes } from "./InvalidUtf8Bytes";
2
3
  import { Json } from "./JSON";
3
4
  /**
4
- * Loosely typed raw result from ExifTool
5
+ * Loosely typed raw result from ExifTool.
6
+ *
7
+ * `readRaw()` skips the rich parsing that `read()` performs, so this omits the
8
+ * timezone fields (`tz`, `zone`, ...) from {@link ExifToolVendoredTags}: only
9
+ * `errors`, `warnings`, and `invalidUtf8Bytes` are ever set here.
5
10
  *
6
11
  * @see https://github.com/photostructure/exiftool-vendored.js/issues/138
7
12
  */
8
- export type RawTags = Record<string, Json> & ErrorsAndWarnings;
13
+ export type RawTags = Record<string, Json | InvalidUtf8Bytes> & ErrorsAndWarnings & {
14
+ invalidUtf8Bytes?: InvalidUtf8Bytes;
15
+ };
@@ -8,6 +8,7 @@ export declare const DefaultReadRawTaskOptions: {
8
8
  };
9
9
  export type ReadRawTaskOptions = Partial<typeof DefaultReadRawTaskOptions>;
10
10
  export declare class ReadRawTask extends ExifToolTask<RawTags> {
11
+ #private;
11
12
  readonly sourceFile: string;
12
13
  readonly args: string[];
13
14
  static for(filename: string, options?: ReadRawTaskOptions): ReadRawTask;
@@ -40,7 +40,9 @@ const DefaultExifToolOptions_1 = require("./DefaultExifToolOptions");
40
40
  const ErrorsAndWarnings_1 = require("./ErrorsAndWarnings");
41
41
  const ExifToolTask_1 = require("./ExifToolTask");
42
42
  const FilenameCharsetArgs_1 = require("./FilenameCharsetArgs");
43
+ const InvalidUtf8Bytes_1 = require("./InvalidUtf8Bytes");
43
44
  const Pick_1 = require("./Pick");
45
+ const Utf8JsonFilter_1 = require("./Utf8JsonFilter");
44
46
  exports.ReadRawTaskOptionFields = [
45
47
  "readArgs",
46
48
  "ignoreMinorErrors",
@@ -52,10 +54,13 @@ exports.DefaultReadRawTaskOptions = {
52
54
  class ReadRawTask extends ExifToolTask_1.ExifToolTask {
53
55
  sourceFile;
54
56
  args;
57
+ #unwrapInvalidUtf8;
55
58
  static for(filename, options) {
59
+ const readArgs = options?.readArgs ?? [];
56
60
  const args = [
57
61
  ...FilenameCharsetArgs_1.Utf8FilenameCharsetArgs,
58
- ...(options?.readArgs ?? []),
62
+ ...readArgs,
63
+ ...(0, Utf8JsonFilter_1.utf8JsonFilterArgs)(readArgs),
59
64
  ];
60
65
  const opts = { ...exports.DefaultReadRawTaskOptions, ...options };
61
66
  if (!args.includes("-json"))
@@ -71,16 +76,24 @@ class ReadRawTask extends ExifToolTask_1.ExifToolTask {
71
76
  super(args, options);
72
77
  this.sourceFile = sourceFile;
73
78
  this.args = args;
79
+ this.#unwrapInvalidUtf8 = (0, Utf8JsonFilter_1.hasBuiltInUtf8Filter)(args);
74
80
  }
75
81
  toString() {
76
82
  return "ReadRawTask(" + this.sourceFile + ")";
77
83
  }
78
84
  parse(data, err) {
79
85
  try {
80
- const tags = JSON.parse(data)[0];
81
- const { errors, warnings } = (0, ErrorsAndWarnings_1.errorsAndWarnings)(this, tags);
86
+ const parsed = JSON.parse(data)[0];
87
+ const decoded = this.#unwrapInvalidUtf8
88
+ ? (0, InvalidUtf8Bytes_1.unwrapInvalidUtf8Tags)(parsed)
89
+ : { tags: parsed };
90
+ const tags = decoded.tags;
91
+ const { errors, warnings } = (0, ErrorsAndWarnings_1.errorsAndWarnings)(this, decoded.tags);
82
92
  tags.errors = errors;
83
93
  tags.warnings = warnings;
94
+ if (decoded.invalidUtf8Bytes != null) {
95
+ tags.invalidUtf8Bytes = decoded.invalidUtf8Bytes;
96
+ }
84
97
  return tags;
85
98
  }
86
99
  catch (jsonError) {