@takazudo/zudo-image-tweaker 0.1.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 (44) hide show
  1. package/CHANGELOG.md +1 -0
  2. package/LICENSE +21 -0
  3. package/README.md +57 -0
  4. package/dist/blurhash/index.d.ts +35 -0
  5. package/dist/blurhash/index.js +40 -0
  6. package/dist/browser/exif-orientation.d.ts +12 -0
  7. package/dist/browser/index.d.ts +7 -0
  8. package/dist/browser/index.js +160 -0
  9. package/dist/browser/orientation-semantics.d.ts +37 -0
  10. package/dist/browser/orientation.d.ts +25 -0
  11. package/dist/browser/prepare-upload.d.ts +100 -0
  12. package/dist/budget/index.d.ts +73 -0
  13. package/dist/budget/index.js +157 -0
  14. package/dist/calibrate/index.d.ts +129 -0
  15. package/dist/calibrate/index.js +206 -0
  16. package/dist/composite/index.d.ts +50 -0
  17. package/dist/composite/index.js +56 -0
  18. package/dist/exif/index.d.ts +41 -0
  19. package/dist/exif/index.js +100 -0
  20. package/dist/heif/index.d.ts +139 -0
  21. package/dist/heif/index.js +275 -0
  22. package/dist/index.d.ts +14 -0
  23. package/dist/index.js +68 -0
  24. package/dist/ogp/index.d.ts +72 -0
  25. package/dist/ogp/index.js +117 -0
  26. package/dist/presets-PTxNoMdy.js +630 -0
  27. package/dist/product-photo/index.d.ts +50 -0
  28. package/dist/product-photo/index.js +189 -0
  29. package/dist/product-photo/shadow.d.ts +48 -0
  30. package/dist/run-BIjGA2Fu.js +22 -0
  31. package/dist/square/index.d.ts +98 -0
  32. package/dist/square/index.js +89 -0
  33. package/dist/variants/engine.d.ts +38 -0
  34. package/dist/variants/feature-detect.d.ts +8 -0
  35. package/dist/variants/hash-cache.d.ts +63 -0
  36. package/dist/variants/heic.d.ts +15 -0
  37. package/dist/variants/index.d.ts +5 -0
  38. package/dist/variants/index.js +19 -0
  39. package/dist/variants/presets.d.ts +18 -0
  40. package/dist/variants/repair.d.ts +28 -0
  41. package/dist/variants/run.d.ts +36 -0
  42. package/dist/variants/tags.d.ts +13 -0
  43. package/dist/variants/types.d.ts +208 -0
  44. package/package.json +126 -0
