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