pdfnative 1.3.0 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +163 -40
- package/dist/index.cjs +1577 -66
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +800 -10
- package/dist/index.d.ts +800 -10
- package/dist/index.js +1566 -67
- package/dist/index.js.map +1 -1
- package/dist/tools/build-emoji-font.js +1139 -0
- package/dist/tools/index.cjs +657 -0
- package/dist/tools/index.cjs.map +1 -0
- package/dist/tools/index.d.cts +110 -0
- package/dist/tools/index.d.ts +110 -0
- package/dist/tools/index.js +654 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/worker/index.cjs +110 -11
- package/dist/worker/index.cjs.map +1 -1
- package/dist/worker/index.js +110 -11
- package/dist/worker/index.js.map +1 -1
- package/fonts/noto-sans-math-data.d.ts +13 -0
- package/fonts/noto-sans-math-data.js +64 -0
- package/package.json +45 -5
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
|
-
|
|
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)
|
|
@@ -2319,6 +2324,16 @@ var MYANMAR_EXTENDED_A_END = 43647;
|
|
|
2319
2324
|
var MYANMAR_EXTENDED_B_START = 43488;
|
|
2320
2325
|
var MYANMAR_EXTENDED_B_END = 43519;
|
|
2321
2326
|
var MYANMAR_VIRAMA = 4153;
|
|
2327
|
+
var MATH_OPERATORS_START = 8704;
|
|
2328
|
+
var MATH_OPERATORS_END = 8959;
|
|
2329
|
+
var SUPPLEMENTAL_MATH_OPERATORS_START = 10752;
|
|
2330
|
+
var SUPPLEMENTAL_MATH_OPERATORS_END = 11007;
|
|
2331
|
+
var GEOMETRIC_SHAPES_START = 9632;
|
|
2332
|
+
var GEOMETRIC_SHAPES_END = 9727;
|
|
2333
|
+
var MISC_MATH_SYMBOLS_A_START = 10176;
|
|
2334
|
+
var MISC_MATH_SYMBOLS_A_END = 10223;
|
|
2335
|
+
var MISC_MATH_SYMBOLS_B_START = 10624;
|
|
2336
|
+
var MISC_MATH_SYMBOLS_B_END = 10751;
|
|
2322
2337
|
var EMOJI_RANGES = [
|
|
2323
2338
|
[127744, 128511],
|
|
2324
2339
|
// Miscellaneous Symbols and Pictographs
|
|
@@ -2397,6 +2412,9 @@ function isMyanmarCodepoint(cp) {
|
|
|
2397
2412
|
function isDevanagariCodepoint(cp) {
|
|
2398
2413
|
return cp >= DEVANAGARI_START && cp <= DEVANAGARI_END || cp >= DEVANAGARI_EXT_START && cp <= DEVANAGARI_EXT_END;
|
|
2399
2414
|
}
|
|
2415
|
+
function isMathCodepoint(cp) {
|
|
2416
|
+
return cp >= MATH_OPERATORS_START && cp <= MATH_OPERATORS_END || cp >= SUPPLEMENTAL_MATH_OPERATORS_START && cp <= SUPPLEMENTAL_MATH_OPERATORS_END || cp >= GEOMETRIC_SHAPES_START && cp <= GEOMETRIC_SHAPES_END || cp >= MISC_MATH_SYMBOLS_A_START && cp <= MISC_MATH_SYMBOLS_A_END || cp >= MISC_MATH_SYMBOLS_B_START && cp <= MISC_MATH_SYMBOLS_B_END;
|
|
2417
|
+
}
|
|
2400
2418
|
function isEmojiCodepoint(cp) {
|
|
2401
2419
|
if (cp >= FITZPATRICK_START && cp <= FITZPATRICK_END) return true;
|
|
2402
2420
|
for (const [lo, hi] of EMOJI_RANGES) {
|
|
@@ -2483,10 +2501,16 @@ function containsDevanagari(str) {
|
|
|
2483
2501
|
}
|
|
2484
2502
|
return false;
|
|
2485
2503
|
}
|
|
2504
|
+
function containsMath(str) {
|
|
2505
|
+
for (let i = 0; i < str.length; i++) {
|
|
2506
|
+
if (isMathCodepoint(str.charCodeAt(i))) return true;
|
|
2507
|
+
}
|
|
2508
|
+
return false;
|
|
2509
|
+
}
|
|
2486
2510
|
|
|
2487
2511
|
// src/shaping/script-detect.ts
|
|
2488
2512
|
function needsUnicodeFont(lang) {
|
|
2489
|
-
return ["th", "ja", "zh", "ko", "el", "hi", "te", "tr", "vi", "pl", "ar", "he", "ru", "ka", "hy", "am", "si", "bo", "km", "my", "emoji"].includes(lang);
|
|
2513
|
+
return ["th", "ja", "zh", "ko", "el", "hi", "te", "tr", "vi", "pl", "ar", "he", "ru", "ka", "hy", "am", "si", "bo", "km", "my", "math", "emoji"].includes(lang);
|
|
2490
2514
|
}
|
|
2491
2515
|
function detectFallbackLangs(texts, primaryLang) {
|
|
2492
2516
|
const needed = /* @__PURE__ */ new Set();
|
|
@@ -2587,6 +2611,10 @@ function detectFallbackLangs(texts, primaryLang) {
|
|
|
2587
2611
|
needed.add("hy");
|
|
2588
2612
|
continue;
|
|
2589
2613
|
}
|
|
2614
|
+
if (isMathCodepoint(cp)) {
|
|
2615
|
+
needed.add("math");
|
|
2616
|
+
continue;
|
|
2617
|
+
}
|
|
2590
2618
|
if (isEmojiCodepoint(cp)) {
|
|
2591
2619
|
needed.add("emoji");
|
|
2592
2620
|
continue;
|
|
@@ -2617,6 +2645,7 @@ function detectCharLang(cp) {
|
|
|
2617
2645
|
if (cp >= 1024 && cp <= 1279 || cp >= 1280 && cp <= 1327) return "ru";
|
|
2618
2646
|
if (cp >= 4256 && cp <= 4351 || cp >= 11520 && cp <= 11567) return "ka";
|
|
2619
2647
|
if (cp >= 1328 && cp <= 1423 || cp >= 64275 && cp <= 64279) return "hy";
|
|
2648
|
+
if (isMathCodepoint(cp)) return "math";
|
|
2620
2649
|
if (isEmojiCodepoint(cp)) return "emoji";
|
|
2621
2650
|
return null;
|
|
2622
2651
|
}
|
|
@@ -5981,15 +6010,86 @@ function radialShadingDict(p, m) {
|
|
|
5981
6010
|
const s = (sx + sy) / 2 || 1;
|
|
5982
6011
|
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
6012
|
}
|
|
6013
|
+
function colorAtOffset(stops, t) {
|
|
6014
|
+
if (stops.length === 0) return [0, 0, 0, 255];
|
|
6015
|
+
const sorted = stops.slice().sort((a, b) => a.offset - b.offset);
|
|
6016
|
+
if (t <= sorted[0].offset) return sorted[0].color;
|
|
6017
|
+
const last = sorted[sorted.length - 1];
|
|
6018
|
+
if (t >= last.offset) return last.color;
|
|
6019
|
+
for (let i = 0; i < sorted.length - 1; i++) {
|
|
6020
|
+
const a = sorted[i], b = sorted[i + 1];
|
|
6021
|
+
if (t >= a.offset && t <= b.offset) {
|
|
6022
|
+
const span = b.offset - a.offset || 1;
|
|
6023
|
+
const f = (t - a.offset) / span;
|
|
6024
|
+
return [
|
|
6025
|
+
Math.round(a.color[0] + (b.color[0] - a.color[0]) * f),
|
|
6026
|
+
Math.round(a.color[1] + (b.color[1] - a.color[1]) * f),
|
|
6027
|
+
Math.round(a.color[2] + (b.color[2] - a.color[2]) * f),
|
|
6028
|
+
Math.round(a.color[3] + (b.color[3] - a.color[3]) * f)
|
|
6029
|
+
];
|
|
6030
|
+
}
|
|
6031
|
+
}
|
|
6032
|
+
return last.color;
|
|
6033
|
+
}
|
|
6034
|
+
function emitSweep(p, cx, cy, maxR, body, gsFor, blendMode) {
|
|
6035
|
+
const start = p.startAngle;
|
|
6036
|
+
const end = p.endAngle;
|
|
6037
|
+
const span = end - start;
|
|
6038
|
+
if (Math.abs(span) < 0.01) {
|
|
6039
|
+
const c = colorAtOffset(p.stops, 0);
|
|
6040
|
+
const gs = gsFor(c[3] / 255, blendMode);
|
|
6041
|
+
if (gs) body.push(`/${gs} gs`);
|
|
6042
|
+
body.push(`${ch(c[0])} ${ch(c[1])} ${ch(c[2])} rg`);
|
|
6043
|
+
body.push(`${n(cx - maxR)} ${n(cy - maxR)} ${n(2 * maxR)} ${n(2 * maxR)} re`);
|
|
6044
|
+
body.push("f");
|
|
6045
|
+
return;
|
|
6046
|
+
}
|
|
6047
|
+
const steps = Math.max(12, Math.min(180, Math.ceil(Math.abs(span) / 3)));
|
|
6048
|
+
const r = maxR * 1.5;
|
|
6049
|
+
const rad = Math.PI / 180;
|
|
6050
|
+
for (let i = 0; i < steps; i++) {
|
|
6051
|
+
const a0 = start + span * i / steps;
|
|
6052
|
+
const a1 = start + span * (i + 1) / steps;
|
|
6053
|
+
const tMid = (i + 0.5) / steps;
|
|
6054
|
+
const c = colorAtOffset(p.stops, tMid);
|
|
6055
|
+
const gs = gsFor(c[3] / 255, blendMode);
|
|
6056
|
+
body.push("q");
|
|
6057
|
+
if (gs) body.push(`/${gs} gs`);
|
|
6058
|
+
body.push(`${ch(c[0])} ${ch(c[1])} ${ch(c[2])} rg`);
|
|
6059
|
+
const x0 = cx + r * Math.cos(a0 * rad), y0 = cy + r * Math.sin(a0 * rad);
|
|
6060
|
+
const x1 = cx + r * Math.cos(a1 * rad), y1 = cy + r * Math.sin(a1 * rad);
|
|
6061
|
+
body.push(`${n(cx)} ${n(cy)} m ${n(x0)} ${n(y0)} l ${n(x1)} ${n(y1)} l h`);
|
|
6062
|
+
body.push("f");
|
|
6063
|
+
body.push("Q");
|
|
6064
|
+
}
|
|
6065
|
+
}
|
|
5984
6066
|
function renderColorGlyph(glyph, outlines, unitsPerEm) {
|
|
5985
6067
|
const body = [];
|
|
5986
6068
|
const shadings = [];
|
|
5987
6069
|
const extGStates = [];
|
|
5988
|
-
const
|
|
6070
|
+
const gsMap = /* @__PURE__ */ new Map();
|
|
5989
6071
|
let shadingIdx = 0;
|
|
6072
|
+
const gsFor = (alpha, bm) => {
|
|
6073
|
+
const a = Math.max(0, Math.min(1, alpha));
|
|
6074
|
+
const needAlpha = a < 0.999;
|
|
6075
|
+
const needBm = bm !== void 0 && bm !== "Normal";
|
|
6076
|
+
if (!needAlpha && !needBm) return "";
|
|
6077
|
+
const key = `${needAlpha ? a.toFixed(3) : "1"}|${needBm ? bm : ""}`;
|
|
6078
|
+
let name = gsMap.get(key);
|
|
6079
|
+
if (!name) {
|
|
6080
|
+
name = `Gs${gsMap.size}`;
|
|
6081
|
+
gsMap.set(key, name);
|
|
6082
|
+
const parts = [];
|
|
6083
|
+
if (needAlpha) parts.push(`/ca ${n(a)}`, `/CA ${n(a)}`);
|
|
6084
|
+
if (needBm) parts.push(`/BM /${bm}`);
|
|
6085
|
+
extGStates.push({ name, dict: `<< ${parts.join(" ")} >>` });
|
|
6086
|
+
}
|
|
6087
|
+
return name;
|
|
6088
|
+
};
|
|
5990
6089
|
let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
|
|
5991
6090
|
for (const layer of glyph.layers) {
|
|
5992
6091
|
const m = layer.transform ?? ID;
|
|
6092
|
+
const bm = layer.blendMode;
|
|
5993
6093
|
const contours = outlines(layer.glyphId);
|
|
5994
6094
|
if (contours.length === 0) continue;
|
|
5995
6095
|
const path = contoursToPath(contours, m);
|
|
@@ -6004,26 +6104,36 @@ function renderColorGlyph(glyph, outlines, unitsPerEm) {
|
|
|
6004
6104
|
}
|
|
6005
6105
|
if (layer.paint.kind === "solid") {
|
|
6006
6106
|
const c = layer.paint.color;
|
|
6007
|
-
const alpha = c[3] / 255;
|
|
6008
6107
|
body.push("q");
|
|
6009
|
-
|
|
6010
|
-
|
|
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
|
-
}
|
|
6108
|
+
const gs = gsFor(c[3] / 255, bm);
|
|
6109
|
+
if (gs) body.push(`/${gs} gs`);
|
|
6018
6110
|
body.push(`${ch(c[0])} ${ch(c[1])} ${ch(c[2])} rg`);
|
|
6019
6111
|
body.push(path);
|
|
6020
6112
|
body.push("f");
|
|
6021
6113
|
body.push("Q");
|
|
6114
|
+
} else if (layer.paint.kind === "sweep") {
|
|
6115
|
+
const [cx, cy] = tx(m, layer.paint.center[0], layer.paint.center[1]);
|
|
6116
|
+
let r2 = 0;
|
|
6117
|
+
for (const contour of contours) {
|
|
6118
|
+
for (const pt of contour) {
|
|
6119
|
+
const [px, py] = tx(m, pt.x, pt.y);
|
|
6120
|
+
const d = (px - cx) * (px - cx) + (py - cy) * (py - cy);
|
|
6121
|
+
if (d > r2) r2 = d;
|
|
6122
|
+
}
|
|
6123
|
+
}
|
|
6124
|
+
const maxR = Math.sqrt(r2) || 1;
|
|
6125
|
+
body.push("q");
|
|
6126
|
+
body.push(path);
|
|
6127
|
+
body.push("W n");
|
|
6128
|
+
emitSweep(layer.paint, cx, cy, maxR, body, gsFor, bm);
|
|
6129
|
+
body.push("Q");
|
|
6022
6130
|
} else {
|
|
6023
6131
|
const name = `Sh${shadingIdx++}`;
|
|
6024
6132
|
const dict = layer.paint.kind === "linear" ? linearShadingDict(layer.paint, m) : radialShadingDict(layer.paint, m);
|
|
6025
6133
|
shadings.push({ name, dict });
|
|
6026
6134
|
body.push("q");
|
|
6135
|
+
const gs = gsFor(1, bm);
|
|
6136
|
+
if (gs) body.push(`/${gs} gs`);
|
|
6027
6137
|
body.push(path);
|
|
6028
6138
|
body.push("W n");
|
|
6029
6139
|
body.push(`/${name} sh`);
|
|
@@ -6165,6 +6275,10 @@ function buildTextRunsWithFallback(text, fontRef, fd, sz, trackGid, pdfA = false
|
|
|
6165
6275
|
for (let i = 0; i < text.length; ) {
|
|
6166
6276
|
const rawCp = text.codePointAt(i) ?? 0;
|
|
6167
6277
|
const charLen = rawCp > 65535 ? 2 : 1;
|
|
6278
|
+
if (rawCp < 32 || rawCp === 127) {
|
|
6279
|
+
i += charLen;
|
|
6280
|
+
continue;
|
|
6281
|
+
}
|
|
6168
6282
|
const cp = rawCp === 8239 || rawCp === 160 ? 32 : rawCp;
|
|
6169
6283
|
const char = text.substring(i, i + charLen);
|
|
6170
6284
|
const gid = fd.cmap[cp] ?? 0;
|
|
@@ -8531,9 +8645,238 @@ function buildPDFBytes(params, layoutOptions) {
|
|
|
8531
8645
|
return toBytes(buildPDF(params, layoutOptions));
|
|
8532
8646
|
}
|
|
8533
8647
|
|
|
8648
|
+
// src/core/pdf-outline.ts
|
|
8649
|
+
function buildOutlineObjects(items, startObjNum, pageObjNumFor, defaultY, fmtNum3, pageCount) {
|
|
8650
|
+
const rootObjNum = startObjNum;
|
|
8651
|
+
const nodes = [];
|
|
8652
|
+
let nextObj = startObjNum + 1;
|
|
8653
|
+
function alloc(list, parentObjNum, depth) {
|
|
8654
|
+
let first = 0;
|
|
8655
|
+
let last = 0;
|
|
8656
|
+
let prev = 0;
|
|
8657
|
+
let visibleCount = 0;
|
|
8658
|
+
const siblingNodes = [];
|
|
8659
|
+
for (const item of list) {
|
|
8660
|
+
const objNum = nextObj++;
|
|
8661
|
+
const open = item.open !== false;
|
|
8662
|
+
const node = {
|
|
8663
|
+
item,
|
|
8664
|
+
objNum,
|
|
8665
|
+
parentObjNum,
|
|
8666
|
+
depth,
|
|
8667
|
+
firstChildObjNum: 0,
|
|
8668
|
+
lastChildObjNum: 0,
|
|
8669
|
+
prevObjNum: prev,
|
|
8670
|
+
nextObjNum: 0,
|
|
8671
|
+
openDescendantCount: 0,
|
|
8672
|
+
open
|
|
8673
|
+
};
|
|
8674
|
+
if (prev !== 0) {
|
|
8675
|
+
const prevNode = siblingNodes[siblingNodes.length - 1];
|
|
8676
|
+
prevNode.nextObjNum = objNum;
|
|
8677
|
+
}
|
|
8678
|
+
nodes.push(node);
|
|
8679
|
+
siblingNodes.push(node);
|
|
8680
|
+
if (first === 0) first = objNum;
|
|
8681
|
+
last = objNum;
|
|
8682
|
+
prev = objNum;
|
|
8683
|
+
visibleCount++;
|
|
8684
|
+
const children = item.children;
|
|
8685
|
+
if (children && children.length > 0) {
|
|
8686
|
+
const sub = alloc(children, objNum, depth + 1);
|
|
8687
|
+
node.firstChildObjNum = sub.first;
|
|
8688
|
+
node.lastChildObjNum = sub.last;
|
|
8689
|
+
node.openDescendantCount = sub.visibleCount;
|
|
8690
|
+
if (open) visibleCount += sub.visibleCount;
|
|
8691
|
+
}
|
|
8692
|
+
}
|
|
8693
|
+
return { first, last, visibleCount };
|
|
8694
|
+
}
|
|
8695
|
+
const top = alloc(items, rootObjNum, 0);
|
|
8696
|
+
const objects = [];
|
|
8697
|
+
objects.push([
|
|
8698
|
+
rootObjNum,
|
|
8699
|
+
`<< /Type /Outlines /First ${top.first} 0 R /Last ${top.last} 0 R /Count ${top.visibleCount} >>`
|
|
8700
|
+
]);
|
|
8701
|
+
for (const node of nodes) {
|
|
8702
|
+
const it = node.item;
|
|
8703
|
+
const pageIdx = Math.max(0, Math.min(pageCount - 1, it.pageIndex | 0));
|
|
8704
|
+
const pageObj = pageObjNumFor(pageIdx);
|
|
8705
|
+
const y = it.y ?? defaultY;
|
|
8706
|
+
const parts = [
|
|
8707
|
+
`/Title ${encodePdfTextString(it.title)}`,
|
|
8708
|
+
`/Parent ${node.parentObjNum} 0 R`
|
|
8709
|
+
];
|
|
8710
|
+
if (node.prevObjNum !== 0) parts.push(`/Prev ${node.prevObjNum} 0 R`);
|
|
8711
|
+
if (node.nextObjNum !== 0) parts.push(`/Next ${node.nextObjNum} 0 R`);
|
|
8712
|
+
if (node.firstChildObjNum !== 0) {
|
|
8713
|
+
parts.push(`/First ${node.firstChildObjNum} 0 R`);
|
|
8714
|
+
parts.push(`/Last ${node.lastChildObjNum} 0 R`);
|
|
8715
|
+
const count = node.open ? node.openDescendantCount : -node.openDescendantCount;
|
|
8716
|
+
parts.push(`/Count ${count}`);
|
|
8717
|
+
}
|
|
8718
|
+
parts.push(`/Dest [${pageObj} 0 R /XYZ 0 ${fmtNum3(y)} null]`);
|
|
8719
|
+
const flags = (it.bold ? 2 : 0) | (it.italic ? 1 : 0);
|
|
8720
|
+
if (flags !== 0) parts.push(`/F ${flags}`);
|
|
8721
|
+
if (it.color) parts.push(`/C [${it.color}]`);
|
|
8722
|
+
objects.push([node.objNum, `<< ${parts.join(" ")} >>`]);
|
|
8723
|
+
}
|
|
8724
|
+
return { objects, rootObjNum, totalObjects: nodes.length + 1 };
|
|
8725
|
+
}
|
|
8726
|
+
|
|
8727
|
+
// src/core/pdf-page-labels.ts
|
|
8728
|
+
var STYLE_OP = {
|
|
8729
|
+
decimal: "D",
|
|
8730
|
+
roman: "r",
|
|
8731
|
+
Roman: "R",
|
|
8732
|
+
alpha: "a",
|
|
8733
|
+
Alpha: "A"
|
|
8734
|
+
};
|
|
8735
|
+
function escapePdfLiteral(s) {
|
|
8736
|
+
return s.replace(/[\\()]/g, (c) => "\\" + c);
|
|
8737
|
+
}
|
|
8738
|
+
function buildPageLabelsDict(ranges, pageCount) {
|
|
8739
|
+
if (ranges.length === 0) {
|
|
8740
|
+
throw new Error("pageLabels must contain at least one range");
|
|
8741
|
+
}
|
|
8742
|
+
const sorted = [...ranges].sort((a, b) => a.startPage - b.startPage);
|
|
8743
|
+
let prev = -1;
|
|
8744
|
+
const nums = [];
|
|
8745
|
+
for (const r of sorted) {
|
|
8746
|
+
const idx = r.startPage | 0;
|
|
8747
|
+
if (idx < 0 || idx >= pageCount) {
|
|
8748
|
+
throw new Error(`pageLabels range startPage ${idx} out of bounds (0-${pageCount - 1})`);
|
|
8749
|
+
}
|
|
8750
|
+
if (idx <= prev) {
|
|
8751
|
+
throw new Error(`pageLabels ranges must have strictly increasing, unique startPage values (got ${idx} after ${prev})`);
|
|
8752
|
+
}
|
|
8753
|
+
prev = idx;
|
|
8754
|
+
const entryParts = [];
|
|
8755
|
+
const style = r.style ?? "decimal";
|
|
8756
|
+
if (style !== "none") {
|
|
8757
|
+
entryParts.push(`/S /${STYLE_OP[style]}`);
|
|
8758
|
+
}
|
|
8759
|
+
if (r.prefix !== void 0 && r.prefix !== "") {
|
|
8760
|
+
entryParts.push(`/P (${escapePdfLiteral(r.prefix)})`);
|
|
8761
|
+
}
|
|
8762
|
+
if (r.start !== void 0 && r.start !== 1) {
|
|
8763
|
+
if (!Number.isInteger(r.start) || r.start < 1) {
|
|
8764
|
+
throw new Error(`pageLabels range start must be a positive integer (got ${r.start})`);
|
|
8765
|
+
}
|
|
8766
|
+
entryParts.push(`/St ${r.start}`);
|
|
8767
|
+
}
|
|
8768
|
+
nums.push(`${idx} << ${entryParts.join(" ")} >>`);
|
|
8769
|
+
}
|
|
8770
|
+
return `<< /Nums [${nums.join(" ")}] >>`;
|
|
8771
|
+
}
|
|
8772
|
+
|
|
8773
|
+
// src/core/pdf-viewer-prefs.ts
|
|
8774
|
+
var PAGE_LAYOUT = {
|
|
8775
|
+
singlePage: "SinglePage",
|
|
8776
|
+
oneColumn: "OneColumn",
|
|
8777
|
+
twoColumnLeft: "TwoColumnLeft",
|
|
8778
|
+
twoColumnRight: "TwoColumnRight",
|
|
8779
|
+
twoPageLeft: "TwoPageLeft",
|
|
8780
|
+
twoPageRight: "TwoPageRight"
|
|
8781
|
+
};
|
|
8782
|
+
var PAGE_MODE = {
|
|
8783
|
+
useNone: "UseNone",
|
|
8784
|
+
useOutlines: "UseOutlines",
|
|
8785
|
+
useThumbs: "UseThumbs",
|
|
8786
|
+
fullScreen: "FullScreen",
|
|
8787
|
+
useOC: "UseOC",
|
|
8788
|
+
useAttachments: "UseAttachments"
|
|
8789
|
+
};
|
|
8790
|
+
var NON_FS_PAGE_MODE = {
|
|
8791
|
+
useNone: "UseNone",
|
|
8792
|
+
useOutlines: "UseOutlines",
|
|
8793
|
+
useThumbs: "UseThumbs",
|
|
8794
|
+
useOC: "UseOC"
|
|
8795
|
+
};
|
|
8796
|
+
function buildViewerPreferences(prefs) {
|
|
8797
|
+
const pageLayout = prefs.pageLayout ? PAGE_LAYOUT[prefs.pageLayout] : void 0;
|
|
8798
|
+
const pageMode = prefs.pageMode ? PAGE_MODE[prefs.pageMode] : void 0;
|
|
8799
|
+
const entries = [];
|
|
8800
|
+
const bool = (key, v) => {
|
|
8801
|
+
if (v !== void 0) entries.push(`/${key} ${v ? "true" : "false"}`);
|
|
8802
|
+
};
|
|
8803
|
+
bool("HideToolbar", prefs.hideToolbar);
|
|
8804
|
+
bool("HideMenubar", prefs.hideMenubar);
|
|
8805
|
+
bool("HideWindowUI", prefs.hideWindowUI);
|
|
8806
|
+
bool("FitWindow", prefs.fitWindow);
|
|
8807
|
+
bool("CenterWindow", prefs.centerWindow);
|
|
8808
|
+
bool("DisplayDocTitle", prefs.displayDocTitle);
|
|
8809
|
+
if (prefs.nonFullScreenPageMode) {
|
|
8810
|
+
entries.push(`/NonFullScreenPageMode /${NON_FS_PAGE_MODE[prefs.nonFullScreenPageMode]}`);
|
|
8811
|
+
}
|
|
8812
|
+
if (prefs.direction) {
|
|
8813
|
+
entries.push(`/Direction /${prefs.direction === "r2l" ? "R2L" : "L2R"}`);
|
|
8814
|
+
}
|
|
8815
|
+
if (prefs.printScaling) {
|
|
8816
|
+
entries.push(`/PrintScaling /${prefs.printScaling === "none" ? "None" : "AppDefault"}`);
|
|
8817
|
+
}
|
|
8818
|
+
const dict = entries.length > 0 ? ` /ViewerPreferences << ${entries.join(" ")} >>` : "";
|
|
8819
|
+
return { pageLayout, pageMode, dict };
|
|
8820
|
+
}
|
|
8821
|
+
|
|
8534
8822
|
// src/core/pdf-document.ts
|
|
8535
8823
|
init_pdf_encrypt();
|
|
8536
8824
|
|
|
8825
|
+
// src/core/pdf-layout-debug.ts
|
|
8826
|
+
function resolveDebugOptions(debug) {
|
|
8827
|
+
if (!debug) return null;
|
|
8828
|
+
if (debug === true) {
|
|
8829
|
+
return { showMargins: true, showContentBounds: true, showCells: true };
|
|
8830
|
+
}
|
|
8831
|
+
const showMargins = debug.showMargins === true;
|
|
8832
|
+
const showContentBounds = debug.showContentBounds === true;
|
|
8833
|
+
const showCells = debug.showCells === true;
|
|
8834
|
+
if (!showMargins && !showContentBounds && !showCells) return null;
|
|
8835
|
+
return { showMargins, showContentBounds, showCells };
|
|
8836
|
+
}
|
|
8837
|
+
var COL_MARGIN = "0 0.45 0.95";
|
|
8838
|
+
var COL_BLOCK = "0.90 0.20 0.30";
|
|
8839
|
+
var COL_CELL = "0 0.62 0.30";
|
|
8840
|
+
function rectOp(x, y, w, h, color, lineW) {
|
|
8841
|
+
return `q ${color} RG ${fmtNum(lineW)} w ${fmtNum(x)} ${fmtNum(y)} ${fmtNum(w)} ${fmtNum(h)} re S Q`;
|
|
8842
|
+
}
|
|
8843
|
+
function marginBoxOps(pgW, pgH, mg, footerH) {
|
|
8844
|
+
const x = mg.l;
|
|
8845
|
+
const yBottom = mg.b + footerH;
|
|
8846
|
+
const w = pgW - mg.l - mg.r;
|
|
8847
|
+
const h = pgH - mg.t - yBottom;
|
|
8848
|
+
return rectOp(x, yBottom, w, h, COL_MARGIN, 0.5);
|
|
8849
|
+
}
|
|
8850
|
+
function blockBoundsOps(x, width, yTop, yBottom) {
|
|
8851
|
+
const h = yTop - yBottom;
|
|
8852
|
+
if (h <= 0) return "";
|
|
8853
|
+
return rectOp(x, yBottom, width, h, COL_BLOCK, 0.4);
|
|
8854
|
+
}
|
|
8855
|
+
function tableCellOps(plan, slice, yTop) {
|
|
8856
|
+
const drawCaption = slice ? slice.drawCaption : true;
|
|
8857
|
+
const drawHeader = slice ? slice.drawHeader : true;
|
|
8858
|
+
const fromRow = slice ? slice.fromRow : 0;
|
|
8859
|
+
const toRow = slice ? slice.toRow : plan.rowHeights.length;
|
|
8860
|
+
const parts = [];
|
|
8861
|
+
let y = yTop - (drawCaption ? plan.captionHeight : 0);
|
|
8862
|
+
if (drawHeader) {
|
|
8863
|
+
const hBottom = y - plan.headerHeight;
|
|
8864
|
+
for (let c = 0; c < plan.cx.length; c++) {
|
|
8865
|
+
parts.push(rectOp(plan.cx[c], hBottom, plan.cwi[c], plan.headerHeight, COL_CELL, 0.3));
|
|
8866
|
+
}
|
|
8867
|
+
y = hBottom;
|
|
8868
|
+
}
|
|
8869
|
+
for (let r = fromRow; r < toRow; r++) {
|
|
8870
|
+
const rh = plan.rowHeights[r];
|
|
8871
|
+
const rBottom = y - rh;
|
|
8872
|
+
for (let c = 0; c < plan.cx.length; c++) {
|
|
8873
|
+
parts.push(rectOp(plan.cx[c], rBottom, plan.cwi[c], rh, COL_CELL, 0.3));
|
|
8874
|
+
}
|
|
8875
|
+
y = rBottom;
|
|
8876
|
+
}
|
|
8877
|
+
return parts.join("\n");
|
|
8878
|
+
}
|
|
8879
|
+
|
|
8537
8880
|
// src/core/pdf-form.ts
|
|
8538
8881
|
var DEFAULT_FIELD_HEIGHT_TEXT = 22;
|
|
8539
8882
|
var DEFAULT_FIELD_HEIGHT_MULTILINE = 60;
|
|
@@ -10668,6 +11011,51 @@ function getNum(attrs, name, def = 0) {
|
|
|
10668
11011
|
const v = getAttr(attrs, name);
|
|
10669
11012
|
return v !== void 0 ? +v : def;
|
|
10670
11013
|
}
|
|
11014
|
+
function getLen(attrs, name) {
|
|
11015
|
+
const v = getAttr(attrs, name);
|
|
11016
|
+
if (v === void 0) return void 0;
|
|
11017
|
+
const n2 = parseFloat(v);
|
|
11018
|
+
return Number.isFinite(n2) ? n2 : void 0;
|
|
11019
|
+
}
|
|
11020
|
+
function decodeSvgEntities(s) {
|
|
11021
|
+
return s.replace(/&(#x[0-9a-fA-F]+|#[0-9]+|[a-zA-Z]+);/g, (_m, ent) => {
|
|
11022
|
+
if (ent[0] === "#") {
|
|
11023
|
+
const cp = ent[1] === "x" || ent[1] === "X" ? parseInt(ent.slice(2), 16) : parseInt(ent.slice(1), 10);
|
|
11024
|
+
return Number.isFinite(cp) && cp >= 0 && cp <= 1114111 ? String.fromCodePoint(cp) : "";
|
|
11025
|
+
}
|
|
11026
|
+
switch (ent) {
|
|
11027
|
+
case "amp":
|
|
11028
|
+
return "&";
|
|
11029
|
+
case "lt":
|
|
11030
|
+
return "<";
|
|
11031
|
+
case "gt":
|
|
11032
|
+
return ">";
|
|
11033
|
+
case "quot":
|
|
11034
|
+
return '"';
|
|
11035
|
+
case "apos":
|
|
11036
|
+
return "'";
|
|
11037
|
+
case "nbsp":
|
|
11038
|
+
return "\xA0";
|
|
11039
|
+
default:
|
|
11040
|
+
return "";
|
|
11041
|
+
}
|
|
11042
|
+
});
|
|
11043
|
+
}
|
|
11044
|
+
function stripSvgTags(raw) {
|
|
11045
|
+
let out = "";
|
|
11046
|
+
let inTag = false;
|
|
11047
|
+
for (const ch2 of raw) {
|
|
11048
|
+
if (ch2 === "<") inTag = true;
|
|
11049
|
+
else if (ch2 === ">") inTag = false;
|
|
11050
|
+
else if (!inTag) out += ch2;
|
|
11051
|
+
}
|
|
11052
|
+
return out;
|
|
11053
|
+
}
|
|
11054
|
+
function sanitizeSvgText(raw) {
|
|
11055
|
+
const noTags = stripSvgTags(raw);
|
|
11056
|
+
const decoded = decodeSvgEntities(noTags);
|
|
11057
|
+
return decoded.replace(/[\u0000-\u001F\u007F-\u009F]+/g, " ").replace(/\s+/g, " ").trim();
|
|
11058
|
+
}
|
|
10671
11059
|
function rectToPath(x, y, w, h, rx, ry) {
|
|
10672
11060
|
if (w <= 0 || h <= 0) return "";
|
|
10673
11061
|
if (rx <= 0 && ry <= 0) {
|
|
@@ -10749,7 +11137,60 @@ function parseSvgMarkup(svg) {
|
|
|
10749
11137
|
});
|
|
10750
11138
|
}
|
|
10751
11139
|
}
|
|
10752
|
-
|
|
11140
|
+
const textElements = parseSvgText(svg);
|
|
11141
|
+
return { elements, textElements, viewBox };
|
|
11142
|
+
}
|
|
11143
|
+
function parseSvgText(svg) {
|
|
11144
|
+
const out = [];
|
|
11145
|
+
const textRe = /<text\b([^>]*)>([\s\S]*?)<\/text>/gi;
|
|
11146
|
+
let tm;
|
|
11147
|
+
while ((tm = textRe.exec(svg)) !== null) {
|
|
11148
|
+
const attrs = tm[1];
|
|
11149
|
+
const inner = tm[2];
|
|
11150
|
+
const baseX = getLen(attrs, "x") ?? 0;
|
|
11151
|
+
const baseY = getLen(attrs, "y") ?? 0;
|
|
11152
|
+
const baseSize = getLen(attrs, "font-size") ?? 16;
|
|
11153
|
+
const baseFillRaw = getAttr(attrs, "fill");
|
|
11154
|
+
const baseFill = baseFillRaw ? normalizeSvgColor(baseFillRaw) : void 0;
|
|
11155
|
+
const baseAnchor = normalizeAnchor(getAttr(attrs, "text-anchor"));
|
|
11156
|
+
const tspanRe = /<tspan\b([^>]*)>([\s\S]*?)<\/tspan>/gi;
|
|
11157
|
+
let sm;
|
|
11158
|
+
let penX = baseX;
|
|
11159
|
+
let penY = baseY;
|
|
11160
|
+
let found = false;
|
|
11161
|
+
while ((sm = tspanRe.exec(inner)) !== null) {
|
|
11162
|
+
found = true;
|
|
11163
|
+
const sa = sm[1];
|
|
11164
|
+
const text = sanitizeSvgText(sm[2]);
|
|
11165
|
+
const ax = getLen(sa, "x");
|
|
11166
|
+
const ay = getLen(sa, "y");
|
|
11167
|
+
const adx = getLen(sa, "dx");
|
|
11168
|
+
const ady = getLen(sa, "dy");
|
|
11169
|
+
penX = ax !== void 0 ? ax : penX + (adx ?? 0);
|
|
11170
|
+
penY = ay !== void 0 ? ay : penY + (ady ?? 0);
|
|
11171
|
+
if (!text) continue;
|
|
11172
|
+
const fillRaw = getAttr(sa, "fill");
|
|
11173
|
+
out.push({
|
|
11174
|
+
text,
|
|
11175
|
+
x: penX,
|
|
11176
|
+
y: penY,
|
|
11177
|
+
fontSize: getLen(sa, "font-size") ?? baseSize,
|
|
11178
|
+
fill: fillRaw ? normalizeSvgColor(fillRaw) : baseFill,
|
|
11179
|
+
anchor: normalizeAnchor(getAttr(sa, "text-anchor")) ?? baseAnchor
|
|
11180
|
+
});
|
|
11181
|
+
}
|
|
11182
|
+
if (!found) {
|
|
11183
|
+
const text = sanitizeSvgText(inner);
|
|
11184
|
+
if (text) {
|
|
11185
|
+
out.push({ text, x: baseX, y: baseY, fontSize: baseSize, fill: baseFill, anchor: baseAnchor });
|
|
11186
|
+
}
|
|
11187
|
+
}
|
|
11188
|
+
}
|
|
11189
|
+
return out;
|
|
11190
|
+
}
|
|
11191
|
+
function normalizeAnchor(v) {
|
|
11192
|
+
if (v === "middle" || v === "end" || v === "start") return v;
|
|
11193
|
+
return void 0;
|
|
10753
11194
|
}
|
|
10754
11195
|
var fn = (n2) => n2.toFixed(2);
|
|
10755
11196
|
function resolveColor(color, fallback) {
|
|
@@ -10785,36 +11226,62 @@ function buildPathOps(segments, fillRgb, strokeRgb, strokeWidth) {
|
|
|
10785
11226
|
ops.push("Q");
|
|
10786
11227
|
return ops.join("\n");
|
|
10787
11228
|
}
|
|
10788
|
-
function renderSvg(data, x, y, w, h, options) {
|
|
11229
|
+
function renderSvg(data, x, y, w, h, options, enc) {
|
|
10789
11230
|
if (!data || w <= 0 || h <= 0) return "";
|
|
10790
11231
|
const isSvgMarkup = data.trimStart().charAt(0) === "<";
|
|
10791
11232
|
let vb;
|
|
10792
11233
|
let elements;
|
|
11234
|
+
let textElements = [];
|
|
10793
11235
|
if (isSvgMarkup) {
|
|
10794
11236
|
const parsed = parseSvgMarkup(data);
|
|
10795
11237
|
vb = options?.viewBox ?? parsed.viewBox ?? [0, 0, w, h];
|
|
10796
11238
|
elements = parsed.elements;
|
|
11239
|
+
textElements = parsed.textElements;
|
|
10797
11240
|
} else {
|
|
10798
11241
|
vb = options?.viewBox ?? [0, 0, w, h];
|
|
10799
11242
|
elements = [{ pathData: data }];
|
|
10800
11243
|
}
|
|
10801
|
-
if (elements.length === 0 || vb[2] <= 0 || vb[3] <= 0) return "";
|
|
11244
|
+
if (elements.length === 0 && textElements.length === 0 || vb[2] <= 0 || vb[3] <= 0) return "";
|
|
10802
11245
|
const sx = w / vb[2];
|
|
10803
11246
|
const sy = h / vb[3];
|
|
10804
|
-
const
|
|
11247
|
+
const tx2 = x - vb[0] * sx;
|
|
11248
|
+
const ty = y + vb[1] * sy;
|
|
11249
|
+
const cmOp = `${fn(sx)} 0 0 ${fn(-sy)} ${fn(tx2)} ${fn(ty)} cm`;
|
|
10805
11250
|
const defaultFill = resolveColor(options?.fill, "0 0 0");
|
|
10806
11251
|
const defaultStroke = resolveColor(options?.stroke, null);
|
|
10807
11252
|
const defaultSw = options?.strokeWidth ?? 1;
|
|
10808
|
-
const
|
|
11253
|
+
const shapeOps = [];
|
|
10809
11254
|
for (const elem of elements) {
|
|
10810
11255
|
const segments = parseSvgPath(elem.pathData);
|
|
10811
11256
|
if (segments.length === 0) continue;
|
|
10812
11257
|
const fillRgb = resolveColor(elem.fill, defaultFill);
|
|
10813
11258
|
const strokeRgb = resolveColor(elem.stroke, defaultStroke);
|
|
10814
11259
|
const sw = elem.strokeWidth ?? defaultSw;
|
|
10815
|
-
|
|
10816
|
-
}
|
|
10817
|
-
|
|
11260
|
+
shapeOps.push(buildPathOps(segments, fillRgb, strokeRgb, sw));
|
|
11261
|
+
}
|
|
11262
|
+
const textOps = [];
|
|
11263
|
+
if (enc && textElements.length > 0) {
|
|
11264
|
+
for (const t of textElements) {
|
|
11265
|
+
const szPdf = t.fontSize * sy;
|
|
11266
|
+
if (szPdf <= 0 || !t.text) continue;
|
|
11267
|
+
const pdfBaseX = t.x * sx + tx2;
|
|
11268
|
+
const pdfBaseY = -sy * t.y + ty;
|
|
11269
|
+
const width = enc.tw(t.text, szPdf);
|
|
11270
|
+
const anchorOffset = t.anchor === "middle" ? width / 2 : t.anchor === "end" ? width : 0;
|
|
11271
|
+
const pdfX = pdfBaseX - anchorOffset;
|
|
11272
|
+
const fillRgb = resolveColor(t.fill, "0 0 0");
|
|
11273
|
+
textOps.push("q");
|
|
11274
|
+
if (fillRgb) textOps.push(`${fillRgb} rg`);
|
|
11275
|
+
textOps.push(txt(t.text, pdfX, pdfBaseY, enc.f1, szPdf, enc));
|
|
11276
|
+
textOps.push("Q");
|
|
11277
|
+
}
|
|
11278
|
+
}
|
|
11279
|
+
if (shapeOps.length === 0 && textOps.length === 0) return "";
|
|
11280
|
+
const allOps = [];
|
|
11281
|
+
if (shapeOps.length > 0) {
|
|
11282
|
+
allOps.push("q", cmOp, ...shapeOps, "Q");
|
|
11283
|
+
}
|
|
11284
|
+
allOps.push(...textOps);
|
|
10818
11285
|
return allOps.join("\n");
|
|
10819
11286
|
}
|
|
10820
11287
|
|
|
@@ -11022,23 +11489,36 @@ function renderList(block, y, enc, mgL, cw, tagCtx, documentChildren) {
|
|
|
11022
11489
|
const sz = block.fontSize ?? DEFAULT_LIST_SIZE;
|
|
11023
11490
|
const lineH = sz * DEFAULT_LINE_HEIGHT;
|
|
11024
11491
|
const color = "0.216 0.255 0.318";
|
|
11025
|
-
const availW = cw - LIST_INDENT - BULLET_MARK_WIDTH;
|
|
11026
11492
|
ops.push(`${color} rg`);
|
|
11027
|
-
const
|
|
11028
|
-
|
|
11029
|
-
|
|
11030
|
-
|
|
11031
|
-
|
|
11493
|
+
const root = renderListLevel(block.items, block.style, 0, y, sz, lineH, enc, mgL, cw, tagCtx);
|
|
11494
|
+
ops.push(...root.ops);
|
|
11495
|
+
if (tagCtx?.tagged && root.struct && root.struct.children.length > 0) {
|
|
11496
|
+
documentChildren.push(root.struct);
|
|
11497
|
+
}
|
|
11498
|
+
return { ops, y: root.y };
|
|
11499
|
+
}
|
|
11500
|
+
function renderListLevel(items, style, depth, y, sz, lineH, enc, mgL, cw, tagCtx) {
|
|
11501
|
+
const ops = [];
|
|
11502
|
+
const indent = LIST_INDENT * (depth + 1);
|
|
11503
|
+
const markerX = mgL + indent;
|
|
11504
|
+
const xOffset = markerX + BULLET_MARK_WIDTH;
|
|
11505
|
+
const availW = cw - indent - BULLET_MARK_WIDTH;
|
|
11506
|
+
const levelChildren = [];
|
|
11507
|
+
for (let idx = 0; idx < items.length; idx++) {
|
|
11508
|
+
const entry = items[idx];
|
|
11509
|
+
const text = typeof entry === "string" ? entry : entry.text;
|
|
11510
|
+
const children = typeof entry === "string" ? void 0 : entry.items;
|
|
11511
|
+
const marker = style === "bullet" ? "\u2022" : `${idx + 1}.`;
|
|
11512
|
+
const lines = wrapText(text, availW, sz, enc);
|
|
11032
11513
|
const liChildren = [];
|
|
11033
11514
|
if (tagCtx?.tagged) {
|
|
11034
11515
|
const mcid = tagCtx.mcidAlloc.next(tagCtx.pageObjNum);
|
|
11035
11516
|
liChildren.push({ mcid, pageObjNum: tagCtx.pageObjNum });
|
|
11036
|
-
ops.push(txtTagged(marker,
|
|
11517
|
+
ops.push(txtTagged(marker, markerX, y - sz, enc.f1, sz, enc, mcid));
|
|
11037
11518
|
} else {
|
|
11038
|
-
ops.push(txt(marker,
|
|
11519
|
+
ops.push(txt(marker, markerX, y - sz, enc.f1, sz, enc));
|
|
11039
11520
|
}
|
|
11040
11521
|
for (let li = 0; li < lines.length; li++) {
|
|
11041
|
-
const xOffset = mgL + LIST_INDENT + BULLET_MARK_WIDTH;
|
|
11042
11522
|
if (li === 0) {
|
|
11043
11523
|
if (tagCtx?.tagged) {
|
|
11044
11524
|
const mcid = tagCtx.mcidAlloc.next(tagCtx.pageObjNum);
|
|
@@ -11059,14 +11539,20 @@ function renderList(block, y, enc, mgL, cw, tagCtx, documentChildren) {
|
|
|
11059
11539
|
}
|
|
11060
11540
|
}
|
|
11061
11541
|
y -= lineH + LIST_ITEM_SPACING;
|
|
11542
|
+
if (children && children.length > 0) {
|
|
11543
|
+
const sub = renderListLevel(children, style, depth + 1, y, sz, lineH, enc, mgL, cw, tagCtx);
|
|
11544
|
+
ops.push(...sub.ops);
|
|
11545
|
+
y = sub.y;
|
|
11546
|
+
if (tagCtx?.tagged && sub.struct && sub.struct.children.length > 0) {
|
|
11547
|
+
liChildren.push(sub.struct);
|
|
11548
|
+
}
|
|
11549
|
+
}
|
|
11062
11550
|
if (tagCtx?.tagged && liChildren.length > 0) {
|
|
11063
|
-
|
|
11551
|
+
levelChildren.push({ type: "LI", children: liChildren });
|
|
11064
11552
|
}
|
|
11065
11553
|
}
|
|
11066
|
-
|
|
11067
|
-
|
|
11068
|
-
}
|
|
11069
|
-
return { ops, y };
|
|
11554
|
+
const struct = tagCtx?.tagged ? { type: "L", children: levelChildren } : void 0;
|
|
11555
|
+
return { ops, y, struct };
|
|
11070
11556
|
}
|
|
11071
11557
|
var DEFAULT_ZEBRA_COLOR = "0.969 0.973 0.984";
|
|
11072
11558
|
var CAPTION_FONT_SIZE = 9;
|
|
@@ -11101,7 +11587,7 @@ function planTable(block, enc, mgL, cw) {
|
|
|
11101
11587
|
headerLines.push(lines);
|
|
11102
11588
|
if (lines.length > headerMaxLines) headerMaxLines = lines.length;
|
|
11103
11589
|
}
|
|
11104
|
-
const headerHeight = headerMaxLines === 1 ? TH_H : Math.max(TH_H, headerMaxLines * fs.th * TABLE_LINE_HEIGHT +
|
|
11590
|
+
const headerHeight = headerMaxLines === 1 ? TH_H : Math.max(TH_H, headerMaxLines * fs.th * TABLE_LINE_HEIGHT + pad + 2);
|
|
11105
11591
|
const rowLines = [];
|
|
11106
11592
|
const rowHeights = [];
|
|
11107
11593
|
for (let r = 0; r < block.rows.length; r++) {
|
|
@@ -11114,7 +11600,7 @@ function planTable(block, enc, mgL, cw) {
|
|
|
11114
11600
|
if (lines.length > maxLines) maxLines = lines.length;
|
|
11115
11601
|
}
|
|
11116
11602
|
rowLines.push(cells);
|
|
11117
|
-
const h = maxLines === 1 ? minRowH : Math.max(minRowH, maxLines * fs.td * TABLE_LINE_HEIGHT +
|
|
11603
|
+
const h = maxLines === 1 ? minRowH : Math.max(minRowH, maxLines * fs.td * TABLE_LINE_HEIGHT + pad + 2);
|
|
11118
11604
|
rowHeights.push(h);
|
|
11119
11605
|
}
|
|
11120
11606
|
const captionLines = block.caption ? wrapText(block.caption, cw, CAPTION_FONT_SIZE, enc) : [];
|
|
@@ -11154,6 +11640,30 @@ function renderTable(block, y, enc, mgL, mgR, pgW, cw, tagCtx, documentChildren,
|
|
|
11154
11640
|
const clip = block.clipCells !== false;
|
|
11155
11641
|
const zebraColor = resolveZebraColor(block.zebra);
|
|
11156
11642
|
const wrapMode = block.wrap ?? "auto";
|
|
11643
|
+
const borders = block.cellBorders;
|
|
11644
|
+
const borderSides = borders ? {
|
|
11645
|
+
top: borders.all === true || borders.top === true,
|
|
11646
|
+
right: borders.all === true || borders.right === true,
|
|
11647
|
+
bottom: borders.all === true || borders.bottom === true,
|
|
11648
|
+
left: borders.all === true || borders.left === true
|
|
11649
|
+
} : null;
|
|
11650
|
+
const borderColor = borders?.color ? parseColor(borders.color) : "0.8 0.8 0.8";
|
|
11651
|
+
const borderWidth = borders?.width ?? 0.5;
|
|
11652
|
+
const borderDash = borders?.style === "dashed" ? "[3] 0 d" : borders?.style === "dotted" ? `[${fmtNum(borderWidth)} ${fmtNum(borderWidth * 2)}] 0 d` : null;
|
|
11653
|
+
const cellBorderOps = (cellX, cellW, top, h) => {
|
|
11654
|
+
if (!borderSides || !borderSides.top && !borderSides.right && !borderSides.bottom && !borderSides.left) {
|
|
11655
|
+
return [];
|
|
11656
|
+
}
|
|
11657
|
+
const o = [];
|
|
11658
|
+
o.push(`${fmtNum(borderWidth)} w ${borderColor} RG${borderDash ? " " + borderDash : ""}`);
|
|
11659
|
+
const x0 = cellX, x1 = cellX + cellW, y0 = top - h, y1 = top;
|
|
11660
|
+
if (borderSides.top) o.push(`${fmtNum(x0)} ${fmtNum(y1)} m ${fmtNum(x1)} ${fmtNum(y1)} l S`);
|
|
11661
|
+
if (borderSides.bottom) o.push(`${fmtNum(x0)} ${fmtNum(y0)} m ${fmtNum(x1)} ${fmtNum(y0)} l S`);
|
|
11662
|
+
if (borderSides.left) o.push(`${fmtNum(x0)} ${fmtNum(y0)} m ${fmtNum(x0)} ${fmtNum(y1)} l S`);
|
|
11663
|
+
if (borderSides.right) o.push(`${fmtNum(x1)} ${fmtNum(y0)} m ${fmtNum(x1)} ${fmtNum(y1)} l S`);
|
|
11664
|
+
if (borderDash) o.push("[] 0 d");
|
|
11665
|
+
return o;
|
|
11666
|
+
};
|
|
11157
11667
|
const clipCell = (op, i, top, h) => clip ? `q ${fmtNum(cx[i])} ${fmtNum(top - h)} ${fmtNum(cwi[i])} ${fmtNum(h)} re W n
|
|
11158
11668
|
${op}
|
|
11159
11669
|
Q` : op;
|
|
@@ -11162,9 +11672,21 @@ Q` : op;
|
|
|
11162
11672
|
const out = [];
|
|
11163
11673
|
const lineH = sz * TABLE_LINE_HEIGHT;
|
|
11164
11674
|
const padBottom = isHeader ? HEADER_PAD_BOTTOM : CELL_PAD_BOTTOM;
|
|
11675
|
+
const vAlign = col.vAlign ?? block.cellVAlign;
|
|
11165
11676
|
for (let li = 0; li < lines.length; li++) {
|
|
11166
11677
|
const t = lines.length === 1 && wrapMode === "never" ? truncate(lines[li], isHeader && col.mxH !== void 0 ? col.mxH : col.mx) : lines[li];
|
|
11167
|
-
|
|
11678
|
+
let baselineY;
|
|
11679
|
+
if (vAlign) {
|
|
11680
|
+
const blockH = lines.length * lineH;
|
|
11681
|
+
let offset;
|
|
11682
|
+
if (vAlign === "top") offset = pad;
|
|
11683
|
+
else if (vAlign === "bottom") offset = rowH - blockH - pad;
|
|
11684
|
+
else offset = (rowH - blockH) / 2;
|
|
11685
|
+
if (offset < pad) offset = pad;
|
|
11686
|
+
baselineY = rowTop - offset - li * lineH - sz + sz * 0.2;
|
|
11687
|
+
} else {
|
|
11688
|
+
baselineY = lines.length === 1 ? rowTop - rowH + padBottom : rowTop - pad - sz + sz * 0.2 - li * lineH;
|
|
11689
|
+
}
|
|
11168
11690
|
let op;
|
|
11169
11691
|
if (mcRefsOut !== null && tagCtx?.tagged) {
|
|
11170
11692
|
const mcid = tagCtx.mcidAlloc.next(tagCtx.pageObjNum);
|
|
@@ -11219,6 +11741,7 @@ Q` : op;
|
|
|
11219
11741
|
for (let i = 0; i < block.headers.length && i < columns.length; i++) {
|
|
11220
11742
|
const cellRefs = tagCtx?.tagged ? [] : null;
|
|
11221
11743
|
ops.push(...emitCell(headerLines[i] ?? [""], i, y, headerHeight, enc.f2, fs.th, cellRefs, true));
|
|
11744
|
+
ops.push(...cellBorderOps(cx[i], cwi[i], y, headerHeight));
|
|
11222
11745
|
if (cellRefs && cellRefs.length > 0) {
|
|
11223
11746
|
thChildren.push({ type: "TH", children: cellRefs });
|
|
11224
11747
|
}
|
|
@@ -11246,6 +11769,7 @@ Q` : op;
|
|
|
11246
11769
|
ops.push(`${color} rg`);
|
|
11247
11770
|
const cellRefs = tagCtx?.tagged ? [] : null;
|
|
11248
11771
|
ops.push(...emitCell(cells[i] ?? [""], i, y, rowH, font, fs.td, cellRefs, false));
|
|
11772
|
+
ops.push(...cellBorderOps(cx[i], cwi[i], y, rowH));
|
|
11249
11773
|
if (cellRefs && cellRefs.length > 0) {
|
|
11250
11774
|
tdChildren.push({ type: "TD", children: cellRefs });
|
|
11251
11775
|
}
|
|
@@ -11502,7 +12026,7 @@ function renderBarcodeBlock(block, y, mgL, cw, tagCtx, documentChildren) {
|
|
|
11502
12026
|
y = by - 6;
|
|
11503
12027
|
return { ops, y };
|
|
11504
12028
|
}
|
|
11505
|
-
function renderSvgBlock(block, y, mgL, cw, tagCtx, documentChildren) {
|
|
12029
|
+
function renderSvgBlock(block, y, mgL, cw, tagCtx, documentChildren, enc) {
|
|
11506
12030
|
const ops = [];
|
|
11507
12031
|
const w = block.width ?? DEFAULT_SVG_SIZE;
|
|
11508
12032
|
const h = block.height ?? DEFAULT_SVG_SIZE;
|
|
@@ -11518,7 +12042,7 @@ function renderSvgBlock(block, y, mgL, cw, tagCtx, documentChildren) {
|
|
|
11518
12042
|
stroke: block.stroke,
|
|
11519
12043
|
strokeWidth: block.strokeWidth,
|
|
11520
12044
|
viewBox: block.viewBox
|
|
11521
|
-
});
|
|
12045
|
+
}, enc);
|
|
11522
12046
|
if (svgOps) {
|
|
11523
12047
|
if (tagCtx?.tagged) {
|
|
11524
12048
|
const mcid = tagCtx.mcidAlloc.next(tagCtx.pageObjNum);
|
|
@@ -11617,13 +12141,20 @@ function estimateBlockHeight(block, enc, cw, headings) {
|
|
|
11617
12141
|
case "list": {
|
|
11618
12142
|
const sz = block.fontSize ?? DEFAULT_LIST_SIZE;
|
|
11619
12143
|
const lineH = sz * DEFAULT_LINE_HEIGHT;
|
|
11620
|
-
const
|
|
11621
|
-
|
|
11622
|
-
|
|
11623
|
-
|
|
11624
|
-
|
|
11625
|
-
|
|
11626
|
-
|
|
12144
|
+
const measureLevel = (items, depth) => {
|
|
12145
|
+
const availW = cw - LIST_INDENT * (depth + 1) - BULLET_MARK_WIDTH;
|
|
12146
|
+
let acc = 0;
|
|
12147
|
+
for (const entry of items) {
|
|
12148
|
+
const text = typeof entry === "string" ? entry : entry.text;
|
|
12149
|
+
const lines = wrapText(text, availW, sz, enc);
|
|
12150
|
+
acc += lineH + (lines.length - 1) * lineH + LIST_ITEM_SPACING;
|
|
12151
|
+
if (typeof entry !== "string" && entry.items && entry.items.length > 0) {
|
|
12152
|
+
acc += measureLevel(entry.items, depth + 1);
|
|
12153
|
+
}
|
|
12154
|
+
}
|
|
12155
|
+
return acc;
|
|
12156
|
+
};
|
|
12157
|
+
return measureLevel(block.items, 0);
|
|
11627
12158
|
}
|
|
11628
12159
|
case "table": {
|
|
11629
12160
|
return TH_H + block.rows.length * ROW_H + 6;
|
|
@@ -11693,6 +12224,7 @@ function assembleDocumentParts(params, layoutOptions) {
|
|
|
11693
12224
|
if (watermarkOpts) {
|
|
11694
12225
|
validateWatermark(watermarkOpts, layout?.tagged);
|
|
11695
12226
|
}
|
|
12227
|
+
const debugOpts = resolveDebugOptions(layout?.debug);
|
|
11696
12228
|
const attachments = layout?.attachments;
|
|
11697
12229
|
validateAttachments(attachments, layout?.tagged);
|
|
11698
12230
|
const mcidAlloc = tagged ? createMCIDAllocator() : void 0;
|
|
@@ -11880,6 +12412,10 @@ function assembleDocumentParts(params, layoutOptions) {
|
|
|
11880
12412
|
const tagCtx = tagged && mcidAlloc ? { tagged: true, mcidAlloc, pageObjNum} : void 0;
|
|
11881
12413
|
const ops = [];
|
|
11882
12414
|
let y = pgH - mg.t;
|
|
12415
|
+
const debugOps = [];
|
|
12416
|
+
if (debugOpts?.showMargins) {
|
|
12417
|
+
debugOps.push(marginBoxOps(pgW, pgH, mg, FT_H));
|
|
12418
|
+
}
|
|
11883
12419
|
if (headerTpl) {
|
|
11884
12420
|
const hOps = renderPageTemplate(
|
|
11885
12421
|
headerTpl,
|
|
@@ -11920,6 +12456,7 @@ function assembleDocumentParts(params, layoutOptions) {
|
|
|
11920
12456
|
}
|
|
11921
12457
|
const blocks = pageBlocks[p] ?? [];
|
|
11922
12458
|
for (const block of blocks) {
|
|
12459
|
+
const yBefore = debugOpts ? y : 0;
|
|
11923
12460
|
switch (block.type) {
|
|
11924
12461
|
case "heading": {
|
|
11925
12462
|
if (headingDestIdx < headingDests.length) {
|
|
@@ -12000,7 +12537,7 @@ function assembleDocumentParts(params, layoutOptions) {
|
|
|
12000
12537
|
break;
|
|
12001
12538
|
}
|
|
12002
12539
|
case "svg": {
|
|
12003
|
-
const result = renderSvgBlock(block, y, mg.l, cw, tagCtx, documentChildren);
|
|
12540
|
+
const result = renderSvgBlock(block, y, mg.l, cw, tagCtx, documentChildren, enc);
|
|
12004
12541
|
ops.push(...result.ops);
|
|
12005
12542
|
y = result.y;
|
|
12006
12543
|
break;
|
|
@@ -12012,6 +12549,19 @@ function assembleDocumentParts(params, layoutOptions) {
|
|
|
12012
12549
|
break;
|
|
12013
12550
|
}
|
|
12014
12551
|
}
|
|
12552
|
+
if (debugOpts) {
|
|
12553
|
+
if (debugOpts.showContentBounds) {
|
|
12554
|
+
const bo = blockBoundsOps(mg.l, cw, yBefore, y);
|
|
12555
|
+
if (bo) debugOps.push(bo);
|
|
12556
|
+
}
|
|
12557
|
+
if (debugOpts.showCells) {
|
|
12558
|
+
if (block.type === "__tableSlice") {
|
|
12559
|
+
debugOps.push(tableCellOps(block.slice.plan, block.slice, yBefore));
|
|
12560
|
+
} else if (block.type === "table") {
|
|
12561
|
+
debugOps.push(tableCellOps(planTable(block, enc, mg.l, cw), void 0, yBefore));
|
|
12562
|
+
}
|
|
12563
|
+
}
|
|
12564
|
+
}
|
|
12015
12565
|
}
|
|
12016
12566
|
if (wmState?.foregroundOps) {
|
|
12017
12567
|
ops.push(wmState.foregroundOps);
|
|
@@ -12032,6 +12582,9 @@ function assembleDocumentParts(params, layoutOptions) {
|
|
|
12032
12582
|
documentChildren
|
|
12033
12583
|
);
|
|
12034
12584
|
ops.push(...ftOps);
|
|
12585
|
+
if (debugOps.length > 0) {
|
|
12586
|
+
ops.push(...debugOps);
|
|
12587
|
+
}
|
|
12035
12588
|
pageStreams.push(ops.join("\n"));
|
|
12036
12589
|
}
|
|
12037
12590
|
const annotsByPage = /* @__PURE__ */ new Map();
|
|
@@ -12481,6 +13034,41 @@ function assembleDocumentParts(params, layoutOptions) {
|
|
|
12481
13034
|
totalObjs += efResult.totalObjects;
|
|
12482
13035
|
}
|
|
12483
13036
|
}
|
|
13037
|
+
let pageLabelsStr = "";
|
|
13038
|
+
if (params.pageLabels && params.pageLabels.length > 0) {
|
|
13039
|
+
pageLabelsStr = ` /PageLabels ${buildPageLabelsDict(params.pageLabels, totalPages)}`;
|
|
13040
|
+
}
|
|
13041
|
+
const viewerPrefs = layout?.viewerPreferences ? buildViewerPreferences(layout.viewerPreferences) : void 0;
|
|
13042
|
+
let viewerPrefsStr = viewerPrefs?.dict ?? "";
|
|
13043
|
+
if (viewerPrefs?.pageLayout) {
|
|
13044
|
+
viewerPrefsStr = ` /PageLayout /${viewerPrefs.pageLayout}${viewerPrefsStr}`;
|
|
13045
|
+
}
|
|
13046
|
+
let outlineCatalogStr = "";
|
|
13047
|
+
if (params.outline) {
|
|
13048
|
+
const items = params.outline === "auto" ? autoOutlineFromHeadings(headingDests) : params.outline.map(mapOutlineItem);
|
|
13049
|
+
if (items.length > 0) {
|
|
13050
|
+
const outlineStart = totalObjs + 1;
|
|
13051
|
+
const built = buildOutlineObjects(
|
|
13052
|
+
items,
|
|
13053
|
+
outlineStart,
|
|
13054
|
+
(pageIndex) => pageObjStart + pageIndex * 2,
|
|
13055
|
+
pgH - mg.t,
|
|
13056
|
+
fmtNum,
|
|
13057
|
+
totalPages
|
|
13058
|
+
);
|
|
13059
|
+
for (const [objNum, content] of built.objects) {
|
|
13060
|
+
emitObj(objNum, content);
|
|
13061
|
+
}
|
|
13062
|
+
totalObjs = outlineStart + built.totalObjects - 1;
|
|
13063
|
+
outlineCatalogStr = ` /Outlines ${built.rootObjNum} 0 R`;
|
|
13064
|
+
if (!viewerPrefs?.pageMode) {
|
|
13065
|
+
outlineCatalogStr += " /PageMode /UseOutlines";
|
|
13066
|
+
}
|
|
13067
|
+
}
|
|
13068
|
+
}
|
|
13069
|
+
if (viewerPrefs?.pageMode) {
|
|
13070
|
+
viewerPrefsStr = ` /PageMode /${viewerPrefs.pageMode}${viewerPrefsStr}`;
|
|
13071
|
+
}
|
|
12484
13072
|
let destsStr = "";
|
|
12485
13073
|
if (hasToc && headingDests.length > 0) {
|
|
12486
13074
|
const destEntries = headingDests.map((h) => {
|
|
@@ -12510,7 +13098,7 @@ function assembleDocumentParts(params, layoutOptions) {
|
|
|
12510
13098
|
acroFormStr = ` ${buildAcroFormDict(fieldObjNums, formFontObjNum)}`;
|
|
12511
13099
|
}
|
|
12512
13100
|
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}`;
|
|
13101
|
+
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
13102
|
if (afArrayStr) {
|
|
12515
13103
|
catalogContent += ` /AF [${afArrayStr}] ${embeddedFilesNamesDict}`;
|
|
12516
13104
|
}
|
|
@@ -12532,8 +13120,8 @@ endobj
|
|
|
12532
13120
|
}
|
|
12533
13121
|
}
|
|
12534
13122
|
}
|
|
12535
|
-
} else if (destsStr || acroFormStr) {
|
|
12536
|
-
const catalogContent = `<< /Type /Catalog /Pages 2 0 R${destsStr}${acroFormStr} >>`;
|
|
13123
|
+
} else if (destsStr || acroFormStr || outlineCatalogStr || pageLabelsStr || viewerPrefsStr) {
|
|
13124
|
+
const catalogContent = `<< /Type /Catalog /Pages 2 0 R${destsStr}${acroFormStr}${outlineCatalogStr}${pageLabelsStr}${viewerPrefsStr} >>`;
|
|
12537
13125
|
const oldCatalog = "1 0 obj\n<< /Type /Catalog /Pages 2 0 R >>\nendobj\n\n";
|
|
12538
13126
|
const newCatalog = `1 0 obj
|
|
12539
13127
|
${catalogContent}
|
|
@@ -12559,6 +13147,207 @@ endobj
|
|
|
12559
13147
|
function buildDocumentPDFBytes(params, layoutOptions) {
|
|
12560
13148
|
return toBytes(buildDocumentPDF(params, layoutOptions));
|
|
12561
13149
|
}
|
|
13150
|
+
function mapOutlineItem(item) {
|
|
13151
|
+
return {
|
|
13152
|
+
title: item.title,
|
|
13153
|
+
pageIndex: item.pageIndex,
|
|
13154
|
+
y: item.y,
|
|
13155
|
+
bold: item.bold,
|
|
13156
|
+
italic: item.italic,
|
|
13157
|
+
open: item.open,
|
|
13158
|
+
color: item.color !== void 0 ? parseColor(item.color) : void 0,
|
|
13159
|
+
children: item.children ? item.children.map(mapOutlineItem) : void 0
|
|
13160
|
+
};
|
|
13161
|
+
}
|
|
13162
|
+
function autoOutlineFromHeadings(headings) {
|
|
13163
|
+
const root = [];
|
|
13164
|
+
const stack = [];
|
|
13165
|
+
for (const h of headings) {
|
|
13166
|
+
const node = {
|
|
13167
|
+
title: h.text,
|
|
13168
|
+
pageIndex: h.pageIndex,
|
|
13169
|
+
y: h.y,
|
|
13170
|
+
children: []
|
|
13171
|
+
};
|
|
13172
|
+
while (stack.length > 0 && stack[stack.length - 1].level >= h.level) {
|
|
13173
|
+
stack.pop();
|
|
13174
|
+
}
|
|
13175
|
+
if (stack.length === 0) {
|
|
13176
|
+
root.push(node);
|
|
13177
|
+
} else {
|
|
13178
|
+
stack[stack.length - 1].children.push(node);
|
|
13179
|
+
}
|
|
13180
|
+
stack.push({ level: h.level, children: node.children });
|
|
13181
|
+
}
|
|
13182
|
+
return root;
|
|
13183
|
+
}
|
|
13184
|
+
|
|
13185
|
+
// src/core/pdf-layout-inspect.ts
|
|
13186
|
+
var TITLE_BAND_H = 22 + 12;
|
|
13187
|
+
function inspectDocumentLayout(params, layoutOptions) {
|
|
13188
|
+
if (!params || typeof params !== "object" || !Array.isArray(params.blocks)) {
|
|
13189
|
+
throw new Error("inspectDocumentLayout: params.blocks must be an array");
|
|
13190
|
+
}
|
|
13191
|
+
const layout = layoutOptions ?? params.layout;
|
|
13192
|
+
const pgW = layout?.pageWidth ?? PG_W;
|
|
13193
|
+
const pgH = layout?.pageHeight ?? PG_H;
|
|
13194
|
+
const mg = layout?.margins ?? { ...DEFAULT_MARGINS };
|
|
13195
|
+
const cw = pgW - mg.l - mg.r;
|
|
13196
|
+
const fontEntries = params.fontEntries ? [...params.fontEntries] : [];
|
|
13197
|
+
const tagged = resolvePdfAConfig(layout?.tagged).enabled;
|
|
13198
|
+
const enc = createEncodingContext(fontEntries, tagged, layout?.normalize ?? false);
|
|
13199
|
+
const headerH = layout?.headerTemplate ? HEADER_H : 0;
|
|
13200
|
+
const availableH = pgH - mg.t - mg.b - FT_H - headerH;
|
|
13201
|
+
const pages = [[]];
|
|
13202
|
+
let remainH = availableH;
|
|
13203
|
+
let curY = pgH - mg.t - headerH;
|
|
13204
|
+
if (params.title) {
|
|
13205
|
+
remainH -= TITLE_BAND_H;
|
|
13206
|
+
curY -= TITLE_BAND_H;
|
|
13207
|
+
}
|
|
13208
|
+
const newPage = () => {
|
|
13209
|
+
pages.push([]);
|
|
13210
|
+
remainH = availableH;
|
|
13211
|
+
curY = pgH - mg.t - headerH;
|
|
13212
|
+
};
|
|
13213
|
+
for (const block of params.blocks) {
|
|
13214
|
+
if (block.type === "pageBreak") {
|
|
13215
|
+
newPage();
|
|
13216
|
+
continue;
|
|
13217
|
+
}
|
|
13218
|
+
if (block.type === "table") {
|
|
13219
|
+
const plan = planTable(block, enc, mg.l, cw);
|
|
13220
|
+
const repeatHeader = block.repeatHeader !== false;
|
|
13221
|
+
const totalRows = block.rows.length;
|
|
13222
|
+
if (totalRows === 0) {
|
|
13223
|
+
const totalH = plan.captionHeight + plan.headerHeight + plan.trailerSpacing;
|
|
13224
|
+
if (totalH > remainH && pages[pages.length - 1].length > 0) newPage();
|
|
13225
|
+
pages[pages.length - 1].push({ type: "table", page: pages.length - 1, x: mg.l, top: curY, width: cw, height: totalH });
|
|
13226
|
+
remainH -= totalH;
|
|
13227
|
+
curY -= totalH;
|
|
13228
|
+
continue;
|
|
13229
|
+
}
|
|
13230
|
+
let rowIdx = 0;
|
|
13231
|
+
let isFirstSlice = true;
|
|
13232
|
+
while (rowIdx < totalRows) {
|
|
13233
|
+
const drawCaption = isFirstSlice;
|
|
13234
|
+
const drawHeader = isFirstSlice || repeatHeader;
|
|
13235
|
+
const tCapH = drawCaption ? plan.captionHeight : 0;
|
|
13236
|
+
const tHdrH = drawHeader ? plan.headerHeight : 0;
|
|
13237
|
+
const availableForRows = remainH - tCapH - tHdrH - plan.trailerSpacing;
|
|
13238
|
+
let usedH = 0;
|
|
13239
|
+
let count = 0;
|
|
13240
|
+
while (rowIdx + count < totalRows && usedH + plan.rowHeights[rowIdx + count] <= availableForRows) {
|
|
13241
|
+
usedH += plan.rowHeights[rowIdx + count];
|
|
13242
|
+
count++;
|
|
13243
|
+
}
|
|
13244
|
+
if (count === 0 && pages[pages.length - 1].length > 0) {
|
|
13245
|
+
newPage();
|
|
13246
|
+
continue;
|
|
13247
|
+
}
|
|
13248
|
+
if (count === 0) count = 1;
|
|
13249
|
+
rowIdx += count;
|
|
13250
|
+
const isFinalSlice = rowIdx >= totalRows;
|
|
13251
|
+
const sliceH = tCapH + tHdrH + usedH + (isFinalSlice ? plan.trailerSpacing : 0);
|
|
13252
|
+
pages[pages.length - 1].push({ type: "table", page: pages.length - 1, x: mg.l, top: curY, width: cw, height: sliceH });
|
|
13253
|
+
remainH -= sliceH;
|
|
13254
|
+
curY -= sliceH;
|
|
13255
|
+
isFirstSlice = false;
|
|
13256
|
+
if (!isFinalSlice) newPage();
|
|
13257
|
+
}
|
|
13258
|
+
continue;
|
|
13259
|
+
}
|
|
13260
|
+
const blockH = estimateBlockHeight(block, enc, cw);
|
|
13261
|
+
if (blockH > remainH && pages[pages.length - 1].length > 0) newPage();
|
|
13262
|
+
pages[pages.length - 1].push({ type: block.type, page: pages.length - 1, x: mg.l, top: curY, width: cw, height: blockH });
|
|
13263
|
+
remainH -= blockH;
|
|
13264
|
+
curY -= blockH;
|
|
13265
|
+
}
|
|
13266
|
+
const inspectedPages = pages.map((blocks, index) => ({ index, blocks }));
|
|
13267
|
+
return {
|
|
13268
|
+
pageWidth: pgW,
|
|
13269
|
+
pageHeight: pgH,
|
|
13270
|
+
margins: { t: mg.t, r: mg.r, b: mg.b, l: mg.l },
|
|
13271
|
+
totalPages: Math.max(1, pages.length),
|
|
13272
|
+
pages: inspectedPages
|
|
13273
|
+
};
|
|
13274
|
+
}
|
|
13275
|
+
|
|
13276
|
+
// src/core/pdf-annot-markup.ts
|
|
13277
|
+
var SUBTYPE = {
|
|
13278
|
+
text: "Text",
|
|
13279
|
+
highlight: "Highlight",
|
|
13280
|
+
underline: "Underline",
|
|
13281
|
+
strikeout: "StrikeOut",
|
|
13282
|
+
squiggly: "Squiggly",
|
|
13283
|
+
square: "Square",
|
|
13284
|
+
circle: "Circle",
|
|
13285
|
+
line: "Line",
|
|
13286
|
+
freetext: "FreeText"
|
|
13287
|
+
};
|
|
13288
|
+
function rectStr(r) {
|
|
13289
|
+
return `[${fmtNum(r[0])} ${fmtNum(r[1])} ${fmtNum(r[2])} ${fmtNum(r[3])}]`;
|
|
13290
|
+
}
|
|
13291
|
+
function quadFromRect(r) {
|
|
13292
|
+
const [x1, y1, x2, y2] = r;
|
|
13293
|
+
return [x1, y2, x2, y2, x1, y1, x2, y1];
|
|
13294
|
+
}
|
|
13295
|
+
function commonEntries(a) {
|
|
13296
|
+
const parts = [];
|
|
13297
|
+
if (a.contents !== void 0) parts.push(`/Contents ${encodePdfTextString(a.contents)}`);
|
|
13298
|
+
if (a.title !== void 0) parts.push(`/T ${encodePdfTextString(a.title)}`);
|
|
13299
|
+
if (a.color !== void 0) parts.push(`/C [${parseColor(a.color)}]`);
|
|
13300
|
+
if (a.opacity !== void 0) parts.push(`/CA ${fmtNum(a.opacity)}`);
|
|
13301
|
+
if (a.modified !== void 0) parts.push(`/M ${encodePdfTextString(a.modified)}`);
|
|
13302
|
+
parts.push(`/F ${a.flags ?? 4}`);
|
|
13303
|
+
return parts.join(" ");
|
|
13304
|
+
}
|
|
13305
|
+
function buildAnnotation(annot, objNum) {
|
|
13306
|
+
return `${objNum} 0 obj
|
|
13307
|
+
${buildAnnotationBody(annot)}
|
|
13308
|
+
endobj`;
|
|
13309
|
+
}
|
|
13310
|
+
function buildAnnotationBody(annot) {
|
|
13311
|
+
const entries = [
|
|
13312
|
+
`/Type /Annot`,
|
|
13313
|
+
`/Subtype /${SUBTYPE[annot.type]}`,
|
|
13314
|
+
`/Rect ${rectStr(annot.rect)}`,
|
|
13315
|
+
commonEntries(annot)
|
|
13316
|
+
];
|
|
13317
|
+
switch (annot.type) {
|
|
13318
|
+
case "text": {
|
|
13319
|
+
if (annot.open !== void 0) entries.push(`/Open ${annot.open ? "true" : "false"}`);
|
|
13320
|
+
if (annot.icon !== void 0) entries.push(`/Name /${annot.icon}`);
|
|
13321
|
+
break;
|
|
13322
|
+
}
|
|
13323
|
+
case "highlight":
|
|
13324
|
+
case "underline":
|
|
13325
|
+
case "strikeout":
|
|
13326
|
+
case "squiggly": {
|
|
13327
|
+
const quad = annot.quadPoints && annot.quadPoints.length >= 8 ? annot.quadPoints : quadFromRect(annot.rect);
|
|
13328
|
+
entries.push(`/QuadPoints [${quad.map(fmtNum).join(" ")}]`);
|
|
13329
|
+
break;
|
|
13330
|
+
}
|
|
13331
|
+
case "square":
|
|
13332
|
+
case "circle": {
|
|
13333
|
+
if (annot.interiorColor !== void 0) entries.push(`/IC [${parseColor(annot.interiorColor)}]`);
|
|
13334
|
+
entries.push(`/BS << /W ${fmtNum(annot.borderWidth ?? 1)} >>`);
|
|
13335
|
+
break;
|
|
13336
|
+
}
|
|
13337
|
+
case "line": {
|
|
13338
|
+
entries.push(`/L [${fmtNum(annot.start[0])} ${fmtNum(annot.start[1])} ${fmtNum(annot.end[0])} ${fmtNum(annot.end[1])}]`);
|
|
13339
|
+
entries.push(`/BS << /W ${fmtNum(annot.borderWidth ?? 1)} >>`);
|
|
13340
|
+
break;
|
|
13341
|
+
}
|
|
13342
|
+
case "freetext": {
|
|
13343
|
+
const sz = annot.fontSize ?? 12;
|
|
13344
|
+
const col = annot.color !== void 0 ? parseColor(annot.color) : "0 0 0";
|
|
13345
|
+
entries.push(`/DA (/Helv ${fmtNum(sz)} Tf ${col} rg)`);
|
|
13346
|
+
break;
|
|
13347
|
+
}
|
|
13348
|
+
}
|
|
13349
|
+
return `<< ${entries.filter(Boolean).join(" ")} >>`;
|
|
13350
|
+
}
|
|
12562
13351
|
|
|
12563
13352
|
// src/core/pdf-signature.ts
|
|
12564
13353
|
init_sha();
|
|
@@ -12568,6 +13357,17 @@ init_asn1();
|
|
|
12568
13357
|
init_sha();
|
|
12569
13358
|
init_rsa();
|
|
12570
13359
|
init_ecdsa();
|
|
13360
|
+
|
|
13361
|
+
// src/crypto/crypto-provider.ts
|
|
13362
|
+
var _cryptoProvider = null;
|
|
13363
|
+
function setCryptoProvider(provider) {
|
|
13364
|
+
_cryptoProvider = provider;
|
|
13365
|
+
}
|
|
13366
|
+
function getCryptoProvider() {
|
|
13367
|
+
return _cryptoProvider;
|
|
13368
|
+
}
|
|
13369
|
+
|
|
13370
|
+
// src/crypto/cms.ts
|
|
12571
13371
|
var OID_SIGNED_DATA = new Uint8Array([42, 134, 72, 134, 247, 13, 1, 7, 2]);
|
|
12572
13372
|
var OID_DATA = new Uint8Array([42, 134, 72, 134, 247, 13, 1, 7, 1]);
|
|
12573
13373
|
var OID_SHA256 = new Uint8Array([96, 134, 72, 1, 101, 3, 4, 2, 1]);
|
|
@@ -12627,18 +13427,26 @@ function buildSignerInfo(options) {
|
|
|
12627
13427
|
const signedAttrsContent = concatUint8Arrays(attrContentType, attrMessageDigest, attrSigningTime);
|
|
12628
13428
|
const signedAttrsImplicit = derWrap(160, signedAttrsContent);
|
|
12629
13429
|
const signedAttrsForSig = derSet(attrContentType, attrMessageDigest, attrSigningTime);
|
|
12630
|
-
const
|
|
13430
|
+
const provider = options.provider ?? getCryptoProvider();
|
|
12631
13431
|
let sigAlgId;
|
|
12632
13432
|
let signatureValue;
|
|
12633
13433
|
if (algorithm === "rsa-sha256") {
|
|
12634
|
-
if (!options.rsaKey) throw new Error("RSA private key required for rsa-sha256");
|
|
12635
13434
|
sigAlgId = derSequence(derOid(OID_SHA256_RSA), derNull());
|
|
12636
|
-
|
|
13435
|
+
if (provider) {
|
|
13436
|
+
signatureValue = provider.sign(signedAttrsForSig, algorithm);
|
|
13437
|
+
} else {
|
|
13438
|
+
if (!options.rsaKey) throw new Error("RSA private key (or a crypto provider) required for rsa-sha256");
|
|
13439
|
+
signatureValue = rsaSignHash(sha256(signedAttrsForSig), options.rsaKey);
|
|
13440
|
+
}
|
|
12637
13441
|
} else if (algorithm === "ecdsa-sha256") {
|
|
12638
|
-
if (!options.ecKey) throw new Error("ECDSA private key required for ecdsa-sha256");
|
|
12639
13442
|
sigAlgId = derSequence(derOid(OID_ECDSA_SHA256));
|
|
12640
|
-
|
|
12641
|
-
|
|
13443
|
+
if (provider) {
|
|
13444
|
+
signatureValue = provider.sign(signedAttrsForSig, algorithm);
|
|
13445
|
+
} else {
|
|
13446
|
+
if (!options.ecKey) throw new Error("ECDSA private key (or a crypto provider) required for ecdsa-sha256");
|
|
13447
|
+
const { r, s } = ecdsaSignHash(sha256(signedAttrsForSig), options.ecKey);
|
|
13448
|
+
signatureValue = encodeDerSignature(r, s);
|
|
13449
|
+
}
|
|
12642
13450
|
} else {
|
|
12643
13451
|
throw new Error(`Unsupported algorithm: ${algorithm}`);
|
|
12644
13452
|
}
|
|
@@ -12717,7 +13525,8 @@ function signPdfBytes(pdfBytes, options) {
|
|
|
12717
13525
|
rsaKey: options.rsaKey,
|
|
12718
13526
|
ecKey: options.ecKey,
|
|
12719
13527
|
algorithm,
|
|
12720
|
-
signingTime: options.signingTime
|
|
13528
|
+
signingTime: options.signingTime,
|
|
13529
|
+
provider: options.provider
|
|
12721
13530
|
};
|
|
12722
13531
|
const cms = buildCmsSignedData(cmsOptions);
|
|
12723
13532
|
if (cms.length * 2 > hexLen) {
|
|
@@ -13887,6 +14696,28 @@ function openPdf(bytes) {
|
|
|
13887
14696
|
if (!entry || entry.type === 0) return null;
|
|
13888
14697
|
return resolveRef({ num, gen: entry.gen });
|
|
13889
14698
|
}
|
|
14699
|
+
let _pageRefs;
|
|
14700
|
+
function collectPageRefs() {
|
|
14701
|
+
if (_pageRefs) return _pageRefs;
|
|
14702
|
+
const catalog = getCatalog();
|
|
14703
|
+
const refs = [];
|
|
14704
|
+
const walk = (nodeVal, depth) => {
|
|
14705
|
+
if (depth > 100) return;
|
|
14706
|
+
const node = resolveValue2(nodeVal);
|
|
14707
|
+
if (!isDict(node)) return;
|
|
14708
|
+
if (dictGetName(node, "Type") === "Page") {
|
|
14709
|
+
if (isRef(nodeVal)) refs.push(nodeVal);
|
|
14710
|
+
return;
|
|
14711
|
+
}
|
|
14712
|
+
const kids = node.get("Kids");
|
|
14713
|
+
if (isArray(kids)) {
|
|
14714
|
+
for (const k of kids) walk(k, depth + 1);
|
|
14715
|
+
}
|
|
14716
|
+
};
|
|
14717
|
+
walk(catalog.get("Pages") ?? null, 0);
|
|
14718
|
+
_pageRefs = refs;
|
|
14719
|
+
return refs;
|
|
14720
|
+
}
|
|
13890
14721
|
function decodeStreamData(stream) {
|
|
13891
14722
|
let data = stream.data;
|
|
13892
14723
|
const filterName = dictGetName(stream.dict, "Filter");
|
|
@@ -13938,10 +14769,107 @@ function openPdf(bytes) {
|
|
|
13938
14769
|
const info = resolveValue2(infoRef);
|
|
13939
14770
|
return isDict(info) ? info : null;
|
|
13940
14771
|
},
|
|
14772
|
+
getPageLabels() {
|
|
14773
|
+
const catalog = getCatalog();
|
|
14774
|
+
const plVal = resolveValue2(catalog.get("PageLabels") ?? null);
|
|
14775
|
+
if (!isDict(plVal)) return null;
|
|
14776
|
+
const entries = /* @__PURE__ */ new Map();
|
|
14777
|
+
collectNumberTree(plVal, resolveValue2, entries);
|
|
14778
|
+
if (entries.size === 0) return null;
|
|
14779
|
+
const ranges = [];
|
|
14780
|
+
for (const startPage of [...entries.keys()].sort((a, b) => a - b)) {
|
|
14781
|
+
const dict = resolveValue2(entries.get(startPage) ?? null);
|
|
14782
|
+
if (!isDict(dict)) continue;
|
|
14783
|
+
const range = { startPage };
|
|
14784
|
+
const sOp = dictGetName(dict, "S");
|
|
14785
|
+
range.style = sOp === void 0 ? "none" : STYLE_FROM_OP[sOp] ?? "none";
|
|
14786
|
+
const prefix = dict.get("P");
|
|
14787
|
+
if (typeof prefix === "string") range.prefix = prefix;
|
|
14788
|
+
const start = dictGetNum(dict, "St");
|
|
14789
|
+
if (start !== void 0) range.start = start;
|
|
14790
|
+
ranges.push(range);
|
|
14791
|
+
}
|
|
14792
|
+
return ranges.length > 0 ? ranges : null;
|
|
14793
|
+
},
|
|
14794
|
+
getAnnotations(pageIndex) {
|
|
14795
|
+
const pages = collectPages();
|
|
14796
|
+
if (pageIndex < 0 || pageIndex >= pages.length) return [];
|
|
14797
|
+
const annotsVal = resolveValue2(pages[pageIndex].get("Annots") ?? null);
|
|
14798
|
+
if (!isArray(annotsVal)) return [];
|
|
14799
|
+
const out = [];
|
|
14800
|
+
for (const a of annotsVal) {
|
|
14801
|
+
const d = resolveValue2(a);
|
|
14802
|
+
if (!isDict(d)) continue;
|
|
14803
|
+
const subtype = dictGetName(d, "Subtype") ?? "";
|
|
14804
|
+
const rectVal = resolveValue2(d.get("Rect") ?? null);
|
|
14805
|
+
let rect = null;
|
|
14806
|
+
if (isArray(rectVal) && rectVal.length === 4 && rectVal.every((n2) => typeof n2 === "number")) {
|
|
14807
|
+
rect = [rectVal[0], rectVal[1], rectVal[2], rectVal[3]];
|
|
14808
|
+
}
|
|
14809
|
+
const parsed = { subtype, rect };
|
|
14810
|
+
const contents = d.get("Contents");
|
|
14811
|
+
if (typeof contents === "string") parsed.contents = decodePdfTextString(contents);
|
|
14812
|
+
const title = d.get("T");
|
|
14813
|
+
if (typeof title === "string") parsed.title = decodePdfTextString(title);
|
|
14814
|
+
const c = resolveValue2(d.get("C") ?? null);
|
|
14815
|
+
if (isArray(c)) {
|
|
14816
|
+
const nums = c.filter((x) => typeof x === "number");
|
|
14817
|
+
if (nums.length > 0) parsed.color = nums;
|
|
14818
|
+
}
|
|
14819
|
+
const qp = resolveValue2(d.get("QuadPoints") ?? null);
|
|
14820
|
+
if (isArray(qp)) {
|
|
14821
|
+
parsed.quadPoints = qp.filter((x) => typeof x === "number");
|
|
14822
|
+
}
|
|
14823
|
+
const action = resolveValue2(d.get("A") ?? null);
|
|
14824
|
+
if (isDict(action)) {
|
|
14825
|
+
const uri = action.get("URI");
|
|
14826
|
+
if (typeof uri === "string") parsed.url = uri;
|
|
14827
|
+
}
|
|
14828
|
+
out.push(parsed);
|
|
14829
|
+
}
|
|
14830
|
+
return out;
|
|
14831
|
+
},
|
|
14832
|
+
getPageRef(pageIndex) {
|
|
14833
|
+
const refs = collectPageRefs();
|
|
14834
|
+
return pageIndex >= 0 && pageIndex < refs.length ? refs[pageIndex] : null;
|
|
14835
|
+
},
|
|
13941
14836
|
decodeStream: decodeStreamData,
|
|
13942
14837
|
getObject
|
|
13943
14838
|
};
|
|
13944
14839
|
}
|
|
14840
|
+
function decodePdfTextString(raw) {
|
|
14841
|
+
if (raw.length >= 2 && raw.charCodeAt(0) === 254 && raw.charCodeAt(1) === 255) {
|
|
14842
|
+
let out = "";
|
|
14843
|
+
for (let i = 2; i + 1 < raw.length; i += 2) {
|
|
14844
|
+
out += String.fromCharCode(raw.charCodeAt(i) << 8 | raw.charCodeAt(i + 1));
|
|
14845
|
+
}
|
|
14846
|
+
return out;
|
|
14847
|
+
}
|
|
14848
|
+
return raw;
|
|
14849
|
+
}
|
|
14850
|
+
var STYLE_FROM_OP = {
|
|
14851
|
+
D: "decimal",
|
|
14852
|
+
r: "roman",
|
|
14853
|
+
R: "Roman",
|
|
14854
|
+
a: "alpha",
|
|
14855
|
+
A: "Alpha"
|
|
14856
|
+
};
|
|
14857
|
+
function collectNumberTree(node, resolve, out) {
|
|
14858
|
+
const nums = resolve(node.get("Nums") ?? null);
|
|
14859
|
+
if (isArray(nums)) {
|
|
14860
|
+
for (let i = 0; i + 1 < nums.length; i += 2) {
|
|
14861
|
+
const key = resolve(nums[i]);
|
|
14862
|
+
if (typeof key === "number") out.set(key, nums[i + 1]);
|
|
14863
|
+
}
|
|
14864
|
+
}
|
|
14865
|
+
const kids = resolve(node.get("Kids") ?? null);
|
|
14866
|
+
if (isArray(kids)) {
|
|
14867
|
+
for (const kid of kids) {
|
|
14868
|
+
const kd = resolve(kid);
|
|
14869
|
+
if (isDict(kd)) collectNumberTree(kd, resolve, out);
|
|
14870
|
+
}
|
|
14871
|
+
}
|
|
14872
|
+
}
|
|
13945
14873
|
function flattenPageTree(node, resolve, pages) {
|
|
13946
14874
|
const type = dictGetName(node, "Type");
|
|
13947
14875
|
if (type === "Page") {
|
|
@@ -14076,6 +15004,21 @@ function createModifier(reader) {
|
|
|
14076
15004
|
if (modified.has(num)) return modified.get(num) ?? null;
|
|
14077
15005
|
return reader.getObject(num);
|
|
14078
15006
|
}
|
|
15007
|
+
function addAnnotation(pageIndex, annotationBody) {
|
|
15008
|
+
const pageRef = reader.getPageRef(pageIndex);
|
|
15009
|
+
if (!pageRef) throw new Error(`addAnnotation: no page at index ${pageIndex}`);
|
|
15010
|
+
const objNum = addRawObject(annotationBody);
|
|
15011
|
+
const page = getObject(pageRef.num);
|
|
15012
|
+
if (!isDict(page)) throw new Error(`addAnnotation: page ${pageIndex} is not a dictionary`);
|
|
15013
|
+
const clone = new Map(page);
|
|
15014
|
+
const existing = clone.get("Annots");
|
|
15015
|
+
const resolved = isRef(existing) ? reader.resolveValue(existing) : existing;
|
|
15016
|
+
const annots = isArray(resolved) ? [...resolved] : [];
|
|
15017
|
+
annots.push({ type: "ref", num: objNum, gen: 0 });
|
|
15018
|
+
clone.set("Annots", annots);
|
|
15019
|
+
setObject(pageRef.num, clone);
|
|
15020
|
+
return objNum;
|
|
15021
|
+
}
|
|
14079
15022
|
function save() {
|
|
14080
15023
|
if (modified.size === 0) {
|
|
14081
15024
|
return reader.bytes;
|
|
@@ -14121,6 +15064,7 @@ ${xrefOffset}
|
|
|
14121
15064
|
setObject,
|
|
14122
15065
|
addObject,
|
|
14123
15066
|
addRawObject,
|
|
15067
|
+
addAnnotation,
|
|
14124
15068
|
getObject,
|
|
14125
15069
|
save,
|
|
14126
15070
|
get nextObjNum() {
|
|
@@ -14534,6 +15478,68 @@ async function streamByteLength(stream) {
|
|
|
14534
15478
|
}
|
|
14535
15479
|
return total;
|
|
14536
15480
|
}
|
|
15481
|
+
async function streamToFile(stream, filePath, opts) {
|
|
15482
|
+
let fs;
|
|
15483
|
+
try {
|
|
15484
|
+
fs = await import('fs');
|
|
15485
|
+
} catch {
|
|
15486
|
+
throw new Error("streamToFile requires a Node.js environment (node:fs is unavailable)");
|
|
15487
|
+
}
|
|
15488
|
+
const signal = opts?.signal;
|
|
15489
|
+
if (signal?.aborted) throw new Error("streamToFile aborted before start");
|
|
15490
|
+
const ws = fs.createWriteStream(filePath);
|
|
15491
|
+
let bytesWritten = 0;
|
|
15492
|
+
const onAbort = () => {
|
|
15493
|
+
ws.destroy(new Error("streamToFile aborted"));
|
|
15494
|
+
};
|
|
15495
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
15496
|
+
try {
|
|
15497
|
+
await new Promise((resolve, reject) => {
|
|
15498
|
+
ws.on("error", reject);
|
|
15499
|
+
ws.on("open", () => resolve());
|
|
15500
|
+
});
|
|
15501
|
+
for await (const chunk of stream) {
|
|
15502
|
+
if (signal?.aborted) throw new Error("streamToFile aborted");
|
|
15503
|
+
bytesWritten += chunk.length;
|
|
15504
|
+
const ok = ws.write(chunk);
|
|
15505
|
+
if (!ok) {
|
|
15506
|
+
await new Promise((resolve, reject) => {
|
|
15507
|
+
const onErr = (e) => {
|
|
15508
|
+
ws.off("drain", onDrain);
|
|
15509
|
+
reject(e);
|
|
15510
|
+
};
|
|
15511
|
+
const onDrain = () => {
|
|
15512
|
+
ws.off("error", onErr);
|
|
15513
|
+
resolve();
|
|
15514
|
+
};
|
|
15515
|
+
ws.once("drain", onDrain);
|
|
15516
|
+
ws.once("error", onErr);
|
|
15517
|
+
});
|
|
15518
|
+
}
|
|
15519
|
+
}
|
|
15520
|
+
if (signal?.aborted) throw new Error("streamToFile aborted");
|
|
15521
|
+
await new Promise((resolve, reject) => {
|
|
15522
|
+
ws.end((err) => err ? reject(err) : resolve());
|
|
15523
|
+
});
|
|
15524
|
+
} catch (err) {
|
|
15525
|
+
await new Promise((resolve) => {
|
|
15526
|
+
if (ws.closed) {
|
|
15527
|
+
resolve();
|
|
15528
|
+
return;
|
|
15529
|
+
}
|
|
15530
|
+
ws.once("close", () => resolve());
|
|
15531
|
+
ws.destroy();
|
|
15532
|
+
});
|
|
15533
|
+
try {
|
|
15534
|
+
fs.rmSync(filePath, { force: true });
|
|
15535
|
+
} catch {
|
|
15536
|
+
}
|
|
15537
|
+
throw err;
|
|
15538
|
+
} finally {
|
|
15539
|
+
signal?.removeEventListener("abort", onAbort);
|
|
15540
|
+
}
|
|
15541
|
+
return { bytesWritten, path: filePath };
|
|
15542
|
+
}
|
|
14537
15543
|
var DEFAULT_CHUNK_SIZE = 65536;
|
|
14538
15544
|
var MIN_CHUNK_SIZE = 1024;
|
|
14539
15545
|
var MAX_CHUNK_SIZE = 16777216;
|
|
@@ -14749,6 +15755,123 @@ async function initCrypto() {
|
|
|
14749
15755
|
initEcdsaAsn12(asn1);
|
|
14750
15756
|
}
|
|
14751
15757
|
|
|
15758
|
+
// src/fonts/font-validator.ts
|
|
15759
|
+
var SFNT_MAGIC = /* @__PURE__ */ new Set([
|
|
15760
|
+
65536,
|
|
15761
|
+
// TrueType outlines
|
|
15762
|
+
1330926671,
|
|
15763
|
+
// 'OTTO' — CFF/OpenType outlines
|
|
15764
|
+
1953658213,
|
|
15765
|
+
// 'true' — legacy Apple TrueType
|
|
15766
|
+
1953784678
|
|
15767
|
+
// 'ttcf' — TrueType Collection
|
|
15768
|
+
]);
|
|
15769
|
+
function decodeBase64Prefix(b64, maxBytes) {
|
|
15770
|
+
if (!/^[A-Za-z0-9+/]*={0,2}$/.test(b64)) return null;
|
|
15771
|
+
if (b64.length % 4 !== 0) return null;
|
|
15772
|
+
const g = globalThis;
|
|
15773
|
+
try {
|
|
15774
|
+
const atobFn = g["atob"];
|
|
15775
|
+
if (typeof atobFn === "function") {
|
|
15776
|
+
const need = Math.ceil(maxBytes / 3 * 4 / 4) * 4;
|
|
15777
|
+
const slice = b64.slice(0, Math.min(b64.length, need));
|
|
15778
|
+
const bin = atobFn(slice);
|
|
15779
|
+
const out = new Uint8Array(Math.min(bin.length, maxBytes));
|
|
15780
|
+
for (let i = 0; i < out.length; i++) out[i] = bin.charCodeAt(i) & 255;
|
|
15781
|
+
return out;
|
|
15782
|
+
}
|
|
15783
|
+
const bufCtor = g["Buffer"];
|
|
15784
|
+
if (bufCtor?.from) {
|
|
15785
|
+
return bufCtor.from(b64, "base64").subarray(0, maxBytes);
|
|
15786
|
+
}
|
|
15787
|
+
} catch {
|
|
15788
|
+
return null;
|
|
15789
|
+
}
|
|
15790
|
+
return null;
|
|
15791
|
+
}
|
|
15792
|
+
function validateFontData(data) {
|
|
15793
|
+
const errors = [];
|
|
15794
|
+
const warnings = [];
|
|
15795
|
+
if (data === null || typeof data !== "object") {
|
|
15796
|
+
return { valid: false, errors: ["font data must be a non-null object"], warnings };
|
|
15797
|
+
}
|
|
15798
|
+
const f = data;
|
|
15799
|
+
const m = f.metrics;
|
|
15800
|
+
if (!m || typeof m !== "object") {
|
|
15801
|
+
errors.push("missing or invalid `metrics` object");
|
|
15802
|
+
} else {
|
|
15803
|
+
const finiteFields = ["unitsPerEm", "numGlyphs", "ascent", "descent", "capHeight", "stemV"];
|
|
15804
|
+
const mRec = m;
|
|
15805
|
+
for (const k of finiteFields) {
|
|
15806
|
+
const v = mRec[k];
|
|
15807
|
+
if (typeof v !== "number" || !Number.isFinite(v)) {
|
|
15808
|
+
errors.push(`metrics.${String(k)} must be a finite number`);
|
|
15809
|
+
}
|
|
15810
|
+
}
|
|
15811
|
+
if (typeof m.unitsPerEm === "number" && m.unitsPerEm <= 0) {
|
|
15812
|
+
errors.push("metrics.unitsPerEm must be positive");
|
|
15813
|
+
}
|
|
15814
|
+
if (!Array.isArray(m.bbox) || m.bbox.length !== 4 || !m.bbox.every((n2) => typeof n2 === "number" && Number.isFinite(n2))) {
|
|
15815
|
+
errors.push("metrics.bbox must be a 4-number array [xMin yMin xMax yMax]");
|
|
15816
|
+
}
|
|
15817
|
+
}
|
|
15818
|
+
if (typeof f.fontName !== "string" || f.fontName.length === 0) {
|
|
15819
|
+
errors.push("`fontName` must be a non-empty string");
|
|
15820
|
+
}
|
|
15821
|
+
const cmap = f.cmap;
|
|
15822
|
+
let cmapEntries = 0;
|
|
15823
|
+
if (!cmap || typeof cmap !== "object") {
|
|
15824
|
+
errors.push("missing or invalid `cmap` (codepoint \u2192 glyph id map)");
|
|
15825
|
+
} else {
|
|
15826
|
+
cmapEntries = Object.keys(cmap).length;
|
|
15827
|
+
if (cmapEntries === 0) {
|
|
15828
|
+
errors.push("`cmap` is empty \u2014 the font maps no characters");
|
|
15829
|
+
}
|
|
15830
|
+
}
|
|
15831
|
+
const widths = f.widths;
|
|
15832
|
+
if (!widths || typeof widths !== "object") {
|
|
15833
|
+
errors.push("missing or invalid `widths` (glyph id \u2192 advance map)");
|
|
15834
|
+
}
|
|
15835
|
+
if (cmap && typeof cmap === "object" && widths && typeof widths === "object") {
|
|
15836
|
+
const numGlyphs = m && typeof m.numGlyphs === "number" ? m.numGlyphs : Infinity;
|
|
15837
|
+
let missingWidth = 0;
|
|
15838
|
+
let outOfRange = 0;
|
|
15839
|
+
for (const gid of Object.values(cmap)) {
|
|
15840
|
+
if (typeof gid !== "number" || !Number.isInteger(gid) || gid < 0) {
|
|
15841
|
+
outOfRange++;
|
|
15842
|
+
continue;
|
|
15843
|
+
}
|
|
15844
|
+
if (Number.isFinite(numGlyphs) && gid >= numGlyphs) outOfRange++;
|
|
15845
|
+
if (widths[gid] === void 0) missingWidth++;
|
|
15846
|
+
}
|
|
15847
|
+
if (outOfRange > 0) {
|
|
15848
|
+
errors.push(`${outOfRange} cmap entr${outOfRange === 1 ? "y maps" : "ies map"} to an out-of-range glyph id`);
|
|
15849
|
+
}
|
|
15850
|
+
if (missingWidth > 0) {
|
|
15851
|
+
warnings.push(`${missingWidth} cmap glyph id(s) have no explicit width (defaultWidth will be used)`);
|
|
15852
|
+
}
|
|
15853
|
+
}
|
|
15854
|
+
if (typeof f.pdfWidthArray !== "string" || f.pdfWidthArray.length === 0) {
|
|
15855
|
+
errors.push("`pdfWidthArray` must be a non-empty PDF /W array string");
|
|
15856
|
+
} else if (!/^\s*\d/.test(f.pdfWidthArray)) {
|
|
15857
|
+
warnings.push("`pdfWidthArray` does not begin with a glyph index \u2014 verify the /W array format");
|
|
15858
|
+
}
|
|
15859
|
+
if (typeof f.ttfBase64 !== "string" || f.ttfBase64.length === 0) {
|
|
15860
|
+
errors.push("`ttfBase64` must be a non-empty base64 string");
|
|
15861
|
+
} else {
|
|
15862
|
+
const head = decodeBase64Prefix(f.ttfBase64, 4);
|
|
15863
|
+
if (!head || head.length < 4) {
|
|
15864
|
+
errors.push("`ttfBase64` is not valid base64");
|
|
15865
|
+
} else {
|
|
15866
|
+
const magic = (head[0] << 24 | head[1] << 16 | head[2] << 8 | head[3]) >>> 0;
|
|
15867
|
+
if (!SFNT_MAGIC.has(magic)) {
|
|
15868
|
+
errors.push(`\`ttfBase64\` is not an SFNT font (unexpected magic 0x${magic.toString(16).padStart(8, "0")})`);
|
|
15869
|
+
}
|
|
15870
|
+
}
|
|
15871
|
+
}
|
|
15872
|
+
return { valid: errors.length === 0, errors, warnings };
|
|
15873
|
+
}
|
|
15874
|
+
|
|
14752
15875
|
// src/fonts/colr-parser.ts
|
|
14753
15876
|
var IDENTITY = [1, 0, 0, 1, 0, 0];
|
|
14754
15877
|
function compose(outer, inner) {
|
|
@@ -14865,6 +15988,22 @@ function resolveFill(ctx, offset, m) {
|
|
|
14865
15988
|
const s = avgScale(m);
|
|
14866
15989
|
return { kind: "radial", c0: apply(m, x0, y0), r0: r0 * s, c1: apply(m, x1, y1), r1: r1 * s, stops, extend };
|
|
14867
15990
|
}
|
|
15991
|
+
case 8: {
|
|
15992
|
+
const colorLineOffset = getUint24(view, offset + 1);
|
|
15993
|
+
const cx = view.getInt16(offset + 4), cy = view.getInt16(offset + 6);
|
|
15994
|
+
const startAngle = f2dot14(view, offset + 8) * 180;
|
|
15995
|
+
const endAngle = f2dot14(view, offset + 10) * 180;
|
|
15996
|
+
const { stops, extend } = readColorLine(ctx, offset + colorLineOffset);
|
|
15997
|
+
const rot = Math.atan2(m[1], m[0]) * 180 / Math.PI;
|
|
15998
|
+
return {
|
|
15999
|
+
kind: "sweep",
|
|
16000
|
+
center: apply(m, cx, cy),
|
|
16001
|
+
startAngle: startAngle + rot,
|
|
16002
|
+
endAngle: endAngle + rot,
|
|
16003
|
+
stops,
|
|
16004
|
+
extend
|
|
16005
|
+
};
|
|
16006
|
+
}
|
|
14868
16007
|
case 12: {
|
|
14869
16008
|
const subOffset = getUint24(view, offset + 1);
|
|
14870
16009
|
const transformOffset = getUint24(view, offset + 4);
|
|
@@ -14894,7 +16033,7 @@ function readAffine(view, pos) {
|
|
|
14894
16033
|
const dy = fixed(view, pos + 20);
|
|
14895
16034
|
return [xx, yx, xy, yy, dx, dy];
|
|
14896
16035
|
}
|
|
14897
|
-
function collectLayers(ctx, offset, m, out, depth) {
|
|
16036
|
+
function collectLayers(ctx, offset, m, out, depth, blendMode) {
|
|
14898
16037
|
if (depth > 16) throw new UnsupportedPaint("paint recursion too deep");
|
|
14899
16038
|
const { view } = ctx;
|
|
14900
16039
|
const format = view.getUint8(offset);
|
|
@@ -14906,7 +16045,7 @@ function collectLayers(ctx, offset, m, out, depth) {
|
|
|
14906
16045
|
for (let i = 0; i < numLayers; i++) {
|
|
14907
16046
|
const idx = firstLayerIndex + i;
|
|
14908
16047
|
const paintOffset = view.getUint32(ctx.layerListBase + 4 + idx * 4);
|
|
14909
|
-
collectLayers(ctx, ctx.layerListBase + paintOffset, m, out, depth + 1);
|
|
16048
|
+
collectLayers(ctx, ctx.layerListBase + paintOffset, m, out, depth + 1, blendMode);
|
|
14910
16049
|
}
|
|
14911
16050
|
return;
|
|
14912
16051
|
}
|
|
@@ -14914,39 +16053,91 @@ function collectLayers(ctx, offset, m, out, depth) {
|
|
|
14914
16053
|
const subOffset = getUint24(view, offset + 1);
|
|
14915
16054
|
const glyphId = view.getUint16(offset + 4);
|
|
14916
16055
|
const paint = resolveFill(ctx, offset + subOffset, IDENTITY);
|
|
14917
|
-
|
|
16056
|
+
const layer = m === IDENTITY ? { glyphId, paint } : { glyphId, paint, transform: m };
|
|
16057
|
+
out.push(blendMode ? { ...layer, blendMode } : layer);
|
|
14918
16058
|
return;
|
|
14919
16059
|
}
|
|
14920
16060
|
case 11: {
|
|
14921
16061
|
const glyphId = view.getUint16(offset + 1);
|
|
14922
16062
|
const paintOffset = baseGlyphPaintOffset(ctx, glyphId);
|
|
14923
16063
|
if (paintOffset === null) throw new UnsupportedPaint("PaintColrGlyph missing base");
|
|
14924
|
-
collectLayers(ctx, paintOffset, m, out, depth + 1);
|
|
16064
|
+
collectLayers(ctx, paintOffset, m, out, depth + 1, blendMode);
|
|
14925
16065
|
return;
|
|
14926
16066
|
}
|
|
14927
16067
|
case 12: {
|
|
14928
16068
|
const subOffset = getUint24(view, offset + 1);
|
|
14929
16069
|
const transformOffset = getUint24(view, offset + 4);
|
|
14930
16070
|
const t = readAffine(view, offset + transformOffset);
|
|
14931
|
-
collectLayers(ctx, offset + subOffset, compose(m, t), out, depth + 1);
|
|
16071
|
+
collectLayers(ctx, offset + subOffset, compose(m, t), out, depth + 1, blendMode);
|
|
14932
16072
|
return;
|
|
14933
16073
|
}
|
|
14934
16074
|
case 14: {
|
|
14935
16075
|
const subOffset = getUint24(view, offset + 1);
|
|
14936
16076
|
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);
|
|
16077
|
+
collectLayers(ctx, offset + subOffset, compose(m, [1, 0, 0, 1, dx, dy]), out, depth + 1, blendMode);
|
|
14938
16078
|
return;
|
|
14939
16079
|
}
|
|
14940
16080
|
case 16: {
|
|
14941
16081
|
const subOffset = getUint24(view, offset + 1);
|
|
14942
16082
|
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);
|
|
16083
|
+
collectLayers(ctx, offset + subOffset, compose(m, [sx, 0, 0, sy, 0, 0]), out, depth + 1, blendMode);
|
|
16084
|
+
return;
|
|
16085
|
+
}
|
|
16086
|
+
case 32: {
|
|
16087
|
+
const sourceOffset = getUint24(view, offset + 1);
|
|
16088
|
+
const mode = view.getUint8(offset + 4);
|
|
16089
|
+
const backdropOffset = getUint24(view, offset + 5);
|
|
16090
|
+
const bm = compositeModeToBlendMode(mode);
|
|
16091
|
+
if (bm === null) throw new UnsupportedPaint(`composite mode ${mode}`);
|
|
16092
|
+
collectLayers(ctx, offset + backdropOffset, m, out, depth + 1, blendMode);
|
|
16093
|
+
collectLayers(ctx, offset + sourceOffset, m, out, depth + 1, bm === "Normal" ? blendMode : bm);
|
|
14944
16094
|
return;
|
|
14945
16095
|
}
|
|
14946
16096
|
default:
|
|
14947
16097
|
throw new UnsupportedPaint(`structural paint format ${format}`);
|
|
14948
16098
|
}
|
|
14949
16099
|
}
|
|
16100
|
+
function compositeModeToBlendMode(mode) {
|
|
16101
|
+
switch (mode) {
|
|
16102
|
+
case 3:
|
|
16103
|
+
return "Normal";
|
|
16104
|
+
// SRC_OVER
|
|
16105
|
+
case 13:
|
|
16106
|
+
return "Screen";
|
|
16107
|
+
case 14:
|
|
16108
|
+
return "Overlay";
|
|
16109
|
+
case 15:
|
|
16110
|
+
return "Darken";
|
|
16111
|
+
case 16:
|
|
16112
|
+
return "Lighten";
|
|
16113
|
+
case 17:
|
|
16114
|
+
return "ColorDodge";
|
|
16115
|
+
case 18:
|
|
16116
|
+
return "ColorBurn";
|
|
16117
|
+
case 19:
|
|
16118
|
+
return "HardLight";
|
|
16119
|
+
case 20:
|
|
16120
|
+
return "SoftLight";
|
|
16121
|
+
case 21:
|
|
16122
|
+
return "Difference";
|
|
16123
|
+
case 22:
|
|
16124
|
+
return "Exclusion";
|
|
16125
|
+
case 23:
|
|
16126
|
+
return "Multiply";
|
|
16127
|
+
case 24:
|
|
16128
|
+
return "Hue";
|
|
16129
|
+
case 25:
|
|
16130
|
+
return "Saturation";
|
|
16131
|
+
case 26:
|
|
16132
|
+
return "Color";
|
|
16133
|
+
case 27:
|
|
16134
|
+
return "Luminosity";
|
|
16135
|
+
// 0 CLEAR, 1 SRC, 2 DEST, 4 DEST_OVER, 5 SRC_IN, 6 DEST_IN, 7 SRC_OUT,
|
|
16136
|
+
// 8 DEST_OUT, 9 SRC_ATOP, 10 DEST_ATOP, 11 XOR, 12 PLUS → unsupported.
|
|
16137
|
+
default:
|
|
16138
|
+
return null;
|
|
16139
|
+
}
|
|
16140
|
+
}
|
|
14950
16141
|
function baseGlyphPaintOffset(ctx, glyphId) {
|
|
14951
16142
|
const { view, colrBase } = ctx;
|
|
14952
16143
|
const baseGlyphListOffset = view.getUint32(colrBase + 14);
|
|
@@ -15022,6 +16213,314 @@ function parseColrCpal(bytes) {
|
|
|
15022
16213
|
return Object.keys(result).length ? result : null;
|
|
15023
16214
|
}
|
|
15024
16215
|
|
|
16216
|
+
// src/parser/pdf-pagetree.ts
|
|
16217
|
+
init_pdf_encrypt();
|
|
16218
|
+
var MAX_MERGE_SOURCES = 50;
|
|
16219
|
+
var DEFAULT_MEDIA_BOX = "[0 0 612 792]";
|
|
16220
|
+
var INHERITABLE_KEYS = ["MediaBox", "CropBox", "Rotate"];
|
|
16221
|
+
var MAX_COPY_DEPTH = 2e3;
|
|
16222
|
+
var DEFAULT_MAX_OUTPUT_SIZE = 256 * 1024 * 1024;
|
|
16223
|
+
function resolveMaxOutputSize(value) {
|
|
16224
|
+
if (value === void 0) return DEFAULT_MAX_OUTPUT_SIZE;
|
|
16225
|
+
if (typeof value !== "number" || Number.isNaN(value) || value <= 0) {
|
|
16226
|
+
throw new Error(
|
|
16227
|
+
`maxOutputSize must be a positive number or Infinity (got ${String(value)})`
|
|
16228
|
+
);
|
|
16229
|
+
}
|
|
16230
|
+
return value;
|
|
16231
|
+
}
|
|
16232
|
+
function mergePdfs(sources, opts) {
|
|
16233
|
+
if (sources.length === 0) throw new Error("mergePdfs requires at least one source PDF");
|
|
16234
|
+
if (sources.length > MAX_MERGE_SOURCES) {
|
|
16235
|
+
throw new Error(`mergePdfs supports at most ${MAX_MERGE_SOURCES} sources (got ${sources.length})`);
|
|
16236
|
+
}
|
|
16237
|
+
resolveMaxOutputSize(opts?.maxOutputSize);
|
|
16238
|
+
const specs = [];
|
|
16239
|
+
for (const src of sources) {
|
|
16240
|
+
const reader = openPdf(src);
|
|
16241
|
+
assertNotEncrypted(reader);
|
|
16242
|
+
const count = reader.pageCount;
|
|
16243
|
+
for (let i = 0; i < count; i++) specs.push({ reader, pageIndex: i });
|
|
16244
|
+
}
|
|
16245
|
+
return assemble(specs, opts ?? {});
|
|
16246
|
+
}
|
|
16247
|
+
function extractPages(src, pageIndices, opts) {
|
|
16248
|
+
if (pageIndices.length === 0) throw new Error("extractPages requires at least one page index");
|
|
16249
|
+
resolveMaxOutputSize(opts?.maxOutputSize);
|
|
16250
|
+
const reader = openPdf(src);
|
|
16251
|
+
assertNotEncrypted(reader);
|
|
16252
|
+
const count = reader.pageCount;
|
|
16253
|
+
const specs = [];
|
|
16254
|
+
for (const idx of pageIndices) {
|
|
16255
|
+
if (!Number.isInteger(idx) || idx < 0 || idx >= count) {
|
|
16256
|
+
throw new Error(`extractPages page index ${idx} out of range (0-${count - 1})`);
|
|
16257
|
+
}
|
|
16258
|
+
specs.push({ reader, pageIndex: idx });
|
|
16259
|
+
}
|
|
16260
|
+
return assemble(specs, opts ?? {});
|
|
16261
|
+
}
|
|
16262
|
+
function splitPdf(src, ranges, opts) {
|
|
16263
|
+
if (ranges.length === 0) throw new Error("splitPdf requires at least one range");
|
|
16264
|
+
resolveMaxOutputSize(opts?.maxOutputSize);
|
|
16265
|
+
const reader = openPdf(src);
|
|
16266
|
+
assertNotEncrypted(reader);
|
|
16267
|
+
const count = reader.pageCount;
|
|
16268
|
+
const out = [];
|
|
16269
|
+
for (const range of ranges) {
|
|
16270
|
+
const start = range.start | 0;
|
|
16271
|
+
const end = (range.end ?? range.start) | 0;
|
|
16272
|
+
if (start < 0 || end < start || end >= count) {
|
|
16273
|
+
throw new Error(`splitPdf range [${start}, ${end}] invalid for ${count}-page document`);
|
|
16274
|
+
}
|
|
16275
|
+
const specs = [];
|
|
16276
|
+
for (let i = start; i <= end; i++) specs.push({ reader, pageIndex: i });
|
|
16277
|
+
out.push(assemble(specs, opts ?? {}));
|
|
16278
|
+
}
|
|
16279
|
+
return out;
|
|
16280
|
+
}
|
|
16281
|
+
function assertNotEncrypted(reader) {
|
|
16282
|
+
if (reader.trailer.get("Encrypt") !== void 0) {
|
|
16283
|
+
throw new Error("Encrypted PDFs are not supported by the page-tree API (decrypt first)");
|
|
16284
|
+
}
|
|
16285
|
+
}
|
|
16286
|
+
function resolveInherited(reader, page, key) {
|
|
16287
|
+
let node = page;
|
|
16288
|
+
const seen = /* @__PURE__ */ new Set();
|
|
16289
|
+
while (node && !seen.has(node)) {
|
|
16290
|
+
seen.add(node);
|
|
16291
|
+
const v = node.get(key);
|
|
16292
|
+
if (v !== void 0) return v;
|
|
16293
|
+
const parent = node.get("Parent");
|
|
16294
|
+
if (parent === void 0) break;
|
|
16295
|
+
const resolved = reader.resolveValue(parent);
|
|
16296
|
+
node = isDict(resolved) ? resolved : void 0;
|
|
16297
|
+
}
|
|
16298
|
+
return void 0;
|
|
16299
|
+
}
|
|
16300
|
+
function assemble(specs, opts) {
|
|
16301
|
+
const ctx = {
|
|
16302
|
+
nextNum: 3,
|
|
16303
|
+
bodies: /* @__PURE__ */ new Map(),
|
|
16304
|
+
memo: /* @__PURE__ */ new Map(),
|
|
16305
|
+
maxOutputSize: resolveMaxOutputSize(opts.maxOutputSize),
|
|
16306
|
+
totalBytes: 0
|
|
16307
|
+
};
|
|
16308
|
+
const pageNums = [];
|
|
16309
|
+
for (const spec of specs) {
|
|
16310
|
+
pageNums.push(copyPage(ctx, spec, opts));
|
|
16311
|
+
}
|
|
16312
|
+
const kids = pageNums.map((n2) => `${n2} 0 R`).join(" ");
|
|
16313
|
+
setBody(ctx, 2, `<< /Type /Pages /Kids [${kids}] /Count ${pageNums.length} >>`);
|
|
16314
|
+
setBody(ctx, 1, "<< /Type /Catalog /Pages 2 0 R >>");
|
|
16315
|
+
return serializeDocument(ctx);
|
|
16316
|
+
}
|
|
16317
|
+
function accountBytes(ctx, n2) {
|
|
16318
|
+
ctx.totalBytes += n2;
|
|
16319
|
+
if (ctx.totalBytes > ctx.maxOutputSize) {
|
|
16320
|
+
throw new Error(
|
|
16321
|
+
`page-tree output exceeded the ${ctx.maxOutputSize}-byte maxOutputSize limit (raise MergeOptions.maxOutputSize or pass Infinity to disable)`
|
|
16322
|
+
);
|
|
16323
|
+
}
|
|
16324
|
+
}
|
|
16325
|
+
function setBody(ctx, num, body) {
|
|
16326
|
+
accountBytes(ctx, body.length);
|
|
16327
|
+
ctx.bodies.set(num, body);
|
|
16328
|
+
}
|
|
16329
|
+
function memoFor(ctx, reader) {
|
|
16330
|
+
let m = ctx.memo.get(reader);
|
|
16331
|
+
if (!m) {
|
|
16332
|
+
m = /* @__PURE__ */ new Map();
|
|
16333
|
+
ctx.memo.set(reader, m);
|
|
16334
|
+
}
|
|
16335
|
+
return m;
|
|
16336
|
+
}
|
|
16337
|
+
function copyPage(ctx, spec, opts) {
|
|
16338
|
+
const { reader, pageIndex } = spec;
|
|
16339
|
+
const page = reader.getPage(pageIndex);
|
|
16340
|
+
const pageNum = ctx.nextNum++;
|
|
16341
|
+
const parts = ["/Type /Page", "/Parent 2 0 R"];
|
|
16342
|
+
for (const key of INHERITABLE_KEYS) {
|
|
16343
|
+
const v = resolveInherited(reader, page, key);
|
|
16344
|
+
if (v !== void 0) {
|
|
16345
|
+
parts.push(`/${key} ${serializeValue2(rewrite(ctx, reader, v))}`);
|
|
16346
|
+
} else if (key === "MediaBox") {
|
|
16347
|
+
parts.push(`/MediaBox ${DEFAULT_MEDIA_BOX}`);
|
|
16348
|
+
}
|
|
16349
|
+
}
|
|
16350
|
+
const res = resolveInherited(reader, page, "Resources");
|
|
16351
|
+
parts.push(`/Resources ${res !== void 0 ? serializeValue2(rewrite(ctx, reader, res)) : "<< >>"}`);
|
|
16352
|
+
const contents = page.get("Contents");
|
|
16353
|
+
if (contents !== void 0) {
|
|
16354
|
+
parts.push(`/Contents ${serializeValue2(rewrite(ctx, reader, contents))}`);
|
|
16355
|
+
}
|
|
16356
|
+
if (!opts.dropAnnotations) {
|
|
16357
|
+
const annots = filterAnnotations(ctx, reader, page);
|
|
16358
|
+
if (annots) parts.push(`/Annots ${annots}`);
|
|
16359
|
+
}
|
|
16360
|
+
setBody(ctx, pageNum, `<< ${parts.join(" ")} >>`);
|
|
16361
|
+
return pageNum;
|
|
16362
|
+
}
|
|
16363
|
+
function filterAnnotations(ctx, reader, page) {
|
|
16364
|
+
const annotsVal = page.get("Annots");
|
|
16365
|
+
if (annotsVal === void 0) return void 0;
|
|
16366
|
+
const arr = reader.resolveValue(annotsVal);
|
|
16367
|
+
if (!isArray(arr)) return void 0;
|
|
16368
|
+
const kept = [];
|
|
16369
|
+
for (const a of arr) {
|
|
16370
|
+
const ad = reader.resolveValue(a);
|
|
16371
|
+
if (!isDict(ad)) continue;
|
|
16372
|
+
if (dictGetName(ad, "Subtype") !== "Link") continue;
|
|
16373
|
+
const action = reader.resolveValue(ad.get("A") ?? null);
|
|
16374
|
+
if (!isDict(action) || dictGetName(action, "S") !== "URI") continue;
|
|
16375
|
+
const clean = /* @__PURE__ */ new Map();
|
|
16376
|
+
for (const [k, v] of ad) {
|
|
16377
|
+
if (k === "P" || k === "Parent") continue;
|
|
16378
|
+
clean.set(k, rewrite(ctx, reader, v));
|
|
16379
|
+
}
|
|
16380
|
+
kept.push(serializeValue2(clean));
|
|
16381
|
+
}
|
|
16382
|
+
return kept.length > 0 ? `[${kept.join(" ")}]` : void 0;
|
|
16383
|
+
}
|
|
16384
|
+
function copyObject(ctx, reader, srcNum, srcGen, depth = 0) {
|
|
16385
|
+
const memo = memoFor(ctx, reader);
|
|
16386
|
+
const existing = memo.get(srcNum);
|
|
16387
|
+
if (existing !== void 0) return existing;
|
|
16388
|
+
const newNum = ctx.nextNum++;
|
|
16389
|
+
memo.set(srcNum, newNum);
|
|
16390
|
+
const resolved = reader.resolve({ type: "ref", num: srcNum, gen: srcGen });
|
|
16391
|
+
if (isStream(resolved)) {
|
|
16392
|
+
ctx.bodies.set(newNum, serializeStreamBody(ctx, reader, resolved, depth));
|
|
16393
|
+
} else {
|
|
16394
|
+
const body = serializeValue2(rewrite(ctx, reader, resolved, depth));
|
|
16395
|
+
setBody(ctx, newNum, body);
|
|
16396
|
+
}
|
|
16397
|
+
return newNum;
|
|
16398
|
+
}
|
|
16399
|
+
function rewrite(ctx, reader, val, depth = 0) {
|
|
16400
|
+
if (depth > MAX_COPY_DEPTH) {
|
|
16401
|
+
throw new Error(
|
|
16402
|
+
`page-tree copy exceeded maximum object nesting depth (${MAX_COPY_DEPTH}) \u2014 malformed or adversarial PDF`
|
|
16403
|
+
);
|
|
16404
|
+
}
|
|
16405
|
+
if (isRef(val)) {
|
|
16406
|
+
const entry = reader.xref.entries.get(val.num);
|
|
16407
|
+
if (!entry || entry.type === 0) return null;
|
|
16408
|
+
const newNum = copyObject(ctx, reader, val.num, val.gen, depth + 1);
|
|
16409
|
+
return { type: "ref", num: newNum, gen: 0 };
|
|
16410
|
+
}
|
|
16411
|
+
if (isArray(val)) return val.map((v) => rewrite(ctx, reader, v, depth + 1));
|
|
16412
|
+
if (isStream(val)) {
|
|
16413
|
+
return val;
|
|
16414
|
+
}
|
|
16415
|
+
if (isDict(val)) {
|
|
16416
|
+
const out = /* @__PURE__ */ new Map();
|
|
16417
|
+
for (const [k, v] of val) out.set(k, rewrite(ctx, reader, v, depth + 1));
|
|
16418
|
+
return out;
|
|
16419
|
+
}
|
|
16420
|
+
return val;
|
|
16421
|
+
}
|
|
16422
|
+
function serializeStreamBody(ctx, reader, stream, depth = 0) {
|
|
16423
|
+
accountBytes(ctx, stream.data.length);
|
|
16424
|
+
const dict = /* @__PURE__ */ new Map();
|
|
16425
|
+
for (const [k, v] of stream.dict) {
|
|
16426
|
+
if (k === "Length") continue;
|
|
16427
|
+
dict.set(k, rewrite(ctx, reader, v, depth + 1));
|
|
16428
|
+
}
|
|
16429
|
+
dict.set("Length", stream.data.length);
|
|
16430
|
+
let body = serializeDict2(dict);
|
|
16431
|
+
body += "\nstream\n";
|
|
16432
|
+
body += bytesToLatin1(stream.data);
|
|
16433
|
+
body += "\nendstream";
|
|
16434
|
+
accountBytes(ctx, body.length - stream.data.length);
|
|
16435
|
+
return body;
|
|
16436
|
+
}
|
|
16437
|
+
function serializeValue2(val) {
|
|
16438
|
+
if (val === null) return "null";
|
|
16439
|
+
if (typeof val === "boolean") return val ? "true" : "false";
|
|
16440
|
+
if (typeof val === "number") {
|
|
16441
|
+
if (Number.isInteger(val)) return String(val);
|
|
16442
|
+
return val.toFixed(4).replace(/\.?0+$/, "");
|
|
16443
|
+
}
|
|
16444
|
+
if (typeof val === "string") return `(${escapePdfStr2(val)})`;
|
|
16445
|
+
if (isName(val)) return `/${val.value}`;
|
|
16446
|
+
if (isRef(val)) return `${val.num} ${val.gen} R`;
|
|
16447
|
+
if (isArray(val)) return "[" + val.map(serializeValue2).join(" ") + "]";
|
|
16448
|
+
if (isStream(val)) return serializeDict2(val.dict);
|
|
16449
|
+
if (isDict(val)) return serializeDict2(val);
|
|
16450
|
+
return "null";
|
|
16451
|
+
}
|
|
16452
|
+
function serializeDict2(dict) {
|
|
16453
|
+
let s = "<<";
|
|
16454
|
+
for (const [key, val] of dict) s += ` /${key} ${serializeValue2(val)}`;
|
|
16455
|
+
s += " >>";
|
|
16456
|
+
return s;
|
|
16457
|
+
}
|
|
16458
|
+
function escapePdfStr2(s) {
|
|
16459
|
+
return s.replace(/[\\()]/g, (c) => "\\" + c);
|
|
16460
|
+
}
|
|
16461
|
+
function bytesToLatin1(bytes) {
|
|
16462
|
+
let s = "";
|
|
16463
|
+
const CHUNK = 32768;
|
|
16464
|
+
for (let i = 0; i < bytes.length; i += CHUNK) {
|
|
16465
|
+
s += String.fromCharCode(...bytes.subarray(i, i + CHUNK));
|
|
16466
|
+
}
|
|
16467
|
+
return s;
|
|
16468
|
+
}
|
|
16469
|
+
function serializeDocument(ctx) {
|
|
16470
|
+
const maxNum = ctx.nextNum - 1;
|
|
16471
|
+
const parts = [];
|
|
16472
|
+
const offsets = new Array(maxNum + 1).fill(0);
|
|
16473
|
+
let offset = 0;
|
|
16474
|
+
function push(s) {
|
|
16475
|
+
parts.push(s);
|
|
16476
|
+
offset += s.length;
|
|
16477
|
+
}
|
|
16478
|
+
push("%PDF-1.7\n");
|
|
16479
|
+
push("%\xE2\xE3\xCF\xD3\n");
|
|
16480
|
+
for (let num = 1; num <= maxNum; num++) {
|
|
16481
|
+
const body = ctx.bodies.get(num);
|
|
16482
|
+
if (body === void 0) continue;
|
|
16483
|
+
offsets[num] = offset;
|
|
16484
|
+
push(`${num} 0 obj
|
|
16485
|
+
${body}
|
|
16486
|
+
endobj
|
|
16487
|
+
`);
|
|
16488
|
+
}
|
|
16489
|
+
const xrefOffset = offset;
|
|
16490
|
+
const size = maxNum + 1;
|
|
16491
|
+
let xref = `xref
|
|
16492
|
+
0 ${size}
|
|
16493
|
+
0000000000 65535 f
|
|
16494
|
+
`;
|
|
16495
|
+
for (let num = 1; num <= maxNum; num++) {
|
|
16496
|
+
xref += `${String(offsets[num]).padStart(10, "0")} 00000 n
|
|
16497
|
+
`;
|
|
16498
|
+
}
|
|
16499
|
+
push(xref);
|
|
16500
|
+
const id = bytesToHex(md5(latin1ToBytes(parts.join(""))));
|
|
16501
|
+
push(`trailer
|
|
16502
|
+
<< /Size ${size} /Root 1 0 R /ID [<${id}> <${id}>] >>
|
|
16503
|
+
`);
|
|
16504
|
+
push(`startxref
|
|
16505
|
+
${xrefOffset}
|
|
16506
|
+
%%EOF
|
|
16507
|
+
`);
|
|
16508
|
+
const full = parts.join("");
|
|
16509
|
+
const out = new Uint8Array(full.length);
|
|
16510
|
+
for (let i = 0; i < full.length; i++) out[i] = full.charCodeAt(i) & 255;
|
|
16511
|
+
return out;
|
|
16512
|
+
}
|
|
16513
|
+
function latin1ToBytes(s) {
|
|
16514
|
+
const out = new Uint8Array(s.length);
|
|
16515
|
+
for (let i = 0; i < s.length; i++) out[i] = s.charCodeAt(i) & 255;
|
|
16516
|
+
return out;
|
|
16517
|
+
}
|
|
16518
|
+
function bytesToHex(bytes) {
|
|
16519
|
+
let s = "";
|
|
16520
|
+
for (let i = 0; i < bytes.length; i++) s += bytes[i].toString(16).padStart(2, "0");
|
|
16521
|
+
return s;
|
|
16522
|
+
}
|
|
16523
|
+
|
|
15025
16524
|
// src/parser/pdf-ua-validator.ts
|
|
15026
16525
|
function pageContentText(reader, page) {
|
|
15027
16526
|
const contents = page.get("Contents");
|
|
@@ -15205,6 +16704,8 @@ exports.WORKER_TIMEOUT_MS = WORKER_TIMEOUT_MS;
|
|
|
15205
16704
|
exports.addSignaturePlaceholder = addSignaturePlaceholder;
|
|
15206
16705
|
exports.applyDecodeFilter = applyDecodeFilter;
|
|
15207
16706
|
exports.buildAcroFormDict = buildAcroFormDict;
|
|
16707
|
+
exports.buildAnnotation = buildAnnotation;
|
|
16708
|
+
exports.buildAnnotationBody = buildAnnotationBody;
|
|
15208
16709
|
exports.buildAppearanceStreamDict = buildAppearanceStreamDict;
|
|
15209
16710
|
exports.buildCmsSignedData = buildCmsSignedData;
|
|
15210
16711
|
exports.buildDocumentPDF = buildDocumentPDF;
|
|
@@ -15239,6 +16740,7 @@ exports.containsDevanagari = containsDevanagari;
|
|
|
15239
16740
|
exports.containsEthiopic = containsEthiopic;
|
|
15240
16741
|
exports.containsHebrew = containsHebrew;
|
|
15241
16742
|
exports.containsKhmer = containsKhmer;
|
|
16743
|
+
exports.containsMath = containsMath;
|
|
15242
16744
|
exports.containsMyanmar = containsMyanmar;
|
|
15243
16745
|
exports.containsRTL = containsRTL;
|
|
15244
16746
|
exports.containsSinhala = containsSinhala;
|
|
@@ -15284,11 +16786,13 @@ exports.encodePdfTextString = encodePdfTextString;
|
|
|
15284
16786
|
exports.estimateCmsSize = estimateCmsSize;
|
|
15285
16787
|
exports.estimateContentsSize = estimateContentsSize;
|
|
15286
16788
|
exports.extractGlyphContours = extractGlyphContours;
|
|
16789
|
+
exports.extractPages = extractPages;
|
|
15287
16790
|
exports.findStartxref = findStartxref;
|
|
15288
16791
|
exports.generateDataMatrix = generateDataMatrix;
|
|
15289
16792
|
exports.generatePDFInWorker = generatePDFInWorker;
|
|
15290
16793
|
exports.generatePDFMainThread = generatePDFMainThread;
|
|
15291
16794
|
exports.generateQR = generateQR;
|
|
16795
|
+
exports.getCryptoProvider = getCryptoProvider;
|
|
15292
16796
|
exports.getMaxInflateOutputSize = getMaxInflateOutputSize;
|
|
15293
16797
|
exports.getRegisteredLangs = getRegisteredLangs;
|
|
15294
16798
|
exports.getTrailerRef = getTrailerRef;
|
|
@@ -15301,6 +16805,7 @@ exports.inflateSync = inflateSync;
|
|
|
15301
16805
|
exports.initCrypto = initCrypto;
|
|
15302
16806
|
exports.initNodeCompression = initNodeCompression;
|
|
15303
16807
|
exports.initNodeDecompression_parser = initNodeDecompression;
|
|
16808
|
+
exports.inspectDocumentLayout = inspectDocumentLayout;
|
|
15304
16809
|
exports.isArmenianCodepoint = isArmenianCodepoint;
|
|
15305
16810
|
exports.isArray = isArray;
|
|
15306
16811
|
exports.isBengaliCodepoint = isBengaliCodepoint;
|
|
@@ -15311,6 +16816,7 @@ exports.isEthiopicCodepoint = isEthiopicCodepoint;
|
|
|
15311
16816
|
exports.isGeorgianCodepoint = isGeorgianCodepoint;
|
|
15312
16817
|
exports.isKhmerCodepoint = isKhmerCodepoint;
|
|
15313
16818
|
exports.isLinkAnnotation = isLinkAnnotation;
|
|
16819
|
+
exports.isMathCodepoint = isMathCodepoint;
|
|
15314
16820
|
exports.isMyanmarCodepoint = isMyanmarCodepoint;
|
|
15315
16821
|
exports.isName = isName;
|
|
15316
16822
|
exports.isRef = isRef;
|
|
@@ -15322,6 +16828,7 @@ exports.isTeluguCodepoint = isTeluguCodepoint;
|
|
|
15322
16828
|
exports.isTibetanCodepoint = isTibetanCodepoint;
|
|
15323
16829
|
exports.isValidPdfRgb = isValidPdfRgb;
|
|
15324
16830
|
exports.loadFontData = loadFontData;
|
|
16831
|
+
exports.mergePdfs = mergePdfs;
|
|
15325
16832
|
exports.nameValue = nameValue;
|
|
15326
16833
|
exports.needsUnicodeFont = needsUnicodeFont;
|
|
15327
16834
|
exports.normalizeBidiEmbeddings = normalizeBidiEmbeddings;
|
|
@@ -15361,6 +16868,7 @@ exports.rsaSign = rsaSign;
|
|
|
15361
16868
|
exports.rsaSignHash = rsaSignHash;
|
|
15362
16869
|
exports.rsaVerify = rsaVerify;
|
|
15363
16870
|
exports.rsaVerifyHash = rsaVerifyHash;
|
|
16871
|
+
exports.setCryptoProvider = setCryptoProvider;
|
|
15364
16872
|
exports.setDeflateImpl = setDeflateImpl;
|
|
15365
16873
|
exports.setInflateImpl = setInflateImpl;
|
|
15366
16874
|
exports.setMaxInflateOutputSize = setMaxInflateOutputSize;
|
|
@@ -15378,8 +16886,10 @@ exports.shapeThaiText = shapeThaiText;
|
|
|
15378
16886
|
exports.shapeTibetanText = shapeTibetanText;
|
|
15379
16887
|
exports.signPdfBytes = signPdfBytes;
|
|
15380
16888
|
exports.slugify = slugify;
|
|
16889
|
+
exports.splitPdf = splitPdf;
|
|
15381
16890
|
exports.splitTextByFont = splitTextByFont;
|
|
15382
16891
|
exports.streamByteLength = streamByteLength;
|
|
16892
|
+
exports.streamToFile = streamToFile;
|
|
15383
16893
|
exports.stripBidiControls = stripBidiControls;
|
|
15384
16894
|
exports.toBytes = toBytes;
|
|
15385
16895
|
exports.toWinAnsi = toWinAnsi;
|
|
@@ -15387,6 +16897,7 @@ exports.truncate = truncate;
|
|
|
15387
16897
|
exports.truncateToWidth = truncateToWidth;
|
|
15388
16898
|
exports.validateAttachments = validateAttachments;
|
|
15389
16899
|
exports.validateDocumentStreamable = validateDocumentStreamable;
|
|
16900
|
+
exports.validateFontData = validateFontData;
|
|
15390
16901
|
exports.validatePdfUA = validatePdfUA;
|
|
15391
16902
|
exports.validateTableStreamable = validateTableStreamable;
|
|
15392
16903
|
exports.validateURL = validateURL;
|