@aglyn/aglyn 1.0.0-beta.197 → 1.0.0-beta.199

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 (80) hide show
  1. package/package.json +11 -11
  2. package/src/lib/app-utils/docs-help.generated.d.ts +1 -1
  3. package/src/lib/app-utils/docs-help.generated.js +1 -0
  4. package/src/lib/app-utils/docs-help.generated.js.map +1 -1
  5. package/src/lib/app-utils/docs-index.generated.js +3 -3
  6. package/src/lib/app-utils/docs-index.generated.js.map +1 -1
  7. package/src/lib/app-utils/media-embedded-fields.d.ts +471 -0
  8. package/src/lib/app-utils/media-embedded-fields.js +619 -0
  9. package/src/lib/app-utils/media-embedded-fields.js.map +1 -0
  10. package/src/lib/app-utils/media-embedded-metadata/exif.d.ts +160 -0
  11. package/src/lib/app-utils/media-embedded-metadata/exif.js +1267 -0
  12. package/src/lib/app-utils/media-embedded-metadata/exif.js.map +1 -0
  13. package/src/lib/app-utils/media-embedded-metadata/gif.d.ts +44 -0
  14. package/src/lib/app-utils/media-embedded-metadata/gif.js +134 -0
  15. package/src/lib/app-utils/media-embedded-metadata/gif.js.map +1 -0
  16. package/src/lib/app-utils/media-embedded-metadata/heif.d.ts +23 -0
  17. package/src/lib/app-utils/media-embedded-metadata/heif.js +354 -0
  18. package/src/lib/app-utils/media-embedded-metadata/heif.js.map +1 -0
  19. package/src/lib/app-utils/media-embedded-metadata/icc.d.ts +22 -0
  20. package/src/lib/app-utils/media-embedded-metadata/icc.js +85 -0
  21. package/src/lib/app-utils/media-embedded-metadata/icc.js.map +1 -0
  22. package/src/lib/app-utils/media-embedded-metadata/image-blocks.d.ts +145 -0
  23. package/src/lib/app-utils/media-embedded-metadata/image-blocks.js +200 -0
  24. package/src/lib/app-utils/media-embedded-metadata/image-blocks.js.map +1 -0
  25. package/src/lib/app-utils/media-embedded-metadata/index.d.ts +93 -0
  26. package/src/lib/app-utils/media-embedded-metadata/index.js +744 -0
  27. package/src/lib/app-utils/media-embedded-metadata/index.js.map +1 -0
  28. package/src/lib/app-utils/media-embedded-metadata/iptc.d.ts +59 -0
  29. package/src/lib/app-utils/media-embedded-metadata/iptc.js +529 -0
  30. package/src/lib/app-utils/media-embedded-metadata/iptc.js.map +1 -0
  31. package/src/lib/app-utils/media-embedded-metadata/jpeg.d.ts +43 -0
  32. package/src/lib/app-utils/media-embedded-metadata/jpeg.js +456 -0
  33. package/src/lib/app-utils/media-embedded-metadata/jpeg.js.map +1 -0
  34. package/src/lib/app-utils/media-embedded-metadata/mp4.d.ts +37 -0
  35. package/src/lib/app-utils/media-embedded-metadata/mp4.js +621 -0
  36. package/src/lib/app-utils/media-embedded-metadata/mp4.js.map +1 -0
  37. package/src/lib/app-utils/media-embedded-metadata/ooxml.d.ts +131 -0
  38. package/src/lib/app-utils/media-embedded-metadata/ooxml.js +1058 -0
  39. package/src/lib/app-utils/media-embedded-metadata/ooxml.js.map +1 -0
  40. package/src/lib/app-utils/media-embedded-metadata/pdf-objects.d.ts +223 -0
  41. package/src/lib/app-utils/media-embedded-metadata/pdf-objects.js +936 -0
  42. package/src/lib/app-utils/media-embedded-metadata/pdf-objects.js.map +1 -0
  43. package/src/lib/app-utils/media-embedded-metadata/pdf.d.ts +82 -0
  44. package/src/lib/app-utils/media-embedded-metadata/pdf.js +1167 -0
  45. package/src/lib/app-utils/media-embedded-metadata/pdf.js.map +1 -0
  46. package/src/lib/app-utils/media-embedded-metadata/png.d.ts +41 -0
  47. package/src/lib/app-utils/media-embedded-metadata/png.js +512 -0
  48. package/src/lib/app-utils/media-embedded-metadata/png.js.map +1 -0
  49. package/src/lib/app-utils/media-embedded-metadata/svg.d.ts +30 -0
  50. package/src/lib/app-utils/media-embedded-metadata/svg.js +154 -0
  51. package/src/lib/app-utils/media-embedded-metadata/svg.js.map +1 -0
  52. package/src/lib/app-utils/media-embedded-metadata/tiff.d.ts +40 -0
  53. package/src/lib/app-utils/media-embedded-metadata/tiff.js +158 -0
  54. package/src/lib/app-utils/media-embedded-metadata/tiff.js.map +1 -0
  55. package/src/lib/app-utils/media-embedded-metadata/types.d.ts +60 -0
  56. package/src/lib/app-utils/media-embedded-metadata/types.js +32 -0
  57. package/src/lib/app-utils/media-embedded-metadata/types.js.map +1 -0
  58. package/src/lib/app-utils/media-embedded-metadata/webm.d.ts +26 -0
  59. package/src/lib/app-utils/media-embedded-metadata/webm.js +417 -0
  60. package/src/lib/app-utils/media-embedded-metadata/webm.js.map +1 -0
  61. package/src/lib/app-utils/media-embedded-metadata/webp.d.ts +37 -0
  62. package/src/lib/app-utils/media-embedded-metadata/webp.js +272 -0
  63. package/src/lib/app-utils/media-embedded-metadata/webp.js.map +1 -0
  64. package/src/lib/app-utils/media-embedded-metadata/xml.d.ts +231 -0
  65. package/src/lib/app-utils/media-embedded-metadata/xml.js +598 -0
  66. package/src/lib/app-utils/media-embedded-metadata/xml.js.map +1 -0
  67. package/src/lib/app-utils/media-embedded-metadata/xmp.d.ts +100 -0
  68. package/src/lib/app-utils/media-embedded-metadata/xmp.js +1475 -0
  69. package/src/lib/app-utils/media-embedded-metadata/xmp.js.map +1 -0
  70. package/src/lib/app-utils/media-embedded-metadata/zip.d.ts +93 -0
  71. package/src/lib/app-utils/media-embedded-metadata/zip.js +531 -0
  72. package/src/lib/app-utils/media-embedded-metadata/zip.js.map +1 -0
  73. package/src/lib/app-utils/name-search.d.ts +12 -0
  74. package/src/lib/app-utils/name-search.js +11 -0
  75. package/src/lib/app-utils/name-search.js.map +1 -1
  76. package/src/lib/foundation/definitions/platform.types.d.ts +11 -0
  77. package/src/lib/foundation/definitions/platform.types.js.map +1 -1
  78. package/src/server.d.ts +1 -0
  79. package/src/server.js +7 -0
  80. package/src/server.js.map +1 -1
