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.
@@ -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$1(data, offset, le) {
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$1(data, entryOffset + 4, le);
9618
- const valueOrOffset = readU32$1(data, entryOffset + 8, le);
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$1(data, pos, le));
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$1(data, 4, le);
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$1(data, nextIfdOffset, le);
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$1(data, 4, le);
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/render/matrix.ts
30531
- /** The identity transform. */
30532
- function identity() {
30533
- return [
30534
- 1,
30535
- 0,
30536
- 0,
30537
- 1,
30538
- 0,
30539
- 0
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
- * Concatenate two transforms: the result applies `m` **first**, then `n`
30544
- * (i.e. `point · m · n`). This matches the PDF `cm` operator, where the new
30545
- * CTM is `cmMatrix × CTM`.
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 multiply(m, n) {
30548
- return [
30549
- m[0] * n[0] + m[1] * n[2],
30550
- m[0] * n[1] + m[1] * n[3],
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
- /** Apply a transform to a point, returning the transformed `[x, y]`. */
30558
- function applyToPoint(m, x, y) {
30559
- return [m[0] * x + m[2] * y + m[4], m[1] * x + m[3] * y + m[5]];
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
- /** A pure translation transform. */
30562
- function translation(tx, ty) {
30563
- return [
30564
- 1,
30565
- 0,
30566
- 0,
30567
- 1,
30568
- tx,
30569
- ty
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
- * The magnitude by which `m` scales lengths — the geometric mean of the
30574
- * x- and y-axis scale factors (`sqrt(|det|)`). Used to map a user-space line
30575
- * width to device space.
30674
+ * Pack one colour (n components) into `packer`.
30675
+ *
30676
+ * @internal
30576
30677
  */
30577
- function meanScale(m) {
30578
- const det = m[0] * m[3] - m[1] * m[2];
30579
- return Math.sqrt(Math.abs(det));
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
- * @module render/interpreter
30688
+ * Build the dictionary shared by every mesh shading and set the common keys.
30585
30689
  *
30586
- * Executes a parsed PDF content stream through a graphics-state machine,
30587
- * producing a resolution-independent {@link DisplayList} (page space, y-up).
30588
- * This is the foundation the rasterizer and Canvas adapter render from.
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
- * Supported operators: graphics state (`q`/`Q`/`cm`/`w`/`J`/`j`/`gs`), path
30591
- * construction (`m`/`l`/`c`/`v`/`y`/`re`/`h`), path painting
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
- * @packageDocumentation
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 initialState$1() {
30601
- return {
30602
- ctm: identity(),
30603
- fill: [
30604
- 0,
30605
- 0,
30606
- 0,
30607
- 255
30608
- ],
30609
- stroke: [
30610
- 0,
30611
- 0,
30612
- 0,
30613
- 255
30614
- ],
30615
- lineWidth: 1,
30616
- lineCap: 0,
30617
- lineJoin: 0,
30618
- fillAlpha: 1,
30619
- strokeAlpha: 1,
30620
- clip: void 0,
30621
- font: void 0,
30622
- fontSize: 0,
30623
- charSpace: 0,
30624
- wordSpace: 0,
30625
- hScale: 1,
30626
- leading: 0,
30627
- rise: 0,
30628
- renderMode: 0
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
- function cloneState(s) {
30632
- return { ...s };
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
- function num$2(op) {
30635
- return typeof op === "number" ? op : 0;
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
- function nameOf(op) {
30638
- if (op instanceof require_pdfObjects.PdfName) return op.value.replace(/^\//, "");
30639
- if (typeof op === "string") return op.replace(/^\//, "");
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
- function clamp255(v) {
30642
- return Math.max(0, Math.min(255, Math.round(v * 255)));
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
- function grayRgba(g) {
30645
- const v = clamp255(g);
30646
- return [
30647
- v,
30648
- v,
30649
- v,
30650
- 255
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
- function rgbRgba(r, g, b) {
30654
- return [
30655
- clamp255(r),
30656
- clamp255(g),
30657
- clamp255(b),
30658
- 255
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
- function cmykRgba(c, m, y, k) {
30662
- const [r, g, b] = require_pdfDocument.cmykToRgb(c, m, y, k);
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
- clamp255(r),
30665
- clamp255(g),
30666
- clamp255(b),
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
- /** Interpret `sc`/`scn` numeric components by arity → RGBA. */
30671
- function componentsToRgba(comps) {
30672
- if (comps.length === 1) return grayRgba(comps[0]);
30673
- if (comps.length === 3) return rgbRgba(comps[0], comps[1], comps[2]);
30674
- if (comps.length === 4) return cmykRgba(comps[0], comps[1], comps[2], comps[3]);
30675
- }
30676
- const BEZIER_STEPS = 16;
30677
- function flattenCubic(out, p0, p1, p2, p3) {
30678
- for (let i = 1; i <= BEZIER_STEPS; i++) {
30679
- const t = i / BEZIER_STEPS;
30680
- const mt = 1 - t;
30681
- const a = mt * mt * mt;
30682
- const b = 3 * mt * mt * t;
30683
- const c = 3 * mt * t * t;
30684
- const d = t * t * t;
30685
- 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]);
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
- * Execute a parsed content stream into a {@link DisplayList}.
31039
+ * CIE L\*a\*b\* nonlinearity f(t) (CIE 15:2004 §8.2.1.1):
30690
31040
  *
30691
- * @param operators - Output of {@link parseContentStream}.
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-MIBq9xGq.cjs.map
38326
+ //# sourceMappingURL=src-B0gmgVEs.cjs.map