pdfnative 1.3.0 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -2,8 +2,13 @@
2
2
 
3
3
  var __defProp = Object.defineProperty;
4
4
  var __getOwnPropNames = Object.getOwnPropertyNames;
5
- var __esm = (fn2, res) => function __init() {
6
- return fn2 && (res = (0, fn2[__getOwnPropNames(fn2)[0]])(fn2 = 0)), res;
5
+ var __esm = (fn2, res, err) => function __init() {
6
+ if (err) throw err[0];
7
+ try {
8
+ return fn2 && (res = (0, fn2[__getOwnPropNames(fn2)[0]])(fn2 = 0)), res;
9
+ } catch (e) {
10
+ throw err = [e], e;
11
+ }
7
12
  };
8
13
  var __export = (target, all) => {
9
14
  for (var name in all)
@@ -5981,15 +5986,86 @@ function radialShadingDict(p, m) {
5981
5986
  const s = (sx + sy) / 2 || 1;
5982
5987
  return `<< /ShadingType 3 /ColorSpace /DeviceRGB /Coords [${n(x0)} ${n(y0)} ${n(p.r0 * s)} ${n(x1)} ${n(y1)} ${n(p.r1 * s)}] /Function ${buildGradientFunction(p.stops)} /Extend ${extendFlags(p.extend)} >>`;
5983
5988
  }
5989
+ function colorAtOffset(stops, t) {
5990
+ if (stops.length === 0) return [0, 0, 0, 255];
5991
+ const sorted = stops.slice().sort((a, b) => a.offset - b.offset);
5992
+ if (t <= sorted[0].offset) return sorted[0].color;
5993
+ const last = sorted[sorted.length - 1];
5994
+ if (t >= last.offset) return last.color;
5995
+ for (let i = 0; i < sorted.length - 1; i++) {
5996
+ const a = sorted[i], b = sorted[i + 1];
5997
+ if (t >= a.offset && t <= b.offset) {
5998
+ const span = b.offset - a.offset || 1;
5999
+ const f = (t - a.offset) / span;
6000
+ return [
6001
+ Math.round(a.color[0] + (b.color[0] - a.color[0]) * f),
6002
+ Math.round(a.color[1] + (b.color[1] - a.color[1]) * f),
6003
+ Math.round(a.color[2] + (b.color[2] - a.color[2]) * f),
6004
+ Math.round(a.color[3] + (b.color[3] - a.color[3]) * f)
6005
+ ];
6006
+ }
6007
+ }
6008
+ return last.color;
6009
+ }
6010
+ function emitSweep(p, cx, cy, maxR, body, gsFor, blendMode) {
6011
+ const start = p.startAngle;
6012
+ const end = p.endAngle;
6013
+ const span = end - start;
6014
+ if (Math.abs(span) < 0.01) {
6015
+ const c = colorAtOffset(p.stops, 0);
6016
+ const gs = gsFor(c[3] / 255, blendMode);
6017
+ if (gs) body.push(`/${gs} gs`);
6018
+ body.push(`${ch(c[0])} ${ch(c[1])} ${ch(c[2])} rg`);
6019
+ body.push(`${n(cx - maxR)} ${n(cy - maxR)} ${n(2 * maxR)} ${n(2 * maxR)} re`);
6020
+ body.push("f");
6021
+ return;
6022
+ }
6023
+ const steps = Math.max(12, Math.min(180, Math.ceil(Math.abs(span) / 3)));
6024
+ const r = maxR * 1.5;
6025
+ const rad = Math.PI / 180;
6026
+ for (let i = 0; i < steps; i++) {
6027
+ const a0 = start + span * i / steps;
6028
+ const a1 = start + span * (i + 1) / steps;
6029
+ const tMid = (i + 0.5) / steps;
6030
+ const c = colorAtOffset(p.stops, tMid);
6031
+ const gs = gsFor(c[3] / 255, blendMode);
6032
+ body.push("q");
6033
+ if (gs) body.push(`/${gs} gs`);
6034
+ body.push(`${ch(c[0])} ${ch(c[1])} ${ch(c[2])} rg`);
6035
+ const x0 = cx + r * Math.cos(a0 * rad), y0 = cy + r * Math.sin(a0 * rad);
6036
+ const x1 = cx + r * Math.cos(a1 * rad), y1 = cy + r * Math.sin(a1 * rad);
6037
+ body.push(`${n(cx)} ${n(cy)} m ${n(x0)} ${n(y0)} l ${n(x1)} ${n(y1)} l h`);
6038
+ body.push("f");
6039
+ body.push("Q");
6040
+ }
6041
+ }
5984
6042
  function renderColorGlyph(glyph, outlines, unitsPerEm) {
5985
6043
  const body = [];
5986
6044
  const shadings = [];
5987
6045
  const extGStates = [];
5988
- const alphaMap = /* @__PURE__ */ new Map();
6046
+ const gsMap = /* @__PURE__ */ new Map();
5989
6047
  let shadingIdx = 0;
6048
+ const gsFor = (alpha, bm) => {
6049
+ const a = Math.max(0, Math.min(1, alpha));
6050
+ const needAlpha = a < 0.999;
6051
+ const needBm = bm !== void 0 && bm !== "Normal";
6052
+ if (!needAlpha && !needBm) return "";
6053
+ const key = `${needAlpha ? a.toFixed(3) : "1"}|${needBm ? bm : ""}`;
6054
+ let name = gsMap.get(key);
6055
+ if (!name) {
6056
+ name = `Gs${gsMap.size}`;
6057
+ gsMap.set(key, name);
6058
+ const parts = [];
6059
+ if (needAlpha) parts.push(`/ca ${n(a)}`, `/CA ${n(a)}`);
6060
+ if (needBm) parts.push(`/BM /${bm}`);
6061
+ extGStates.push({ name, dict: `<< ${parts.join(" ")} >>` });
6062
+ }
6063
+ return name;
6064
+ };
5990
6065
  let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
5991
6066
  for (const layer of glyph.layers) {
5992
6067
  const m = layer.transform ?? ID;
6068
+ const bm = layer.blendMode;
5993
6069
  const contours = outlines(layer.glyphId);
5994
6070
  if (contours.length === 0) continue;
5995
6071
  const path = contoursToPath(contours, m);
@@ -6004,26 +6080,36 @@ function renderColorGlyph(glyph, outlines, unitsPerEm) {
6004
6080
  }
6005
6081
  if (layer.paint.kind === "solid") {
6006
6082
  const c = layer.paint.color;
6007
- const alpha = c[3] / 255;
6008
6083
  body.push("q");
6009
- if (alpha < 0.999) {
6010
- let gs = alphaMap.get(c[3]);
6011
- if (!gs) {
6012
- gs = `GsA${alphaMap.size}`;
6013
- alphaMap.set(c[3], gs);
6014
- extGStates.push({ name: gs, dict: `<< /ca ${n(alpha)} /CA ${n(alpha)} >>` });
6015
- }
6016
- body.push(`/${gs} gs`);
6017
- }
6084
+ const gs = gsFor(c[3] / 255, bm);
6085
+ if (gs) body.push(`/${gs} gs`);
6018
6086
  body.push(`${ch(c[0])} ${ch(c[1])} ${ch(c[2])} rg`);
6019
6087
  body.push(path);
6020
6088
  body.push("f");
6021
6089
  body.push("Q");
6090
+ } else if (layer.paint.kind === "sweep") {
6091
+ const [cx, cy] = tx(m, layer.paint.center[0], layer.paint.center[1]);
6092
+ let r2 = 0;
6093
+ for (const contour of contours) {
6094
+ for (const pt of contour) {
6095
+ const [px, py] = tx(m, pt.x, pt.y);
6096
+ const d = (px - cx) * (px - cx) + (py - cy) * (py - cy);
6097
+ if (d > r2) r2 = d;
6098
+ }
6099
+ }
6100
+ const maxR = Math.sqrt(r2) || 1;
6101
+ body.push("q");
6102
+ body.push(path);
6103
+ body.push("W n");
6104
+ emitSweep(layer.paint, cx, cy, maxR, body, gsFor, bm);
6105
+ body.push("Q");
6022
6106
  } else {
6023
6107
  const name = `Sh${shadingIdx++}`;
6024
6108
  const dict = layer.paint.kind === "linear" ? linearShadingDict(layer.paint, m) : radialShadingDict(layer.paint, m);
6025
6109
  shadings.push({ name, dict });
6026
6110
  body.push("q");
6111
+ const gs = gsFor(1, bm);
6112
+ if (gs) body.push(`/${gs} gs`);
6027
6113
  body.push(path);
6028
6114
  body.push("W n");
6029
6115
  body.push(`/${name} sh`);
@@ -8531,6 +8617,180 @@ function buildPDFBytes(params, layoutOptions) {
8531
8617
  return toBytes(buildPDF(params, layoutOptions));
8532
8618
  }
8533
8619
 
8620
+ // src/core/pdf-outline.ts
8621
+ function buildOutlineObjects(items, startObjNum, pageObjNumFor, defaultY, fmtNum3, pageCount) {
8622
+ const rootObjNum = startObjNum;
8623
+ const nodes = [];
8624
+ let nextObj = startObjNum + 1;
8625
+ function alloc(list, parentObjNum, depth) {
8626
+ let first = 0;
8627
+ let last = 0;
8628
+ let prev = 0;
8629
+ let visibleCount = 0;
8630
+ const siblingNodes = [];
8631
+ for (const item of list) {
8632
+ const objNum = nextObj++;
8633
+ const open = item.open !== false;
8634
+ const node = {
8635
+ item,
8636
+ objNum,
8637
+ parentObjNum,
8638
+ depth,
8639
+ firstChildObjNum: 0,
8640
+ lastChildObjNum: 0,
8641
+ prevObjNum: prev,
8642
+ nextObjNum: 0,
8643
+ openDescendantCount: 0,
8644
+ open
8645
+ };
8646
+ if (prev !== 0) {
8647
+ const prevNode = siblingNodes[siblingNodes.length - 1];
8648
+ prevNode.nextObjNum = objNum;
8649
+ }
8650
+ nodes.push(node);
8651
+ siblingNodes.push(node);
8652
+ if (first === 0) first = objNum;
8653
+ last = objNum;
8654
+ prev = objNum;
8655
+ visibleCount++;
8656
+ const children = item.children;
8657
+ if (children && children.length > 0) {
8658
+ const sub = alloc(children, objNum, depth + 1);
8659
+ node.firstChildObjNum = sub.first;
8660
+ node.lastChildObjNum = sub.last;
8661
+ node.openDescendantCount = sub.visibleCount;
8662
+ if (open) visibleCount += sub.visibleCount;
8663
+ }
8664
+ }
8665
+ return { first, last, visibleCount };
8666
+ }
8667
+ const top = alloc(items, rootObjNum, 0);
8668
+ const objects = [];
8669
+ objects.push([
8670
+ rootObjNum,
8671
+ `<< /Type /Outlines /First ${top.first} 0 R /Last ${top.last} 0 R /Count ${top.visibleCount} >>`
8672
+ ]);
8673
+ for (const node of nodes) {
8674
+ const it = node.item;
8675
+ const pageIdx = Math.max(0, Math.min(pageCount - 1, it.pageIndex | 0));
8676
+ const pageObj = pageObjNumFor(pageIdx);
8677
+ const y = it.y ?? defaultY;
8678
+ const parts = [
8679
+ `/Title ${encodePdfTextString(it.title)}`,
8680
+ `/Parent ${node.parentObjNum} 0 R`
8681
+ ];
8682
+ if (node.prevObjNum !== 0) parts.push(`/Prev ${node.prevObjNum} 0 R`);
8683
+ if (node.nextObjNum !== 0) parts.push(`/Next ${node.nextObjNum} 0 R`);
8684
+ if (node.firstChildObjNum !== 0) {
8685
+ parts.push(`/First ${node.firstChildObjNum} 0 R`);
8686
+ parts.push(`/Last ${node.lastChildObjNum} 0 R`);
8687
+ const count = node.open ? node.openDescendantCount : -node.openDescendantCount;
8688
+ parts.push(`/Count ${count}`);
8689
+ }
8690
+ parts.push(`/Dest [${pageObj} 0 R /XYZ 0 ${fmtNum3(y)} null]`);
8691
+ const flags = (it.bold ? 2 : 0) | (it.italic ? 1 : 0);
8692
+ if (flags !== 0) parts.push(`/F ${flags}`);
8693
+ if (it.color) parts.push(`/C [${it.color}]`);
8694
+ objects.push([node.objNum, `<< ${parts.join(" ")} >>`]);
8695
+ }
8696
+ return { objects, rootObjNum, totalObjects: nodes.length + 1 };
8697
+ }
8698
+
8699
+ // src/core/pdf-page-labels.ts
8700
+ var STYLE_OP = {
8701
+ decimal: "D",
8702
+ roman: "r",
8703
+ Roman: "R",
8704
+ alpha: "a",
8705
+ Alpha: "A"
8706
+ };
8707
+ function escapePdfLiteral(s) {
8708
+ return s.replace(/[\\()]/g, (c) => "\\" + c);
8709
+ }
8710
+ function buildPageLabelsDict(ranges, pageCount) {
8711
+ if (ranges.length === 0) {
8712
+ throw new Error("pageLabels must contain at least one range");
8713
+ }
8714
+ const sorted = [...ranges].sort((a, b) => a.startPage - b.startPage);
8715
+ let prev = -1;
8716
+ const nums = [];
8717
+ for (const r of sorted) {
8718
+ const idx = r.startPage | 0;
8719
+ if (idx < 0 || idx >= pageCount) {
8720
+ throw new Error(`pageLabels range startPage ${idx} out of bounds (0-${pageCount - 1})`);
8721
+ }
8722
+ if (idx <= prev) {
8723
+ throw new Error(`pageLabels ranges must have strictly increasing, unique startPage values (got ${idx} after ${prev})`);
8724
+ }
8725
+ prev = idx;
8726
+ const entryParts = [];
8727
+ const style = r.style ?? "decimal";
8728
+ if (style !== "none") {
8729
+ entryParts.push(`/S /${STYLE_OP[style]}`);
8730
+ }
8731
+ if (r.prefix !== void 0 && r.prefix !== "") {
8732
+ entryParts.push(`/P (${escapePdfLiteral(r.prefix)})`);
8733
+ }
8734
+ if (r.start !== void 0 && r.start !== 1) {
8735
+ if (!Number.isInteger(r.start) || r.start < 1) {
8736
+ throw new Error(`pageLabels range start must be a positive integer (got ${r.start})`);
8737
+ }
8738
+ entryParts.push(`/St ${r.start}`);
8739
+ }
8740
+ nums.push(`${idx} << ${entryParts.join(" ")} >>`);
8741
+ }
8742
+ return `<< /Nums [${nums.join(" ")}] >>`;
8743
+ }
8744
+
8745
+ // src/core/pdf-viewer-prefs.ts
8746
+ var PAGE_LAYOUT = {
8747
+ singlePage: "SinglePage",
8748
+ oneColumn: "OneColumn",
8749
+ twoColumnLeft: "TwoColumnLeft",
8750
+ twoColumnRight: "TwoColumnRight",
8751
+ twoPageLeft: "TwoPageLeft",
8752
+ twoPageRight: "TwoPageRight"
8753
+ };
8754
+ var PAGE_MODE = {
8755
+ useNone: "UseNone",
8756
+ useOutlines: "UseOutlines",
8757
+ useThumbs: "UseThumbs",
8758
+ fullScreen: "FullScreen",
8759
+ useOC: "UseOC",
8760
+ useAttachments: "UseAttachments"
8761
+ };
8762
+ var NON_FS_PAGE_MODE = {
8763
+ useNone: "UseNone",
8764
+ useOutlines: "UseOutlines",
8765
+ useThumbs: "UseThumbs",
8766
+ useOC: "UseOC"
8767
+ };
8768
+ function buildViewerPreferences(prefs) {
8769
+ const pageLayout = prefs.pageLayout ? PAGE_LAYOUT[prefs.pageLayout] : void 0;
8770
+ const pageMode = prefs.pageMode ? PAGE_MODE[prefs.pageMode] : void 0;
8771
+ const entries = [];
8772
+ const bool = (key, v) => {
8773
+ if (v !== void 0) entries.push(`/${key} ${v ? "true" : "false"}`);
8774
+ };
8775
+ bool("HideToolbar", prefs.hideToolbar);
8776
+ bool("HideMenubar", prefs.hideMenubar);
8777
+ bool("HideWindowUI", prefs.hideWindowUI);
8778
+ bool("FitWindow", prefs.fitWindow);
8779
+ bool("CenterWindow", prefs.centerWindow);
8780
+ bool("DisplayDocTitle", prefs.displayDocTitle);
8781
+ if (prefs.nonFullScreenPageMode) {
8782
+ entries.push(`/NonFullScreenPageMode /${NON_FS_PAGE_MODE[prefs.nonFullScreenPageMode]}`);
8783
+ }
8784
+ if (prefs.direction) {
8785
+ entries.push(`/Direction /${prefs.direction === "r2l" ? "R2L" : "L2R"}`);
8786
+ }
8787
+ if (prefs.printScaling) {
8788
+ entries.push(`/PrintScaling /${prefs.printScaling === "none" ? "None" : "AppDefault"}`);
8789
+ }
8790
+ const dict = entries.length > 0 ? ` /ViewerPreferences << ${entries.join(" ")} >>` : "";
8791
+ return { pageLayout, pageMode, dict };
8792
+ }
8793
+
8534
8794
  // src/core/pdf-document.ts
8535
8795
  init_pdf_encrypt();
8536
8796
 
@@ -11022,23 +11282,36 @@ function renderList(block, y, enc, mgL, cw, tagCtx, documentChildren) {
11022
11282
  const sz = block.fontSize ?? DEFAULT_LIST_SIZE;
11023
11283
  const lineH = sz * DEFAULT_LINE_HEIGHT;
11024
11284
  const color = "0.216 0.255 0.318";
11025
- const availW = cw - LIST_INDENT - BULLET_MARK_WIDTH;
11026
11285
  ops.push(`${color} rg`);
11027
- const listChildren = [];
11028
- for (let idx = 0; idx < block.items.length; idx++) {
11029
- const item = block.items[idx];
11030
- const marker = block.style === "bullet" ? "\u2022" : `${idx + 1}.`;
11031
- const lines = wrapText(item, availW, sz, enc);
11286
+ const root = renderListLevel(block.items, block.style, 0, y, sz, lineH, enc, mgL, cw, tagCtx);
11287
+ ops.push(...root.ops);
11288
+ if (tagCtx?.tagged && root.struct && root.struct.children.length > 0) {
11289
+ documentChildren.push(root.struct);
11290
+ }
11291
+ return { ops, y: root.y };
11292
+ }
11293
+ function renderListLevel(items, style, depth, y, sz, lineH, enc, mgL, cw, tagCtx) {
11294
+ const ops = [];
11295
+ const indent = LIST_INDENT * (depth + 1);
11296
+ const markerX = mgL + indent;
11297
+ const xOffset = markerX + BULLET_MARK_WIDTH;
11298
+ const availW = cw - indent - BULLET_MARK_WIDTH;
11299
+ const levelChildren = [];
11300
+ for (let idx = 0; idx < items.length; idx++) {
11301
+ const entry = items[idx];
11302
+ const text = typeof entry === "string" ? entry : entry.text;
11303
+ const children = typeof entry === "string" ? void 0 : entry.items;
11304
+ const marker = style === "bullet" ? "\u2022" : `${idx + 1}.`;
11305
+ const lines = wrapText(text, availW, sz, enc);
11032
11306
  const liChildren = [];
11033
11307
  if (tagCtx?.tagged) {
11034
11308
  const mcid = tagCtx.mcidAlloc.next(tagCtx.pageObjNum);
11035
11309
  liChildren.push({ mcid, pageObjNum: tagCtx.pageObjNum });
11036
- ops.push(txtTagged(marker, mgL + LIST_INDENT, y - sz, enc.f1, sz, enc, mcid));
11310
+ ops.push(txtTagged(marker, markerX, y - sz, enc.f1, sz, enc, mcid));
11037
11311
  } else {
11038
- ops.push(txt(marker, mgL + LIST_INDENT, y - sz, enc.f1, sz, enc));
11312
+ ops.push(txt(marker, markerX, y - sz, enc.f1, sz, enc));
11039
11313
  }
11040
11314
  for (let li = 0; li < lines.length; li++) {
11041
- const xOffset = mgL + LIST_INDENT + BULLET_MARK_WIDTH;
11042
11315
  if (li === 0) {
11043
11316
  if (tagCtx?.tagged) {
11044
11317
  const mcid = tagCtx.mcidAlloc.next(tagCtx.pageObjNum);
@@ -11059,14 +11332,20 @@ function renderList(block, y, enc, mgL, cw, tagCtx, documentChildren) {
11059
11332
  }
11060
11333
  }
11061
11334
  y -= lineH + LIST_ITEM_SPACING;
11335
+ if (children && children.length > 0) {
11336
+ const sub = renderListLevel(children, style, depth + 1, y, sz, lineH, enc, mgL, cw, tagCtx);
11337
+ ops.push(...sub.ops);
11338
+ y = sub.y;
11339
+ if (tagCtx?.tagged && sub.struct && sub.struct.children.length > 0) {
11340
+ liChildren.push(sub.struct);
11341
+ }
11342
+ }
11062
11343
  if (tagCtx?.tagged && liChildren.length > 0) {
11063
- listChildren.push({ type: "LI", children: liChildren });
11344
+ levelChildren.push({ type: "LI", children: liChildren });
11064
11345
  }
11065
11346
  }
11066
- if (tagCtx?.tagged && listChildren.length > 0) {
11067
- documentChildren.push({ type: "L", children: listChildren });
11068
- }
11069
- return { ops, y };
11347
+ const struct = tagCtx?.tagged ? { type: "L", children: levelChildren } : void 0;
11348
+ return { ops, y, struct };
11070
11349
  }
11071
11350
  var DEFAULT_ZEBRA_COLOR = "0.969 0.973 0.984";
11072
11351
  var CAPTION_FONT_SIZE = 9;
@@ -11154,6 +11433,30 @@ function renderTable(block, y, enc, mgL, mgR, pgW, cw, tagCtx, documentChildren,
11154
11433
  const clip = block.clipCells !== false;
11155
11434
  const zebraColor = resolveZebraColor(block.zebra);
11156
11435
  const wrapMode = block.wrap ?? "auto";
11436
+ const borders = block.cellBorders;
11437
+ const borderSides = borders ? {
11438
+ top: borders.all === true || borders.top === true,
11439
+ right: borders.all === true || borders.right === true,
11440
+ bottom: borders.all === true || borders.bottom === true,
11441
+ left: borders.all === true || borders.left === true
11442
+ } : null;
11443
+ const borderColor = borders?.color ? parseColor(borders.color) : "0.8 0.8 0.8";
11444
+ const borderWidth = borders?.width ?? 0.5;
11445
+ const borderDash = borders?.style === "dashed" ? "[3] 0 d" : borders?.style === "dotted" ? `[${fmtNum(borderWidth)} ${fmtNum(borderWidth * 2)}] 0 d` : null;
11446
+ const cellBorderOps = (cellX, cellW, top, h) => {
11447
+ if (!borderSides || !borderSides.top && !borderSides.right && !borderSides.bottom && !borderSides.left) {
11448
+ return [];
11449
+ }
11450
+ const o = [];
11451
+ o.push(`${fmtNum(borderWidth)} w ${borderColor} RG${borderDash ? " " + borderDash : ""}`);
11452
+ const x0 = cellX, x1 = cellX + cellW, y0 = top - h, y1 = top;
11453
+ if (borderSides.top) o.push(`${fmtNum(x0)} ${fmtNum(y1)} m ${fmtNum(x1)} ${fmtNum(y1)} l S`);
11454
+ if (borderSides.bottom) o.push(`${fmtNum(x0)} ${fmtNum(y0)} m ${fmtNum(x1)} ${fmtNum(y0)} l S`);
11455
+ if (borderSides.left) o.push(`${fmtNum(x0)} ${fmtNum(y0)} m ${fmtNum(x0)} ${fmtNum(y1)} l S`);
11456
+ if (borderSides.right) o.push(`${fmtNum(x1)} ${fmtNum(y0)} m ${fmtNum(x1)} ${fmtNum(y1)} l S`);
11457
+ if (borderDash) o.push("[] 0 d");
11458
+ return o;
11459
+ };
11157
11460
  const clipCell = (op, i, top, h) => clip ? `q ${fmtNum(cx[i])} ${fmtNum(top - h)} ${fmtNum(cwi[i])} ${fmtNum(h)} re W n
11158
11461
  ${op}
11159
11462
  Q` : op;
@@ -11162,9 +11465,21 @@ Q` : op;
11162
11465
  const out = [];
11163
11466
  const lineH = sz * TABLE_LINE_HEIGHT;
11164
11467
  const padBottom = isHeader ? HEADER_PAD_BOTTOM : CELL_PAD_BOTTOM;
11468
+ const vAlign = col.vAlign ?? block.cellVAlign;
11165
11469
  for (let li = 0; li < lines.length; li++) {
11166
11470
  const t = lines.length === 1 && wrapMode === "never" ? truncate(lines[li], isHeader && col.mxH !== void 0 ? col.mxH : col.mx) : lines[li];
11167
- const baselineY = lines.length === 1 ? rowTop - rowH + padBottom : rowTop - pad - sz + sz * 0.2 - li * lineH;
11471
+ let baselineY;
11472
+ if (vAlign) {
11473
+ const blockH = lines.length * lineH;
11474
+ let offset;
11475
+ if (vAlign === "top") offset = pad;
11476
+ else if (vAlign === "bottom") offset = rowH - blockH - pad;
11477
+ else offset = (rowH - blockH) / 2;
11478
+ if (offset < pad) offset = pad;
11479
+ baselineY = rowTop - offset - li * lineH - sz + sz * 0.2;
11480
+ } else {
11481
+ baselineY = lines.length === 1 ? rowTop - rowH + padBottom : rowTop - pad - sz + sz * 0.2 - li * lineH;
11482
+ }
11168
11483
  let op;
11169
11484
  if (mcRefsOut !== null && tagCtx?.tagged) {
11170
11485
  const mcid = tagCtx.mcidAlloc.next(tagCtx.pageObjNum);
@@ -11219,6 +11534,7 @@ Q` : op;
11219
11534
  for (let i = 0; i < block.headers.length && i < columns.length; i++) {
11220
11535
  const cellRefs = tagCtx?.tagged ? [] : null;
11221
11536
  ops.push(...emitCell(headerLines[i] ?? [""], i, y, headerHeight, enc.f2, fs.th, cellRefs, true));
11537
+ ops.push(...cellBorderOps(cx[i], cwi[i], y, headerHeight));
11222
11538
  if (cellRefs && cellRefs.length > 0) {
11223
11539
  thChildren.push({ type: "TH", children: cellRefs });
11224
11540
  }
@@ -11246,6 +11562,7 @@ Q` : op;
11246
11562
  ops.push(`${color} rg`);
11247
11563
  const cellRefs = tagCtx?.tagged ? [] : null;
11248
11564
  ops.push(...emitCell(cells[i] ?? [""], i, y, rowH, font, fs.td, cellRefs, false));
11565
+ ops.push(...cellBorderOps(cx[i], cwi[i], y, rowH));
11249
11566
  if (cellRefs && cellRefs.length > 0) {
11250
11567
  tdChildren.push({ type: "TD", children: cellRefs });
11251
11568
  }
@@ -11617,13 +11934,20 @@ function estimateBlockHeight(block, enc, cw, headings) {
11617
11934
  case "list": {
11618
11935
  const sz = block.fontSize ?? DEFAULT_LIST_SIZE;
11619
11936
  const lineH = sz * DEFAULT_LINE_HEIGHT;
11620
- const availW = cw - LIST_INDENT - BULLET_MARK_WIDTH;
11621
- let h = 0;
11622
- for (const item of block.items) {
11623
- const lines = wrapText(item, availW, sz, enc);
11624
- h += lineH + (lines.length - 1) * lineH + LIST_ITEM_SPACING;
11625
- }
11626
- return h;
11937
+ const measureLevel = (items, depth) => {
11938
+ const availW = cw - LIST_INDENT * (depth + 1) - BULLET_MARK_WIDTH;
11939
+ let acc = 0;
11940
+ for (const entry of items) {
11941
+ const text = typeof entry === "string" ? entry : entry.text;
11942
+ const lines = wrapText(text, availW, sz, enc);
11943
+ acc += lineH + (lines.length - 1) * lineH + LIST_ITEM_SPACING;
11944
+ if (typeof entry !== "string" && entry.items && entry.items.length > 0) {
11945
+ acc += measureLevel(entry.items, depth + 1);
11946
+ }
11947
+ }
11948
+ return acc;
11949
+ };
11950
+ return measureLevel(block.items, 0);
11627
11951
  }
11628
11952
  case "table": {
11629
11953
  return TH_H + block.rows.length * ROW_H + 6;
@@ -12481,6 +12805,41 @@ function assembleDocumentParts(params, layoutOptions) {
12481
12805
  totalObjs += efResult.totalObjects;
12482
12806
  }
12483
12807
  }
12808
+ let pageLabelsStr = "";
12809
+ if (params.pageLabels && params.pageLabels.length > 0) {
12810
+ pageLabelsStr = ` /PageLabels ${buildPageLabelsDict(params.pageLabels, totalPages)}`;
12811
+ }
12812
+ const viewerPrefs = layout?.viewerPreferences ? buildViewerPreferences(layout.viewerPreferences) : void 0;
12813
+ let viewerPrefsStr = viewerPrefs?.dict ?? "";
12814
+ if (viewerPrefs?.pageLayout) {
12815
+ viewerPrefsStr = ` /PageLayout /${viewerPrefs.pageLayout}${viewerPrefsStr}`;
12816
+ }
12817
+ let outlineCatalogStr = "";
12818
+ if (params.outline) {
12819
+ const items = params.outline === "auto" ? autoOutlineFromHeadings(headingDests) : params.outline.map(mapOutlineItem);
12820
+ if (items.length > 0) {
12821
+ const outlineStart = totalObjs + 1;
12822
+ const built = buildOutlineObjects(
12823
+ items,
12824
+ outlineStart,
12825
+ (pageIndex) => pageObjStart + pageIndex * 2,
12826
+ pgH - mg.t,
12827
+ fmtNum,
12828
+ totalPages
12829
+ );
12830
+ for (const [objNum, content] of built.objects) {
12831
+ emitObj(objNum, content);
12832
+ }
12833
+ totalObjs = outlineStart + built.totalObjects - 1;
12834
+ outlineCatalogStr = ` /Outlines ${built.rootObjNum} 0 R`;
12835
+ if (!viewerPrefs?.pageMode) {
12836
+ outlineCatalogStr += " /PageMode /UseOutlines";
12837
+ }
12838
+ }
12839
+ }
12840
+ if (viewerPrefs?.pageMode) {
12841
+ viewerPrefsStr = ` /PageMode /${viewerPrefs.pageMode}${viewerPrefsStr}`;
12842
+ }
12484
12843
  let destsStr = "";
12485
12844
  if (hasToc && headingDests.length > 0) {
12486
12845
  const destEntries = headingDests.map((h) => {
@@ -12510,7 +12869,7 @@ function assembleDocumentParts(params, layoutOptions) {
12510
12869
  acroFormStr = ` ${buildAcroFormDict(fieldObjNums, formFontObjNum)}`;
12511
12870
  }
12512
12871
  if (tagged) {
12513
- let catalogContent = `<< /Type /Catalog /Pages 2 0 R /MarkInfo << /Marked true >> /StructTreeRoot ${structTreeRootObjNum} 0 R /Metadata ${xmpObjNum} 0 R /OutputIntents [${outputIntentObjNum} 0 R]${destsStr}${acroFormStr}`;
12872
+ let catalogContent = `<< /Type /Catalog /Pages 2 0 R /MarkInfo << /Marked true >> /StructTreeRoot ${structTreeRootObjNum} 0 R /Metadata ${xmpObjNum} 0 R /OutputIntents [${outputIntentObjNum} 0 R]${destsStr}${acroFormStr}${outlineCatalogStr}${pageLabelsStr}${viewerPrefsStr}`;
12514
12873
  if (afArrayStr) {
12515
12874
  catalogContent += ` /AF [${afArrayStr}] ${embeddedFilesNamesDict}`;
12516
12875
  }
@@ -12532,8 +12891,8 @@ endobj
12532
12891
  }
12533
12892
  }
12534
12893
  }
12535
- } else if (destsStr || acroFormStr) {
12536
- const catalogContent = `<< /Type /Catalog /Pages 2 0 R${destsStr}${acroFormStr} >>`;
12894
+ } else if (destsStr || acroFormStr || outlineCatalogStr || pageLabelsStr || viewerPrefsStr) {
12895
+ const catalogContent = `<< /Type /Catalog /Pages 2 0 R${destsStr}${acroFormStr}${outlineCatalogStr}${pageLabelsStr}${viewerPrefsStr} >>`;
12537
12896
  const oldCatalog = "1 0 obj\n<< /Type /Catalog /Pages 2 0 R >>\nendobj\n\n";
12538
12897
  const newCatalog = `1 0 obj
12539
12898
  ${catalogContent}
@@ -12559,6 +12918,40 @@ endobj
12559
12918
  function buildDocumentPDFBytes(params, layoutOptions) {
12560
12919
  return toBytes(buildDocumentPDF(params, layoutOptions));
12561
12920
  }
12921
+ function mapOutlineItem(item) {
12922
+ return {
12923
+ title: item.title,
12924
+ pageIndex: item.pageIndex,
12925
+ y: item.y,
12926
+ bold: item.bold,
12927
+ italic: item.italic,
12928
+ open: item.open,
12929
+ color: item.color !== void 0 ? parseColor(item.color) : void 0,
12930
+ children: item.children ? item.children.map(mapOutlineItem) : void 0
12931
+ };
12932
+ }
12933
+ function autoOutlineFromHeadings(headings) {
12934
+ const root = [];
12935
+ const stack = [];
12936
+ for (const h of headings) {
12937
+ const node = {
12938
+ title: h.text,
12939
+ pageIndex: h.pageIndex,
12940
+ y: h.y,
12941
+ children: []
12942
+ };
12943
+ while (stack.length > 0 && stack[stack.length - 1].level >= h.level) {
12944
+ stack.pop();
12945
+ }
12946
+ if (stack.length === 0) {
12947
+ root.push(node);
12948
+ } else {
12949
+ stack[stack.length - 1].children.push(node);
12950
+ }
12951
+ stack.push({ level: h.level, children: node.children });
12952
+ }
12953
+ return root;
12954
+ }
12562
12955
 
12563
12956
  // src/core/pdf-signature.ts
12564
12957
  init_sha();
@@ -12568,6 +12961,17 @@ init_asn1();
12568
12961
  init_sha();
12569
12962
  init_rsa();
12570
12963
  init_ecdsa();
12964
+
12965
+ // src/crypto/crypto-provider.ts
12966
+ var _cryptoProvider = null;
12967
+ function setCryptoProvider(provider) {
12968
+ _cryptoProvider = provider;
12969
+ }
12970
+ function getCryptoProvider() {
12971
+ return _cryptoProvider;
12972
+ }
12973
+
12974
+ // src/crypto/cms.ts
12571
12975
  var OID_SIGNED_DATA = new Uint8Array([42, 134, 72, 134, 247, 13, 1, 7, 2]);
12572
12976
  var OID_DATA = new Uint8Array([42, 134, 72, 134, 247, 13, 1, 7, 1]);
12573
12977
  var OID_SHA256 = new Uint8Array([96, 134, 72, 1, 101, 3, 4, 2, 1]);
@@ -12627,18 +13031,26 @@ function buildSignerInfo(options) {
12627
13031
  const signedAttrsContent = concatUint8Arrays(attrContentType, attrMessageDigest, attrSigningTime);
12628
13032
  const signedAttrsImplicit = derWrap(160, signedAttrsContent);
12629
13033
  const signedAttrsForSig = derSet(attrContentType, attrMessageDigest, attrSigningTime);
12630
- const attrsHash = sha256(signedAttrsForSig);
13034
+ const provider = options.provider ?? getCryptoProvider();
12631
13035
  let sigAlgId;
12632
13036
  let signatureValue;
12633
13037
  if (algorithm === "rsa-sha256") {
12634
- if (!options.rsaKey) throw new Error("RSA private key required for rsa-sha256");
12635
13038
  sigAlgId = derSequence(derOid(OID_SHA256_RSA), derNull());
12636
- signatureValue = rsaSignHash(attrsHash, options.rsaKey);
13039
+ if (provider) {
13040
+ signatureValue = provider.sign(signedAttrsForSig, algorithm);
13041
+ } else {
13042
+ if (!options.rsaKey) throw new Error("RSA private key (or a crypto provider) required for rsa-sha256");
13043
+ signatureValue = rsaSignHash(sha256(signedAttrsForSig), options.rsaKey);
13044
+ }
12637
13045
  } else if (algorithm === "ecdsa-sha256") {
12638
- if (!options.ecKey) throw new Error("ECDSA private key required for ecdsa-sha256");
12639
13046
  sigAlgId = derSequence(derOid(OID_ECDSA_SHA256));
12640
- const { r, s } = ecdsaSignHash(attrsHash, options.ecKey);
12641
- signatureValue = encodeDerSignature(r, s);
13047
+ if (provider) {
13048
+ signatureValue = provider.sign(signedAttrsForSig, algorithm);
13049
+ } else {
13050
+ if (!options.ecKey) throw new Error("ECDSA private key (or a crypto provider) required for ecdsa-sha256");
13051
+ const { r, s } = ecdsaSignHash(sha256(signedAttrsForSig), options.ecKey);
13052
+ signatureValue = encodeDerSignature(r, s);
13053
+ }
12642
13054
  } else {
12643
13055
  throw new Error(`Unsupported algorithm: ${algorithm}`);
12644
13056
  }
@@ -12717,7 +13129,8 @@ function signPdfBytes(pdfBytes, options) {
12717
13129
  rsaKey: options.rsaKey,
12718
13130
  ecKey: options.ecKey,
12719
13131
  algorithm,
12720
- signingTime: options.signingTime
13132
+ signingTime: options.signingTime,
13133
+ provider: options.provider
12721
13134
  };
12722
13135
  const cms = buildCmsSignedData(cmsOptions);
12723
13136
  if (cms.length * 2 > hexLen) {
@@ -14534,6 +14947,68 @@ async function streamByteLength(stream) {
14534
14947
  }
14535
14948
  return total;
14536
14949
  }
14950
+ async function streamToFile(stream, filePath, opts) {
14951
+ let fs;
14952
+ try {
14953
+ fs = await import('fs');
14954
+ } catch {
14955
+ throw new Error("streamToFile requires a Node.js environment (node:fs is unavailable)");
14956
+ }
14957
+ const signal = opts?.signal;
14958
+ if (signal?.aborted) throw new Error("streamToFile aborted before start");
14959
+ const ws = fs.createWriteStream(filePath);
14960
+ let bytesWritten = 0;
14961
+ const onAbort = () => {
14962
+ ws.destroy(new Error("streamToFile aborted"));
14963
+ };
14964
+ signal?.addEventListener("abort", onAbort, { once: true });
14965
+ try {
14966
+ await new Promise((resolve, reject) => {
14967
+ ws.on("error", reject);
14968
+ ws.on("open", () => resolve());
14969
+ });
14970
+ for await (const chunk of stream) {
14971
+ if (signal?.aborted) throw new Error("streamToFile aborted");
14972
+ bytesWritten += chunk.length;
14973
+ const ok = ws.write(chunk);
14974
+ if (!ok) {
14975
+ await new Promise((resolve, reject) => {
14976
+ const onErr = (e) => {
14977
+ ws.off("drain", onDrain);
14978
+ reject(e);
14979
+ };
14980
+ const onDrain = () => {
14981
+ ws.off("error", onErr);
14982
+ resolve();
14983
+ };
14984
+ ws.once("drain", onDrain);
14985
+ ws.once("error", onErr);
14986
+ });
14987
+ }
14988
+ }
14989
+ if (signal?.aborted) throw new Error("streamToFile aborted");
14990
+ await new Promise((resolve, reject) => {
14991
+ ws.end((err) => err ? reject(err) : resolve());
14992
+ });
14993
+ } catch (err) {
14994
+ await new Promise((resolve) => {
14995
+ if (ws.closed) {
14996
+ resolve();
14997
+ return;
14998
+ }
14999
+ ws.once("close", () => resolve());
15000
+ ws.destroy();
15001
+ });
15002
+ try {
15003
+ fs.rmSync(filePath, { force: true });
15004
+ } catch {
15005
+ }
15006
+ throw err;
15007
+ } finally {
15008
+ signal?.removeEventListener("abort", onAbort);
15009
+ }
15010
+ return { bytesWritten, path: filePath };
15011
+ }
14537
15012
  var DEFAULT_CHUNK_SIZE = 65536;
14538
15013
  var MIN_CHUNK_SIZE = 1024;
14539
15014
  var MAX_CHUNK_SIZE = 16777216;
@@ -14749,6 +15224,123 @@ async function initCrypto() {
14749
15224
  initEcdsaAsn12(asn1);
14750
15225
  }
14751
15226
 
15227
+ // src/fonts/font-validator.ts
15228
+ var SFNT_MAGIC = /* @__PURE__ */ new Set([
15229
+ 65536,
15230
+ // TrueType outlines
15231
+ 1330926671,
15232
+ // 'OTTO' — CFF/OpenType outlines
15233
+ 1953658213,
15234
+ // 'true' — legacy Apple TrueType
15235
+ 1953784678
15236
+ // 'ttcf' — TrueType Collection
15237
+ ]);
15238
+ function decodeBase64Prefix(b64, maxBytes) {
15239
+ if (!/^[A-Za-z0-9+/]*={0,2}$/.test(b64)) return null;
15240
+ if (b64.length % 4 !== 0) return null;
15241
+ const g = globalThis;
15242
+ try {
15243
+ const atobFn = g["atob"];
15244
+ if (typeof atobFn === "function") {
15245
+ const need = Math.ceil(maxBytes / 3 * 4 / 4) * 4;
15246
+ const slice = b64.slice(0, Math.min(b64.length, need));
15247
+ const bin = atobFn(slice);
15248
+ const out = new Uint8Array(Math.min(bin.length, maxBytes));
15249
+ for (let i = 0; i < out.length; i++) out[i] = bin.charCodeAt(i) & 255;
15250
+ return out;
15251
+ }
15252
+ const bufCtor = g["Buffer"];
15253
+ if (bufCtor?.from) {
15254
+ return bufCtor.from(b64, "base64").subarray(0, maxBytes);
15255
+ }
15256
+ } catch {
15257
+ return null;
15258
+ }
15259
+ return null;
15260
+ }
15261
+ function validateFontData(data) {
15262
+ const errors = [];
15263
+ const warnings = [];
15264
+ if (data === null || typeof data !== "object") {
15265
+ return { valid: false, errors: ["font data must be a non-null object"], warnings };
15266
+ }
15267
+ const f = data;
15268
+ const m = f.metrics;
15269
+ if (!m || typeof m !== "object") {
15270
+ errors.push("missing or invalid `metrics` object");
15271
+ } else {
15272
+ const finiteFields = ["unitsPerEm", "numGlyphs", "ascent", "descent", "capHeight", "stemV"];
15273
+ const mRec = m;
15274
+ for (const k of finiteFields) {
15275
+ const v = mRec[k];
15276
+ if (typeof v !== "number" || !Number.isFinite(v)) {
15277
+ errors.push(`metrics.${String(k)} must be a finite number`);
15278
+ }
15279
+ }
15280
+ if (typeof m.unitsPerEm === "number" && m.unitsPerEm <= 0) {
15281
+ errors.push("metrics.unitsPerEm must be positive");
15282
+ }
15283
+ if (!Array.isArray(m.bbox) || m.bbox.length !== 4 || !m.bbox.every((n2) => typeof n2 === "number" && Number.isFinite(n2))) {
15284
+ errors.push("metrics.bbox must be a 4-number array [xMin yMin xMax yMax]");
15285
+ }
15286
+ }
15287
+ if (typeof f.fontName !== "string" || f.fontName.length === 0) {
15288
+ errors.push("`fontName` must be a non-empty string");
15289
+ }
15290
+ const cmap = f.cmap;
15291
+ let cmapEntries = 0;
15292
+ if (!cmap || typeof cmap !== "object") {
15293
+ errors.push("missing or invalid `cmap` (codepoint \u2192 glyph id map)");
15294
+ } else {
15295
+ cmapEntries = Object.keys(cmap).length;
15296
+ if (cmapEntries === 0) {
15297
+ errors.push("`cmap` is empty \u2014 the font maps no characters");
15298
+ }
15299
+ }
15300
+ const widths = f.widths;
15301
+ if (!widths || typeof widths !== "object") {
15302
+ errors.push("missing or invalid `widths` (glyph id \u2192 advance map)");
15303
+ }
15304
+ if (cmap && typeof cmap === "object" && widths && typeof widths === "object") {
15305
+ const numGlyphs = m && typeof m.numGlyphs === "number" ? m.numGlyphs : Infinity;
15306
+ let missingWidth = 0;
15307
+ let outOfRange = 0;
15308
+ for (const gid of Object.values(cmap)) {
15309
+ if (typeof gid !== "number" || !Number.isInteger(gid) || gid < 0) {
15310
+ outOfRange++;
15311
+ continue;
15312
+ }
15313
+ if (Number.isFinite(numGlyphs) && gid >= numGlyphs) outOfRange++;
15314
+ if (widths[gid] === void 0) missingWidth++;
15315
+ }
15316
+ if (outOfRange > 0) {
15317
+ errors.push(`${outOfRange} cmap entr${outOfRange === 1 ? "y maps" : "ies map"} to an out-of-range glyph id`);
15318
+ }
15319
+ if (missingWidth > 0) {
15320
+ warnings.push(`${missingWidth} cmap glyph id(s) have no explicit width (defaultWidth will be used)`);
15321
+ }
15322
+ }
15323
+ if (typeof f.pdfWidthArray !== "string" || f.pdfWidthArray.length === 0) {
15324
+ errors.push("`pdfWidthArray` must be a non-empty PDF /W array string");
15325
+ } else if (!/^\s*\d/.test(f.pdfWidthArray)) {
15326
+ warnings.push("`pdfWidthArray` does not begin with a glyph index \u2014 verify the /W array format");
15327
+ }
15328
+ if (typeof f.ttfBase64 !== "string" || f.ttfBase64.length === 0) {
15329
+ errors.push("`ttfBase64` must be a non-empty base64 string");
15330
+ } else {
15331
+ const head = decodeBase64Prefix(f.ttfBase64, 4);
15332
+ if (!head || head.length < 4) {
15333
+ errors.push("`ttfBase64` is not valid base64");
15334
+ } else {
15335
+ const magic = (head[0] << 24 | head[1] << 16 | head[2] << 8 | head[3]) >>> 0;
15336
+ if (!SFNT_MAGIC.has(magic)) {
15337
+ errors.push(`\`ttfBase64\` is not an SFNT font (unexpected magic 0x${magic.toString(16).padStart(8, "0")})`);
15338
+ }
15339
+ }
15340
+ }
15341
+ return { valid: errors.length === 0, errors, warnings };
15342
+ }
15343
+
14752
15344
  // src/fonts/colr-parser.ts
14753
15345
  var IDENTITY = [1, 0, 0, 1, 0, 0];
14754
15346
  function compose(outer, inner) {
@@ -14865,6 +15457,22 @@ function resolveFill(ctx, offset, m) {
14865
15457
  const s = avgScale(m);
14866
15458
  return { kind: "radial", c0: apply(m, x0, y0), r0: r0 * s, c1: apply(m, x1, y1), r1: r1 * s, stops, extend };
14867
15459
  }
15460
+ case 8: {
15461
+ const colorLineOffset = getUint24(view, offset + 1);
15462
+ const cx = view.getInt16(offset + 4), cy = view.getInt16(offset + 6);
15463
+ const startAngle = f2dot14(view, offset + 8) * 180;
15464
+ const endAngle = f2dot14(view, offset + 10) * 180;
15465
+ const { stops, extend } = readColorLine(ctx, offset + colorLineOffset);
15466
+ const rot = Math.atan2(m[1], m[0]) * 180 / Math.PI;
15467
+ return {
15468
+ kind: "sweep",
15469
+ center: apply(m, cx, cy),
15470
+ startAngle: startAngle + rot,
15471
+ endAngle: endAngle + rot,
15472
+ stops,
15473
+ extend
15474
+ };
15475
+ }
14868
15476
  case 12: {
14869
15477
  const subOffset = getUint24(view, offset + 1);
14870
15478
  const transformOffset = getUint24(view, offset + 4);
@@ -14894,7 +15502,7 @@ function readAffine(view, pos) {
14894
15502
  const dy = fixed(view, pos + 20);
14895
15503
  return [xx, yx, xy, yy, dx, dy];
14896
15504
  }
14897
- function collectLayers(ctx, offset, m, out, depth) {
15505
+ function collectLayers(ctx, offset, m, out, depth, blendMode) {
14898
15506
  if (depth > 16) throw new UnsupportedPaint("paint recursion too deep");
14899
15507
  const { view } = ctx;
14900
15508
  const format = view.getUint8(offset);
@@ -14906,7 +15514,7 @@ function collectLayers(ctx, offset, m, out, depth) {
14906
15514
  for (let i = 0; i < numLayers; i++) {
14907
15515
  const idx = firstLayerIndex + i;
14908
15516
  const paintOffset = view.getUint32(ctx.layerListBase + 4 + idx * 4);
14909
- collectLayers(ctx, ctx.layerListBase + paintOffset, m, out, depth + 1);
15517
+ collectLayers(ctx, ctx.layerListBase + paintOffset, m, out, depth + 1, blendMode);
14910
15518
  }
14911
15519
  return;
14912
15520
  }
@@ -14914,39 +15522,91 @@ function collectLayers(ctx, offset, m, out, depth) {
14914
15522
  const subOffset = getUint24(view, offset + 1);
14915
15523
  const glyphId = view.getUint16(offset + 4);
14916
15524
  const paint = resolveFill(ctx, offset + subOffset, IDENTITY);
14917
- out.push(m === IDENTITY ? { glyphId, paint } : { glyphId, paint, transform: m });
15525
+ const layer = m === IDENTITY ? { glyphId, paint } : { glyphId, paint, transform: m };
15526
+ out.push(blendMode ? { ...layer, blendMode } : layer);
14918
15527
  return;
14919
15528
  }
14920
15529
  case 11: {
14921
15530
  const glyphId = view.getUint16(offset + 1);
14922
15531
  const paintOffset = baseGlyphPaintOffset(ctx, glyphId);
14923
15532
  if (paintOffset === null) throw new UnsupportedPaint("PaintColrGlyph missing base");
14924
- collectLayers(ctx, paintOffset, m, out, depth + 1);
15533
+ collectLayers(ctx, paintOffset, m, out, depth + 1, blendMode);
14925
15534
  return;
14926
15535
  }
14927
15536
  case 12: {
14928
15537
  const subOffset = getUint24(view, offset + 1);
14929
15538
  const transformOffset = getUint24(view, offset + 4);
14930
15539
  const t = readAffine(view, offset + transformOffset);
14931
- collectLayers(ctx, offset + subOffset, compose(m, t), out, depth + 1);
15540
+ collectLayers(ctx, offset + subOffset, compose(m, t), out, depth + 1, blendMode);
14932
15541
  return;
14933
15542
  }
14934
15543
  case 14: {
14935
15544
  const subOffset = getUint24(view, offset + 1);
14936
15545
  const dx = view.getInt16(offset + 4), dy = view.getInt16(offset + 6);
14937
- collectLayers(ctx, offset + subOffset, compose(m, [1, 0, 0, 1, dx, dy]), out, depth + 1);
15546
+ collectLayers(ctx, offset + subOffset, compose(m, [1, 0, 0, 1, dx, dy]), out, depth + 1, blendMode);
14938
15547
  return;
14939
15548
  }
14940
15549
  case 16: {
14941
15550
  const subOffset = getUint24(view, offset + 1);
14942
15551
  const sx = f2dot14(view, offset + 4), sy = f2dot14(view, offset + 6);
14943
- collectLayers(ctx, offset + subOffset, compose(m, [sx, 0, 0, sy, 0, 0]), out, depth + 1);
15552
+ collectLayers(ctx, offset + subOffset, compose(m, [sx, 0, 0, sy, 0, 0]), out, depth + 1, blendMode);
15553
+ return;
15554
+ }
15555
+ case 32: {
15556
+ const sourceOffset = getUint24(view, offset + 1);
15557
+ const mode = view.getUint8(offset + 4);
15558
+ const backdropOffset = getUint24(view, offset + 5);
15559
+ const bm = compositeModeToBlendMode(mode);
15560
+ if (bm === null) throw new UnsupportedPaint(`composite mode ${mode}`);
15561
+ collectLayers(ctx, offset + backdropOffset, m, out, depth + 1, blendMode);
15562
+ collectLayers(ctx, offset + sourceOffset, m, out, depth + 1, bm === "Normal" ? blendMode : bm);
14944
15563
  return;
14945
15564
  }
14946
15565
  default:
14947
15566
  throw new UnsupportedPaint(`structural paint format ${format}`);
14948
15567
  }
14949
15568
  }
15569
+ function compositeModeToBlendMode(mode) {
15570
+ switch (mode) {
15571
+ case 3:
15572
+ return "Normal";
15573
+ // SRC_OVER
15574
+ case 13:
15575
+ return "Screen";
15576
+ case 14:
15577
+ return "Overlay";
15578
+ case 15:
15579
+ return "Darken";
15580
+ case 16:
15581
+ return "Lighten";
15582
+ case 17:
15583
+ return "ColorDodge";
15584
+ case 18:
15585
+ return "ColorBurn";
15586
+ case 19:
15587
+ return "HardLight";
15588
+ case 20:
15589
+ return "SoftLight";
15590
+ case 21:
15591
+ return "Difference";
15592
+ case 22:
15593
+ return "Exclusion";
15594
+ case 23:
15595
+ return "Multiply";
15596
+ case 24:
15597
+ return "Hue";
15598
+ case 25:
15599
+ return "Saturation";
15600
+ case 26:
15601
+ return "Color";
15602
+ case 27:
15603
+ return "Luminosity";
15604
+ // 0 CLEAR, 1 SRC, 2 DEST, 4 DEST_OVER, 5 SRC_IN, 6 DEST_IN, 7 SRC_OUT,
15605
+ // 8 DEST_OUT, 9 SRC_ATOP, 10 DEST_ATOP, 11 XOR, 12 PLUS → unsupported.
15606
+ default:
15607
+ return null;
15608
+ }
15609
+ }
14950
15610
  function baseGlyphPaintOffset(ctx, glyphId) {
14951
15611
  const { view, colrBase } = ctx;
14952
15612
  const baseGlyphListOffset = view.getUint32(colrBase + 14);
@@ -15022,6 +15682,314 @@ function parseColrCpal(bytes) {
15022
15682
  return Object.keys(result).length ? result : null;
15023
15683
  }
15024
15684
 
15685
+ // src/parser/pdf-pagetree.ts
15686
+ init_pdf_encrypt();
15687
+ var MAX_MERGE_SOURCES = 50;
15688
+ var DEFAULT_MEDIA_BOX = "[0 0 612 792]";
15689
+ var INHERITABLE_KEYS = ["MediaBox", "CropBox", "Rotate"];
15690
+ var MAX_COPY_DEPTH = 2e3;
15691
+ var DEFAULT_MAX_OUTPUT_SIZE = 256 * 1024 * 1024;
15692
+ function resolveMaxOutputSize(value) {
15693
+ if (value === void 0) return DEFAULT_MAX_OUTPUT_SIZE;
15694
+ if (typeof value !== "number" || Number.isNaN(value) || value <= 0) {
15695
+ throw new Error(
15696
+ `maxOutputSize must be a positive number or Infinity (got ${String(value)})`
15697
+ );
15698
+ }
15699
+ return value;
15700
+ }
15701
+ function mergePdfs(sources, opts) {
15702
+ if (sources.length === 0) throw new Error("mergePdfs requires at least one source PDF");
15703
+ if (sources.length > MAX_MERGE_SOURCES) {
15704
+ throw new Error(`mergePdfs supports at most ${MAX_MERGE_SOURCES} sources (got ${sources.length})`);
15705
+ }
15706
+ resolveMaxOutputSize(opts?.maxOutputSize);
15707
+ const specs = [];
15708
+ for (const src of sources) {
15709
+ const reader = openPdf(src);
15710
+ assertNotEncrypted(reader);
15711
+ const count = reader.pageCount;
15712
+ for (let i = 0; i < count; i++) specs.push({ reader, pageIndex: i });
15713
+ }
15714
+ return assemble(specs, opts ?? {});
15715
+ }
15716
+ function extractPages(src, pageIndices, opts) {
15717
+ if (pageIndices.length === 0) throw new Error("extractPages requires at least one page index");
15718
+ resolveMaxOutputSize(opts?.maxOutputSize);
15719
+ const reader = openPdf(src);
15720
+ assertNotEncrypted(reader);
15721
+ const count = reader.pageCount;
15722
+ const specs = [];
15723
+ for (const idx of pageIndices) {
15724
+ if (!Number.isInteger(idx) || idx < 0 || idx >= count) {
15725
+ throw new Error(`extractPages page index ${idx} out of range (0-${count - 1})`);
15726
+ }
15727
+ specs.push({ reader, pageIndex: idx });
15728
+ }
15729
+ return assemble(specs, opts ?? {});
15730
+ }
15731
+ function splitPdf(src, ranges, opts) {
15732
+ if (ranges.length === 0) throw new Error("splitPdf requires at least one range");
15733
+ resolveMaxOutputSize(opts?.maxOutputSize);
15734
+ const reader = openPdf(src);
15735
+ assertNotEncrypted(reader);
15736
+ const count = reader.pageCount;
15737
+ const out = [];
15738
+ for (const range of ranges) {
15739
+ const start = range.start | 0;
15740
+ const end = (range.end ?? range.start) | 0;
15741
+ if (start < 0 || end < start || end >= count) {
15742
+ throw new Error(`splitPdf range [${start}, ${end}] invalid for ${count}-page document`);
15743
+ }
15744
+ const specs = [];
15745
+ for (let i = start; i <= end; i++) specs.push({ reader, pageIndex: i });
15746
+ out.push(assemble(specs, opts ?? {}));
15747
+ }
15748
+ return out;
15749
+ }
15750
+ function assertNotEncrypted(reader) {
15751
+ if (reader.trailer.get("Encrypt") !== void 0) {
15752
+ throw new Error("Encrypted PDFs are not supported by the page-tree API (decrypt first)");
15753
+ }
15754
+ }
15755
+ function resolveInherited(reader, page, key) {
15756
+ let node = page;
15757
+ const seen = /* @__PURE__ */ new Set();
15758
+ while (node && !seen.has(node)) {
15759
+ seen.add(node);
15760
+ const v = node.get(key);
15761
+ if (v !== void 0) return v;
15762
+ const parent = node.get("Parent");
15763
+ if (parent === void 0) break;
15764
+ const resolved = reader.resolveValue(parent);
15765
+ node = isDict(resolved) ? resolved : void 0;
15766
+ }
15767
+ return void 0;
15768
+ }
15769
+ function assemble(specs, opts) {
15770
+ const ctx = {
15771
+ nextNum: 3,
15772
+ bodies: /* @__PURE__ */ new Map(),
15773
+ memo: /* @__PURE__ */ new Map(),
15774
+ maxOutputSize: resolveMaxOutputSize(opts.maxOutputSize),
15775
+ totalBytes: 0
15776
+ };
15777
+ const pageNums = [];
15778
+ for (const spec of specs) {
15779
+ pageNums.push(copyPage(ctx, spec, opts));
15780
+ }
15781
+ const kids = pageNums.map((n2) => `${n2} 0 R`).join(" ");
15782
+ setBody(ctx, 2, `<< /Type /Pages /Kids [${kids}] /Count ${pageNums.length} >>`);
15783
+ setBody(ctx, 1, "<< /Type /Catalog /Pages 2 0 R >>");
15784
+ return serializeDocument(ctx);
15785
+ }
15786
+ function accountBytes(ctx, n2) {
15787
+ ctx.totalBytes += n2;
15788
+ if (ctx.totalBytes > ctx.maxOutputSize) {
15789
+ throw new Error(
15790
+ `page-tree output exceeded the ${ctx.maxOutputSize}-byte maxOutputSize limit (raise MergeOptions.maxOutputSize or pass Infinity to disable)`
15791
+ );
15792
+ }
15793
+ }
15794
+ function setBody(ctx, num, body) {
15795
+ accountBytes(ctx, body.length);
15796
+ ctx.bodies.set(num, body);
15797
+ }
15798
+ function memoFor(ctx, reader) {
15799
+ let m = ctx.memo.get(reader);
15800
+ if (!m) {
15801
+ m = /* @__PURE__ */ new Map();
15802
+ ctx.memo.set(reader, m);
15803
+ }
15804
+ return m;
15805
+ }
15806
+ function copyPage(ctx, spec, opts) {
15807
+ const { reader, pageIndex } = spec;
15808
+ const page = reader.getPage(pageIndex);
15809
+ const pageNum = ctx.nextNum++;
15810
+ const parts = ["/Type /Page", "/Parent 2 0 R"];
15811
+ for (const key of INHERITABLE_KEYS) {
15812
+ const v = resolveInherited(reader, page, key);
15813
+ if (v !== void 0) {
15814
+ parts.push(`/${key} ${serializeValue2(rewrite(ctx, reader, v))}`);
15815
+ } else if (key === "MediaBox") {
15816
+ parts.push(`/MediaBox ${DEFAULT_MEDIA_BOX}`);
15817
+ }
15818
+ }
15819
+ const res = resolveInherited(reader, page, "Resources");
15820
+ parts.push(`/Resources ${res !== void 0 ? serializeValue2(rewrite(ctx, reader, res)) : "<< >>"}`);
15821
+ const contents = page.get("Contents");
15822
+ if (contents !== void 0) {
15823
+ parts.push(`/Contents ${serializeValue2(rewrite(ctx, reader, contents))}`);
15824
+ }
15825
+ if (!opts.dropAnnotations) {
15826
+ const annots = filterAnnotations(ctx, reader, page);
15827
+ if (annots) parts.push(`/Annots ${annots}`);
15828
+ }
15829
+ setBody(ctx, pageNum, `<< ${parts.join(" ")} >>`);
15830
+ return pageNum;
15831
+ }
15832
+ function filterAnnotations(ctx, reader, page) {
15833
+ const annotsVal = page.get("Annots");
15834
+ if (annotsVal === void 0) return void 0;
15835
+ const arr = reader.resolveValue(annotsVal);
15836
+ if (!isArray(arr)) return void 0;
15837
+ const kept = [];
15838
+ for (const a of arr) {
15839
+ const ad = reader.resolveValue(a);
15840
+ if (!isDict(ad)) continue;
15841
+ if (dictGetName(ad, "Subtype") !== "Link") continue;
15842
+ const action = reader.resolveValue(ad.get("A") ?? null);
15843
+ if (!isDict(action) || dictGetName(action, "S") !== "URI") continue;
15844
+ const clean = /* @__PURE__ */ new Map();
15845
+ for (const [k, v] of ad) {
15846
+ if (k === "P" || k === "Parent") continue;
15847
+ clean.set(k, rewrite(ctx, reader, v));
15848
+ }
15849
+ kept.push(serializeValue2(clean));
15850
+ }
15851
+ return kept.length > 0 ? `[${kept.join(" ")}]` : void 0;
15852
+ }
15853
+ function copyObject(ctx, reader, srcNum, srcGen, depth = 0) {
15854
+ const memo = memoFor(ctx, reader);
15855
+ const existing = memo.get(srcNum);
15856
+ if (existing !== void 0) return existing;
15857
+ const newNum = ctx.nextNum++;
15858
+ memo.set(srcNum, newNum);
15859
+ const resolved = reader.resolve({ type: "ref", num: srcNum, gen: srcGen });
15860
+ if (isStream(resolved)) {
15861
+ ctx.bodies.set(newNum, serializeStreamBody(ctx, reader, resolved, depth));
15862
+ } else {
15863
+ const body = serializeValue2(rewrite(ctx, reader, resolved, depth));
15864
+ setBody(ctx, newNum, body);
15865
+ }
15866
+ return newNum;
15867
+ }
15868
+ function rewrite(ctx, reader, val, depth = 0) {
15869
+ if (depth > MAX_COPY_DEPTH) {
15870
+ throw new Error(
15871
+ `page-tree copy exceeded maximum object nesting depth (${MAX_COPY_DEPTH}) \u2014 malformed or adversarial PDF`
15872
+ );
15873
+ }
15874
+ if (isRef(val)) {
15875
+ const entry = reader.xref.entries.get(val.num);
15876
+ if (!entry || entry.type === 0) return null;
15877
+ const newNum = copyObject(ctx, reader, val.num, val.gen, depth + 1);
15878
+ return { type: "ref", num: newNum, gen: 0 };
15879
+ }
15880
+ if (isArray(val)) return val.map((v) => rewrite(ctx, reader, v, depth + 1));
15881
+ if (isStream(val)) {
15882
+ return val;
15883
+ }
15884
+ if (isDict(val)) {
15885
+ const out = /* @__PURE__ */ new Map();
15886
+ for (const [k, v] of val) out.set(k, rewrite(ctx, reader, v, depth + 1));
15887
+ return out;
15888
+ }
15889
+ return val;
15890
+ }
15891
+ function serializeStreamBody(ctx, reader, stream, depth = 0) {
15892
+ accountBytes(ctx, stream.data.length);
15893
+ const dict = /* @__PURE__ */ new Map();
15894
+ for (const [k, v] of stream.dict) {
15895
+ if (k === "Length") continue;
15896
+ dict.set(k, rewrite(ctx, reader, v, depth + 1));
15897
+ }
15898
+ dict.set("Length", stream.data.length);
15899
+ let body = serializeDict2(dict);
15900
+ body += "\nstream\n";
15901
+ body += bytesToLatin1(stream.data);
15902
+ body += "\nendstream";
15903
+ accountBytes(ctx, body.length - stream.data.length);
15904
+ return body;
15905
+ }
15906
+ function serializeValue2(val) {
15907
+ if (val === null) return "null";
15908
+ if (typeof val === "boolean") return val ? "true" : "false";
15909
+ if (typeof val === "number") {
15910
+ if (Number.isInteger(val)) return String(val);
15911
+ return val.toFixed(4).replace(/\.?0+$/, "");
15912
+ }
15913
+ if (typeof val === "string") return `(${escapePdfStr2(val)})`;
15914
+ if (isName(val)) return `/${val.value}`;
15915
+ if (isRef(val)) return `${val.num} ${val.gen} R`;
15916
+ if (isArray(val)) return "[" + val.map(serializeValue2).join(" ") + "]";
15917
+ if (isStream(val)) return serializeDict2(val.dict);
15918
+ if (isDict(val)) return serializeDict2(val);
15919
+ return "null";
15920
+ }
15921
+ function serializeDict2(dict) {
15922
+ let s = "<<";
15923
+ for (const [key, val] of dict) s += ` /${key} ${serializeValue2(val)}`;
15924
+ s += " >>";
15925
+ return s;
15926
+ }
15927
+ function escapePdfStr2(s) {
15928
+ return s.replace(/[\\()]/g, (c) => "\\" + c);
15929
+ }
15930
+ function bytesToLatin1(bytes) {
15931
+ let s = "";
15932
+ const CHUNK = 32768;
15933
+ for (let i = 0; i < bytes.length; i += CHUNK) {
15934
+ s += String.fromCharCode(...bytes.subarray(i, i + CHUNK));
15935
+ }
15936
+ return s;
15937
+ }
15938
+ function serializeDocument(ctx) {
15939
+ const maxNum = ctx.nextNum - 1;
15940
+ const parts = [];
15941
+ const offsets = new Array(maxNum + 1).fill(0);
15942
+ let offset = 0;
15943
+ function push(s) {
15944
+ parts.push(s);
15945
+ offset += s.length;
15946
+ }
15947
+ push("%PDF-1.7\n");
15948
+ push("%\xE2\xE3\xCF\xD3\n");
15949
+ for (let num = 1; num <= maxNum; num++) {
15950
+ const body = ctx.bodies.get(num);
15951
+ if (body === void 0) continue;
15952
+ offsets[num] = offset;
15953
+ push(`${num} 0 obj
15954
+ ${body}
15955
+ endobj
15956
+ `);
15957
+ }
15958
+ const xrefOffset = offset;
15959
+ const size = maxNum + 1;
15960
+ let xref = `xref
15961
+ 0 ${size}
15962
+ 0000000000 65535 f
15963
+ `;
15964
+ for (let num = 1; num <= maxNum; num++) {
15965
+ xref += `${String(offsets[num]).padStart(10, "0")} 00000 n
15966
+ `;
15967
+ }
15968
+ push(xref);
15969
+ const id = bytesToHex(md5(latin1ToBytes(parts.join(""))));
15970
+ push(`trailer
15971
+ << /Size ${size} /Root 1 0 R /ID [<${id}> <${id}>] >>
15972
+ `);
15973
+ push(`startxref
15974
+ ${xrefOffset}
15975
+ %%EOF
15976
+ `);
15977
+ const full = parts.join("");
15978
+ const out = new Uint8Array(full.length);
15979
+ for (let i = 0; i < full.length; i++) out[i] = full.charCodeAt(i) & 255;
15980
+ return out;
15981
+ }
15982
+ function latin1ToBytes(s) {
15983
+ const out = new Uint8Array(s.length);
15984
+ for (let i = 0; i < s.length; i++) out[i] = s.charCodeAt(i) & 255;
15985
+ return out;
15986
+ }
15987
+ function bytesToHex(bytes) {
15988
+ let s = "";
15989
+ for (let i = 0; i < bytes.length; i++) s += bytes[i].toString(16).padStart(2, "0");
15990
+ return s;
15991
+ }
15992
+
15025
15993
  // src/parser/pdf-ua-validator.ts
15026
15994
  function pageContentText(reader, page) {
15027
15995
  const contents = page.get("Contents");
@@ -15284,11 +16252,13 @@ exports.encodePdfTextString = encodePdfTextString;
15284
16252
  exports.estimateCmsSize = estimateCmsSize;
15285
16253
  exports.estimateContentsSize = estimateContentsSize;
15286
16254
  exports.extractGlyphContours = extractGlyphContours;
16255
+ exports.extractPages = extractPages;
15287
16256
  exports.findStartxref = findStartxref;
15288
16257
  exports.generateDataMatrix = generateDataMatrix;
15289
16258
  exports.generatePDFInWorker = generatePDFInWorker;
15290
16259
  exports.generatePDFMainThread = generatePDFMainThread;
15291
16260
  exports.generateQR = generateQR;
16261
+ exports.getCryptoProvider = getCryptoProvider;
15292
16262
  exports.getMaxInflateOutputSize = getMaxInflateOutputSize;
15293
16263
  exports.getRegisteredLangs = getRegisteredLangs;
15294
16264
  exports.getTrailerRef = getTrailerRef;
@@ -15322,6 +16292,7 @@ exports.isTeluguCodepoint = isTeluguCodepoint;
15322
16292
  exports.isTibetanCodepoint = isTibetanCodepoint;
15323
16293
  exports.isValidPdfRgb = isValidPdfRgb;
15324
16294
  exports.loadFontData = loadFontData;
16295
+ exports.mergePdfs = mergePdfs;
15325
16296
  exports.nameValue = nameValue;
15326
16297
  exports.needsUnicodeFont = needsUnicodeFont;
15327
16298
  exports.normalizeBidiEmbeddings = normalizeBidiEmbeddings;
@@ -15361,6 +16332,7 @@ exports.rsaSign = rsaSign;
15361
16332
  exports.rsaSignHash = rsaSignHash;
15362
16333
  exports.rsaVerify = rsaVerify;
15363
16334
  exports.rsaVerifyHash = rsaVerifyHash;
16335
+ exports.setCryptoProvider = setCryptoProvider;
15364
16336
  exports.setDeflateImpl = setDeflateImpl;
15365
16337
  exports.setInflateImpl = setInflateImpl;
15366
16338
  exports.setMaxInflateOutputSize = setMaxInflateOutputSize;
@@ -15378,8 +16350,10 @@ exports.shapeThaiText = shapeThaiText;
15378
16350
  exports.shapeTibetanText = shapeTibetanText;
15379
16351
  exports.signPdfBytes = signPdfBytes;
15380
16352
  exports.slugify = slugify;
16353
+ exports.splitPdf = splitPdf;
15381
16354
  exports.splitTextByFont = splitTextByFont;
15382
16355
  exports.streamByteLength = streamByteLength;
16356
+ exports.streamToFile = streamToFile;
15383
16357
  exports.stripBidiControls = stripBidiControls;
15384
16358
  exports.toBytes = toBytes;
15385
16359
  exports.toWinAnsi = toWinAnsi;
@@ -15387,6 +16361,7 @@ exports.truncate = truncate;
15387
16361
  exports.truncateToWidth = truncateToWidth;
15388
16362
  exports.validateAttachments = validateAttachments;
15389
16363
  exports.validateDocumentStreamable = validateDocumentStreamable;
16364
+ exports.validateFontData = validateFontData;
15390
16365
  exports.validatePdfUA = validatePdfUA;
15391
16366
  exports.validateTableStreamable = validateTableStreamable;
15392
16367
  exports.validateURL = validateURL;