@@ -0,0 +1,354 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Aglyn LLC
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */ /**
17
+ * HEIF metadata blocks — HEIC, HEIF and AVIF — read-only (AGL-3331).
18
+ *
19
+ * These are ISO base media files (ISO/IEC 14496-12): a run of boxes, each a
20
+ * 32-bit size (1 = a 64-bit size follows, 0 = to the end), a four-character
21
+ * type, and a payload. A still image's metadata is not in a box of its own
22
+ * but in ITEMS (ISO/IEC 23008-12, 6 and Annex A):
23
+ *
24
+ * - `meta` (a full box) holds the item catalogue.
25
+ * - `iinf` lists the items; each `infe` (versions 2 and 3) names an item's
26
+ * type. EXIF is item type `Exif`; XMP is item type `mime` with content
27
+ * type `application/rdf+xml`.
28
+ * - `iloc` (versions 0–2) says where each item's bytes are: a base offset
29
+ * and extents, with field widths the box itself declares. Only
30
+ * construction method 0 (offsets into this file) is followed.
31
+ * - An `Exif` item starts with a 4-byte `exif_tiff_header_offset` (Annex
32
+ * A.2.1), the number of bytes — usually the six of `Exif\0\0` — between
33
+ * that field and the TIFF header.
34
+ * - `iref` `cdsc` references say which item a metadata item describes; the
35
+ * EXIF and XMP of the primary item (`pitm`) are preferred over those of a
36
+ * thumbnail or an auxiliary image.
37
+ * - The ICC profile is a `colr` property of type `prof` or `rICC` in
38
+ * `iprp`/`ipco`.
39
+ *
40
+ * HEIF is not writable here: its item offsets are absolute, so resizing an
41
+ * item would move every extent after it.
42
+ */ import { inflateRawSync, inflateSync } from "zlib";
43
+ import { asciiBytes, bytesAt, concatBytes, readAscii, readU16BE, readU32BE, trimNul, utf8Decode } from "./image-blocks.js";
44
+ /** `ftyp` brands that mark an image in the HEIF family. */ const HEIF_BRANDS = new Set([
45
+ 'mif1',
46
+ 'mif2',
47
+ 'msf1',
48
+ 'miaf',
49
+ 'heic',
50
+ 'heix',
51
+ 'heim',
52
+ 'heis',
53
+ 'hevc',
54
+ 'hevx',
55
+ 'hevm',
56
+ 'hevs',
57
+ 'avif',
58
+ 'avis',
59
+ 'avio'
60
+ ]);
61
+ const EXIF_HEADER = asciiBytes('Exif\0\0');
62
+ const TIFF_II = asciiBytes('II*\0');
63
+ const TIFF_MM = asciiBytes('MM\0*');
64
+ /** Box and item counts past which the walk stops. */ const MAX_BOXES = 65536;
65
+ const MAX_ITEMS = 65536;
66
+ /** The most a deflate-encoded XMP item may inflate to. */ const MAX_INFLATED = 32 * 1024 * 1024;
67
+ /** 2^53 bounds a safe integer; a 64-bit field above it is not an offset. */ function readU64BE(bytes, offset) {
68
+ const high = readU32BE(bytes, offset);
69
+ if (high > 0x1fffff) return Number.NaN;
70
+ return high * 0x100000000 + readU32BE(bytes, offset + 4);
71
+ }
72
+ /** Reads the boxes laid end to end in `[start, end)`, stopping at damage. */ function readBoxes(bytes, start, end) {
73
+ const boxes = [];
74
+ const limit = Math.min(end, bytes.length);
75
+ let offset = start;
76
+ while(offset + 8 <= limit && boxes.length < MAX_BOXES){
77
+ let size = readU32BE(bytes, offset);
78
+ let header = 8;
79
+ if (size === 1) {
80
+ if (offset + 16 > limit) break;
81
+ size = readU64BE(bytes, offset + 8);
82
+ header = 16;
83
+ } else if (size === 0) {
84
+ size = limit - offset;
85
+ }
86
+ if (!(size >= header) || offset + size > limit) break;
87
+ boxes.push({
88
+ type: readAscii(bytes, offset + 4, 4),
89
+ start: offset,
90
+ payload: offset + header,
91
+ end: offset + size
92
+ });
93
+ offset += size;
94
+ }
95
+ return boxes;
96
+ }
97
+ /** An unsigned big-endian integer of 0, 4 or 8 bytes, or NaN otherwise. */ function readSized(bytes, offset, size) {
98
+ if (size === 0) return 0;
99
+ if (size === 4) return readU32BE(bytes, offset);
100
+ if (size === 8) return readU64BE(bytes, offset);
101
+ return Number.NaN;
102
+ }
103
+ /** A NUL-terminated string inside `[offset, end)`, and where it ends. */ function readCString(bytes, offset, end) {
104
+ const nul = bytes.subarray(0, end).indexOf(0, offset);
105
+ const stop = nul < 0 ? end : nul;
106
+ return {
107
+ text: utf8Decode(bytes.subarray(offset, stop)),
108
+ next: stop + 1
109
+ };
110
+ }
111
+ /** The `infe` entries of an `iinf` box (ISO/IEC 14496-12, 8.11.6). */ function readItemInfos(bytes, iinf) {
112
+ var _bytes_iinf_payload;
113
+ const version = (_bytes_iinf_payload = bytes[iinf.payload]) != null ? _bytes_iinf_payload : 0;
114
+ const entries = iinf.payload + 4 + (version === 0 ? 2 : 4);
115
+ const items = [];
116
+ for (const infe of readBoxes(bytes, entries, iinf.end)){
117
+ var _bytes_infe_payload;
118
+ if (infe.type !== 'infe' || items.length >= MAX_ITEMS) continue;
119
+ const infeVersion = (_bytes_infe_payload = bytes[infe.payload]) != null ? _bytes_infe_payload : 0;
120
+ // Versions 0 and 1 predate item types; HEIF requires 2 or 3.
121
+ if (infeVersion !== 2 && infeVersion !== 3) continue;
122
+ let at = infe.payload + 4;
123
+ const id = infeVersion === 2 ? readU16BE(bytes, at) : readU32BE(bytes, at);
124
+ at += infeVersion === 2 ? 2 : 4;
125
+ at += 2; // item_protection_index
126
+ if (at + 4 > infe.end) continue;
127
+ const type = readAscii(bytes, at, 4);
128
+ at += 4;
129
+ const item = {
130
+ id,
131
+ type
132
+ };
133
+ const name = readCString(bytes, at, infe.end);
134
+ if (type === 'mime' && name.next < infe.end) {
135
+ const contentType = readCString(bytes, name.next, infe.end);
136
+ item.contentType = contentType.text;
137
+ if (contentType.next < infe.end) {
138
+ item.contentEncoding = readCString(bytes, contentType.next, infe.end).text;
139
+ }
140
+ }
141
+ items.push(item);
142
+ }
143
+ return items;
144
+ }
145
+ /**
146
+ * The locations of the wanted items from an `iloc` box (ISO/IEC 14496-12,
147
+ * 8.11.3), or an empty map when the box is malformed.
148
+ */ function readItemLocations(bytes, iloc, wanted) {
149
+ var _bytes_iloc_payload, _bytes_at, _bytes_;
150
+ const out = new Map();
151
+ const version = (_bytes_iloc_payload = bytes[iloc.payload]) != null ? _bytes_iloc_payload : 0;
152
+ if (version > 2) return out;
153
+ let at = iloc.payload + 4;
154
+ const sizes = (_bytes_at = bytes[at]) != null ? _bytes_at : 0;
155
+ const sizes2 = (_bytes_ = bytes[at + 1]) != null ? _bytes_ : 0;
156
+ const offsetSize = sizes >> 4;
157
+ const lengthSize = sizes & 0x0f;
158
+ const baseOffsetSize = sizes2 >> 4;
159
+ const indexSize = version === 1 || version === 2 ? sizes2 & 0x0f : 0;
160
+ at += 2;
161
+ const itemCount = version < 2 ? readU16BE(bytes, at) : readU32BE(bytes, at);
162
+ at += version < 2 ? 2 : 4;
163
+ for(let i = 0; i < itemCount && i < MAX_ITEMS; i += 1){
164
+ if (at >= iloc.end) break;
165
+ const id = version < 2 ? readU16BE(bytes, at) : readU32BE(bytes, at);
166
+ at += version < 2 ? 2 : 4;
167
+ let constructionMethod = 0;
168
+ if (version === 1 || version === 2) {
169
+ constructionMethod = readU16BE(bytes, at) & 0x0f;
170
+ at += 2;
171
+ }
172
+ const dataReferenceIndex = readU16BE(bytes, at);
173
+ at += 2;
174
+ const baseOffset = readSized(bytes, at, baseOffsetSize);
175
+ at += baseOffsetSize;
176
+ const extentCount = readU16BE(bytes, at);
177
+ at += 2;
178
+ const extents = [];
179
+ for(let e = 0; e < extentCount; e += 1){
180
+ at += indexSize;
181
+ const offset = readSized(bytes, at, offsetSize);
182
+ at += offsetSize;
183
+ const length = readSized(bytes, at, lengthSize);
184
+ at += lengthSize;
185
+ if (at > iloc.end || Number.isNaN(offset) || Number.isNaN(length) || Number.isNaN(baseOffset)) {
186
+ return out;
187
+ }
188
+ extents.push({
189
+ offset: baseOffset + offset,
190
+ length
191
+ });
192
+ }
193
+ if (wanted.has(id) && !out.has(id)) {
194
+ out.set(id, {
195
+ constructionMethod,
196
+ dataReferenceIndex,
197
+ extents
198
+ });
199
+ }
200
+ }
201
+ return out;
202
+ }
203
+ /** The item's bytes, or `null` when it is stored anywhere but this file. */ function itemBytes(bytes, location) {
204
+ if (!location || location.constructionMethod !== 0 || location.dataReferenceIndex !== 0) {
205
+ return null;
206
+ }
207
+ if (!location.extents.length) return null;
208
+ const parts = [];
209
+ for (const { offset, length } of location.extents){
210
+ // A zero length means "to the end of the file" (8.11.3.3).
211
+ const end = length === 0 ? bytes.length : offset + length;
212
+ if (offset < 0 || offset > bytes.length || end > bytes.length) return null;
213
+ parts.push(bytes.subarray(offset, end));
214
+ }
215
+ return concatBytes(parts);
216
+ }
217
+ /**
218
+ * The TIFF bytes of an `Exif` item: past the 4-byte offset field and the
219
+ * offset it gives. Writers have been seen to leave `Exif\0\0` in front of
220
+ * the header while declaring an offset of 0, or to declare 6 with no prefix
221
+ * written, so those two readings are tried too — but only where a TIFF
222
+ * header actually begins, never a guess.
223
+ */ function exifFromItem(payload) {
224
+ const isTiff = (at)=>bytesAt(payload, at, TIFF_II) || bytesAt(payload, at, TIFF_MM);
225
+ const declared = 4 + readU32BE(payload, 0);
226
+ for (const at of [
227
+ declared,
228
+ 4
229
+ ]){
230
+ if (isTiff(at)) return payload.slice(at);
231
+ if (bytesAt(payload, at, EXIF_HEADER) && isTiff(at + EXIF_HEADER.length)) {
232
+ return payload.slice(at + EXIF_HEADER.length);
233
+ }
234
+ }
235
+ return undefined;
236
+ }
237
+ /** The text of an XMP item, decoding a `deflate` content encoding. */ function xmpFromItem(payload, encoding) {
238
+ let data = payload;
239
+ if (encoding === 'deflate') {
240
+ try {
241
+ data = new Uint8Array(inflateSync(payload, {
242
+ maxOutputLength: MAX_INFLATED
243
+ }));
244
+ } catch (unused) {
245
+ try {
246
+ data = new Uint8Array(inflateRawSync(payload, {
247
+ maxOutputLength: MAX_INFLATED
248
+ }));
249
+ } catch (unused) {
250
+ return undefined;
251
+ }
252
+ }
253
+ } else if (encoding) {
254
+ return undefined;
255
+ }
256
+ return trimNul(utf8Decode(data));
257
+ }
258
+ /** The item ids each `cdsc` reference says an item describes. */ function describedBy(bytes, iref) {
259
+ var _bytes_iref_payload;
260
+ const out = new Map();
261
+ if (!iref) return out;
262
+ const wide = ((_bytes_iref_payload = bytes[iref.payload]) != null ? _bytes_iref_payload : 0) === 1;
263
+ const idSize = wide ? 4 : 2;
264
+ const readId = (at)=>wide ? readU32BE(bytes, at) : readU16BE(bytes, at);
265
+ for (const reference of readBoxes(bytes, iref.payload + 4, iref.end)){
266
+ if (reference.type !== 'cdsc') continue;
267
+ const from = readId(reference.payload);
268
+ const count = readU16BE(bytes, reference.payload + idSize);
269
+ const to = [];
270
+ for(let i = 0; i < count; i += 1){
271
+ const at = reference.payload + idSize + 2 + i * idSize;
272
+ if (at + idSize > reference.end) break;
273
+ to.push(readId(at));
274
+ }
275
+ out.set(from, to);
276
+ }
277
+ return out;
278
+ }
279
+ /**
280
+ * Reads the metadata blocks of a HEIC, HEIF or AVIF image. Returns `null`
281
+ * when the bytes are not in the HEIF family (no `ftyp` with a HEIF brand);
282
+ * a malformed file yields what could be read.
283
+ */ export function readHeifBlocks(bytes) {
284
+ var _bytes_pitm_payload;
285
+ const top = readBoxes(bytes, 0, bytes.length);
286
+ const ftyp = top[0];
287
+ if (!ftyp || ftyp.type !== 'ftyp') return null;
288
+ // The major brand, a minor version (skipped), then compatible brands.
289
+ let heif = HEIF_BRANDS.has(readAscii(bytes, ftyp.payload, 4));
290
+ for(let at = ftyp.payload + 8; at + 4 <= ftyp.end && !heif; at += 4){
291
+ heif = HEIF_BRANDS.has(readAscii(bytes, at, 4));
292
+ }
293
+ if (!heif) return null;
294
+ const blocks = {};
295
+ const meta = top.find((box)=>box.type === 'meta');
296
+ if (!meta) return blocks;
297
+ const children = readBoxes(bytes, meta.payload + 4, meta.end);
298
+ const child = (type)=>children.find((box)=>box.type === type);
299
+ const iprp = child('iprp');
300
+ const ipco = iprp && readBoxes(bytes, iprp.payload, iprp.end).find((box)=>box.type === 'ipco');
301
+ for (const property of ipco ? readBoxes(bytes, ipco.payload, ipco.end) : []){
302
+ if (property.type !== 'colr' || property.payload + 4 > property.end) continue;
303
+ const colourType = readAscii(bytes, property.payload, 4);
304
+ if (colourType === 'prof' || colourType === 'rICC') {
305
+ blocks.icc = bytes.slice(property.payload + 4, property.end);
306
+ break;
307
+ }
308
+ }
309
+ const iinf = child('iinf');
310
+ const iloc = child('iloc');
311
+ if (!iinf || !iloc) return blocks;
312
+ const infos = readItemInfos(bytes, iinf);
313
+ const exifItems = infos.filter((item)=>item.type === 'Exif');
314
+ const xmpItems = infos.filter((item)=>{
315
+ var _item_contentType;
316
+ return item.type === 'mime' && ((_item_contentType = item.contentType) == null ? void 0 : _item_contentType.trim().toLowerCase()) === 'application/rdf+xml';
317
+ });
318
+ if (!exifItems.length && !xmpItems.length) return blocks;
319
+ const pitm = child('pitm');
320
+ const primary = pitm ? ((_bytes_pitm_payload = bytes[pitm.payload]) != null ? _bytes_pitm_payload : 0) === 0 ? readU16BE(bytes, pitm.payload + 4) : readU32BE(bytes, pitm.payload + 4) : undefined;
321
+ const references = describedBy(bytes, child('iref'));
322
+ // The primary image's own metadata first, then the rest in file order.
323
+ const describesPrimary = (item)=>{
324
+ var _references_get;
325
+ return primary !== undefined && ((_references_get = references.get(item.id)) != null ? _references_get : []).includes(primary);
326
+ };
327
+ const rank = (items)=>[
328
+ ...items.filter(describesPrimary),
329
+ ...items.filter((item)=>!describesPrimary(item))
330
+ ];
331
+ const locations = readItemLocations(bytes, iloc, new Set([
332
+ ...exifItems,
333
+ ...xmpItems
334
+ ].map((item)=>item.id)));
335
+ for (const item of rank(exifItems)){
336
+ const payload = itemBytes(bytes, locations.get(item.id));
337
+ const exif = payload && exifFromItem(payload);
338
+ if (exif) {
339
+ blocks.exif = exif;
340
+ break;
341
+ }
342
+ }
343
+ for (const item of rank(xmpItems)){
344
+ const payload = itemBytes(bytes, locations.get(item.id));
345
+ const xmp = payload && xmpFromItem(payload, item.contentEncoding);
346
+ if (xmp) {
347
+ blocks.xmp = xmp;
348
+ break;
349
+ }
350
+ }
351
+ return blocks;
352
+ }
353
+
354
+ //# sourceMappingURL=heif.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../../libs/aglyn/src/lib/app-utils/media-embedded-metadata/heif.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * HEIF metadata blocks — HEIC, HEIF and AVIF — read-only (AGL-3331).\n *\n * These are ISO base media files (ISO/IEC 14496-12): a run of boxes, each a\n * 32-bit size (1 = a 64-bit size follows, 0 = to the end), a four-character\n * type, and a payload. A still image's metadata is not in a box of its own\n * but in ITEMS (ISO/IEC 23008-12, 6 and Annex A):\n *\n * - `meta` (a full box) holds the item catalogue.\n * - `iinf` lists the items; each `infe` (versions 2 and 3) names an item's\n * type. EXIF is item type `Exif`; XMP is item type `mime` with content\n * type `application/rdf+xml`.\n * - `iloc` (versions 0–2) says where each item's bytes are: a base offset\n * and extents, with field widths the box itself declares. Only\n * construction method 0 (offsets into this file) is followed.\n * - An `Exif` item starts with a 4-byte `exif_tiff_header_offset` (Annex\n * A.2.1), the number of bytes — usually the six of `Exif\\0\\0` — between\n * that field and the TIFF header.\n * - `iref` `cdsc` references say which item a metadata item describes; the\n * EXIF and XMP of the primary item (`pitm`) are preferred over those of a\n * thumbnail or an auxiliary image.\n * - The ICC profile is a `colr` property of type `prof` or `rICC` in\n * `iprp`/`ipco`.\n *\n * HEIF is not writable here: its item offsets are absolute, so resizing an\n * item would move every extent after it.\n */\n\nimport { inflateRawSync, inflateSync } from 'zlib'\nimport {\n asciiBytes,\n bytesAt,\n concatBytes,\n readAscii,\n readU16BE,\n readU32BE,\n trimNul,\n utf8Decode,\n type ImageBlocks,\n} from './image-blocks'\n\n/** `ftyp` brands that mark an image in the HEIF family. */\nconst HEIF_BRANDS = new Set([\n 'mif1',\n 'mif2',\n 'msf1',\n 'miaf',\n 'heic',\n 'heix',\n 'heim',\n 'heis',\n 'hevc',\n 'hevx',\n 'hevm',\n 'hevs',\n 'avif',\n 'avis',\n 'avio',\n])\n\nconst EXIF_HEADER = asciiBytes('Exif\\0\\0')\nconst TIFF_II = asciiBytes('II*\\0')\nconst TIFF_MM = asciiBytes('MM\\0*')\n\n/** Box and item counts past which the walk stops. */\nconst MAX_BOXES = 65536\nconst MAX_ITEMS = 65536\n/** The most a deflate-encoded XMP item may inflate to. */\nconst MAX_INFLATED = 32 * 1024 * 1024\n\ninterface Box {\n type: string\n start: number\n /** First payload byte, after the size, type and any large size. */\n payload: number\n end: number\n}\n\n/** 2^53 bounds a safe integer; a 64-bit field above it is not an offset. */\nfunction readU64BE(bytes: Uint8Array, offset: number): number {\n const high = readU32BE(bytes, offset)\n if (high > 0x1fffff) return Number.NaN\n return high * 0x100000000 + readU32BE(bytes, offset + 4)\n}\n\n/** Reads the boxes laid end to end in `[start, end)`, stopping at damage. */\nfunction readBoxes(bytes: Uint8Array, start: number, end: number): Box[] {\n const boxes: Box[] = []\n const limit = Math.min(end, bytes.length)\n let offset = start\n while (offset + 8 <= limit && boxes.length < MAX_BOXES) {\n let size = readU32BE(bytes, offset)\n let header = 8\n if (size === 1) {\n if (offset + 16 > limit) break\n size = readU64BE(bytes, offset + 8)\n header = 16\n } else if (size === 0) {\n size = limit - offset\n }\n if (!(size >= header) || offset + size > limit) break\n boxes.push({\n type: readAscii(bytes, offset + 4, 4),\n start: offset,\n payload: offset + header,\n end: offset + size,\n })\n offset += size\n }\n return boxes\n}\n\n/** An unsigned big-endian integer of 0, 4 or 8 bytes, or NaN otherwise. */\nfunction readSized(bytes: Uint8Array, offset: number, size: number): number {\n if (size === 0) return 0\n if (size === 4) return readU32BE(bytes, offset)\n if (size === 8) return readU64BE(bytes, offset)\n return Number.NaN\n}\n\n/** A NUL-terminated string inside `[offset, end)`, and where it ends. */\nfunction readCString(\n bytes: Uint8Array,\n offset: number,\n end: number,\n): { text: string; next: number } {\n const nul = bytes.subarray(0, end).indexOf(0, offset)\n const stop = nul < 0 ? end : nul\n return { text: utf8Decode(bytes.subarray(offset, stop)), next: stop + 1 }\n}\n\ninterface ItemInfo {\n id: number\n type: string\n contentType?: string\n contentEncoding?: string\n}\n\n/** The `infe` entries of an `iinf` box (ISO/IEC 14496-12, 8.11.6). */\nfunction readItemInfos(bytes: Uint8Array, iinf: Box): ItemInfo[] {\n const version = bytes[iinf.payload] ?? 0\n const entries = iinf.payload + 4 + (version === 0 ? 2 : 4)\n const items: ItemInfo[] = []\n for (const infe of readBoxes(bytes, entries, iinf.end)) {\n if (infe.type !== 'infe' || items.length >= MAX_ITEMS) continue\n const infeVersion = bytes[infe.payload] ?? 0\n // Versions 0 and 1 predate item types; HEIF requires 2 or 3.\n if (infeVersion !== 2 && infeVersion !== 3) continue\n let at = infe.payload + 4\n const id = infeVersion === 2 ? readU16BE(bytes, at) : readU32BE(bytes, at)\n at += infeVersion === 2 ? 2 : 4\n at += 2 // item_protection_index\n if (at + 4 > infe.end) continue\n const type = readAscii(bytes, at, 4)\n at += 4\n const item: ItemInfo = { id, type }\n const name = readCString(bytes, at, infe.end)\n if (type === 'mime' && name.next < infe.end) {\n const contentType = readCString(bytes, name.next, infe.end)\n item.contentType = contentType.text\n if (contentType.next < infe.end) {\n item.contentEncoding = readCString(\n bytes,\n contentType.next,\n infe.end,\n ).text\n }\n }\n items.push(item)\n }\n return items\n}\n\ninterface ItemLocation {\n constructionMethod: number\n dataReferenceIndex: number\n extents: { offset: number; length: number }[]\n}\n\n/**\n * The locations of the wanted items from an `iloc` box (ISO/IEC 14496-12,\n * 8.11.3), or an empty map when the box is malformed.\n */\nfunction readItemLocations(\n bytes: Uint8Array,\n iloc: Box,\n wanted: Set<number>,\n): Map<number, ItemLocation> {\n const out = new Map<number, ItemLocation>()\n const version = bytes[iloc.payload] ?? 0\n if (version > 2) return out\n let at = iloc.payload + 4\n const sizes = bytes[at] ?? 0\n const sizes2 = bytes[at + 1] ?? 0\n const offsetSize = sizes >> 4\n const lengthSize = sizes & 0x0f\n const baseOffsetSize = sizes2 >> 4\n const indexSize = version === 1 || version === 2 ? sizes2 & 0x0f : 0\n at += 2\n const itemCount = version < 2 ? readU16BE(bytes, at) : readU32BE(bytes, at)\n at += version < 2 ? 2 : 4\n for (let i = 0; i < itemCount && i < MAX_ITEMS; i += 1) {\n if (at >= iloc.end) break\n const id = version < 2 ? readU16BE(bytes, at) : readU32BE(bytes, at)\n at += version < 2 ? 2 : 4\n let constructionMethod = 0\n if (version === 1 || version === 2) {\n constructionMethod = readU16BE(bytes, at) & 0x0f\n at += 2\n }\n const dataReferenceIndex = readU16BE(bytes, at)\n at += 2\n const baseOffset = readSized(bytes, at, baseOffsetSize)\n at += baseOffsetSize\n const extentCount = readU16BE(bytes, at)\n at += 2\n const extents: { offset: number; length: number }[] = []\n for (let e = 0; e < extentCount; e += 1) {\n at += indexSize\n const offset = readSized(bytes, at, offsetSize)\n at += offsetSize\n const length = readSized(bytes, at, lengthSize)\n at += lengthSize\n if (\n at > iloc.end ||\n Number.isNaN(offset) ||\n Number.isNaN(length) ||\n Number.isNaN(baseOffset)\n ) {\n return out\n }\n extents.push({ offset: baseOffset + offset, length })\n }\n if (wanted.has(id) && !out.has(id)) {\n out.set(id, { constructionMethod, dataReferenceIndex, extents })\n }\n }\n return out\n}\n\n/** The item's bytes, or `null` when it is stored anywhere but this file. */\nfunction itemBytes(\n bytes: Uint8Array,\n location: ItemLocation | undefined,\n): Uint8Array | null {\n if (\n !location ||\n location.constructionMethod !== 0 ||\n location.dataReferenceIndex !== 0\n ) {\n return null\n }\n if (!location.extents.length) return null\n const parts: Uint8Array[] = []\n for (const { offset, length } of location.extents) {\n // A zero length means \"to the end of the file\" (8.11.3.3).\n const end = length === 0 ? bytes.length : offset + length\n if (offset < 0 || offset > bytes.length || end > bytes.length) return null\n parts.push(bytes.subarray(offset, end))\n }\n return concatBytes(parts)\n}\n\n/**\n * The TIFF bytes of an `Exif` item: past the 4-byte offset field and the\n * offset it gives. Writers have been seen to leave `Exif\\0\\0` in front of\n * the header while declaring an offset of 0, or to declare 6 with no prefix\n * written, so those two readings are tried too — but only where a TIFF\n * header actually begins, never a guess.\n */\nfunction exifFromItem(payload: Uint8Array): Uint8Array | undefined {\n const isTiff = (at: number) =>\n bytesAt(payload, at, TIFF_II) || bytesAt(payload, at, TIFF_MM)\n const declared = 4 + readU32BE(payload, 0)\n for (const at of [declared, 4]) {\n if (isTiff(at)) return payload.slice(at)\n if (bytesAt(payload, at, EXIF_HEADER) && isTiff(at + EXIF_HEADER.length)) {\n return payload.slice(at + EXIF_HEADER.length)\n }\n }\n return undefined\n}\n\n/** The text of an XMP item, decoding a `deflate` content encoding. */\nfunction xmpFromItem(\n payload: Uint8Array,\n encoding: string | undefined,\n): string | undefined {\n let data: Uint8Array = payload\n if (encoding === 'deflate') {\n try {\n data = new Uint8Array(\n inflateSync(payload, { maxOutputLength: MAX_INFLATED }),\n )\n } catch {\n try {\n data = new Uint8Array(\n inflateRawSync(payload, { maxOutputLength: MAX_INFLATED }),\n )\n } catch {\n return undefined\n }\n }\n } else if (encoding) {\n return undefined\n }\n return trimNul(utf8Decode(data))\n}\n\n/** The item ids each `cdsc` reference says an item describes. */\nfunction describedBy(\n bytes: Uint8Array,\n iref: Box | undefined,\n): Map<number, number[]> {\n const out = new Map<number, number[]>()\n if (!iref) return out\n const wide = (bytes[iref.payload] ?? 0) === 1\n const idSize = wide ? 4 : 2\n const readId = (at: number) =>\n wide ? readU32BE(bytes, at) : readU16BE(bytes, at)\n for (const reference of readBoxes(bytes, iref.payload + 4, iref.end)) {\n if (reference.type !== 'cdsc') continue\n const from = readId(reference.payload)\n const count = readU16BE(bytes, reference.payload + idSize)\n const to: number[] = []\n for (let i = 0; i < count; i += 1) {\n const at = reference.payload + idSize + 2 + i * idSize\n if (at + idSize > reference.end) break\n to.push(readId(at))\n }\n out.set(from, to)\n }\n return out\n}\n\n/**\n * Reads the metadata blocks of a HEIC, HEIF or AVIF image. Returns `null`\n * when the bytes are not in the HEIF family (no `ftyp` with a HEIF brand);\n * a malformed file yields what could be read.\n */\nexport function readHeifBlocks(bytes: Uint8Array): ImageBlocks | null {\n const top = readBoxes(bytes, 0, bytes.length)\n const ftyp = top[0]\n if (!ftyp || ftyp.type !== 'ftyp') return null\n // The major brand, a minor version (skipped), then compatible brands.\n let heif = HEIF_BRANDS.has(readAscii(bytes, ftyp.payload, 4))\n for (let at = ftyp.payload + 8; at + 4 <= ftyp.end && !heif; at += 4) {\n heif = HEIF_BRANDS.has(readAscii(bytes, at, 4))\n }\n if (!heif) return null\n\n const blocks: ImageBlocks = {}\n const meta = top.find((box) => box.type === 'meta')\n if (!meta) return blocks\n const children = readBoxes(bytes, meta.payload + 4, meta.end)\n const child = (type: string) => children.find((box) => box.type === type)\n\n const iprp = child('iprp')\n const ipco =\n iprp &&\n readBoxes(bytes, iprp.payload, iprp.end).find((box) => box.type === 'ipco')\n for (const property of ipco ? readBoxes(bytes, ipco.payload, ipco.end) : []) {\n if (property.type !== 'colr' || property.payload + 4 > property.end)\n continue\n const colourType = readAscii(bytes, property.payload, 4)\n if (colourType === 'prof' || colourType === 'rICC') {\n blocks.icc = bytes.slice(property.payload + 4, property.end)\n break\n }\n }\n\n const iinf = child('iinf')\n const iloc = child('iloc')\n if (!iinf || !iloc) return blocks\n const infos = readItemInfos(bytes, iinf)\n const exifItems = infos.filter((item) => item.type === 'Exif')\n const xmpItems = infos.filter(\n (item) =>\n item.type === 'mime' &&\n item.contentType?.trim().toLowerCase() === 'application/rdf+xml',\n )\n if (!exifItems.length && !xmpItems.length) return blocks\n\n const pitm = child('pitm')\n const primary = pitm\n ? (bytes[pitm.payload] ?? 0) === 0\n ? readU16BE(bytes, pitm.payload + 4)\n : readU32BE(bytes, pitm.payload + 4)\n : undefined\n const references = describedBy(bytes, child('iref'))\n // The primary image's own metadata first, then the rest in file order.\n const describesPrimary = (item: ItemInfo) =>\n primary !== undefined && (references.get(item.id) ?? []).includes(primary)\n const rank = (items: ItemInfo[]) => [\n ...items.filter(describesPrimary),\n ...items.filter((item) => !describesPrimary(item)),\n ]\n\n const locations = readItemLocations(\n bytes,\n iloc,\n new Set([...exifItems, ...xmpItems].map((item) => item.id)),\n )\n for (const item of rank(exifItems)) {\n const payload = itemBytes(bytes, locations.get(item.id))\n const exif = payload && exifFromItem(payload)\n if (exif) {\n blocks.exif = exif\n break\n }\n }\n for (const item of rank(xmpItems)) {\n const payload = itemBytes(bytes, locations.get(item.id))\n const xmp = payload && xmpFromItem(payload, item.contentEncoding)\n if (xmp) {\n blocks.xmp = xmp\n break\n }\n }\n return blocks\n}\n"],"names":["inflateRawSync","inflateSync","asciiBytes","bytesAt","concatBytes","readAscii","readU16BE","readU32BE","trimNul","utf8Decode","HEIF_BRANDS","Set","EXIF_HEADER","TIFF_II","TIFF_MM","MAX_BOXES","MAX_ITEMS","MAX_INFLATED","readU64BE","bytes","offset","high","Number","NaN","readBoxes","start","end","boxes","limit","Math","min","length","size","header","push","type","payload","readSized","readCString","nul","subarray","indexOf","stop","text","next","readItemInfos","iinf","version","entries","items","infe","infeVersion","at","id","item","name","contentType","contentEncoding","readItemLocations","iloc","wanted","out","Map","sizes","sizes2","offsetSize","lengthSize","baseOffsetSize","indexSize","itemCount","i","constructionMethod","dataReferenceIndex","baseOffset","extentCount","extents","e","isNaN","has","set","itemBytes","location","parts","exifFromItem","isTiff","declared","slice","undefined","xmpFromItem","encoding","data","Uint8Array","maxOutputLength","describedBy","iref","wide","idSize","readId","reference","from","count","to","readHeifBlocks","top","ftyp","heif","blocks","meta","find","box","children","child","iprp","ipco","property","colourType","icc","infos","exifItems","filter","xmpItems","trim","toLowerCase","pitm","primary","references","describesPrimary","get","includes","rank","locations","map","exif","xmp"],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Aglyn LLC
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+ /**
18
+ * The profile description of an ICC profile, or `null` when the bytes are
19
+ * not a profile, carry no `desc` tag, or it holds no readable text. Never
20
+ * throws.
21
+ */
22
+ export declare function readIccDescription(icc: Uint8Array): string | null;
@@ -0,0 +1,85 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Aglyn LLC
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */ /**
17
+ * The one thing the DAM shows from an ICC profile: its description — "sRGB
18
+ * IEC61966-2.1", "Display P3" (AGL-3331).
19
+ *
20
+ * An ICC profile is a 128-byte header (the signature `acsp` at offset 36), a
21
+ * tag count at 128, then 12-byte tag table entries of signature, offset and
22
+ * size (ICC.1:2010, 7.3). The description is the `desc` tag, whose type
23
+ * depends on the profile's version:
24
+ *
25
+ * - v2: `textDescriptionType` (ICC.1:2001-04) — an ASCII string with its
26
+ * length, then an optional Unicode and ScriptCode copy.
27
+ * - v4: `multiLocalizedUnicodeType` (ICC.1:2010) — records of language,
28
+ * country, length and offset over UTF-16BE text. The first record is the
29
+ * profile's own choice of default.
30
+ * - Some writers use a plain `textType`; that is read as ASCII.
31
+ */ import { readAscii, readU32BE, trimNul, utf16beDecode } from "./image-blocks.js";
32
+ /** Tags read before giving up on finding `desc`. */ const MAX_TAGS = 1024;
33
+ /** A description longer than this is not a name; it is cut here. */ const MAX_DESCRIPTION = 512;
34
+ /** Keeps a decoded description to something a person can read. */ function clean(text) {
35
+ const trimmed = trimNul(text).replace(/\0[\s\S]*$/, '').trim();
36
+ return trimmed ? trimmed.slice(0, MAX_DESCRIPTION) : null;
37
+ }
38
+ /** A `textDescriptionType` body: ASCII first, then its Unicode copy. */ function textDescription(tag) {
39
+ const asciiCount = readU32BE(tag, 8);
40
+ const asciiEnd = 12 + asciiCount;
41
+ if (asciiCount > 0 && asciiEnd <= tag.length) {
42
+ const ascii = clean(readAscii(tag, 12, asciiCount));
43
+ if (ascii) return ascii;
44
+ }
45
+ // Unicode language code (4) and character count (4), then UTF-16BE.
46
+ if (asciiEnd + 8 > tag.length) return null;
47
+ const unicodeCount = readU32BE(tag, asciiEnd + 4);
48
+ const start = asciiEnd + 8;
49
+ const end = Math.min(tag.length, start + unicodeCount * 2);
50
+ return unicodeCount ? clean(utf16beDecode(tag.subarray(start, end))) : null;
51
+ }
52
+ /** A `multiLocalizedUnicodeType` body: its first record. */ function multiLocalized(tag) {
53
+ const records = readU32BE(tag, 8);
54
+ const recordSize = readU32BE(tag, 12);
55
+ if (!records || recordSize < 12 || 16 + recordSize > tag.length) return null;
56
+ const length = readU32BE(tag, 16 + 4);
57
+ const offset = readU32BE(tag, 16 + 8);
58
+ if (offset >= tag.length) return null;
59
+ return clean(utf16beDecode(tag.subarray(offset, Math.min(tag.length, offset + length))));
60
+ }
61
+ /**
62
+ * The profile description of an ICC profile, or `null` when the bytes are
63
+ * not a profile, carry no `desc` tag, or it holds no readable text. Never
64
+ * throws.
65
+ */ export function readIccDescription(icc) {
66
+ if (icc.length < 132 || readAscii(icc, 36, 4) !== 'acsp') return null;
67
+ const count = Math.min(readU32BE(icc, 128), MAX_TAGS);
68
+ for(let i = 0; i < count; i += 1){
69
+ const entry = 132 + i * 12;
70
+ if (entry + 12 > icc.length) return null;
71
+ if (readAscii(icc, entry, 4) !== 'desc') continue;
72
+ const offset = readU32BE(icc, entry + 4);
73
+ const size = readU32BE(icc, entry + 8);
74
+ if (offset < 128 || offset + 12 > icc.length) return null;
75
+ const tag = icc.subarray(offset, Math.min(icc.length, offset + size));
76
+ const type = readAscii(tag, 0, 4);
77
+ if (type === 'desc') return textDescription(tag);
78
+ if (type === 'mluc') return multiLocalized(tag);
79
+ if (type === 'text') return clean(readAscii(tag, 8, tag.length - 8));
80
+ return null;
81
+ }
82
+ return null;
83
+ }
84
+
85
+ //# sourceMappingURL=icc.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../../libs/aglyn/src/lib/app-utils/media-embedded-metadata/icc.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * The one thing the DAM shows from an ICC profile: its description — \"sRGB\n * IEC61966-2.1\", \"Display P3\" (AGL-3331).\n *\n * An ICC profile is a 128-byte header (the signature `acsp` at offset 36), a\n * tag count at 128, then 12-byte tag table entries of signature, offset and\n * size (ICC.1:2010, 7.3). The description is the `desc` tag, whose type\n * depends on the profile's version:\n *\n * - v2: `textDescriptionType` (ICC.1:2001-04) — an ASCII string with its\n * length, then an optional Unicode and ScriptCode copy.\n * - v4: `multiLocalizedUnicodeType` (ICC.1:2010) — records of language,\n * country, length and offset over UTF-16BE text. The first record is the\n * profile's own choice of default.\n * - Some writers use a plain `textType`; that is read as ASCII.\n */\n\nimport { readAscii, readU32BE, trimNul, utf16beDecode } from './image-blocks'\n\n/** Tags read before giving up on finding `desc`. */\nconst MAX_TAGS = 1024\n/** A description longer than this is not a name; it is cut here. */\nconst MAX_DESCRIPTION = 512\n\n/** Keeps a decoded description to something a person can read. */\nfunction clean(text: string): string | null {\n const trimmed = trimNul(text)\n .replace(/\\0[\\s\\S]*$/, '')\n .trim()\n return trimmed ? trimmed.slice(0, MAX_DESCRIPTION) : null\n}\n\n/** A `textDescriptionType` body: ASCII first, then its Unicode copy. */\nfunction textDescription(tag: Uint8Array): string | null {\n const asciiCount = readU32BE(tag, 8)\n const asciiEnd = 12 + asciiCount\n if (asciiCount > 0 && asciiEnd <= tag.length) {\n const ascii = clean(readAscii(tag, 12, asciiCount))\n if (ascii) return ascii\n }\n // Unicode language code (4) and character count (4), then UTF-16BE.\n if (asciiEnd + 8 > tag.length) return null\n const unicodeCount = readU32BE(tag, asciiEnd + 4)\n const start = asciiEnd + 8\n const end = Math.min(tag.length, start + unicodeCount * 2)\n return unicodeCount ? clean(utf16beDecode(tag.subarray(start, end))) : null\n}\n\n/** A `multiLocalizedUnicodeType` body: its first record. */\nfunction multiLocalized(tag: Uint8Array): string | null {\n const records = readU32BE(tag, 8)\n const recordSize = readU32BE(tag, 12)\n if (!records || recordSize < 12 || 16 + recordSize > tag.length) return null\n const length = readU32BE(tag, 16 + 4)\n const offset = readU32BE(tag, 16 + 8)\n if (offset >= tag.length) return null\n return clean(\n utf16beDecode(tag.subarray(offset, Math.min(tag.length, offset + length))),\n )\n}\n\n/**\n * The profile description of an ICC profile, or `null` when the bytes are\n * not a profile, carry no `desc` tag, or it holds no readable text. Never\n * throws.\n */\nexport function readIccDescription(icc: Uint8Array): string | null {\n if (icc.length < 132 || readAscii(icc, 36, 4) !== 'acsp') return null\n const count = Math.min(readU32BE(icc, 128), MAX_TAGS)\n for (let i = 0; i < count; i += 1) {\n const entry = 132 + i * 12\n if (entry + 12 > icc.length) return null\n if (readAscii(icc, entry, 4) !== 'desc') continue\n const offset = readU32BE(icc, entry + 4)\n const size = readU32BE(icc, entry + 8)\n if (offset < 128 || offset + 12 > icc.length) return null\n const tag = icc.subarray(offset, Math.min(icc.length, offset + size))\n const type = readAscii(tag, 0, 4)\n if (type === 'desc') return textDescription(tag)\n if (type === 'mluc') return multiLocalized(tag)\n if (type === 'text') return clean(readAscii(tag, 8, tag.length - 8))\n return null\n }\n return null\n}\n"],"names":["readAscii","readU32BE","trimNul","utf16beDecode","MAX_TAGS","MAX_DESCRIPTION","clean","text","trimmed","replace","trim","slice","textDescription","tag","asciiCount","asciiEnd","length","ascii","unicodeCount","start","end","Math","min","subarray","multiLocalized","records","recordSize","offset","readIccDescription","icc","count","i","entry","size","type"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED;;;;;;;;;;;;;;;CAeC,GAED,SAASA,SAAS,EAAEC,SAAS,EAAEC,OAAO,EAAEC,aAAa,QAAQ,oBAAgB;AAE7E,kDAAkD,GAClD,MAAMC,WAAW;AACjB,kEAAkE,GAClE,MAAMC,kBAAkB;AAExB,gEAAgE,GAChE,SAASC,MAAMC,IAAY;IACzB,MAAMC,UAAUN,QAAQK,MACrBE,OAAO,CAAC,cAAc,IACtBC,IAAI;IACP,OAAOF,UAAUA,QAAQG,KAAK,CAAC,GAAGN,mBAAmB;AACvD;AAEA,sEAAsE,GACtE,SAASO,gBAAgBC,GAAe;IACtC,MAAMC,aAAab,UAAUY,KAAK;IAClC,MAAME,WAAW,KAAKD;IACtB,IAAIA,aAAa,KAAKC,YAAYF,IAAIG,MAAM,EAAE;QAC5C,MAAMC,QAAQX,MAAMN,UAAUa,KAAK,IAAIC;QACvC,IAAIG,OAAO,OAAOA;IACpB;IACA,oEAAoE;IACpE,IAAIF,WAAW,IAAIF,IAAIG,MAAM,EAAE,OAAO;IACtC,MAAME,eAAejB,UAAUY,KAAKE,WAAW;IAC/C,MAAMI,QAAQJ,WAAW;IACzB,MAAMK,MAAMC,KAAKC,GAAG,CAACT,IAAIG,MAAM,EAAEG,QAAQD,eAAe;IACxD,OAAOA,eAAeZ,MAAMH,cAAcU,IAAIU,QAAQ,CAACJ,OAAOC,SAAS;AACzE;AAEA,0DAA0D,GAC1D,SAASI,eAAeX,GAAe;IACrC,MAAMY,UAAUxB,UAAUY,KAAK;IAC/B,MAAMa,aAAazB,UAAUY,KAAK;IAClC,IAAI,CAACY,WAAWC,aAAa,MAAM,KAAKA,aAAab,IAAIG,MAAM,EAAE,OAAO;IACxE,MAAMA,SAASf,UAAUY,KAAK,KAAK;IACnC,MAAMc,SAAS1B,UAAUY,KAAK,KAAK;IACnC,IAAIc,UAAUd,IAAIG,MAAM,EAAE,OAAO;IACjC,OAAOV,MACLH,cAAcU,IAAIU,QAAQ,CAACI,QAAQN,KAAKC,GAAG,CAACT,IAAIG,MAAM,EAAEW,SAASX;AAErE;AAEA;;;;CAIC,GACD,OAAO,SAASY,mBAAmBC,GAAe;IAChD,IAAIA,IAAIb,MAAM,GAAG,OAAOhB,UAAU6B,KAAK,IAAI,OAAO,QAAQ,OAAO;IACjE,MAAMC,QAAQT,KAAKC,GAAG,CAACrB,UAAU4B,KAAK,MAAMzB;IAC5C,IAAK,IAAI2B,IAAI,GAAGA,IAAID,OAAOC,KAAK,EAAG;QACjC,MAAMC,QAAQ,MAAMD,IAAI;QACxB,IAAIC,QAAQ,KAAKH,IAAIb,MAAM,EAAE,OAAO;QACpC,IAAIhB,UAAU6B,KAAKG,OAAO,OAAO,QAAQ;QACzC,MAAML,SAAS1B,UAAU4B,KAAKG,QAAQ;QACtC,MAAMC,OAAOhC,UAAU4B,KAAKG,QAAQ;QACpC,IAAIL,SAAS,OAAOA,SAAS,KAAKE,IAAIb,MAAM,EAAE,OAAO;QACrD,MAAMH,MAAMgB,IAAIN,QAAQ,CAACI,QAAQN,KAAKC,GAAG,CAACO,IAAIb,MAAM,EAAEW,SAASM;QAC/D,MAAMC,OAAOlC,UAAUa,KAAK,GAAG;QAC/B,IAAIqB,SAAS,QAAQ,OAAOtB,gBAAgBC;QAC5C,IAAIqB,SAAS,QAAQ,OAAOV,eAAeX;QAC3C,IAAIqB,SAAS,QAAQ,OAAO5B,MAAMN,UAAUa,KAAK,GAAGA,IAAIG,MAAM,GAAG;QACjE,OAAO;IACT;IACA,OAAO;AACT"}