modern-pdf-lib 0.36.0 → 0.37.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.
@@ -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);
@@ -30527,6 +30527,865 @@ function getColorGlyphLayers(fontData, glyphId, paletteIndex = 0) {
30527
30527
  return layers;
30528
30528
  }
30529
30529
  //#endregion
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;
30634
+ }
30635
+ /**
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
30641
+ */
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));
30646
+ }
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
+ }
30662
+ }
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);
30672
+ }
30673
+ /**
30674
+ * Pack one colour (n components) into `packer`.
30675
+ *
30676
+ * @internal
30677
+ */
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
+ }
30686
+ }
30687
+ /**
30688
+ * Build the dictionary shared by every mesh shading and set the common keys.
30689
+ *
30690
+ * @internal
30691
+ */
30692
+ function buildCommonDict(shadingType, common, includeFlag) {
30693
+ const dict = new PdfDict();
30694
+ dict.set("/ShadingType", PdfNumber.of(shadingType));
30695
+ dict.set("/ColorSpace", common.colorSpace);
30696
+ dict.set("/BitsPerCoordinate", PdfNumber.of(common.bitsPerCoordinate));
30697
+ dict.set("/BitsPerComponent", PdfNumber.of(common.bitsPerComponent));
30698
+ if (includeFlag) dict.set("/BitsPerFlag", PdfNumber.of(common.bitsPerFlag));
30699
+ dict.set("/Decode", 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).
30706
+ *
30707
+ * Each vertex is packed as `flag · x · y · colour[n]`, MSB-first, with the flag
30708
+ * `bitsPerFlag` wide.
30709
+ *
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.
30713
+ */
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 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
30744
+ };
30745
+ const dict = buildCommonDict(5, common, false);
30746
+ dict.set("/VerticesPerRow", 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 PdfStream.fromBytes(data, dict);
30752
+ }
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 PdfStream.fromBytes(data, dict);
30775
+ }
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 PdfStream.fromBytes(data, dict);
30798
+ }
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;
30829
+ }
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;
30853
+ }
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
+ };
30893
+ }
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).`);
30960
+ }
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).`);
30966
+ return [
30967
+ readS15Fixed16(view, tag.offset + 8),
30968
+ readS15Fixed16(view, tag.offset + 12),
30969
+ readS15Fixed16(view, tag.offset + 16)
30970
+ ];
30971
+ }
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
+ ];
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
+ ];
31037
+ }
31038
+ /**
31039
+ * CIE L\*a\*b\* nonlinearity f(t) (CIE 15:2004 §8.2.1.1):
31040
+ *
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
30530
31389
  //#region src/render/matrix.ts
30531
31390
  /** The identity transform. */
