@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.
- package/CHANGELOG.md +1 -0
- package/LICENSE +21 -0
- package/README.md +57 -0
- package/dist/blurhash/index.d.ts +35 -0
- package/dist/blurhash/index.js +40 -0
- package/dist/browser/exif-orientation.d.ts +12 -0
- package/dist/browser/index.d.ts +7 -0
- package/dist/browser/index.js +160 -0
- package/dist/browser/orientation-semantics.d.ts +37 -0
- package/dist/browser/orientation.d.ts +25 -0
- package/dist/browser/prepare-upload.d.ts +100 -0
- package/dist/budget/index.d.ts +73 -0
- package/dist/budget/index.js +157 -0
- package/dist/calibrate/index.d.ts +129 -0
- package/dist/calibrate/index.js +206 -0
- package/dist/composite/index.d.ts +50 -0
- package/dist/composite/index.js +56 -0
- package/dist/exif/index.d.ts +41 -0
- package/dist/exif/index.js +100 -0
- package/dist/heif/index.d.ts +139 -0
- package/dist/heif/index.js +275 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.js +68 -0
- package/dist/ogp/index.d.ts +72 -0
- package/dist/ogp/index.js +117 -0
- package/dist/presets-PTxNoMdy.js +630 -0
- package/dist/product-photo/index.d.ts +50 -0
- package/dist/product-photo/index.js +189 -0
- package/dist/product-photo/shadow.d.ts +48 -0
- package/dist/run-BIjGA2Fu.js +22 -0
- package/dist/square/index.d.ts +98 -0
- package/dist/square/index.js +89 -0
- package/dist/variants/engine.d.ts +38 -0
- package/dist/variants/feature-detect.d.ts +8 -0
- package/dist/variants/hash-cache.d.ts +63 -0
- package/dist/variants/heic.d.ts +15 -0
- package/dist/variants/index.d.ts +5 -0
- package/dist/variants/index.js +19 -0
- package/dist/variants/presets.d.ts +18 -0
- package/dist/variants/repair.d.ts +28 -0
- package/dist/variants/run.d.ts +36 -0
- package/dist/variants/tags.d.ts +13 -0
- package/dist/variants/types.d.ts +208 -0
- 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
|
+
};
|
package/dist/index.d.ts
ADDED
|
@@ -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>;
|