modern-pdf-lib 0.36.0 → 0.38.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/README.md +3 -3
- package/dist/browser.cjs +29 -1
- package/dist/browser.d.cts +2 -2
- package/dist/browser.d.mts +2 -2
- package/dist/browser.mjs +2 -2
- package/dist/{index-Bg3y1BTk.d.mts → index-CDg5gwYg.d.mts} +810 -4
- package/dist/index-CDg5gwYg.d.mts.map +1 -0
- package/dist/{index-06FgCx99.d.cts → index-CNtfRlyP.d.cts} +810 -4
- package/dist/index-CNtfRlyP.d.cts.map +1 -0
- package/dist/index.cjs +29 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.mts +2 -2
- package/dist/index.mjs +2 -2
- package/dist/{src-MIBq9xGq.cjs → src-B0gmgVEs.cjs} +1974 -166
- package/dist/{src-4wfgzZvz.mjs → src-DlRTrvWg.mjs} +1674 -34
- package/package.json +1 -1
- package/dist/index-06FgCx99.d.cts.map +0 -1
- package/dist/index-Bg3y1BTk.d.mts.map +0 -1
|
@@ -9599,7 +9599,7 @@ function readU16$1(data, offset, le) {
|
|
|
9599
9599
|
* Read a 32-bit unsigned integer.
|
|
9600
9600
|
* @internal
|
|
9601
9601
|
*/
|
|
9602
|
-
function readU32$
|
|
9602
|
+
function readU32$2(data, offset, le) {
|
|
9603
9603
|
return getView(data).getUint32(offset, le);
|
|
9604
9604
|
}
|
|
9605
9605
|
/**
|
|
@@ -9614,8 +9614,8 @@ function parseIfd(data, offset, le) {
|
|
|
9614
9614
|
if (entryOffset + 12 > data.length) break;
|
|
9615
9615
|
const tag = readU16$1(data, entryOffset, le);
|
|
9616
9616
|
const type = readU16$1(data, entryOffset + 2, le);
|
|
9617
|
-
const cnt = readU32$
|
|
9618
|
-
const valueOrOffset = readU32$
|
|
9617
|
+
const cnt = readU32$2(data, entryOffset + 4, le);
|
|
9618
|
+
const valueOrOffset = readU32$2(data, entryOffset + 8, le);
|
|
9619
9619
|
entries.push({
|
|
9620
9620
|
tag,
|
|
9621
9621
|
type,
|
|
@@ -9653,7 +9653,7 @@ function readOffsetArray(data, entry, le) {
|
|
|
9653
9653
|
const pos = offset + i * elementSize;
|
|
9654
9654
|
if (pos + elementSize > data.length) break;
|
|
9655
9655
|
if (entry.type === 3) values.push(readU16$1(data, pos, le));
|
|
9656
|
-
else values.push(readU32$
|
|
9656
|
+
else values.push(readU32$2(data, pos, le));
|
|
9657
9657
|
}
|
|
9658
9658
|
return values;
|
|
9659
9659
|
}
|
|
@@ -9662,13 +9662,13 @@ function readOffsetArray(data, entry, le) {
|
|
|
9662
9662
|
* @internal
|
|
9663
9663
|
*/
|
|
9664
9664
|
function findIfdOffset(data, le, page) {
|
|
9665
|
-
let ifdOffset = readU32$
|
|
9665
|
+
let ifdOffset = readU32$2(data, 4, le);
|
|
9666
9666
|
for (let i = 0; i < page; i++) {
|
|
9667
9667
|
if (ifdOffset === 0 || ifdOffset + 2 > data.length) throw new Error(`TIFF page ${page} not found (only ${i} pages available)`);
|
|
9668
9668
|
const entryCount = readU16$1(data, ifdOffset, le);
|
|
9669
9669
|
const nextIfdOffset = ifdOffset + 2 + entryCount * 12;
|
|
9670
9670
|
if (nextIfdOffset + 4 > data.length) throw new Error(`TIFF page ${page} not found (only ${i + 1} pages available)`);
|
|
9671
|
-
ifdOffset = readU32$
|
|
9671
|
+
ifdOffset = readU32$2(data, nextIfdOffset, le);
|
|
9672
9672
|
if (ifdOffset === 0) throw new Error(`TIFF page ${page} not found (only ${i + 1} pages available)`);
|
|
9673
9673
|
}
|
|
9674
9674
|
return ifdOffset;
|
|
@@ -9725,7 +9725,7 @@ function canDirectEmbed(data) {
|
|
|
9725
9725
|
if (!isLE && !isBE) return false;
|
|
9726
9726
|
const le = isLE;
|
|
9727
9727
|
if (readU16$1(data, 2, le) !== 42) return false;
|
|
9728
|
-
const ifdOffset = readU32$
|
|
9728
|
+
const ifdOffset = readU32$2(data, 4, le);
|
|
9729
9729
|
if (ifdOffset === 0 || ifdOffset + 2 > data.length) return false;
|
|
9730
9730
|
const compression = getTag(parseIfd(data, ifdOffset, le), TAG_COMPRESSION, data, le) ?? 1;
|
|
9731
9731
|
return compression === COMPRESSION_NONE || compression === COMPRESSION_DEFLATE || compression === COMPRESSION_DEFLATE_PKZIP || compression === COMPRESSION_JPEG || compression === COMPRESSION_OLD_JPEG;
|
|
@@ -19503,15 +19503,15 @@ const D50_WHITE_POINT = [
|
|
|
19503
19503
|
.8249
|
|
19504
19504
|
];
|
|
19505
19505
|
/** CIE constant δ = 6/29; the linear/cubic break is at t ≤ δ. */
|
|
19506
|
-
const LAB_DELTA = 6 / 29;
|
|
19506
|
+
const LAB_DELTA$2 = 6 / 29;
|
|
19507
19507
|
/** 3δ² — slope of the inverse linear branch. */
|
|
19508
|
-
const LAB_3_DELTA_SQUARED = 3 * LAB_DELTA * LAB_DELTA;
|
|
19508
|
+
const LAB_3_DELTA_SQUARED$1 = 3 * LAB_DELTA$2 * LAB_DELTA$2;
|
|
19509
19509
|
/** Inverse of the CIE L*a*b* nonlinearity f(t). */
|
|
19510
19510
|
function labInverseF(t) {
|
|
19511
|
-
return t > LAB_DELTA ? t * t * t : LAB_3_DELTA_SQUARED * (t - 4 / 29);
|
|
19511
|
+
return t > LAB_DELTA$2 ? t * t * t : LAB_3_DELTA_SQUARED$1 * (t - 4 / 29);
|
|
19512
19512
|
}
|
|
19513
19513
|
/** Apply the sRGB transfer function (linear → companded), input clamped 0..1. */
|
|
19514
|
-
function linearToSrgb(c) {
|
|
19514
|
+
function linearToSrgb$1(c) {
|
|
19515
19515
|
const clamped = c < 0 ? 0 : c > 1 ? 1 : c;
|
|
19516
19516
|
const v = clamped <= .0031308 ? 12.92 * clamped : 1.055 * clamped ** (1 / 2.4) - .055;
|
|
19517
19517
|
return v < 0 ? 0 : v > 1 ? 1 : v;
|
|
@@ -19545,9 +19545,9 @@ function labToRgb(L, a, b, whitePoint = D50_WHITE_POINT) {
|
|
|
19545
19545
|
const gLin = -.9787684 * X + 1.9161415 * Y + .033454 * Z;
|
|
19546
19546
|
const bLin = .0719453 * X - .2289914 * Y + 1.4052427 * Z;
|
|
19547
19547
|
return [
|
|
19548
|
-
linearToSrgb(rLin),
|
|
19549
|
-
linearToSrgb(gLin),
|
|
19550
|
-
linearToSrgb(bLin)
|
|
19548
|
+
linearToSrgb$1(rLin),
|
|
19549
|
+
linearToSrgb$1(gLin),
|
|
19550
|
+
linearToSrgb$1(bLin)
|
|
19551
19551
|
];
|
|
19552
19552
|
}
|
|
19553
19553
|
//#endregion
|
|
@@ -20153,7 +20153,7 @@ function breakLongWord$1(word, font, size, limit) {
|
|
|
20153
20153
|
//#endregion
|
|
20154
20154
|
//#region src/core/pdfFunctions.ts
|
|
20155
20155
|
/** Clamp `x` into the inclusive `[lo, hi]` interval. */
|
|
20156
|
-
function clamp(x, lo, hi) {
|
|
20156
|
+
function clamp$1(x, lo, hi) {
|
|
20157
20157
|
if (x < lo) return lo;
|
|
20158
20158
|
if (x > hi) return hi;
|
|
20159
20159
|
return x;
|
|
@@ -20173,14 +20173,14 @@ function clampToRange(out, range) {
|
|
|
20173
20173
|
for (let i = 0; i < out.length; i++) {
|
|
20174
20174
|
const lo = range[2 * i];
|
|
20175
20175
|
const hi = range[2 * i + 1];
|
|
20176
|
-
if (lo !== void 0 && hi !== void 0) out[i] = clamp(out[i], lo, hi);
|
|
20176
|
+
if (lo !== void 0 && hi !== void 0) out[i] = clamp$1(out[i], lo, hi);
|
|
20177
20177
|
}
|
|
20178
20178
|
return out;
|
|
20179
20179
|
}
|
|
20180
20180
|
function evaluateExponential(fn, inputs) {
|
|
20181
20181
|
const c0 = fn.c0 ?? [0];
|
|
20182
20182
|
const c1 = fn.c1 ?? [1];
|
|
20183
|
-
const x = clamp(inputs[0] ?? 0, fn.domain[0] ?? 0, fn.domain[1] ?? 1);
|
|
20183
|
+
const x = clamp$1(inputs[0] ?? 0, fn.domain[0] ?? 0, fn.domain[1] ?? 1);
|
|
20184
20184
|
const xn = fn.n === 1 ? x : x ** fn.n;
|
|
20185
20185
|
const count = Math.max(c0.length, c1.length);
|
|
20186
20186
|
const out = [];
|
|
@@ -20194,7 +20194,7 @@ function evaluateExponential(fn, inputs) {
|
|
|
20194
20194
|
function evaluateStitching(fn, inputs) {
|
|
20195
20195
|
const d0 = fn.domain[0] ?? 0;
|
|
20196
20196
|
const d1 = fn.domain[1] ?? 1;
|
|
20197
|
-
const x = clamp(inputs[0] ?? 0, d0, d1);
|
|
20197
|
+
const x = clamp$1(inputs[0] ?? 0, d0, d1);
|
|
20198
20198
|
const k = fn.functions.length;
|
|
20199
20199
|
let i = 0;
|
|
20200
20200
|
while (i < fn.bounds.length && x >= fn.bounds[i]) i++;
|
|
@@ -20212,10 +20212,10 @@ function evaluateSampled(fn, inputs) {
|
|
|
20212
20212
|
for (let j = 0; j < m; j++) {
|
|
20213
20213
|
const dMin = fn.domain[2 * j] ?? 0;
|
|
20214
20214
|
const dMax = fn.domain[2 * j + 1] ?? 1;
|
|
20215
|
-
const x = clamp(inputs[j] ?? 0, dMin, dMax);
|
|
20215
|
+
const x = clamp$1(inputs[j] ?? 0, dMin, dMax);
|
|
20216
20216
|
const size = fn.size[j] ?? 1;
|
|
20217
20217
|
const enc = interpolate(x, dMin, dMax, fn.encode ? fn.encode[2 * j] ?? 0 : 0, fn.encode ? fn.encode[2 * j + 1] ?? size - 1 : size - 1);
|
|
20218
|
-
e.push(clamp(enc, 0, size - 1));
|
|
20218
|
+
e.push(clamp$1(enc, 0, size - 1));
|
|
20219
20219
|
}
|
|
20220
20220
|
const lower = [];
|
|
20221
20221
|
const frac = [];
|
|
@@ -20561,7 +20561,7 @@ function evaluatePostScript(fn, inputs) {
|
|
|
20561
20561
|
for (let j = 0; j < m; j++) {
|
|
20562
20562
|
const dMin = fn.domain[2 * j] ?? 0;
|
|
20563
20563
|
const dMax = fn.domain[2 * j + 1] ?? 1;
|
|
20564
|
-
stack.push(clamp(inputs[j] ?? 0, dMin, dMax));
|
|
20564
|
+
stack.push(clamp$1(inputs[j] ?? 0, dMin, dMax));
|
|
20565
20565
|
}
|
|
20566
20566
|
execPostScript(program, stack);
|
|
20567
20567
|
const nOut = Math.floor(fn.range.length / 2);
|
|
@@ -25147,7 +25147,7 @@ function readU16(data, offset) {
|
|
|
25147
25147
|
/**
|
|
25148
25148
|
* Read a 32-bit big-endian unsigned integer.
|
|
25149
25149
|
*/
|
|
25150
|
-
function readU32(data, offset) {
|
|
25150
|
+
function readU32$1(data, offset) {
|
|
25151
25151
|
return (data[offset] << 24 | data[offset + 1] << 16 | data[offset + 2] << 8 | data[offset + 3]) >>> 0;
|
|
25152
25152
|
}
|
|
25153
25153
|
/**
|
|
@@ -25158,7 +25158,7 @@ function findCodestreamStart(data) {
|
|
|
25158
25158
|
if (data.length >= 12 && data[0] === 0 && data[1] === 0 && data[2] === 0 && data[3] === 12 && data[4] === 106 && data[5] === 80) {
|
|
25159
25159
|
let offset = 0;
|
|
25160
25160
|
while (offset + 8 <= data.length) {
|
|
25161
|
-
const boxLen = readU32(data, offset);
|
|
25161
|
+
const boxLen = readU32$1(data, offset);
|
|
25162
25162
|
if (String.fromCharCode(data[offset + 4], data[offset + 5], data[offset + 6], data[offset + 7]) === "jp2c") return offset + 8;
|
|
25163
25163
|
const advance = boxLen === 0 ? data.length - offset : boxLen < 8 ? 8 : boxLen;
|
|
25164
25164
|
offset += advance;
|
|
@@ -25184,14 +25184,14 @@ function parseTileInfo(data) {
|
|
|
25184
25184
|
if (sizOffset < 0) throw new Error("JPEG2000 Tiles: SIZ marker (0xFF51) not found");
|
|
25185
25185
|
const base = sizOffset + 2;
|
|
25186
25186
|
if (base + 38 > data.length) throw new Error("JPEG2000 Tiles: SIZ marker segment is truncated");
|
|
25187
|
-
const xsiz = readU32(data, base + 4);
|
|
25188
|
-
const ysiz = readU32(data, base + 8);
|
|
25189
|
-
const xosiz = readU32(data, base + 12);
|
|
25190
|
-
const yosiz = readU32(data, base + 16);
|
|
25191
|
-
const xtsiz = readU32(data, base + 20);
|
|
25192
|
-
const ytsiz = readU32(data, base + 24);
|
|
25193
|
-
const xtosiz = readU32(data, base + 28);
|
|
25194
|
-
const ytosiz = readU32(data, base + 32);
|
|
25187
|
+
const xsiz = readU32$1(data, base + 4);
|
|
25188
|
+
const ysiz = readU32$1(data, base + 8);
|
|
25189
|
+
const xosiz = readU32$1(data, base + 12);
|
|
25190
|
+
const yosiz = readU32$1(data, base + 16);
|
|
25191
|
+
const xtsiz = readU32$1(data, base + 20);
|
|
25192
|
+
const ytsiz = readU32$1(data, base + 24);
|
|
25193
|
+
const xtosiz = readU32$1(data, base + 28);
|
|
25194
|
+
const ytosiz = readU32$1(data, base + 32);
|
|
25195
25195
|
const csiz = readU16(data, base + 36);
|
|
25196
25196
|
const imageWidth = xsiz - xosiz;
|
|
25197
25197
|
const imageHeight = ysiz - yosiz;
|
|
@@ -25231,7 +25231,7 @@ function findTileParts(data, csStart) {
|
|
|
25231
25231
|
if (offset + 12 > data.length) break;
|
|
25232
25232
|
const lsot = readU16(data, offset + 2);
|
|
25233
25233
|
const isot = readU16(data, offset + 4);
|
|
25234
|
-
const psot = readU32(data, offset + 6);
|
|
25234
|
+
const psot = readU32$1(data, offset + 6);
|
|
25235
25235
|
let sodOffset = offset + 2 + lsot;
|
|
25236
25236
|
while (sodOffset + 1 < data.length) {
|
|
25237
25237
|
if (data[sodOffset] === 255 && data[sodOffset + 1] === 147) break;
|
|
@@ -30527,168 +30527,1808 @@ function getColorGlyphLayers(fontData, glyphId, paletteIndex = 0) {
|
|
|
30527
30527
|
return layers;
|
|
30528
30528
|
}
|
|
30529
30529
|
//#endregion
|
|
30530
|
-
//#region src/
|
|
30531
|
-
/**
|
|
30532
|
-
|
|
30533
|
-
|
|
30534
|
-
|
|
30535
|
-
|
|
30536
|
-
|
|
30537
|
-
|
|
30538
|
-
|
|
30539
|
-
|
|
30540
|
-
|
|
30530
|
+
//#region src/core/meshShading.ts
|
|
30531
|
+
/**
|
|
30532
|
+
* @module core/meshShading
|
|
30533
|
+
*
|
|
30534
|
+
* Mesh shadings — ShadingType 4, 5, 6 and 7 (ISO 32000-2:2020 §8.7.4.5.5–.8).
|
|
30535
|
+
*
|
|
30536
|
+
* Mesh shadings define colour over a 2-D region by tessellating it into either
|
|
30537
|
+
* Gouraud-shaded triangles (types 4 and 5) or parametric patches (types 6 and
|
|
30538
|
+
* 7). Unlike the dictionary-only shadings (types 1–3), a mesh shading's data is
|
|
30539
|
+
* carried in a **stream** whose body is a packed sequence of binary records.
|
|
30540
|
+
*
|
|
30541
|
+
* Bit-packing rule (ISO 32000-2 §8.7.4.5.5, "All vertex coordinates and colour
|
|
30542
|
+
* values shall be encoded as ... a sequence of bits ... most significant bit
|
|
30543
|
+
* first"): every value — flag, coordinate and colour component — is written
|
|
30544
|
+
* big-endian, MSB-first, into a continuous bit stream with **no padding between
|
|
30545
|
+
* values**. Only the very end of the stream is padded with zero bits up to the
|
|
30546
|
+
* next byte boundary. (§8.7.4.5.5 further notes each value may begin at any bit
|
|
30547
|
+
* position; there is no per-record byte alignment.)
|
|
30548
|
+
*
|
|
30549
|
+
* Numeric mapping (ISO 32000-2 §8.7.4.5.5, Table 84 /Decode semantics): a
|
|
30550
|
+
* coordinate or colour value is stored as an unsigned integer `v` in the range
|
|
30551
|
+
* `[0, 2^bits − 1]` and decoded as
|
|
30552
|
+
* `decoded = Dmin + v · (Dmax − Dmin) / (2^bits − 1)`
|
|
30553
|
+
* where `[Dmin, Dmax]` is the matching pair in the shading's /Decode array. The
|
|
30554
|
+
* /Decode layout is `[xmin xmax ymin ymax c1min c1max … cnmin cnmax]` (or, when
|
|
30555
|
+
* /Function is present, a single `[tmin tmax]` colour pair). The builders here
|
|
30556
|
+
* take **decoded** user values and invert that mapping to obtain the raw
|
|
30557
|
+
* integers to pack, rounding to nearest.
|
|
30558
|
+
*
|
|
30559
|
+
* Record layouts verified against ISO 32000-2:2020:
|
|
30560
|
+
* - Type 4 (§8.7.4.5.5): per vertex → flag (BitsPerFlag) · x · y · colour[n].
|
|
30561
|
+
* - Type 5 (§8.7.4.5.6): per vertex → x · y · colour[n]; NO flag; adds
|
|
30562
|
+
* /VerticesPerRow (≥ 2). Vertices are given row by row.
|
|
30563
|
+
* - Type 6 (§8.7.4.5.7): per patch → flag (BitsPerFlag) · coordinate pairs ·
|
|
30564
|
+
* colours. Flag 0 (new patch) = 12 coordinate pairs + 4 colours; flags 1–3
|
|
30565
|
+
* (shared edge) = 8 coordinate pairs + 2 colours.
|
|
30566
|
+
* - Type 7 (§8.7.4.5.8): like type 6 but flag 0 = 16 coordinate pairs +
|
|
30567
|
+
* 4 colours; flags 1–3 = 12 coordinate pairs + 2 colours.
|
|
30568
|
+
*
|
|
30569
|
+
* No Buffer — uses Uint8Array exclusively.
|
|
30570
|
+
*/
|
|
30571
|
+
/**
|
|
30572
|
+
* Accumulates unsigned integer values of arbitrary bit width into a byte
|
|
30573
|
+
* stream, big-endian and most-significant-bit-first, with no inter-value
|
|
30574
|
+
* padding. The final byte is zero-padded to the byte boundary on
|
|
30575
|
+
* {@link BitPacker.finish}.
|
|
30576
|
+
*
|
|
30577
|
+
* @internal
|
|
30578
|
+
*/
|
|
30579
|
+
var BitPacker = class {
|
|
30580
|
+
bytes = [];
|
|
30581
|
+
/** Bits already filled in the current (pending) byte, 0–7. */
|
|
30582
|
+
bitPos = 0;
|
|
30583
|
+
/** The partially filled current byte (high bits set first). */
|
|
30584
|
+
current = 0;
|
|
30585
|
+
/**
|
|
30586
|
+
* Append `bits`-wide unsigned integer `value`, MSB first.
|
|
30587
|
+
*
|
|
30588
|
+
* @param value - non-negative integer in `[0, 2^bits − 1]`.
|
|
30589
|
+
* @param bits - field width (1–32).
|
|
30590
|
+
*/
|
|
30591
|
+
push(value, bits) {
|
|
30592
|
+
for (let i = bits - 1; i >= 0; i--) {
|
|
30593
|
+
const bit = value >>> i & 1;
|
|
30594
|
+
this.current = this.current << 1 | bit;
|
|
30595
|
+
this.bitPos++;
|
|
30596
|
+
if (this.bitPos === 8) {
|
|
30597
|
+
this.bytes.push(this.current & 255);
|
|
30598
|
+
this.current = 0;
|
|
30599
|
+
this.bitPos = 0;
|
|
30600
|
+
}
|
|
30601
|
+
}
|
|
30602
|
+
}
|
|
30603
|
+
/**
|
|
30604
|
+
* Flush the pending partial byte (zero-padded on the low bits) and return the
|
|
30605
|
+
* packed buffer.
|
|
30606
|
+
*/
|
|
30607
|
+
finish() {
|
|
30608
|
+
if (this.bitPos > 0) {
|
|
30609
|
+
this.bytes.push(this.current << 8 - this.bitPos & 255);
|
|
30610
|
+
this.current = 0;
|
|
30611
|
+
this.bitPos = 0;
|
|
30612
|
+
}
|
|
30613
|
+
return Uint8Array.from(this.bytes);
|
|
30614
|
+
}
|
|
30615
|
+
};
|
|
30616
|
+
/**
|
|
30617
|
+
* Invert the /Decode mapping for one value: given a decoded user value and its
|
|
30618
|
+
* `[min, max]` pair, return the raw unsigned integer in `[0, 2^bits − 1]`.
|
|
30619
|
+
*
|
|
30620
|
+
* `raw = round((decoded − min) / (max − min) · (2^bits − 1))`, clamped to the
|
|
30621
|
+
* representable range (ISO 32000-2 §8.7.4.5.5, /Decode semantics). A degenerate
|
|
30622
|
+
* `min == max` pair maps everything to 0.
|
|
30623
|
+
*
|
|
30624
|
+
* @internal
|
|
30625
|
+
*/
|
|
30626
|
+
function encodeValue(decoded, min, max, bits) {
|
|
30627
|
+
const maxRaw = bits >= 32 ? 4294967295 : (1 << bits) - 1;
|
|
30628
|
+
if (max === min) return 0;
|
|
30629
|
+
const t = (decoded - min) / (max - min);
|
|
30630
|
+
const raw = Math.round(t * maxRaw);
|
|
30631
|
+
if (raw < 0) return 0;
|
|
30632
|
+
if (raw > maxRaw) return maxRaw;
|
|
30633
|
+
return raw;
|
|
30541
30634
|
}
|
|
30542
30635
|
/**
|
|
30543
|
-
*
|
|
30544
|
-
*
|
|
30545
|
-
*
|
|
30636
|
+
* Number of colour components carried per vertex/corner:
|
|
30637
|
+
* 1 when a /Function is present (parametric `t`), else the count implied by the
|
|
30638
|
+
* colour /Decode pairs.
|
|
30639
|
+
*
|
|
30640
|
+
* @internal
|
|
30546
30641
|
*/
|
|
30547
|
-
function
|
|
30548
|
-
return
|
|
30549
|
-
|
|
30550
|
-
|
|
30551
|
-
m[2] * n[0] + m[3] * n[2],
|
|
30552
|
-
m[2] * n[1] + m[3] * n[3],
|
|
30553
|
-
m[4] * n[0] + m[5] * n[2] + n[4],
|
|
30554
|
-
m[4] * n[1] + m[5] * n[3] + n[5]
|
|
30555
|
-
];
|
|
30642
|
+
function colorComponentCount(common) {
|
|
30643
|
+
if (common.function !== void 0) return 1;
|
|
30644
|
+
const colorPairs = (common.decode.length - 4) / 2;
|
|
30645
|
+
return Math.max(1, Math.trunc(colorPairs));
|
|
30556
30646
|
}
|
|
30557
|
-
/**
|
|
30558
|
-
|
|
30559
|
-
|
|
30647
|
+
/**
|
|
30648
|
+
* Pack one vertex's coordinates then its colours (no flag) into `packer`.
|
|
30649
|
+
*
|
|
30650
|
+
* @internal
|
|
30651
|
+
*/
|
|
30652
|
+
function packVertexData(packer, vertex, common, nColor) {
|
|
30653
|
+
const { decode, bitsPerCoordinate: bpcoord, bitsPerComponent: bpcomp } = common;
|
|
30654
|
+
packer.push(encodeValue(vertex.x, decode[0] ?? 0, decode[1] ?? 1, bpcoord), bpcoord);
|
|
30655
|
+
packer.push(encodeValue(vertex.y, decode[2] ?? 0, decode[3] ?? 1, bpcoord), bpcoord);
|
|
30656
|
+
for (let k = 0; k < nColor; k++) {
|
|
30657
|
+
const cmin = decode[4 + 2 * k] ?? 0;
|
|
30658
|
+
const cmax = decode[5 + 2 * k] ?? 1;
|
|
30659
|
+
const comp = vertex.color[k] ?? 0;
|
|
30660
|
+
packer.push(encodeValue(comp, cmin, cmax, bpcomp), bpcomp);
|
|
30661
|
+
}
|
|
30560
30662
|
}
|
|
30561
|
-
/**
|
|
30562
|
-
|
|
30563
|
-
|
|
30564
|
-
|
|
30565
|
-
|
|
30566
|
-
|
|
30567
|
-
|
|
30568
|
-
|
|
30569
|
-
|
|
30570
|
-
];
|
|
30663
|
+
/**
|
|
30664
|
+
* Pack a single `[x, y]` control point (coordinates only) into `packer`.
|
|
30665
|
+
*
|
|
30666
|
+
* @internal
|
|
30667
|
+
*/
|
|
30668
|
+
function packPoint(packer, point, common) {
|
|
30669
|
+
const { decode, bitsPerCoordinate: bpcoord } = common;
|
|
30670
|
+
packer.push(encodeValue(point[0], decode[0] ?? 0, decode[1] ?? 1, bpcoord), bpcoord);
|
|
30671
|
+
packer.push(encodeValue(point[1], decode[2] ?? 0, decode[3] ?? 1, bpcoord), bpcoord);
|
|
30571
30672
|
}
|
|
30572
30673
|
/**
|
|
30573
|
-
*
|
|
30574
|
-
*
|
|
30575
|
-
*
|
|
30674
|
+
* Pack one colour (n components) into `packer`.
|
|
30675
|
+
*
|
|
30676
|
+
* @internal
|
|
30576
30677
|
*/
|
|
30577
|
-
function
|
|
30578
|
-
const
|
|
30579
|
-
|
|
30678
|
+
function packColor(packer, color, common, nColor) {
|
|
30679
|
+
const { decode, bitsPerComponent: bpcomp } = common;
|
|
30680
|
+
for (let k = 0; k < nColor; k++) {
|
|
30681
|
+
const cmin = decode[4 + 2 * k] ?? 0;
|
|
30682
|
+
const cmax = decode[5 + 2 * k] ?? 1;
|
|
30683
|
+
const comp = color[k] ?? 0;
|
|
30684
|
+
packer.push(encodeValue(comp, cmin, cmax, bpcomp), bpcomp);
|
|
30685
|
+
}
|
|
30580
30686
|
}
|
|
30581
|
-
//#endregion
|
|
30582
|
-
//#region src/render/interpreter.ts
|
|
30583
30687
|
/**
|
|
30584
|
-
*
|
|
30688
|
+
* Build the dictionary shared by every mesh shading and set the common keys.
|
|
30585
30689
|
*
|
|
30586
|
-
*
|
|
30587
|
-
|
|
30588
|
-
|
|
30690
|
+
* @internal
|
|
30691
|
+
*/
|
|
30692
|
+
function buildCommonDict(shadingType, common, includeFlag) {
|
|
30693
|
+
const dict = new require_pdfObjects.PdfDict();
|
|
30694
|
+
dict.set("/ShadingType", require_pdfObjects.PdfNumber.of(shadingType));
|
|
30695
|
+
dict.set("/ColorSpace", common.colorSpace);
|
|
30696
|
+
dict.set("/BitsPerCoordinate", require_pdfObjects.PdfNumber.of(common.bitsPerCoordinate));
|
|
30697
|
+
dict.set("/BitsPerComponent", require_pdfObjects.PdfNumber.of(common.bitsPerComponent));
|
|
30698
|
+
if (includeFlag) dict.set("/BitsPerFlag", require_pdfObjects.PdfNumber.of(common.bitsPerFlag));
|
|
30699
|
+
dict.set("/Decode", require_pdfObjects.PdfArray.fromNumbers([...common.decode]));
|
|
30700
|
+
if (common.function !== void 0) dict.set("/Function", common.function);
|
|
30701
|
+
return dict;
|
|
30702
|
+
}
|
|
30703
|
+
/**
|
|
30704
|
+
* Build a free-form Gouraud-shaded triangle mesh shading
|
|
30705
|
+
* (ISO 32000-2 §8.7.4.5.5, /ShadingType 4).
|
|
30589
30706
|
*
|
|
30590
|
-
*
|
|
30591
|
-
*
|
|
30592
|
-
* (`f`/`F`/`f*`/`S`/`s`/`B`/`B*`/`b`/`b*`/`n`), clipping (`W`/`W*`), color
|
|
30593
|
-
* (`rg`/`RG`/`g`/`G`/`k`/`K`/`cs`/`CS`/`sc`/`scn`/`SC`/`SCN`), text
|
|
30594
|
-
* (`BT`/`ET`/`Tf`/`Td`/`TD`/`Tm`/`T*`/`Tc`/`Tw`/`Tz`/`TL`/`Ts`/`Tr`/`Tj`/`TJ`/`'`/`"`),
|
|
30595
|
-
* and XObjects (`Do`, recursing into form XObjects). Bézier curves are
|
|
30596
|
-
* flattened to polylines.
|
|
30707
|
+
* Each vertex is packed as `flag · x · y · colour[n]`, MSB-first, with the flag
|
|
30708
|
+
* `bitsPerFlag` wide.
|
|
30597
30709
|
*
|
|
30598
|
-
* @
|
|
30710
|
+
* @param options - the common mesh keys plus the triangle/vertex data.
|
|
30711
|
+
* @returns a {@link PdfStream} whose dict has /ShadingType 4 and whose body is
|
|
30712
|
+
* the packed vertex stream.
|
|
30599
30713
|
*/
|
|
30600
|
-
function
|
|
30601
|
-
|
|
30602
|
-
|
|
30603
|
-
|
|
30604
|
-
|
|
30605
|
-
|
|
30606
|
-
|
|
30607
|
-
|
|
30608
|
-
|
|
30609
|
-
|
|
30610
|
-
|
|
30611
|
-
|
|
30612
|
-
|
|
30613
|
-
|
|
30614
|
-
|
|
30615
|
-
|
|
30616
|
-
|
|
30617
|
-
|
|
30618
|
-
|
|
30619
|
-
|
|
30620
|
-
|
|
30621
|
-
|
|
30622
|
-
|
|
30623
|
-
|
|
30624
|
-
|
|
30625
|
-
|
|
30626
|
-
|
|
30627
|
-
|
|
30628
|
-
|
|
30714
|
+
function buildFreeFormGouraudShading(options) {
|
|
30715
|
+
const dict = buildCommonDict(4, options, true);
|
|
30716
|
+
const nColor = colorComponentCount(options);
|
|
30717
|
+
const packer = new BitPacker();
|
|
30718
|
+
for (const tri of options.triangles) for (const vertex of tri.vertices) {
|
|
30719
|
+
packer.push(vertex.flag, options.bitsPerFlag);
|
|
30720
|
+
packVertexData(packer, vertex, options, nColor);
|
|
30721
|
+
}
|
|
30722
|
+
const data = packer.finish();
|
|
30723
|
+
return require_pdfObjects.PdfStream.fromBytes(data, dict);
|
|
30724
|
+
}
|
|
30725
|
+
/**
|
|
30726
|
+
* Build a lattice-form Gouraud-shaded triangle mesh shading
|
|
30727
|
+
* (ISO 32000-2 §8.7.4.5.6, /ShadingType 5).
|
|
30728
|
+
*
|
|
30729
|
+
* Vertices carry **no** flag; the mesh topology is implied by /VerticesPerRow.
|
|
30730
|
+
* Each vertex is packed as `x · y · colour[n]`, MSB-first.
|
|
30731
|
+
*
|
|
30732
|
+
* @param options - the common mesh keys (sans /BitsPerFlag) plus
|
|
30733
|
+
* /VerticesPerRow and the row-major vertex list.
|
|
30734
|
+
* @returns a {@link PdfStream} with /ShadingType 5 and /VerticesPerRow.
|
|
30735
|
+
*/
|
|
30736
|
+
function buildLatticeFormGouraudShading(options) {
|
|
30737
|
+
const common = {
|
|
30738
|
+
colorSpace: options.colorSpace,
|
|
30739
|
+
bitsPerCoordinate: options.bitsPerCoordinate,
|
|
30740
|
+
bitsPerComponent: options.bitsPerComponent,
|
|
30741
|
+
bitsPerFlag: 8,
|
|
30742
|
+
decode: options.decode,
|
|
30743
|
+
function: options.function
|
|
30629
30744
|
};
|
|
30745
|
+
const dict = buildCommonDict(5, common, false);
|
|
30746
|
+
dict.set("/VerticesPerRow", require_pdfObjects.PdfNumber.of(options.verticesPerRow));
|
|
30747
|
+
const nColor = colorComponentCount(common);
|
|
30748
|
+
const packer = new BitPacker();
|
|
30749
|
+
for (const vertex of options.vertices) packVertexData(packer, vertex, common, nColor);
|
|
30750
|
+
const data = packer.finish();
|
|
30751
|
+
return require_pdfObjects.PdfStream.fromBytes(data, dict);
|
|
30630
30752
|
}
|
|
30631
|
-
|
|
30632
|
-
|
|
30753
|
+
/**
|
|
30754
|
+
* Build a Coons patch mesh shading
|
|
30755
|
+
* (ISO 32000-2 §8.7.4.5.7, /ShadingType 6).
|
|
30756
|
+
*
|
|
30757
|
+
* Each patch is packed as `flag · points · colours`. A flag-0 patch carries 12
|
|
30758
|
+
* control points and 4 corner colours; flags 1–3 carry 8 control points and 2
|
|
30759
|
+
* colours (the rest are inherited from the shared edge of the previous patch).
|
|
30760
|
+
*
|
|
30761
|
+
* @param options - the common mesh keys plus the patch list.
|
|
30762
|
+
* @returns a {@link PdfStream} with /ShadingType 6.
|
|
30763
|
+
*/
|
|
30764
|
+
function buildCoonsPatchShading(options) {
|
|
30765
|
+
const dict = buildCommonDict(6, options, true);
|
|
30766
|
+
const nColor = colorComponentCount(options);
|
|
30767
|
+
const packer = new BitPacker();
|
|
30768
|
+
for (const patch of options.patches) {
|
|
30769
|
+
packer.push(patch.flag, options.bitsPerFlag);
|
|
30770
|
+
for (const point of patch.points) packPoint(packer, point, options);
|
|
30771
|
+
for (const color of patch.colors) packColor(packer, color, options, nColor);
|
|
30772
|
+
}
|
|
30773
|
+
const data = packer.finish();
|
|
30774
|
+
return require_pdfObjects.PdfStream.fromBytes(data, dict);
|
|
30633
30775
|
}
|
|
30634
|
-
|
|
30635
|
-
|
|
30776
|
+
/**
|
|
30777
|
+
* Build a tensor-product patch mesh shading
|
|
30778
|
+
* (ISO 32000-2 §8.7.4.5.8, /ShadingType 7).
|
|
30779
|
+
*
|
|
30780
|
+
* Identical packing to type 6 but a flag-0 patch carries 16 control points (the
|
|
30781
|
+
* 4 internal tensor points in addition to the 12 boundary points) plus 4 corner
|
|
30782
|
+
* colours; flags 1–3 carry 12 control points and 2 colours.
|
|
30783
|
+
*
|
|
30784
|
+
* @param options - the common mesh keys plus the patch list.
|
|
30785
|
+
* @returns a {@link PdfStream} with /ShadingType 7.
|
|
30786
|
+
*/
|
|
30787
|
+
function buildTensorPatchShading(options) {
|
|
30788
|
+
const dict = buildCommonDict(7, options, true);
|
|
30789
|
+
const nColor = colorComponentCount(options);
|
|
30790
|
+
const packer = new BitPacker();
|
|
30791
|
+
for (const patch of options.patches) {
|
|
30792
|
+
packer.push(patch.flag, options.bitsPerFlag);
|
|
30793
|
+
for (const point of patch.points) packPoint(packer, point, options);
|
|
30794
|
+
for (const color of patch.colors) packColor(packer, color, options, nColor);
|
|
30795
|
+
}
|
|
30796
|
+
const data = packer.finish();
|
|
30797
|
+
return require_pdfObjects.PdfStream.fromBytes(data, dict);
|
|
30636
30798
|
}
|
|
30637
|
-
|
|
30638
|
-
|
|
30639
|
-
|
|
30799
|
+
//#endregion
|
|
30800
|
+
//#region src/color/iccTransform.ts
|
|
30801
|
+
/**
|
|
30802
|
+
* ICC PCS reference white, D50, in XYZ (ICC.1:2010 §6.3.4.3 / Annex A,
|
|
30803
|
+
* encoded white point `[0.9642, 1.0000, 0.8249]`).
|
|
30804
|
+
*/
|
|
30805
|
+
const D50_WHITE = [
|
|
30806
|
+
.9642,
|
|
30807
|
+
1,
|
|
30808
|
+
.8249
|
|
30809
|
+
];
|
|
30810
|
+
/** CIE L\*a\*b\* threshold δ = 6/29 (CIE 15:2004 §8.2.1). */
|
|
30811
|
+
const LAB_DELTA$1 = 6 / 29;
|
|
30812
|
+
/** δ³ — the f(t) breakpoint in the t domain. */
|
|
30813
|
+
const LAB_DELTA_CUBED$1 = LAB_DELTA$1 * LAB_DELTA$1 * LAB_DELTA$1;
|
|
30814
|
+
/** 1 / (3·δ²) — slope of the linear segment of f(t). */
|
|
30815
|
+
const LAB_LINEAR_SLOPE = 1 / (3 * LAB_DELTA$1 * LAB_DELTA$1);
|
|
30816
|
+
/** 4/29 — intercept of the linear segment of f(t). */
|
|
30817
|
+
const LAB_LINEAR_INTERCEPT = 4 / 29;
|
|
30818
|
+
const HEADER_SIZE = 128;
|
|
30819
|
+
const TAG_COUNT_OFFSET = 128;
|
|
30820
|
+
const TAG_TABLE_OFFSET = 132;
|
|
30821
|
+
const TAG_RECORD_SIZE = 12;
|
|
30822
|
+
/** Decode a 4-byte ASCII signature at `off`. */
|
|
30823
|
+
function readSig(data, off) {
|
|
30824
|
+
return String.fromCharCode(data[off], data[off + 1], data[off + 2], data[off + 3]);
|
|
30825
|
+
}
|
|
30826
|
+
/** s15Fixed16Number → JS number (ICC.1:2010 §4.2): signed int32 / 65536. */
|
|
30827
|
+
function readS15Fixed16(view, off) {
|
|
30828
|
+
return view.getInt32(off, false) / 65536;
|
|
30640
30829
|
}
|
|
30641
|
-
|
|
30642
|
-
|
|
30830
|
+
/**
|
|
30831
|
+
* Read the tag table into a `sig → {offset,size}` map.
|
|
30832
|
+
*
|
|
30833
|
+
* @throws if the profile is shorter than a header + tag count, or the tag
|
|
30834
|
+
* table extends past the end of the data.
|
|
30835
|
+
*/
|
|
30836
|
+
function readTagTable(data) {
|
|
30837
|
+
if (data.length < TAG_TABLE_OFFSET) throw new Error(`ICC profile too short: ${data.length} bytes (need at least ${TAG_TABLE_OFFSET}).`);
|
|
30838
|
+
const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
|
|
30839
|
+
const count = view.getUint32(TAG_COUNT_OFFSET, false);
|
|
30840
|
+
if (TAG_TABLE_OFFSET + count * TAG_RECORD_SIZE > data.length) throw new Error(`ICC tag table (${count} tags) overruns profile of ${data.length} bytes.`);
|
|
30841
|
+
const table = /* @__PURE__ */ new Map();
|
|
30842
|
+
for (let i = 0; i < count; i++) {
|
|
30843
|
+
const rec = TAG_TABLE_OFFSET + i * TAG_RECORD_SIZE;
|
|
30844
|
+
const sig = readSig(data, rec);
|
|
30845
|
+
const offset = view.getUint32(rec + 4, false);
|
|
30846
|
+
const size = view.getUint32(rec + 8, false);
|
|
30847
|
+
table.set(sig, {
|
|
30848
|
+
offset,
|
|
30849
|
+
size
|
|
30850
|
+
});
|
|
30851
|
+
}
|
|
30852
|
+
return table;
|
|
30643
30853
|
}
|
|
30644
|
-
|
|
30645
|
-
|
|
30646
|
-
|
|
30647
|
-
|
|
30648
|
-
|
|
30649
|
-
|
|
30650
|
-
|
|
30651
|
-
|
|
30854
|
+
/**
|
|
30855
|
+
* Parse the matrix/TRC-relevant header fields and detect whether the profile
|
|
30856
|
+
* carries a complete matrix/TRC model.
|
|
30857
|
+
*
|
|
30858
|
+
* Reads the 128-byte ICC header (ICC.1:2010 §7.2): version (offset 8),
|
|
30859
|
+
* deviceClass (offset 12), data colour space (offset 16) and PCS (offset 20),
|
|
30860
|
+
* then inspects the tag table (§7.3) for the colorant + TRC tags.
|
|
30861
|
+
*
|
|
30862
|
+
* @param profile - Raw ICC profile bytes.
|
|
30863
|
+
* @returns Parsed {@link IccTransformInfo}.
|
|
30864
|
+
* @throws if the profile is too short to contain a header and tag table.
|
|
30865
|
+
*
|
|
30866
|
+
* @example
|
|
30867
|
+
* ```ts
|
|
30868
|
+
* import { parseIccTransform } from 'modern-pdf-lib';
|
|
30869
|
+
*
|
|
30870
|
+
* const info = parseIccTransform(profileBytes);
|
|
30871
|
+
* if (info.hasMatrixTrc && info.colorSpace === 'RGB ') {
|
|
30872
|
+
* // safe to call deviceRgbToXyz
|
|
30873
|
+
* }
|
|
30874
|
+
* ```
|
|
30875
|
+
*/
|
|
30876
|
+
function parseIccTransform(profile) {
|
|
30877
|
+
if (profile.length < HEADER_SIZE) throw new Error(`ICC profile too short: ${profile.length} bytes (need at least ${HEADER_SIZE}).`);
|
|
30878
|
+
const version = new DataView(profile.buffer, profile.byteOffset, profile.byteLength).getUint32(8, false);
|
|
30879
|
+
const deviceClass = readSig(profile, 12);
|
|
30880
|
+
const colorSpace = readSig(profile, 16);
|
|
30881
|
+
const pcs = readSig(profile, 20);
|
|
30882
|
+
const table = readTagTable(profile);
|
|
30883
|
+
let hasMatrixTrc;
|
|
30884
|
+
if (colorSpace === "GRAY") hasMatrixTrc = table.has("kTRC");
|
|
30885
|
+
else hasMatrixTrc = table.has("rXYZ") && table.has("gXYZ") && table.has("bXYZ") && table.has("rTRC") && table.has("gTRC") && table.has("bTRC");
|
|
30886
|
+
return {
|
|
30887
|
+
version,
|
|
30888
|
+
deviceClass,
|
|
30889
|
+
colorSpace,
|
|
30890
|
+
pcs,
|
|
30891
|
+
hasMatrixTrc
|
|
30892
|
+
};
|
|
30652
30893
|
}
|
|
30653
|
-
|
|
30654
|
-
|
|
30655
|
-
|
|
30656
|
-
|
|
30657
|
-
|
|
30658
|
-
|
|
30659
|
-
|
|
30894
|
+
/**
|
|
30895
|
+
* Evaluate a TRC tag (`curv` or `para`) at a normalised input `x ∈ [0,1]`,
|
|
30896
|
+
* returning the linearised value in `[0,1]`.
|
|
30897
|
+
*
|
|
30898
|
+
* - `curv` (ICC.1:2010 §10.6): `count` u16 follows the 8-byte type header.
|
|
30899
|
+
* `count == 0` → identity; `count == 1` → a single u8Fixed8Number gamma
|
|
30900
|
+
* (Y = X^g); `count >= 2` → a uInt16 sampled curve over the input domain
|
|
30901
|
+
* [0,1], linearly interpolated, output normalised by 65535.
|
|
30902
|
+
* - `para` (ICC.1:2010 §10.18): a u16 function type + s15Fixed16 parameters.
|
|
30903
|
+
* Implements function types 0–4 from the spec.
|
|
30904
|
+
*
|
|
30905
|
+
* @throws if the tag type is neither `curv` nor `para`, or is truncated.
|
|
30906
|
+
*/
|
|
30907
|
+
function evalTrc(data, tag, x) {
|
|
30908
|
+
const start = tag.offset;
|
|
30909
|
+
const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
|
|
30910
|
+
const type = readSig(data, start);
|
|
30911
|
+
const xi = x < 0 ? 0 : x > 1 ? 1 : x;
|
|
30912
|
+
if (type === "curv") {
|
|
30913
|
+
const count = view.getUint32(start + 8, false);
|
|
30914
|
+
if (count === 0) return xi;
|
|
30915
|
+
if (count === 1) return xi ** (view.getUint16(start + 12, false) / 256);
|
|
30916
|
+
const lutStart = start + 12;
|
|
30917
|
+
const pos = xi * (count - 1);
|
|
30918
|
+
const i0 = Math.floor(pos);
|
|
30919
|
+
const i1 = i0 >= count - 1 ? count - 1 : i0 + 1;
|
|
30920
|
+
const frac = pos - i0;
|
|
30921
|
+
const v0 = view.getUint16(lutStart + i0 * 2, false);
|
|
30922
|
+
return (v0 + (view.getUint16(lutStart + i1 * 2, false) - v0) * frac) / 65535;
|
|
30923
|
+
}
|
|
30924
|
+
if (type === "para") {
|
|
30925
|
+
const funcType = view.getUint16(start + 8, false);
|
|
30926
|
+
const p = (i) => readS15Fixed16(view, start + 12 + i * 4);
|
|
30927
|
+
const g = p(0);
|
|
30928
|
+
switch (funcType) {
|
|
30929
|
+
case 0: return xi ** g;
|
|
30930
|
+
case 1: {
|
|
30931
|
+
const a = p(1);
|
|
30932
|
+
const b = p(2);
|
|
30933
|
+
return a * xi + b >= 0 ? (a * xi + b) ** g : 0;
|
|
30934
|
+
}
|
|
30935
|
+
case 2: {
|
|
30936
|
+
const a = p(1);
|
|
30937
|
+
const b = p(2);
|
|
30938
|
+
const c = p(3);
|
|
30939
|
+
return (a * xi + b >= 0 ? (a * xi + b) ** g : 0) + c;
|
|
30940
|
+
}
|
|
30941
|
+
case 3: {
|
|
30942
|
+
const a = p(1);
|
|
30943
|
+
const b = p(2);
|
|
30944
|
+
const c = p(3);
|
|
30945
|
+
return xi >= p(4) ? (a * xi + b) ** g : c * xi;
|
|
30946
|
+
}
|
|
30947
|
+
case 4: {
|
|
30948
|
+
const a = p(1);
|
|
30949
|
+
const b = p(2);
|
|
30950
|
+
const c = p(3);
|
|
30951
|
+
const d = p(4);
|
|
30952
|
+
const e = p(5);
|
|
30953
|
+
const f = p(6);
|
|
30954
|
+
return xi >= d ? (a * xi + b) ** g + e : c * xi + f;
|
|
30955
|
+
}
|
|
30956
|
+
default: throw new Error(`Unsupported parametricCurveType function type ${funcType} (ICC.1:2010 §10.18 defines 0–4).`);
|
|
30957
|
+
}
|
|
30958
|
+
}
|
|
30959
|
+
throw new Error(`Unsupported TRC tag type '${type}': only 'curv' and 'para' are matrix/TRC curves (got a LUT/other type).`);
|
|
30660
30960
|
}
|
|
30661
|
-
|
|
30662
|
-
|
|
30961
|
+
/** Read a 3-component colorant XYZ from an 'XYZ ' tag. */
|
|
30962
|
+
function readXyzTag(data, tag) {
|
|
30963
|
+
const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
|
|
30964
|
+
const type = readSig(data, tag.offset);
|
|
30965
|
+
if (type !== "XYZ ") throw new Error(`Expected an 'XYZ ' colorant tag but found '${type}' (ICC.1:2010 §10.31).`);
|
|
30663
30966
|
return [
|
|
30664
|
-
|
|
30665
|
-
|
|
30666
|
-
|
|
30667
|
-
255
|
|
30967
|
+
readS15Fixed16(view, tag.offset + 8),
|
|
30968
|
+
readS15Fixed16(view, tag.offset + 12),
|
|
30969
|
+
readS15Fixed16(view, tag.offset + 16)
|
|
30668
30970
|
];
|
|
30669
30971
|
}
|
|
30670
|
-
/**
|
|
30671
|
-
|
|
30672
|
-
|
|
30673
|
-
|
|
30674
|
-
|
|
30675
|
-
|
|
30676
|
-
|
|
30677
|
-
|
|
30678
|
-
|
|
30679
|
-
|
|
30680
|
-
|
|
30681
|
-
|
|
30682
|
-
|
|
30683
|
-
|
|
30684
|
-
|
|
30685
|
-
|
|
30972
|
+
/**
|
|
30973
|
+
* Transform a device colour through a **matrix/TRC** ICC profile into the
|
|
30974
|
+
* D50-relative PCS XYZ.
|
|
30975
|
+
*
|
|
30976
|
+
* For RGB (`colorSpace === 'RGB '`): each channel is linearised through its
|
|
30977
|
+
* `rTRC`/`gTRC`/`bTRC` curve, then multiplied by the colorant matrix whose
|
|
30978
|
+
* columns are the `rXYZ`/`gXYZ`/`bXYZ` tags (ICC.1:2010 §E.1.1).
|
|
30979
|
+
*
|
|
30980
|
+
* For GRAY (`colorSpace === 'GRAY'`): the single value is linearised through
|
|
30981
|
+
* `kTRC` and scaled by the media white point (`wtpt`, defaulting to D50).
|
|
30982
|
+
*
|
|
30983
|
+
* The result is **D50-relative XYZ**, matching the ICC PCS reference
|
|
30984
|
+
* illuminant; feed it directly to {@link xyzToLab} (whose default white point
|
|
30985
|
+
* is D50).
|
|
30986
|
+
*
|
|
30987
|
+
* @param profile - Raw ICC profile bytes.
|
|
30988
|
+
* @param rgb - Device colour in `[0,1]`. For gray profiles only the first
|
|
30989
|
+
* component is used.
|
|
30990
|
+
* @returns PCS XYZ `[X, Y, Z]`, D50-relative.
|
|
30991
|
+
* @throws if the profile is **not** matrix/TRC (i.e. it is LUT-based —
|
|
30992
|
+
* `mft1`/`mft2`/`mAB `/`mBA `), or its colour space is unsupported.
|
|
30993
|
+
*
|
|
30994
|
+
* @example
|
|
30995
|
+
* ```ts
|
|
30996
|
+
* import { deviceRgbToXyz } from 'modern-pdf-lib';
|
|
30997
|
+
*
|
|
30998
|
+
* const xyz = deviceRgbToXyz(srgbProfileBytes, [1, 1, 1]);
|
|
30999
|
+
* // ~ [0.9642, 1.0, 0.8249] (D50 white)
|
|
31000
|
+
* ```
|
|
31001
|
+
*/
|
|
31002
|
+
function deviceRgbToXyz(profile, rgb) {
|
|
31003
|
+
const info = parseIccTransform(profile);
|
|
31004
|
+
const table = readTagTable(profile);
|
|
31005
|
+
if (info.colorSpace === "GRAY") {
|
|
31006
|
+
const kTrc = table.get("kTRC");
|
|
31007
|
+
if (!kTrc) throw new Error("Gray ICC profile lacks a 'kTRC' tag; not a matrix/TRC profile.");
|
|
31008
|
+
const lin = evalTrc(profile, kTrc, rgb[0]);
|
|
31009
|
+
const wtptRec = table.get("wtpt");
|
|
31010
|
+
const white = wtptRec ? readXyzTag(profile, wtptRec) : [
|
|
31011
|
+
D50_WHITE[0],
|
|
31012
|
+
D50_WHITE[1],
|
|
31013
|
+
D50_WHITE[2]
|
|
31014
|
+
];
|
|
31015
|
+
return [
|
|
31016
|
+
lin * white[0],
|
|
31017
|
+
lin * white[1],
|
|
31018
|
+
lin * white[2]
|
|
31019
|
+
];
|
|
30686
31020
|
}
|
|
31021
|
+
if (info.colorSpace !== "RGB ") throw new Error(`deviceRgbToXyz supports 'RGB ' and 'GRAY' profiles; got '${info.colorSpace}'.`);
|
|
31022
|
+
if (!info.hasMatrixTrc) throw new Error("ICC profile is not matrix/TRC (LUT-based: mft1/mft2/mAB /mBA ); matrix/TRC transform cannot be applied. Use a CMM/LUT evaluator instead.");
|
|
31023
|
+
const rTrc = table.get("rTRC");
|
|
31024
|
+
const gTrc = table.get("gTRC");
|
|
31025
|
+
const bTrc = table.get("bTRC");
|
|
31026
|
+
const rLin = evalTrc(profile, rTrc, rgb[0]);
|
|
31027
|
+
const gLin = evalTrc(profile, gTrc, rgb[1]);
|
|
31028
|
+
const bLin = evalTrc(profile, bTrc, rgb[2]);
|
|
31029
|
+
const rCol = readXyzTag(profile, table.get("rXYZ"));
|
|
31030
|
+
const gCol = readXyzTag(profile, table.get("gXYZ"));
|
|
31031
|
+
const bCol = readXyzTag(profile, table.get("bXYZ"));
|
|
31032
|
+
return [
|
|
31033
|
+
rCol[0] * rLin + gCol[0] * gLin + bCol[0] * bLin,
|
|
31034
|
+
rCol[1] * rLin + gCol[1] * gLin + bCol[1] * bLin,
|
|
31035
|
+
rCol[2] * rLin + gCol[2] * gLin + bCol[2] * bLin
|
|
31036
|
+
];
|
|
30687
31037
|
}
|
|
30688
31038
|
/**
|
|
30689
|
-
*
|
|
31039
|
+
* CIE L\*a\*b\* nonlinearity f(t) (CIE 15:2004 §8.2.1.1):
|
|
30690
31040
|
*
|
|
30691
|
-
*
|
|
31041
|
+
* ```
|
|
31042
|
+
* f(t) = t^(1/3) for t > δ³
|
|
31043
|
+
* f(t) = t / (3·δ²) + 4/29 for t <= δ³, δ = 6/29
|
|
31044
|
+
* ```
|
|
31045
|
+
*/
|
|
31046
|
+
function labF$1(t) {
|
|
31047
|
+
return t > LAB_DELTA_CUBED$1 ? Math.cbrt(t) : t * LAB_LINEAR_SLOPE + LAB_LINEAR_INTERCEPT;
|
|
31048
|
+
}
|
|
31049
|
+
/**
|
|
31050
|
+
* Convert PCS XYZ to CIE L\*a\*b\* (CIE 15:2004 §8.2.1 / ICC PCS):
|
|
31051
|
+
*
|
|
31052
|
+
* ```
|
|
31053
|
+
* L* = 116·f(Y/Yn) − 16
|
|
31054
|
+
* a* = 500·(f(X/Xn) − f(Y/Yn))
|
|
31055
|
+
* b* = 200·(f(Y/Yn) − f(Z/Zn))
|
|
31056
|
+
* ```
|
|
31057
|
+
*
|
|
31058
|
+
* with f as in {@link labF}. The default reference white is **D50**
|
|
31059
|
+
* `[0.9642, 1.0000, 0.8249]`, matching the ICC PCS (ICC.1:2010 §6.3.4.3), so
|
|
31060
|
+
* XYZ produced by {@link deviceRgbToXyz} maps straight through.
|
|
31061
|
+
*
|
|
31062
|
+
* @param xyz - XYZ tristimulus, relative to `whitePoint`.
|
|
31063
|
+
* @param whitePoint - Reference white `[Xn, Yn, Zn]`. Defaults to D50.
|
|
31064
|
+
* @returns `[L*, a*, b*]`.
|
|
31065
|
+
*
|
|
31066
|
+
* @example
|
|
31067
|
+
* ```ts
|
|
31068
|
+
* import { xyzToLab } from 'modern-pdf-lib';
|
|
31069
|
+
*
|
|
31070
|
+
* xyzToLab([0.9642, 1.0, 0.8249]); // ~ [100, 0, 0] (D50 white)
|
|
31071
|
+
* ```
|
|
31072
|
+
*/
|
|
31073
|
+
function xyzToLab(xyz, whitePoint = [
|
|
31074
|
+
D50_WHITE[0],
|
|
31075
|
+
D50_WHITE[1],
|
|
31076
|
+
D50_WHITE[2]
|
|
31077
|
+
]) {
|
|
31078
|
+
const fx = labF$1(xyz[0] / whitePoint[0]);
|
|
31079
|
+
const fy = labF$1(xyz[1] / whitePoint[1]);
|
|
31080
|
+
const fz = labF$1(xyz[2] / whitePoint[2]);
|
|
31081
|
+
return [
|
|
31082
|
+
116 * fy - 16,
|
|
31083
|
+
500 * (fx - fy),
|
|
31084
|
+
200 * (fy - fz)
|
|
31085
|
+
];
|
|
31086
|
+
}
|
|
31087
|
+
//#endregion
|
|
31088
|
+
//#region src/color/colorConvert.ts
|
|
31089
|
+
/**
|
|
31090
|
+
* @module color/colorConvert
|
|
31091
|
+
*
|
|
31092
|
+
* Pure **device** colour-space conversions — no ICC, no chromatic adaptation
|
|
31093
|
+
* beyond what each standard formula already bakes in. These are the textbook
|
|
31094
|
+
* conversions used for previewing, picking and authoring colour; they are not
|
|
31095
|
+
* a substitute for ICC-based colour management.
|
|
31096
|
+
*
|
|
31097
|
+
* All channels are normalised to `0..1`, with two exceptions that follow the
|
|
31098
|
+
* natural ranges of their spaces:
|
|
31099
|
+
* - **Hue** is in degrees `[0, 360)`.
|
|
31100
|
+
* - **CIE L\*a\*b\*** uses its native ranges: `L ∈ [0, 100]`, `a`/`b`
|
|
31101
|
+
* unbounded (typically `[-128, 127]`).
|
|
31102
|
+
*
|
|
31103
|
+
* ## Verified reference constants
|
|
31104
|
+
*
|
|
31105
|
+
* **sRGB transfer function** — IEC 61966-2-1:1999 (sRGB). The companded↔linear
|
|
31106
|
+
* piecewise curve:
|
|
31107
|
+
* - expand (companded→linear): `c ≤ 0.04045 → c/12.92`,
|
|
31108
|
+
* else `((c + 0.055)/1.055)^2.4`.
|
|
31109
|
+
* - compress (linear→companded): `c ≤ 0.0031308 → 12.92·c`,
|
|
31110
|
+
* else `1.055·c^(1/2.4) − 0.055`.
|
|
31111
|
+
*
|
|
31112
|
+
* **sRGB ↔ XYZ (D65) matrices** — IEC 61966-2-1, as tabulated by Bruce
|
|
31113
|
+
* Lindbloom (sRGB, reference white D65). Forward (linear sRGB → XYZ):
|
|
31114
|
+
* ```
|
|
31115
|
+
* X = 0.4124564 R + 0.3575761 G + 0.1804375 B
|
|
31116
|
+
* Y = 0.2126729 R + 0.7151522 G + 0.0721750 B
|
|
31117
|
+
* Z = 0.0193339 R + 0.1191920 G + 0.9503041 B
|
|
31118
|
+
* ```
|
|
31119
|
+
* Inverse (XYZ → linear sRGB):
|
|
31120
|
+
* ```
|
|
31121
|
+
* R = 3.2404542 X − 1.5371385 Y − 0.4985314 Z
|
|
31122
|
+
* G = −0.9692660 X + 1.8760108 Y + 0.0415560 Z
|
|
31123
|
+
* B = 0.0556434 X − 0.2040259 Y + 1.0572252 Z
|
|
31124
|
+
* ```
|
|
31125
|
+
*
|
|
31126
|
+
* **D65 reference white** — CIE 1931 2° observer, normalised to `Y = 1`:
|
|
31127
|
+
* `Xn = 0.95047, Yn = 1.00000, Zn = 1.08883`.
|
|
31128
|
+
*
|
|
31129
|
+
* **CIE L\*a\*b\*** — CIE 15 colorimetry, using the rational `δ = 6/29`
|
|
31130
|
+
* formulation (numerically identical to the `0.008856 / 903.3` form but
|
|
31131
|
+
* continuous at the join):
|
|
31132
|
+
* ```
|
|
31133
|
+
* f(t) = t^(1/3) if t > δ³
|
|
31134
|
+
* = t/(3δ²) + 4/29 otherwise
|
|
31135
|
+
* L = 116·f(Y/Yn) − 16
|
|
31136
|
+
* a = 500·(f(X/Xn) − f(Y/Yn))
|
|
31137
|
+
* b = 200·(f(Y/Yn) − f(Z/Zn))
|
|
31138
|
+
* ```
|
|
31139
|
+
* with the inverse `f⁻¹(t) = t³` if `t > δ` else `3δ²·(t − 4/29)`.
|
|
31140
|
+
*
|
|
31141
|
+
* > **Note on Lab white point.** This module's `rgbToLab` / `labToRgb` use the
|
|
31142
|
+
* > **D65** PCS that matches the sRGB→XYZ pipeline, per this module's spec.
|
|
31143
|
+
* > That differs from {@link module:core/colorSpacesCIE}'s `labToRgb`, which
|
|
31144
|
+
* > targets the **D50** ICC profile-connection-space convention. The two are
|
|
31145
|
+
* > intentionally distinct and should not be confused.
|
|
31146
|
+
*
|
|
31147
|
+
* No `Buffer` — plain `number` arithmetic and tuples only.
|
|
31148
|
+
*/
|
|
31149
|
+
/**
|
|
31150
|
+
* CIE 1931 2° D65 reference white, normalised so `Yn = 1`.
|
|
31151
|
+
* (IEC 61966-2-1 / CIE 15.)
|
|
31152
|
+
*/
|
|
31153
|
+
const D65_WHITE = [
|
|
31154
|
+
.95047,
|
|
31155
|
+
1,
|
|
31156
|
+
1.08883
|
|
31157
|
+
];
|
|
31158
|
+
/** CIE Lab constant `δ = 6/29`. */
|
|
31159
|
+
const LAB_DELTA = 6 / 29;
|
|
31160
|
+
/** `δ³` — the linear/cubic break point for the forward nonlinearity `f(t)`. */
|
|
31161
|
+
const LAB_DELTA_CUBED = LAB_DELTA * LAB_DELTA * LAB_DELTA;
|
|
31162
|
+
/** `3δ²` — slope of the linear branch. */
|
|
31163
|
+
const LAB_3_DELTA_SQUARED = 3 * LAB_DELTA * LAB_DELTA;
|
|
31164
|
+
/** Clamp `x` to the closed interval `[lo, hi]`. */
|
|
31165
|
+
function clamp(x, lo, hi) {
|
|
31166
|
+
return x < lo ? lo : x > hi ? hi : x;
|
|
31167
|
+
}
|
|
31168
|
+
/** Normalise a hue in degrees into `[0, 360)`. */
|
|
31169
|
+
function normalizeHue(h) {
|
|
31170
|
+
const m = h % 360;
|
|
31171
|
+
return m < 0 ? m + 360 : m;
|
|
31172
|
+
}
|
|
31173
|
+
/**
|
|
31174
|
+
* Convert device RGB to HSL.
|
|
31175
|
+
*
|
|
31176
|
+
* @param r - Red, 0..1.
|
|
31177
|
+
* @param g - Green, 0..1.
|
|
31178
|
+
* @param b - Blue, 0..1.
|
|
31179
|
+
* @returns `[h, s, l]` with `h` in `[0, 360)`, `s`/`l` in `0..1`.
|
|
31180
|
+
*/
|
|
31181
|
+
function rgbToHsl(r, g, b) {
|
|
31182
|
+
const max = Math.max(r, g, b);
|
|
31183
|
+
const min = Math.min(r, g, b);
|
|
31184
|
+
const delta = max - min;
|
|
31185
|
+
const l = (max + min) / 2;
|
|
31186
|
+
if (delta === 0) return [
|
|
31187
|
+
0,
|
|
31188
|
+
0,
|
|
31189
|
+
l
|
|
31190
|
+
];
|
|
31191
|
+
const s = delta / (1 - Math.abs(2 * l - 1));
|
|
31192
|
+
let h;
|
|
31193
|
+
if (max === r) h = (g - b) / delta % 6;
|
|
31194
|
+
else if (max === g) h = (b - r) / delta + 2;
|
|
31195
|
+
else h = (r - g) / delta + 4;
|
|
31196
|
+
h *= 60;
|
|
31197
|
+
return [
|
|
31198
|
+
normalizeHue(h),
|
|
31199
|
+
s,
|
|
31200
|
+
l
|
|
31201
|
+
];
|
|
31202
|
+
}
|
|
31203
|
+
/**
|
|
31204
|
+
* Convert HSL to device RGB.
|
|
31205
|
+
*
|
|
31206
|
+
* @param h - Hue in degrees (any real; wrapped into `[0, 360)`).
|
|
31207
|
+
* @param s - Saturation, 0..1.
|
|
31208
|
+
* @param l - Lightness, 0..1.
|
|
31209
|
+
* @returns `[r, g, b]`, each 0..1.
|
|
31210
|
+
*/
|
|
31211
|
+
function hslToRgb(h, s, l) {
|
|
31212
|
+
const hue = normalizeHue(h);
|
|
31213
|
+
const c = (1 - Math.abs(2 * l - 1)) * s;
|
|
31214
|
+
const x = c * (1 - Math.abs(hue / 60 % 2 - 1));
|
|
31215
|
+
const mAdd = l - c / 2;
|
|
31216
|
+
let r1 = 0;
|
|
31217
|
+
let g1 = 0;
|
|
31218
|
+
let b1 = 0;
|
|
31219
|
+
if (hue < 60) {
|
|
31220
|
+
r1 = c;
|
|
31221
|
+
g1 = x;
|
|
31222
|
+
} else if (hue < 120) {
|
|
31223
|
+
r1 = x;
|
|
31224
|
+
g1 = c;
|
|
31225
|
+
} else if (hue < 180) {
|
|
31226
|
+
g1 = c;
|
|
31227
|
+
b1 = x;
|
|
31228
|
+
} else if (hue < 240) {
|
|
31229
|
+
g1 = x;
|
|
31230
|
+
b1 = c;
|
|
31231
|
+
} else if (hue < 300) {
|
|
31232
|
+
r1 = x;
|
|
31233
|
+
b1 = c;
|
|
31234
|
+
} else {
|
|
31235
|
+
r1 = c;
|
|
31236
|
+
b1 = x;
|
|
31237
|
+
}
|
|
31238
|
+
return [
|
|
31239
|
+
r1 + mAdd,
|
|
31240
|
+
g1 + mAdd,
|
|
31241
|
+
b1 + mAdd
|
|
31242
|
+
];
|
|
31243
|
+
}
|
|
31244
|
+
/**
|
|
31245
|
+
* Convert device RGB to HSV.
|
|
31246
|
+
*
|
|
31247
|
+
* @param r - Red, 0..1.
|
|
31248
|
+
* @param g - Green, 0..1.
|
|
31249
|
+
* @param b - Blue, 0..1.
|
|
31250
|
+
* @returns `[h, s, v]` with `h` in `[0, 360)`, `s`/`v` in `0..1`.
|
|
31251
|
+
*/
|
|
31252
|
+
function rgbToHsv(r, g, b) {
|
|
31253
|
+
const max = Math.max(r, g, b);
|
|
31254
|
+
const delta = max - Math.min(r, g, b);
|
|
31255
|
+
const v = max;
|
|
31256
|
+
if (delta === 0) return [
|
|
31257
|
+
0,
|
|
31258
|
+
0,
|
|
31259
|
+
v
|
|
31260
|
+
];
|
|
31261
|
+
const s = max === 0 ? 0 : delta / max;
|
|
31262
|
+
let h;
|
|
31263
|
+
if (max === r) h = (g - b) / delta % 6;
|
|
31264
|
+
else if (max === g) h = (b - r) / delta + 2;
|
|
31265
|
+
else h = (r - g) / delta + 4;
|
|
31266
|
+
h *= 60;
|
|
31267
|
+
return [
|
|
31268
|
+
normalizeHue(h),
|
|
31269
|
+
s,
|
|
31270
|
+
v
|
|
31271
|
+
];
|
|
31272
|
+
}
|
|
31273
|
+
/**
|
|
31274
|
+
* Convert HSV to device RGB.
|
|
31275
|
+
*
|
|
31276
|
+
* @param h - Hue in degrees (any real; wrapped into `[0, 360)`).
|
|
31277
|
+
* @param s - Saturation, 0..1.
|
|
31278
|
+
* @param v - Value, 0..1.
|
|
31279
|
+
* @returns `[r, g, b]`, each 0..1.
|
|
31280
|
+
*/
|
|
31281
|
+
function hsvToRgb(h, s, v) {
|
|
31282
|
+
const hue = normalizeHue(h);
|
|
31283
|
+
const c = v * s;
|
|
31284
|
+
const x = c * (1 - Math.abs(hue / 60 % 2 - 1));
|
|
31285
|
+
const mAdd = v - c;
|
|
31286
|
+
let r1 = 0;
|
|
31287
|
+
let g1 = 0;
|
|
31288
|
+
let b1 = 0;
|
|
31289
|
+
if (hue < 60) {
|
|
31290
|
+
r1 = c;
|
|
31291
|
+
g1 = x;
|
|
31292
|
+
} else if (hue < 120) {
|
|
31293
|
+
r1 = x;
|
|
31294
|
+
g1 = c;
|
|
31295
|
+
} else if (hue < 180) {
|
|
31296
|
+
g1 = c;
|
|
31297
|
+
b1 = x;
|
|
31298
|
+
} else if (hue < 240) {
|
|
31299
|
+
g1 = x;
|
|
31300
|
+
b1 = c;
|
|
31301
|
+
} else if (hue < 300) {
|
|
31302
|
+
r1 = x;
|
|
31303
|
+
b1 = c;
|
|
31304
|
+
} else {
|
|
31305
|
+
r1 = c;
|
|
31306
|
+
b1 = x;
|
|
31307
|
+
}
|
|
31308
|
+
return [
|
|
31309
|
+
r1 + mAdd,
|
|
31310
|
+
g1 + mAdd,
|
|
31311
|
+
b1 + mAdd
|
|
31312
|
+
];
|
|
31313
|
+
}
|
|
31314
|
+
/** Gamma-expand a companded sRGB channel to linear (IEC 61966-2-1). */
|
|
31315
|
+
function srgbToLinear(c) {
|
|
31316
|
+
return c <= .04045 ? c / 12.92 : ((c + .055) / 1.055) ** 2.4;
|
|
31317
|
+
}
|
|
31318
|
+
/** Gamma-compress a linear channel to companded sRGB (IEC 61966-2-1). */
|
|
31319
|
+
function linearToSrgb(c) {
|
|
31320
|
+
return c <= .0031308 ? 12.92 * c : 1.055 * c ** (1 / 2.4) - .055;
|
|
31321
|
+
}
|
|
31322
|
+
/**
|
|
31323
|
+
* Convert device (sRGB) RGB to CIE XYZ relative to the D65 white point.
|
|
31324
|
+
*
|
|
31325
|
+
* Gamma-expands each channel, then applies the IEC 61966-2-1 sRGB→XYZ D65
|
|
31326
|
+
* matrix. The returned `Y` is `1.0` for input white `(1, 1, 1)`.
|
|
31327
|
+
*
|
|
31328
|
+
* @param r - Red, 0..1.
|
|
31329
|
+
* @param g - Green, 0..1.
|
|
31330
|
+
* @param b - Blue, 0..1.
|
|
31331
|
+
* @returns `[X, Y, Z]` (D65, `Y = 1` for white).
|
|
31332
|
+
*/
|
|
31333
|
+
function rgbToXyz(r, g, b) {
|
|
31334
|
+
const rl = srgbToLinear(r);
|
|
31335
|
+
const gl = srgbToLinear(g);
|
|
31336
|
+
const bl = srgbToLinear(b);
|
|
31337
|
+
return [
|
|
31338
|
+
.4124564 * rl + .3575761 * gl + .1804375 * bl,
|
|
31339
|
+
.2126729 * rl + .7151522 * gl + .072175 * bl,
|
|
31340
|
+
.0193339 * rl + .119192 * gl + .9503041 * bl
|
|
31341
|
+
];
|
|
31342
|
+
}
|
|
31343
|
+
/**
|
|
31344
|
+
* Convert CIE XYZ (D65) to device (sRGB) RGB.
|
|
31345
|
+
*
|
|
31346
|
+
* Applies the inverse sRGB matrix, then gamma-companding. Results are clamped
|
|
31347
|
+
* to `0..1` to keep them in the displayable device gamut.
|
|
31348
|
+
*
|
|
31349
|
+
* @param x - CIE X (D65).
|
|
31350
|
+
* @param y - CIE Y (D65).
|
|
31351
|
+
* @param z - CIE Z (D65).
|
|
31352
|
+
* @returns `[r, g, b]`, each clamped to 0..1.
|
|
31353
|
+
*/
|
|
31354
|
+
function xyzToRgb(x, y, z) {
|
|
31355
|
+
const rl = 3.2404542 * x - 1.5371385 * y - .4985314 * z;
|
|
31356
|
+
const gl = -.969266 * x + 1.8760108 * y + .041556 * z;
|
|
31357
|
+
const bl = .0556434 * x - .2040259 * y + 1.0572252 * z;
|
|
31358
|
+
return [
|
|
31359
|
+
clamp(linearToSrgb(rl), 0, 1),
|
|
31360
|
+
clamp(linearToSrgb(gl), 0, 1),
|
|
31361
|
+
clamp(linearToSrgb(bl), 0, 1)
|
|
31362
|
+
];
|
|
31363
|
+
}
|
|
31364
|
+
/** Forward CIE Lab nonlinearity `f(t)`. */
|
|
31365
|
+
function labF(t) {
|
|
31366
|
+
return t > LAB_DELTA_CUBED ? Math.cbrt(t) : t / LAB_3_DELTA_SQUARED + 4 / 29;
|
|
31367
|
+
}
|
|
31368
|
+
/**
|
|
31369
|
+
* Convert device (sRGB) RGB to CIE L\*a\*b\* via XYZ, using the D65 white
|
|
31370
|
+
* point.
|
|
31371
|
+
*
|
|
31372
|
+
* @param r - Red, 0..1.
|
|
31373
|
+
* @param g - Green, 0..1.
|
|
31374
|
+
* @param b - Blue, 0..1.
|
|
31375
|
+
* @returns `[L, a, b]` with `L ∈ [0, 100]`, `a`/`b` in their natural ranges.
|
|
31376
|
+
*/
|
|
31377
|
+
function rgbToLab(r, g, b) {
|
|
31378
|
+
const [x, y, z] = rgbToXyz(r, g, b);
|
|
31379
|
+
const fx = labF(x / D65_WHITE[0]);
|
|
31380
|
+
const fy = labF(y / D65_WHITE[1]);
|
|
31381
|
+
const fz = labF(z / D65_WHITE[2]);
|
|
31382
|
+
return [
|
|
31383
|
+
116 * fy - 16,
|
|
31384
|
+
500 * (fx - fy),
|
|
31385
|
+
200 * (fy - fz)
|
|
31386
|
+
];
|
|
31387
|
+
}
|
|
31388
|
+
//#endregion
|
|
31389
|
+
//#region src/assets/image/nextGenImageDetect.ts
|
|
31390
|
+
/** AVIF file-level brands (AV1 Image File Format §4). */
|
|
31391
|
+
const AVIF_BRANDS = /* @__PURE__ */ new Set(["avif", "avis"]);
|
|
31392
|
+
/** HEVC-coded HEIF brands (ISO/IEC 23008-12 Annex B). */
|
|
31393
|
+
const HEIC_BRANDS = /* @__PURE__ */ new Set([
|
|
31394
|
+
"heic",
|
|
31395
|
+
"heix",
|
|
31396
|
+
"heim",
|
|
31397
|
+
"heis",
|
|
31398
|
+
"hevc",
|
|
31399
|
+
"hevx"
|
|
31400
|
+
]);
|
|
31401
|
+
/** Generic (codec-agnostic) HEIF brands (ISO/IEC 23008-12 Annex B). */
|
|
31402
|
+
const HEIF_BRANDS = /* @__PURE__ */ new Set(["mif1", "msf1"]);
|
|
31403
|
+
/** Read a 4-byte big-endian unsigned integer at `offset`. */
|
|
31404
|
+
function readU32(bytes, offset) {
|
|
31405
|
+
const b0 = bytes[offset] ?? 0;
|
|
31406
|
+
const b1 = bytes[offset + 1] ?? 0;
|
|
31407
|
+
const b2 = bytes[offset + 2] ?? 0;
|
|
31408
|
+
const b3 = bytes[offset + 3] ?? 0;
|
|
31409
|
+
return b0 * 16777216 + (b1 << 16 | b2 << 8 | b3);
|
|
31410
|
+
}
|
|
31411
|
+
/** Decode a 4-byte FourCC at `offset` as an ASCII string. */
|
|
31412
|
+
function readFourCC(bytes, offset) {
|
|
31413
|
+
return String.fromCharCode(bytes[offset] ?? 0, bytes[offset + 1] ?? 0, bytes[offset + 2] ?? 0, bytes[offset + 3] ?? 0);
|
|
31414
|
+
}
|
|
31415
|
+
/**
|
|
31416
|
+
* Parse an ISOBMFF box header starting at `offset` (ISO/IEC 14496-12 §4.2).
|
|
31417
|
+
*
|
|
31418
|
+
* Handles the standard 32-bit `size`, the 64-bit `largesize` (`size == 1`),
|
|
31419
|
+
* and the to-EOF form (`size == 0`). Returns `null` on a malformed / truncated
|
|
31420
|
+
* header.
|
|
31421
|
+
*/
|
|
31422
|
+
function parseBoxHeader(bytes, offset, end) {
|
|
31423
|
+
if (offset + 8 > end) return null;
|
|
31424
|
+
let size = readU32(bytes, offset);
|
|
31425
|
+
const type = readFourCC(bytes, offset + 4);
|
|
31426
|
+
let headerLen = 8;
|
|
31427
|
+
if (size === 1) {
|
|
31428
|
+
if (offset + 16 > end) return null;
|
|
31429
|
+
const high = readU32(bytes, offset + 8);
|
|
31430
|
+
const low = readU32(bytes, offset + 12);
|
|
31431
|
+
size = high * 4294967296 + low;
|
|
31432
|
+
headerLen = 16;
|
|
31433
|
+
} else if (size === 0) size = end - offset;
|
|
31434
|
+
const boxEnd = offset + size;
|
|
31435
|
+
if (size < headerLen || boxEnd > end) return null;
|
|
31436
|
+
return {
|
|
31437
|
+
type,
|
|
31438
|
+
contentStart: offset + headerLen,
|
|
31439
|
+
boxEnd
|
|
31440
|
+
};
|
|
31441
|
+
}
|
|
31442
|
+
/**
|
|
31443
|
+
* Find a direct child box of a given `type` within [start, end).
|
|
31444
|
+
* Returns the matching {@link BoxHeader} or `null`.
|
|
31445
|
+
*/
|
|
31446
|
+
function findChildBox(bytes, start, end, type) {
|
|
31447
|
+
let cursor = start;
|
|
31448
|
+
while (cursor + 8 <= end) {
|
|
31449
|
+
const header = parseBoxHeader(bytes, cursor, end);
|
|
31450
|
+
if (header === null) return null;
|
|
31451
|
+
if (header.type === type) return header;
|
|
31452
|
+
if (header.boxEnd <= cursor) return null;
|
|
31453
|
+
cursor = header.boxEnd;
|
|
31454
|
+
}
|
|
31455
|
+
return null;
|
|
31456
|
+
}
|
|
31457
|
+
/** Verify a buffer begins with the given byte sequence. */
|
|
31458
|
+
function startsWith(bytes, sig) {
|
|
31459
|
+
if (bytes.length < sig.length) return false;
|
|
31460
|
+
for (let i = 0; i < sig.length; i++) if (bytes[i] !== sig[i]) return false;
|
|
31461
|
+
return true;
|
|
31462
|
+
}
|
|
31463
|
+
/** JPEG XL bare-codestream signature `FF 0A` (ISO/IEC 18181-1). */
|
|
31464
|
+
const JXL_CODESTREAM_SIG = [255, 10];
|
|
31465
|
+
/**
|
|
31466
|
+
* JPEG XL ISOBMFF container signature box:
|
|
31467
|
+
* `00 00 00 0C 'JXL ' 0D 0A 87 0A` (ISO/IEC 18181-1 / 18181-2).
|
|
31468
|
+
*/
|
|
31469
|
+
const JXL_CONTAINER_SIG = [
|
|
31470
|
+
0,
|
|
31471
|
+
0,
|
|
31472
|
+
0,
|
|
31473
|
+
12,
|
|
31474
|
+
74,
|
|
31475
|
+
88,
|
|
31476
|
+
76,
|
|
31477
|
+
32,
|
|
31478
|
+
13,
|
|
31479
|
+
10,
|
|
31480
|
+
135,
|
|
31481
|
+
10
|
|
31482
|
+
];
|
|
31483
|
+
/**
|
|
31484
|
+
* Inspect an ISOBMFF `ftyp` box and classify its brand set into a next-gen
|
|
31485
|
+
* format. Precedence: AVIF > HEIC > HEIF. Returns `null` if no brand matches.
|
|
31486
|
+
*/
|
|
31487
|
+
function classifyFtypBrands(bytes, ftyp) {
|
|
31488
|
+
const brands = [];
|
|
31489
|
+
if (ftyp.contentStart + 4 <= ftyp.boxEnd) brands.push(readFourCC(bytes, ftyp.contentStart));
|
|
31490
|
+
for (let off = ftyp.contentStart + 8; off + 4 <= ftyp.boxEnd; off += 4) brands.push(readFourCC(bytes, off));
|
|
31491
|
+
if (brands.some((b) => AVIF_BRANDS.has(b))) return "avif";
|
|
31492
|
+
if (brands.some((b) => HEIC_BRANDS.has(b))) return "heic";
|
|
31493
|
+
if (brands.some((b) => HEIF_BRANDS.has(b))) return "heif";
|
|
31494
|
+
return null;
|
|
31495
|
+
}
|
|
31496
|
+
/** Locate the top-level `ftyp` box (it must be the first box). */
|
|
31497
|
+
function findFtyp(bytes) {
|
|
31498
|
+
if (bytes.length < 8 || readFourCC(bytes, 4) !== "ftyp") return null;
|
|
31499
|
+
return parseBoxHeader(bytes, 0, bytes.length);
|
|
31500
|
+
}
|
|
31501
|
+
/**
|
|
31502
|
+
* Detect a next-generation image format from raw bytes.
|
|
31503
|
+
*
|
|
31504
|
+
* Recognizes AVIF / HEIC / HEIF via the ISOBMFF `ftyp` brand set, and JPEG XL
|
|
31505
|
+
* via either the bare codestream signature (`FF 0A`) or the container
|
|
31506
|
+
* signature box (`00 00 00 0C 'JXL ' 0D 0A 87 0A`).
|
|
31507
|
+
*
|
|
31508
|
+
* @param bytes Raw image file bytes.
|
|
31509
|
+
* @returns The detected {@link NextGenFormat}, or `null` if unrecognized.
|
|
31510
|
+
*/
|
|
31511
|
+
function detectNextGenFormat(bytes) {
|
|
31512
|
+
if (startsWith(bytes, JXL_CODESTREAM_SIG)) return "jpegxl";
|
|
31513
|
+
if (startsWith(bytes, JXL_CONTAINER_SIG)) return "jpegxl";
|
|
31514
|
+
const ftyp = findFtyp(bytes);
|
|
31515
|
+
if (ftyp === null) return null;
|
|
31516
|
+
return classifyFtypBrands(bytes, ftyp);
|
|
31517
|
+
}
|
|
31518
|
+
/**
|
|
31519
|
+
* Walk an ISOBMFF box tree to `meta` > `iprp` > `ipco` and read the first
|
|
31520
|
+
* `ispe` (dimensions) and `pixi` (bit depth) property boxes.
|
|
31521
|
+
*/
|
|
31522
|
+
function probeIsobmffDimensions(bytes) {
|
|
31523
|
+
const result = {};
|
|
31524
|
+
const ftyp = findFtyp(bytes);
|
|
31525
|
+
if (ftyp === null) return result;
|
|
31526
|
+
const meta = findChildBox(bytes, ftyp.boxEnd, bytes.length, "meta");
|
|
31527
|
+
if (meta === null) return result;
|
|
31528
|
+
const metaChildren = meta.contentStart + 4;
|
|
31529
|
+
if (metaChildren > meta.boxEnd) return result;
|
|
31530
|
+
const iprp = findChildBox(bytes, metaChildren, meta.boxEnd, "iprp");
|
|
31531
|
+
if (iprp === null) return result;
|
|
31532
|
+
const ipco = findChildBox(bytes, iprp.contentStart, iprp.boxEnd, "ipco");
|
|
31533
|
+
if (ipco === null) return result;
|
|
31534
|
+
const ispe = findChildBox(bytes, ipco.contentStart, ipco.boxEnd, "ispe");
|
|
31535
|
+
if (ispe !== null && ispe.contentStart + 12 <= ispe.boxEnd) {
|
|
31536
|
+
result.width = readU32(bytes, ispe.contentStart + 4);
|
|
31537
|
+
result.height = readU32(bytes, ispe.contentStart + 8);
|
|
31538
|
+
}
|
|
31539
|
+
const pixi = findChildBox(bytes, ipco.contentStart, ipco.boxEnd, "pixi");
|
|
31540
|
+
if (pixi !== null && pixi.contentStart + 5 <= pixi.boxEnd) {
|
|
31541
|
+
const numChannels = bytes[pixi.contentStart + 4] ?? 0;
|
|
31542
|
+
if (numChannels > 0 && pixi.contentStart + 5 + numChannels <= pixi.boxEnd) result.bitDepth = bytes[pixi.contentStart + 5] ?? 0;
|
|
31543
|
+
}
|
|
31544
|
+
return result;
|
|
31545
|
+
}
|
|
31546
|
+
const REASON_AVIF = "AVIF detected; pure-JS AV1 decoding is not bundled — decode/convert to PNG or JPEG, or register a WASM decoder.";
|
|
31547
|
+
const REASON_HEIC = "HEIC detected; pure-JS HEVC decoding is not bundled — decode/convert to PNG or JPEG, or register a WASM decoder.";
|
|
31548
|
+
const REASON_HEIF = "HEIF detected; pure-JS HEVC/AV1 decoding is not bundled — decode/convert to PNG or JPEG, or register a WASM decoder.";
|
|
31549
|
+
const REASON_JXL = "JPEG XL detected; pure-JS JPEG XL decoding is not bundled — decode/convert to PNG or JPEG, or register a WASM decoder.";
|
|
31550
|
+
/** Map a detected format to its non-decodable explanation. */
|
|
31551
|
+
function reasonFor(format) {
|
|
31552
|
+
switch (format) {
|
|
31553
|
+
case "avif": return REASON_AVIF;
|
|
31554
|
+
case "heic": return REASON_HEIC;
|
|
31555
|
+
case "heif": return REASON_HEIF;
|
|
31556
|
+
case "jpegxl": return REASON_JXL;
|
|
31557
|
+
}
|
|
31558
|
+
}
|
|
31559
|
+
/**
|
|
31560
|
+
* Probe a next-generation image for container metadata WITHOUT decoding pixels.
|
|
31561
|
+
*
|
|
31562
|
+
* For AVIF / HEIC / HEIF, walks the ISOBMFF box tree
|
|
31563
|
+
* (`meta` > `iprp` > `ipco` > `ispe`/`pixi`) to recover width, height, and
|
|
31564
|
+
* bit depth when present. For JPEG XL, the format is identified but dimensions
|
|
31565
|
+
* are not parsed (the `SizeHeader` is a packed-bit codestream field and is
|
|
31566
|
+
* intentionally scoped out here); only `format` is returned.
|
|
31567
|
+
*
|
|
31568
|
+
* The returned {@link NextGenImageInfo} **always** has `decodable: false`.
|
|
31569
|
+
* No pixel data is ever produced.
|
|
31570
|
+
*
|
|
31571
|
+
* @param bytes Raw image file bytes.
|
|
31572
|
+
* @returns A {@link NextGenImageInfo}, or `null` if not a next-gen image.
|
|
31573
|
+
*/
|
|
31574
|
+
function probeNextGenImage(bytes) {
|
|
31575
|
+
const format = detectNextGenFormat(bytes);
|
|
31576
|
+
if (format === null) return null;
|
|
31577
|
+
const info = {
|
|
31578
|
+
format,
|
|
31579
|
+
decodable: false,
|
|
31580
|
+
reason: reasonFor(format)
|
|
31581
|
+
};
|
|
31582
|
+
if (format === "jpegxl") return info;
|
|
31583
|
+
const dims = probeIsobmffDimensions(bytes);
|
|
31584
|
+
if (dims.width !== void 0) info.width = dims.width;
|
|
31585
|
+
if (dims.height !== void 0) info.height = dims.height;
|
|
31586
|
+
if (dims.bitDepth !== void 0) info.bitDepth = dims.bitDepth;
|
|
31587
|
+
return info;
|
|
31588
|
+
}
|
|
31589
|
+
//#endregion
|
|
31590
|
+
//#region src/assets/image/imageDecoderRegistry.ts
|
|
31591
|
+
/**
|
|
31592
|
+
* The single, module-scoped decoder registry, keyed by normalized format name.
|
|
31593
|
+
*/
|
|
31594
|
+
const registry = /* @__PURE__ */ new Map();
|
|
31595
|
+
/**
|
|
31596
|
+
* Normalize a format key: trim surrounding whitespace and lowercase it, so that
|
|
31597
|
+
* `'AVIF'`, `' avif '`, and `'avif'` all map to the same entry.
|
|
31598
|
+
*
|
|
31599
|
+
* @param format The caller-supplied format key.
|
|
31600
|
+
* @returns The normalized key.
|
|
31601
|
+
*/
|
|
31602
|
+
function normalizeFormat(format) {
|
|
31603
|
+
return format.trim().toLowerCase();
|
|
31604
|
+
}
|
|
31605
|
+
/**
|
|
31606
|
+
* Register a decoder for a given image format.
|
|
31607
|
+
*
|
|
31608
|
+
* If a decoder is already registered for the (normalized) format, it is
|
|
31609
|
+
* replaced.
|
|
31610
|
+
*
|
|
31611
|
+
* @param format Format key, e.g. `'avif'`, `'heic'`, `'jpegxl'`. The key is
|
|
31612
|
+
* normalized (trimmed + lowercased) before storage.
|
|
31613
|
+
* @param decoder The {@link ImageDecoder} that turns encoded bytes into RGBA.
|
|
31614
|
+
*/
|
|
31615
|
+
function registerImageDecoder(format, decoder) {
|
|
31616
|
+
registry.set(normalizeFormat(format), decoder);
|
|
31617
|
+
}
|
|
31618
|
+
/**
|
|
31619
|
+
* Remove the decoder registered for a given format, if any.
|
|
31620
|
+
*
|
|
31621
|
+
* This is a no-op when no decoder is registered for the (normalized) format.
|
|
31622
|
+
*
|
|
31623
|
+
* @param format Format key; normalized (trimmed + lowercased) before lookup.
|
|
31624
|
+
*/
|
|
31625
|
+
function unregisterImageDecoder(format) {
|
|
31626
|
+
registry.delete(normalizeFormat(format));
|
|
31627
|
+
}
|
|
31628
|
+
/**
|
|
31629
|
+
* Report whether a decoder is currently registered for a given format.
|
|
31630
|
+
*
|
|
31631
|
+
* @param format Format key; normalized (trimmed + lowercased) before lookup.
|
|
31632
|
+
* @returns `true` if a decoder is registered, otherwise `false`.
|
|
31633
|
+
*/
|
|
31634
|
+
function hasImageDecoder(format) {
|
|
31635
|
+
return registry.has(normalizeFormat(format));
|
|
31636
|
+
}
|
|
31637
|
+
/**
|
|
31638
|
+
* Get the decoder registered for a given format, if any.
|
|
31639
|
+
*
|
|
31640
|
+
* @param format Format key; normalized (trimmed + lowercased) before lookup.
|
|
31641
|
+
* @returns The registered {@link ImageDecoder}, or `undefined` if none.
|
|
31642
|
+
*/
|
|
31643
|
+
function getImageDecoder(format) {
|
|
31644
|
+
return registry.get(normalizeFormat(format));
|
|
31645
|
+
}
|
|
31646
|
+
/**
|
|
31647
|
+
* Validate that a value returned by a decoder is a well-formed
|
|
31648
|
+
* {@link DecodedRasterImage}.
|
|
31649
|
+
*
|
|
31650
|
+
* Throws a descriptive error (naming the format) if the result is malformed:
|
|
31651
|
+
* not an object, non-positive-integer dimensions, a non-`Uint8Array` `rgba`, or
|
|
31652
|
+
* an `rgba` length that does not equal `width * height * 4`.
|
|
31653
|
+
*
|
|
31654
|
+
* @param format Normalized format key, used only for error messages.
|
|
31655
|
+
* @param result The value returned by the decoder.
|
|
31656
|
+
* @returns The same value, narrowed to {@link DecodedRasterImage}.
|
|
31657
|
+
*/
|
|
31658
|
+
function validateDecodedResult(format, result) {
|
|
31659
|
+
if (result === null || typeof result !== "object") throw new Error(`Image decoder for format "${format}" returned a malformed result: expected an object with { width, height, rgba }, got ${typeof result}.`);
|
|
31660
|
+
const { width, height, rgba } = result;
|
|
31661
|
+
if (typeof width !== "number" || !Number.isInteger(width) || width <= 0) throw new Error(`Image decoder for format "${format}" returned a malformed result: width must be a positive integer, got ${String(width)}.`);
|
|
31662
|
+
if (typeof height !== "number" || !Number.isInteger(height) || height <= 0) throw new Error(`Image decoder for format "${format}" returned a malformed result: height must be a positive integer, got ${String(height)}.`);
|
|
31663
|
+
if (!(rgba instanceof Uint8Array)) throw new Error(`Image decoder for format "${format}" returned a malformed result: rgba must be a Uint8Array, got ${rgba === null ? "null" : typeof rgba}.`);
|
|
31664
|
+
const expected = width * height * 4;
|
|
31665
|
+
if (rgba.length !== expected) throw new Error(`Image decoder for format "${format}" returned a malformed result: rgba length ${rgba.length} does not match width*height*4 = ${width}*${height}*4 = ${expected}.`);
|
|
31666
|
+
return {
|
|
31667
|
+
width,
|
|
31668
|
+
height,
|
|
31669
|
+
rgba
|
|
31670
|
+
};
|
|
31671
|
+
}
|
|
31672
|
+
/**
|
|
31673
|
+
* Decode raw image bytes using a previously-registered decoder for `format`.
|
|
31674
|
+
*
|
|
31675
|
+
* Looks up the decoder by (normalized) format key, awaits its result, validates
|
|
31676
|
+
* the result shape (`rgba.length === width * height * 4`, positive-integer
|
|
31677
|
+
* dimensions, `Uint8Array` pixel buffer), and returns it.
|
|
31678
|
+
*
|
|
31679
|
+
* This function does **not** decode anything itself — it dispatches to the
|
|
31680
|
+
* consumer-supplied {@link ImageDecoder}. If no decoder is registered for the
|
|
31681
|
+
* format, it throws an error naming the format and explaining how to register
|
|
31682
|
+
* one via {@link registerImageDecoder}.
|
|
31683
|
+
*
|
|
31684
|
+
* @param format Format key, e.g. `'avif'`; normalized before lookup.
|
|
31685
|
+
* @param bytes Raw, encoded image bytes, forwarded verbatim to the decoder.
|
|
31686
|
+
* @returns A validated {@link DecodedRasterImage}.
|
|
31687
|
+
* @throws If no decoder is registered for the format, or if the
|
|
31688
|
+
* registered decoder returns a malformed result.
|
|
31689
|
+
*/
|
|
31690
|
+
async function decodeRegisteredImage(format, bytes) {
|
|
31691
|
+
const normalized = normalizeFormat(format);
|
|
31692
|
+
const decoder = registry.get(normalized);
|
|
31693
|
+
if (decoder === void 0) throw new Error(`No image decoder registered for format "${normalized}". This library does not bundle a decoder for this format; supply one with registerImageDecoder(${JSON.stringify(normalized)}, decoder), where decoder turns the encoded bytes into RGBA8888 ({ width, height, rgba }).`);
|
|
31694
|
+
return validateDecodedResult(normalized, await decoder(bytes));
|
|
31695
|
+
}
|
|
31696
|
+
//#endregion
|
|
31697
|
+
//#region src/assets/image/svgFilters.ts
|
|
31698
|
+
/** Clamp a number to the closed interval [0, 255] and round to an integer. */
|
|
31699
|
+
function clamp8(v) {
|
|
31700
|
+
if (v <= 0) return 0;
|
|
31701
|
+
if (v >= 255) return 255;
|
|
31702
|
+
return Math.round(v);
|
|
31703
|
+
}
|
|
31704
|
+
/** Allocate a transparent-black buffer of the given dimensions. */
|
|
31705
|
+
function allocBuffer(width, height) {
|
|
31706
|
+
return {
|
|
31707
|
+
width,
|
|
31708
|
+
height,
|
|
31709
|
+
rgba: new Uint8Array(width * height * 4)
|
|
31710
|
+
};
|
|
31711
|
+
}
|
|
31712
|
+
/** Throw if two buffers differ in dimensions (an op needs them equal). */
|
|
31713
|
+
function assertSameSize(a, b) {
|
|
31714
|
+
if (a.width !== b.width || a.height !== b.height) throw new Error(`svgFilters: buffer size mismatch (${a.width}x${a.height} vs ${b.width}x${b.height})`);
|
|
31715
|
+
}
|
|
31716
|
+
/**
|
|
31717
|
+
* Convert a straight RGBA8888 buffer to a premultiplied Float32Array in the
|
|
31718
|
+
* 0..1 range, channel order R,G,B,A. Premultiplying = colour × alpha.
|
|
31719
|
+
*/
|
|
31720
|
+
function toPremultipliedF32(src) {
|
|
31721
|
+
const n = src.width * src.height;
|
|
31722
|
+
const out = new Float32Array(n * 4);
|
|
31723
|
+
for (let i = 0; i < n; i++) {
|
|
31724
|
+
const a = src.rgba[i * 4 + 3] / 255;
|
|
31725
|
+
out[i * 4 + 0] = src.rgba[i * 4 + 0] / 255 * a;
|
|
31726
|
+
out[i * 4 + 1] = src.rgba[i * 4 + 1] / 255 * a;
|
|
31727
|
+
out[i * 4 + 2] = src.rgba[i * 4 + 2] / 255 * a;
|
|
31728
|
+
out[i * 4 + 3] = a;
|
|
31729
|
+
}
|
|
31730
|
+
return out;
|
|
31731
|
+
}
|
|
31732
|
+
/**
|
|
31733
|
+
* Convert a premultiplied Float32Array (0..1) back to a straight RGBA8888
|
|
31734
|
+
* RasterBuffer, un-premultiplying (colour ÷ alpha) and clamping.
|
|
31735
|
+
*/
|
|
31736
|
+
function fromPremultipliedF32(buf, width, height) {
|
|
31737
|
+
const out = allocBuffer(width, height);
|
|
31738
|
+
const n = width * height;
|
|
31739
|
+
for (let i = 0; i < n; i++) {
|
|
31740
|
+
const a = buf[i * 4 + 3];
|
|
31741
|
+
if (a <= 0) {
|
|
31742
|
+
out.rgba[i * 4 + 0] = 0;
|
|
31743
|
+
out.rgba[i * 4 + 1] = 0;
|
|
31744
|
+
out.rgba[i * 4 + 2] = 0;
|
|
31745
|
+
out.rgba[i * 4 + 3] = 0;
|
|
31746
|
+
} else {
|
|
31747
|
+
out.rgba[i * 4 + 0] = clamp8(buf[i * 4 + 0] / a * 255);
|
|
31748
|
+
out.rgba[i * 4 + 1] = clamp8(buf[i * 4 + 1] / a * 255);
|
|
31749
|
+
out.rgba[i * 4 + 2] = clamp8(buf[i * 4 + 2] / a * 255);
|
|
31750
|
+
out.rgba[i * 4 + 3] = clamp8(a * 255);
|
|
31751
|
+
}
|
|
31752
|
+
}
|
|
31753
|
+
return out;
|
|
31754
|
+
}
|
|
31755
|
+
/**
|
|
31756
|
+
* Produce a buffer filled with a single constant colour.
|
|
31757
|
+
*
|
|
31758
|
+
* Implements `feFlood` (SVG 1.1 §15.12): every pixel is set to the given
|
|
31759
|
+
* straight RGBA8888 colour.
|
|
31760
|
+
*
|
|
31761
|
+
* @param width Output width in pixels (must be > 0).
|
|
31762
|
+
* @param height Output height in pixels (must be > 0).
|
|
31763
|
+
* @param rgba The fill colour as `[R, G, B, A]`, each 0..255.
|
|
31764
|
+
* @returns A new buffer of `width × height` filled with `rgba`.
|
|
31765
|
+
*/
|
|
31766
|
+
function feFlood(width, height, rgba) {
|
|
31767
|
+
if (width <= 0 || height <= 0 || !Number.isInteger(width) || !Number.isInteger(height)) throw new Error(`svgFilters.feFlood: invalid dimensions ${width}x${height}`);
|
|
31768
|
+
const out = allocBuffer(width, height);
|
|
31769
|
+
const r = clamp8(rgba[0]);
|
|
31770
|
+
const g = clamp8(rgba[1]);
|
|
31771
|
+
const b = clamp8(rgba[2]);
|
|
31772
|
+
const a = clamp8(rgba[3]);
|
|
31773
|
+
const n = width * height;
|
|
31774
|
+
for (let i = 0; i < n; i++) {
|
|
31775
|
+
out.rgba[i * 4 + 0] = r;
|
|
31776
|
+
out.rgba[i * 4 + 1] = g;
|
|
31777
|
+
out.rgba[i * 4 + 2] = b;
|
|
31778
|
+
out.rgba[i * 4 + 3] = a;
|
|
31779
|
+
}
|
|
31780
|
+
return out;
|
|
31781
|
+
}
|
|
31782
|
+
/**
|
|
31783
|
+
* Apply a 4×5 colour matrix to every pixel.
|
|
31784
|
+
*
|
|
31785
|
+
* Implements `feColorMatrix` with `type="matrix"` (SVG 1.1 §15.10). The 20
|
|
31786
|
+
* values are row-major:
|
|
31787
|
+
*
|
|
31788
|
+
* ```
|
|
31789
|
+
* | R' | | m0 m1 m2 m3 m4 | | R |
|
|
31790
|
+
* | G' | | m5 m6 m7 m8 m9 | | G |
|
|
31791
|
+
* | B' | = | m10 m11 m12 m13 m14 | · | B |
|
|
31792
|
+
* | A' | | m15 m16 m17 m18 m19 | | A |
|
|
31793
|
+
* | 1 | ( the implicit identity row ) | 1 |
|
|
31794
|
+
* ```
|
|
31795
|
+
*
|
|
31796
|
+
* Channels are normalised to 0..1, the matrix is applied to the **straight**
|
|
31797
|
+
* (un-premultiplied) `[R, G, B, A, 1]` vector, then the result is scaled back
|
|
31798
|
+
* to 0..255 with clamping.
|
|
31799
|
+
*
|
|
31800
|
+
* @param src Source buffer.
|
|
31801
|
+
* @param matrix Exactly 20 numbers (4 rows × 5 columns).
|
|
31802
|
+
* @returns A new buffer with the matrix applied.
|
|
31803
|
+
* @throws If `matrix` does not contain exactly 20 values.
|
|
31804
|
+
*/
|
|
31805
|
+
function feColorMatrix(src, matrix) {
|
|
31806
|
+
if (matrix.length !== 20) throw new Error(`svgFilters.feColorMatrix: matrix must have 20 values, got ${matrix.length}`);
|
|
31807
|
+
const m = matrix;
|
|
31808
|
+
const out = allocBuffer(src.width, src.height);
|
|
31809
|
+
const n = src.width * src.height;
|
|
31810
|
+
for (let i = 0; i < n; i++) {
|
|
31811
|
+
const r = src.rgba[i * 4 + 0] / 255;
|
|
31812
|
+
const g = src.rgba[i * 4 + 1] / 255;
|
|
31813
|
+
const b = src.rgba[i * 4 + 2] / 255;
|
|
31814
|
+
const a = src.rgba[i * 4 + 3] / 255;
|
|
31815
|
+
const nr = m[0] * r + m[1] * g + m[2] * b + m[3] * a + m[4];
|
|
31816
|
+
const ng = m[5] * r + m[6] * g + m[7] * b + m[8] * a + m[9];
|
|
31817
|
+
const nb = m[10] * r + m[11] * g + m[12] * b + m[13] * a + m[14];
|
|
31818
|
+
const na = m[15] * r + m[16] * g + m[17] * b + m[18] * a + m[19];
|
|
31819
|
+
out.rgba[i * 4 + 0] = clamp8(nr * 255);
|
|
31820
|
+
out.rgba[i * 4 + 1] = clamp8(ng * 255);
|
|
31821
|
+
out.rgba[i * 4 + 2] = clamp8(nb * 255);
|
|
31822
|
+
out.rgba[i * 4 + 3] = clamp8(na * 255);
|
|
31823
|
+
}
|
|
31824
|
+
return out;
|
|
31825
|
+
}
|
|
31826
|
+
/**
|
|
31827
|
+
* Apply the `saturate` shorthand colour matrix.
|
|
31828
|
+
*
|
|
31829
|
+
* Implements `feColorMatrix type="saturate"` (SVG 1.1 §15.10). The luma
|
|
31830
|
+
* coefficients are the spec's exact constants 0.213 (R), 0.715 (G),
|
|
31831
|
+
* 0.072 (B). `s = 1` is the identity; `s = 0` fully desaturates each pixel to
|
|
31832
|
+
* its luma (so R = G = B); `s > 1` over-saturates.
|
|
31833
|
+
*
|
|
31834
|
+
* The spec saturate matrix (rows R, G, B; alpha untouched):
|
|
31835
|
+
* ```
|
|
31836
|
+
* | 0.213+0.787s 0.715-0.715s 0.072-0.072s 0 0 |
|
|
31837
|
+
* | 0.213-0.213s 0.715+0.285s 0.072-0.072s 0 0 |
|
|
31838
|
+
* | 0.213-0.213s 0.715-0.715s 0.072+0.928s 0 0 |
|
|
31839
|
+
* | 0 0 0 1 0 |
|
|
31840
|
+
* ```
|
|
31841
|
+
*
|
|
31842
|
+
* @param src Source buffer.
|
|
31843
|
+
* @param s Saturation factor (0 = greyscale, 1 = identity, >1 = boosted).
|
|
31844
|
+
* @returns A new buffer with the saturate matrix applied.
|
|
31845
|
+
*/
|
|
31846
|
+
function feColorMatrixSaturate(src, s) {
|
|
31847
|
+
const rl = .213;
|
|
31848
|
+
const gl = .715;
|
|
31849
|
+
const bl = .072;
|
|
31850
|
+
return feColorMatrix(src, [
|
|
31851
|
+
rl + .787 * s,
|
|
31852
|
+
gl - gl * s,
|
|
31853
|
+
bl - bl * s,
|
|
31854
|
+
0,
|
|
31855
|
+
0,
|
|
31856
|
+
rl - rl * s,
|
|
31857
|
+
gl + .285 * s,
|
|
31858
|
+
bl - bl * s,
|
|
31859
|
+
0,
|
|
31860
|
+
0,
|
|
31861
|
+
rl - rl * s,
|
|
31862
|
+
gl - gl * s,
|
|
31863
|
+
bl + .928 * s,
|
|
31864
|
+
0,
|
|
31865
|
+
0,
|
|
31866
|
+
0,
|
|
31867
|
+
0,
|
|
31868
|
+
0,
|
|
31869
|
+
1,
|
|
31870
|
+
0
|
|
31871
|
+
]);
|
|
31872
|
+
}
|
|
31873
|
+
/**
|
|
31874
|
+
* Shift the image by an integer offset, leaving exposed edges transparent.
|
|
31875
|
+
*
|
|
31876
|
+
* Implements `feOffset` (SVG 1.1 §15.15). Pixels are copied to
|
|
31877
|
+
* `(x + dx, y + dy)`; destinations that fall outside the buffer are dropped
|
|
31878
|
+
* and exposed areas remain transparent black. `dx` / `dy` are rounded to the
|
|
31879
|
+
* nearest integer pixel.
|
|
31880
|
+
*
|
|
31881
|
+
* @param src Source buffer.
|
|
31882
|
+
* @param dx Horizontal offset in pixels (positive = right).
|
|
31883
|
+
* @param dy Vertical offset in pixels (positive = down).
|
|
31884
|
+
* @returns A new buffer of the same size with the shifted content.
|
|
31885
|
+
*/
|
|
31886
|
+
function feOffset(src, dx, dy) {
|
|
31887
|
+
const out = allocBuffer(src.width, src.height);
|
|
31888
|
+
const idx = Math.round(dx);
|
|
31889
|
+
const idy = Math.round(dy);
|
|
31890
|
+
const { width, height } = src;
|
|
31891
|
+
for (let y = 0; y < height; y++) {
|
|
31892
|
+
const sy = y + idy;
|
|
31893
|
+
if (sy < 0 || sy >= height) continue;
|
|
31894
|
+
for (let x = 0; x < width; x++) {
|
|
31895
|
+
const sx = x + idx;
|
|
31896
|
+
if (sx < 0 || sx >= width) continue;
|
|
31897
|
+
const di = (sy * width + sx) * 4;
|
|
31898
|
+
const si = (y * width + x) * 4;
|
|
31899
|
+
out.rgba[di + 0] = src.rgba[si + 0];
|
|
31900
|
+
out.rgba[di + 1] = src.rgba[si + 1];
|
|
31901
|
+
out.rgba[di + 2] = src.rgba[si + 2];
|
|
31902
|
+
out.rgba[di + 3] = src.rgba[si + 3];
|
|
31903
|
+
}
|
|
31904
|
+
}
|
|
31905
|
+
return out;
|
|
31906
|
+
}
|
|
31907
|
+
/**
|
|
31908
|
+
* Compute the box size `d` for the three-box-blur approximation.
|
|
31909
|
+
*
|
|
31910
|
+
* SVG 1.1 §15.17: `d = floor(s · 3 · √(2π) / 4 + 0.5)`.
|
|
31911
|
+
*/
|
|
31912
|
+
function boxBlurD(stdDev) {
|
|
31913
|
+
return Math.floor(stdDev * 3 * Math.sqrt(2 * Math.PI) / 4 + .5);
|
|
31914
|
+
}
|
|
31915
|
+
/**
|
|
31916
|
+
* One horizontal box blur of the given (odd) length, premultiplied float
|
|
31917
|
+
* data, with an asymmetric left/right radius to support the even-`d` rule.
|
|
31918
|
+
* The window covers offsets `[-leftRadius, +rightRadius]` inclusive. Edge
|
|
31919
|
+
* samples are clamped (extend-edge) rather than treated as transparent so a
|
|
31920
|
+
* flat region stays flat.
|
|
31921
|
+
*/
|
|
31922
|
+
function boxBlurH(data, width, height, leftRadius, rightRadius) {
|
|
31923
|
+
const out = new Float32Array(data.length);
|
|
31924
|
+
const windowLen = leftRadius + rightRadius + 1;
|
|
31925
|
+
for (let y = 0; y < height; y++) {
|
|
31926
|
+
const row = y * width;
|
|
31927
|
+
for (let c = 0; c < 4; c++) {
|
|
31928
|
+
let sum = 0;
|
|
31929
|
+
for (let k = -leftRadius; k <= rightRadius; k++) {
|
|
31930
|
+
const cx = k < 0 ? 0 : k >= width ? width - 1 : k;
|
|
31931
|
+
sum += data[(row + cx) * 4 + c];
|
|
31932
|
+
}
|
|
31933
|
+
for (let x = 0; x < width; x++) {
|
|
31934
|
+
out[(row + x) * 4 + c] = sum / windowLen;
|
|
31935
|
+
const dropX = x - leftRadius;
|
|
31936
|
+
const addX = x + rightRadius + 1;
|
|
31937
|
+
const dc = dropX < 0 ? 0 : dropX >= width ? width - 1 : dropX;
|
|
31938
|
+
const ac = addX < 0 ? 0 : addX >= width ? width - 1 : addX;
|
|
31939
|
+
sum += data[(row + ac) * 4 + c] - data[(row + dc) * 4 + c];
|
|
31940
|
+
}
|
|
31941
|
+
}
|
|
31942
|
+
}
|
|
31943
|
+
return out;
|
|
31944
|
+
}
|
|
31945
|
+
/** One vertical box blur — the transpose of {@link boxBlurH}. */
|
|
31946
|
+
function boxBlurV(data, width, height, topRadius, bottomRadius) {
|
|
31947
|
+
const out = new Float32Array(data.length);
|
|
31948
|
+
const windowLen = topRadius + bottomRadius + 1;
|
|
31949
|
+
for (let x = 0; x < width; x++) for (let c = 0; c < 4; c++) {
|
|
31950
|
+
let sum = 0;
|
|
31951
|
+
for (let k = -topRadius; k <= bottomRadius; k++) {
|
|
31952
|
+
const cy = k < 0 ? 0 : k >= height ? height - 1 : k;
|
|
31953
|
+
sum += data[(cy * width + x) * 4 + c];
|
|
31954
|
+
}
|
|
31955
|
+
for (let y = 0; y < height; y++) {
|
|
31956
|
+
out[(y * width + x) * 4 + c] = sum / windowLen;
|
|
31957
|
+
const dropY = y - topRadius;
|
|
31958
|
+
const addY = y + bottomRadius + 1;
|
|
31959
|
+
const dc = dropY < 0 ? 0 : dropY >= height ? height - 1 : dropY;
|
|
31960
|
+
const ac = addY < 0 ? 0 : addY >= height ? height - 1 : addY;
|
|
31961
|
+
sum += data[(ac * width + x) * 4 + c] - data[(dc * width + x) * 4 + c];
|
|
31962
|
+
}
|
|
31963
|
+
}
|
|
31964
|
+
return out;
|
|
31965
|
+
}
|
|
31966
|
+
/**
|
|
31967
|
+
* Apply the three successive box blurs that approximate a Gaussian along one
|
|
31968
|
+
* axis, following the SVG 1.1 §15.17 odd/even centring rule for box size `d`.
|
|
31969
|
+
*
|
|
31970
|
+
* - `d` odd: three box blurs of size `d`, centred on the output pixel
|
|
31971
|
+
* (`radius = (d-1)/2` each side).
|
|
31972
|
+
* - `d` even: two box blurs of size `d` (one offset left-of-centre, one
|
|
31973
|
+
* right-of-centre) plus one box blur of size `d + 1` centred.
|
|
31974
|
+
* - `d <= 1`: identity (no perceivable blur on this axis).
|
|
31975
|
+
*/
|
|
31976
|
+
function threeBoxBlurAxis(data, width, height, d, horizontal) {
|
|
31977
|
+
if (d <= 1) return data;
|
|
31978
|
+
const blur = horizontal ? boxBlurH : boxBlurV;
|
|
31979
|
+
if (d % 2 === 1) {
|
|
31980
|
+
const r = (d - 1) / 2;
|
|
31981
|
+
let cur = blur(data, width, height, r, r);
|
|
31982
|
+
cur = blur(cur, width, height, r, r);
|
|
31983
|
+
cur = blur(cur, width, height, r, r);
|
|
31984
|
+
return cur;
|
|
31985
|
+
}
|
|
31986
|
+
const half = d / 2;
|
|
31987
|
+
let cur = blur(data, width, height, half, half - 1);
|
|
31988
|
+
cur = blur(cur, width, height, half - 1, half);
|
|
31989
|
+
cur = blur(cur, width, height, half, half);
|
|
31990
|
+
return cur;
|
|
31991
|
+
}
|
|
31992
|
+
/**
|
|
31993
|
+
* Approximate a Gaussian blur via three successive box blurs.
|
|
31994
|
+
*
|
|
31995
|
+
* Implements `feGaussianBlur` (SVG 1.1 §15.17). The blur is performed in
|
|
31996
|
+
* **premultiplied** colour space (the correct space for averaging colours
|
|
31997
|
+
* with varying alpha) and un-premultiplied on output. A standard deviation of
|
|
31998
|
+
* 0 on an axis is a no-op for that axis.
|
|
31999
|
+
*
|
|
32000
|
+
* The per-axis box size is `d = floor(s · 3 · √(2π) / 4 + 0.5)` with the
|
|
32001
|
+
* spec's odd/even centring rule.
|
|
32002
|
+
*
|
|
32003
|
+
* @param src Source buffer.
|
|
32004
|
+
* @param stdDevX Standard deviation along X (>= 0).
|
|
32005
|
+
* @param stdDevY Standard deviation along Y; defaults to `stdDevX`.
|
|
32006
|
+
* @returns A new, blurred buffer of the same size.
|
|
32007
|
+
* @throws If either standard deviation is negative.
|
|
32008
|
+
*/
|
|
32009
|
+
function feGaussianBlur(src, stdDevX, stdDevY) {
|
|
32010
|
+
const sx = stdDevX;
|
|
32011
|
+
const sy = stdDevY ?? stdDevX;
|
|
32012
|
+
if (sx < 0 || sy < 0 || Number.isNaN(sx) || Number.isNaN(sy)) throw new Error(`svgFilters.feGaussianBlur: standard deviation must be >= 0 (got ${sx}, ${sy})`);
|
|
32013
|
+
if (sx === 0 && sy === 0) return {
|
|
32014
|
+
width: src.width,
|
|
32015
|
+
height: src.height,
|
|
32016
|
+
rgba: src.rgba.slice()
|
|
32017
|
+
};
|
|
32018
|
+
let data = toPremultipliedF32(src);
|
|
32019
|
+
if (sx > 0) data = threeBoxBlurAxis(data, src.width, src.height, boxBlurD(sx), true);
|
|
32020
|
+
if (sy > 0) data = threeBoxBlurAxis(data, src.width, src.height, boxBlurD(sy), false);
|
|
32021
|
+
return fromPremultipliedF32(data, src.width, src.height);
|
|
32022
|
+
}
|
|
32023
|
+
/**
|
|
32024
|
+
* Composite two buffers with a Porter-Duff operator.
|
|
32025
|
+
*
|
|
32026
|
+
* Implements `feComposite` with operators `over`, `in`, `out`, `atop`, `xor`
|
|
32027
|
+
* (SVG 1.1 §15.13; Porter & Duff, SIGGRAPH 1984). The general form on
|
|
32028
|
+
* premultiplied colour is:
|
|
32029
|
+
*
|
|
32030
|
+
* ```
|
|
32031
|
+
* cr = ca · Fa + cb · Fb
|
|
32032
|
+
* ar = aa · Fa + ab · Fb
|
|
32033
|
+
* ```
|
|
32034
|
+
*
|
|
32035
|
+
* with coefficients (`aa`, `ab` = source/destination alpha):
|
|
32036
|
+
*
|
|
32037
|
+
* | op | Fa | Fb |
|
|
32038
|
+
* | ---- | --------- | --------- |
|
|
32039
|
+
* | over | 1 | 1 − aa |
|
|
32040
|
+
* | in | ab | 0 |
|
|
32041
|
+
* | out | 1 − ab | 0 |
|
|
32042
|
+
* | atop | ab | 1 − aa |
|
|
32043
|
+
* | xor | 1 − ab | 1 − aa |
|
|
32044
|
+
*
|
|
32045
|
+
* Here `a` is the source (foreground) and `b` is the destination
|
|
32046
|
+
* (background). Both buffers must be the same size.
|
|
32047
|
+
*
|
|
32048
|
+
* @param a Source (foreground) buffer.
|
|
32049
|
+
* @param b Destination (background) buffer.
|
|
32050
|
+
* @param op Porter-Duff operator.
|
|
32051
|
+
* @returns A new composited buffer of the same size.
|
|
32052
|
+
* @throws If the buffers differ in size.
|
|
32053
|
+
*/
|
|
32054
|
+
function feComposite(a, b, op) {
|
|
32055
|
+
assertSameSize(a, b);
|
|
32056
|
+
const out = allocBuffer(a.width, a.height);
|
|
32057
|
+
const n = a.width * a.height;
|
|
32058
|
+
for (let i = 0; i < n; i++) {
|
|
32059
|
+
const aa = a.rgba[i * 4 + 3] / 255;
|
|
32060
|
+
const ab = b.rgba[i * 4 + 3] / 255;
|
|
32061
|
+
const ar = a.rgba[i * 4 + 0] / 255 * aa;
|
|
32062
|
+
const ag = a.rgba[i * 4 + 1] / 255 * aa;
|
|
32063
|
+
const abl = a.rgba[i * 4 + 2] / 255 * aa;
|
|
32064
|
+
const br = b.rgba[i * 4 + 0] / 255 * ab;
|
|
32065
|
+
const bg = b.rgba[i * 4 + 1] / 255 * ab;
|
|
32066
|
+
const bb = b.rgba[i * 4 + 2] / 255 * ab;
|
|
32067
|
+
let fa;
|
|
32068
|
+
let fb;
|
|
32069
|
+
switch (op) {
|
|
32070
|
+
case "over":
|
|
32071
|
+
fa = 1;
|
|
32072
|
+
fb = 1 - aa;
|
|
32073
|
+
break;
|
|
32074
|
+
case "in":
|
|
32075
|
+
fa = ab;
|
|
32076
|
+
fb = 0;
|
|
32077
|
+
break;
|
|
32078
|
+
case "out":
|
|
32079
|
+
fa = 1 - ab;
|
|
32080
|
+
fb = 0;
|
|
32081
|
+
break;
|
|
32082
|
+
case "atop":
|
|
32083
|
+
fa = ab;
|
|
32084
|
+
fb = 1 - aa;
|
|
32085
|
+
break;
|
|
32086
|
+
case "xor":
|
|
32087
|
+
fa = 1 - ab;
|
|
32088
|
+
fb = 1 - aa;
|
|
32089
|
+
break;
|
|
32090
|
+
default: throw new Error(`svgFilters.feComposite: unknown operator ${String(op)}`);
|
|
32091
|
+
}
|
|
32092
|
+
const cr = ar * fa + br * fb;
|
|
32093
|
+
const cg = ag * fa + bg * fb;
|
|
32094
|
+
const cb = abl * fa + bb * fb;
|
|
32095
|
+
const car = aa * fa + ab * fb;
|
|
32096
|
+
if (car <= 0) {
|
|
32097
|
+
out.rgba[i * 4 + 0] = 0;
|
|
32098
|
+
out.rgba[i * 4 + 1] = 0;
|
|
32099
|
+
out.rgba[i * 4 + 2] = 0;
|
|
32100
|
+
out.rgba[i * 4 + 3] = 0;
|
|
32101
|
+
} else {
|
|
32102
|
+
out.rgba[i * 4 + 0] = clamp8(cr / car * 255);
|
|
32103
|
+
out.rgba[i * 4 + 1] = clamp8(cg / car * 255);
|
|
32104
|
+
out.rgba[i * 4 + 2] = clamp8(cb / car * 255);
|
|
32105
|
+
out.rgba[i * 4 + 3] = clamp8(car * 255);
|
|
32106
|
+
}
|
|
32107
|
+
}
|
|
32108
|
+
return out;
|
|
32109
|
+
}
|
|
32110
|
+
/**
|
|
32111
|
+
* Blend two buffers with one of the basic SVG 1.1 blend modes.
|
|
32112
|
+
*
|
|
32113
|
+
* Implements `feBlend` (SVG 1.1 §15.11). The spec defines the result on
|
|
32114
|
+
* **premultiplied** colour with `qa` = source alpha, `qb` = destination
|
|
32115
|
+
* alpha:
|
|
32116
|
+
*
|
|
32117
|
+
* - normal: `cr = (1 − qa)·cb + ca`
|
|
32118
|
+
* - multiply: `cr = (1 − qa)·cb + (1 − qb)·ca + ca·cb`
|
|
32119
|
+
* - screen: `cr = cb + ca − ca·cb`
|
|
32120
|
+
* - darken: `cr = min((1 − qa)·cb + ca, (1 − qb)·ca + cb)`
|
|
32121
|
+
* - lighten: `cr = max((1 − qa)·cb + ca, (1 − qb)·ca + cb)`
|
|
32122
|
+
*
|
|
32123
|
+
* with the common result alpha `ar = 1 − (1 − qa)·(1 − qb)`. Inputs `a`
|
|
32124
|
+
* (source) and `b` (destination) must be the same size.
|
|
32125
|
+
*
|
|
32126
|
+
* @param a Source (top) buffer.
|
|
32127
|
+
* @param b Destination (bottom) buffer.
|
|
32128
|
+
* @param mode Blend mode.
|
|
32129
|
+
* @returns A new blended buffer of the same size.
|
|
32130
|
+
* @throws If the buffers differ in size.
|
|
32131
|
+
*/
|
|
32132
|
+
function feBlend(a, b, mode) {
|
|
32133
|
+
assertSameSize(a, b);
|
|
32134
|
+
const out = allocBuffer(a.width, a.height);
|
|
32135
|
+
const n = a.width * a.height;
|
|
32136
|
+
for (let i = 0; i < n; i++) {
|
|
32137
|
+
const qa = a.rgba[i * 4 + 3] / 255;
|
|
32138
|
+
const qb = b.rgba[i * 4 + 3] / 255;
|
|
32139
|
+
const ar = 1 - (1 - qa) * (1 - qb);
|
|
32140
|
+
out.rgba[i * 4 + 3] = clamp8(ar * 255);
|
|
32141
|
+
for (let c = 0; c < 3; c++) {
|
|
32142
|
+
const ca = a.rgba[i * 4 + c] / 255 * qa;
|
|
32143
|
+
const cb = b.rgba[i * 4 + c] / 255 * qb;
|
|
32144
|
+
let cr;
|
|
32145
|
+
switch (mode) {
|
|
32146
|
+
case "normal":
|
|
32147
|
+
cr = (1 - qa) * cb + ca;
|
|
32148
|
+
break;
|
|
32149
|
+
case "multiply":
|
|
32150
|
+
cr = (1 - qa) * cb + (1 - qb) * ca + ca * cb;
|
|
32151
|
+
break;
|
|
32152
|
+
case "screen":
|
|
32153
|
+
cr = cb + ca - ca * cb;
|
|
32154
|
+
break;
|
|
32155
|
+
case "darken":
|
|
32156
|
+
cr = Math.min((1 - qa) * cb + ca, (1 - qb) * ca + cb);
|
|
32157
|
+
break;
|
|
32158
|
+
case "lighten":
|
|
32159
|
+
cr = Math.max((1 - qa) * cb + ca, (1 - qb) * ca + cb);
|
|
32160
|
+
break;
|
|
32161
|
+
default: throw new Error(`svgFilters.feBlend: unknown mode ${String(mode)}`);
|
|
32162
|
+
}
|
|
32163
|
+
if (ar <= 0) out.rgba[i * 4 + c] = 0;
|
|
32164
|
+
else out.rgba[i * 4 + c] = clamp8(cr / ar * 255);
|
|
32165
|
+
}
|
|
32166
|
+
}
|
|
32167
|
+
return out;
|
|
32168
|
+
}
|
|
32169
|
+
//#endregion
|
|
32170
|
+
//#region src/render/matrix.ts
|
|
32171
|
+
/** The identity transform. */
|
|
32172
|
+
function identity() {
|
|
32173
|
+
return [
|
|
32174
|
+
1,
|
|
32175
|
+
0,
|
|
32176
|
+
0,
|
|
32177
|
+
1,
|
|
32178
|
+
0,
|
|
32179
|
+
0
|
|
32180
|
+
];
|
|
32181
|
+
}
|
|
32182
|
+
/**
|
|
32183
|
+
* Concatenate two transforms: the result applies `m` **first**, then `n`
|
|
32184
|
+
* (i.e. `point · m · n`). This matches the PDF `cm` operator, where the new
|
|
32185
|
+
* CTM is `cmMatrix × CTM`.
|
|
32186
|
+
*/
|
|
32187
|
+
function multiply(m, n) {
|
|
32188
|
+
return [
|
|
32189
|
+
m[0] * n[0] + m[1] * n[2],
|
|
32190
|
+
m[0] * n[1] + m[1] * n[3],
|
|
32191
|
+
m[2] * n[0] + m[3] * n[2],
|
|
32192
|
+
m[2] * n[1] + m[3] * n[3],
|
|
32193
|
+
m[4] * n[0] + m[5] * n[2] + n[4],
|
|
32194
|
+
m[4] * n[1] + m[5] * n[3] + n[5]
|
|
32195
|
+
];
|
|
32196
|
+
}
|
|
32197
|
+
/** Apply a transform to a point, returning the transformed `[x, y]`. */
|
|
32198
|
+
function applyToPoint(m, x, y) {
|
|
32199
|
+
return [m[0] * x + m[2] * y + m[4], m[1] * x + m[3] * y + m[5]];
|
|
32200
|
+
}
|
|
32201
|
+
/** A pure translation transform. */
|
|
32202
|
+
function translation(tx, ty) {
|
|
32203
|
+
return [
|
|
32204
|
+
1,
|
|
32205
|
+
0,
|
|
32206
|
+
0,
|
|
32207
|
+
1,
|
|
32208
|
+
tx,
|
|
32209
|
+
ty
|
|
32210
|
+
];
|
|
32211
|
+
}
|
|
32212
|
+
/**
|
|
32213
|
+
* The magnitude by which `m` scales lengths — the geometric mean of the
|
|
32214
|
+
* x- and y-axis scale factors (`sqrt(|det|)`). Used to map a user-space line
|
|
32215
|
+
* width to device space.
|
|
32216
|
+
*/
|
|
32217
|
+
function meanScale(m) {
|
|
32218
|
+
const det = m[0] * m[3] - m[1] * m[2];
|
|
32219
|
+
return Math.sqrt(Math.abs(det));
|
|
32220
|
+
}
|
|
32221
|
+
//#endregion
|
|
32222
|
+
//#region src/render/interpreter.ts
|
|
32223
|
+
/**
|
|
32224
|
+
* @module render/interpreter
|
|
32225
|
+
*
|
|
32226
|
+
* Executes a parsed PDF content stream through a graphics-state machine,
|
|
32227
|
+
* producing a resolution-independent {@link DisplayList} (page space, y-up).
|
|
32228
|
+
* This is the foundation the rasterizer and Canvas adapter render from.
|
|
32229
|
+
*
|
|
32230
|
+
* Supported operators: graphics state (`q`/`Q`/`cm`/`w`/`J`/`j`/`gs`), path
|
|
32231
|
+
* construction (`m`/`l`/`c`/`v`/`y`/`re`/`h`), path painting
|
|
32232
|
+
* (`f`/`F`/`f*`/`S`/`s`/`B`/`B*`/`b`/`b*`/`n`), clipping (`W`/`W*`), color
|
|
32233
|
+
* (`rg`/`RG`/`g`/`G`/`k`/`K`/`cs`/`CS`/`sc`/`scn`/`SC`/`SCN`), text
|
|
32234
|
+
* (`BT`/`ET`/`Tf`/`Td`/`TD`/`Tm`/`T*`/`Tc`/`Tw`/`Tz`/`TL`/`Ts`/`Tr`/`Tj`/`TJ`/`'`/`"`),
|
|
32235
|
+
* and XObjects (`Do`, recursing into form XObjects). Bézier curves are
|
|
32236
|
+
* flattened to polylines.
|
|
32237
|
+
*
|
|
32238
|
+
* @packageDocumentation
|
|
32239
|
+
*/
|
|
32240
|
+
function initialState$1() {
|
|
32241
|
+
return {
|
|
32242
|
+
ctm: identity(),
|
|
32243
|
+
fill: [
|
|
32244
|
+
0,
|
|
32245
|
+
0,
|
|
32246
|
+
0,
|
|
32247
|
+
255
|
|
32248
|
+
],
|
|
32249
|
+
stroke: [
|
|
32250
|
+
0,
|
|
32251
|
+
0,
|
|
32252
|
+
0,
|
|
32253
|
+
255
|
|
32254
|
+
],
|
|
32255
|
+
lineWidth: 1,
|
|
32256
|
+
lineCap: 0,
|
|
32257
|
+
lineJoin: 0,
|
|
32258
|
+
fillAlpha: 1,
|
|
32259
|
+
strokeAlpha: 1,
|
|
32260
|
+
clip: void 0,
|
|
32261
|
+
font: void 0,
|
|
32262
|
+
fontSize: 0,
|
|
32263
|
+
charSpace: 0,
|
|
32264
|
+
wordSpace: 0,
|
|
32265
|
+
hScale: 1,
|
|
32266
|
+
leading: 0,
|
|
32267
|
+
rise: 0,
|
|
32268
|
+
renderMode: 0
|
|
32269
|
+
};
|
|
32270
|
+
}
|
|
32271
|
+
function cloneState(s) {
|
|
32272
|
+
return { ...s };
|
|
32273
|
+
}
|
|
32274
|
+
function num$2(op) {
|
|
32275
|
+
return typeof op === "number" ? op : 0;
|
|
32276
|
+
}
|
|
32277
|
+
function nameOf(op) {
|
|
32278
|
+
if (op instanceof require_pdfObjects.PdfName) return op.value.replace(/^\//, "");
|
|
32279
|
+
if (typeof op === "string") return op.replace(/^\//, "");
|
|
32280
|
+
}
|
|
32281
|
+
function clamp255(v) {
|
|
32282
|
+
return Math.max(0, Math.min(255, Math.round(v * 255)));
|
|
32283
|
+
}
|
|
32284
|
+
function grayRgba(g) {
|
|
32285
|
+
const v = clamp255(g);
|
|
32286
|
+
return [
|
|
32287
|
+
v,
|
|
32288
|
+
v,
|
|
32289
|
+
v,
|
|
32290
|
+
255
|
|
32291
|
+
];
|
|
32292
|
+
}
|
|
32293
|
+
function rgbRgba(r, g, b) {
|
|
32294
|
+
return [
|
|
32295
|
+
clamp255(r),
|
|
32296
|
+
clamp255(g),
|
|
32297
|
+
clamp255(b),
|
|
32298
|
+
255
|
|
32299
|
+
];
|
|
32300
|
+
}
|
|
32301
|
+
function cmykRgba(c, m, y, k) {
|
|
32302
|
+
const [r, g, b] = require_pdfDocument.cmykToRgb(c, m, y, k);
|
|
32303
|
+
return [
|
|
32304
|
+
clamp255(r),
|
|
32305
|
+
clamp255(g),
|
|
32306
|
+
clamp255(b),
|
|
32307
|
+
255
|
|
32308
|
+
];
|
|
32309
|
+
}
|
|
32310
|
+
/** Interpret `sc`/`scn` numeric components by arity → RGBA. */
|
|
32311
|
+
function componentsToRgba(comps) {
|
|
32312
|
+
if (comps.length === 1) return grayRgba(comps[0]);
|
|
32313
|
+
if (comps.length === 3) return rgbRgba(comps[0], comps[1], comps[2]);
|
|
32314
|
+
if (comps.length === 4) return cmykRgba(comps[0], comps[1], comps[2], comps[3]);
|
|
32315
|
+
}
|
|
32316
|
+
const BEZIER_STEPS = 16;
|
|
32317
|
+
function flattenCubic(out, p0, p1, p2, p3) {
|
|
32318
|
+
for (let i = 1; i <= BEZIER_STEPS; i++) {
|
|
32319
|
+
const t = i / BEZIER_STEPS;
|
|
32320
|
+
const mt = 1 - t;
|
|
32321
|
+
const a = mt * mt * mt;
|
|
32322
|
+
const b = 3 * mt * mt * t;
|
|
32323
|
+
const c = 3 * mt * t * t;
|
|
32324
|
+
const d = t * t * t;
|
|
32325
|
+
out.push(a * p0[0] + b * p1[0] + c * p2[0] + d * p3[0], a * p0[1] + b * p1[1] + c * p2[1] + d * p3[1]);
|
|
32326
|
+
}
|
|
32327
|
+
}
|
|
32328
|
+
/**
|
|
32329
|
+
* Execute a parsed content stream into a {@link DisplayList}.
|
|
32330
|
+
*
|
|
32331
|
+
* @param operators - Output of {@link parseContentStream}.
|
|
30692
32332
|
* @param options - Page dimensions and (optional) resources/registry.
|
|
30693
32333
|
*/
|
|
30694
32334
|
function interpretContentStream(operators, options) {
|
|
@@ -34876,6 +36516,12 @@ Object.defineProperty(exports, "buildColorKeyMask", {
|
|
|
34876
36516
|
return buildColorKeyMask;
|
|
34877
36517
|
}
|
|
34878
36518
|
});
|
|
36519
|
+
Object.defineProperty(exports, "buildCoonsPatchShading", {
|
|
36520
|
+
enumerable: true,
|
|
36521
|
+
get: function() {
|
|
36522
|
+
return buildCoonsPatchShading;
|
|
36523
|
+
}
|
|
36524
|
+
});
|
|
34879
36525
|
Object.defineProperty(exports, "buildDPartRoot", {
|
|
34880
36526
|
enumerable: true,
|
|
34881
36527
|
get: function() {
|
|
@@ -34924,6 +36570,12 @@ Object.defineProperty(exports, "buildFieldLockDict", {
|
|
|
34924
36570
|
return buildFieldLockDict;
|
|
34925
36571
|
}
|
|
34926
36572
|
});
|
|
36573
|
+
Object.defineProperty(exports, "buildFreeFormGouraudShading", {
|
|
36574
|
+
enumerable: true,
|
|
36575
|
+
get: function() {
|
|
36576
|
+
return buildFreeFormGouraudShading;
|
|
36577
|
+
}
|
|
36578
|
+
});
|
|
34927
36579
|
Object.defineProperty(exports, "buildFunctionShading", {
|
|
34928
36580
|
enumerable: true,
|
|
34929
36581
|
get: function() {
|
|
@@ -34948,6 +36600,12 @@ Object.defineProperty(exports, "buildLab", {
|
|
|
34948
36600
|
return buildLab;
|
|
34949
36601
|
}
|
|
34950
36602
|
});
|
|
36603
|
+
Object.defineProperty(exports, "buildLatticeFormGouraudShading", {
|
|
36604
|
+
enumerable: true,
|
|
36605
|
+
get: function() {
|
|
36606
|
+
return buildLatticeFormGouraudShading;
|
|
36607
|
+
}
|
|
36608
|
+
});
|
|
34951
36609
|
Object.defineProperty(exports, "buildNamespace", {
|
|
34952
36610
|
enumerable: true,
|
|
34953
36611
|
get: function() {
|
|
@@ -35056,6 +36714,12 @@ Object.defineProperty(exports, "buildStencilMask", {
|
|
|
35056
36714
|
return buildStencilMask;
|
|
35057
36715
|
}
|
|
35058
36716
|
});
|
|
36717
|
+
Object.defineProperty(exports, "buildTensorPatchShading", {
|
|
36718
|
+
enumerable: true,
|
|
36719
|
+
get: function() {
|
|
36720
|
+
return buildTensorPatchShading;
|
|
36721
|
+
}
|
|
36722
|
+
});
|
|
35059
36723
|
Object.defineProperty(exports, "buildThresholdHalftone", {
|
|
35060
36724
|
enumerable: true,
|
|
35061
36725
|
get: function() {
|
|
@@ -35230,6 +36894,12 @@ Object.defineProperty(exports, "dataMatrixToOperators", {
|
|
|
35230
36894
|
return dataMatrixToOperators;
|
|
35231
36895
|
}
|
|
35232
36896
|
});
|
|
36897
|
+
Object.defineProperty(exports, "decodeRegisteredImage", {
|
|
36898
|
+
enumerable: true,
|
|
36899
|
+
get: function() {
|
|
36900
|
+
return decodeRegisteredImage;
|
|
36901
|
+
}
|
|
36902
|
+
});
|
|
35233
36903
|
Object.defineProperty(exports, "decodeTile", {
|
|
35234
36904
|
enumerable: true,
|
|
35235
36905
|
get: function() {
|
|
@@ -35266,12 +36936,24 @@ Object.defineProperty(exports, "detectModifications", {
|
|
|
35266
36936
|
return detectModifications;
|
|
35267
36937
|
}
|
|
35268
36938
|
});
|
|
36939
|
+
Object.defineProperty(exports, "detectNextGenFormat", {
|
|
36940
|
+
enumerable: true,
|
|
36941
|
+
get: function() {
|
|
36942
|
+
return detectNextGenFormat;
|
|
36943
|
+
}
|
|
36944
|
+
});
|
|
35269
36945
|
Object.defineProperty(exports, "detectTransparency", {
|
|
35270
36946
|
enumerable: true,
|
|
35271
36947
|
get: function() {
|
|
35272
36948
|
return detectTransparency;
|
|
35273
36949
|
}
|
|
35274
36950
|
});
|
|
36951
|
+
Object.defineProperty(exports, "deviceRgbToXyz", {
|
|
36952
|
+
enumerable: true,
|
|
36953
|
+
get: function() {
|
|
36954
|
+
return deviceRgbToXyz;
|
|
36955
|
+
}
|
|
36956
|
+
});
|
|
35275
36957
|
Object.defineProperty(exports, "didYouMean", {
|
|
35276
36958
|
enumerable: true,
|
|
35277
36959
|
get: function() {
|
|
@@ -35488,6 +37170,48 @@ Object.defineProperty(exports, "extractXmpMetadata", {
|
|
|
35488
37170
|
return extractXmpMetadata;
|
|
35489
37171
|
}
|
|
35490
37172
|
});
|
|
37173
|
+
Object.defineProperty(exports, "feBlend", {
|
|
37174
|
+
enumerable: true,
|
|
37175
|
+
get: function() {
|
|
37176
|
+
return feBlend;
|
|
37177
|
+
}
|
|
37178
|
+
});
|
|
37179
|
+
Object.defineProperty(exports, "feColorMatrix", {
|
|
37180
|
+
enumerable: true,
|
|
37181
|
+
get: function() {
|
|
37182
|
+
return feColorMatrix;
|
|
37183
|
+
}
|
|
37184
|
+
});
|
|
37185
|
+
Object.defineProperty(exports, "feColorMatrixSaturate", {
|
|
37186
|
+
enumerable: true,
|
|
37187
|
+
get: function() {
|
|
37188
|
+
return feColorMatrixSaturate;
|
|
37189
|
+
}
|
|
37190
|
+
});
|
|
37191
|
+
Object.defineProperty(exports, "feComposite", {
|
|
37192
|
+
enumerable: true,
|
|
37193
|
+
get: function() {
|
|
37194
|
+
return feComposite;
|
|
37195
|
+
}
|
|
37196
|
+
});
|
|
37197
|
+
Object.defineProperty(exports, "feFlood", {
|
|
37198
|
+
enumerable: true,
|
|
37199
|
+
get: function() {
|
|
37200
|
+
return feFlood;
|
|
37201
|
+
}
|
|
37202
|
+
});
|
|
37203
|
+
Object.defineProperty(exports, "feGaussianBlur", {
|
|
37204
|
+
enumerable: true,
|
|
37205
|
+
get: function() {
|
|
37206
|
+
return feGaussianBlur;
|
|
37207
|
+
}
|
|
37208
|
+
});
|
|
37209
|
+
Object.defineProperty(exports, "feOffset", {
|
|
37210
|
+
enumerable: true,
|
|
37211
|
+
get: function() {
|
|
37212
|
+
return feOffset;
|
|
37213
|
+
}
|
|
37214
|
+
});
|
|
35491
37215
|
Object.defineProperty(exports, "findChangedObjects", {
|
|
35492
37216
|
enumerable: true,
|
|
35493
37217
|
get: function() {
|
|
@@ -35704,6 +37428,12 @@ Object.defineProperty(exports, "getFieldValue", {
|
|
|
35704
37428
|
return getFieldValue;
|
|
35705
37429
|
}
|
|
35706
37430
|
});
|
|
37431
|
+
Object.defineProperty(exports, "getImageDecoder", {
|
|
37432
|
+
enumerable: true,
|
|
37433
|
+
get: function() {
|
|
37434
|
+
return getImageDecoder;
|
|
37435
|
+
}
|
|
37436
|
+
});
|
|
35707
37437
|
Object.defineProperty(exports, "getLinearizationInfo", {
|
|
35708
37438
|
enumerable: true,
|
|
35709
37439
|
get: function() {
|
|
@@ -35740,12 +37470,30 @@ Object.defineProperty(exports, "h", {
|
|
|
35740
37470
|
return h;
|
|
35741
37471
|
}
|
|
35742
37472
|
});
|
|
37473
|
+
Object.defineProperty(exports, "hasImageDecoder", {
|
|
37474
|
+
enumerable: true,
|
|
37475
|
+
get: function() {
|
|
37476
|
+
return hasImageDecoder;
|
|
37477
|
+
}
|
|
37478
|
+
});
|
|
35743
37479
|
Object.defineProperty(exports, "hasLtvData", {
|
|
35744
37480
|
enumerable: true,
|
|
35745
37481
|
get: function() {
|
|
35746
37482
|
return hasLtvData;
|
|
35747
37483
|
}
|
|
35748
37484
|
});
|
|
37485
|
+
Object.defineProperty(exports, "hslToRgb", {
|
|
37486
|
+
enumerable: true,
|
|
37487
|
+
get: function() {
|
|
37488
|
+
return hslToRgb;
|
|
37489
|
+
}
|
|
37490
|
+
});
|
|
37491
|
+
Object.defineProperty(exports, "hsvToRgb", {
|
|
37492
|
+
enumerable: true,
|
|
37493
|
+
get: function() {
|
|
37494
|
+
return hsvToRgb;
|
|
37495
|
+
}
|
|
37496
|
+
});
|
|
35749
37497
|
Object.defineProperty(exports, "identityTransferFunction", {
|
|
35750
37498
|
enumerable: true,
|
|
35751
37499
|
get: function() {
|
|
@@ -35938,6 +37686,12 @@ Object.defineProperty(exports, "parseExistingTrailer", {
|
|
|
35938
37686
|
return parseExistingTrailer;
|
|
35939
37687
|
}
|
|
35940
37688
|
});
|
|
37689
|
+
Object.defineProperty(exports, "parseIccTransform", {
|
|
37690
|
+
enumerable: true,
|
|
37691
|
+
get: function() {
|
|
37692
|
+
return parseIccTransform;
|
|
37693
|
+
}
|
|
37694
|
+
});
|
|
35941
37695
|
Object.defineProperty(exports, "parseTileInfo", {
|
|
35942
37696
|
enumerable: true,
|
|
35943
37697
|
get: function() {
|
|
@@ -35986,6 +37740,12 @@ Object.defineProperty(exports, "preflightPdfA", {
|
|
|
35986
37740
|
return preflightPdfA;
|
|
35987
37741
|
}
|
|
35988
37742
|
});
|
|
37743
|
+
Object.defineProperty(exports, "probeNextGenImage", {
|
|
37744
|
+
enumerable: true,
|
|
37745
|
+
get: function() {
|
|
37746
|
+
return probeNextGenImage;
|
|
37747
|
+
}
|
|
37748
|
+
});
|
|
35989
37749
|
Object.defineProperty(exports, "processBatch", {
|
|
35990
37750
|
enumerable: true,
|
|
35991
37751
|
get: function() {
|
|
@@ -36076,6 +37836,12 @@ Object.defineProperty(exports, "registerEmbeddedFile", {
|
|
|
36076
37836
|
return registerEmbeddedFile;
|
|
36077
37837
|
}
|
|
36078
37838
|
});
|
|
37839
|
+
Object.defineProperty(exports, "registerImageDecoder", {
|
|
37840
|
+
enumerable: true,
|
|
37841
|
+
get: function() {
|
|
37842
|
+
return registerImageDecoder;
|
|
37843
|
+
}
|
|
37844
|
+
});
|
|
36079
37845
|
Object.defineProperty(exports, "removeAllBookmarks", {
|
|
36080
37846
|
enumerable: true,
|
|
36081
37847
|
get: function() {
|
|
@@ -36172,6 +37938,30 @@ Object.defineProperty(exports, "resolveInstanceCoordinates", {
|
|
|
36172
37938
|
return resolveInstanceCoordinates;
|
|
36173
37939
|
}
|
|
36174
37940
|
});
|
|
37941
|
+
Object.defineProperty(exports, "rgbToHsl", {
|
|
37942
|
+
enumerable: true,
|
|
37943
|
+
get: function() {
|
|
37944
|
+
return rgbToHsl;
|
|
37945
|
+
}
|
|
37946
|
+
});
|
|
37947
|
+
Object.defineProperty(exports, "rgbToHsv", {
|
|
37948
|
+
enumerable: true,
|
|
37949
|
+
get: function() {
|
|
37950
|
+
return rgbToHsv;
|
|
37951
|
+
}
|
|
37952
|
+
});
|
|
37953
|
+
Object.defineProperty(exports, "rgbToLab", {
|
|
37954
|
+
enumerable: true,
|
|
37955
|
+
get: function() {
|
|
37956
|
+
return rgbToLab;
|
|
37957
|
+
}
|
|
37958
|
+
});
|
|
37959
|
+
Object.defineProperty(exports, "rgbToXyz", {
|
|
37960
|
+
enumerable: true,
|
|
37961
|
+
get: function() {
|
|
37962
|
+
return rgbToXyz;
|
|
37963
|
+
}
|
|
37964
|
+
});
|
|
36175
37965
|
Object.defineProperty(exports, "sampleShadingColor", {
|
|
36176
37966
|
enumerable: true,
|
|
36177
37967
|
get: function() {
|
|
@@ -36376,6 +38166,12 @@ Object.defineProperty(exports, "truncateText", {
|
|
|
36376
38166
|
return truncateText;
|
|
36377
38167
|
}
|
|
36378
38168
|
});
|
|
38169
|
+
Object.defineProperty(exports, "unregisterImageDecoder", {
|
|
38170
|
+
enumerable: true,
|
|
38171
|
+
get: function() {
|
|
38172
|
+
return unregisterImageDecoder;
|
|
38173
|
+
}
|
|
38174
|
+
});
|
|
36379
38175
|
Object.defineProperty(exports, "upcAToOperators", {
|
|
36380
38176
|
enumerable: true,
|
|
36381
38177
|
get: function() {
|
|
@@ -36514,5 +38310,17 @@ Object.defineProperty(exports, "wrapText", {
|
|
|
36514
38310
|
return wrapText$2;
|
|
36515
38311
|
}
|
|
36516
38312
|
});
|
|
38313
|
+
Object.defineProperty(exports, "xyzToLab", {
|
|
38314
|
+
enumerable: true,
|
|
38315
|
+
get: function() {
|
|
38316
|
+
return xyzToLab;
|
|
38317
|
+
}
|
|
38318
|
+
});
|
|
38319
|
+
Object.defineProperty(exports, "xyzToRgb", {
|
|
38320
|
+
enumerable: true,
|
|
38321
|
+
get: function() {
|
|
38322
|
+
return xyzToRgb;
|
|
38323
|
+
}
|
|
38324
|
+
});
|
|
36517
38325
|
|
|
36518
|
-
//# sourceMappingURL=src-
|
|
38326
|
+
//# sourceMappingURL=src-B0gmgVEs.cjs.map
|