modern-pdf-lib 0.28.1 → 0.30.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.
Files changed (44) hide show
  1. package/README.md +10 -7
  2. package/dist/browser.cjs +31 -3
  3. package/dist/browser.d.cts +5 -5
  4. package/dist/browser.d.mts +5 -5
  5. package/dist/browser.mjs +4 -4
  6. package/dist/{compressionAnalysis-InMeGrqw.d.mts → compressionAnalysis-0m5jJG1_.d.mts} +2 -2
  7. package/dist/{compressionAnalysis-CyBVhRU5.d.cts.map → compressionAnalysis-0m5jJG1_.d.mts.map} +1 -1
  8. package/dist/{compressionAnalysis-CyBVhRU5.d.cts → compressionAnalysis-CSBcmi0_.d.cts} +2 -2
  9. package/dist/{compressionAnalysis-InMeGrqw.d.mts.map → compressionAnalysis-CSBcmi0_.d.cts.map} +1 -1
  10. package/dist/create.cjs +2 -2
  11. package/dist/create.d.cts +2 -2
  12. package/dist/create.d.mts +2 -2
  13. package/dist/create.mjs +2 -2
  14. package/dist/{fontEmbed-N-VWoZ0F.d.mts → fontEmbed-Bzv89wgp.d.mts} +2 -2
  15. package/dist/{fontEmbed-DxtdZxUj.d.cts.map → fontEmbed-Bzv89wgp.d.mts.map} +1 -1
  16. package/dist/{fontEmbed-DxtdZxUj.d.cts → fontEmbed-Dwl1XeEA.d.cts} +2 -2
  17. package/dist/{fontEmbed-N-VWoZ0F.d.mts.map → fontEmbed-Dwl1XeEA.d.cts.map} +1 -1
  18. package/dist/{index-_Onej9Wy.d.cts → index-BJ4KxqDL.d.cts} +807 -68
  19. package/dist/index-BJ4KxqDL.d.cts.map +1 -0
  20. package/dist/{index-5Fv1-Z-p.d.mts → index-CERay5r2.d.mts} +807 -68
  21. package/dist/index-CERay5r2.d.mts.map +1 -0
  22. package/dist/index.cjs +31 -3
  23. package/dist/index.d.cts +5 -5
  24. package/dist/index.d.mts +5 -5
  25. package/dist/index.mjs +4 -4
  26. package/dist/{layout-DiO_3JLh.cjs → layout-BYXpcYtC.cjs} +2 -2
  27. package/dist/{layout-D035GpU8.mjs → layout-xOcesNCh.mjs} +2 -2
  28. package/dist/parse.cjs +1 -1
  29. package/dist/parse.d.cts +2 -2
  30. package/dist/parse.d.mts +2 -2
  31. package/dist/parse.mjs +1 -1
  32. package/dist/{pdfDocument-5Do0LKQh.d.cts → pdfDocument-B-8iPl_j.d.mts} +85 -3
  33. package/dist/pdfDocument-B-8iPl_j.d.mts.map +1 -0
  34. package/dist/{pdfDocument-B_0VSUHL.d.mts → pdfDocument-B8tQcZqj.d.cts} +85 -3
  35. package/dist/pdfDocument-B8tQcZqj.d.cts.map +1 -0
  36. package/dist/{pdfDocument-BaQlOkYr.cjs → pdfDocument-Bh4ebDqE.cjs} +41 -2
  37. package/dist/{pdfDocument-B6czyaVI.mjs → pdfDocument-Cgkkk8F8.mjs} +41 -2
  38. package/dist/{src-vNbsqoIy.cjs → src-Tb7wHOKz.cjs} +2764 -18
  39. package/dist/{src-BOzRFaEE.mjs → src-s9zNsZAT.mjs} +2599 -21
  40. package/package.json +3 -2
  41. package/dist/index-5Fv1-Z-p.d.mts.map +0 -1
  42. package/dist/index-_Onej9Wy.d.cts.map +0 -1
  43. package/dist/pdfDocument-5Do0LKQh.d.cts.map +0 -1
  44. package/dist/pdfDocument-B_0VSUHL.d.mts.map +0 -1
@@ -1,6 +1,9 @@
1
- import { A as encodeUTCTime, Br as rectangle, Bt as PageSizes, C as encodeInteger, E as encodeOctetString, F as parseDerTlv, Gr as setLineWidth, H as mergePdfs, I as toBuffer, Jr as beginText, Kn as applyFillColor, L as computeSignatureHash, M as extractIssuerAndSerial, N as extractSubjectPublicKeyInfo, O as encodeSequence, P as getSubtle, Pr as fill, Rn as restoreState, Rr as lineTo, S as encodeContextTag, Sn as formatPdfDate, T as encodeOID, Vn as saveState, Vr as setDashPattern, Xr as moveText, Yr as endText, at as sha256, b as detectKeyAlgorithm, ei as setFont, gn as buildAnnotationDict, gt as PdfParseError, k as encodeSet, mn as PdfAnnotation, n as StandardFonts, oi as setWordSpacing, qn as applyStrokeColor, qr as stroke, r as createPdf, s as setPageLabels, si as showText, t as PdfDocument, x as detectNamedCurve, y as decodeOidBytes, z as findSignatures, zr as moveTo } from "./pdfDocument-B6czyaVI.mjs";
2
- import { i as PdfName, l as PdfStream, n as PdfBool, o as PdfNumber, r as PdfDict, t as PdfArray, u as PdfString } from "./pdfObjects-CB7TEdbW.mjs";
1
+ import { A as encodeUTCTime, Br as rectangle, Bt as PageSizes, C as encodeInteger, E as encodeOctetString, F as parseDerTlv, Gr as setLineWidth, H as mergePdfs, I as toBuffer, Jr as beginText, Kn as applyFillColor, L as computeSignatureHash, M as extractIssuerAndSerial, N as extractSubjectPublicKeyInfo, O as encodeSequence, P as getSubtle, Pr as fill, Rn as restoreState, Rr as lineTo, S as encodeContextTag, Sn as formatPdfDate, T as encodeOID, Vn as saveState, Vr as setDashPattern, Xr as moveText, Yn as cmykToRgb, Yr as endText, at as sha256, b as detectKeyAlgorithm, ei as setFont, gn as buildAnnotationDict, gt as PdfParseError, k as encodeSet, mn as PdfAnnotation, n as StandardFonts, oi as setWordSpacing, qn as applyStrokeColor, qr as stroke, r as createPdf, s as setPageLabels, si as showText$1, t as PdfDocument, x as detectNamedCurve, y as decodeOidBytes, z as findSignatures, zr as moveTo } from "./pdfDocument-Cgkkk8F8.mjs";
2
+ import { c as PdfRef, i as PdfName, l as PdfStream, n as PdfBool, o as PdfNumber, r as PdfDict, t as PdfArray, u as PdfString } from "./pdfObjects-CB7TEdbW.mjs";
3
+ import { n as decodeStreamData, r as getStreamFilters } from "./streamDecode-DCpgTocN.mjs";
3
4
  import { g as FieldFlags, v as numVal } from "./pdfForm-CfLnlsj-.mjs";
5
+ import { i as parseContentStream } from "./compressionAnalysis-BEd_745I.mjs";
6
+ import { n as decodeJpegWasm } from "./bridge-7dhURZj7.mjs";
4
7
  import { r as detectRuntime } from "./loader-DkrXz-h4.mjs";
5
8
  import { deflateSync, inflateSync, unzlibSync } from "fflate";
6
9
  //#region src/core/incrementalWriter.ts