@@ -0,0 +1,100 @@
1
+ import x from "sharp";
2
+ const y = /(\d{4}):(\d{2}):(\d{2}) (\d{2}):(\d{2}):(\d{2})/, N = 306, p = 34665, D = 36867, F = 36868, h = 2, U = 12, B = 64, M = 64;
3
+ function C(n) {
4
+ const r = n.match(y);
5
+ if (!r) return null;
6
+ const [, t, d, s, c, T, E] = r, o = /* @__PURE__ */ new Date(`${t}-${d}-${s}T${c}:${T}:${E}Z`);
7
+ return Number.isNaN(o.getTime()) ? null : o.getUTCFullYear() === Number(t) && o.getUTCMonth() === Number(d) - 1 && o.getUTCDate() === Number(s) && o.getUTCHours() === Number(c) && o.getUTCMinutes() === Number(T) && o.getUTCSeconds() === Number(E) ? o : null;
8
+ }
9
+ function R(n) {
10
+ const r = Math.min(n.length - 8, B);
11
+ for (let t = 0; t <= r; t++) {
12
+ if (n[t] === 73 && n[t + 1] === 73 && n[t + 2] === 42 && n[t + 3] === 0)
13
+ return { base: t, littleEndian: !0 };
14
+ if (n[t] === 77 && n[t + 1] === 77 && n[t + 2] === 0 && n[t + 3] === 42)
15
+ return { base: t, littleEndian: !1 };
16
+ }
17
+ return null;
18
+ }
19
+ function S(n) {
20
+ const r = R(n);
21
+ if (!r) return null;
22
+ const { base: t, littleEndian: d } = r, s = (e) => e >= 0 && e + 2 <= n.length ? d ? n.readUInt16LE(e) : n.readUInt16BE(e) : null, c = (e) => e >= 0 && e + 4 <= n.length ? d ? n.readUInt32LE(e) : n.readUInt32BE(e) : null, T = (e, u) => {
23
+ const i = s(e);
24
+ if (i === null || e + 2 + i * U > n.length) return null;
25
+ const l = /* @__PURE__ */ new Map();
26
+ for (let a = 0; a < i; a++) {
27
+ const A = e + 2 + a * U, m = s(A);
28
+ m !== null && u.includes(m) && l.set(m, A);
29
+ }
30
+ return l;
31
+ }, E = (e) => {
32
+ const u = s(e + 2), i = c(e + 4);
33
+ if (u !== h || i === null || i < 1 || i > M)
34
+ return null;
35
+ let l;
36
+ if (i <= 4)
37
+ l = e + 8;
38
+ else {
39
+ const a = c(e + 8);
40
+ if (a === null) return null;
41
+ l = t + a;
42
+ }
43
+ return l + i > n.length ? null : n.toString("latin1", l, l + i);
44
+ }, o = c(t + 4);
45
+ if (o === null) return null;
46
+ const f = T(t + o, [N, p]);
47
+ if (f === null) return null;
48
+ const I = [], g = f.get(p);
49
+ if (g !== void 0) {
50
+ const e = c(g + 8);
51
+ if (e !== null) {
52
+ const u = T(t + e, [D, F]);
53
+ if (u !== null)
54
+ for (const i of [D, F]) {
55
+ const l = u.get(i);
56
+ l !== void 0 && I.push(l);
57
+ }
58
+ }
59
+ }
60
+ const _ = f.get(N);
61
+ _ !== void 0 && I.push(_);
62
+ for (const e of I) {
63
+ const u = E(e);
64
+ if (u === null) continue;
65
+ const i = C(u);
66
+ if (i) return i;
67
+ }
68
+ return null;
69
+ }
70
+ function v(n) {
71
+ if (!n || n.length === 0) return null;
72
+ const r = Buffer.isBuffer(n) ? n : Buffer.from(n);
73
+ try {
74
+ const t = S(r);
75
+ if (t) return t;
76
+ } catch {
77
+ }
78
+ return C(r.toString("latin1"));
79
+ }
80
+ async function G(n) {
81
+ return x(n).rotate().keepIccProfile().toBuffer();
82
+ }
83
+ async function P(n) {
84
+ return x(n).toBuffer();
85
+ }
86
+ function H(n, r) {
87
+ if (!Number.isFinite(n) || !Number.isFinite(r) || n <= 0 || r <= 0)
88
+ throw new Error(`deriveOrientation: invalid dimensions width=${n} height=${r}`);
89
+ return n > r ? "landscape" : n < r ? "portrait" : "square";
90
+ }
91
+ function O(n, r) {
92
+ return !Number.isFinite(n) || n <= 0 ? [] : r.filter((t) => t <= n);
93
+ }
94
+ export {
95
+ G as bakeOrientation,
96
+ H as deriveOrientation,
97
+ v as parseExifDate,
98
+ O as pickVariantWidths,
99
+ P as stripExif
100
+ };
@@ -0,0 +1,139 @@
1
+ /**
2
+ * Node-native HEIC/HEIF -> JPEG conversion, with a dependency-free ISOBMFF
3
+ * ICC-profile extractor.
4
+ *
5
+ * TRUSTED-INPUT ONLY: the Node/WASM fallback decoder (`heic-decode` ->
6
+ * bundled `libheif-js` 1.19.8) predates the libheif 1.22.0 fixes for
7
+ * CVE-2026-32740 (heap overflow) and CVE-2026-32739 (infinite-loop DoS).
8
+ * Only decode HEIC/HEIF files from sources you trust. The decoder runs
9
+ * inside a WASM sandbox and `maxInputBytes` rejects oversized inputs
10
+ * before they reach it, but both are defense-in-depth, not a substitute
11
+ * for trusting the source. Bump `heic-decode` once its bundled
12
+ * `libheif-js` reaches >= 1.22.0.
13
+ *
14
+ * Why the box parser: `libheif-js` 1.19.8's high-level ICC API
15
+ * (`heif_image_handle_get_color_profile_type` et al.) is broken and
16
+ * reports "not present" even when a profile exists, so the ICC profile is
17
+ * pulled directly out of the container's meta/iprp/ipco/ipma boxes
18
+ * instead. It is then spliced into the output JPEG as a raw APP2 marker
19
+ * (see `embedIccProfileInJpeg`) rather than handed to sharp's
20
+ * `withIccProfile()`, which performs a colour-managed transform from the
21
+ * (assumed sRGB) input to the target profile -- since the decoded pixels
22
+ * are already in the space the extracted profile describes, that would
23
+ * alter already-correct sample values instead of just tagging them.
24
+ *
25
+ * Why the sips/Node split exists at all: system libheif (what macOS
26
+ * `sips` and sharp's bundled libvips use) can predate 1.18.0 and
27
+ * hardcodes a strict security limit on auxiliary-image references, which
28
+ * rejects the HDR "gain map" (`tmap` compatible-brand) HEIC files emitted
29
+ * by recent iPhone/Android cameras ("Too many auxiliary image
30
+ * references"). `sips` is tried first on macOS for speed with no extra
31
+ * deps; the Node fallback's bundled WASM `libheif-js` doesn't carry that
32
+ * limit, so it also serves as the fallback whenever `sips` is unavailable
33
+ * OR fails on a given input (including that documented limitation).
34
+ * `ConvertHeifResult.converter` reports which path produced the output.
35
+ *
36
+ * Residual Node-fallback divergence from `sips`: files whose wide-gamut
37
+ * colour is described only by an `nclx` colr box (no embedded ICC
38
+ * profile) lose that colour description on the Node path — the output
39
+ * JPEG carries no ICC profile and decodes as sRGB. `sips` converts such
40
+ * files through the system colour engine and tags the output itself.
41
+ * Source EXIF, previously also lost on this path, is now copied into the
42
+ * output (see `extractExifFromHeif`), with the EXIF Orientation tag
43
+ * neutralised to 1 because the WASM decoder already applies the
44
+ * container's irot/imir transforms to the pixels.
45
+ */
46
+ export interface ConvertHeifToJpegOptions {
47
+ /** JPEG encode quality, 1-100. @default 90 */
48
+ quality?: number;
49
+ /** Reject inputs larger than this many bytes before decoding. @default 268435456 (256 MiB) */
50
+ maxInputBytes?: number;
51
+ /**
52
+ * Node path only: reject inputs whose container declares more pixels
53
+ * than this before the WASM decode. @default 268402689 (sharp's default
54
+ * limitInputPixels, 0x3FFF x 0x3FFF)
55
+ */
56
+ maxDecodePixels?: number;
57
+ /**
58
+ * `sips` path only: kill the `sips` subprocess if it hasn't exited after
59
+ * this many ms, falling back to the Node/WASM path. @default 60000
60
+ */
61
+ sipsTimeoutMs?: number;
62
+ }
63
+ export interface ConvertHeifResult {
64
+ buffer: Buffer;
65
+ width: number;
66
+ height: number;
67
+ iccApplied: boolean;
68
+ /**
69
+ * Which conversion path produced the output: macOS `sips`, or the
70
+ * Node/WASM fallback. The fallback diverges from `sips` for nclx-only
71
+ * wide-gamut files (see the module doc comment), so callers that care
72
+ * can detect it here.
73
+ */
74
+ converter: 'sips' | 'node';
75
+ }
76
+ /**
77
+ * Convert a HEIF/HEIC source to JPEG.
78
+ *
79
+ * Primary path: macOS `sips`, tried only when `input` is a file path —
80
+ * feature-detected via `execFile`'s ENOENT (never a shell string, so
81
+ * paths containing spaces or shell metacharacters pass through
82
+ * unmangled). Fallback (non-macOS, `sips` missing, `sips` fails for any
83
+ * reason -- including the documented auxiliary-image-reference limit on
84
+ * HDR gain-map files -- or `input` is a Buffer): {@link
85
+ * convertHeifToJpegNode}. If both paths fail, the thrown error includes
86
+ * both underlying failures.
87
+ */
88
+ export declare function convertHeifToJpeg(input: string | Buffer, opts?: ConvertHeifToJpegOptions): Promise<ConvertHeifResult>;
89
+ /**
90
+ * Node-native HEIF/HEIC -> JPEG conversion: decode via `heic-decode`
91
+ * (`libheif-js` WASM), extract the source ICC profile via
92
+ * {@link extractIccFromHeif}, and encode via sharp.
93
+ *
94
+ * `heic-decode` is imported lazily so its WASM decoder only loads on this
95
+ * fallback path, not on every `sips`-capable macOS call.
96
+ *
97
+ * Known limitation: `heic-decode`'s default export decodes the first
98
+ * top-level image in the container, not necessarily the item marked
99
+ * primary in the `pitm` box (which is what {@link extractIccFromHeif}
100
+ * targets). This is a non-issue for single-image consumer photos,
101
+ * including HDR gain-map ("tmap") files, whose gain map is an auxiliary
102
+ * image reference on the same item rather than a separate top-level
103
+ * image — but a multi-image container whose primary item isn't first
104
+ * would decode the wrong image while attaching the correct image's ICC
105
+ * profile to it.
106
+ */
107
+ export declare function convertHeifToJpegNode(input: string | Buffer, opts?: ConvertHeifToJpegOptions): Promise<ConvertHeifResult>;
108
+ /**
109
+ * Extract the EXIF metadata of a HEIF file as a TIFF-format block (the
110
+ * payload of a JPEG APP1 `Exif\0\0` segment), by finding the `Exif` item
111
+ * in meta/iinf and resolving its bytes through meta/iloc.
112
+ *
113
+ * The EXIF Orientation tag (IFD0 0x0112) in the returned block is
114
+ * neutralised to 1: the WASM decoder already applies the container's
115
+ * irot/imir transforms to the pixels, so carrying the source orientation
116
+ * forward would double-rotate in EXIF-aware viewers.
117
+ *
118
+ * Returns null when the file has no EXIF item or on any
119
+ * malformed/truncated box layout — same degrade-gracefully contract as
120
+ * {@link extractIccFromHeif}.
121
+ */
122
+ export declare function extractExifFromHeif(b: Buffer): Buffer | null;
123
+ /**
124
+ * Extract the ICC profile associated with a HEIF file's primary item, by
125
+ * walking meta/pitm (primary item id) and meta/iprp/{ipco,ipma} (item
126
+ * properties + their associations) to find the primary item's `colr` box.
127
+ *
128
+ * Returns the ICC profile bytes when the primary item has a `colr` box of
129
+ * type `prof`/`rICC`, or null when it doesn't (e.g. nclx-only files, or
130
+ * any structurally unexpected/malformed box layout). Never throws: any
131
+ * out-of-bounds read on a malformed/truncated box layout is caught and
132
+ * surfaces as "no ICC available" instead of an uncaught RangeError.
133
+ *
134
+ * Known limitation: only the first `ipma` box under `iprp` is consulted
135
+ * (ISOBMFF technically permits more than one). Every real-world file this
136
+ * was validated against — including camera-generated HDR gain-map files
137
+ * — carries a single ipma box, so this hasn't been a problem in practice.
138
+ */
139
+ export declare function extractIccFromHeif(b: Buffer): Buffer | null;
@@ -0,0 +1,275 @@
1
+ import { randomUUID as O } from "node:crypto";
2
+ import S from "node:fs/promises";
3
+ import k from "node:os";
4
+ import v from "node:path";
5
+ import P from "sharp";
6
+ import { r as $, a as z, i as H } from "../run-BIjGA2Fu.js";
7
+ const T = 90, D = 256 * 1024 * 1024, N = 6e4, F = 16383 * 16383;
8
+ async function J(t) {
9
+ return typeof t == "string" ? (await S.stat(t)).size : t.length;
10
+ }
11
+ async function L(t, r) {
12
+ const a = await J(t);
13
+ if (a > r)
14
+ throw new Error(`HEIF input of ${a} bytes exceeds maxInputBytes (${r})`);
15
+ }
16
+ async function at(t, r = {}) {
17
+ const a = r.quality ?? T, n = r.maxInputBytes ?? D, o = r.maxDecodePixels ?? F, s = r.sipsTimeoutMs ?? N;
18
+ await L(t, n);
19
+ let e = null;
20
+ if (typeof t == "string") {
21
+ const i = await R(t, a, s);
22
+ if (i.result) return i.result;
23
+ e = i.error;
24
+ }
25
+ try {
26
+ return await X(t, { quality: a, maxInputBytes: n, maxDecodePixels: o });
27
+ } catch (i) {
28
+ const d = [typeof t == "string" ? e ? `'sips' failed (${e.message})` : "'sips' is unavailable (non-macOS)" : null, `the Node fallback also failed: ${i.message}`].filter(Boolean).join(" and ");
29
+ throw new Error(`HEIF conversion failed: ${d}.`);
30
+ }
31
+ }
32
+ async function R(t, r, a) {
33
+ const n = v.join(k.tmpdir(), `heif-sips-${process.pid}-${O()}.jpg`);
34
+ try {
35
+ await $(
36
+ "sips",
37
+ [
38
+ "-s",
39
+ "format",
40
+ "jpeg",
41
+ "-s",
42
+ "formatOptions",
43
+ String(r),
44
+ z(t),
45
+ "--out",
46
+ n
47
+ ],
48
+ { timeoutMs: a }
49
+ );
50
+ } catch (o) {
51
+ return await S.rm(n, { force: !0 }), H(o) ? { result: null, error: null } : { result: null, error: o };
52
+ }
53
+ try {
54
+ const o = await S.readFile(n), s = await P(o).metadata();
55
+ return {
56
+ result: {
57
+ buffer: o,
58
+ width: s.width ?? 0,
59
+ height: s.height ?? 0,
60
+ iccApplied: !!s.icc,
61
+ converter: "sips"
62
+ },
63
+ error: null
64
+ };
65
+ } finally {
66
+ await S.rm(n, { force: !0 });
67
+ }
68
+ }
69
+ async function X(t, r = {}) {
70
+ const a = r.quality ?? T, n = r.maxInputBytes ?? D, o = r.maxDecodePixels ?? F;
71
+ await L(t, n);
72
+ const s = typeof t == "string" ? await S.readFile(t) : t;
73
+ V(s, o);
74
+ const { default: e } = await import("heic-decode"), { width: i, height: l, data: d } = await e({ buffer: s }), y = Z(s), h = y !== null && y.length <= q ? y : null, c = G(s);
75
+ let f = await P(Buffer.from(d.buffer, d.byteOffset, d.byteLength), {
76
+ raw: { width: i, height: l, channels: 4 }
77
+ }).removeAlpha().jpeg({ quality: a }).toBuffer();
78
+ return h && (f = Y(f, h)), c && (f = W(f, c)), {
79
+ buffer: f,
80
+ width: i,
81
+ height: l,
82
+ iccApplied: h !== null,
83
+ converter: "node"
84
+ };
85
+ }
86
+ function V(t, r) {
87
+ let a = null;
88
+ try {
89
+ const n = E(t, 0, t.length, "meta");
90
+ if (!n) return;
91
+ const o = E(t, n.bodyStart + 4, n.bodyEnd, "iprp");
92
+ if (!o) return;
93
+ const s = E(t, o.bodyStart, o.bodyEnd, "ipco");
94
+ if (!s) return;
95
+ for (const e of b(t, s.bodyStart, s.bodyEnd)) {
96
+ if (e.type !== "ispe" || e.bodyEnd - e.bodyStart < 12) continue;
97
+ const i = t.readUInt32BE(e.bodyStart + 4), l = t.readUInt32BE(e.bodyStart + 8);
98
+ if (i * l > r) {
99
+ a = { width: i, height: l };
100
+ break;
101
+ }
102
+ }
103
+ } catch {
104
+ return;
105
+ }
106
+ if (a)
107
+ throw new Error(
108
+ `HEIF declares an image of ${a.width}x${a.height} pixels, exceeding the ${r}-pixel decode limit (maxDecodePixels)`
109
+ );
110
+ }
111
+ const U = Buffer.from("ICC_PROFILE\0", "latin1"), x = 65533 - U.length - 1 - 1, q = 255 * x;
112
+ function Y(t, r) {
113
+ if (t.length < 2 || t[0] !== 255 || t[1] !== 216)
114
+ throw new Error("Expected a JPEG buffer starting with the SOI marker");
115
+ const a = Math.max(1, Math.ceil(r.length / x)), n = [];
116
+ for (let o = 0; o < a; o++) {
117
+ const s = r.subarray(o * x, (o + 1) * x), e = 2 + U.length + 1 + 1 + s.length, i = Buffer.alloc(4 + U.length + 2);
118
+ i.writeUInt8(255, 0), i.writeUInt8(226, 1), i.writeUInt16BE(e, 2), U.copy(i, 4), i.writeUInt8(o + 1, 4 + U.length), i.writeUInt8(a, 4 + U.length + 1), n.push(i, s);
119
+ }
120
+ return Buffer.concat([t.subarray(0, 2), ...n, t.subarray(2)]);
121
+ }
122
+ const C = Buffer.from("Exif\0\0", "latin1");
123
+ function W(t, r) {
124
+ if (t.length < 2 || t[0] !== 255 || t[1] !== 216)
125
+ throw new Error("Expected a JPEG buffer starting with the SOI marker");
126
+ const a = 2 + C.length + r.length;
127
+ if (a > 65535) return t;
128
+ const n = Buffer.alloc(4 + C.length);
129
+ return n.writeUInt8(255, 0), n.writeUInt8(225, 1), n.writeUInt16BE(a, 2), C.copy(n, 4), Buffer.concat([t.subarray(0, 2), n, r, t.subarray(2)]);
130
+ }
131
+ function G(t) {
132
+ try {
133
+ const r = E(t, 0, t.length, "meta");
134
+ if (!r) return null;
135
+ const a = r.bodyStart + 4, n = K(t, a, r.bodyEnd);
136
+ if (n === null) return null;
137
+ const o = Q(t, a, r.bodyEnd, n);
138
+ if (!o || o.length < 12 || o.offset + o.length > t.length) return null;
139
+ const s = t.subarray(o.offset, o.offset + o.length), e = 4 + s.readUInt32BE(0);
140
+ if (e >= s.length - 8) return null;
141
+ const i = s.toString("latin1", e, e + 2);
142
+ return i !== "II" && i !== "MM" ? null : j(s.subarray(e));
143
+ } catch {
144
+ return null;
145
+ }
146
+ }
147
+ function K(t, r, a) {
148
+ const n = E(t, r, a, "iinf");
149
+ if (!n) return null;
150
+ const o = t.readUInt8(n.bodyStart), s = n.bodyStart + 4 + (o === 0 ? 2 : 4);
151
+ for (const e of b(t, s, n.bodyEnd)) {
152
+ if (e.type !== "infe") continue;
153
+ const i = t.readUInt8(e.bodyStart);
154
+ if (i < 2) continue;
155
+ const l = i === 2 ? 2 : 4, d = i === 2 ? t.readUInt16BE(e.bodyStart + 4) : t.readUInt32BE(e.bodyStart + 4), y = e.bodyStart + 4 + l + 2;
156
+ if (t.toString("latin1", y, y + 4) === "Exif") return d;
157
+ }
158
+ return null;
159
+ }
160
+ function Q(t, r, a, n) {
161
+ const o = E(t, r, a, "iloc");
162
+ if (!o) return null;
163
+ const s = t.readUInt8(o.bodyStart);
164
+ let e = o.bodyStart + 4;
165
+ const i = t.readUInt16BE(e);
166
+ e += 2;
167
+ const l = i >> 12 & 15, d = i >> 8 & 15, y = i >> 4 & 15, h = s >= 1 ? i & 15 : 0, c = (f) => {
168
+ let u;
169
+ return f === 0 ? u = 0 : f === 8 ? u = Number(t.readBigUInt64BE(e)) : u = t.readUIntBE(e, f), e += f, u;
170
+ };
171
+ let m;
172
+ s < 2 ? (m = t.readUInt16BE(e), e += 2) : (m = t.readUInt32BE(e), e += 4);
173
+ for (let f = 0; f < m; f++) {
174
+ let u;
175
+ s < 2 ? (u = t.readUInt16BE(e), e += 2) : (u = t.readUInt32BE(e), e += 4);
176
+ let I = 0;
177
+ s >= 1 && (I = t.readUInt16BE(e) & 15, e += 2);
178
+ const p = t.readUInt16BE(e);
179
+ e += 2;
180
+ const w = c(y), g = t.readUInt16BE(e);
181
+ e += 2;
182
+ let B = null;
183
+ for (let _ = 0; _ < g; _++) {
184
+ h > 0 && c(h);
185
+ const A = c(l), M = c(d);
186
+ _ === 0 && (B = { offset: w + A, length: M });
187
+ }
188
+ if (u === n)
189
+ return I !== 0 || p !== 0 || g !== 1 ? null : B;
190
+ }
191
+ return null;
192
+ }
193
+ function j(t) {
194
+ const r = Buffer.from(t);
195
+ try {
196
+ const a = r.toString("latin1", 0, 2) === "II", n = (i) => a ? r.readUInt16LE(i) : r.readUInt16BE(i), s = ((i) => a ? r.readUInt32LE(i) : r.readUInt32BE(i))(4), e = n(s);
197
+ for (let i = 0; i < e; i++) {
198
+ const l = s + 2 + i * 12;
199
+ if (n(l) === 274) {
200
+ a ? r.writeUInt16LE(1, l + 8) : r.writeUInt16BE(1, l + 8);
201
+ break;
202
+ }
203
+ }
204
+ } catch {
205
+ }
206
+ return r;
207
+ }
208
+ function* b(t, r, a) {
209
+ let n = r;
210
+ for (; n + 8 <= a; ) {
211
+ let o = t.readUInt32BE(n);
212
+ const s = t.toString("latin1", n + 4, n + 8);
213
+ let e = 8;
214
+ if (o === 1) {
215
+ if (n + 16 > a) break;
216
+ o = Number(t.readBigUInt64BE(n + 8)), e = 16;
217
+ } else o === 0 && (o = a - n);
218
+ if (o < e || n + o > a) break;
219
+ yield { type: s, start: n, header: e, size: o, bodyStart: n + e, bodyEnd: n + o }, n += o;
220
+ }
221
+ }
222
+ function E(t, r, a, n) {
223
+ for (const o of b(t, r, a))
224
+ if (o.type === n) return o;
225
+ return null;
226
+ }
227
+ function Z(t) {
228
+ try {
229
+ const r = E(t, 0, t.length, "meta");
230
+ if (!r) return null;
231
+ const a = r.bodyStart + 4, n = E(t, a, r.bodyEnd, "pitm");
232
+ if (!n) return null;
233
+ const s = t.readUInt8(n.bodyStart) === 0 ? t.readUInt16BE(n.bodyStart + 4) : t.readUInt32BE(n.bodyStart + 4), e = E(t, a, r.bodyEnd, "iprp");
234
+ if (!e) return null;
235
+ const i = E(t, e.bodyStart, e.bodyEnd, "ipco");
236
+ if (!i) return null;
237
+ const l = [...b(t, i.bodyStart, i.bodyEnd)], d = E(t, e.bodyStart, e.bodyEnd, "ipma");
238
+ if (!d) return null;
239
+ const y = t.readUInt8(d.bodyStart), h = t.readUIntBE(d.bodyStart + 1, 3);
240
+ let c = d.bodyStart + 4;
241
+ const m = t.readUInt32BE(c);
242
+ c += 4;
243
+ let f = null;
244
+ for (let u = 0; u < m; u++) {
245
+ const I = y < 1 ? t.readUInt16BE(c) : t.readUInt32BE(c);
246
+ c += y < 1 ? 2 : 4;
247
+ const p = t.readUInt8(c);
248
+ c += 1;
249
+ const w = [];
250
+ for (let g = 0; g < p; g++) {
251
+ let B;
252
+ h & 1 ? (B = t.readUInt16BE(c) & 32767, c += 2) : (B = t.readUInt8(c) & 127, c += 1), w.push(B);
253
+ }
254
+ I === s && (f = w);
255
+ }
256
+ if (!f) return null;
257
+ for (const u of f) {
258
+ const I = l[u - 1];
259
+ if (I && I.type === "colr") {
260
+ const p = t.toString("latin1", I.bodyStart, I.bodyStart + 4);
261
+ if (p === "prof" || p === "rICC")
262
+ return t.subarray(I.bodyStart + 4, I.bodyEnd);
263
+ }
264
+ }
265
+ return null;
266
+ } catch {
267
+ return null;
268
+ }
269
+ }
270
+ export {
271
+ at as convertHeifToJpeg,
272
+ X as convertHeifToJpegNode,
273
+ G as extractExifFromHeif,
274
+ Z as extractIccFromHeif
275
+ };
@@ -0,0 +1,14 @@
1
+ export * from './variants/index.js';
2
+ export * from './heif/index.js';
3
+ export * from './ogp/index.js';
4
+ export * from './budget/index.js';
5
+ export * from './square/index.js';
6
+ export * from './product-photo/index.js';
7
+ export * from './calibrate/index.js';
8
+ export * from './composite/index.js';
9
+ export * from './blurhash/index.js';
10
+ export * from './exif/index.js';
11
+ export * from './browser/index.js';
12
+ export type { ImageInput } from './composite/index.js';
13
+ export type { Orientation } from './exif/index.js';
14
+ export { deriveOrientation } from './exif/index.js';
package/dist/index.js ADDED
@@ -0,0 +1,68 @@
1
+ import { D as a, a as o, b as t, c as s, d as E, e as p, I as i, P as n, S as A, V as m, f as T, n as S, p as I, g as _, h as c, s as d } from "./presets-PTxNoMdy.js";
2
+ import { convertHeifToJpeg as f, convertHeifToJpegNode as O, extractExifFromHeif as F, extractIccFromHeif as D } from "./heif/index.js";
3
+ import { generateOgpFromLandscape as N, generateOgpImage as h, generateSmartOgp as u } from "./ogp/index.js";
4
+ import { encodeUnderByteBudget as L } from "./budget/index.js";
5
+ import { cropToSquare as P, insetOnSquare as C, padToSquare as M, padToSquareCentered as G, trimPadSquare as R } from "./square/index.js";
6
+ import { alphaTrim as H, composeProductPhoto as V, generateShadowLayers as k, removeBackground as v } from "./product-photo/index.js";
7
+ import { calibrateTargetFromSamples as b, normalizeBackgroundColor as q, sampleBackgroundColor as y } from "./calibrate/index.js";
8
+ import { compositeBatch as Y, compositeOverlay as J } from "./composite/index.js";
9
+ import { batchBlurhashToDataUri as z, blurhashToDataUri as K, encodeImageToBlurhash as Q } from "./blurhash/index.js";
10
+ import { bakeOrientation as Z, deriveOrientation as $, parseExifDate as ee, pickVariantWidths as re, stripExif as ae } from "./exif/index.js";
11
+ import { DEFAULT_MAX_CANVAS_AREA as te, HEIC_DECODE_FAILED_MESSAGE as se, MISSING_EXIFR_MESSAGE as Ee, MISSING_HEIC2ANY_MESSAGE as pe, ORIENTATION_BAKE_FAILED_MESSAGE_PREFIX as ie, deriveGeometry as ne, needsOrientationBake as Ae, prepareImageForUpload as me, roundAspectRatio as Te } from "./browser/index.js";
12
+ export {
13
+ a as DEFAULT_CACHE_FILENAME,
14
+ o as DEFAULT_CONCURRENCY,
15
+ t as DEFAULT_FORMATS,
16
+ te as DEFAULT_MAX_CANVAS_AREA,
17
+ s as DEFAULT_OGP_FILENAME,
18
+ E as DEFAULT_QUALITY,
19
+ p as DEFAULT_WIDTHS,
20
+ se as HEIC_DECODE_FAILED_MESSAGE,
21
+ i as IMAGE_EXTENSION_REGEX,
22
+ Ee as MISSING_EXIFR_MESSAGE,
23
+ pe as MISSING_HEIC2ANY_MESSAGE,
24
+ ie as ORIENTATION_BAKE_FAILED_MESSAGE_PREFIX,
25
+ n as PHOTO_VARIANT_WIDTHS,
26
+ A as SUPPORTED_IMAGE_EXTENSIONS,
27
+ m as VariantProcessingError,
28
+ H as alphaTrim,
29
+ Z as bakeOrientation,
30
+ z as batchBlurhashToDataUri,
31
+ K as blurhashToDataUri,
32
+ b as calibrateTargetFromSamples,
33
+ V as composeProductPhoto,
34
+ Y as compositeBatch,
35
+ J as compositeOverlay,
36
+ f as convertHeifToJpeg,
37
+ O as convertHeifToJpegNode,
38
+ P as cropToSquare,
39
+ T as defaultTagParser,
40
+ ne as deriveGeometry,
41
+ $ as deriveOrientation,
42
+ Q as encodeImageToBlurhash,
43
+ L as encodeUnderByteBudget,
44
+ F as extractExifFromHeif,
45
+ D as extractIccFromHeif,
46
+ N as generateOgpFromLandscape,
47
+ h as generateOgpImage,
48
+ k as generateShadowLayers,
49
+ u as generateSmartOgp,
50
+ C as insetOnSquare,
51
+ Ae as needsOrientationBake,
52
+ S as noTagParser,
53
+ q as normalizeBackgroundColor,
54
+ M as padToSquare,
55
+ G as padToSquareCentered,
56
+ ee as parseExifDate,
57
+ I as photoVariantsPreset,
58
+ re as pickVariantWidths,
59
+ me as prepareImageForUpload,
60
+ _ as processImages,
61
+ c as processOne,
62
+ v as removeBackground,
63
+ Te as roundAspectRatio,
64
+ y as sampleBackgroundColor,
65
+ d as selectVariantWidths,
66
+ ae as stripExif,
67
+ R as trimPadSquare
68
+ };
@@ -0,0 +1,72 @@
1
+ /** File path or in-memory buffer accepted as the compositor's source image. */
2
+ export type OgpInput = string | Buffer;
3
+ export interface OgpBaseOptions {
4
+ /** Output canvas width. Default 1200. */
5
+ width?: number;
6
+ /** Output canvas height. Default 630. */
7
+ height?: number;
8
+ /** JPEG quality (1-100). Default 85. */
9
+ quality?: number;
10
+ /** Progressive JPEG encoding. Default true. */
11
+ progressive?: boolean;
12
+ /** When set, also writes the result to this path; the result then carries `path`. */
13
+ outPath?: string;
14
+ }
15
+ export interface OgpImageOptions extends OgpBaseOptions {
16
+ /** Size of the centered square card. Default 600. */
17
+ foregroundSize?: number;
18
+ /** Corner radius applied to the card via an SVG mask. 0 (default) skips masking. */
19
+ cornerRadius?: number;
20
+ /** Gaussian blur sigma for the background. Default 45. */
21
+ blurSigma?: number;
22
+ /** Background desaturation, 0-1. Default 0.1. */
23
+ desaturate?: number;
24
+ /** Top gradient darkness, 0-1. Default 0.3. */
25
+ topGradientOpacity?: number;
26
+ /** Bottom gradient darkness, 0-1. Default 0.2. */
27
+ bottomGradientOpacity?: number;
28
+ /** Fraction of canvas height covered by the top gradient. Default 0.25. */
29
+ gradientTopHeight?: number;
30
+ /** Fraction of canvas height covered by the bottom gradient. Default 0.25. */
31
+ gradientBottomHeight?: number;
32
+ /** Drop shadow opacity, 0-1. Default 0.25. */
33
+ shadowOpacity?: number;
34
+ /** Drop shadow blur radius. Default 24. */
35
+ shadowBlur?: number;
36
+ /** Vertical drop shadow offset. Default 8. */
37
+ shadowOffsetY?: number;
38
+ }
39
+ export type OgpFromLandscapeOptions = OgpBaseOptions;
40
+ export interface SmartOgpOptions extends OgpImageOptions {
41
+ /** Aspect ratio (width / height) at or above which the landscape branch is used. Default 1.5. */
42
+ landscapeThreshold?: number;
43
+ }
44
+ export interface OgpResult {
45
+ buffer: Buffer;
46
+ width: number;
47
+ height: number;
48
+ format: 'jpeg';
49
+ /** Present only when `outPath` was passed. */
50
+ path?: string;
51
+ }
52
+ export interface SmartOgpResult extends OgpResult {
53
+ /** Which dispatch branch produced this result. */
54
+ method: 'landscape' | 'composite';
55
+ }
56
+ /**
57
+ * Composite a blurred, desaturated cover-crop background with gradient
58
+ * overlays, a drop shadow, and a centered square card (optionally
59
+ * rounded-corner masked). Best suited to square/portrait sources.
60
+ */
61
+ export declare function generateOgpImage(input: OgpInput, opts?: OgpImageOptions): Promise<OgpResult>;
62
+ /**
63
+ * Simple cover-crop to the target canvas, no compositing. Best suited to
64
+ * already-landscape sources where a blurred-background card would waste
65
+ * detail.
66
+ */
67
+ export declare function generateOgpFromLandscape(input: OgpInput, opts?: OgpFromLandscapeOptions): Promise<OgpResult>;
68
+ /**
69
+ * Dispatches to {@link generateOgpFromLandscape} or {@link generateOgpImage}
70
+ * based on the source's aspect ratio.
71
+ */
72
+ export declare function generateSmartOgp(input: OgpInput, opts?: SmartOgpOptions): Promise<SmartOgpResult>;