@reportwright/pdf 0.1.0-beta.3 → 0.1.0-beta.5
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 +6 -4
- package/dist/index.js +165 -70
- package/index.d.ts +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -322,7 +322,7 @@ PDF/A levels and the reader. The most used:
|
|
|
322
322
|
| `createPdf(sink, o)` / `toBytes(build, o)` | `title`, `author`, `creationDate`, `compress`, `metadata`, `pdfa`, `tagged`, `encrypt`, `sign`, `form.flatten`, `signal`, `timeoutMs`, `id` ([table](#createpdfsink-options)) |
|
|
323
323
|
| `pdf.embedFont(bytes, o)` | `subset`, `shaper`, `textMapping` ([Fonts](#fonts), [Shaping](#shaping)) |
|
|
324
324
|
| `page.text` / `page.textBox` | `x`, `y`, `font`, `size`, `color`, `width`, `align`, `maxLines`, `fallback`, `direction`, `kerning` ([Text options](#pages)) |
|
|
325
|
-
| `pdf.embedImage` / `page.image` | `colorSpace`, `maxDecodedBytes` / `width`, `height`, `fit`, `opacity`, `alt` ([Images](#images)) |
|
|
325
|
+
| `pdf.embedImage` / `page.image` | `colorSpace`, `maxDecodedBytes`, `compression` / `width`, `height`, `fit`, `opacity`, `alt` ([Images](#images)) |
|
|
326
326
|
| `page.fill` / `stroke` | `fill`, `stroke`, `width`, `dash`, `cap`, `join`, `opacity`, `blendMode`, `rule` ([Vector graphics](#vector-graphics)) |
|
|
327
327
|
| `encrypt` | `userPassword`, `ownerPassword`, `algorithm`, `permissions`, `encryptMetadata` ([Encryption](#encryption-1)) |
|
|
328
328
|
| `pdf.sign(field, o)` | `signer`, `reason`, `location`, `name`, `certify`, `timestamp`, `subFilter`, `reserve` ([Signatures](#signatures)) |
|
|
@@ -573,10 +573,12 @@ const logo = await pdf.embedImage(fs.readFileSync('logo.png')); // alpha
|
|
|
573
573
|
for (const y of [400, 500, 600]) page.image(logo, { x: 40, y, height: 24, opacity: 0.6 }); // written once
|
|
574
574
|
```
|
|
575
575
|
|
|
576
|
-
- `await pdf.embedImage(bytes, { colorSpace, maxDecodedBytes })` — a JPEG or PNG, written to the file at once as an image XObject;
|
|
576
|
+
- `await pdf.embedImage(bytes, { colorSpace, maxDecodedBytes, compression })` — a JPEG or PNG, written to the file at once as an image XObject;
|
|
577
577
|
returns a handle with `width` and `height` (pixels, after the EXIF orientation). `colorSpace`: an ICC colour space
|
|
578
578
|
(`pdf.iccColorSpace`) with the image's number of components, instead of the device space. `maxDecodedBytes`: the
|
|
579
|
-
decoded size allowed for a PNG that needs decoding (default 64 MB, 67,108,864 bytes).
|
|
579
|
+
decoded size allowed for a PNG that needs decoding (default 64 MB, 67,108,864 bytes). `compression`: the zlib level
|
|
580
|
+
for a PNG that is re-compressed (alpha or interlacing): `'default'` is 6, or 3 above 2 megapixels (about half the
|
|
581
|
+
time for 2–3% more bytes on photos); `'fast'` is 1; or a level 1–9. JPEGs and passed-through PNGs are not affected.
|
|
580
582
|
- **JPEG**: baseline and progressive, gray, RGB and CMYK, embedded unchanged (DCTDecode, no re-encoding). Adobe's
|
|
581
583
|
inverted CMYK (an APP14 "Adobe" segment) is drawn with `/Decode [1 0 1 0 1 0 1 0]`. The EXIF orientation (1–8)
|
|
582
584
|
is honoured. 12-bit, lossless, hierarchical and arithmetic-coded JPEGs are refused.
|
|
@@ -596,7 +598,7 @@ for (const y of [400, 500, 600]) page.image(logo, { x: 40, y, height: 24, opacit
|
|
|
596
598
|
PDFs: `alt` makes the image a `Figure` with that alternate text; without `alt` it is an artifact (decoration).
|
|
597
599
|
- Images are deduplicated automatically: `embedImage` called again with the same bytes (and the same options) in the
|
|
598
600
|
same document returns the same handle and writes nothing new (keyed by a SHA-256 of the bytes; Node's `node:crypto`,
|
|
599
|
-
else WebCrypto, else a fast hash confirmed byte for byte). A different option (`colorSpace`, `maxDecodedBytes`)
|
|
601
|
+
else WebCrypto, else a fast hash confirmed byte for byte). A different option (`colorSpace`, `maxDecodedBytes`, `compression`)
|
|
600
602
|
embeds it again.
|
|
601
603
|
- An image drawn any number of times, on any pages, is one XObject in the file. Images are always XObjects, never
|
|
602
604
|
inline images: an inline image would be repeated in every content stream and cannot have a soft mask.
|
package/dist/index.js
CHANGED
|
@@ -957,29 +957,32 @@ var init_browser = __esm({
|
|
|
957
957
|
});
|
|
958
958
|
|
|
959
959
|
// src/core/deflate.js
|
|
960
|
-
async function deflate(bytes) {
|
|
960
|
+
async function deflate(bytes, level = 6) {
|
|
961
961
|
if (slots.free > 0) slots.free--;
|
|
962
962
|
else await new Promise((ok) => slots.wait.push(
|
|
963
963
|
/** @type {() => void} */
|
|
964
964
|
ok
|
|
965
965
|
));
|
|
966
966
|
try {
|
|
967
|
-
return await now(bytes);
|
|
967
|
+
return await now(bytes, level);
|
|
968
968
|
} finally {
|
|
969
969
|
const next = slots.wait.shift();
|
|
970
970
|
if (next) next();
|
|
971
971
|
else slots.free++;
|
|
972
972
|
}
|
|
973
973
|
}
|
|
974
|
-
async function now(bytes) {
|
|
975
|
-
if (NODE_ZLIB) return new Promise((ok, fail2) => NODE_ZLIB.deflate(bytes, { level
|
|
976
|
-
if (typeof CompressionStream === "undefined") return zlibSync(bytes, { level:
|
|
974
|
+
async function now(bytes, level) {
|
|
975
|
+
if (NODE_ZLIB) return new Promise((ok, fail2) => NODE_ZLIB.deflate(bytes, { level }, (e, z) => e ? fail2(e) : ok(z)));
|
|
976
|
+
if (typeof CompressionStream === "undefined" || level !== 6) return zlibSync(bytes, { level: (
|
|
977
|
+
/** @type {any} */
|
|
978
|
+
level
|
|
979
|
+
) });
|
|
977
980
|
return new Uint8Array(await new Response(new Blob([
|
|
978
981
|
/** @type {any} */
|
|
979
982
|
bytes
|
|
980
983
|
]).stream().pipeThrough(new CompressionStream("deflate"))).arrayBuffer());
|
|
981
984
|
}
|
|
982
|
-
function deflateStream() {
|
|
985
|
+
function deflateStream(level = 6) {
|
|
983
986
|
const out = [];
|
|
984
987
|
const join = () => {
|
|
985
988
|
let n = 0;
|
|
@@ -993,7 +996,7 @@ function deflateStream() {
|
|
|
993
996
|
return z;
|
|
994
997
|
};
|
|
995
998
|
if (NODE_ZLIB) {
|
|
996
|
-
const z = NODE_ZLIB.createDeflate({ level:
|
|
999
|
+
const z = NODE_ZLIB.createDeflate({ level, chunkSize: 1 << 20 });
|
|
997
1000
|
const ended = new Promise((ok, fail2) => {
|
|
998
1001
|
z.on("data", (c) => out.push(c));
|
|
999
1002
|
z.on("end", ok);
|
|
@@ -1017,7 +1020,19 @@ function deflateStream() {
|
|
|
1017
1020
|
if (typeof CompressionStream === "undefined") return { async push(b) {
|
|
1018
1021
|
out.push(b.slice());
|
|
1019
1022
|
}, async end() {
|
|
1020
|
-
return zlibSync(join(), { level:
|
|
1023
|
+
return zlibSync(join(), { level: (
|
|
1024
|
+
/** @type {any} */
|
|
1025
|
+
level
|
|
1026
|
+
) });
|
|
1027
|
+
}, abort() {
|
|
1028
|
+
} };
|
|
1029
|
+
if (level !== 6) return { async push(b) {
|
|
1030
|
+
out.push(b.slice());
|
|
1031
|
+
}, async end() {
|
|
1032
|
+
return zlibSync(join(), { level: (
|
|
1033
|
+
/** @type {any} */
|
|
1034
|
+
level
|
|
1035
|
+
) });
|
|
1021
1036
|
}, abort() {
|
|
1022
1037
|
} };
|
|
1023
1038
|
const cs = new CompressionStream("deflate"), w = cs.writable.getWriter();
|
|
@@ -3432,6 +3447,7 @@ var init_links = __esm({
|
|
|
3432
3447
|
|
|
3433
3448
|
// src/images/png.js
|
|
3434
3449
|
function crc32(b, from, to) {
|
|
3450
|
+
if (NODE_ZLIB2?.crc32) return NODE_ZLIB2.crc32(b.subarray(from, to));
|
|
3435
3451
|
if (!CRC_TABLE) {
|
|
3436
3452
|
CRC_TABLE = new Uint32Array(256);
|
|
3437
3453
|
for (let n = 0; n < 256; n++) {
|
|
@@ -3572,16 +3588,24 @@ function unfilterRow(f, c, p, bpp) {
|
|
|
3572
3588
|
}
|
|
3573
3589
|
for (let i2 = 0; i2 < bpp; i2++) c[i2] += p[i2];
|
|
3574
3590
|
for (let i2 = bpp; i2 < rb; i2++) {
|
|
3575
|
-
const a = c[i2 - bpp], u = p[i2], cc = p[i2 - bpp]
|
|
3576
|
-
|
|
3591
|
+
const a = c[i2 - bpp], u = p[i2], cc = p[i2 - bpp];
|
|
3592
|
+
let pa = u - cc, pb = a - cc, pc = pa + pb, k;
|
|
3593
|
+
k = pa >> 31;
|
|
3594
|
+
pa = (pa ^ k) - k;
|
|
3595
|
+
k = pb >> 31;
|
|
3596
|
+
pb = (pb ^ k) - k;
|
|
3597
|
+
k = pc >> 31;
|
|
3598
|
+
pc = (pc ^ k) - k;
|
|
3599
|
+
c[i2] += a ^ (a ^ (u ^ (u ^ cc) & pc - pb >> 31)) & (pb - pa | pc - pa) >> 31;
|
|
3577
3600
|
}
|
|
3578
3601
|
}
|
|
3579
3602
|
function filterRow(x2, up, bpp, out, o, t) {
|
|
3580
3603
|
const rb = x2.length, m = Math.min(bpp, rb);
|
|
3581
|
-
let best = 0, sum = 0;
|
|
3604
|
+
let best = 0, sum = 0, s, k;
|
|
3582
3605
|
for (let i2 = 0; i2 < rb; i2++) {
|
|
3583
|
-
|
|
3584
|
-
|
|
3606
|
+
s = x2[i2] << 24 >> 24;
|
|
3607
|
+
k = s >> 31;
|
|
3608
|
+
sum += (s ^ k) - k;
|
|
3585
3609
|
}
|
|
3586
3610
|
out[o] = 0;
|
|
3587
3611
|
out.set(x2, o + 1);
|
|
@@ -3590,12 +3614,15 @@ function filterRow(x2, up, bpp, out, o, t) {
|
|
|
3590
3614
|
for (let i2 = 0; i2 < m; i2++) {
|
|
3591
3615
|
const v = x2[i2];
|
|
3592
3616
|
t[i2] = v;
|
|
3593
|
-
|
|
3617
|
+
s = v << 24 >> 24;
|
|
3618
|
+
k = s >> 31;
|
|
3619
|
+
sum += (s ^ k) - k;
|
|
3594
3620
|
}
|
|
3595
3621
|
for (let i2 = m; i2 < rb; i2++) {
|
|
3596
|
-
|
|
3597
|
-
t[i2] =
|
|
3598
|
-
|
|
3622
|
+
s = x2[i2] - x2[i2 - bpp] << 24 >> 24;
|
|
3623
|
+
t[i2] = s;
|
|
3624
|
+
k = s >> 31;
|
|
3625
|
+
sum += (s ^ k) - k;
|
|
3599
3626
|
}
|
|
3600
3627
|
if (sum < best) {
|
|
3601
3628
|
best = sum;
|
|
@@ -3605,9 +3632,10 @@ function filterRow(x2, up, bpp, out, o, t) {
|
|
|
3605
3632
|
if (!up) return;
|
|
3606
3633
|
sum = 0;
|
|
3607
3634
|
for (let i2 = 0; i2 < rb; i2++) {
|
|
3608
|
-
|
|
3609
|
-
t[i2] =
|
|
3610
|
-
|
|
3635
|
+
s = x2[i2] - up[i2] << 24 >> 24;
|
|
3636
|
+
t[i2] = s;
|
|
3637
|
+
k = s >> 31;
|
|
3638
|
+
sum += (s ^ k) - k;
|
|
3611
3639
|
}
|
|
3612
3640
|
if (sum < best) {
|
|
3613
3641
|
best = sum;
|
|
@@ -3616,15 +3644,25 @@ function filterRow(x2, up, bpp, out, o, t) {
|
|
|
3616
3644
|
}
|
|
3617
3645
|
sum = 0;
|
|
3618
3646
|
for (let i2 = 0; i2 < m; i2++) {
|
|
3619
|
-
|
|
3620
|
-
t[i2] =
|
|
3621
|
-
|
|
3647
|
+
s = x2[i2] - up[i2] << 24 >> 24;
|
|
3648
|
+
t[i2] = s;
|
|
3649
|
+
k = s >> 31;
|
|
3650
|
+
sum += (s ^ k) - k;
|
|
3622
3651
|
}
|
|
3623
3652
|
for (let i2 = m; i2 < rb; i2++) {
|
|
3624
|
-
const a = x2[i2 - bpp], u = up[i2], c = up[i2 - bpp]
|
|
3625
|
-
|
|
3626
|
-
|
|
3627
|
-
|
|
3653
|
+
const a = x2[i2 - bpp], u = up[i2], c = up[i2 - bpp];
|
|
3654
|
+
let pa = u - c, pb = a - c, pc = pa + pb;
|
|
3655
|
+
k = pa >> 31;
|
|
3656
|
+
pa = (pa ^ k) - k;
|
|
3657
|
+
k = pb >> 31;
|
|
3658
|
+
pb = (pb ^ k) - k;
|
|
3659
|
+
k = pc >> 31;
|
|
3660
|
+
pc = (pc ^ k) - k;
|
|
3661
|
+
const notA = (pb - pa | pc - pa) >> 31, uc = u ^ (u ^ c) & pc - pb >> 31;
|
|
3662
|
+
s = x2[i2] - (a ^ (a ^ uc) & notA) << 24 >> 24;
|
|
3663
|
+
t[i2] = s;
|
|
3664
|
+
k = s >> 31;
|
|
3665
|
+
sum += (s ^ k) - k;
|
|
3628
3666
|
}
|
|
3629
3667
|
if (sum < best) {
|
|
3630
3668
|
out[o] = 4;
|
|
@@ -3857,13 +3895,14 @@ async function embedImage(doc, bytes, opt = {}) {
|
|
|
3857
3895
|
/** @type {any} */
|
|
3858
3896
|
opt[k2]
|
|
3859
3897
|
) : void 0;
|
|
3860
|
-
const o = { colorSpace: own2("colorSpace"), maxDecodedBytes: own2("maxDecodedBytes") ?? MAX_DECODED };
|
|
3898
|
+
const o = { colorSpace: own2("colorSpace"), maxDecodedBytes: own2("maxDecodedBytes") ?? MAX_DECODED, compression: own2("compression") ?? "default" };
|
|
3899
|
+
if (!(o.compression === "default" || o.compression === "fast" || Number.isInteger(o.compression) && o.compression >= 1 && o.compression <= 9)) throw new TypeError("embedImage: compression is 'default', 'fast' or a zlib level 1–9");
|
|
3861
3900
|
if (!(Number.isSafeInteger(o.maxDecodedBytes) && o.maxDecodedBytes > 0)) throw new TypeError("embedImage: maxDecodedBytes is a whole number of bytes > 0");
|
|
3862
3901
|
if (b.length > MAX_INPUT) throw new RangeError(`embedImage: at most ${MAX_INPUT >> 20} MB`);
|
|
3863
3902
|
let seen = SEEN.get(doc);
|
|
3864
3903
|
if (!seen) SEEN.set(doc, seen = /* @__PURE__ */ new Map());
|
|
3865
3904
|
const { key, copy } = await digest(b);
|
|
3866
|
-
const k = `${key} ${o.colorSpace == null ? "-" : idOf(o.colorSpace)} ${o.maxDecodedBytes}`;
|
|
3905
|
+
const k = `${key} ${o.colorSpace == null ? "-" : idOf(o.colorSpace)} ${o.maxDecodedBytes} ${o.compression}`;
|
|
3867
3906
|
const hit = seen.get(k);
|
|
3868
3907
|
if (hit && (!hit.copy || same(hit.copy, b))) return hit.img;
|
|
3869
3908
|
const img = embed(doc, b, o);
|
|
@@ -3921,7 +3960,8 @@ async function embed(doc, b, o) {
|
|
|
3921
3960
|
let color, a = null;
|
|
3922
3961
|
if (p.filtered) color = [p.data, parms(ch, p.bpc)];
|
|
3923
3962
|
else {
|
|
3924
|
-
const
|
|
3963
|
+
const lv = o.compression === "fast" ? 1 : o.compression === "default" ? w * h > 2e6 ? 3 : 6 : o.compression;
|
|
3964
|
+
const ce = encoder(h, rb, bpp, parms(ch, p.bpc), lv), ae = p.alpha ? encoder(h, arb, Math.max(1, abpc >> 3), parms(1, abpc), lv) : null;
|
|
3925
3965
|
try {
|
|
3926
3966
|
await p.rows(async (c, al) => {
|
|
3927
3967
|
await Promise.all([ce.row(c), ae && al ? ae.row(al) : null]);
|
|
@@ -3957,8 +3997,8 @@ async function embed(doc, b, o) {
|
|
|
3957
3997
|
{ ref, w: turned ? h : w, h: turned ? w : h, orient: ORIENT[orientation], alpha }
|
|
3958
3998
|
);
|
|
3959
3999
|
}
|
|
3960
|
-
function encoder(rows, rb, bpp, dp) {
|
|
3961
|
-
const z = deflateStream(), per = Math.max(1, Math.floor((1 << 20) / (rb + 1))), t = new Uint8Array(rb);
|
|
4000
|
+
function encoder(rows, rb, bpp, dp, level) {
|
|
4001
|
+
const z = deflateStream(level), per = Math.max(1, Math.floor((1 << 20) / (rb + 1))), t = new Uint8Array(rb);
|
|
3962
4002
|
const plain2 = rows * rb <= 256 << 10 ? new Uint8Array(rows * rb) : null;
|
|
3963
4003
|
const strips = [new Uint8Array(per * (rb + 1)), new Uint8Array(per * (rb + 1))], busy = [Promise.resolve(), Promise.resolve()];
|
|
3964
4004
|
let s = 0, strip = strips[0], k = 0, y = 0;
|
|
@@ -3982,7 +4022,7 @@ function encoder(rows, rb, bpp, dp) {
|
|
|
3982
4022
|
await busy[s ^ 1];
|
|
3983
4023
|
const f = await z.end();
|
|
3984
4024
|
if (!plain2) return [f, dp];
|
|
3985
|
-
const p = await deflate(plain2);
|
|
4025
|
+
const p = await deflate(plain2, level);
|
|
3986
4026
|
return p.length <= f.length + dp.length ? [p, ""] : [f, dp];
|
|
3987
4027
|
},
|
|
3988
4028
|
abort: () => z.abort()
|
|
@@ -12364,13 +12404,13 @@ function createForm(doc, opt = {}) {
|
|
|
12364
12404
|
if (!Array.isArray(list) || list.length > MAX_OPTIONS) throw new RangeError(`${who}: options is an array of at most ${MAX_OPTIONS} options`);
|
|
12365
12405
|
const P = placeOf(o, who), L = lookOf(o, who, { border: 1 }), tip = tipOf(o, who);
|
|
12366
12406
|
const { t, da } = textOf(P.page, o, who), q = qOf(o.align, who);
|
|
12367
|
-
let
|
|
12407
|
+
let chars3 = 0;
|
|
12368
12408
|
const index = /* @__PURE__ */ new Map();
|
|
12369
12409
|
const opts = list.map((it, i2) => {
|
|
12370
12410
|
const pair = Array.isArray(it) ? it : [it, it];
|
|
12371
12411
|
if (pair.length !== 2) throw new TypeError(`${who}: an option is a string or [export, display]`);
|
|
12372
12412
|
const [ex, dp] = pair.map((v) => str2(v, "an option", MAX_TOOLTIP, who));
|
|
12373
|
-
if ((
|
|
12413
|
+
if ((chars3 += ex.length + dp.length) > MAX_OPTION_CHARS) throw new RangeError(`${who}: the options have more than ${MAX_OPTION_CHARS} characters in all`);
|
|
12374
12414
|
if (index.has(ex)) throw new TypeError(`${who}: two options have the export value ${JSON.stringify(ex.slice(0, 80))}`);
|
|
12375
12415
|
index.set(ex, i2);
|
|
12376
12416
|
return { ex, dp };
|
|
@@ -13233,8 +13273,9 @@ function indexOf(buf, needle, from, to = buf.length) {
|
|
|
13233
13273
|
to = Math.min(to, buf.length);
|
|
13234
13274
|
if (to - from > 64) {
|
|
13235
13275
|
if (NodeBuffer) {
|
|
13236
|
-
|
|
13237
|
-
|
|
13276
|
+
let v = VIEWS.get(buf);
|
|
13277
|
+
if (!v) VIEWS.set(buf, v = NodeBuffer.from(buf.buffer, buf.byteOffset, buf.length));
|
|
13278
|
+
return (to === v.length ? v : v.subarray(0, to)).indexOf(needle, from, "latin1");
|
|
13238
13279
|
}
|
|
13239
13280
|
for (let i2 = buf.indexOf(n0, from); i2 >= 0 && i2 + L <= to; i2 = buf.indexOf(n0, i2 + 1)) {
|
|
13240
13281
|
let j = 1;
|
|
@@ -13258,7 +13299,7 @@ function lastIndexOf(buf, needle, to, span = to) {
|
|
|
13258
13299
|
}
|
|
13259
13300
|
return -1;
|
|
13260
13301
|
}
|
|
13261
|
-
var MAX_DEPTH4, MAX_ITEMS, Ref, PdfString, PdfStream, Op, CLASS, T, HEX, Lexer, parseValueEnd, NodeBuffer;
|
|
13302
|
+
var MAX_DEPTH4, MAX_ITEMS, Ref, PdfString, PdfStream, Op, CLASS, T, HEX, Lexer, parseValueEnd, NodeBuffer, VIEWS;
|
|
13262
13303
|
var init_lexer = __esm({
|
|
13263
13304
|
"src/reader/lexer.js"() {
|
|
13264
13305
|
init_budget();
|
|
@@ -13429,7 +13470,7 @@ var init_lexer = __esm({
|
|
|
13429
13470
|
}
|
|
13430
13471
|
this.budget.tick(1 + (n >> 10));
|
|
13431
13472
|
this.pos = i2;
|
|
13432
|
-
this.val = new PdfString(out.slice(0, n), false);
|
|
13473
|
+
this.val = new PdfString(n === out.length ? out : out.slice(0, n), false);
|
|
13433
13474
|
return T.STR;
|
|
13434
13475
|
}
|
|
13435
13476
|
/** @param {number} i after "<" */
|
|
@@ -13451,7 +13492,7 @@ var init_lexer = __esm({
|
|
|
13451
13492
|
if (hi >= 0) out[n++] = hi << 4;
|
|
13452
13493
|
this.budget.tick(1 + (n >> 10));
|
|
13453
13494
|
this.pos = j < e ? j + 1 : j;
|
|
13454
|
-
this.val = new PdfString(out.slice(0, n), true);
|
|
13495
|
+
this.val = new PdfString(n === out.length ? out : out.slice(0, n), true);
|
|
13455
13496
|
return T.STR;
|
|
13456
13497
|
}
|
|
13457
13498
|
/** @param {number} i after "/" */
|
|
@@ -13483,6 +13524,8 @@ var init_lexer = __esm({
|
|
|
13483
13524
|
parseValueEnd = () => void 0;
|
|
13484
13525
|
NodeBuffer = /** @type {any} */
|
|
13485
13526
|
globalThis.Buffer;
|
|
13527
|
+
VIEWS = /** @type {WeakMap<Uint8Array, any>} */
|
|
13528
|
+
/* @__PURE__ */ new WeakMap();
|
|
13486
13529
|
}
|
|
13487
13530
|
});
|
|
13488
13531
|
|
|
@@ -14644,14 +14687,26 @@ function decodeText(v) {
|
|
|
14644
14687
|
if (!(v instanceof PdfString)) return typeof v === "string" ? v : v == null ? "" : String(v);
|
|
14645
14688
|
const b = v.bytes;
|
|
14646
14689
|
if (b.length >= 2 && b[0] === 254 && b[1] === 255) {
|
|
14647
|
-
|
|
14648
|
-
for (let
|
|
14649
|
-
return
|
|
14690
|
+
const u2 = new Uint16Array(b.length - 2 >> 1);
|
|
14691
|
+
for (let i3 = 0; i3 < u2.length; i3++) u2[i3] = b[2 + 2 * i3] << 8 | b[3 + 2 * i3];
|
|
14692
|
+
return chars2(u2);
|
|
14650
14693
|
}
|
|
14651
14694
|
if (b.length >= 3 && b[0] === 239 && b[1] === 187 && b[2] === 191) return new TextDecoder().decode(b.subarray(3));
|
|
14652
|
-
let
|
|
14653
|
-
|
|
14654
|
-
return
|
|
14695
|
+
let i2 = 0;
|
|
14696
|
+
while (i2 < b.length && DOC_T[b[i2]] === b[i2]) i2++;
|
|
14697
|
+
if (i2 === b.length) return latin12(b);
|
|
14698
|
+
const u = new Uint16Array(b.length);
|
|
14699
|
+
for (i2 = 0; i2 < b.length; i2++) u[i2] = DOC_T[b[i2]];
|
|
14700
|
+
return chars2(u);
|
|
14701
|
+
}
|
|
14702
|
+
function chars2(u) {
|
|
14703
|
+
const parts = [];
|
|
14704
|
+
for (let i2 = 0; i2 < u.length; i2 += 8192) parts.push(String.fromCharCode.apply(
|
|
14705
|
+
null,
|
|
14706
|
+
/** @type {any} */
|
|
14707
|
+
u.subarray(i2, i2 + 8192)
|
|
14708
|
+
));
|
|
14709
|
+
return parts.join("");
|
|
14655
14710
|
}
|
|
14656
14711
|
function glyphText(n) {
|
|
14657
14712
|
if (Object.hasOwn(NAMES2, n)) return NAMES2[n];
|
|
@@ -14689,11 +14744,11 @@ function parseCMap(data, budget, held = { n: 0 }) {
|
|
|
14689
14744
|
for (let i2 = 0; i2 + 1 < b.length; i2 += 2) u.push(b[i2] << 8 | b[i2 + 1]);
|
|
14690
14745
|
return String.fromCharCode(...u);
|
|
14691
14746
|
};
|
|
14692
|
-
let
|
|
14747
|
+
let chars3 = 0;
|
|
14693
14748
|
const set2 = (c, t) => {
|
|
14694
|
-
|
|
14749
|
+
chars3 += t.length;
|
|
14695
14750
|
held.n += 8 + t.length;
|
|
14696
|
-
if (
|
|
14751
|
+
if (chars3 > MAX_CMAP_CHARS) fail("E_BUDGET", `A CMap maps codes to more than ${MAX_CMAP_CHARS.toLocaleString("en")} characters`);
|
|
14697
14752
|
if (held.n > MAX_HELD) fail("E_BUDGET", `A page's fonts hold more than ${MAX_HELD.toLocaleString("en")} units of code mappings (an entry 8, a character 1)`);
|
|
14698
14753
|
map.set(c, t);
|
|
14699
14754
|
};
|
|
@@ -15012,12 +15067,30 @@ function pageText(src, pageDict, resources, raw = false) {
|
|
|
15012
15067
|
const PX = [], PY = [], PE = [], PS = [], PG = [];
|
|
15013
15068
|
const PA = [], PAE = [], PC = [], PD = [];
|
|
15014
15069
|
const PT = [];
|
|
15015
|
-
let
|
|
15070
|
+
let chars3 = 0;
|
|
15016
15071
|
let moved = true;
|
|
15017
15072
|
const fonts = /* @__PURE__ */ new Map();
|
|
15018
|
-
|
|
15073
|
+
let run2 = [];
|
|
15074
|
+
let runs = false;
|
|
15075
|
+
const flush = () => {
|
|
15076
|
+
if (run2.length) {
|
|
15077
|
+
PT[PT.length - 1] += run2.join("");
|
|
15078
|
+
run2 = [];
|
|
15079
|
+
}
|
|
15080
|
+
};
|
|
15081
|
+
const add2 = (x2, y, end, size, text3, d0, d1, d2, d3, glyph = false) => {
|
|
15082
|
+
const n = PT.length - 1;
|
|
15083
|
+
if (glyph && runs && !moved && !(PG[n] & 1) && PD[n] === d3 && PC[n] === d2 && PS[n] === size && d0 >= PA[n] && d0 >= PAE[n] - 0.01 && d0 - PAE[n] <= Math.max(size, 1) * 0.25 && !RTL2.test(text3)) {
|
|
15084
|
+
if ((chars3 += text3.length) > MAX_TEXT_CHARS) throw new PdfReadError("E_BUDGET", `A page makes more than ${MAX_TEXT_CHARS.toLocaleString("en")} characters of text`);
|
|
15085
|
+
run2.push(text3);
|
|
15086
|
+
if (run2.length >= 4096) flush();
|
|
15087
|
+
PAE[n] = Math.max(PAE[n], d1);
|
|
15088
|
+
return;
|
|
15089
|
+
}
|
|
15090
|
+
flush();
|
|
15091
|
+
runs = glyph && !raw && !RTL2.test(text3);
|
|
15019
15092
|
if (PT.length >= budget.maxTextItems) throw new PdfReadError("E_BUDGET", `A page has more than ${budget.maxTextItems.toLocaleString("en")} pieces of text (budget.maxTextItems)`);
|
|
15020
|
-
if ((
|
|
15093
|
+
if ((chars3 += text3.length) > MAX_TEXT_CHARS) throw new PdfReadError("E_BUDGET", `A page makes more than ${MAX_TEXT_CHARS.toLocaleString("en")} characters of text`);
|
|
15021
15094
|
if (raw) {
|
|
15022
15095
|
PX.push(x2);
|
|
15023
15096
|
PY.push(y);
|
|
@@ -15081,7 +15154,7 @@ function pageText(src, pageDict, resources, raw = false) {
|
|
|
15081
15154
|
}
|
|
15082
15155
|
const n = PT.length - 1;
|
|
15083
15156
|
if (text3.includes("")) text3 = text3.replaceAll("", "");
|
|
15084
|
-
if (text3) add2(x2, y, end, size, text3, d0, d1, d2, d3);
|
|
15157
|
+
if (text3) add2(x2, y, end, size, text3, d0, d1, d2, d3, true);
|
|
15085
15158
|
else if (n >= 0 && PAE[n] < d1 && PD[n] === d3 && Math.abs(PC[n] - d2) < 0.01) {
|
|
15086
15159
|
if (raw) PE[n] = end;
|
|
15087
15160
|
PAE[n] = d1;
|
|
@@ -15247,6 +15320,7 @@ function pageText(src, pageDict, resources, raw = false) {
|
|
|
15247
15320
|
default:
|
|
15248
15321
|
}
|
|
15249
15322
|
}
|
|
15323
|
+
flush();
|
|
15250
15324
|
if (raw) return PT.map((text3, i2) => ({ x: PX[i2], y: PY[i2], end: PE[i2], size: PS[i2], text: text3, gap: !!(PG[i2] & 1) }));
|
|
15251
15325
|
const out = [];
|
|
15252
15326
|
for (let i2 = 0; i2 < PT.length; ) {
|
|
@@ -15273,7 +15347,7 @@ function pageText(src, pageDict, resources, raw = false) {
|
|
|
15273
15347
|
}
|
|
15274
15348
|
return out.join("");
|
|
15275
15349
|
}
|
|
15276
|
-
var MAX_FORMS, MAX_CMAP, MAX_MARKS, MAX_ACTUAL, MAX_TEXT_CHARS, MAX_DST, MAX_CMAP_CHARS, MAX_HELD, MAX_W, MAX_DO, TEXT_TIMEOUT_MS, DOC, WIN, WIN_T, MAC_HIGH, BASE, STD_HIGH, NAMES2, ACCENT, own, STD_ALIAS, NONE, stops, MAX_CMAP_FONTS, mul6, mul2, ID, matrixOf, C0, G0, G1, RTL2, STRONG_L, NUM, rev2;
|
|
15350
|
+
var MAX_FORMS, MAX_CMAP, MAX_MARKS, MAX_ACTUAL, MAX_TEXT_CHARS, MAX_DST, MAX_CMAP_CHARS, MAX_HELD, MAX_W, MAX_DO, TEXT_TIMEOUT_MS, DOC, DOC_T, WIN, WIN_T, MAC_HIGH, BASE, STD_HIGH, NAMES2, ACCENT, own, STD_ALIAS, NONE, stops, MAX_CMAP_FONTS, mul6, mul2, ID, matrixOf, C0, G0, G1, RTL2, STRONG_L, NUM, rev2;
|
|
15277
15351
|
var init_text2 = __esm({
|
|
15278
15352
|
"src/reader/text.js"() {
|
|
15279
15353
|
init_lexer();
|
|
@@ -15333,6 +15407,7 @@ var init_text2 = __esm({
|
|
|
15333
15407
|
[158, 382],
|
|
15334
15408
|
[160, 8364]
|
|
15335
15409
|
]);
|
|
15410
|
+
DOC_T = new Uint16Array(256).map((_, c) => DOC.get(c) ?? c);
|
|
15336
15411
|
WIN = /* @__PURE__ */ new Map();
|
|
15337
15412
|
{
|
|
15338
15413
|
const p = ENCODINGS.WinAnsi;
|
|
@@ -15732,9 +15807,15 @@ function numText(v) {
|
|
|
15732
15807
|
return Math.abs(v) < 1 ? v.toFixed(10).replace(/\.?0+$/, "") || "0" : v.toFixed(0);
|
|
15733
15808
|
}
|
|
15734
15809
|
function hexText(b) {
|
|
15735
|
-
|
|
15736
|
-
|
|
15737
|
-
|
|
15810
|
+
if (NodeBuffer2 && b.length > 64) return `<${NodeBuffer2.from(b.buffer, b.byteOffset, b.length).toString("hex")}>`;
|
|
15811
|
+
const parts = ["<"];
|
|
15812
|
+
for (let i2 = 0; i2 < b.length; i2 += 4096) {
|
|
15813
|
+
let s = "";
|
|
15814
|
+
for (let k = i2; k < Math.min(b.length, i2 + 4096); k++) s += HEXD[b[k] >> 4] + HEXD[b[k] & 15];
|
|
15815
|
+
parts.push(s);
|
|
15816
|
+
}
|
|
15817
|
+
parts.push(">");
|
|
15818
|
+
return parts.join("");
|
|
15738
15819
|
}
|
|
15739
15820
|
function createCopier(t) {
|
|
15740
15821
|
const maps = /* @__PURE__ */ new Map();
|
|
@@ -16136,7 +16217,7 @@ function createCopier(t) {
|
|
|
16136
16217
|
}
|
|
16137
16218
|
};
|
|
16138
16219
|
}
|
|
16139
|
-
var SHARE, GRAY, HEXD, Raw, Foreign, AUDIT, OPEN_A, CLOSE_A, OPEN_D, CLOSE_D;
|
|
16220
|
+
var SHARE, GRAY, HEXD, NodeBuffer2, Raw, Foreign, AUDIT, OPEN_A, CLOSE_A, OPEN_D, CLOSE_D;
|
|
16140
16221
|
var init_copy = __esm({
|
|
16141
16222
|
"src/reader/copy.js"() {
|
|
16142
16223
|
init_lexer();
|
|
@@ -16147,6 +16228,8 @@ var init_copy = __esm({
|
|
|
16147
16228
|
SHARE = /* @__PURE__ */ new Set(["Font", "FontDescriptor", "ExtGState", "Encoding", "Pattern", "Shading", "Halftone", "Function", "XObject", "Mask", "CMap", "OutputIntent", "Filespec", "EmbeddedFile"]);
|
|
16148
16229
|
GRAY = -2;
|
|
16149
16230
|
HEXD = "0123456789abcdef";
|
|
16231
|
+
NodeBuffer2 = /** @type {any} */
|
|
16232
|
+
globalThis.Buffer;
|
|
16150
16233
|
Raw = class {
|
|
16151
16234
|
/** @param {string} s */
|
|
16152
16235
|
constructor(s) {
|
|
@@ -16438,7 +16521,8 @@ async function run(self, sink, o) {
|
|
|
16438
16521
|
if (level && o.encrypt) throw new TypeError("save: PDF/A forbids encryption: pass pdfa: false to drop the PDF/A claim and encrypt");
|
|
16439
16522
|
const keepStruct = single && o.keepStructure !== false && !self.edits.flatten;
|
|
16440
16523
|
const info = self.info();
|
|
16441
|
-
const
|
|
16524
|
+
const tooBig = ["title", "author", "subject", "keywords"].some((k) => typeof info[k] === "string" && info[k].length > INFO_MAX);
|
|
16525
|
+
const edited = !!self.edits.info || tooBig || level?.conformance !== self.pdfa()?.conformance;
|
|
16442
16526
|
const uaLost = !keepStruct && /pdfuaid:part/.test(self.xmp() ?? "");
|
|
16443
16527
|
const srcXmp = self.edits.xmp ?? (edited || uaLost || !level && self.pdfa() ? null : self.xmp());
|
|
16444
16528
|
const created = o.creationDate ?? (info.creationDate instanceof Date ? info.creationDate : /* @__PURE__ */ new Date());
|
|
@@ -16451,6 +16535,13 @@ async function run(self, sink, o) {
|
|
|
16451
16535
|
warn("the source's output intent profile was not usable; the bundled sRGB profile was written instead");
|
|
16452
16536
|
}
|
|
16453
16537
|
}
|
|
16538
|
+
const infoText = (k) => {
|
|
16539
|
+
const v = info[k];
|
|
16540
|
+
if (v == null) return void 0;
|
|
16541
|
+
if (v.length <= INFO_MAX) return v;
|
|
16542
|
+
warn(`the source's ${k} (${v.length.toLocaleString("en")} characters) was cut to ${INFO_MAX.toLocaleString("en")}`);
|
|
16543
|
+
return v.slice(0, INFO_MAX);
|
|
16544
|
+
};
|
|
16454
16545
|
const pdf = (
|
|
16455
16546
|
/** @type {any} */
|
|
16456
16547
|
createPdf(sink, {
|
|
@@ -16461,10 +16552,10 @@ async function run(self, sink, o) {
|
|
|
16461
16552
|
creationDate: created,
|
|
16462
16553
|
metadata: !!(level || self.edits.xmp != null || self.xmp() != null),
|
|
16463
16554
|
// an XMP packet only when the source had one or PDF/A needs one: a file split into pages is not a kilobyte of XMP each
|
|
16464
|
-
title:
|
|
16465
|
-
author:
|
|
16466
|
-
subject:
|
|
16467
|
-
keywords:
|
|
16555
|
+
title: infoText("title"),
|
|
16556
|
+
author: infoText("author"),
|
|
16557
|
+
subject: infoText("subject"),
|
|
16558
|
+
keywords: infoText("keywords"),
|
|
16468
16559
|
...level ? { pdfa: { part: level.part, conformance: "b", ...intentOpt ? { outputIntent: intentOpt } : {} } } : {}
|
|
16469
16560
|
})
|
|
16470
16561
|
);
|
|
@@ -16775,7 +16866,7 @@ function sameBytes(a, b) {
|
|
|
16775
16866
|
for (let i2 = 0; i2 < a.length; i2++) if (a[i2] !== b[i2]) return false;
|
|
16776
16867
|
return true;
|
|
16777
16868
|
}
|
|
16778
|
-
var MAX_OPEN, KEYS2, OWN;
|
|
16869
|
+
var INFO_MAX, MAX_OPEN, KEYS2, OWN;
|
|
16779
16870
|
var init_save = __esm({
|
|
16780
16871
|
"src/reader/save.js"() {
|
|
16781
16872
|
init_index();
|
|
@@ -16789,6 +16880,7 @@ var init_save = __esm({
|
|
|
16789
16880
|
init_pdf();
|
|
16790
16881
|
init_text2();
|
|
16791
16882
|
init_budget();
|
|
16883
|
+
INFO_MAX = 65536;
|
|
16792
16884
|
MAX_OPEN = 256;
|
|
16793
16885
|
KEYS2 = ["compress", "keepActiveContent", "encrypt", "pdfa", "creationDate", "signal", "timeoutMs", "keepStructure"];
|
|
16794
16886
|
OWN = /* @__PURE__ */ new Set(["Type", "Parent", "Contents", "Resources", "Annots", "MediaBox", "CropBox", "Rotate", "B"]);
|
|
@@ -16866,17 +16958,19 @@ async function saveIncremental(self, o = {}) {
|
|
|
16866
16958
|
keepStruct: () => true
|
|
16867
16959
|
});
|
|
16868
16960
|
const override = async (n, v) => put(n, await copier.text(self, v), null, v instanceof Map ? v : null);
|
|
16869
|
-
let
|
|
16870
|
-
|
|
16871
|
-
|
|
16872
|
-
|
|
16961
|
+
let scan = SCANS.get(src);
|
|
16962
|
+
if (!scan) try {
|
|
16963
|
+
const objects2 = [];
|
|
16964
|
+
const present2 = findActive(self, (n, v) => {
|
|
16873
16965
|
const d = v instanceof PdfStream ? v.dict : v;
|
|
16874
|
-
if (d instanceof Map && (d.has("ByteRange") || d.has("Perms") || d.has("Lock") || R(d.get("FT")) === "Sig" || d.has("V") || nestedSigs(d).length))
|
|
16966
|
+
if (d instanceof Map && (d.has("ByteRange") || d.has("Perms") || d.has("Lock") || R(d.get("FT")) === "Sig" || d.has("V") || nestedSigs(d).length)) objects2.push([n, v]);
|
|
16875
16967
|
});
|
|
16968
|
+
SCANS.set(src, scan = { present: present2, objects: objects2 });
|
|
16876
16969
|
} catch (e) {
|
|
16877
16970
|
throw new PdfReadError("E_ACTIVE", `${who}: the file could not be checked for active content (${/** @type {Error} */
|
|
16878
16971
|
e.message}); use save() to rewrite the file without it (this invalidates existing signatures)`);
|
|
16879
16972
|
}
|
|
16973
|
+
const { present, objects } = scan;
|
|
16880
16974
|
checkSigned(self, sig, o, objects, who);
|
|
16881
16975
|
if (present.length && !o.keepActiveContent) throw new PdfReadError("E_ACTIVE", `${who}: the file has active content (${present.map((p) => `${p.what} ×${p.count}`).join(", ")}), which an incremental update cannot remove (the original bytes stay). Use save() to rewrite the file without it (this invalidates existing signatures), or pass keepActiveContent: true to keep it knowingly`);
|
|
16882
16976
|
const fills = await fillReplacements(self, { pdfa: !!self.pdfa(), warn: (m) => warnings.push(m) });
|
|
@@ -17328,7 +17422,7 @@ function checkSigned(self, sig, o, objects, who) {
|
|
|
17328
17422
|
}
|
|
17329
17423
|
}
|
|
17330
17424
|
}
|
|
17331
|
-
var SIGN_KEYS, MAX_WIDGET_SHARE, TRANSFORMS, isSigDict;
|
|
17425
|
+
var SIGN_KEYS, SCANS, MAX_WIDGET_SHARE, TRANSFORMS, isSigDict;
|
|
17332
17426
|
var init_incremental = __esm({
|
|
17333
17427
|
"src/reader/incremental.js"() {
|
|
17334
17428
|
init_lexer();
|
|
@@ -17344,6 +17438,7 @@ var init_incremental = __esm({
|
|
|
17344
17438
|
init_serialize();
|
|
17345
17439
|
init_serialize();
|
|
17346
17440
|
SIGN_KEYS = ["field", "signer", "reason", "location", "contactInfo", "name", "date", "timestamp", "subFilter", "reserve", "hash"];
|
|
17441
|
+
SCANS = /* @__PURE__ */ new WeakMap();
|
|
17347
17442
|
MAX_WIDGET_SHARE = 0.4;
|
|
17348
17443
|
TRANSFORMS = /* @__PURE__ */ new Set(["DocMDP", "FieldMDP", "UR", "UR3"]);
|
|
17349
17444
|
isSigDict = (d) => d instanceof Map && d.has("ByteRange") && (d.has("Contents") || d.has("Type") || d.has("Filter"));
|
package/index.d.ts
CHANGED
|
@@ -650,7 +650,7 @@ export interface Pdf {
|
|
|
650
650
|
* textMapping: 'carrier' (default: Firefox, pdf.js, search) or 'actualText' (a shaped cluster's text only in its ActualText: MuPDF, AI pipelines) */
|
|
651
651
|
embedFont(bytes: Uint8Array | ArrayBuffer, o?: { subset?: boolean | Subsetter; shaper?: Shaper; textMapping?: 'carrier' | 'actualText' }): Promise<Font>;
|
|
652
652
|
/** a JPEG or PNG, written now as an image XObject. colorSpace: an ICC colour space with the image's component count */
|
|
653
|
-
embedImage(bytes: Uint8Array | ArrayBuffer, o?: { colorSpace?: IccColorSpace; /** the decoded size a PNG with alpha or interlacing may have; default 64 MB */ maxDecodedBytes?: number }): Promise<Image>;
|
|
653
|
+
embedImage(bytes: Uint8Array | ArrayBuffer, o?: { colorSpace?: IccColorSpace; /** the decoded size a PNG with alpha or interlacing may have; default 64 MB */ maxDecodedBytes?: number; /** zlib level for a re-compressed PNG (alpha, interlacing): 'default' = 6, or 3 above 2 megapixels; 'fast' = 1; or 1–9 */ compression?: 'default' | 'fast' | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 }): Promise<Image>;
|
|
654
654
|
/** a spot colour: its name and the device colour (gray, RGB, CMYK) shown for it at full tint. One definition per name */
|
|
655
655
|
spotColor(name: string, alternate: number | string | readonly number[]): SpotColor;
|
|
656
656
|
/** an ICC-based colour space from a gray, RGB or CMYK profile (its header is checked) */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@reportwright/pdf",
|
|
3
|
-
"version": "0.1.0-beta.
|
|
3
|
+
"version": "0.1.0-beta.5",
|
|
4
4
|
"description": "A streaming PDF writer for Node and browsers with no runtime dependencies: flat memory, PDF/A-1b to 3a, PDF/UA-1, Factur-X, embedded TrueType with Unicode text.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|