30532
31391
  function identity() {
@@ -34354,6 +35213,6 @@ async function initWasm(options) {
34354
35213
  wasmInitialized = true;
34355
35214
  }
34356
35215
  //#endregion
34357
- export { getColorGlyphLayers as $, requestTimestamp as $a, applyRedaction as $i, PDF2_NAMESPACE as $n, shrinkFontSize as $r, parseAcrobatDate as $t, buildSoftMaskNone as A, optimizeIncrementalSave as Aa, computeCode39CheckDigit as Ai, STANDARD_SPOT_FUNCTIONS as An, parseXmpMetadata$1 as Ao, FieldAlreadyExistsError as Ar, summarizeBitDepth as At, redactRegions as B, detectModifications as Ba, recompressWebP as Bi, reconstructLines as Bn, RemovePageFromEmptyDocumentError as Br, isCertificateRevoked as Bt, tagTableHeaderCell as C, validatePdfA as Ca, ean13ToOperators as Ci, levenshtein as Cn, generateSquigglyAppearance as Co, flattenField as Cr, buildPdfXOutputIntent as Ct, buildImageSoftMask as D, linearizePdf as Da, encodeItf as Di, buildGtsPdfxVersion as Dn, PdfTextAnnotation as Do, CombedTextLayoutError as Dr, getComponentDepths as Dt, buildColorKeyMask as E, isLinearized as Ea, encodeEan8 as Ei, buildBoxDict as En, PdfLinkAnnotation as Eo, BatchProcessingError as Er, downscale16To8 as Et, attachAssociatedFiles as F, getCounterSignatures as Fa, valuesToModules as Fi, identityTransferFunction as Fn, saveDocumentIncremental as Fo, InvalidFieldNamePartError as Fr, parseTileInfo as Ft, extractImages as G, validateSignatureChain as Ga, isCmykTiff as Gi, buildCalRGB as Gn, applyHeaderFooterToPage as Gr, resolveFieldReference as Gt, compareImages as H, buildDocMdpReference as Ha, webpToPng as Hi, DEFAULT_DOC_TIMESTAMP_CONTENTS_SIZE as Hn, StreamingParseError as Hr, extractOcspUrl as Ht, registerEmbeddedFile as I, diffSignedContent as Ia, PdfWorker as Ii, nameHalftone as In, saveIncremental as Io, InvalidPageSizeError as Ir, verifySignatureDetailed as It, renderPageToCanvas as J, parseExistingTrailer as Ja, analyzeJpegMarkers as Ji, DEFAULT_SARIF_TOOL_NAME as Jn, toAlpha as Jr, setFieldVisibility as Jt, generateThumbnail as K, appendIncrementalUpdate as Ka, extractJpegMetadata as Ki, buildLab as Kn, formatDate$1 as Kr, setFieldValue as Kt, RenderCache as L, addFieldLock as La, canDirectEmbed as Li, evaluateFunction as Ln, MissingOnValueCheckError as Lr, TrustStore as Lt, buildUnencryptedWrapper as M, embedLtvData as Ma, code128ToOperators as Mi, buildThresholdHalftone as Mn, buildDeviceNColorSpace as Mo, FontNotEmbeddedError as Mr, assembleTiles as Mt, attachOutputIntents as N, hasLtvData as Na, encodeCode128 as Ni, buildType1Halftone as Nn, buildSeparationColorSpace as No, ForeignPageError as Nr, decodeTile as Nt, buildStencilMask as O, computeObjectHash as Oa, itfToOperators as Oi, buildPdfX6OutputIntent as On, buildXmpMetadata as Oo, EncryptedPdfError as Or, normalizeComponentDepth as Ot, buildPageOutputIntent as P, addCounterSignature as Pa, encodeCode128Values as Pi, buildType5Halftone as Pn, ChangeTracker as Po, InvalidColorError as Pr, decodeTileRegion as Pt, interpretPage as Q, parseTimestampResponse as Qa, recompressImage as Qi, MATHML_NAMESPACE as Qn, estimateTextWidth as Qr, formatDate as Qt, computeTileGrid as R, buildFieldLockDict as Ra, embedTiffDirect as Ri, markdownToPdf as Rn, NoSuchFieldError as Rr, downloadCrl as Rt, tagTableDataCell as S, enforcePdfA as Sa, calculateEanCheckDigit as Si, didYouMean as Sn, generateSquareAppearance as So, removeBookmark as Sr, layoutTextFlow as St, buildBlackPointCompensationExtGState as T, getLinearizationInfo as Ta, encodeEan13 as Ti, generateCiiXml as Tn, generateUnderlineAppearance as To, flattenForm as Tr, validatePdfX as Tt, comparePages as U, getCertificationLevel as Ua, convertTiffCmykToRgb as Ui, buildDocTimeStampDict as Un, UnexpectedFieldTypeError as Ur, createSandbox as Ut, applyOcr as V, MdpPermission as Va, webpToJpeg as Vi, reconstructParagraphs as Vn, RichTextFieldReadError as Vr, checkCertificateStatus as Vt, extractFonts as W, setCertificationLevel as Wa, embedTiffCmyk as Wi, buildCalGray as Wn, applyHeaderFooter as Wr, getFieldValue as Wt, renderPageToImage as X, validateByteRangeIntegrity as Xa, estimateJpegQuality as Xi, toJsonReport as Xn, applyOverflow as Xr, AFSpecial_Format as Xt, rasterize as Y, saveIncrementalWithSignaturePreservation as Ya, downscaleImage as Yi, SARIF_SCHEMA_URI as Yn, toRoman as Yr, validateFieldValue as Yt, interpretContentStream as Z, buildTimestampRequest as Za, optimizeImage as Zi, toSarif as Zn, ellipsisText as Zr, AFDate_FormatEx as Zt, tagLink as _, buildOutputIntent as _a, dataMatrixToOperators as _i, generateXRechnungCii as _n, generateCircleAppearance as _o, batchMerge as _r, buildCertificateChain as _t, assembleFacturX as a, buildAfArray as aa, minimalPreset as ai, readWoffHeader as an, PdfInkAnnotation as ao, buildDPartRoot as ar, resolveBidi as at, tagParagraph as b, detectTransparency as ba, encodeUpcA as bi, buildFunctionShading as bn, generateInkAppearance as bo, getBookmarks as br, layoutColumns as bt, buildWtpdfIdentificationXmp as c, parseXmpMetadata as ca, readBarcode as ci, buildPdfVtDParts as cn, PdfLineAnnotation as co, tableToJson as cr, sanitizePdf as ct, autoTagPage as d, getSupportedLevels as da, readEan13 as di, h as dn, PdfSquareAnnotation as do, timestampPlugin as dr, extractEmbeddedRevocationData as dt, enforcePdfAFull as ea, truncateText as ei, AFNumber_Format as en, searchTextItems as eo, buildNamespace as er, parseColorFont as et, buildPdfUa2Xmp as f, isValidLevel as fa, readEan8 as fi, renderToPdf as fn, PdfHighlightAnnotation as fo, StreamingPdfParser as fr, verifyOfflineRevocation as ft, tagHeading as g, getToUnicodeCmap as ga, pdf417ToOperators as gi, generateOrderX as gn, PdfFreeTextAnnotation as go, batchFlatten as gr, validateKeyUsage as gt, tagFigure as h, generateZapfDingbatsToUnicodeCmap as ha, encodePdf417 as hi, splitByScript as hn, PdfUnderlineAnnotation as ho, validatePdfUa as hr, validateExtendedKeyUsage as ht, validateEn16931 as i, stripProhibitedFeatures as ia, borderedPreset as ii, isWoff2 as in, PdfRedactAnnotation as io, buildRequirements as ir, reorderVisual as it, buildEncryptedPayload as j, buildDssDictionary as ja, encodeCode39 as ji, buildSampledTransferFunction as jn, PDFOperator as jo, FieldExistsAsNonTerminalError as jr, upscale8To16 as jt, buildSoftMaskGroupExtGState as k, findChangedObjects as ka, code39ToOperators as ki, validateBoxGeometry as kn, createXmpStream as ko, ExceededMaxLengthError as kr, offsetSignedToUnsigned as kt, convertPdfAConformanceXmp as l, validateXmpMetadata as la, readCode128 as li, buildVtDpm as ln, PdfPolyLineAnnotation as lo, accessibilityPlugin as lr, scanPdfThreats as lt, LIST_NUMBERING_KEY as m, generateWinAnsiToUnicodeCmap as ma, renderStyledBarcode as mi, resolveFallback as mn, PdfStrikeOutAnnotation as mo, enforcePdfUa as mr, validateCertificatePolicy as mt, detectFacturXProfile as n, generatePdfAXmpBytes as na, applyPreset as ni, decodeWoff as nn, PdfCaretAnnotation as no, buildPieceInfo as nr, parseVariableFont as nt, buildFacturXXmp as o, createAssociatedFile as oa, professionalPreset as oi, signDeferred as on, PdfStampAnnotation as oo, extractTables as or, inspectEncryption as ot, validatePdfUa2 as p, generateSymbolToUnicodeCmap as pa, calculateBarcodeDimensions as pi, createRangeFetcher as pn, PdfSquigglyAnnotation as po, PdfDocumentBuilder as pr, EKU_OIDS as pt, renderDisplayListToCanvas as q, findExistingSignatures as qa, injectJpegMetadata as qi, labToRgb as qn, replaceTemplateVariables as qr, addVisibilityAction as qt, parseCiiXml as r, countOccurrences as ra, applyTablePreset as ri, isWoff as rn, PdfPopupAnnotation as ro, buildRequirement as rr, resolveInstanceCoordinates as rt, buildPdfRIdentificationXmp as s, extractXmpMetadata as sa, stripedPreset as si, createWorkerPool as sn, PdfCircleAnnotation as so, tableToCsv as sr, verifyRedactions as st, initWasm as t, generatePdfAXmp as ta, wrapText$2 as ti, formatNumber$1 as tn, PdfFileAttachmentAnnotation as to, buildNamespacesArray as tr, normalizeAxisCoordinate as tt, preflightPdfA as u, getProfile as ua, readCode39 as ui, gtsPdfVtVersion as un, PdfPolygonAnnotation as uo, metadataPlugin as ur, buildCertPath as ut, tagList as v, SRGB_ICC_PROFILE as va, encodeDataMatrix as vi, buildPdfA4Xmp as vn, generateFreeTextAppearance as vo, processBatch as vr, validateCertificateChain as vt, tagTableRow as w, delinearizePdf as wa, ean8ToOperators as wi, renderCodeFrame as wn, generateStrikeOutAppearance as wo, flattenFields as wr, enforcePdfX as wt, tagTable as x, flattenTransparency as xa, upcAToOperators as xi, sampleShadingColor as xn, generateLineAppearance as xo, removeAllBookmarks as xr, layoutParagraph as xt, tagListItem as y, generateSrgbIccProfile as ya, calculateUpcCheckDigit as yi, pdfA4Rules as yn, generateHighlightAppearance as yo, addBookmark as yr, findHyphenationPoints as yt, renderPageTile as z, getFieldLocks as za, encodePngFromPixels as zi, buildCollection as zn, PluginError as zr, extractCrlUrls as zt };
35216
+ export { hslToRgb as $, getFieldLocks as $a, encodePngFromPixels as $i, buildCollection as $n, PluginError as $r, extractCrlUrls as $t, buildSoftMaskNone as A, getToUnicodeCmap as Aa, pdf417ToOperators as Ai, generateOrderX as An, PdfFreeTextAnnotation as Ao, batchFlatten as Ar, validateKeyUsage as At, redactRegions as B, isLinearized as Ba, encodeEan8 as Bi, buildBoxDict as Bn, PdfLinkAnnotation as Bo, BatchProcessingError as Br, downscale16To8 as Bt, tagTableHeaderCell as C, validateXmpMetadata as Ca, readCode128 as Ci, buildVtDpm as Cn, PdfPolyLineAnnotation as Co, accessibilityPlugin as Cr, scanPdfThreats as Ct, buildImageSoftMask as D, generateSymbolToUnicodeCmap as Da, calculateBarcodeDimensions as Di, createRangeFetcher as Dn, PdfSquigglyAnnotation as Do, PdfDocumentBuilder as Dr, EKU_OIDS as Dt, buildColorKeyMask as E, isValidLevel as Ea, readEan8 as Ei, renderToPdf as En, PdfHighlightAnnotation as Eo, StreamingPdfParser as Er, verifyOfflineRevocation as Et, attachAssociatedFiles as F, flattenTransparency as Fa, upcAToOperators as Fi, sampleShadingColor as Fn, generateLineAppearance as Fo, removeAllBookmarks as Fr, layoutParagraph as Ft, extractImages as G, buildDssDictionary as Ga, encodeCode39 as Gi, buildSampledTransferFunction as Gn, PDFOperator as Go, FieldExistsAsNonTerminalError as Gr, upscale8To16 as Gt, compareImages as H, computeObjectHash as Ha, itfToOperators as Hi, buildPdfX6OutputIntent as Hn, buildXmpMetadata as Ho, EncryptedPdfError as Hr, normalizeComponentDepth as Ht, registerEmbeddedFile as I, enforcePdfA as Ia, calculateEanCheckDigit as Ii, didYouMean as In, generateSquareAppearance as Io, removeBookmark as Ir, layoutTextFlow as It, renderPageToCanvas as J, addCounterSignature as Ja, encodeCode128Values as Ji, buildType5Halftone as Jn, ChangeTracker as Jo, InvalidColorError as Jr, decodeTileRegion as Jt, generateThumbnail as K, embedLtvData as Ka, code128ToOperators as Ki, buildThresholdHalftone as Kn, buildDeviceNColorSpace as Ko, FontNotEmbeddedError as Kr, assembleTiles as Kt, RenderCache as L, validatePdfA as La, ean13ToOperators as Li, levenshtein as Ln, generateSquigglyAppearance as Lo, flattenField as Lr, buildPdfXOutputIntent as Lt, buildUnencryptedWrapper as M, SRGB_ICC_PROFILE as Ma, encodeDataMatrix as Mi, buildPdfA4Xmp as Mn, generateFreeTextAppearance as Mo, processBatch as Mr, validateCertificateChain as Mt, attachOutputIntents as N, generateSrgbIccProfile as Na, calculateUpcCheckDigit as Ni, pdfA4Rules as Nn, generateHighlightAppearance as No, addBookmark as Nr, findHyphenationPoints as Nt, buildStencilMask as O, generateWinAnsiToUnicodeCmap as Oa, renderStyledBarcode as Oi, resolveFallback as On, PdfStrikeOutAnnotation as Oo, enforcePdfUa as Or, validateCertificatePolicy as Ot, buildPageOutputIntent as P, detectTransparency as Pa, encodeUpcA as Pi, buildFunctionShading as Pn, generateInkAppearance as Po, getBookmarks as Pr, layoutColumns as Pt, interpretPage as Q, buildFieldLockDict as Qa, embedTiffDirect as Qi, markdownToPdf as Qn, NoSuchFieldError as Qr, downloadCrl as Qt, computeTileGrid as R, delinearizePdf as Ra, ean8ToOperators as Ri, renderCodeFrame as Rn, generateStrikeOutAppearance as Ro, flattenFields as Rr, enforcePdfX as Rt, tagTableDataCell as S, parseXmpMetadata as Sa, readBarcode as Si, buildPdfVtDParts as Sn, PdfLineAnnotation as So, tableToJson as Sr, sanitizePdf as St, buildBlackPointCompensationExtGState as T, getSupportedLevels as Ta, readEan13 as Ti, h as Tn, PdfSquareAnnotation as To, timestampPlugin as Tr, extractEmbeddedRevocationData as Tt, comparePages as U, findChangedObjects as Ua, code39ToOperators as Ui, validateBoxGeometry as Un, createXmpStream as Uo, ExceededMaxLengthError as Ur, offsetSignedToUnsigned as Ut, applyOcr as V, linearizePdf as Va, encodeItf as Vi, buildGtsPdfxVersion as Vn, PdfTextAnnotation as Vo, CombedTextLayoutError as Vr, getComponentDepths as Vt, extractFonts as W, optimizeIncrementalSave as Wa, computeCode39CheckDigit as Wi, STANDARD_SPOT_FUNCTIONS as Wn, parseXmpMetadata$1 as Wo, FieldAlreadyExistsError as Wr, summarizeBitDepth as Wt, renderPageToImage as X, diffSignedContent as Xa, PdfWorker as Xi, nameHalftone as Xn, saveIncremental as Xo, InvalidPageSizeError as Xr, verifySignatureDetailed as Xt, rasterize as Y, getCounterSignatures as Ya, valuesToModules as Yi, identityTransferFunction as Yn, saveDocumentIncremental as Yo, InvalidFieldNamePartError as Yr, parseTileInfo as Yt, interpretContentStream as Z, addFieldLock as Za, canDirectEmbed as Zi, evaluateFunction as Zn, MissingOnValueCheckError as Zr, TrustStore as Zt, tagLink as _, countOccurrences as _a, applyTablePreset as _i, isWoff as _n, PdfPopupAnnotation as _o, buildRequirement as _r, resolveInstanceCoordinates as _t, assembleFacturX as a, isCmykTiff as aa, applyHeaderFooterToPage as ai, resolveFieldReference as an, validateSignatureChain as ao, buildCalRGB as ar, xyzToRgb as at, tagParagraph as b, createAssociatedFile as ba, professionalPreset as bi, signDeferred as bn, PdfStampAnnotation as bo, extractTables as br, inspectEncryption as bt, buildWtpdfIdentificationXmp as c, analyzeJpegMarkers as ca, toAlpha as ci, setFieldVisibility as cn, parseExistingTrailer as co, DEFAULT_SARIF_TOOL_NAME as cr, xyzToLab as ct, autoTagPage as d, optimizeImage as da, ellipsisText as di, AFDate_FormatEx as dn, buildTimestampRequest as do, toSarif as dr, buildLatticeFormGouraudShading as dt, recompressWebP as ea, RemovePageFromEmptyDocumentError as ei, isCertificateRevoked as en, detectModifications as eo, reconstructLines as er, hsvToRgb as et, buildPdfUa2Xmp as f, recompressImage as fa, estimateTextWidth as fi, formatDate as fn, parseTimestampResponse as fo, MATHML_NAMESPACE as fr, buildTensorPatchShading as ft, tagHeading as g, generatePdfAXmpBytes as ga, applyPreset as gi, decodeWoff as gn, PdfCaretAnnotation as go, buildPieceInfo as gr, parseVariableFont as gt, tagFigure as h, generatePdfAXmp as ha, wrapText$2 as hi, formatNumber$1 as hn, PdfFileAttachmentAnnotation as ho, buildNamespacesArray as hr, normalizeAxisCoordinate as ht, validateEn16931 as i, embedTiffCmyk as ia, applyHeaderFooter as ii, getFieldValue as in, setCertificationLevel as io, buildCalGray as ir, rgbToXyz as it, buildEncryptedPayload as j, buildOutputIntent as ja, dataMatrixToOperators as ji, generateXRechnungCii as jn, generateCircleAppearance as jo, batchMerge as jr, buildCertificateChain as jt, buildSoftMaskGroupExtGState as k, generateZapfDingbatsToUnicodeCmap as ka, encodePdf417 as ki, splitByScript as kn, PdfUnderlineAnnotation as ko, validatePdfUa as kr, validateExtendedKeyUsage as kt, convertPdfAConformanceXmp as l, downscaleImage as la, toRoman as li, validateFieldValue as ln, saveIncrementalWithSignaturePreservation as lo, SARIF_SCHEMA_URI as lr, buildCoonsPatchShading as lt, LIST_NUMBERING_KEY as m, enforcePdfAFull as ma, truncateText as mi, AFNumber_Format as mn, searchTextItems as mo, buildNamespace as mr, parseColorFont as mt, detectFacturXProfile as n, webpToPng as na, StreamingParseError as ni, extractOcspUrl as nn, buildDocMdpReference as no, DEFAULT_DOC_TIMESTAMP_CONTENTS_SIZE as nr, rgbToHsv as nt, buildFacturXXmp as o, extractJpegMetadata as oa, formatDate$1 as oi, setFieldValue as on, appendIncrementalUpdate as oo, buildLab as or, deviceRgbToXyz as ot, validatePdfUa2 as p, applyRedaction as pa, shrinkFontSize as pi, parseAcrobatDate as pn, requestTimestamp as po, PDF2_NAMESPACE as pr, getColorGlyphLayers as pt, renderDisplayListToCanvas as q, hasLtvData as qa, encodeCode128 as qi, buildType1Halftone as qn, buildSeparationColorSpace as qo, ForeignPageError as qr, decodeTile as qt, parseCiiXml as r, convertTiffCmykToRgb as ra, UnexpectedFieldTypeError as ri, createSandbox as rn, getCertificationLevel as ro, buildDocTimeStampDict as rr, rgbToLab as rt, buildPdfRIdentificationXmp as s, injectJpegMetadata as sa, replaceTemplateVariables as si, addVisibilityAction as sn, findExistingSignatures as so, labToRgb as sr, parseIccTransform as st, initWasm as t, webpToJpeg as ta, RichTextFieldReadError as ti, checkCertificateStatus as tn, MdpPermission as to, reconstructParagraphs as tr, rgbToHsl as tt, preflightPdfA as u, estimateJpegQuality as ua, applyOverflow as ui, AFSpecial_Format as un, validateByteRangeIntegrity as uo, toJsonReport as ur, buildFreeFormGouraudShading as ut, tagList as v, stripProhibitedFeatures as va, borderedPreset as vi, isWoff2 as vn, PdfRedactAnnotation as vo, buildRequirements as vr, reorderVisual as vt, tagTableRow as w, getProfile as wa, readCode39 as wi, gtsPdfVtVersion as wn, PdfPolygonAnnotation as wo, metadataPlugin as wr, buildCertPath as wt, tagTable as x, extractXmpMetadata as xa, stripedPreset as xi, createWorkerPool as xn, PdfCircleAnnotation as xo, tableToCsv as xr, verifyRedactions as xt, tagListItem as y, buildAfArray as ya, minimalPreset as yi, readWoffHeader as yn, PdfInkAnnotation as yo, buildDPartRoot as yr, resolveBidi as yt, renderPageTile as z, getLinearizationInfo as za, encodeEan13 as zi, generateCiiXml as zn, generateUnderlineAppearance as zo, flattenForm as zr, validatePdfX as zt };
34358
35217
 
34359
- //# sourceMappingURL=src-4wfgzZvz.mjs.map
35218
+ //# sourceMappingURL=src-DGxzt-jG.mjs.map