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 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).
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 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
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);
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 require_pdfObjects.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 require_pdfObjects.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() {
@@ -34876,6 +35735,12 @@ Object.defineProperty(exports, "buildColorKeyMask", {
34876
35735
  return buildColorKeyMask;
34877
35736
  }
34878
35737
  });
35738
+ Object.defineProperty(exports, "buildCoonsPatchShading", {
35739
+ enumerable: true,
35740
+ get: function() {
35741
+ return buildCoonsPatchShading;
35742
+ }
35743
+ });
34879
35744
  Object.defineProperty(exports, "buildDPartRoot", {
34880
35745
  enumerable: true,
34881
35746
  get: function() {
@@ -34924,6 +35789,12 @@ Object.defineProperty(exports, "buildFieldLockDict", {
34924
35789
  return buildFieldLockDict;
34925
35790
  }
34926
35791
  });
35792
+ Object.defineProperty(exports, "buildFreeFormGouraudShading", {
35793
+ enumerable: true,
35794
+ get: function() {
35795
+ return buildFreeFormGouraudShading;
35796
+ }
35797
+ });
34927
35798
  Object.defineProperty(exports, "buildFunctionShading", {
34928
35799
  enumerable: true,
34929
35800
  get: function() {
@@ -34948,6 +35819,12 @@ Object.defineProperty(exports, "buildLab", {
34948
35819
  return buildLab;
34949
35820
  }
34950
35821
  });
35822
+ Object.defineProperty(exports, "buildLatticeFormGouraudShading", {
35823
+ enumerable: true,
35824
+ get: function() {
35825
+ return buildLatticeFormGouraudShading;
35826
+ }
35827
+ });
34951
35828
  Object.defineProperty(exports, "buildNamespace", {
34952
35829
  enumerable: true,
34953
35830
  get: function() {
@@ -35056,6 +35933,12 @@ Object.defineProperty(exports, "buildStencilMask", {
35056
35933
  return buildStencilMask;
35057
35934
  }
35058
35935
  });
35936
+ Object.defineProperty(exports, "buildTensorPatchShading", {
35937
+ enumerable: true,
35938
+ get: function() {
35939
+ return buildTensorPatchShading;
35940
+ }
35941
+ });
35059
35942
  Object.defineProperty(exports, "buildThresholdHalftone", {
35060
35943
  enumerable: true,
35061
35944
  get: function() {
@@ -35272,6 +36155,12 @@ Object.defineProperty(exports, "detectTransparency", {
35272
36155
  return detectTransparency;
35273
36156
  }
35274
36157
  });
36158
+ Object.defineProperty(exports, "deviceRgbToXyz", {
36159
+ enumerable: true,
36160
+ get: function() {
36161
+ return deviceRgbToXyz;
36162
+ }
36163
+ });
35275
36164
  Object.defineProperty(exports, "didYouMean", {
35276
36165
  enumerable: true,
35277
36166
  get: function() {
@@ -35746,6 +36635,18 @@ Object.defineProperty(exports, "hasLtvData", {
35746
36635
  return hasLtvData;
35747
36636
  }
35748
36637
  });
36638
+ Object.defineProperty(exports, "hslToRgb", {
36639
+ enumerable: true,
36640
+ get: function() {
36641
+ return hslToRgb;
36642
+ }
36643
+ });
36644
+ Object.defineProperty(exports, "hsvToRgb", {
36645
+ enumerable: true,
36646
+ get: function() {
36647
+ return hsvToRgb;
36648
+ }
36649
+ });
35749
36650
  Object.defineProperty(exports, "identityTransferFunction", {
35750
36651
  enumerable: true,
35751
36652
  get: function() {
@@ -35938,6 +36839,12 @@ Object.defineProperty(exports, "parseExistingTrailer", {
35938
36839
  return parseExistingTrailer;
35939
36840
  }
35940
36841
  });
36842
+ Object.defineProperty(exports, "parseIccTransform", {
36843
+ enumerable: true,
36844
+ get: function() {
36845
+ return parseIccTransform;
36846
+ }
36847
+ });
35941
36848
  Object.defineProperty(exports, "parseTileInfo", {
35942
36849
  enumerable: true,
35943
36850
  get: function() {
@@ -36172,6 +37079,30 @@ Object.defineProperty(exports, "resolveInstanceCoordinates", {
36172
37079
  return resolveInstanceCoordinates;
36173
37080
  }
36174
37081
  });
37082
+ Object.defineProperty(exports, "rgbToHsl", {
37083
+ enumerable: true,
37084
+ get: function() {
37085
+ return rgbToHsl;
37086
+ }
37087
+ });
37088
+ Object.defineProperty(exports, "rgbToHsv", {
37089
+ enumerable: true,
37090
+ get: function() {
37091
+ return rgbToHsv;
37092
+ }
37093
+ });
37094
+ Object.defineProperty(exports, "rgbToLab", {
37095
+ enumerable: true,
37096
+ get: function() {
37097
+ return rgbToLab;
37098
+ }
37099
+ });
37100
+ Object.defineProperty(exports, "rgbToXyz", {
37101
+ enumerable: true,
37102
+ get: function() {
37103
+ return rgbToXyz;
37104
+ }
37105
+ });
36175
37106
  Object.defineProperty(exports, "sampleShadingColor", {
36176
37107
  enumerable: true,
36177
37108
  get: function() {
@@ -36514,5 +37445,17 @@ Object.defineProperty(exports, "wrapText", {
36514
37445
  return wrapText$2;
36515
37446
  }
36516
37447
  });
37448
+ Object.defineProperty(exports, "xyzToLab", {
37449
+ enumerable: true,
37450
+ get: function() {
37451
+ return xyzToLab;
37452
+ }
37453
+ });
37454
+ Object.defineProperty(exports, "xyzToRgb", {
37455
+ enumerable: true,
37456
+ get: function() {
37457
+ return xyzToRgb;
37458
+ }
37459
+ });
36517
37460
 
36518
- //# sourceMappingURL=src-MIBq9xGq.cjs.map
37461
+ //# sourceMappingURL=src-CbPUzRbg.cjs.map