@@ -274,7 +277,7 @@ function compressStream(stream, level) {
274
277
  * @returns The incremental save result.
275
278
  */
276
279
  async function saveDocumentIncremental(originalBytes, doc, options) {
277
- const { buildDocumentStructure } = await import("./pdfDocument-B6czyaVI.mjs").then((n) => n.Cn);
280
+ const { buildDocumentStructure } = await import("./pdfDocument-Cgkkk8F8.mjs").then((n) => n.Cn);
278
281
  const registry = doc.getRegistry();
279
282
  const structure = buildDocumentStructure(doc.getInternalPages().map((p) => p.finalize()), {
280
283
  producer: doc.getProducer(),
@@ -13324,7 +13327,7 @@ function renderStyledBarcode(matrix, x, y, text, options) {
13324
13327
  ops += setFont(fontName, fontSize);
13325
13328
  ops += applyFillColor(textColor);
13326
13329
  ops += moveText(textX, textY);
13327
- ops += showText(text);
13330
+ ops += showText$1(text);
13328
13331
  ops += endText();
13329
13332
  }
13330
13333
  ops += restoreState();
@@ -17923,7 +17926,7 @@ function skipWS(data, pos) {
17923
17926
  * Read a decimal integer from `data` starting at `pos`.
17924
17927
  * Returns `{ value, nextPos }`.
17925
17928
  */
17926
- function readInt(data, pos) {
17929
+ function readInt$1(data, pos) {
17927
17930
  let value = 0;
17928
17931
  let started = false;
17929
17932
  while (pos < data.length) {
@@ -18472,10 +18475,10 @@ var StreamingPdfParser = class StreamingPdfParser {
18472
18475
  pos = skipWS(data, pos);
18473
18476
  while (pos < data.length) {
18474
18477
  if (pos + 7 <= data.length && data[pos] === 116 && data[pos + 1] === 114 && data[pos + 2] === 97 && data[pos + 3] === 105 && data[pos + 4] === 108 && data[pos + 5] === 101 && data[pos + 6] === 114) break;
18475
- const firstObjResult = readInt(data, pos);
18478
+ const firstObjResult = readInt$1(data, pos);
18476
18479
  if (firstObjResult.value === -1) break;
18477
18480
  pos = skipWS(data, firstObjResult.nextPos);
18478
- const countResult = readInt(data, pos);
18481
+ const countResult = readInt$1(data, pos);
18479
18482
  if (countResult.value === -1) break;
18480
18483
  pos = skipWS(data, countResult.nextPos);
18481
18484
  const firstObjNum = firstObjResult.value;
@@ -18554,8 +18557,8 @@ var StreamingPdfParser = class StreamingPdfParser {
18554
18557
  }
18555
18558
  parseXrefStream(data, offset) {
18556
18559
  let pos = skipWS(data, offset);
18557
- pos = skipWS(data, readInt(data, pos).nextPos);
18558
- pos = skipWS(data, readInt(data, pos).nextPos);
18560
+ pos = skipWS(data, readInt$1(data, pos).nextPos);
18561
+ pos = skipWS(data, readInt$1(data, pos).nextPos);
18559
18562
  if (pos + 3 <= data.length && data[pos] === 111 && data[pos + 1] === 98 && data[pos + 2] === 106) pos += 3;
18560
18563
  pos = skipWS(data, pos);
18561
18564
  const dictResult = parseLightDict(data, pos);
@@ -18642,10 +18645,10 @@ var StreamingPdfParser = class StreamingPdfParser {
18642
18645
  */
18643
18646
  parseObjectDict(data, offset) {
18644
18647
  let pos = skipWS(data, offset);
18645
- const objNumResult = readInt(data, pos);
18648
+ const objNumResult = readInt$1(data, pos);
18646
18649
  if (objNumResult.value === -1) return void 0;
18647
18650
  pos = skipWS(data, objNumResult.nextPos);
18648
- const genResult = readInt(data, pos);
18651
+ const genResult = readInt$1(data, pos);
18649
18652
  if (genResult.value === -1) return void 0;
18650
18653
  pos = skipWS(data, genResult.nextPos);
18651
18654
  if (pos + 3 <= data.length && data[pos] === 111 && data[pos + 1] === 98 && data[pos + 2] === 106) pos += 3;
@@ -18741,10 +18744,10 @@ var StreamingPdfParser = class StreamingPdfParser {
18741
18744
  while (pos < data.length) {
18742
18745
  pos = skipWS(data, pos);
18743
18746
  if (pos >= data.length || data[pos] === 93) break;
18744
- const objResult = readInt(data, pos);
18747
+ const objResult = readInt$1(data, pos);
18745
18748
  if (objResult.value === -1) break;
18746
18749
  pos = skipWS(data, objResult.nextPos);
18747
- const genResult = readInt(data, pos);
18750
+ const genResult = readInt$1(data, pos);
18748
18751
  if (genResult.value === -1) break;
18749
18752
  pos = skipWS(data, genResult.nextPos);
18750
18753
  if (pos < data.length && data[pos] === 82) {
@@ -22590,7 +22593,7 @@ function parseFormattedNumber(text) {
22590
22593
  * @param options Formatting options.
22591
22594
  * @returns The formatted string.
22592
22595
  */
22593
- function formatNumber(value, options = {}) {
22596
+ function formatNumber$1(value, options = {}) {
22594
22597
  const { decimals = 2, thousandsSep = ",", decimalSep = ".", negativeStyle = "minus", currency = "", currencyPrepend = true } = options;
22595
22598
  const isNeg = value < 0;
22596
22599
  const fixed = Math.abs(value).toFixed(decimals);
@@ -22625,7 +22628,7 @@ function AFNumber_Format(nDec, sepStyle, negStyle, currStyle, strCurrency, bCurr
22625
22628
  return (value) => {
22626
22629
  const num = parseFormattedNumber(value);
22627
22630
  if (Number.isNaN(num)) return value;
22628
- return formatNumber(num, {
22631
+ return formatNumber$1(num, {
22629
22632
  decimals: nDec,
22630
22633
  thousandsSep: seps.thousands,
22631
22634
  decimalSep: seps.decimal,
@@ -26385,7 +26388,7 @@ function renderLine(line, x, y, availableWidth, alignment, measure, defaultFontN
26385
26388
  ops += moveText(spaceWidth, 0);
26386
26389
  currentX += spaceWidth;
26387
26390
  }
26388
- ops += showText(word.text);
26391
+ ops += showText$1(word.text);
26389
26392
  currentX += word.width;
26390
26393
  if (word.underline) {
26391
26394
  ops += endText();
@@ -27838,6 +27841,2581 @@ function extractNodeBytesFromParent(fullBuffer, parent, child) {
27838
27841
  return fullBuffer.subarray(start, start + child.totalLength);
27839
27842
  }
27840
27843
  //#endregion
27844
+ //#region src/render/matrix.ts
27845
+ /** The identity transform. */
27846
+ function identity() {
27847
+ return [
27848
+ 1,
27849
+ 0,
27850
+ 0,
27851
+ 1,
27852
+ 0,
27853
+ 0
27854
+ ];
27855
+ }
27856
+ /**
27857
+ * Concatenate two transforms: the result applies `m` **first**, then `n`
27858
+ * (i.e. `point · m · n`). This matches the PDF `cm` operator, where the new
27859
+ * CTM is `cmMatrix × CTM`.
27860
+ */
27861
+ function multiply(m, n) {
27862
+ return [
27863
+ m[0] * n[0] + m[1] * n[2],
27864
+ m[0] * n[1] + m[1] * n[3],
27865
+ m[2] * n[0] + m[3] * n[2],
27866
+ m[2] * n[1] + m[3] * n[3],
27867
+ m[4] * n[0] + m[5] * n[2] + n[4],
27868
+ m[4] * n[1] + m[5] * n[3] + n[5]
27869
+ ];
27870
+ }
27871
+ /** Apply a transform to a point, returning the transformed `[x, y]`. */
27872
+ function applyToPoint(m, x, y) {
27873
+ return [m[0] * x + m[2] * y + m[4], m[1] * x + m[3] * y + m[5]];
27874
+ }
27875
+ /** A pure translation transform. */
27876
+ function translation(tx, ty) {
27877
+ return [
27878
+ 1,
27879
+ 0,
27880
+ 0,
27881
+ 1,
27882
+ tx,
27883
+ ty
27884
+ ];
27885
+ }
27886
+ /**
27887
+ * The magnitude by which `m` scales lengths — the geometric mean of the
27888
+ * x- and y-axis scale factors (`sqrt(|det|)`). Used to map a user-space line
27889
+ * width to device space.
27890
+ */
27891
+ function meanScale(m) {
27892
+ const det = m[0] * m[3] - m[1] * m[2];
27893
+ return Math.sqrt(Math.abs(det));
27894
+ }
27895
+ //#endregion
27896
+ //#region src/render/interpreter.ts
27897
+ /**
27898
+ * @module render/interpreter
27899
+ *
27900
+ * Executes a parsed PDF content stream through a graphics-state machine,
27901
+ * producing a resolution-independent {@link DisplayList} (page space, y-up).
27902
+ * This is the foundation the rasterizer and Canvas adapter render from.
27903
+ *
27904
+ * Supported operators: graphics state (`q`/`Q`/`cm`/`w`/`J`/`j`/`gs`), path
27905
+ * construction (`m`/`l`/`c`/`v`/`y`/`re`/`h`), path painting
27906
+ * (`f`/`F`/`f*`/`S`/`s`/`B`/`B*`/`b`/`b*`/`n`), clipping (`W`/`W*`), color
27907
+ * (`rg`/`RG`/`g`/`G`/`k`/`K`/`cs`/`CS`/`sc`/`scn`/`SC`/`SCN`), text
27908
+ * (`BT`/`ET`/`Tf`/`Td`/`TD`/`Tm`/`T*`/`Tc`/`Tw`/`Tz`/`TL`/`Ts`/`Tr`/`Tj`/`TJ`/`'`/`"`),
27909
+ * and XObjects (`Do`, recursing into form XObjects). Bézier curves are
27910
+ * flattened to polylines.
27911
+ *
27912
+ * @packageDocumentation
27913
+ */
27914
+ function initialState$1() {
27915
+ return {
27916
+ ctm: identity(),
27917
+ fill: [
27918
+ 0,
27919
+ 0,
27920
+ 0,
27921
+ 255
27922
+ ],
27923
+ stroke: [
27924
+ 0,
27925
+ 0,
27926
+ 0,
27927
+ 255
27928
+ ],
27929
+ lineWidth: 1,
27930
+ lineCap: 0,
27931
+ lineJoin: 0,
27932
+ fillAlpha: 1,
27933
+ strokeAlpha: 1,
27934
+ clip: void 0,
27935
+ font: void 0,
27936
+ fontSize: 0,
27937
+ charSpace: 0,
27938
+ wordSpace: 0,
27939
+ hScale: 1,
27940
+ leading: 0,
27941
+ rise: 0,
27942
+ renderMode: 0
27943
+ };
27944
+ }
27945
+ function cloneState(s) {
27946
+ return { ...s };
27947
+ }
27948
+ function num$2(op) {
27949
+ return typeof op === "number" ? op : 0;
27950
+ }
27951
+ function nameOf(op) {
27952
+ if (op instanceof PdfName) return op.value.replace(/^\//, "");
27953
+ if (typeof op === "string") return op.replace(/^\//, "");
27954
+ }
27955
+ function clamp255(v) {
27956
+ return Math.max(0, Math.min(255, Math.round(v * 255)));
27957
+ }
27958
+ function grayRgba(g) {
27959
+ const v = clamp255(g);
27960
+ return [
27961
+ v,
27962
+ v,
27963
+ v,
27964
+ 255
27965
+ ];
27966
+ }
27967
+ function rgbRgba(r, g, b) {
27968
+ return [
27969
+ clamp255(r),
27970
+ clamp255(g),
27971
+ clamp255(b),
27972
+ 255
27973
+ ];
27974
+ }
27975
+ function cmykRgba(c, m, y, k) {
27976
+ const [r, g, b] = cmykToRgb(c, m, y, k);
27977
+ return [
27978
+ clamp255(r),
27979
+ clamp255(g),
27980
+ clamp255(b),
27981
+ 255
27982
+ ];
27983
+ }
27984
+ /** Interpret `sc`/`scn` numeric components by arity → RGBA. */
27985
+ function componentsToRgba(comps) {
27986
+ if (comps.length === 1) return grayRgba(comps[0]);
27987
+ if (comps.length === 3) return rgbRgba(comps[0], comps[1], comps[2]);
27988
+ if (comps.length === 4) return cmykRgba(comps[0], comps[1], comps[2], comps[3]);
27989
+ }
27990
+ const BEZIER_STEPS = 16;
27991
+ function flattenCubic(out, p0, p1, p2, p3) {
27992
+ for (let i = 1; i <= BEZIER_STEPS; i++) {
27993
+ const t = i / BEZIER_STEPS;
27994
+ const mt = 1 - t;
27995
+ const a = mt * mt * mt;
27996
+ const b = 3 * mt * mt * t;
27997
+ const c = 3 * mt * t * t;
27998
+ const d = t * t * t;
27999
+ out.push(a * p0[0] + b * p1[0] + c * p2[0] + d * p3[0], a * p0[1] + b * p1[1] + c * p2[1] + d * p3[1]);
28000
+ }
28001
+ }
28002
+ /**
28003
+ * Execute a parsed content stream into a {@link DisplayList}.
28004
+ *
28005
+ * @param operators - Output of {@link parseContentStream}.
28006
+ * @param options - Page dimensions and (optional) resources/registry.
28007
+ */
28008
+ function interpretContentStream(operators, options) {
28009
+ const items = [];
28010
+ const origin = options.origin ?? [0, 0];
28011
+ interpretInto(items, operators, initialState$1(), options, 0);
28012
+ return {
28013
+ items,
28014
+ width: options.width,
28015
+ height: options.height,
28016
+ origin
28017
+ };
28018
+ }
28019
+ const MAX_FORM_DEPTH = 12;
28020
+ function interpretInto(items, operators, startState, options, depth) {
28021
+ const stack = [];
28022
+ let gs = startState;
28023
+ let path = [];
28024
+ let cur = null;
28025
+ let curUser = [0, 0];
28026
+ let curPage = [0, 0];
28027
+ let pendingClip = null;
28028
+ let tm = identity();
28029
+ let tlm = identity();
28030
+ const startSub = (x, y) => {
28031
+ if (cur && cur.length >= 4) path.push({
28032
+ points: cur,
28033
+ closed: false
28034
+ });
28035
+ curUser = [x, y];
28036
+ curPage = applyToPoint(gs.ctm, x, y);
28037
+ cur = [curPage[0], curPage[1]];
28038
+ };
28039
+ const lineTo = (x, y) => {
28040
+ if (!cur) startSub(x, y);
28041
+ curUser = [x, y];
28042
+ curPage = applyToPoint(gs.ctm, x, y);
28043
+ cur.push(curPage[0], curPage[1]);
28044
+ };
28045
+ const finishPath = () => {
28046
+ if (cur && cur.length >= 4) path.push({
28047
+ points: cur,
28048
+ closed: false
28049
+ });
28050
+ const result = path;
28051
+ path = [];
28052
+ cur = null;
28053
+ return result;
28054
+ };
28055
+ const applyClipIfPending = (subs) => {
28056
+ if (pendingClip) {
28057
+ gs = {
28058
+ ...gs,
28059
+ clip: subs.map((s) => ({
28060
+ points: s.points.slice(),
28061
+ closed: true
28062
+ }))
28063
+ };
28064
+ pendingClip = null;
28065
+ }
28066
+ };
28067
+ const pushOpen = (closed) => {
28068
+ if (cur && cur.length >= 4) path.push({
28069
+ points: cur,
28070
+ closed
28071
+ });
28072
+ cur = null;
28073
+ };
28074
+ for (const { operator, operands } of operators) switch (operator) {
28075
+ case "q":
28076
+ stack.push(cloneState(gs));
28077
+ break;
28078
+ case "Q":
28079
+ if (stack.length) gs = stack.pop();
28080
+ break;
28081
+ case "cm":
28082
+ gs = {
28083
+ ...gs,
28084
+ ctm: multiply([
28085
+ num$2(operands[0]),
28086
+ num$2(operands[1]),
28087
+ num$2(operands[2]),
28088
+ num$2(operands[3]),
28089
+ num$2(operands[4]),
28090
+ num$2(operands[5])
28091
+ ], gs.ctm)
28092
+ };
28093
+ break;
28094
+ case "w":
28095
+ gs = {
28096
+ ...gs,
28097
+ lineWidth: num$2(operands[0])
28098
+ };
28099
+ break;
28100
+ case "J":
28101
+ gs = {
28102
+ ...gs,
28103
+ lineCap: num$2(operands[0]) | 0
28104
+ };
28105
+ break;
28106
+ case "j":
28107
+ gs = {
28108
+ ...gs,
28109
+ lineJoin: num$2(operands[0]) | 0
28110
+ };
28111
+ break;
28112
+ case "gs":
28113
+ gs = applyExtGState(gs, nameOf(operands[0]), options);
28114
+ break;
28115
+ case "rg":
28116
+ gs = {
28117
+ ...gs,
28118
+ fill: rgbRgba(num$2(operands[0]), num$2(operands[1]), num$2(operands[2]))
28119
+ };
28120
+ break;
28121
+ case "RG":
28122
+ gs = {
28123
+ ...gs,
28124
+ stroke: rgbRgba(num$2(operands[0]), num$2(operands[1]), num$2(operands[2]))
28125
+ };
28126
+ break;
28127
+ case "g":
28128
+ gs = {
28129
+ ...gs,
28130
+ fill: grayRgba(num$2(operands[0]))
28131
+ };
28132
+ break;
28133
+ case "G":
28134
+ gs = {
28135
+ ...gs,
28136
+ stroke: grayRgba(num$2(operands[0]))
28137
+ };
28138
+ break;
28139
+ case "k":
28140
+ gs = {
28141
+ ...gs,
28142
+ fill: cmykRgba(num$2(operands[0]), num$2(operands[1]), num$2(operands[2]), num$2(operands[3]))
28143
+ };
28144
+ break;
28145
+ case "K":
28146
+ gs = {
28147
+ ...gs,
28148
+ stroke: cmykRgba(num$2(operands[0]), num$2(operands[1]), num$2(operands[2]), num$2(operands[3]))
28149
+ };
28150
+ break;
28151
+ case "sc":
28152
+ case "scn": {
28153
+ const rgba = componentsToRgba(operands.filter((o) => typeof o === "number"));
28154
+ if (rgba) gs = {
28155
+ ...gs,
28156
+ fill: rgba
28157
+ };
28158
+ break;
28159
+ }
28160
+ case "SC":
28161
+ case "SCN": {
28162
+ const rgba = componentsToRgba(operands.filter((o) => typeof o === "number"));
28163
+ if (rgba) gs = {
28164
+ ...gs,
28165
+ stroke: rgba
28166
+ };
28167
+ break;
28168
+ }
28169
+ case "m":
28170
+ startSub(num$2(operands[0]), num$2(operands[1]));
28171
+ break;
28172
+ case "l":
28173
+ lineTo(num$2(operands[0]), num$2(operands[1]));
28174
+ break;
28175
+ case "c": {
28176
+ if (!cur) startSub(curUser[0], curUser[1]);
28177
+ const p1 = applyToPoint(gs.ctm, num$2(operands[0]), num$2(operands[1]));
28178
+ const p2 = applyToPoint(gs.ctm, num$2(operands[2]), num$2(operands[3]));
28179
+ const p3 = applyToPoint(gs.ctm, num$2(operands[4]), num$2(operands[5]));
28180
+ flattenCubic(cur, curPage, p1, p2, p3);
28181
+ curUser = [num$2(operands[4]), num$2(operands[5])];
28182
+ curPage = p3;
28183
+ break;
28184
+ }
28185
+ case "v": {
28186
+ if (!cur) startSub(curUser[0], curUser[1]);
28187
+ const p2 = applyToPoint(gs.ctm, num$2(operands[0]), num$2(operands[1]));
28188
+ const p3 = applyToPoint(gs.ctm, num$2(operands[2]), num$2(operands[3]));
28189
+ flattenCubic(cur, curPage, curPage, p2, p3);
28190
+ curUser = [num$2(operands[2]), num$2(operands[3])];
28191
+ curPage = p3;
28192
+ break;
28193
+ }
28194
+ case "y": {
28195
+ if (!cur) startSub(curUser[0], curUser[1]);
28196
+ const p1 = applyToPoint(gs.ctm, num$2(operands[0]), num$2(operands[1]));
28197
+ const p3 = applyToPoint(gs.ctm, num$2(operands[2]), num$2(operands[3]));
28198
+ flattenCubic(cur, curPage, p1, p3, p3);
28199
+ curUser = [num$2(operands[2]), num$2(operands[3])];
28200
+ curPage = p3;
28201
+ break;
28202
+ }
28203
+ case "re": {
28204
+ const x = num$2(operands[0]);
28205
+ const y = num$2(operands[1]);
28206
+ const w = num$2(operands[2]);
28207
+ const h = num$2(operands[3]);
28208
+ pushOpen(false);
28209
+ const corners = [
28210
+ applyToPoint(gs.ctm, x, y),
28211
+ applyToPoint(gs.ctm, x + w, y),
28212
+ applyToPoint(gs.ctm, x + w, y + h),
28213
+ applyToPoint(gs.ctm, x, y + h)
28214
+ ];
28215
+ path.push({
28216
+ points: corners.flat(),
28217
+ closed: true
28218
+ });
28219
+ cur = null;
28220
+ curUser = [x, y];
28221
+ curPage = corners[0];
28222
+ break;
28223
+ }
28224
+ case "h":
28225
+ pushOpen(true);
28226
+ break;
28227
+ case "f":
28228
+ case "F":
28229
+ case "f*": {
28230
+ const subs = finishPath();
28231
+ emitFill(items, subs, operator === "f*" ? "evenodd" : "nonzero", gs);
28232
+ applyClipIfPending(subs);
28233
+ break;
28234
+ }
28235
+ case "S":
28236
+ case "s": {
28237
+ if (operator === "s") pushOpen(true);
28238
+ const subs = finishPath();
28239
+ emitStroke(items, subs, gs);
28240
+ applyClipIfPending(subs);
28241
+ break;
28242
+ }
28243
+ case "B":
28244
+ case "B*":
28245
+ case "b":
28246
+ case "b*": {
28247
+ if (operator === "b" || operator === "b*") pushOpen(true);
28248
+ const subs = finishPath();
28249
+ emitFill(items, subs, operator === "B*" || operator === "b*" ? "evenodd" : "nonzero", gs);
28250
+ emitStroke(items, subs, gs);
28251
+ applyClipIfPending(subs);
28252
+ break;
28253
+ }
28254
+ case "n":
28255
+ applyClipIfPending(finishPath());
28256
+ break;
28257
+ case "W":
28258
+ pendingClip = "nonzero";
28259
+ break;
28260
+ case "W*":
28261
+ pendingClip = "evenodd";
28262
+ break;
28263
+ case "BT":
28264
+ tm = identity();
28265
+ tlm = identity();
28266
+ break;
28267
+ case "ET": break;
28268
+ case "Tf":
28269
+ gs = {
28270
+ ...gs,
28271
+ font: nameOf(operands[0]),
28272
+ fontSize: num$2(operands[1])
28273
+ };
28274
+ break;
28275
+ case "Tc":
28276
+ gs = {
28277
+ ...gs,
28278
+ charSpace: num$2(operands[0])
28279
+ };
28280
+ break;
28281
+ case "Tw":
28282
+ gs = {
28283
+ ...gs,
28284
+ wordSpace: num$2(operands[0])
28285
+ };
28286
+ break;
28287
+ case "Tz":
28288
+ gs = {
28289
+ ...gs,
28290
+ hScale: num$2(operands[0]) / 100
28291
+ };
28292
+ break;
28293
+ case "TL":
28294
+ gs = {
28295
+ ...gs,
28296
+ leading: num$2(operands[0])
28297
+ };
28298
+ break;
28299
+ case "Ts":
28300
+ gs = {
28301
+ ...gs,
28302
+ rise: num$2(operands[0])
28303
+ };
28304
+ break;
28305
+ case "Tr":
28306
+ gs = {
28307
+ ...gs,
28308
+ renderMode: num$2(operands[0]) | 0
28309
+ };
28310
+ break;
28311
+ case "Td":
28312
+ tlm = multiply(translation(num$2(operands[0]), num$2(operands[1])), tlm);
28313
+ tm = tlm;
28314
+ break;
28315
+ case "TD":
28316
+ gs = {
28317
+ ...gs,
28318
+ leading: -num$2(operands[1])
28319
+ };
28320
+ tlm = multiply(translation(num$2(operands[0]), num$2(operands[1])), tlm);
28321
+ tm = tlm;
28322
+ break;
28323
+ case "Tm":
28324
+ tm = [
28325
+ num$2(operands[0]),
28326
+ num$2(operands[1]),
28327
+ num$2(operands[2]),
28328
+ num$2(operands[3]),
28329
+ num$2(operands[4]),
28330
+ num$2(operands[5])
28331
+ ];
28332
+ tlm = tm;
28333
+ break;
28334
+ case "T*":
28335
+ tlm = multiply(translation(0, -gs.leading), tlm);
28336
+ tm = tlm;
28337
+ break;
28338
+ case "Tj":
28339
+ tm = showText(items, textString(operands[0]), tm, gs);
28340
+ break;
28341
+ case "TJ": {
28342
+ const arr = Array.isArray(operands[0]) ? operands[0] : [];
28343
+ let combined = "";
28344
+ for (const el of arr) if (typeof el === "number") tm = multiply(translation(-el / 1e3 * gs.fontSize * gs.hScale, 0), tm);
28345
+ else {
28346
+ combined += textString(el);
28347
+ tm = showText(items, textString(el), tm, gs);
28348
+ }
28349
+ break;
28350
+ }
28351
+ case "'":
28352
+ tlm = multiply(translation(0, -gs.leading), tlm);
28353
+ tm = tlm;
28354
+ tm = showText(items, textString(operands[0]), tm, gs);
28355
+ break;
28356
+ case "\"":
28357
+ gs = {
28358
+ ...gs,
28359
+ wordSpace: num$2(operands[0]),
28360
+ charSpace: num$2(operands[1])
28361
+ };
28362
+ tlm = multiply(translation(0, -gs.leading), tlm);
28363
+ tm = tlm;
28364
+ tm = showText(items, textString(operands[2]), tm, gs);
28365
+ break;
28366
+ case "Do":
28367
+ doXObject(items, nameOf(operands[0]), gs, options, depth);
28368
+ break;
28369
+ default: break;
28370
+ }
28371
+ }
28372
+ function freeze(subs) {
28373
+ return subs.map((s) => ({
28374
+ points: s.points,
28375
+ closed: s.closed
28376
+ }));
28377
+ }
28378
+ function emitFill(items, subs, rule, gs) {
28379
+ if (!subs.length) return;
28380
+ items.push({
28381
+ type: "fill",
28382
+ subpaths: freeze(subs),
28383
+ rule,
28384
+ color: gs.fill,
28385
+ alpha: gs.fillAlpha,
28386
+ clip: gs.clip
28387
+ });
28388
+ }
28389
+ function emitStroke(items, subs, gs) {
28390
+ if (!subs.length) return;
28391
+ items.push({
28392
+ type: "stroke",
28393
+ subpaths: freeze(subs),
28394
+ color: gs.stroke,
28395
+ alpha: gs.strokeAlpha,
28396
+ lineWidth: Math.max(gs.lineWidth * meanScale(gs.ctm), .1),
28397
+ lineCap: gs.lineCap,
28398
+ lineJoin: gs.lineJoin,
28399
+ clip: gs.clip
28400
+ });
28401
+ }
28402
+ /** Decode a content-stream string operand to text (best-effort latin-1/WinAnsi). */
28403
+ function textString(op) {
28404
+ return typeof op === "string" ? op : "";
28405
+ }
28406
+ /** Emit a text run and advance the text matrix by an approximate width. */
28407
+ function showText(items, text, tm, gs) {
28408
+ if (text.length) items.push({
28409
+ type: "text",
28410
+ text,
28411
+ font: gs.font,
28412
+ fontSize: gs.fontSize,
28413
+ transform: multiply(multiply([
28414
+ gs.fontSize * gs.hScale,
28415
+ 0,
28416
+ 0,
28417
+ gs.fontSize,
28418
+ 0,
28419
+ gs.rise
28420
+ ], tm), gs.ctm),
28421
+ color: gs.fill,
28422
+ alpha: gs.fillAlpha,
28423
+ renderMode: gs.renderMode,
28424
+ clip: gs.clip
28425
+ });
28426
+ let w = 0;
28427
+ for (const ch of text) w += .5 * gs.fontSize + gs.charSpace + (ch === " " ? gs.wordSpace : 0);
28428
+ return multiply(translation(w * gs.hScale, 0), tm);
28429
+ }
28430
+ function resolve$1(obj, options) {
28431
+ if (obj && obj.kind === "ref" && options.registry) return options.registry.resolve(obj);
28432
+ return obj;
28433
+ }
28434
+ function subDict(resources, key, options) {
28435
+ if (!resources) return void 0;
28436
+ const d = resolve$1(resources.get(key), options);
28437
+ return d && d.kind === "dict" ? d : void 0;
28438
+ }
28439
+ function applyExtGState(gs, name, options) {
28440
+ if (!name) return gs;
28441
+ const egs = subDict(options.resources, "/ExtGState", options);
28442
+ const dict = egs ? resolve$1(egs.get("/" + name), options) : void 0;
28443
+ if (!dict || dict.kind !== "dict") return gs;
28444
+ const d = dict;
28445
+ const ca = resolve$1(d.get("/ca"), options);
28446
+ const CA = resolve$1(d.get("/CA"), options);
28447
+ const LW = resolve$1(d.get("/LW"), options);
28448
+ const next = { ...gs };
28449
+ if (ca && ca.kind === "number") next.fillAlpha = ca.value;
28450
+ if (CA && CA.kind === "number") next.strokeAlpha = CA.value;
28451
+ if (LW && LW.kind === "number") next.lineWidth = LW.value;
28452
+ return next;
28453
+ }
28454
+ function doXObject(items, name, gs, options, depth) {
28455
+ if (!name) return;
28456
+ const xobjects = subDict(options.resources, "/XObject", options);
28457
+ const xobj = xobjects ? resolve$1(xobjects.get("/" + name), options) : void 0;
28458
+ if (!xobj || xobj.kind !== "stream") {
28459
+ items.push({
28460
+ type: "image",
28461
+ name,
28462
+ transform: gs.ctm,
28463
+ alpha: gs.fillAlpha,
28464
+ clip: gs.clip
28465
+ });
28466
+ return;
28467
+ }
28468
+ const stream = xobj;
28469
+ const subtype = stream.dict.get("/Subtype");
28470
+ if ((subtype instanceof PdfName ? subtype.value.replace(/^\//, "") : "") === "Form" && depth < MAX_FORM_DEPTH) {
28471
+ const matrixObj = resolve$1(stream.dict.get("/Matrix"), options);
28472
+ let formCtm = gs.ctm;
28473
+ if (matrixObj && matrixObj.kind === "array") {
28474
+ const m = matrixObj.items.map((o) => o && o.kind === "number" ? o.value : 0);
28475
+ if (m.length === 6) formCtm = multiply(m, gs.ctm);
28476
+ }
28477
+ const formResources = subDict(stream.dict, "/Resources", options) ?? options.resources;
28478
+ try {
28479
+ interpretInto(items, parseContentStream(decodeStreamData(stream)), {
28480
+ ...gs,
28481
+ ctm: formCtm
28482
+ }, {
28483
+ ...options,
28484
+ resources: formResources
28485
+ }, depth + 1);
28486
+ } catch {}
28487
+ return;
28488
+ }
28489
+ items.push({
28490
+ type: "image",
28491
+ name,
28492
+ transform: gs.ctm,
28493
+ alpha: gs.fillAlpha,
28494
+ clip: gs.clip
28495
+ });
28496
+ }
28497
+ /**
28498
+ * Interpret a {@link PdfPage}'s content into a {@link DisplayList}, using its
28499
+ * crop/media box for dimensions and its resources/registry for `Do`/`gs`/`Tf`
28500
+ * resolution. The entry point for {@link renderPageToImage} et al.
28501
+ */
28502
+ function interpretPage(page) {
28503
+ const box = page.getCropBox() ?? page.getMediaBox();
28504
+ return interpretContentStream(parseContentStream(page.getContentStream()), {
28505
+ width: box.width,
28506
+ height: box.height,
28507
+ origin: [box.x, box.y],
28508
+ resources: page.getOriginalResources(),
28509
+ registry: page.getRegistry()
28510
+ });
28511
+ }
28512
+ //#endregion
28513
+ //#region src/render/rasterizer.ts
28514
+ function resolveScale(opts) {
28515
+ if (typeof opts.scale === "number") return opts.scale;
28516
+ if (typeof opts.dpi === "number") return opts.dpi / 72;
28517
+ return 1;
28518
+ }
28519
+ /** Rasterize a display list to an RGBA8888 buffer. */
28520
+ function rasterize(dl, opts = {}) {
28521
+ const scale = resolveScale(opts);
28522
+ const fullH = Math.max(1, Math.ceil(dl.height * scale));
28523
+ const region = opts.region ?? {
28524
+ x: 0,
28525
+ y: 0,
28526
+ width: Math.max(1, Math.ceil(dl.width * scale)),
28527
+ height: fullH
28528
+ };
28529
+ const W = Math.max(1, region.width);
28530
+ const H = Math.max(1, region.height);
28531
+ const data = new Uint8Array(W * H * 4);
28532
+ const bg = opts.background ?? [
28533
+ 255,
28534
+ 255,
28535
+ 255,
28536
+ 255
28537
+ ];
28538
+ if (bg !== "transparent") for (let i = 0; i < data.length; i += 4) {
28539
+ data[i] = bg[0];
28540
+ data[i + 1] = bg[1];
28541
+ data[i + 2] = bg[2];
28542
+ data[i + 3] = bg[3];
28543
+ }
28544
+ const [ox, oy] = dl.origin;
28545
+ /** page space → region-local pixel space (with y-flip + tile offset). */
28546
+ const toPx = (x, y) => [(x - ox) * scale - region.x, fullH - (y - oy) * scale - region.y];
28547
+ const renderText = opts.renderText ?? true;
28548
+ for (const item of dl.items) {
28549
+ const clip = item.clip ? toPixelSubpaths(item.clip, toPx) : void 0;
28550
+ if (item.type === "fill") fillSubpaths(data, W, H, toPixelSubpaths(item.subpaths, toPx), item.rule, item.color, item.alpha, clip);
28551
+ else if (item.type === "stroke") strokeSubpaths(data, W, H, item.subpaths, toPx, item.lineWidth * scale, item.color, item.alpha, clip);
28552
+ else if (item.type === "text" && renderText && item.renderMode !== 3 && item.renderMode !== 7) fillTextBoxes(data, W, H, item, toPx, clip);
28553
+ }
28554
+ return {
28555
+ data,
28556
+ width: W,
28557
+ height: H
28558
+ };
28559
+ }
28560
+ /**
28561
+ * Render a {@link PdfPage} to a PNG image.
28562
+ * @returns The PNG bytes plus pixel dimensions.
28563
+ */
28564
+ async function renderPageToImage(page, opts = {}) {
28565
+ const img = rasterize(interpretPage(page), opts);
28566
+ return {
28567
+ data: encodePngFromPixels(img.data, img.width, img.height, 4),
28568
+ width: img.width,
28569
+ height: img.height
28570
+ };
28571
+ }
28572
+ function toPixelSubpaths(subpaths, toPx) {
28573
+ return subpaths.map((s) => {
28574
+ const out = [];
28575
+ for (let i = 0; i + 1 < s.points.length; i += 2) {
28576
+ const [px, py] = toPx(s.points[i], s.points[i + 1]);
28577
+ out.push(px, py);
28578
+ }
28579
+ return out;
28580
+ });
28581
+ }
28582
+ function buildEdges(subpaths) {
28583
+ const edges = [];
28584
+ for (const pts of subpaths) {
28585
+ const n = pts.length / 2;
28586
+ if (n < 2) continue;
28587
+ for (let i = 0; i < n; i++) {
28588
+ const x0 = pts[i * 2];
28589
+ const y0 = pts[i * 2 + 1];
28590
+ const j = (i + 1) % n;
28591
+ const x1 = pts[j * 2];
28592
+ const y1 = pts[j * 2 + 1];
28593
+ if (y0 === y1) continue;
28594
+ if (y0 < y1) edges.push({
28595
+ ymin: y0,
28596
+ ymax: y1,
28597
+ xAtYmin: x0,
28598
+ dxdy: (x1 - x0) / (y1 - y0),
28599
+ dir: 1
28600
+ });
28601
+ else edges.push({
28602
+ ymin: y1,
28603
+ ymax: y0,
28604
+ xAtYmin: x1,
28605
+ dxdy: (x0 - x1) / (y0 - y1),
28606
+ dir: -1
28607
+ });
28608
+ }
28609
+ }
28610
+ return edges;
28611
+ }
28612
+ const SUBSAMPLES = 4;
28613
+ function fillSubpaths(data, W, H, subpaths, rule, color, alpha, clip) {
28614
+ const edges = buildEdges(subpaths);
28615
+ if (!edges.length) return;
28616
+ const clipEdges = clip ? buildEdges(clip) : void 0;
28617
+ let yTop = H;
28618
+ let yBot = 0;
28619
+ for (const e of edges) {
28620
+ yTop = Math.min(yTop, e.ymin);
28621
+ yBot = Math.max(yBot, e.ymax);
28622
+ }
28623
+ const rowStart = Math.max(0, Math.floor(yTop));
28624
+ const rowEnd = Math.min(H, Math.ceil(yBot));
28625
+ const coverage = new Float32Array(W);
28626
+ const inc = 1 / SUBSAMPLES;
28627
+ for (let y = rowStart; y < rowEnd; y++) {
28628
+ coverage.fill(0);
28629
+ for (let s = 0; s < SUBSAMPLES; s++) accumulateSpans(coverage, W, edges, y + (s + .5) / SUBSAMPLES, rule, inc, clipEdges);
28630
+ const r = color[0];
28631
+ const g = color[1];
28632
+ const b = color[2];
28633
+ const ca = color[3] / 255 * alpha;
28634
+ const base = y * W * 4;
28635
+ for (let x = 0; x < W; x++) {
28636
+ const cov = coverage[x];
28637
+ if (cov <= 0) continue;
28638
+ blend(data, base + x * 4, r, g, b, Math.min(1, cov) * ca);
28639
+ }
28640
+ }
28641
+ }
28642
+ /** Build winding spans at sub-scanline `sy`, adding fractional coverage. */
28643
+ function accumulateSpans(coverage, W, edges, sy, rule, weight, clipEdges) {
28644
+ const xs = crossings(edges, sy);
28645
+ if (xs.length < 2) return;
28646
+ const clipSpans = clipEdges ? rowSpans(crossings(clipEdges, sy), "nonzero") : void 0;
28647
+ const spans = rowSpans(xs, rule);
28648
+ for (const [x0, x1] of spans) if (clipSpans) for (const [c0, c1] of clipSpans) addSpan(coverage, W, Math.max(x0, c0), Math.min(x1, c1), weight);
28649
+ else addSpan(coverage, W, x0, x1, weight);
28650
+ }
28651
+ function crossings(edges, sy) {
28652
+ const xs = [];
28653
+ for (const e of edges) if (sy >= e.ymin && sy < e.ymax) xs.push({
28654
+ x: e.xAtYmin + (sy - e.ymin) * e.dxdy,
28655
+ dir: e.dir
28656
+ });
28657
+ xs.sort((a, b) => a.x - b.x);
28658
+ return xs;
28659
+ }
28660
+ function rowSpans(xs, rule) {
28661
+ const spans = [];
28662
+ let winding = 0;
28663
+ for (let i = 0; i + 1 < xs.length; i++) {
28664
+ winding += xs[i].dir;
28665
+ if (rule === "evenodd" ? (i + 1) % 2 === 1 : winding !== 0) spans.push([xs[i].x, xs[i + 1].x]);
28666
+ }
28667
+ return spans;
28668
+ }
28669
+ /** Add `weight` coverage over `[x0, x1)` with fractional pixel ends. */
28670
+ function addSpan(coverage, W, x0, x1, weight) {
28671
+ if (x1 <= x0) return;
28672
+ const a = Math.max(0, x0);
28673
+ const b = Math.min(W, x1);
28674
+ if (b <= a) return;
28675
+ const ia = Math.floor(a);
28676
+ const ib = Math.floor(b - 1e-9);
28677
+ if (ia === ib) {
28678
+ coverage[ia] += (b - a) * weight;
28679
+ return;
28680
+ }
28681
+ coverage[ia] += (ia + 1 - a) * weight;
28682
+ for (let x = ia + 1; x < ib; x++) coverage[x] += weight;
28683
+ coverage[ib] += (b - ib) * weight;
28684
+ }
28685
+ function blend(data, idx, r, g, b, a) {
28686
+ if (a <= 0) return;
28687
+ const ia = 1 - a;
28688
+ data[idx] = Math.round(r * a + data[idx] * ia);
28689
+ data[idx + 1] = Math.round(g * a + data[idx + 1] * ia);
28690
+ data[idx + 2] = Math.round(b * a + data[idx + 2] * ia);
28691
+ data[idx + 3] = Math.round(255 * a + data[idx + 3] * ia);
28692
+ }
28693
+ function strokeSubpaths(data, W, H, subpaths, toPx, widthPx, color, alpha, clip) {
28694
+ const hw = Math.max(widthPx / 2, .35);
28695
+ for (const sp of subpaths) {
28696
+ const pts = sp.points;
28697
+ const n = pts.length / 2;
28698
+ const segCount = sp.closed ? n : n - 1;
28699
+ for (let i = 0; i < segCount; i++) {
28700
+ const a = toPx(pts[i * 2], pts[i * 2 + 1]);
28701
+ const j = (i + 1) % n;
28702
+ const b = toPx(pts[j * 2], pts[j * 2 + 1]);
28703
+ const dx = b[0] - a[0];
28704
+ const dy = b[1] - a[1];
28705
+ const len = Math.hypot(dx, dy);
28706
+ if (len < 1e-6) continue;
28707
+ const nx = -dy / len * hw;
28708
+ const ny = dx / len * hw;
28709
+ fillSubpaths(data, W, H, [[
28710
+ a[0] + nx,
28711
+ a[1] + ny,
28712
+ b[0] + nx,
28713
+ b[1] + ny,
28714
+ b[0] - nx,
28715
+ b[1] - ny,
28716
+ a[0] - nx,
28717
+ a[1] - ny
28718
+ ]], "nonzero", color, alpha, clip);
28719
+ }
28720
+ }
28721
+ }
28722
+ function fillTextBoxes(data, W, H, item, toPx, clip) {
28723
+ const m = item.transform;
28724
+ const boxes = [];
28725
+ let i = 0;
28726
+ for (const ch of item.text) {
28727
+ if (ch !== " ") {
28728
+ const x0 = i * .5 + .05;
28729
+ const x1 = i * .5 + .45;
28730
+ const src = [
28731
+ [x0, 0],
28732
+ [x1, 0],
28733
+ [x1, .62],
28734
+ [x0, .62]
28735
+ ];
28736
+ const flat = [];
28737
+ for (const [tx, ty] of src) {
28738
+ const [ux, uy] = applyToPoint(m, tx, ty);
28739
+ const [px, py] = toPx(ux, uy);
28740
+ flat.push(px, py);
28741
+ }
28742
+ boxes.push(flat);
28743
+ }
28744
+ i++;
28745
+ }
28746
+ if (boxes.length) fillSubpaths(data, W, H, boxes, "nonzero", item.color, item.alpha * .85, clip);
28747
+ }
28748
+ //#endregion
28749
+ //#region src/render/canvas.ts
28750
+ function effectiveScale(opts) {
28751
+ return (typeof opts.scale === "number" ? opts.scale : typeof opts.dpi === "number" ? opts.dpi / 72 : 1) * (opts.pixelRatio ?? 1);
28752
+ }
28753
+ function rgbaStr(c, alpha) {
28754
+ const a = c[3] / 255 * alpha;
28755
+ return `rgba(${c[0]}, ${c[1]}, ${c[2]}, ${Number(a.toFixed(3))})`;
28756
+ }
28757
+ /** Replay a display list onto a 2D context. */
28758
+ function renderDisplayListToCanvas(dl, ctx, opts = {}) {
28759
+ const scale = effectiveScale(opts);
28760
+ const H = dl.height * scale;
28761
+ const [ox, oy] = dl.origin;
28762
+ const toPx = (x, y) => [(x - ox) * scale, H - (y - oy) * scale];
28763
+ const family = opts.fontFamily ?? "sans-serif";
28764
+ for (const item of dl.items) if (item.type === "fill") {
28765
+ ctx.fillStyle = rgbaStr(item.color, item.alpha);
28766
+ ctx.beginPath();
28767
+ tracePath(ctx, item.subpaths, toPx);
28768
+ ctx.fill(item.rule);
28769
+ } else if (item.type === "stroke") {
28770
+ ctx.strokeStyle = rgbaStr(item.color, item.alpha);
28771
+ ctx.lineWidth = Math.max(item.lineWidth * scale, .5);
28772
+ ctx.beginPath();
28773
+ tracePath(ctx, item.subpaths, toPx);
28774
+ ctx.stroke();
28775
+ } else if (item.type === "text" && item.renderMode !== 3 && item.renderMode !== 7) {
28776
+ const m = item.transform;
28777
+ const [px, py] = toPx(...applyToPoint(m, 0, 0));
28778
+ const sizePx = Math.hypot(m[2], m[3]) * scale;
28779
+ if (sizePx >= .5 && item.text.length) {
28780
+ ctx.fillStyle = rgbaStr(item.color, item.alpha);
28781
+ ctx.font = `${Number(sizePx.toFixed(2))}px ${family}`;
28782
+ ctx.fillText(item.text, px, py);
28783
+ }
28784
+ }
28785
+ }
28786
+ function tracePath(ctx, subpaths, toPx) {
28787
+ for (const sp of subpaths) {
28788
+ const pts = sp.points;
28789
+ if (pts.length < 4) continue;
28790
+ const [x0, y0] = toPx(pts[0], pts[1]);
28791
+ ctx.moveTo(x0, y0);
28792
+ for (let i = 2; i + 1 < pts.length; i += 2) {
28793
+ const [x, y] = toPx(pts[i], pts[i + 1]);
28794
+ ctx.lineTo(x, y);
28795
+ }
28796
+ if (sp.closed) ctx.closePath();
28797
+ }
28798
+ }
28799
+ /**
28800
+ * Render a {@link PdfPage} onto a 2D context. The context's canvas should be
28801
+ * sized to `page.width × scale` by `page.height × scale`.
28802
+ */
28803
+ function renderPageToCanvas(page, ctx, opts = {}) {
28804
+ renderDisplayListToCanvas(interpretPage(page), ctx, opts);
28805
+ }
28806
+ //#endregion
28807
+ //#region src/render/thumbnail.ts
28808
+ /**
28809
+ * @module render/thumbnail
28810
+ *
28811
+ * Generate a small preview PNG of a page by rasterizing it at a scale that fits
28812
+ * the longest side to a target size. (Persisting a `/Thumb` image XObject on the
28813
+ * page — ISO 32000-2 §12.3.4 — requires page-dictionary write access not yet on
28814
+ * the public {@link PdfPage} API; tracked for a later patch.)
28815
+ *
28816
+ * @packageDocumentation
28817
+ */
28818
+ /**
28819
+ * Render `page` to a thumbnail PNG whose longest side is `maxSize` pixels,
28820
+ * preserving aspect ratio.
28821
+ */
28822
+ async function generateThumbnail(page, opts = {}) {
28823
+ const maxSize = opts.maxSize ?? 256;
28824
+ const dl = interpretPage(page);
28825
+ const img = rasterize(dl, {
28826
+ scale: maxSize / (Math.max(dl.width, dl.height) || 1),
28827
+ background: opts.background,
28828
+ renderText: opts.renderText ?? true
28829
+ });
28830
+ return {
28831
+ data: encodePngFromPixels(img.data, img.width, img.height, 4),
28832
+ width: img.width,
28833
+ height: img.height
28834
+ };
28835
+ }
28836
+ //#endregion
28837
+ //#region src/render/imageExtract.ts
28838
+ /**
28839
+ * Extract and decode all image XObjects referenced by a page.
28840
+ *
28841
+ * @param page - The page whose `/Resources /XObject` images are extracted.
28842
+ * @returns One {@link ExtractedImage} per successfully decoded image XObject.
28843
+ * Images that cannot be decoded (unsupported filters/colour spaces,
28844
+ * missing WASM, malformed data) are skipped silently.
28845
+ *
28846
+ * @example
28847
+ * ```ts
28848
+ * import { loadPdf } from 'modern-pdf-lib';
28849
+ * import { extractImages } from 'modern-pdf-lib/render';
28850
+ *
28851
+ * const doc = await loadPdf(bytes);
28852
+ * for (const img of extractImages(doc.getPage(0))) {
28853
+ * console.log(img.name, img.width, img.height, img.channels, img.hasAlpha);
28854
+ * }
28855
+ * ```
28856
+ */
28857
+ function extractImages(page) {
28858
+ const out = [];
28859
+ const resources = page.getOriginalResources();
28860
+ if (!resources) return out;
28861
+ const registry = page.getRegistry();
28862
+ const xObjDict = resolveDict$1(resources.get("/XObject"), registry);
28863
+ if (!xObjDict) return out;
28864
+ for (const [name, value] of xObjDict) {
28865
+ const stream = resolveStream$1(value, registry);
28866
+ if (!stream) continue;
28867
+ const subtype = stream.dict.get("/Subtype");
28868
+ if (!(subtype instanceof PdfName) || subtype.value !== "/Image") continue;
28869
+ try {
28870
+ const extracted = decodeImage(name, stream, registry);
28871
+ if (extracted) out.push(extracted);
28872
+ } catch {
28873
+ continue;
28874
+ }
28875
+ }
28876
+ return out;
28877
+ }
28878
+ /**
28879
+ * Decode a single image XObject stream to interleaved 8-bit pixels and
28880
+ * composite any soft mask. Returns `undefined` when the image cannot be
28881
+ * decoded (e.g. JPEG WASM unavailable, unsupported colour space).
28882
+ * @internal
28883
+ */
28884
+ function decodeImage(name, stream, registry) {
28885
+ const width = readInt(stream.dict.get("/Width"), 0);
28886
+ const height = readInt(stream.dict.get("/Height"), 0);
28887
+ if (width <= 0 || height <= 0) return void 0;
28888
+ const bitsPerComponent = readInt(stream.dict.get("/BitsPerComponent"), 8);
28889
+ const cs = resolveColorSpace(stream.dict.get("/ColorSpace"), registry);
28890
+ const { filters } = getStreamFilters(stream.dict);
28891
+ const isJpeg = filters.includes("DCTDecode");
28892
+ let base;
28893
+ if (isJpeg) base = decodeJpegBase(stream, width, height);
28894
+ else base = interpretSamples(decodeStreamData(stream), width, height, bitsPerComponent, cs);
28895
+ if (!base) return void 0;
28896
+ const alpha = extractSoftMask(stream, registry, width, height);
28897
+ if (alpha) {
28898
+ const pixels = compositeAlpha(base.pixels, base.channels, width, height, alpha);
28899
+ return {
28900
+ name: stripSlash(name),
28901
+ width,
28902
+ height,
28903
+ channels: 4,
28904
+ pixels,
28905
+ colorSpace: cs.name,
28906
+ bitsPerComponent,
28907
+ hasAlpha: true
28908
+ };
28909
+ }
28910
+ return {
28911
+ name: stripSlash(name),
28912
+ width,
28913
+ height,
28914
+ channels: base.channels,
28915
+ pixels: base.pixels,
28916
+ colorSpace: cs.name,
28917
+ bitsPerComponent,
28918
+ hasAlpha: false
28919
+ };
28920
+ }
28921
+ /**
28922
+ * Decode a DCTDecode (JPEG) image XObject to interleaved 8-bit pixels.
28923
+ *
28924
+ * `decodeStreamData` passes DCTDecode through unchanged (decoding any outer
28925
+ * filters such as an ASCII85 wrapper first), so the result is the raw JPEG
28926
+ * byte stream handed to the WASM decoder.
28927
+ * @internal
28928
+ */
28929
+ function decodeJpegBase(stream, width, height) {
28930
+ const decoded = decodeJpegWasm(decodeStreamData(stream));
28931
+ if (!decoded) return void 0;
28932
+ const w = decoded.width || width;
28933
+ const h = decoded.height || height;
28934
+ const channels = decoded.channels;
28935
+ if (w <= 0 || h <= 0 || channels <= 0) return void 0;
28936
+ if (decoded.pixels.length < w * h * channels) return void 0;
28937
+ return {
28938
+ pixels: decoded.pixels,
28939
+ channels
28940
+ };
28941
+ }
28942
+ /**
28943
+ * Interpret decoded sample bytes as interleaved 8-bit RGB / grayscale
28944
+ * pixels according to the colour space and bits-per-component.
28945
+ * @internal
28946
+ */
28947
+ function interpretSamples(data, width, height, bpc, cs) {
28948
+ const pixelCount = width * height;
28949
+ if (cs.name === "Indexed" && cs.palette) return expandIndexed(data, width, height, bpc, cs.palette);
28950
+ const components = cs.components;
28951
+ const samples = readComponentSamples(data, pixelCount * components, bpc);
28952
+ if (!samples) return void 0;
28953
+ if (cs.name === "DeviceGray" || cs.name === "CalGray") {
28954
+ const pixels = new Uint8Array(pixelCount);
28955
+ for (let i = 0; i < pixelCount; i++) pixels[i] = samples[i] ?? 0;
28956
+ return {
28957
+ pixels,
28958
+ channels: 1
28959
+ };
28960
+ }
28961
+ if (cs.name === "DeviceCMYK") {
28962
+ const pixels = new Uint8Array(pixelCount * 3);
28963
+ for (let p = 0; p < pixelCount; p++) {
28964
+ const si = p * 4;
28965
+ const [r, g, b] = cmykToRgb((samples[si] ?? 0) / 255, (samples[si + 1] ?? 0) / 255, (samples[si + 2] ?? 0) / 255, (samples[si + 3] ?? 0) / 255);
28966
+ const di = p * 3;
28967
+ pixels[di] = Math.round(r * 255);
28968
+ pixels[di + 1] = Math.round(g * 255);
28969
+ pixels[di + 2] = Math.round(b * 255);
28970
+ }
28971
+ return {
28972
+ pixels,
28973
+ channels: 3
28974
+ };
28975
+ }
28976
+ const pixels = new Uint8Array(pixelCount * 3);
28977
+ for (let i = 0; i < pixelCount * 3; i++) pixels[i] = samples[i] ?? 0;
28978
+ return {
28979
+ pixels,
28980
+ channels: 3
28981
+ };
28982
+ }
28983
+ /**
28984
+ * Expand an indexed (palette) image to interleaved RGB pixels.
28985
+ * @internal
28986
+ */
28987
+ function expandIndexed(data, width, height, bpc, palette) {
28988
+ const pixelCount = width * height;
28989
+ const pixels = new Uint8Array(pixelCount * 3);
28990
+ const maxIndex = Math.floor(palette.length / 3) - 1;
28991
+ const indices = readPackedRows(data, width, height, 1, bpc);
28992
+ for (let p = 0; p < pixelCount; p++) {
28993
+ let idx = indices[p] ?? 0;
28994
+ if (idx > maxIndex) idx = maxIndex < 0 ? 0 : maxIndex;
28995
+ const pi = idx * 3;
28996
+ const di = p * 3;
28997
+ pixels[di] = palette[pi] ?? 0;
28998
+ pixels[di + 1] = palette[pi + 1] ?? 0;
28999
+ pixels[di + 2] = palette[pi + 2] ?? 0;
29000
+ }
29001
+ return {
29002
+ pixels,
29003
+ channels: 3
29004
+ };
29005
+ }
29006
+ /**
29007
+ * Read `count` component samples, normalizing sub-byte / 16-bit components
29008
+ * to 8-bit values. Returns `undefined` when the buffer is too small.
29009
+ * @internal
29010
+ */
29011
+ function readComponentSamples(data, count, bpc) {
29012
+ if (bpc === 8) {
29013
+ if (data.length < count) return void 0;
29014
+ return data.subarray(0, count);
29015
+ }
29016
+ if (bpc === 16) {
29017
+ if (data.length < count * 2) return void 0;
29018
+ const out = new Uint8Array(count);
29019
+ for (let i = 0; i < count; i++) out[i] = data[i * 2] ?? 0;
29020
+ return out;
29021
+ }
29022
+ if (bpc === 1 || bpc === 2 || bpc === 4) {
29023
+ const out = new Uint8Array(count);
29024
+ const max = (1 << bpc) - 1;
29025
+ let bitPos = 0;
29026
+ for (let i = 0; i < count; i++) {
29027
+ const byteIdx = bitPos >>> 3;
29028
+ if (byteIdx >= data.length) return void 0;
29029
+ const shift = 8 - (bitPos & 7) - bpc;
29030
+ const val = data[byteIdx] >> shift & max;
29031
+ out[i] = Math.round(val / max * 255);
29032
+ bitPos += bpc;
29033
+ }
29034
+ return out;
29035
+ }
29036
+ }
29037
+ /**
29038
+ * Read packed single-component rows (e.g. indexed indices) honouring PDF
29039
+ * row-byte alignment (each scanline starts on a byte boundary).
29040
+ * @internal
29041
+ */
29042
+ function readPackedRows(data, width, height, components, bpc) {
29043
+ const out = new Uint8Array(width * height * components);
29044
+ const rowBits = width * components * bpc;
29045
+ const rowBytes = Math.ceil(rowBits / 8);
29046
+ const max = (1 << bpc) - 1;
29047
+ for (let row = 0; row < height; row++) {
29048
+ const rowStart = row * rowBytes;
29049
+ let bitPos = 0;
29050
+ for (let col = 0; col < width * components; col++) {
29051
+ const byteIdx = rowStart + (bitPos >>> 3);
29052
+ let val = 0;
29053
+ if (byteIdx < data.length) if (bpc === 8) val = data[byteIdx];
29054
+ else if (bpc === 16) val = data[byteIdx];
29055
+ else {
29056
+ const shift = 8 - (bitPos & 7) - bpc;
29057
+ val = data[byteIdx] >> shift & max;
29058
+ }
29059
+ out[row * width * components + col] = val;
29060
+ bitPos += bpc;
29061
+ }
29062
+ }
29063
+ return out;
29064
+ }
29065
+ /**
29066
+ * Resolve and decode the `/SMask` of an image to a per-pixel 8-bit alpha
29067
+ * buffer sized `width * height`, nearest-sampling when the mask resolution
29068
+ * differs from the base image. Returns `undefined` when no usable mask is
29069
+ * present.
29070
+ * @internal
29071
+ */
29072
+ function extractSoftMask(stream, registry, width, height) {
29073
+ const smask = resolveStream$1(stream.dict.get("/SMask"), registry);
29074
+ if (!smask) return void 0;
29075
+ const mw = readInt(smask.dict.get("/Width"), 0);
29076
+ const mh = readInt(smask.dict.get("/Height"), 0);
29077
+ if (mw <= 0 || mh <= 0) return void 0;
29078
+ const mbpc = readInt(smask.dict.get("/BitsPerComponent"), 8);
29079
+ let maskData;
29080
+ const { filters } = getStreamFilters(smask.dict);
29081
+ if (filters.includes("DCTDecode")) {
29082
+ const decoded = decodeJpegWasm(decodeStreamData(smask));
29083
+ if (!decoded) return void 0;
29084
+ maskData = decoded.pixels;
29085
+ } else {
29086
+ const raw = decodeStreamData(smask);
29087
+ const samples = readComponentSamples(raw, mw * mh, mbpc);
29088
+ if (!samples) maskData = readPackedRows(raw, mw, mh, 1, mbpc);
29089
+ else maskData = samples;
29090
+ }
29091
+ if (maskData.length < mw * mh) return void 0;
29092
+ if (mw === width && mh === height) return maskData.subarray(0, width * height);
29093
+ const alpha = new Uint8Array(width * height);
29094
+ for (let y = 0; y < height; y++) {
29095
+ const sy = Math.min(mh - 1, Math.floor(y * mh / height));
29096
+ for (let x = 0; x < width; x++) {
29097
+ const sx = Math.min(mw - 1, Math.floor(x * mw / width));
29098
+ alpha[y * width + x] = maskData[sy * mw + sx] ?? 255;
29099
+ }
29100
+ }
29101
+ return alpha;
29102
+ }
29103
+ /**
29104
+ * Composite a base colour buffer with an alpha channel into interleaved
29105
+ * RGBA pixels. A grayscale base is promoted to RGB first.
29106
+ * @internal
29107
+ */
29108
+ function compositeAlpha(base, channels, width, height, alpha) {
29109
+ const pixelCount = width * height;
29110
+ const out = new Uint8Array(pixelCount * 4);
29111
+ for (let p = 0; p < pixelCount; p++) {
29112
+ const di = p * 4;
29113
+ if (channels === 1) {
29114
+ const g = base[p] ?? 0;
29115
+ out[di] = g;
29116
+ out[di + 1] = g;
29117
+ out[di + 2] = g;
29118
+ } else {
29119
+ const si = p * channels;
29120
+ out[di] = base[si] ?? 0;
29121
+ out[di + 1] = base[si + 1] ?? 0;
29122
+ out[di + 2] = base[si + 2] ?? 0;
29123
+ }
29124
+ out[di + 3] = alpha[p] ?? 255;
29125
+ }
29126
+ return out;
29127
+ }
29128
+ /**
29129
+ * Resolve a `/ColorSpace` entry into a canonical name, component count, and
29130
+ * (for indexed spaces) the palette bytes.
29131
+ * @internal
29132
+ */
29133
+ function resolveColorSpace(entry, registry) {
29134
+ const resolved = deref(entry, registry);
29135
+ if (resolved instanceof PdfName) return nameToColorSpace(resolved.value);
29136
+ if (resolved instanceof PdfArray) {
29137
+ const first = deref(resolved.items[0], registry);
29138
+ if (first instanceof PdfName) {
29139
+ const head = stripSlash(first.value);
29140
+ if (head === "ICCBased") {
29141
+ const profile = resolveStream$1(resolved.items[1], registry);
29142
+ const n = profile ? readInt(profile.dict.get("/N"), 3) : 3;
29143
+ if (n === 1) return {
29144
+ name: "DeviceGray",
29145
+ components: 1
29146
+ };
29147
+ if (n === 4) return {
29148
+ name: "DeviceCMYK",
29149
+ components: 4
29150
+ };
29151
+ return {
29152
+ name: "DeviceRGB",
29153
+ components: 3
29154
+ };
29155
+ }
29156
+ if (head === "Indexed" || head === "I") return {
29157
+ name: "Indexed",
29158
+ components: 1,
29159
+ palette: readIndexedPalette(resolved, registry)
29160
+ };
29161
+ if (head === "CalRGB" || head === "Lab") return {
29162
+ name: head,
29163
+ components: 3
29164
+ };
29165
+ if (head === "CalGray") return {
29166
+ name: "CalGray",
29167
+ components: 1
29168
+ };
29169
+ return nameToColorSpace(first.value);
29170
+ }
29171
+ }
29172
+ return {
29173
+ name: "DeviceRGB",
29174
+ components: 3
29175
+ };
29176
+ }
29177
+ /** Map a colour-space name to its canonical form + component count. @internal */
29178
+ function nameToColorSpace(rawName) {
29179
+ switch (stripSlash(rawName)) {
29180
+ case "DeviceGray":
29181
+ case "G":
29182
+ case "CalGray": return {
29183
+ name: "DeviceGray",
29184
+ components: 1
29185
+ };
29186
+ case "DeviceCMYK":
29187
+ case "CMYK": return {
29188
+ name: "DeviceCMYK",
29189
+ components: 4
29190
+ };
29191
+ case "DeviceRGB":
29192
+ case "RGB":
29193
+ case "CalRGB": return {
29194
+ name: "DeviceRGB",
29195
+ components: 3
29196
+ };
29197
+ default: return {
29198
+ name: "DeviceRGB",
29199
+ components: 3
29200
+ };
29201
+ }
29202
+ }
29203
+ /**
29204
+ * Read the lookup table (palette) from an `[/Indexed base hival lookup]`
29205
+ * colour-space array. The lookup table may be a hex/literal string or a
29206
+ * stream.
29207
+ * @internal
29208
+ */
29209
+ function readIndexedPalette(arr, registry) {
29210
+ const lookup = deref(arr.items[3], registry);
29211
+ if (lookup instanceof PdfString) {
29212
+ const value = lookup.value;
29213
+ const bytes = new Uint8Array(value.length);
29214
+ for (let i = 0; i < value.length; i++) bytes[i] = value.charCodeAt(i) & 255;
29215
+ return bytes;
29216
+ }
29217
+ if (lookup instanceof PdfStream) return decodeStreamData(lookup);
29218
+ }
29219
+ /** Resolve a value to its target object, following one indirect reference. @internal */
29220
+ function deref(obj, registry) {
29221
+ if (obj instanceof PdfRef) return registry.resolve(obj);
29222
+ return obj;
29223
+ }
29224
+ /** Resolve a value (possibly a ref) to a `PdfDict`, or `undefined`. @internal */
29225
+ function resolveDict$1(obj, registry) {
29226
+ const resolved = deref(obj, registry);
29227
+ return resolved instanceof PdfDict ? resolved : void 0;
29228
+ }
29229
+ /** Resolve a value (possibly a ref) to a `PdfStream`, or `undefined`. @internal */
29230
+ function resolveStream$1(obj, registry) {
29231
+ const resolved = deref(obj, registry);
29232
+ return resolved instanceof PdfStream ? resolved : void 0;
29233
+ }
29234
+ /** Read an integer from a PDF object, or fall back. @internal */
29235
+ function readInt(obj, fallback) {
29236
+ return obj instanceof PdfNumber ? Math.round(obj.value) : fallback;
29237
+ }
29238
+ /** Strip a single leading slash from a PDF name string. @internal */
29239
+ function stripSlash(s) {
29240
+ return s.startsWith("/") ? s.slice(1) : s;
29241
+ }
29242
+ //#endregion
29243
+ //#region src/render/fontExtract.ts
29244
+ /**
29245
+ * Resolve an object through the registry if it is an indirect reference,
29246
+ * otherwise return it unchanged.
29247
+ */
29248
+ function resolve(obj, registry) {
29249
+ if (obj && obj.kind === "ref") return registry.resolve(obj);
29250
+ return obj;
29251
+ }
29252
+ /** Resolve `dict[key]` and return it only if it is a dictionary. */
29253
+ function resolveDict(dict, key, registry) {
29254
+ const v = resolve(dict.get(key), registry);
29255
+ return v && v.kind === "dict" ? v : void 0;
29256
+ }
29257
+ /** Resolve `dict[key]` and return it only if it is a stream. */
29258
+ function resolveStream(dict, key, registry) {
29259
+ const v = resolve(dict.get(key), registry);
29260
+ return v && v.kind === "stream" ? v : void 0;
29261
+ }
29262
+ /** Read a `/Name` value with the leading slash stripped, if present. */
29263
+ function nameValue(obj, registry) {
29264
+ const v = resolve(obj, registry);
29265
+ if (v && v.kind === "name") {
29266
+ const raw = v.value;
29267
+ return raw.startsWith("/") ? raw.slice(1) : raw;
29268
+ }
29269
+ }
29270
+ /**
29271
+ * Locate the `/FontDescriptor` for a font dictionary.
29272
+ *
29273
+ * For simple fonts the descriptor is a direct child. For composite
29274
+ * (Type0) fonts it lives on the first `/DescendantFonts` entry.
29275
+ */
29276
+ function findFontDescriptor(fontDict, registry) {
29277
+ const direct = resolveDict(fontDict, "/FontDescriptor", registry);
29278
+ if (direct) return direct;
29279
+ if (nameValue(fontDict.get("/Subtype"), registry) === "Type0") {
29280
+ const descendants = resolve(fontDict.get("/DescendantFonts"), registry);
29281
+ if (descendants && descendants.kind === "array") {
29282
+ const first = descendants.items[0];
29283
+ const cidFont = resolve(first, registry);
29284
+ if (cidFont && cidFont.kind === "dict") return resolveDict(cidFont, "/FontDescriptor", registry);
29285
+ }
29286
+ }
29287
+ }
29288
+ /**
29289
+ * Pull the embedded font program (if any) out of a `/FontDescriptor`.
29290
+ *
29291
+ * Returns the decoded bytes and the detected format, or `undefined` when
29292
+ * the descriptor carries no embedded file (e.g. a standard font).
29293
+ */
29294
+ function extractFromDescriptor(descriptor, registry) {
29295
+ const fontFile = resolveStream(descriptor, "/FontFile", registry);
29296
+ if (fontFile) return {
29297
+ format: "type1",
29298
+ data: decodeStreamData(fontFile)
29299
+ };
29300
+ const fontFile2 = resolveStream(descriptor, "/FontFile2", registry);
29301
+ if (fontFile2) return {
29302
+ format: "truetype",
29303
+ data: decodeStreamData(fontFile2)
29304
+ };
29305
+ const fontFile3 = resolveStream(descriptor, "/FontFile3", registry);
29306
+ if (fontFile3) return {
29307
+ format: nameValue(fontFile3.dict.get("/Subtype"), registry) === "OpenType" ? "opentype" : "cff",
29308
+ data: decodeStreamData(fontFile3)
29309
+ };
29310
+ }
29311
+ /**
29312
+ * Determine whether a base-font name carries a subset tag.
29313
+ *
29314
+ * A subset tag is exactly six uppercase ASCII letters followed by `+`,
29315
+ * e.g. `ABCDEF+Helvetica`.
29316
+ */
29317
+ function hasSubsetTag(baseFont) {
29318
+ if (baseFont.length < 7 || baseFont[6] !== "+") return false;
29319
+ for (let i = 0; i < 6; i++) {
29320
+ const code = baseFont.charCodeAt(i);
29321
+ if (code < 65 || code > 90) return false;
29322
+ }
29323
+ return true;
29324
+ }
29325
+ /**
29326
+ * Extract every embedded font program referenced by a page.
29327
+ *
29328
+ * Walks the page's original `/Resources` → `/Font` dictionary. For each
29329
+ * font that carries an embedded file (`/FontFile`, `/FontFile2`, or
29330
+ * `/FontFile3`) — directly or via a Type0 descendant — the standalone,
29331
+ * filter-decoded font bytes are returned. Standard-14 fonts with no
29332
+ * embedded file are omitted, and malformed entries are skipped silently.
29333
+ *
29334
+ * @param page The page to scan (typically from a loaded PDF).
29335
+ * @returns One {@link ExtractedFont} per embedded font program.
29336
+ *
29337
+ * @example
29338
+ * ```ts
29339
+ * const loaded = await PdfDocument.load(bytes);
29340
+ * const fonts = extractFonts(loaded.getPage(0));
29341
+ * for (const f of fonts) {
29342
+ * console.log(f.resourceName, f.baseFont, f.format, f.data.length, f.subset);
29343
+ * }
29344
+ * ```
29345
+ */
29346
+ function extractFonts(page) {
29347
+ const resources = page.getOriginalResources();
29348
+ if (!resources) return [];
29349
+ const registry = page.getRegistry();
29350
+ const fontDict = resolveDict(resources, "/Font", registry);
29351
+ if (!fontDict) return [];
29352
+ const result = [];
29353
+ for (const [key, value] of fontDict) {
29354
+ const dict = resolve(value, registry);
29355
+ if (!dict || dict.kind !== "dict") continue;
29356
+ const descriptor = findFontDescriptor(dict, registry);
29357
+ if (!descriptor) continue;
29358
+ let extracted;
29359
+ try {
29360
+ extracted = extractFromDescriptor(descriptor, registry);
29361
+ } catch {
29362
+ continue;
29363
+ }
29364
+ if (!extracted) continue;
29365
+ const baseFont = nameValue(dict.get("/BaseFont"), registry) ?? "";
29366
+ const resourceName = key.startsWith("/") ? key.slice(1) : key;
29367
+ result.push({
29368
+ resourceName,
29369
+ baseFont,
29370
+ format: extracted.format,
29371
+ data: extracted.data,
29372
+ subset: hasSubsetTag(baseFont)
29373
+ });
29374
+ }
29375
+ return result;
29376
+ }
29377
+ //#endregion
29378
+ //#region src/render/diff.ts
29379
+ /**
29380
+ * @module render/diff
29381
+ *
29382
+ * Visual regression for PDFs: rasterize two pages (or compare two raster
29383
+ * images) and report a per-pixel difference count, a difference heatmap, and a
29384
+ * structural-similarity (SSIM) score over 8×8 luminance blocks.
29385
+ *
29386
+ * @packageDocumentation
29387
+ */
29388
+ const C1 = (.01 * 255) ** 2;
29389
+ const C2 = (.03 * 255) ** 2;
29390
+ function luma(data, i) {
29391
+ return .299 * data[i] + .587 * data[i + 1] + .114 * data[i + 2];
29392
+ }
29393
+ /** Compare two equally-sized raster images. */
29394
+ function compareImages(a, b, opts = {}) {
29395
+ if (a.width !== b.width || a.height !== b.height) throw new Error(`compareImages: size mismatch (${a.width}×${a.height} vs ${b.width}×${b.height}); render both at the same scale`);
29396
+ const W = a.width;
29397
+ const H = a.height;
29398
+ const threshold = opts.threshold ?? 16;
29399
+ const heatmap = new Uint8Array(W * H * 4);
29400
+ let changed = 0;
29401
+ for (let p = 0; p < W * H; p++) {
29402
+ const i = p * 4;
29403
+ const dr = Math.abs(a.data[i] - b.data[i]);
29404
+ const dg = Math.abs(a.data[i + 1] - b.data[i + 1]);
29405
+ const db = Math.abs(a.data[i + 2] - b.data[i + 2]);
29406
+ if (Math.max(dr, dg, db) > threshold) {
29407
+ changed++;
29408
+ heatmap[i] = 255;
29409
+ heatmap[i + 1] = 40;
29410
+ heatmap[i + 2] = 40;
29411
+ heatmap[i + 3] = 255;
29412
+ } else {
29413
+ const g = Math.round(luma(a.data, i) * .4 + 153);
29414
+ heatmap[i] = g;
29415
+ heatmap[i + 1] = g;
29416
+ heatmap[i + 2] = g;
29417
+ heatmap[i + 3] = 255;
29418
+ }
29419
+ }
29420
+ return {
29421
+ width: W,
29422
+ height: H,
29423
+ changedPixels: changed,
29424
+ changedRatio: changed / (W * H),
29425
+ ssim: meanSsim(a, b, W, H),
29426
+ heatmap
29427
+ };
29428
+ }
29429
+ /** Mean SSIM over non-overlapping 8×8 luminance blocks. */
29430
+ function meanSsim(a, b, W, H) {
29431
+ const block = 8;
29432
+ let total = 0;
29433
+ let count = 0;
29434
+ for (let by = 0; by < H; by += block) for (let bx = 0; bx < W; bx += block) {
29435
+ const bw = Math.min(block, W - bx);
29436
+ const bh = Math.min(block, H - by);
29437
+ const n = bw * bh;
29438
+ let sa = 0;
29439
+ let sb = 0;
29440
+ let saa = 0;
29441
+ let sbb = 0;
29442
+ let sab = 0;
29443
+ for (let y = 0; y < bh; y++) for (let x = 0; x < bw; x++) {
29444
+ const i = ((by + y) * W + (bx + x)) * 4;
29445
+ const la = luma(a.data, i);
29446
+ const lb = luma(b.data, i);
29447
+ sa += la;
29448
+ sb += lb;
29449
+ saa += la * la;
29450
+ sbb += lb * lb;
29451
+ sab += la * lb;
29452
+ }
29453
+ const ma = sa / n;
29454
+ const mb = sb / n;
29455
+ const va = saa / n - ma * ma;
29456
+ const vb = sbb / n - mb * mb;
29457
+ const cov = sab / n - ma * mb;
29458
+ const ssim = (2 * ma * mb + C1) * (2 * cov + C2) / ((ma * ma + mb * mb + C1) * (va + vb + C2));
29459
+ total += ssim;
29460
+ count++;
29461
+ }
29462
+ return count ? total / count : 1;
29463
+ }
29464
+ /** Rasterize two pages at the same scale and compare them. */
29465
+ async function comparePages(pageA, pageB, opts = {}) {
29466
+ return compareImages(rasterize(interpretPage(pageA), opts), rasterize(interpretPage(pageB), opts), opts);
29467
+ }
29468
+ //#endregion
29469
+ //#region src/render/ocr.ts
29470
+ /**
29471
+ * Escape a string for use inside a PDF literal string `(…)`.
29472
+ * Backslash and both parentheses must be escaped so the content stream
29473
+ * remains well-formed.
29474
+ */
29475
+ function escapePdfLiteral(text) {
29476
+ return text.replaceAll("\\", "\\\\").replaceAll("(", "\\(").replaceAll(")", "\\)");
29477
+ }
29478
+ /**
29479
+ * Format a number for PDF output: at most 4 decimal places, trailing zeros
29480
+ * stripped, never `-0`.
29481
+ */
29482
+ function num$1(value) {
29483
+ if (!Number.isFinite(value)) return "0";
29484
+ if (Number.isInteger(value)) return value.toString();
29485
+ const s = value.toFixed(4).replace(/\.?0+$/, "");
29486
+ return s === "-0" ? "0" : s;
29487
+ }
29488
+ /**
29489
+ * Build a standard Helvetica (Type 1) font dictionary, register it in the
29490
+ * page's object registry, and attach it to the page under `resourceName`.
29491
+ *
29492
+ * This lets {@link applyOcr} guarantee a usable font resource exists for the
29493
+ * invisible overlay without needing a reference to the owning document.
29494
+ */
29495
+ function ensureOverlayFont(page, resourceName) {
29496
+ const registry = page.getRegistry();
29497
+ const fontDict = new PdfDict();
29498
+ fontDict.set("/Type", PdfName.of("Font"));
29499
+ fontDict.set("/Subtype", PdfName.of("Type1"));
29500
+ fontDict.set("/BaseFont", PdfName.of("Helvetica"));
29501
+ fontDict.set("/Encoding", PdfName.of("WinAnsiEncoding"));
29502
+ const ref = registry.register(fontDict);
29503
+ page.registerFont(resourceName, ref);
29504
+ }
29505
+ /**
29506
+ * Run OCR on a page and append the recognized text as an invisible,
29507
+ * selectable/searchable overlay.
29508
+ *
29509
+ * The page is rasterized to RGBA at `opts.dpi`, passed to `engine.recognize`,
29510
+ * and each returned word is written back as a content-stream fragment:
29511
+ *
29512
+ * ```text
29513
+ * BT 3 Tr /<font> <size> Tf <x> <y> Td (<escaped text>) Tj ET
29514
+ * ```
29515
+ *
29516
+ * where `<size> ≈ word.height` and `<x> <y>` is the word's lower-left corner
29517
+ * in page space. Render mode `3` makes the text invisible (no fill, no
29518
+ * stroke) while keeping it part of the document's text content.
29519
+ *
29520
+ * @param page The page to OCR and annotate.
29521
+ * @param engine The OCR backend.
29522
+ * @param opts Optional DPI and font-resource overrides.
29523
+ * @returns The words returned by the engine (unmodified).
29524
+ */
29525
+ async function applyOcr(page, engine, opts) {
29526
+ const dpi = opts?.dpi ?? 150;
29527
+ const fontName = opts?.fontResourceName ?? "OCRFont";
29528
+ const raster = rasterize(interpretPage(page), { dpi });
29529
+ const words = await engine.recognize(raster.data, raster.width, raster.height);
29530
+ if (words.length === 0) return words;
29531
+ ensureOverlayFont(page, fontName);
29532
+ let fragment = "";
29533
+ for (const word of words) {
29534
+ const size = word.height > 0 ? word.height : 1;
29535
+ const escaped = escapePdfLiteral(word.text);
29536
+ fragment += `BT 3 Tr /${fontName} ${num$1(size)} Tf ${num$1(word.x)} ${num$1(word.y)} Td (${escaped}) Tj ET\n`;
29537
+ }
29538
+ page.pushOperators(fragment);
29539
+ return words;
29540
+ }
29541
+ //#endregion
29542
+ //#region src/render/redactContent.ts
29543
+ /** Build a {@link BBox} from a set of page-space corner points. */
29544
+ function bboxOfPoints(points) {
29545
+ let minX = Infinity;
29546
+ let minY = Infinity;
29547
+ let maxX = -Infinity;
29548
+ let maxY = -Infinity;
29549
+ for (const [x, y] of points) {
29550
+ if (x < minX) minX = x;
29551
+ if (y < minY) minY = y;
29552
+ if (x > maxX) maxX = x;
29553
+ if (y > maxY) maxY = y;
29554
+ }
29555
+ return {
29556
+ minX,
29557
+ minY,
29558
+ maxX,
29559
+ maxY
29560
+ };
29561
+ }
29562
+ /** Whether a page-space bounding box intersects a redaction rect. */
29563
+ function intersects(box, rect) {
29564
+ const rMinX = rect.x;
29565
+ const rMinY = rect.y;
29566
+ const rMaxX = rect.x + rect.width;
29567
+ const rMaxY = rect.y + rect.height;
29568
+ return box.minX <= rMaxX && box.maxX >= rMinX && box.minY <= rMaxY && box.maxY >= rMinY;
29569
+ }
29570
+ /** Whether a bounding box intersects *any* of the redaction rects. */
29571
+ function intersectsAny(box, rects) {
29572
+ for (const rect of rects) {
29573
+ if (rect.width <= 0 || rect.height <= 0) continue;
29574
+ if (intersects(box, rect)) return true;
29575
+ }
29576
+ return false;
29577
+ }
29578
+ function num(op) {
29579
+ return typeof op === "number" ? op : 0;
29580
+ }
29581
+ /** Length of the visible characters a text-show operand contributes. */
29582
+ function operandTextLength(op) {
29583
+ if (typeof op === "string") return op.length;
29584
+ if (Array.isArray(op)) {
29585
+ let total = 0;
29586
+ for (const el of op) if (typeof el === "string") total += el.length;
29587
+ return total;
29588
+ }
29589
+ return 0;
29590
+ }
29591
+ const AVG_GLYPH_WIDTH_EM = .5;
29592
+ const ASCENT_EM = .85;
29593
+ const DESCENT_EM = -.2;
29594
+ /**
29595
+ * Compute the page-space bounding box of a text run shown at the current
29596
+ * text-rendering matrix. The run is approximated as a rectangle spanning
29597
+ * `[0, advance]` horizontally and `[descent, ascent]` vertically in text
29598
+ * space (em units, before the font-size scale baked into `trm`).
29599
+ */
29600
+ function textRunBBox(trm, glyphCount) {
29601
+ const advance = Math.max(glyphCount, 1) * AVG_GLYPH_WIDTH_EM;
29602
+ return bboxOfPoints([
29603
+ applyToPoint(trm, 0, DESCENT_EM),
29604
+ applyToPoint(trm, advance, DESCENT_EM),
29605
+ applyToPoint(trm, advance, ASCENT_EM),
29606
+ applyToPoint(trm, 0, ASCENT_EM)
29607
+ ]);
29608
+ }
29609
+ /**
29610
+ * Compute the page-space bounding box of an image placed by `Do`. An image
29611
+ * XObject is painted into the unit square `[0,1]×[0,1]` transformed by the
29612
+ * current CTM.
29613
+ */
29614
+ function imageBBox(ctm) {
29615
+ return bboxOfPoints([
29616
+ applyToPoint(ctm, 0, 0),
29617
+ applyToPoint(ctm, 1, 0),
29618
+ applyToPoint(ctm, 1, 1),
29619
+ applyToPoint(ctm, 0, 1)
29620
+ ]);
29621
+ }
29622
+ /**
29623
+ * Serialize a string operand as a PDF hex string `<…>`.
29624
+ *
29625
+ * Hex strings are byte-exact and contain only ASCII, so they survive the
29626
+ * UTF-8 encoding applied when the content stream is written. The parser
29627
+ * decodes both `(…)` and `<…>` strings to a JS string of latin-1 char codes,
29628
+ * so re-emitting as hex preserves the original bytes regardless of the
29629
+ * original literal form (important for multi-byte / CID-encoded text).
29630
+ */
29631
+ function serializeString(s) {
29632
+ let out = "<";
29633
+ for (let i = 0; i < s.length; i++) {
29634
+ const byte = s.charCodeAt(i) & 255;
29635
+ out += byte.toString(16).padStart(2, "0");
29636
+ }
29637
+ return out + ">";
29638
+ }
29639
+ /** Format a number the way PDF content streams expect (no trailing zeros). */
29640
+ function formatNumber(value) {
29641
+ if (Number.isInteger(value)) return value.toString();
29642
+ const s = value.toFixed(6).replace(/\.?0+$/, "");
29643
+ return s === "-0" ? "0" : s;
29644
+ }
29645
+ /** Serialize a single operand to its content-stream textual form. */
29646
+ function serializeOperand(op) {
29647
+ if (op === null) return "null";
29648
+ if (typeof op === "boolean") return op ? "true" : "false";
29649
+ if (typeof op === "number") return formatNumber(op);
29650
+ if (typeof op === "string") return serializeString(op);
29651
+ if (op instanceof PdfName) return op.value;
29652
+ if (Array.isArray(op)) return "[" + op.map(serializeOperand).join(" ") + "]";
29653
+ return "";
29654
+ }
29655
+ /** Serialize one operator (operands followed by the operator keyword). */
29656
+ function serializeOperator(op) {
29657
+ if (op.operands.length === 0) return op.operator;
29658
+ return `${op.operands.map(serializeOperand).join(" ")} ${op.operator}`;
29659
+ }
29660
+ function initialState() {
29661
+ return {
29662
+ ctm: identity(),
29663
+ fontSize: 0,
29664
+ hScale: 1,
29665
+ rise: 0,
29666
+ leading: 0
29667
+ };
29668
+ }
29669
+ /**
29670
+ * Build the text-rendering matrix used to map text-space (em) coordinates to
29671
+ * page space, matching the interpreter:
29672
+ * `Trm = [fontSize·hScale, 0, 0, fontSize, 0, rise] × Tm × CTM`.
29673
+ */
29674
+ function textRenderingMatrix(state, tm) {
29675
+ return multiply(multiply([
29676
+ state.fontSize * state.hScale,
29677
+ 0,
29678
+ 0,
29679
+ state.fontSize,
29680
+ 0,
29681
+ state.rise
29682
+ ], tm), state.ctm);
29683
+ }
29684
+ /**
29685
+ * Walk the operators, tracking position, and return both the filtered
29686
+ * operator list and the removal counts.
29687
+ */
29688
+ function filterOperators(operators, rects) {
29689
+ const kept = [];
29690
+ let removedText = 0;
29691
+ let removedImages = 0;
29692
+ const stack = [];
29693
+ let state = initialState();
29694
+ let tm = identity();
29695
+ let tlm = identity();
29696
+ for (const op of operators) {
29697
+ const { operator, operands } = op;
29698
+ let drop = false;
29699
+ switch (operator) {
29700
+ case "q":
29701
+ stack.push({ ...state });
29702
+ break;
29703
+ case "Q":
29704
+ if (stack.length > 0) state = stack.pop();
29705
+ break;
29706
+ case "cm":
29707
+ state = {
29708
+ ...state,
29709
+ ctm: multiply([
29710
+ num(operands[0]),
29711
+ num(operands[1]),
29712
+ num(operands[2]),
29713
+ num(operands[3]),
29714
+ num(operands[4]),
29715
+ num(operands[5])
29716
+ ], state.ctm)
29717
+ };
29718
+ break;
29719
+ case "BT":
29720
+ tm = identity();
29721
+ tlm = identity();
29722
+ break;
29723
+ case "ET": break;
29724
+ case "Tf":
29725
+ state = {
29726
+ ...state,
29727
+ fontSize: num(operands[1])
29728
+ };
29729
+ break;
29730
+ case "Tz":
29731
+ state = {
29732
+ ...state,
29733
+ hScale: num(operands[0]) / 100
29734
+ };
29735
+ break;
29736
+ case "Ts":
29737
+ state = {
29738
+ ...state,
29739
+ rise: num(operands[0])
29740
+ };
29741
+ break;
29742
+ case "TL":
29743
+ state = {
29744
+ ...state,
29745
+ leading: num(operands[0])
29746
+ };
29747
+ break;
29748
+ case "Td":
29749
+ tlm = multiply(translation(num(operands[0]), num(operands[1])), tlm);
29750
+ tm = tlm;
29751
+ break;
29752
+ case "TD":
29753
+ state = {
29754
+ ...state,
29755
+ leading: -num(operands[1])
29756
+ };
29757
+ tlm = multiply(translation(num(operands[0]), num(operands[1])), tlm);
29758
+ tm = tlm;
29759
+ break;
29760
+ case "Tm":
29761
+ tm = [
29762
+ num(operands[0]),
29763
+ num(operands[1]),
29764
+ num(operands[2]),
29765
+ num(operands[3]),
29766
+ num(operands[4]),
29767
+ num(operands[5])
29768
+ ];
29769
+ tlm = tm;
29770
+ break;
29771
+ case "T*":
29772
+ tlm = multiply(translation(0, -state.leading), tlm);
29773
+ tm = tlm;
29774
+ break;
29775
+ case "Tj":
29776
+ case "TJ":
29777
+ case "'":
29778
+ case "\"": {
29779
+ if (operator === "'" || operator === "\"") {
29780
+ tlm = multiply(translation(0, -state.leading), tlm);
29781
+ tm = tlm;
29782
+ }
29783
+ if (operator === "\"") state = { ...state };
29784
+ const glyphCount = operandTextLength(operator === "\"" ? operands[2] : operator === "'" ? operands[0] : operands[0]);
29785
+ if (intersectsAny(textRunBBox(textRenderingMatrix(state, tm), glyphCount), rects)) {
29786
+ drop = true;
29787
+ removedText++;
29788
+ }
29789
+ break;
29790
+ }
29791
+ case "Do":
29792
+ if (intersectsAny(imageBBox(state.ctm), rects)) {
29793
+ drop = true;
29794
+ removedImages++;
29795
+ }
29796
+ break;
29797
+ default: break;
29798
+ }
29799
+ if (!drop) kept.push(op);
29800
+ }
29801
+ return {
29802
+ kept,
29803
+ removedText,
29804
+ removedImages
29805
+ };
29806
+ }
29807
+ /** Replace the page's entire content stream with the given operator string. */
29808
+ function replacePageContent(page, content) {
29809
+ const internals = page;
29810
+ internals.ops = content;
29811
+ internals._originalContentRefs = [];
29812
+ }
29813
+ /**
29814
+ * Permanently remove text and image content within the given regions from a
29815
+ * page (redaction by removal).
29816
+ *
29817
+ * The page's content stream is parsed, replayed through a position-tracking
29818
+ * graphics-state machine, and re-emitted with every text-showing operator and
29819
+ * image placement that falls inside any redaction rect omitted. All other
29820
+ * operators — paths, fills, colors, clips, state changes — are preserved, so
29821
+ * the surrounding page layout is left intact. The filtered content is written
29822
+ * back to the page and takes effect on the next `save()`.
29823
+ *
29824
+ * @param page The page to redact (modified in place).
29825
+ * @param rects Redaction regions in page space (PDF points, y-up).
29826
+ * @returns Counts of removed text runs and image placements.
29827
+ *
29828
+ * @example
29829
+ * ```ts
29830
+ * const result = redactRegions(page, [{ x: 0, y: 90, width: 200, height: 30 }]);
29831
+ * console.log(`Removed ${result.removedText} text runs`);
29832
+ * const bytes = await doc.save();
29833
+ * ```
29834
+ */
29835
+ function redactRegions(page, rects) {
29836
+ if (rects.length === 0) return {
29837
+ removedText: 0,
29838
+ removedImages: 0
29839
+ };
29840
+ const { kept, removedText, removedImages } = filterOperators(parseContentStream(page.getContentStream()), rects);
29841
+ replacePageContent(page, kept.map(serializeOperator).join("\n") + (kept.length > 0 ? "\n" : ""));
29842
+ return {
29843
+ removedText,
29844
+ removedImages
29845
+ };
29846
+ }
29847
+ //#endregion
29848
+ //#region src/render/tiles.ts
29849
+ /**
29850
+ * @module render/tiles
29851
+ *
29852
+ * Tile-based rendering of large pages plus a small LRU cache. A page is
29853
+ * interpreted once into a {@link DisplayList}; each tile rasterizes only its
29854
+ * pixel sub-window via the rasterizer's `region` option, so arbitrarily large
29855
+ * pages render in bounded memory.
29856
+ *
29857
+ * @packageDocumentation
29858
+ */
29859
+ function scaleOf(opts) {
29860
+ if (typeof opts.scale === "number") return opts.scale;
29861
+ if (typeof opts.dpi === "number") return opts.dpi / 72;
29862
+ return 1;
29863
+ }
29864
+ function gridFromDisplayList(dl, opts) {
29865
+ const scale = scaleOf(opts);
29866
+ const tileSize = opts.tileSize ?? 512;
29867
+ const fullWidth = Math.max(1, Math.ceil(dl.width * scale));
29868
+ const fullHeight = Math.max(1, Math.ceil(dl.height * scale));
29869
+ return {
29870
+ columns: Math.max(1, Math.ceil(fullWidth / tileSize)),
29871
+ rows: Math.max(1, Math.ceil(fullHeight / tileSize)),
29872
+ tileSize,
29873
+ fullWidth,
29874
+ fullHeight,
29875
+ scale
29876
+ };
29877
+ }
29878
+ /** Compute the tile grid for a page. */
29879
+ function computeTileGrid(page, opts = {}) {
29880
+ return gridFromDisplayList(interpretPage(page), opts);
29881
+ }
29882
+ /** Render a single tile `(column, row)` of a page to an RGBA image. */
29883
+ function renderPageTile(page, column, row, opts = {}) {
29884
+ const dl = interpretPage(page);
29885
+ const grid = gridFromDisplayList(dl, opts);
29886
+ const x = column * grid.tileSize;
29887
+ const y = row * grid.tileSize;
29888
+ const width = Math.min(grid.tileSize, grid.fullWidth - x);
29889
+ const height = Math.min(grid.tileSize, grid.fullHeight - y);
29890
+ return rasterize(dl, {
29891
+ scale: grid.scale,
29892
+ background: opts.background,
29893
+ renderText: opts.renderText ?? true,
29894
+ region: {
29895
+ x,
29896
+ y,
29897
+ width: Math.max(1, width),
29898
+ height: Math.max(1, height)
29899
+ }
29900
+ });
29901
+ }
29902
+ /**
29903
+ * A simple least-recently-used cache (Map-backed). Useful for memoizing
29904
+ * interpreted display lists or rasterized tiles across renders.
29905
+ */
29906
+ var RenderCache = class {
29907
+ maxEntries;
29908
+ map = /* @__PURE__ */ new Map();
29909
+ constructor(maxEntries = 64) {
29910
+ this.maxEntries = maxEntries;
29911
+ }
29912
+ /** Number of cached entries. */
29913
+ get size() {
29914
+ return this.map.size;
29915
+ }
29916
+ /** Look up a value, marking it most-recently-used. */
29917
+ get(key) {
29918
+ const v = this.map.get(key);
29919
+ if (v !== void 0) {
29920
+ this.map.delete(key);
29921
+ this.map.set(key, v);
29922
+ }
29923
+ return v;
29924
+ }
29925
+ /** Whether a key is present (without affecting recency). */
29926
+ has(key) {
29927
+ return this.map.has(key);
29928
+ }
29929
+ /** Insert/replace a value, evicting the LRU entry when over capacity. */
29930
+ set(key, value) {
29931
+ if (this.map.has(key)) this.map.delete(key);
29932
+ this.map.set(key, value);
29933
+ while (this.map.size > this.maxEntries) {
29934
+ const oldest = this.map.keys().next().value;
29935
+ if (oldest === void 0) break;
29936
+ this.map.delete(oldest);
29937
+ }
29938
+ }
29939
+ /** Remove a key. */
29940
+ delete(key) {
29941
+ return this.map.delete(key);
29942
+ }
29943
+ /** Empty the cache. */
29944
+ clear() {
29945
+ this.map.clear();
29946
+ }
29947
+ };
29948
+ //#endregion
29949
+ //#region src/compliance/afAttach.ts
29950
+ /**
29951
+ * @module compliance/afAttach
29952
+ *
29953
+ * Attach embedded / associated files to any PDF object.
29954
+ *
29955
+ * The Associated Files mechanism (`/AF`) lets a PDF declare that one or more
29956
+ * embedded files are *associated* with a given object. ISO 32000-2 allows the
29957
+ * `/AF` key on the document catalog (§7.11.4) as well as on individual objects
29958
+ * — page dictionaries, annotation dictionaries, Form/Image XObject stream
29959
+ * dictionaries, and structure elements (§14.13). Because every one of those
29960
+ * targets is a plain {@link PdfDict}, the same helper attaches `/AF` uniformly.
29961
+ *
29962
+ * In addition, a *document-level* associated file must also be reachable
29963
+ * through the catalog's embedded-files name tree
29964
+ * (`/Names` → `/EmbeddedFiles` → `/Names`). {@link createAssociatedFile} (in
29965
+ * `associatedFiles.ts`) intentionally leaves that wiring to the caller;
29966
+ * {@link registerEmbeddedFile} completes it.
29967
+ *
29968
+ * These are pure builders: they mutate the supplied dictionaries in place and
29969
+ * return nothing. Object numbers / serialization remain the writer's concern.
29970
+ *
29971
+ * Reference: ISO 32000-2:2020, §7.11.4 (embedded file streams) and §14.13
29972
+ * (associated files).
29973
+ */
29974
+ /**
29975
+ * Attach one or more associated files to any PDF object via its `/AF` key.
29976
+ *
29977
+ * Works uniformly for the document catalog, a page dictionary, an annotation
29978
+ * dictionary, a Form/Image XObject stream's dictionary, or a structure-element
29979
+ * dictionary — all of which are plain {@link PdfDict} instances.
29980
+ *
29981
+ * If `target` has no `/AF` yet, a fresh array (built with
29982
+ * {@link buildAfArray}) is set. If `/AF` is already a {@link PdfArray}, the
29983
+ * new refs are appended to it, preserving the existing entries and order. Any
29984
+ * other (malformed) `/AF` value is replaced with a fresh array.
29985
+ *
29986
+ * @param target The object dictionary to attach the files to.
29987
+ * @param fileSpecRefs Indirect references to file-specification dictionaries.
29988
+ */
29989
+ function attachAssociatedFiles(target, fileSpecRefs) {
29990
+ const existing = target.get("/AF");
29991
+ if (existing instanceof PdfArray) {
29992
+ for (const ref of fileSpecRefs) existing.push(ref);
29993
+ return;
29994
+ }
29995
+ target.set("/AF", buildAfArray([...fileSpecRefs]));
29996
+ }
29997
+ /**
29998
+ * Locate the `/EmbeddedFiles` name-tree `/Names` array on the catalog,
29999
+ * creating the intermediate `/Names` and `/EmbeddedFiles` dictionaries (and
30000
+ * the leaf `/Names` array) if they do not yet exist. Existing dictionaries
30001
+ * are reused in place rather than replaced.
30002
+ */
30003
+ function ensureEmbeddedFilesNames(catalog) {
30004
+ const namesEntry = catalog.get("/Names");
30005
+ const namesDict = namesEntry instanceof PdfDict ? namesEntry : new PdfDict();
30006
+ if (namesDict !== namesEntry) catalog.set("/Names", namesDict);
30007
+ const efEntry = namesDict.get("/EmbeddedFiles");
30008
+ const efDict = efEntry instanceof PdfDict ? efEntry : new PdfDict();
30009
+ if (efDict !== efEntry) namesDict.set("/EmbeddedFiles", efDict);
30010
+ const leafEntry = efDict.get("/Names");
30011
+ const leaf = leafEntry instanceof PdfArray ? leafEntry : new PdfArray();
30012
+ if (leaf !== leafEntry) efDict.set("/Names", leaf);
30013
+ return leaf;
30014
+ }
30015
+ /**
30016
+ * Insert a `[name, fileSpecRef]` pair into an embedded-files name-tree `/Names`
30017
+ * array, keeping the array sorted by name. The array is a flat sequence of
30018
+ * alternating key/value entries: `[ name0, ref0, name1, ref1, … ]`.
30019
+ */
30020
+ function insertNamePairSorted(leaf, name, fileSpecRef) {
30021
+ const items = leaf.items;
30022
+ let insertAt = items.length;
30023
+ for (let i = 0; i < items.length; i += 2) {
30024
+ const entry = items[i];
30025
+ if ((entry instanceof PdfString ? entry.value : "") > name) {
30026
+ insertAt = i;
30027
+ break;
30028
+ }
30029
+ }
30030
+ items.splice(insertAt, 0, PdfString.literal(name), fileSpecRef);
30031
+ }
30032
+ /**
30033
+ * Complete {@link createAssociatedFile}'s "caller responsibility" by wiring a
30034
+ * file-specification reference into the document catalog at the *document
30035
+ * level*.
30036
+ *
30037
+ * Two things happen:
30038
+ * 1. The pair `[PdfString.literal(name), fileSpecRef]` is added to the
30039
+ * catalog's `/Names` → `/EmbeddedFiles` → `/Names` array, keeping the
30040
+ * array's name/value pairs sorted by name (as required for a name tree).
30041
+ * The intermediate dictionaries and the leaf array are created on demand
30042
+ * and reused if already present.
30043
+ * 2. `fileSpecRef` is added to the catalog's `/AF` array (via
30044
+ * {@link attachAssociatedFiles}).
30045
+ *
30046
+ * @param catalog The document catalog dictionary.
30047
+ * @param name The name under which the file is registered in the tree.
30048
+ * @param fileSpecRef Indirect reference to the file-specification dictionary.
30049
+ */
30050
+ function registerEmbeddedFile(catalog, name, fileSpecRef) {
30051
+ insertNamePairSorted(ensureEmbeddedFilesNames(catalog), name, fileSpecRef);
30052
+ attachAssociatedFiles(catalog, [fileSpecRef]);
30053
+ }
30054
+ //#endregion
30055
+ //#region src/compliance/pageOutputIntent.ts
30056
+ /**
30057
+ * @module compliance/pageOutputIntent
30058
+ *
30059
+ * Per-page and per-stream output intents (ISO 32000-2 §14.11.5).
30060
+ *
30061
+ * In addition to the document-level `/OutputIntents` array carried on the
30062
+ * catalog, PDF 2.0 permits an `/OutputIntents` array on individual *page*
30063
+ * dictionaries and on *Form XObject* stream dictionaries. These narrower
30064
+ * intents override the document intent for the colour-managed content of that
30065
+ * page or form, which is essential for documents that mix output conditions
30066
+ * (for example, a PDF/X job whose individual pages target different print
30067
+ * presses).
30068
+ *
30069
+ * This module provides two pure builders:
30070
+ *
30071
+ * - {@link buildPageOutputIntent} constructs a single `/OutputIntent`
30072
+ * dictionary (reusing {@link buildOutputIntent} for the embedded ICC
30073
+ * `/DestOutputProfile`) and registers it as an indirect object.
30074
+ * - {@link attachOutputIntents} sets — or, when one already exists, appends
30075
+ * to — the `/OutputIntents` array on a target dictionary (a page dict for
30076
+ * per-page intents, or a Form XObject stream's dict for per-stream).
30077
+ *
30078
+ * Reference: ISO 32000-2:2020, §14.11.5 (Output intents).
30079
+ */
30080
+ /**
30081
+ * Attach output-intent references to a target dictionary's `/OutputIntents`
30082
+ * array.
30083
+ *
30084
+ * If the target has no `/OutputIntents` entry, a fresh {@link PdfArray} is
30085
+ * created and populated with `intentRefs`. If one already exists (and is an
30086
+ * array), the references are appended to it in order, preserving the existing
30087
+ * array instance.
30088
+ *
30089
+ * The target may be a page dictionary (for per-page intents) or a Form
30090
+ * XObject stream's dictionary (for per-stream intents).
30091
+ *
30092
+ * @param target The page or Form XObject dictionary to mutate.
30093
+ * @param intentRefs Indirect references to `/OutputIntent` dictionaries.
30094
+ */
30095
+ function attachOutputIntents(target, intentRefs) {
30096
+ const existing = target.get("/OutputIntents");
30097
+ const array = existing instanceof PdfArray ? existing : new PdfArray();
30098
+ for (const ref of intentRefs) array.push(ref);
30099
+ if (!(existing instanceof PdfArray)) target.set("/OutputIntents", array);
30100
+ }
30101
+ /**
30102
+ * Build a per-page / per-stream `/OutputIntent` dictionary and register it.
30103
+ *
30104
+ * The embedded ICC `/DestOutputProfile` is produced by reusing
30105
+ * {@link buildOutputIntent}; this builder then layers the per-page subtype
30106
+ * (`/S`), output-condition identifier and optional `/Info` string on top.
30107
+ * The resulting dictionary carries `/Type /OutputIntent` and may be attached
30108
+ * to a page or Form XObject dictionary via {@link attachOutputIntents}.
30109
+ *
30110
+ * @param registry The PDF object registry to register objects into.
30111
+ * @param options Output-intent configuration.
30112
+ * @returns An indirect reference to the `/OutputIntent` dictionary.
30113
+ */
30114
+ function buildPageOutputIntent(registry, options = {}) {
30115
+ const intentRef = buildOutputIntent(registry, {
30116
+ subtype: options.subtype ?? "/GTS_PDFA1",
30117
+ ...options.iccProfile !== void 0 ? { iccProfile: options.iccProfile } : {},
30118
+ ...options.components !== void 0 ? { components: options.components } : {},
30119
+ ...options.outputConditionIdentifier !== void 0 ? { outputConditionIdentifier: options.outputConditionIdentifier } : {}
30120
+ });
30121
+ const intentDict = registry.resolve(intentRef);
30122
+ if (intentDict !== void 0 && intentDict.kind === "dict") {
30123
+ intentDict.set("/Type", PdfName.of("OutputIntent"));
30124
+ if (options.outputConditionIdentifier !== void 0) intentDict.set("/OutputConditionIdentifier", PdfString.literal(options.outputConditionIdentifier));
30125
+ if (options.info !== void 0) intentDict.set("/Info", PdfString.literal(options.info));
30126
+ }
30127
+ return intentRef;
30128
+ }
30129
+ //#endregion
30130
+ //#region src/compliance/encryptedPayload.ts
30131
+ /**
30132
+ * @module compliance/encryptedPayload
30133
+ *
30134
+ * Unencrypted wrapper documents and encrypted payloads
30135
+ * (ISO 32000-2:2020 §7.6.7).
30136
+ *
30137
+ * A PDF 2.0 *unencrypted wrapper document* is an ordinary, viewable PDF that
30138
+ * carries an *encrypted payload* as an embedded file. The wrapper itself is
30139
+ * not encrypted (so any conforming reader can open it and show a cover page),
30140
+ * while the sensitive content lives in the embedded payload, which is
30141
+ * protected by an external/handler-specific crypto filter.
30142
+ *
30143
+ * The payload is declared through an *Encrypted Payload* dictionary
30144
+ * (`/Type /EncryptedPayload`). Its `/Subtype` names the crypto filter used to
30145
+ * protect the payload (for example `/AESV3`), and an optional `/Version`
30146
+ * records the version of that filter. The Encrypted Payload dictionary is
30147
+ * attached to the embedded file's *file specification* dictionary via the
30148
+ * `/EP` key, and that file specification is also marked with
30149
+ * `/AFRelationship /EncryptedPayload`.
30150
+ *
30151
+ * This module provides two pure builders:
30152
+ *
30153
+ * - {@link buildEncryptedPayload} — build the `/EncryptedPayload` dictionary.
30154
+ * - {@link buildUnencryptedWrapper} — embed the payload bytes as an
30155
+ * associated file and attach the `/EP` dictionary to its file
30156
+ * specification, returning the file-spec reference.
30157
+ *
30158
+ * The caller attaches the returned file-spec reference to the catalog `/AF`
30159
+ * array and to the `/Names /EmbeddedFiles` name tree (for example via
30160
+ * `afAttach`).
30161
+ *
30162
+ * Reference: ISO 32000-2:2020, §7.6.7 (Unencrypted wrapper document).
30163
+ */
30164
+ /**
30165
+ * Build an `/EncryptedPayload` dictionary (ISO 32000-2 §7.6.7).
30166
+ *
30167
+ * The returned dictionary has:
30168
+ * - `/Type /EncryptedPayload`
30169
+ * - `/Subtype /<subtype>` — the crypto filter name
30170
+ * - `/Version /<version>` — only when {@link EncryptedPayloadOptions.version}
30171
+ * is supplied
30172
+ *
30173
+ * @param options - The crypto-filter subtype and optional version.
30174
+ * @returns A spec-shaped `/EncryptedPayload` {@link PdfDict}.
30175
+ */
30176
+ function buildEncryptedPayload(options) {
30177
+ const dict = new PdfDict();
30178
+ dict.set("/Type", PdfName.of("EncryptedPayload"));
30179
+ dict.set("/Subtype", PdfName.of(options.subtype));
30180
+ if (options.version !== void 0) dict.set("/Version", PdfName.of(options.version));
30181
+ return dict;
30182
+ }
30183
+ /**
30184
+ * Build the embedded encrypted-payload file for an unencrypted wrapper
30185
+ * document and return its file-specification reference.
30186
+ *
30187
+ * The payload bytes are embedded via {@link createAssociatedFile} with an
30188
+ * `/AFRelationship` of `/EncryptedPayload` and an `application/octet-stream`
30189
+ * MIME type. The resolved file-specification dictionary then receives an
30190
+ * `/EP` entry holding the {@link buildEncryptedPayload} dictionary.
30191
+ *
30192
+ * The caller is responsible for attaching the returned file-spec reference to
30193
+ * the catalog `/AF` array and the `/Names /EmbeddedFiles` name tree.
30194
+ *
30195
+ * @param registry - The PDF object registry to register objects into.
30196
+ * @param options - The payload bytes, filename, crypto subtype/version, and
30197
+ * optional description.
30198
+ * @returns An indirect reference to the file-specification dictionary.
30199
+ */
30200
+ function buildUnencryptedWrapper(registry, options) {
30201
+ const { fileSpecRef } = createAssociatedFile(registry, {
30202
+ data: options.data,
30203
+ filename: options.filename,
30204
+ mimeType: "application/octet-stream",
30205
+ relationship: "EncryptedPayload",
30206
+ description: options.description
30207
+ });
30208
+ const fileSpec = registry.resolve(fileSpecRef);
30209
+ if (fileSpec instanceof PdfDict) {
30210
+ const ep = buildEncryptedPayload({
30211
+ subtype: options.subtype,
30212
+ version: options.version
30213
+ });
30214
+ fileSpec.set("/EP", ep);
30215
+ }
30216
+ return fileSpecRef;
30217
+ }
30218
+ //#endregion
30219
+ //#region src/core/softMask.ts
30220
+ /**
30221
+ * @module core/softMask
30222
+ *
30223
+ * Soft-mask groups in graphics state (ISO 32000-1 §11.6.5.2).
30224
+ *
30225
+ * A *soft mask* defines a per-pixel shape/opacity for subsequent painting
30226
+ * operations. It is carried in an `ExtGState` dictionary under the `/SMask`
30227
+ * key. The mask is derived from a *transparency-group XObject* (a form
30228
+ * XObject whose `/Group` dictionary has `/S /Transparency`):
30229
+ *
30230
+ * - A **luminosity** mask (`/S /Luminosity`) computes the mask value from the
30231
+ * luminosity of the group's composited result. An optional `/BC` backdrop
30232
+ * color sets the colour against which the group is composited before the
30233
+ * luminosity is measured.
30234
+ * - An **alpha** mask (`/S /Alpha`) computes the mask value from the group's
30235
+ * accumulated alpha (shape/opacity). `/BC` is not meaningful and is omitted.
30236
+ *
30237
+ * An optional `/TR` *transfer function* remaps the computed mask values; it may
30238
+ * be an indirect reference to a function object or the name `/Identity`.
30239
+ *
30240
+ * These are pure builders: they return spec-shaped `ExtGState` dictionaries.
30241
+ * Registering the result and naming it in a page's `/Resources /ExtGState`
30242
+ * (e.g. via `page.registerExtGState(name, registry.register(extGState))`) is
30243
+ * the caller's concern.
30244
+ *
30245
+ * Reference: ISO 32000-1:2008, §11.6.5.2 (Mask shape and opacity);
30246
+ * ISO 32000-1:2008, §8.4.5 (Graphics state, `/SMask`).
30247
+ */
30248
+ /**
30249
+ * Build an `ExtGState` dictionary that installs a soft-mask group.
30250
+ *
30251
+ * The returned dictionary has `/Type /ExtGState` and an `/SMask` mask
30252
+ * dictionary:
30253
+ *
30254
+ * - `/Type /Mask`
30255
+ * - `/S /Luminosity` or `/S /Alpha` (per {@link SoftMaskGroupOptions.type},
30256
+ * defaulting to `Luminosity`)
30257
+ * - `/G` → the supplied group XObject reference
30258
+ * - `/BC [ … ]` — backdrop colour, **only** for luminosity masks and only
30259
+ * when {@link SoftMaskGroupOptions.backdropColor} is provided
30260
+ * - `/TR` → the transfer function (indirect reference) or the name
30261
+ * `/Identity`, only when {@link SoftMaskGroupOptions.transferFunction} is
30262
+ * provided
30263
+ *
30264
+ * @param options - The soft-mask group description.
30265
+ * @returns A spec-shaped `ExtGState` {@link PdfDict}.
30266
+ */
30267
+ function buildSoftMaskGroupExtGState(options) {
30268
+ const subtype = options.type ?? "Luminosity";
30269
+ const smask = new PdfDict();
30270
+ smask.set("/Type", PdfName.of("Mask"));
30271
+ smask.set("/S", PdfName.of(subtype));
30272
+ smask.set("/G", options.groupXObject);
30273
+ if (subtype === "Luminosity" && options.backdropColor !== void 0 && options.backdropColor.length > 0) smask.set("/BC", PdfArray.fromNumbers([...options.backdropColor]));
30274
+ if (options.transferFunction !== void 0) smask.set("/TR", options.transferFunction === "Identity" ? PdfName.of("Identity") : options.transferFunction);
30275
+ const extGState = new PdfDict();
30276
+ extGState.set("/Type", PdfName.of("ExtGState"));
30277
+ extGState.set("/SMask", smask);
30278
+ return extGState;
30279
+ }
30280
+ /**
30281
+ * Build an `ExtGState` dictionary that clears any active soft mask by setting
30282
+ * `/SMask /None`. Apply this after a masked region to restore unmasked
30283
+ * painting.
30284
+ *
30285
+ * @returns An `ExtGState` {@link PdfDict} with `/SMask /None`.
30286
+ */
30287
+ function buildSoftMaskNone() {
30288
+ const extGState = new PdfDict();
30289
+ extGState.set("/Type", PdfName.of("ExtGState"));
30290
+ extGState.set("/SMask", PdfName.of("None"));
30291
+ return extGState;
30292
+ }
30293
+ //#endregion
30294
+ //#region src/core/imageMask.ts
30295
+ /**
30296
+ * @module core/imageMask
30297
+ *
30298
+ * Image masks and black-point compensation per ISO 32000-2 §8.9.6
30299
+ * (Masked images) and §8.6.5.9 (Black-point compensation).
30300
+ *
30301
+ * PDF supports three ways of masking an image XObject:
30302
+ *
30303
+ * - **Stencil mask** — a 1-bit-per-component `/ImageMask` image whose sample
30304
+ * values select which areas of the page are painted with the current fill
30305
+ * colour and which are left untouched (§8.9.6.2). The mask data is packed
30306
+ * one bit per sample, each row byte-aligned (`ceil(width / 8)` bytes per
30307
+ * row); the caller supplies the already-packed bytes.
30308
+ * - **Colour-key mask** — a `/Mask` array of `[min0 max0 min1 max1 …]`
30309
+ * integer ranges (two per colour component) that makes matching sample
30310
+ * colours transparent (§8.9.6.4).
30311
+ * - **Soft mask** — a separate DeviceGray image referenced from a base
30312
+ * image's `/SMask` entry, giving per-pixel opacity (§11.6.5.2).
30313
+ *
30314
+ * Black-point compensation (§8.6.5.9) is controlled through an ExtGState's
30315
+ * `/UseBlackPtComp` entry, which selects whether the conversion between
30316
+ * colour spaces maps source black to destination black.
30317
+ *
30318
+ * These are pure builders: they construct (and, for the stream variants,
30319
+ * register) PDF objects but do not wire them into a page or resource
30320
+ * dictionary — that is the caller's concern.
30321
+ *
30322
+ * Reference: ISO 32000-2:2020, §8.9.6 (Masked images), §8.6.5.9
30323
+ * (Black-point compensation).
30324
+ */
30325
+ /**
30326
+ * Build a stencil mask: a 1-bit `/ImageMask` image XObject (§8.9.6.2).
30327
+ *
30328
+ * The returned stream has `/Type /XObject`, `/Subtype /Image`,
30329
+ * `/ImageMask true`, `/BitsPerComponent 1`, and the supplied `/Width` and
30330
+ * `/Height`. When a `decode` pair is given it is emitted as a `/Decode`
30331
+ * array, allowing the mask polarity to be inverted (`[1 0]`).
30332
+ *
30333
+ * The `bits` buffer must already be packed one bit per sample with each row
30334
+ * byte-aligned (`ceil(width / 8)` bytes per row); this builder stores it
30335
+ * verbatim and does not repack it.
30336
+ *
30337
+ * @param registry - Registry to register the stream into.
30338
+ * @param bits - Packed, byte-aligned 1-bpc mask data.
30339
+ * @param width - Image width in samples.
30340
+ * @param height - Image height in samples.
30341
+ * @param decode - Optional `[d0, d1]` decode pair.
30342
+ * @returns An indirect reference to the registered image XObject stream.
30343
+ */
30344
+ function buildStencilMask(registry, bits, width, height, decode) {
30345
+ const dict = new PdfDict();
30346
+ dict.set("/Type", PdfName.of("XObject"));
30347
+ dict.set("/Subtype", PdfName.of("Image"));
30348
+ dict.set("/ImageMask", PdfBool.TRUE);
30349
+ dict.set("/BitsPerComponent", PdfNumber.of(1));
30350
+ dict.set("/Width", PdfNumber.of(width));
30351
+ dict.set("/Height", PdfNumber.of(height));
30352
+ if (decode !== void 0) dict.set("/Decode", PdfArray.fromNumbers([decode[0], decode[1]]));
30353
+ dict.set("/Length", PdfNumber.of(bits.length));
30354
+ return registry.register(new PdfStream(dict, bits));
30355
+ }
30356
+ /**
30357
+ * Build a colour-key masking array (§8.9.6.4).
30358
+ *
30359
+ * The `ranges` list holds two integers — a minimum and a maximum — per
30360
+ * colour component, laid out as `[min0 max0 min1 max1 …]`. Sample colours
30361
+ * whose every component falls within its range become transparent. The
30362
+ * returned {@link PdfArray} is assigned to a base image's `/Mask` entry.
30363
+ *
30364
+ * @param ranges - An even-length list of `[min, max]` integer pairs.
30365
+ * @returns A `/Mask` {@link PdfArray} of {@link PdfNumber}s.
30366
+ * @throws RangeError if `ranges` is empty or has an odd length.
30367
+ */
30368
+ function buildColorKeyMask(ranges) {
30369
+ if (ranges.length === 0 || ranges.length % 2 !== 0) throw new RangeError(`colour-key mask requires a non-empty even number of values (min/max pairs); got ${ranges.length}`);
30370
+ return new PdfArray(ranges.map((v) => PdfNumber.of(v)));
30371
+ }
30372
+ /**
30373
+ * Build an image soft mask: a DeviceGray image XObject usable as a base
30374
+ * image's `/SMask` (§11.6.5.2).
30375
+ *
30376
+ * The returned stream has `/Type /XObject`, `/Subtype /Image`,
30377
+ * `/ColorSpace /DeviceGray`, the supplied `/Width` and `/Height`, and
30378
+ * `/BitsPerComponent` (defaulting to 8). Each grayscale sample provides the
30379
+ * opacity of the corresponding base-image pixel.
30380
+ *
30381
+ * @param registry - Registry to register the stream into.
30382
+ * @param gray - Grayscale opacity samples.
30383
+ * @param width - Image width in samples.
30384
+ * @param height - Image height in samples.
30385
+ * @param bitsPerComponent - Bits per sample; defaults to 8.
30386
+ * @returns An indirect reference to the registered soft-mask image XObject.
30387
+ */
30388
+ function buildImageSoftMask(registry, gray, width, height, bitsPerComponent) {
30389
+ const dict = new PdfDict();
30390
+ dict.set("/Type", PdfName.of("XObject"));
30391
+ dict.set("/Subtype", PdfName.of("Image"));
30392
+ dict.set("/Width", PdfNumber.of(width));
30393
+ dict.set("/Height", PdfNumber.of(height));
30394
+ dict.set("/ColorSpace", PdfName.of("DeviceGray"));
30395
+ dict.set("/BitsPerComponent", PdfNumber.of(bitsPerComponent ?? 8));
30396
+ dict.set("/Length", PdfNumber.of(gray.length));
30397
+ return registry.register(new PdfStream(dict, gray));
30398
+ }
30399
+ /**
30400
+ * Build an ExtGState that controls black-point compensation (§8.6.5.9).
30401
+ *
30402
+ * The returned dictionary has `/Type /ExtGState` and `/UseBlackPtComp` set
30403
+ * to the requested mode:
30404
+ *
30405
+ * - `Default` — leave the choice to the conforming reader.
30406
+ * - `ON` — always apply black-point compensation.
30407
+ * - `OFF` — never apply black-point compensation.
30408
+ *
30409
+ * @param mode - The `/UseBlackPtComp` mode.
30410
+ * @returns An ExtGState {@link PdfDict}.
30411
+ */
30412
+ function buildBlackPointCompensationExtGState(mode) {
30413
+ const gs = new PdfDict();
30414
+ gs.set("/Type", PdfName.of("ExtGState"));
30415
+ gs.set("/UseBlackPtComp", PdfName.of(mode));
30416
+ return gs;
30417
+ }
30418
+ //#endregion
27841
30419
  //#region src/index.ts
27842
30420
  /** Whether initWasm has already completed successfully. */
27843
30421
  let wasmInitialized = false;
@@ -27864,15 +30442,15 @@ async function initWasm(options) {
27864
30442
  if (wasmInitialized) return;
27865
30443
  const inits = [];
27866
30444
  if (options.deflate || options.deflateWasm) inits.push((async () => {
27867
- const { initDeflateWasm } = await import("./pdfDocument-B6czyaVI.mjs").then((n) => n.zt);
30445
+ const { initDeflateWasm } = await import("./pdfDocument-Cgkkk8F8.mjs").then((n) => n.zt);
27868
30446
  await initDeflateWasm(options.deflateWasm);
27869
30447
  })());
27870
30448
  if (options.png || options.pngWasm) inits.push((async () => {
27871
- const { initPngWasm } = await import("./pdfDocument-B6czyaVI.mjs").then((n) => n.At);
30449
+ const { initPngWasm } = await import("./pdfDocument-Cgkkk8F8.mjs").then((n) => n.At);
27872
30450
  await initPngWasm(options.pngWasm);
27873
30451
  })());
27874
30452
  if (options.fonts || options.fontWasm) inits.push((async () => {
27875
- const { initSubsetWasm } = await import("./pdfDocument-B6czyaVI.mjs").then((n) => n.Pt);
30453
+ const { initSubsetWasm } = await import("./pdfDocument-Cgkkk8F8.mjs").then((n) => n.Pt);
27876
30454
  await initSubsetWasm(options.fontWasm);
27877
30455
  })());
27878
30456
  if (options.jpeg || options.jpegWasm) inits.push((async () => {
@@ -27883,6 +30461,6 @@ async function initWasm(options) {
27883
30461
  wasmInitialized = true;
27884
30462
  }
27885
30463
  //#endregion
27886
- export { buildPdfVtDParts as $, PdfLineAnnotation as $i, readBarcode as $n, parseXmpMetadata as $r, tableToJson as $t, extractCrlUrls as A, getFieldLocks as Ai, PluginError as An, encodePngFromPixels as Ar, buildCollection as At, validateFieldValue as B, saveIncrementalWithSignaturePreservation as Bi, toRoman as Bn, downscaleImage as Br, SARIF_SCHEMA_URI as Bt, assembleTiles as C, buildDeviceNColorSpace as Ca, embedLtvData as Ci, FontNotEmbeddedError as Cn, code128ToOperators as Cr, buildThresholdHalftone as Ct, verifySignatureDetailed as D, saveIncremental as Da, diffSignedContent as Di, InvalidPageSizeError as Dn, PdfWorker as Dr, nameHalftone as Dt, parseTileInfo as E, saveDocumentIncremental as Ea, getCounterSignatures as Ei, InvalidFieldNamePartError as En, valuesToModules as Er, identityTransferFunction as Et, getFieldValue as F, setCertificationLevel as Fi, applyHeaderFooter as Fn, embedTiffCmyk as Fr, buildCalGray as Ft, AFNumber_Format as G, searchTextItems as Gi, truncateText as Gn, enforcePdfAFull as Gr, buildNamespace as Gt, AFDate_FormatEx as H, buildTimestampRequest as Hi, ellipsisText as Hn, optimizeImage as Hr, toSarif as Ht, resolveFieldReference as I, validateSignatureChain as Ii, applyHeaderFooterToPage as In, isCmykTiff as Ir, buildCalRGB as It, isWoff as J, PdfPopupAnnotation as Ji, applyTablePreset as Jn, countOccurrences as Jr, buildRequirement as Jt, formatNumber as K, PdfFileAttachmentAnnotation as Ki, wrapText$2 as Kn, generatePdfAXmp as Kr, buildNamespacesArray as Kt, setFieldValue as L, appendIncrementalUpdate as Li, formatDate$1 as Ln, extractJpegMetadata as Lr, buildLab as Lt, checkCertificateStatus as M, MdpPermission as Mi, RichTextFieldReadError as Mn, webpToJpeg as Mr, reconstructParagraphs as Mt, extractOcspUrl as N, buildDocMdpReference as Ni, StreamingParseError as Nn, webpToPng as Nr, DEFAULT_DOC_TIMESTAMP_CONTENTS_SIZE as Nt, TrustStore as O, addFieldLock as Oi, MissingOnValueCheckError as On, canDirectEmbed as Or, evaluateFunction as Ot, createSandbox as P, getCertificationLevel as Pi, UnexpectedFieldTypeError as Pn, convertTiffCmykToRgb as Pr, buildDocTimeStampDict as Pt, createWorkerPool as Q, PdfCircleAnnotation as Qi, stripedPreset as Qn, extractXmpMetadata as Qr, tableToCsv as Qt, addVisibilityAction as R, findExistingSignatures as Ri, replaceTemplateVariables as Rn, injectJpegMetadata as Rr, labToRgb as Rt, upscale8To16 as S, PDFOperator as Sa, buildDssDictionary as Si, FieldExistsAsNonTerminalError as Sn, encodeCode39 as Sr, buildSampledTransferFunction as St, decodeTileRegion as T, ChangeTracker as Ta, addCounterSignature as Ti, InvalidColorError as Tn, encodeCode128Values as Tr, buildType5Halftone as Tt, formatDate as U, parseTimestampResponse as Ui, estimateTextWidth as Un, recompressImage as Ur, MATHML_NAMESPACE as Ut, AFSpecial_Format as V, validateByteRangeIntegrity as Vi, applyOverflow as Vn, estimateJpegQuality as Vr, toJsonReport as Vt, parseAcrobatDate as W, requestTimestamp as Wi, shrinkFontSize as Wn, applyRedaction as Wr, PDF2_NAMESPACE as Wt, readWoffHeader as X, PdfInkAnnotation as Xi, minimalPreset as Xn, buildAfArray as Xr, buildDPartRoot as Xt, isWoff2 as Y, PdfRedactAnnotation as Yi, borderedPreset as Yn, stripProhibitedFeatures as Yr, buildRequirements as Yt, signDeferred as Z, PdfStampAnnotation as Zi, professionalPreset as Zn, createAssociatedFile as Zr, extractTables as Zt, downscale16To8 as _, PdfLinkAnnotation as _a, isLinearized as _i, BatchProcessingError as _n, encodeEan8 as _r, buildBoxDict as _t, validateCertificatePolicy as a, PdfStrikeOutAnnotation as aa, generateWinAnsiToUnicodeCmap as ai, enforcePdfUa as an, renderStyledBarcode as ar, resolveFallback as at, offsetSignedToUnsigned as b, createXmpStream as ba, findChangedObjects as bi, ExceededMaxLengthError as bn, code39ToOperators as br, validateBoxGeometry as bt, buildCertificateChain as c, generateCircleAppearance as ca, buildOutputIntent as ci, batchMerge as cn, dataMatrixToOperators as cr, generateXRechnungCii as ct, layoutColumns as d, generateInkAppearance as da, detectTransparency as di, getBookmarks as dn, encodeUpcA as dr, buildFunctionShading as dt, PdfPolyLineAnnotation as ea, validateXmpMetadata as ei, accessibilityPlugin as en, readCode128 as er, buildVtDpm as et, layoutParagraph as f, generateLineAppearance as fa, flattenTransparency as fi, removeAllBookmarks as fn, upcAToOperators as fr, sampleShadingColor as ft, validatePdfX as g, generateUnderlineAppearance as ga, getLinearizationInfo as gi, flattenForm as gn, encodeEan13 as gr, generateCiiXml as gt, enforcePdfX as h, generateStrikeOutAppearance as ha, delinearizePdf as hi, flattenFields as hn, ean8ToOperators as hr, renderCodeFrame as ht, EKU_OIDS as i, PdfSquigglyAnnotation as ia, generateSymbolToUnicodeCmap as ii, PdfDocumentBuilder as in, calculateBarcodeDimensions as ir, createRangeFetcher as it, isCertificateRevoked as j, detectModifications as ji, RemovePageFromEmptyDocumentError as jn, recompressWebP as jr, reconstructLines as jt, downloadCrl as k, buildFieldLockDict as ki, NoSuchFieldError as kn, embedTiffDirect as kr, markdownToPdf as kt, validateCertificateChain as l, generateFreeTextAppearance as la, SRGB_ICC_PROFILE as li, processBatch as ln, encodeDataMatrix as lr, buildPdfA4Xmp as lt, buildPdfXOutputIntent as m, generateSquigglyAppearance as ma, validatePdfA as mi, flattenField as mn, ean13ToOperators as mr, levenshtein as mt, extractEmbeddedRevocationData as n, PdfSquareAnnotation as na, getSupportedLevels as ni, timestampPlugin as nn, readEan13 as nr, h as nt, validateExtendedKeyUsage as o, PdfUnderlineAnnotation as oa, generateZapfDingbatsToUnicodeCmap as oi, validatePdfUa as on, encodePdf417 as or, splitByScript as ot, layoutTextFlow as p, generateSquareAppearance as pa, enforcePdfA as pi, removeBookmark as pn, calculateEanCheckDigit as pr, didYouMean as pt, decodeWoff as q, PdfCaretAnnotation as qi, applyPreset as qn, generatePdfAXmpBytes as qr, buildPieceInfo as qt, verifyOfflineRevocation as r, PdfHighlightAnnotation as ra, isValidLevel as ri, StreamingPdfParser as rn, readEan8 as rr, renderToPdf as rt, validateKeyUsage as s, PdfFreeTextAnnotation as sa, getToUnicodeCmap as si, batchFlatten as sn, pdf417ToOperators as sr, generateOrderX as st, initWasm as t, PdfPolygonAnnotation as ta, getProfile as ti, metadataPlugin as tn, readCode39 as tr, gtsPdfVtVersion as tt, findHyphenationPoints as u, generateHighlightAppearance as ua, generateSrgbIccProfile as ui, addBookmark as un, calculateUpcCheckDigit as ur, pdfA4Rules as ut, getComponentDepths as v, PdfTextAnnotation as va, linearizePdf as vi, CombedTextLayoutError as vn, encodeItf as vr, buildGtsPdfxVersion as vt, decodeTile as w, buildSeparationColorSpace as wa, hasLtvData as wi, ForeignPageError as wn, encodeCode128 as wr, buildType1Halftone as wt, summarizeBitDepth as x, parseXmpMetadata$1 as xa, optimizeIncrementalSave as xi, FieldAlreadyExistsError as xn, computeCode39CheckDigit as xr, STANDARD_SPOT_FUNCTIONS as xt, normalizeComponentDepth as y, buildXmpMetadata as ya, computeObjectHash as yi, EncryptedPdfError as yn, itfToOperators as yr, buildPdfX6OutputIntent as yt, setFieldVisibility as z, parseExistingTrailer as zi, toAlpha as zn, analyzeJpegMarkers as zr, DEFAULT_SARIF_TOOL_NAME as zt };
30464
+ export { decodeTileRegion as $, ChangeTracker as $a, addCounterSignature as $i, InvalidColorError as $n, encodeCode128Values as $r, buildType5Halftone as $t, extractEmbeddedRevocationData as A, PdfSquareAnnotation as Aa, getSupportedLevels as Ai, timestampPlugin as An, readEan13 as Ar, h as At, layoutParagraph as B, generateLineAppearance as Ba, flattenTransparency as Bi, removeAllBookmarks as Bn, upcAToOperators as Br, sampleShadingColor as Bt, generateThumbnail as C, PdfRedactAnnotation as Ca, stripProhibitedFeatures as Ci, buildRequirements as Cn, borderedPreset as Cr, isWoff2 as Ct, renderPageToImage as D, PdfLineAnnotation as Da, parseXmpMetadata as Di, tableToJson as Dn, readBarcode as Dr, buildPdfVtDParts as Dt, rasterize as E, PdfCircleAnnotation as Ea, extractXmpMetadata as Ei, tableToCsv as En, stripedPreset as Er, createWorkerPool as Et, validateKeyUsage as F, PdfFreeTextAnnotation as Fa, getToUnicodeCmap as Fi, batchFlatten as Fn, pdf417ToOperators as Fr, generateOrderX as Ft, downscale16To8 as G, PdfLinkAnnotation as Ga, isLinearized as Gi, BatchProcessingError as Gn, encodeEan8 as Gr, buildBoxDict as Gt, buildPdfXOutputIntent as H, generateSquigglyAppearance as Ha, validatePdfA as Hi, flattenField as Hn, ean13ToOperators as Hr, levenshtein as Ht, buildCertificateChain as I, generateCircleAppearance as Ia, buildOutputIntent as Ii, batchMerge as In, dataMatrixToOperators as Ir, generateXRechnungCii as It, offsetSignedToUnsigned as J, createXmpStream as Ja, findChangedObjects as Ji, ExceededMaxLengthError as Jn, code39ToOperators as Jr, validateBoxGeometry as Jt, getComponentDepths as K, PdfTextAnnotation as Ka, linearizePdf as Ki, CombedTextLayoutError as Kn, encodeItf as Kr, buildGtsPdfxVersion as Kt, validateCertificateChain as L, generateFreeTextAppearance as La, SRGB_ICC_PROFILE as Li, processBatch as Ln, encodeDataMatrix as Lr, buildPdfA4Xmp as Lt, EKU_OIDS as M, PdfSquigglyAnnotation as Ma, generateSymbolToUnicodeCmap as Mi, PdfDocumentBuilder as Mn, calculateBarcodeDimensions as Mr, createRangeFetcher as Mt, validateCertificatePolicy as N, PdfStrikeOutAnnotation as Na, generateWinAnsiToUnicodeCmap as Ni, enforcePdfUa as Nn, renderStyledBarcode as Nr, resolveFallback as Nt, interpretContentStream as O, PdfPolyLineAnnotation as Oa, validateXmpMetadata as Oi, accessibilityPlugin as On, readCode128 as Or, buildVtDpm as Ot, validateExtendedKeyUsage as P, PdfUnderlineAnnotation as Pa, generateZapfDingbatsToUnicodeCmap as Pi, validatePdfUa as Pn, encodePdf417 as Pr, splitByScript as Pt, decodeTile as Q, buildSeparationColorSpace as Qa, hasLtvData as Qi, ForeignPageError as Qn, encodeCode128 as Qr, buildType1Halftone as Qt, findHyphenationPoints as R, generateHighlightAppearance as Ra, generateSrgbIccProfile as Ri, addBookmark as Rn, calculateUpcCheckDigit as Rr, pdfA4Rules as Rt, extractImages as S, PdfPopupAnnotation as Sa, countOccurrences as Si, buildRequirement as Sn, applyTablePreset as Sr, isWoff as St, renderPageToCanvas as T, PdfStampAnnotation as Ta, createAssociatedFile as Ti, extractTables as Tn, professionalPreset as Tr, signDeferred as Tt, enforcePdfX as U, generateStrikeOutAppearance as Ua, delinearizePdf as Ui, flattenFields as Un, ean8ToOperators as Ur, renderCodeFrame as Ut, layoutTextFlow as V, generateSquareAppearance as Va, enforcePdfA as Vi, removeBookmark as Vn, calculateEanCheckDigit as Vr, didYouMean as Vt, validatePdfX as W, generateUnderlineAppearance as Wa, getLinearizationInfo as Wi, flattenForm as Wn, encodeEan13 as Wr, generateCiiXml as Wt, upscale8To16 as X, PDFOperator as Xa, buildDssDictionary as Xi, FieldExistsAsNonTerminalError as Xn, encodeCode39 as Xr, buildSampledTransferFunction as Xt, summarizeBitDepth as Y, parseXmpMetadata$1 as Ya, optimizeIncrementalSave as Yi, FieldAlreadyExistsError as Yn, computeCode39CheckDigit as Yr, STANDARD_SPOT_FUNCTIONS as Yt, assembleTiles as Z, buildDeviceNColorSpace as Za, embedLtvData as Zi, FontNotEmbeddedError as Zn, code128ToOperators as Zr, buildThresholdHalftone as Zt, redactRegions as _, parseTimestampResponse as _a, recompressImage as _i, MATHML_NAMESPACE as _n, estimateTextWidth as _r, formatDate as _t, buildStencilMask as a, detectModifications as aa, recompressWebP as ai, reconstructLines as an, RemovePageFromEmptyDocumentError as ar, isCertificateRevoked as at, comparePages as b, PdfFileAttachmentAnnotation as ba, generatePdfAXmp as bi, buildNamespacesArray as bn, wrapText$2 as br, formatNumber$1 as bt, buildEncryptedPayload as c, getCertificationLevel as ca, convertTiffCmykToRgb as ci, buildDocTimeStampDict as cn, UnexpectedFieldTypeError as cr, createSandbox as ct, buildPageOutputIntent as d, appendIncrementalUpdate as da, extractJpegMetadata as di, buildLab as dn, formatDate$1 as dr, setFieldValue as dt, getCounterSignatures as ea, valuesToModules as ei, identityTransferFunction as en, saveDocumentIncremental as eo, InvalidFieldNamePartError as er, parseTileInfo as et, attachAssociatedFiles as f, findExistingSignatures as fa, injectJpegMetadata as fi, labToRgb as fn, replaceTemplateVariables as fr, addVisibilityAction as ft, renderPageTile as g, buildTimestampRequest as ga, optimizeImage as gi, toSarif as gn, ellipsisText as gr, AFDate_FormatEx as gt, computeTileGrid as h, validateByteRangeIntegrity as ha, estimateJpegQuality as hi, toJsonReport as hn, applyOverflow as hr, AFSpecial_Format as ht, buildImageSoftMask as i, getFieldLocks as ia, encodePngFromPixels as ii, buildCollection as in, PluginError as ir, extractCrlUrls as it, verifyOfflineRevocation as j, PdfHighlightAnnotation as ja, isValidLevel as ji, StreamingPdfParser as jn, readEan8 as jr, renderToPdf as jt, interpretPage as k, PdfPolygonAnnotation as ka, getProfile as ki, metadataPlugin as kn, readCode39 as kr, gtsPdfVtVersion as kt, buildUnencryptedWrapper as l, setCertificationLevel as la, embedTiffCmyk as li, buildCalGray as ln, applyHeaderFooter as lr, getFieldValue as lt, RenderCache as m, saveIncrementalWithSignaturePreservation as ma, downscaleImage as mi, SARIF_SCHEMA_URI as mn, toRoman as mr, validateFieldValue as mt, buildBlackPointCompensationExtGState as n, addFieldLock as na, canDirectEmbed as ni, evaluateFunction as nn, MissingOnValueCheckError as nr, TrustStore as nt, buildSoftMaskGroupExtGState as o, MdpPermission as oa, webpToJpeg as oi, reconstructParagraphs as on, RichTextFieldReadError as or, checkCertificateStatus as ot, registerEmbeddedFile as p, parseExistingTrailer as pa, analyzeJpegMarkers as pi, DEFAULT_SARIF_TOOL_NAME as pn, toAlpha as pr, setFieldVisibility as pt, normalizeComponentDepth as q, buildXmpMetadata as qa, computeObjectHash as qi, EncryptedPdfError as qn, itfToOperators as qr, buildPdfX6OutputIntent as qt, buildColorKeyMask as r, buildFieldLockDict as ra, embedTiffDirect as ri, markdownToPdf as rn, NoSuchFieldError as rr, downloadCrl as rt, buildSoftMaskNone as s, buildDocMdpReference as sa, webpToPng as si, DEFAULT_DOC_TIMESTAMP_CONTENTS_SIZE as sn, StreamingParseError as sr, extractOcspUrl as st, initWasm as t, diffSignedContent as ta, PdfWorker as ti, nameHalftone as tn, saveIncremental as to, InvalidPageSizeError as tr, verifySignatureDetailed as tt, attachOutputIntents as u, validateSignatureChain as ua, isCmykTiff as ui, buildCalRGB as un, applyHeaderFooterToPage as ur, resolveFieldReference as ut, applyOcr as v, requestTimestamp as va, applyRedaction as vi, PDF2_NAMESPACE as vn, shrinkFontSize as vr, parseAcrobatDate as vt, renderDisplayListToCanvas as w, PdfInkAnnotation as wa, buildAfArray as wi, buildDPartRoot as wn, minimalPreset as wr, readWoffHeader as wt, extractFonts as x, PdfCaretAnnotation as xa, generatePdfAXmpBytes as xi, buildPieceInfo as xn, applyPreset as xr, decodeWoff as xt, compareImages as y, searchTextItems as ya, enforcePdfAFull as yi, buildNamespace as yn, truncateText as yr, AFNumber_Format as yt, layoutColumns as z, generateInkAppearance as za, detectTransparency as zi, getBookmarks as zn, encodeUpcA as zr, buildFunctionShading as zt };
27887
30465
 
27888
- //# sourceMappingURL=src-BOzRFaEE.mjs.map
30466
+ //# sourceMappingURL=src-s9zNsZAT.mjs.map