exiftool-vendored 36.0.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.
- package/CHANGELOG.md +64 -5
- package/CLAUDE.md +2 -1
- package/README.md +128 -35
- package/RELEASE.md +1 -1
- package/data/TagMetadata.json +10 -3
- package/dist/DateTime.d.ts +2 -2
- package/dist/DateTime.js +1 -1
- package/dist/DateTime.js.map +1 -1
- package/dist/ExifTool.d.ts +45 -8
- package/dist/ExifTool.dispose.spec.js +3 -3
- package/dist/ExifTool.dispose.spec.js.map +1 -1
- package/dist/ExifTool.js +54 -10
- package/dist/ExifTool.js.map +1 -1
- package/dist/ExifToolOptions.d.ts +15 -2
- package/dist/ExifToolOptions.js.map +1 -1
- package/dist/ExifToolVendoredTags.d.ts +11 -1
- package/dist/ExifToolVendoredTags.js +1 -1
- package/dist/ExifToolVendoredTags.js.map +1 -1
- package/dist/ExiftoolPath.js +1 -1
- package/dist/ExiftoolPath.js.map +1 -1
- package/dist/InvalidUtf8Bytes.d.ts +20 -0
- package/dist/InvalidUtf8Bytes.js +106 -0
- package/dist/InvalidUtf8Bytes.js.map +1 -0
- package/dist/InvalidUtf8Bytes.spec.d.ts +1 -0
- package/dist/InvalidUtf8Bytes.spec.js +75 -0
- package/dist/InvalidUtf8Bytes.spec.js.map +1 -0
- package/dist/InvalidUtf8Xml.spec.d.ts +1 -0
- package/dist/InvalidUtf8Xml.spec.js +156 -0
- package/dist/InvalidUtf8Xml.spec.js.map +1 -0
- package/dist/JSON.d.ts +1 -0
- package/dist/JSON.js +6 -1
- package/dist/JSON.js.map +1 -1
- package/dist/RawTags.d.ts +9 -2
- package/dist/ReadRawTask.d.ts +1 -0
- package/dist/ReadRawTask.js +16 -3
- package/dist/ReadRawTask.js.map +1 -1
- package/dist/ReadRawTask.spec.js +191 -0
- package/dist/ReadRawTask.spec.js.map +1 -1
- package/dist/ReadTask.js +24 -2
- package/dist/ReadTask.js.map +1 -1
- package/dist/ReadTask.spec.js +52 -0
- package/dist/ReadTask.spec.js.map +1 -1
- package/dist/TagEdit.d.ts +62 -0
- package/dist/TagEdit.js +224 -0
- package/dist/TagEdit.js.map +1 -0
- package/dist/TagEdit.spec.d.ts +1 -0
- package/dist/TagEdit.spec.js +863 -0
- package/dist/TagEdit.spec.js.map +1 -0
- package/dist/Tags.d.ts +56 -38
- package/dist/Tags.js +3 -3
- package/dist/Tags.js.map +1 -1
- package/dist/Tags.spec.js +8 -0
- package/dist/Tags.spec.js.map +1 -1
- package/dist/Timezones.js.map +1 -1
- package/dist/Utf8JsonFilter.d.ts +7 -0
- package/dist/Utf8JsonFilter.js +85 -0
- package/dist/Utf8JsonFilter.js.map +1 -0
- package/dist/WriteTask.d.ts +6 -0
- package/dist/WriteTask.js +58 -18
- package/dist/WriteTask.js.map +1 -1
- package/dist/WriteTask.spec.js +17 -0
- package/dist/WriteTask.spec.js.map +1 -1
- package/dist/update/mktags.js +19 -1
- package/dist/update/mktags.js.map +1 -1
- package/package.json +15 -47
|
@@ -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 & 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, (
|
|
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":";;
|
|
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
|
+
};
|
package/dist/ReadRawTask.d.ts
CHANGED
|
@@ -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;
|
package/dist/ReadRawTask.js
CHANGED
|
@@ -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
|
-
...
|
|
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
|
|
81
|
-
const
|
|
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) {
|
package/dist/ReadRawTask.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ReadRawTask.js","sourceRoot":"","sources":["../src/ReadRawTask.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,iDAAuC;AACvC,iDAAmC;AACnC,qEAAkE;AAClE,2DAAwD;AAExD,iDAA8C;AAC9C,+DAAgE;AAChE,iCAA8B;
|
|
1
|
+
{"version":3,"file":"ReadRawTask.js","sourceRoot":"","sources":["../src/ReadRawTask.ts"],"names":[],"mappings":"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"}
|
package/dist/ReadRawTask.spec.js
CHANGED
|
@@ -6,9 +6,200 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
const promises_1 = require("node:fs/promises");
|
|
7
7
|
const node_path_1 = __importDefault(require("node:path"));
|
|
8
8
|
const ExifTool_1 = require("./ExifTool");
|
|
9
|
+
const InvalidUtf8Bytes_1 = require("./InvalidUtf8Bytes");
|
|
9
10
|
const _chai_spec_1 = require("./_chai.spec");
|
|
10
11
|
after(() => (0, _chai_spec_1.end)(ExifTool_1.exiftool));
|
|
11
12
|
describe("ReadRawTask", () => {
|
|
13
|
+
describe("malformed UTF-8", () => {
|
|
14
|
+
const malformedUtf8File = node_path_1.default.join(_chai_spec_1.testDir, "malformed-utf8.jpg");
|
|
15
|
+
const binaryListFile = node_path_1.default.join(_chai_spec_1.testDir, "malformed-utf8.lfp");
|
|
16
|
+
const binaryCollisionFile = node_path_1.default.join(_chai_spec_1.testDir, "malformed-utf8-collision.mie");
|
|
17
|
+
const customFilter = 'Image::ExifTool::XMP::FixUTF8(\\$_,"X")';
|
|
18
|
+
function withoutInvalidUtf8Bytes(value) {
|
|
19
|
+
const { invalidUtf8Bytes: _, ...rest } = value;
|
|
20
|
+
return rest;
|
|
21
|
+
}
|
|
22
|
+
function legacyUtf8Replacement(value) {
|
|
23
|
+
if (typeof value === "string")
|
|
24
|
+
return value.replaceAll("\uFFFD", "?");
|
|
25
|
+
if (Array.isArray(value))
|
|
26
|
+
return value.map(legacyUtf8Replacement);
|
|
27
|
+
if (value != null && typeof value === "object") {
|
|
28
|
+
return Object.fromEntries(Object.entries(value).map(([key, child]) => [
|
|
29
|
+
key,
|
|
30
|
+
legacyUtf8Replacement(child),
|
|
31
|
+
]));
|
|
32
|
+
}
|
|
33
|
+
return value;
|
|
34
|
+
}
|
|
35
|
+
it("marks malformed scalar and list values without changing valid text", async () => {
|
|
36
|
+
const tags = await ExifTool_1.exiftool.readRaw(malformedUtf8File);
|
|
37
|
+
(0, _chai_spec_1.expect)(tags).to.containSubset({
|
|
38
|
+
City: "\uFFFDK",
|
|
39
|
+
Title: "Authored? 世界",
|
|
40
|
+
Subject: ["Valid? item", "damaged:\uFFFDtail"],
|
|
41
|
+
UserComment: "SKEYս",
|
|
42
|
+
});
|
|
43
|
+
(0, _chai_spec_1.expect)(tags.invalidUtf8Bytes?.City).to.deep.equal(new Uint8Array([0xdc, 0x4b]));
|
|
44
|
+
(0, _chai_spec_1.expect)(tags.invalidUtf8Bytes?.Subject).to.deep.equal({
|
|
45
|
+
1: new Uint8Array([
|
|
46
|
+
0x64, 0x61, 0x6d, 0x61, 0x67, 0x65, 0x64, 0x3a, 0xdc, 0x74, 0x61,
|
|
47
|
+
0x69, 0x6c,
|
|
48
|
+
]),
|
|
49
|
+
});
|
|
50
|
+
(0, _chai_spec_1.expect)(tags.invalidUtf8Bytes?.UserComment).to.deep.equal(new Uint8Array([
|
|
51
|
+
0x53, 0x4b, 0x45, 0x59, 0x00, 0x00, 0x00, 0x00, 0xd5, 0x00, 0xbd,
|
|
52
|
+
]));
|
|
53
|
+
});
|
|
54
|
+
it("mirrors JSON NUL removal before deciding which bytes are malformed", async () => {
|
|
55
|
+
const readArgs = ["-G1", "-UserComment"];
|
|
56
|
+
const marked = await ExifTool_1.exiftool.readRaw(malformedUtf8File, { readArgs });
|
|
57
|
+
const legacy = await ExifTool_1.exiftool.readRaw(malformedUtf8File, {
|
|
58
|
+
readArgs: [...readArgs, "-api", "Filter=1"],
|
|
59
|
+
});
|
|
60
|
+
// The stored bytes d5 00 bd become valid UTF-8 (U+057D) when JSON removes
|
|
61
|
+
// NUL. Repairing before that deletion would incorrectly produce two U+FFFDs.
|
|
62
|
+
(0, _chai_spec_1.expect)(marked).to.containSubset({ "ExifIFD:UserComment": "SKEYս" });
|
|
63
|
+
(0, _chai_spec_1.expect)(marked.invalidUtf8Bytes).to.deep.equal({
|
|
64
|
+
"ExifIFD:UserComment": new Uint8Array([
|
|
65
|
+
0x53, 0x4b, 0x45, 0x59, 0x00, 0x00, 0x00, 0x00, 0xd5, 0x00, 0xbd,
|
|
66
|
+
]),
|
|
67
|
+
});
|
|
68
|
+
(0, _chai_spec_1.expect)(withoutInvalidUtf8Bytes(marked)).to.deep.equal(legacy);
|
|
69
|
+
});
|
|
70
|
+
it("otherwise differs from legacy JSON only by ? to U+FFFD repair", async () => {
|
|
71
|
+
for (const [file, readArgs] of [
|
|
72
|
+
[malformedUtf8File, ["-City", "-Title", "-Subject", "-UserComment"]],
|
|
73
|
+
[binaryListFile, ["-G1", "-JSONMetadata", "-Foo"]],
|
|
74
|
+
[binaryCollisionFile, ["-G1", "-City", "-RelatedAudioFile"]],
|
|
75
|
+
]) {
|
|
76
|
+
const marked = await ExifTool_1.exiftool.readRaw(file, {
|
|
77
|
+
readArgs: [...readArgs],
|
|
78
|
+
});
|
|
79
|
+
const legacy = await ExifTool_1.exiftool.readRaw(file, {
|
|
80
|
+
readArgs: [...readArgs, "-api", "Filter=1"],
|
|
81
|
+
});
|
|
82
|
+
(0, _chai_spec_1.expect)(legacyUtf8Replacement(withoutInvalidUtf8Bytes(marked))).to.deep.equal(legacy);
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
it("keeps the marker when per-call readArgs replace the defaults", async () => {
|
|
86
|
+
(0, _chai_spec_1.expect)(await ExifTool_1.exiftool.readRaw(malformedUtf8File, { readArgs: [] })).to.containSubset({
|
|
87
|
+
City: "\uFFFDK",
|
|
88
|
+
Subject: ["Valid? item", "damaged:\uFFFDtail"],
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
for (const { api, filter } of [
|
|
92
|
+
{ api: "-api", filter: `Filter=${customFilter}` },
|
|
93
|
+
{ api: "-API", filter: `filter=${customFilter}` },
|
|
94
|
+
{ api: "-api", filter: `FILTER^=${customFilter}` },
|
|
95
|
+
]) {
|
|
96
|
+
it(`leaves UTF-8 repair to ${api} ${filter.split("=")[0]}=`, async () => {
|
|
97
|
+
const tags = await ExifTool_1.exiftool.readRaw(malformedUtf8File, {
|
|
98
|
+
readArgs: [api, filter],
|
|
99
|
+
});
|
|
100
|
+
(0, _chai_spec_1.expect)(tags).to.containSubset({
|
|
101
|
+
City: "XK",
|
|
102
|
+
Subject: ["Valid? item", "damaged:Xtail"],
|
|
103
|
+
});
|
|
104
|
+
(0, _chai_spec_1.expect)(tags.invalidUtf8Bytes).to.equal(undefined);
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
for (const bareFilter of ["Filter", "Filter^"]) {
|
|
108
|
+
it(`treats a bare ${bareFilter} as a custom no-op Filter`, async () => {
|
|
109
|
+
// Ground truth:
|
|
110
|
+
// exiftool -j -City -api 'Filter[^]' test/malformed-utf8.jpg
|
|
111
|
+
const tags = await ExifTool_1.exiftool.readRaw(malformedUtf8File, {
|
|
112
|
+
readArgs: ["-City", "-api", bareFilter],
|
|
113
|
+
});
|
|
114
|
+
(0, _chai_spec_1.expect)(tags.City).to.equal("?K");
|
|
115
|
+
(0, _chai_spec_1.expect)(tags.invalidUtf8Bytes).to.equal(undefined);
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
it("does not decode marker-shaped output owned by a custom Filter", async () => {
|
|
119
|
+
const markerValue = {
|
|
120
|
+
[InvalidUtf8Bytes_1.InvalidUtf8Marker]: {
|
|
121
|
+
replacement: "s:forged",
|
|
122
|
+
rawBase64: "b64:3Es=",
|
|
123
|
+
},
|
|
124
|
+
};
|
|
125
|
+
const markerFilter = String.raw `Filter=$_={"${InvalidUtf8Bytes_1.InvalidUtf8Marker}"=>{replacement=>"s:forged",rawBase64=>"b64:3Es="}} if $_ eq "\xdcK"`;
|
|
126
|
+
const tags = await ExifTool_1.exiftool.readRaw(malformedUtf8File, {
|
|
127
|
+
readArgs: ["-City", "-api", markerFilter],
|
|
128
|
+
});
|
|
129
|
+
(0, _chai_spec_1.expect)(tags.City).to.deep.equal(markerValue);
|
|
130
|
+
(0, _chai_spec_1.expect)(tags.invalidUtf8Bytes).to.equal(undefined);
|
|
131
|
+
});
|
|
132
|
+
it("does not let a custom Filter leak into the next stay-open command", async () => {
|
|
133
|
+
(0, _chai_spec_1.expect)(await ExifTool_1.exiftool.readRaw(malformedUtf8File, {
|
|
134
|
+
readArgs: ["-api", `Filter=${customFilter}`],
|
|
135
|
+
})).to.containSubset({ City: "XK" });
|
|
136
|
+
(0, _chai_spec_1.expect)(await ExifTool_1.exiftool.readRaw(malformedUtf8File, { readArgs: [] })).to.containSubset({ City: "\uFFFDK" });
|
|
137
|
+
});
|
|
138
|
+
for (const emptyFilter of ["Filter=", "Filter^="]) {
|
|
139
|
+
it(`does not treat ${emptyFilter} as a custom Filter`, async () => {
|
|
140
|
+
(0, _chai_spec_1.expect)(await ExifTool_1.exiftool.readRaw(malformedUtf8File, {
|
|
141
|
+
readArgs: ["-api", emptyFilter],
|
|
142
|
+
})).to.containSubset({ City: "\uFFFDK" });
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
for (const clearedFilter of ["Filter=", "Filter^="]) {
|
|
146
|
+
it(`honors a final ${clearedFilter} after a custom Filter`, async () => {
|
|
147
|
+
// ExifTool API options are last-assignment-wins. Verified with:
|
|
148
|
+
// exiftool -j -api 'Filter=..."X"' -api 'Filter=' malformed-utf8.jpg
|
|
149
|
+
(0, _chai_spec_1.expect)(await ExifTool_1.exiftool.readRaw(malformedUtf8File, {
|
|
150
|
+
readArgs: ["-api", `Filter=${customFilter}`, "-api", clearedFilter],
|
|
151
|
+
})).to.containSubset({ City: "\uFFFDK" });
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
it("honors a final custom Filter after an empty Filter", async () => {
|
|
155
|
+
(0, _chai_spec_1.expect)(await ExifTool_1.exiftool.readRaw(malformedUtf8File, {
|
|
156
|
+
readArgs: ["-api", "Filter=", "-api", `Filter=${customFilter}`],
|
|
157
|
+
})).to.containSubset({ City: "XK" });
|
|
158
|
+
});
|
|
159
|
+
it("does not alter malformed binary list items", async () => {
|
|
160
|
+
// The synthetic LFP contains two 13-byte JSONMetadata binary items.
|
|
161
|
+
// Ground truth without a Filter:
|
|
162
|
+
// exiftool -j -G1 -struct test/malformed-utf8.lfp
|
|
163
|
+
const tags = await ExifTool_1.exiftool.readRaw(binaryListFile, {
|
|
164
|
+
readArgs: ["-G1", "-JSONMetadata", "-Foo"],
|
|
165
|
+
});
|
|
166
|
+
(0, _chai_spec_1.expect)(tags).to.containSubset({
|
|
167
|
+
"Lytro:JSONMetadata": [
|
|
168
|
+
"(Binary data 13 bytes, use -b option to extract)",
|
|
169
|
+
"(Binary data 13 bytes, use -b option to extract)",
|
|
170
|
+
],
|
|
171
|
+
"Lytro:Foo": "\uFFFDL",
|
|
172
|
+
});
|
|
173
|
+
(0, _chai_spec_1.expect)(tags.invalidUtf8Bytes).to.deep.equal({
|
|
174
|
+
"Lytro:Foo": new Uint8Array([0xdd, 0x4c]),
|
|
175
|
+
});
|
|
176
|
+
});
|
|
177
|
+
it("repairs text that has the same bytes as an unrelated binary tag", async () => {
|
|
178
|
+
// Both tags contain dc 4b. Ground truth:
|
|
179
|
+
// exiftool -b -MIE-Geo:City malformed-utf8-collision.mie | od -An -tx1
|
|
180
|
+
// exiftool -b -MIE-Audio:RelatedAudioFile ... | od -An -tx1
|
|
181
|
+
const tags = await ExifTool_1.exiftool.readRaw(binaryCollisionFile, {
|
|
182
|
+
readArgs: ["-G1", "-City", "-RelatedAudioFile"],
|
|
183
|
+
});
|
|
184
|
+
(0, _chai_spec_1.expect)(tags).to.containSubset({
|
|
185
|
+
"MIE-Geo:City": "\uFFFDK",
|
|
186
|
+
"MIE-Audio:RelatedAudioFile": "(Binary data 2 bytes, use -b option to extract)",
|
|
187
|
+
});
|
|
188
|
+
(0, _chai_spec_1.expect)(tags.invalidUtf8Bytes).to.deep.equal({
|
|
189
|
+
"MIE-Geo:City": new Uint8Array([0xdc, 0x4b]),
|
|
190
|
+
});
|
|
191
|
+
});
|
|
192
|
+
it("preserves malformed binary output", async () => {
|
|
193
|
+
const tags = await ExifTool_1.exiftool.readRaw(binaryCollisionFile, {
|
|
194
|
+
readArgs: ["-G1", "-b", "-RelatedAudioFile"],
|
|
195
|
+
});
|
|
196
|
+
(0, _chai_spec_1.expect)(tags).to.containSubset({
|
|
197
|
+
// base64 for the original bytes dc 4b
|
|
198
|
+
"MIE-Audio:RelatedAudioFile": "base64:3Es=",
|
|
199
|
+
});
|
|
200
|
+
(0, _chai_spec_1.expect)(tags.invalidUtf8Bytes).to.equal(undefined);
|
|
201
|
+
});
|
|
202
|
+
});
|
|
12
203
|
describe("non-alphanumeric filenames", () => {
|
|
13
204
|
for (const { str, desc } of _chai_spec_1.NonAlphaStrings) {
|
|
14
205
|
it("reads with " + desc, async () => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ReadRawTask.spec.js","sourceRoot":"","sources":["../src/ReadRawTask.spec.ts"],"names":[],"mappings":";;;;;AAAA,+CAA4C;AAC5C,0DAA6B;AAC7B,yCAAsC;AACtC,6CAQsB;AAEtB,KAAK,CAAC,GAAG,EAAE,CAAC,IAAA,gBAAG,EAAC,mBAAQ,CAAC,CAAC,CAAC;AAE3B,QAAQ,CAAC,aAAa,EAAE,GAAG,EAAE;IAC3B,QAAQ,CAAC,4BAA4B,EAAE,GAAG,EAAE;QAC1C,KAAK,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,IAAI,4BAAe,EAAE,CAAC;YAC5C,EAAE,CAAC,aAAa,GAAG,IAAI,EAAE,KAAK,IAAI,EAAE;gBAClC,IAAA,mBAAM,EAAC,mBAAQ,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;gBAC/C,MAAM,QAAQ,GAAG,GAAG,GAAG,MAAM,CAAC;gBAC9B,MAAM,IAAI,GAAG,mBAAI,CAAC,IAAI,CAAC,IAAA,mBAAM,GAAE,EAAE,QAAQ,CAAC,CAAC;gBAC3C,MAAM,IAAA,mBAAM,EAAC,IAAA,mBAAM,GAAE,CAAC,CAAC;gBACvB,MAAM,IAAA,mBAAQ,EAAC,mBAAI,CAAC,IAAI,CAAC,oBAAO,EAAE,YAAY,CAAC,EAAE,IAAI,CAAC,CAAC;gBACvD,MAAM,CAAC,GAAG,MAAM,mBAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;gBACvC,IAAA,mBAAM,EAAC,CAAC,CAAC,CAAC,EAAE,CAAC,aAAa,CAAC;oBACzB,QAAQ,EAAE,YAAY;oBACtB,QAAQ;oBACR,gBAAgB,EAAE,yBAAyB;oBAC3C,IAAI,EAAE,OAAO;oBACb,KAAK,EAAE,eAAe;oBACtB,gBAAgB,EAAE,mCAAmC;oBACrD,cAAc,EAAE,CAAC,MAAM,EAAE,UAAU,EAAE,OAAO,CAAC;oBAC7C,KAAK,EAAE,+BAAkB;iBAC1B,CAAC,CAAC;YACL,CAAC,CAAC,CAAC;QACL,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,6DAA6D;IAC7D,qEAAqE;IACrE,uBAAuB;IACvB,QAAQ,CAAC,8BAA8B,EAAE,GAAG,EAAE;QAC5C,EAAE,CAAC,0DAA0D,EAAE,KAAK,IAAI,EAAE;YACxE,OAAO,IAAA,mBAAM,EACX,mBAAQ,CAAC,OAAO,CAAC,2CAA2C,CAAC,CAC9D,CAAC,EAAE,CAAC,EAAE,CAAC,YAAY,CAAC,mBAAmB,CAAC,CAAC;QAC5C,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,2DAA2D,EAAE,KAAK,IAAI,EAAE;YACzE,OAAO,IAAA,mBAAM,EACX,mBAAQ,CAAC,OAAO,CAAC,4BAA4B,CAAC,CAC/C,CAAC,EAAE,CAAC,EAAE,CAAC,YAAY,CAAC,mBAAmB,CAAC,CAAC;QAC5C,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
1
|
+
{"version":3,"file":"ReadRawTask.spec.js","sourceRoot":"","sources":["../src/ReadRawTask.spec.ts"],"names":[],"mappings":"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"}
|
package/dist/ReadTask.js
CHANGED
|
@@ -50,6 +50,7 @@ const ExifToolTask_1 = require("./ExifToolTask");
|
|
|
50
50
|
const File_1 = require("./File");
|
|
51
51
|
const FilenameCharsetArgs_1 = require("./FilenameCharsetArgs");
|
|
52
52
|
const GPS_1 = require("./GPS");
|
|
53
|
+
const InvalidUtf8Bytes_1 = require("./InvalidUtf8Bytes");
|
|
53
54
|
const Lazy_1 = require("./Lazy");
|
|
54
55
|
const Number_1 = require("./Number");
|
|
55
56
|
const Object_1 = require("./Object");
|
|
@@ -58,6 +59,7 @@ const Pick_1 = require("./Pick");
|
|
|
58
59
|
const String_1 = require("./String");
|
|
59
60
|
const TagNameValidation_1 = require("./TagNameValidation");
|
|
60
61
|
const Timezones_1 = require("./Timezones");
|
|
62
|
+
const Utf8JsonFilter_1 = require("./Utf8JsonFilter");
|
|
61
63
|
/**
|
|
62
64
|
* tag names we don't need to muck with, but name conventions (like including
|
|
63
65
|
* "date") suggest they might be date/time tags
|
|
@@ -104,6 +106,8 @@ class ReadTask extends ExifToolTask_1.ExifToolTask {
|
|
|
104
106
|
degroup;
|
|
105
107
|
#raw = {};
|
|
106
108
|
#rawDegrouped = {};
|
|
109
|
+
#invalidUtf8Bytes;
|
|
110
|
+
#unwrapInvalidUtf8;
|
|
107
111
|
#tags = {};
|
|
108
112
|
/**
|
|
109
113
|
* @param sourceFile the file to read
|
|
@@ -116,6 +120,7 @@ class ReadTask extends ExifToolTask_1.ExifToolTask {
|
|
|
116
120
|
this.sourceFile = sourceFile;
|
|
117
121
|
this.args = args;
|
|
118
122
|
this.options = options;
|
|
123
|
+
this.#unwrapInvalidUtf8 = (0, Utf8JsonFilter_1.hasBuiltInUtf8Filter)(args);
|
|
119
124
|
// See https://github.com/photostructure/exiftool-vendored.js/issues/147#issuecomment-1642580118
|
|
120
125
|
this.degroup = this.args.includes("-G");
|
|
121
126
|
this.#tags = { SourceFile: sourceFile };
|
|
@@ -127,10 +132,12 @@ class ReadTask extends ExifToolTask_1.ExifToolTask {
|
|
|
127
132
|
...options,
|
|
128
133
|
});
|
|
129
134
|
const sourceFile = _path.resolve(filename);
|
|
135
|
+
const readArgs = (0, Array_1.toArray)(opts.readArgs);
|
|
130
136
|
const args = [
|
|
131
137
|
...FilenameCharsetArgs_1.Utf8FilenameCharsetArgs,
|
|
132
138
|
"-json",
|
|
133
|
-
...
|
|
139
|
+
...readArgs,
|
|
140
|
+
...(0, Utf8JsonFilter_1.utf8JsonFilterArgs)(readArgs),
|
|
134
141
|
];
|
|
135
142
|
// "-api struct=undef" doesn't work: but it's the same as struct=0:
|
|
136
143
|
args.push("-api", "struct=" + ((0, Number_1.isNumber)(opts.struct) ? opts.struct : "0"));
|
|
@@ -171,7 +178,12 @@ class ReadTask extends ExifToolTask_1.ExifToolTask {
|
|
|
171
178
|
try {
|
|
172
179
|
// Fix ExifToolVersion to be a string to preserve version distinctions like 12.3 vs 12.30
|
|
173
180
|
const versionFixedData = data.replace(/"ExifToolVersion"\s*:\s*(\d+(?:\.\d+)?)/, '"ExifToolVersion":"$1"');
|
|
174
|
-
|
|
181
|
+
const parsed = JSON.parse(versionFixedData)[0];
|
|
182
|
+
const decoded = this.#unwrapInvalidUtf8
|
|
183
|
+
? (0, InvalidUtf8Bytes_1.unwrapInvalidUtf8Tags)(parsed)
|
|
184
|
+
: { tags: parsed };
|
|
185
|
+
this.#raw = decoded.tags;
|
|
186
|
+
this.#invalidUtf8Bytes = decoded.invalidUtf8Bytes;
|
|
175
187
|
}
|
|
176
188
|
catch (jsonError) {
|
|
177
189
|
// TODO: should restart exiftool?
|
|
@@ -243,6 +255,16 @@ class ReadTask extends ExifToolTask_1.ExifToolTask {
|
|
|
243
255
|
const { errors, warnings } = (0, ErrorsAndWarnings_1.errorsAndWarnings)(this, tags);
|
|
244
256
|
tags.errors = errors;
|
|
245
257
|
tags.warnings = warnings;
|
|
258
|
+
if (this.#invalidUtf8Bytes != null) {
|
|
259
|
+
for (const key of Object.keys(this.#invalidUtf8Bytes)) {
|
|
260
|
+
if (!Object.hasOwn(tags, key)) {
|
|
261
|
+
Reflect.deleteProperty(this.#invalidUtf8Bytes, key);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
if (Object.keys(this.#invalidUtf8Bytes).length > 0) {
|
|
265
|
+
tags.invalidUtf8Bytes = this.#invalidUtf8Bytes;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
246
268
|
return tags;
|
|
247
269
|
}
|
|
248
270
|
#extractGpsMetadata = (0, Lazy_1.lazy)(() => {
|