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.
- package/README.md +3 -3
- package/dist/browser.cjs +15 -1
- package/dist/browser.d.cts +2 -2
- package/dist/browser.d.mts +2 -2
- package/dist/browser.mjs +2 -2
- package/dist/{index-06FgCx99.d.cts → index-BnS4WpNK.d.cts} +399 -3
- package/dist/index-BnS4WpNK.d.cts.map +1 -0
- package/dist/{index-Bg3y1BTk.d.mts → index-n0WBuf3b.d.mts} +399 -3
- package/dist/index-n0WBuf3b.d.mts.map +1 -0
- package/dist/index.cjs +15 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.mts +2 -2
- package/dist/index.mjs +2 -2
- package/dist/{src-MIBq9xGq.cjs → src-CbPUzRbg.cjs} +958 -15
- package/dist/{src-4wfgzZvz.mjs → src-DGxzt-jG.mjs} +875 -16
- package/package.json +1 -1
- package/dist/index-06FgCx99.d.cts.map +0 -1
- package/dist/index-Bg3y1BTk.d.mts.map +0 -1
|
@@ -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-
|
|
37461
|
+
//# sourceMappingURL=src-CbPUzRbg.cjs.map
|