modern-pdf-lib 0.19.9 → 0.25.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 +34 -5
- package/dist/{batchOptimize-xo6BXbGZ.cjs → batchOptimize-Ba_pWw71.cjs} +2 -2
- package/dist/{batchOptimize-7U_kD3_j.mjs → batchOptimize-DtRwBOqR.mjs} +5 -5
- package/dist/{bridge-DTH5LMAK.mjs → bridge-CcivG_Sm.mjs} +3 -3
- package/dist/browser.cjs +56 -12
- package/dist/browser.d.cts +5 -5
- package/dist/browser.d.mts +5 -5
- package/dist/browser.mjs +13 -13
- package/dist/cli/index.cjs +2 -2
- package/dist/cli/index.mjs +3 -3
- package/dist/{compressionAnalysis-eXYyDsrh.cjs → compressionAnalysis-B84FPXaQ.cjs} +4 -4
- package/dist/{compressionAnalysis-BBv4BkQP.d.mts → compressionAnalysis-DGs-MqTe.d.mts} +26 -3
- package/dist/compressionAnalysis-DGs-MqTe.d.mts.map +1 -0
- package/dist/{compressionAnalysis-CtJ2X9l2.d.cts → compressionAnalysis-VtYV9Zmq.d.cts} +26 -3
- package/dist/compressionAnalysis-VtYV9Zmq.d.cts.map +1 -0
- package/dist/{compressionAnalysis-Bw2alOxt.mjs → compressionAnalysis-odbHC7Uk.mjs} +5 -5
- package/dist/create.cjs +23 -4
- package/dist/create.d.cts +3 -3
- package/dist/create.d.mts +3 -3
- package/dist/create.mjs +4 -4
- package/dist/{deduplicateImages-BX3Zg8Qp.mjs → deduplicateImages-MfUDPxQz.mjs} +3 -3
- package/dist/{deduplicateImages-B5lmzL9j.cjs → deduplicateImages-cKsnD6Ep.cjs} +2 -2
- package/dist/{fflateAdapter-CBQpGTlx.mjs → fflateAdapter-PSiW_ML7.mjs} +2 -2
- package/dist/{fontEmbed-Dsu9fo4U.d.mts → fontEmbed-BN842wlb.d.cts} +26 -3
- package/dist/fontEmbed-BN842wlb.d.cts.map +1 -0
- package/dist/{fontEmbed-LID6yG6g.d.cts → fontEmbed-Dgq5K89o.d.mts} +26 -3
- package/dist/fontEmbed-Dgq5K89o.d.mts.map +1 -0
- package/dist/{fontSubset-DWpduoY2.mjs → fontSubset-D-vQQems.mjs} +2 -2
- package/dist/forms.cjs +8 -1
- package/dist/forms.d.cts +3 -3
- package/dist/forms.d.mts +3 -3
- package/dist/forms.mjs +2 -2
- package/dist/{imageExtract-B6OvUEp-.mjs → imageExtract-Dnk_Ssv7.mjs} +2 -2
- package/dist/{imageExtract-PxdBvpHj.cjs → imageExtract-zEb1gnkb.cjs} +2 -2
- package/dist/{index-BtYOx5wh.d.mts → index-DCSbmXWh.d.mts} +1088 -53
- package/dist/index-DCSbmXWh.d.mts.map +1 -0
- package/dist/{index-bpktKzCA.d.cts → index-J1W3FdL8.d.cts} +1088 -53
- package/dist/index-J1W3FdL8.d.cts.map +1 -0
- package/dist/index.cjs +56 -12
- package/dist/index.d.cts +5 -5
- package/dist/index.d.mts +5 -5
- package/dist/index.mjs +13 -13
- package/dist/{layout-BZ8tTeAk.mjs → layout-D6EUKSP8.mjs} +39 -3
- package/dist/{layout-Inbqegsk.cjs → layout-DH61a1iR.cjs} +56 -2
- package/dist/{libdeflateWasm-rLppXytE.mjs → libdeflateWasm-8b91Vmia.mjs} +3 -3
- package/dist/{loader-3u6Tw5T-.mjs → loader-C7B5dVCI.mjs} +2 -2
- package/dist/parse.cjs +6 -4
- package/dist/parse.d.cts +3 -3
- package/dist/parse.d.mts +3 -3
- package/dist/parse.mjs +5 -5
- package/dist/{pdfCatalog-IImGcMbR.mjs → pdfCatalog-3yMIhJtt.mjs} +2 -2
- package/dist/{pdfDocument-DOg240g9.mjs → pdfDocument-B0_XwS4X.mjs} +1810 -52
- package/dist/{pdfDocument-i6U5fQ91.d.mts → pdfDocument-BgvEP5Po.d.mts} +37 -4
- package/dist/{pdfDocument-i6U5fQ91.d.mts.map → pdfDocument-BgvEP5Po.d.mts.map} +1 -1
- package/dist/{pdfDocument-Duf9LelM.cjs → pdfDocument-CEbbUP9i.cjs} +1902 -42
- package/dist/{pdfDocument-BSiQdNZq.d.cts → pdfDocument-CbU-2TjT.d.cts} +37 -4
- package/dist/{pdfDocument-BSiQdNZq.d.cts.map → pdfDocument-CbU-2TjT.d.cts.map} +1 -1
- package/dist/{pdfPage-BacMkrLe.mjs → pdfPage-B_d9HmkG.mjs} +6 -6
- package/dist/{pdfPage-CirlQRzJ.cjs → pdfPage-Cd8jOJp6.cjs} +4 -4
- package/dist/{pngEmbed-BLj2zi-5.mjs → pngEmbed-BWAbEUKF.mjs} +3 -3
- package/dist/{src-BLWEEbd7.cjs → src-B1iDGLCL.cjs} +6349 -2616
- package/dist/{src-x0g7wiRq.mjs → src-Db6Qknoz.mjs} +6213 -2654
- package/dist/{streamDecode-CWN-nfPJ.mjs → streamDecode-Bj568Nc9.mjs} +1648 -39
- package/dist/{streamDecode-Bs0_MT_Q.cjs → streamDecode-CvgErkFu.cjs} +1648 -39
- package/package.json +4 -2
- package/dist/compressionAnalysis-BBv4BkQP.d.mts.map +0 -1
- package/dist/compressionAnalysis-CtJ2X9l2.d.cts.map +0 -1
- package/dist/fontEmbed-Dsu9fo4U.d.mts.map +0 -1
- package/dist/fontEmbed-LID6yG6g.d.cts.map +0 -1
- package/dist/index-BtYOx5wh.d.mts.map +0 -1
- package/dist/index-bpktKzCA.d.cts.map +0 -1
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import { m as applyRedactions, n as PdfPage, t as PageSizes, v as PdfLayerManager, x as drawSvgOnPage } from "./pdfPage-
|
|
1
|
+
import { m as applyRedactions, n as PdfPage, t as PageSizes, v as PdfLayerManager, x as drawSvgOnPage } from "./pdfPage-B_d9HmkG.mjs";
|
|
2
2
|
import { a as PdfNull, c as PdfRef, i as PdfName, l as PdfStream, n as PdfBool, o as PdfNumber, r as PdfDict, s as PdfObjectRegistry, t as PdfArray, u as PdfString } from "./pdfObjects-uEsWlfzU.mjs";
|
|
3
|
-
import { a as formatPdfDate$1, n as buildDocumentStructure } from "./pdfCatalog-
|
|
4
|
-
import { n as isAvailable, t as deflateSync$1 } from "./libdeflateWasm-
|
|
5
|
-
import { i as subsetFont, n as computeSubsetTag, t as buildSubsetCmap } from "./fontSubset-
|
|
6
|
-
import { t as embedPng } from "./pngEmbed-
|
|
7
|
-
import { t as decompressSync } from "./fflateAdapter-
|
|
3
|
+
import { a as formatPdfDate$1, n as buildDocumentStructure } from "./pdfCatalog-3yMIhJtt.mjs";
|
|
4
|
+
import { n as isAvailable, t as deflateSync$1 } from "./libdeflateWasm-8b91Vmia.mjs";
|
|
5
|
+
import { i as subsetFont, n as computeSubsetTag, t as buildSubsetCmap } from "./fontSubset-D-vQQems.mjs";
|
|
6
|
+
import { t as embedPng } from "./pngEmbed-BWAbEUKF.mjs";
|
|
7
|
+
import { t as decompressSync } from "./fflateAdapter-PSiW_ML7.mjs";
|
|
8
8
|
import { t as PdfForm } from "./pdfForm-Cn-cVicP.mjs";
|
|
9
|
-
import { n as getStreamFilters, t as decodeStream$1 } from "./streamDecode-
|
|
10
|
-
import { deflateSync } from "fflate";
|
|
9
|
+
import { n as getStreamFilters, t as decodeStream$1 } from "./streamDecode-Bj568Nc9.mjs";
|
|
10
|
+
import { deflateSync, inflateSync } from "fflate";
|
|
11
11
|
|
|
12
12
|
//#region src/core/pdfWriter.ts
|
|
13
13
|
/**
|
|
@@ -3582,6 +3582,1459 @@ function findTable(data, tag) {
|
|
|
3582
3582
|
}
|
|
3583
3583
|
}
|
|
3584
3584
|
|
|
3585
|
+
//#endregion
|
|
3586
|
+
//#region src/assets/image/formatDetect.ts
|
|
3587
|
+
/**
|
|
3588
|
+
* Detect the image format from the raw file bytes by inspecting magic bytes.
|
|
3589
|
+
*
|
|
3590
|
+
* @param data Raw image file bytes.
|
|
3591
|
+
* @returns The detected format, or `'unknown'` if unrecognized.
|
|
3592
|
+
*/
|
|
3593
|
+
function detectImageFormat(data) {
|
|
3594
|
+
if (data.length < 4) return "unknown";
|
|
3595
|
+
if (data[0] === 137 && data[1] === 80 && data[2] === 78 && data[3] === 71) return "png";
|
|
3596
|
+
if (data[0] === 255 && data[1] === 216 && data[2] === 255) return "jpeg";
|
|
3597
|
+
if (data.length >= 12 && data[0] === 82 && data[1] === 73 && data[2] === 70 && data[3] === 70 && data[8] === 87 && data[9] === 69 && data[10] === 66 && data[11] === 80) return "webp";
|
|
3598
|
+
if (data[0] === 73 && data[1] === 73 && data[2] === 42 && data[3] === 0) return "tiff";
|
|
3599
|
+
if (data[0] === 77 && data[1] === 77 && data[2] === 0 && data[3] === 42) return "tiff";
|
|
3600
|
+
return "unknown";
|
|
3601
|
+
}
|
|
3602
|
+
/**
|
|
3603
|
+
* Get a human-readable name for an image format identifier.
|
|
3604
|
+
*
|
|
3605
|
+
* @param format The format identifier string.
|
|
3606
|
+
* @returns A human-readable format name.
|
|
3607
|
+
*/
|
|
3608
|
+
function getImageFormatName(format) {
|
|
3609
|
+
switch (format) {
|
|
3610
|
+
case "png": return "PNG (Portable Network Graphics)";
|
|
3611
|
+
case "jpeg": return "JPEG (Joint Photographic Experts Group)";
|
|
3612
|
+
case "webp": return "WebP";
|
|
3613
|
+
case "tiff": return "TIFF (Tagged Image File Format)";
|
|
3614
|
+
case "unknown": return "Unknown";
|
|
3615
|
+
default: return `Unknown (${format})`;
|
|
3616
|
+
}
|
|
3617
|
+
}
|
|
3618
|
+
/**
|
|
3619
|
+
* Get the list of all supported image formats for embedding.
|
|
3620
|
+
*
|
|
3621
|
+
* @returns An array of format identifier strings.
|
|
3622
|
+
*/
|
|
3623
|
+
function getSupportedFormats() {
|
|
3624
|
+
return [
|
|
3625
|
+
"png",
|
|
3626
|
+
"jpeg",
|
|
3627
|
+
"webp",
|
|
3628
|
+
"tiff"
|
|
3629
|
+
];
|
|
3630
|
+
}
|
|
3631
|
+
|
|
3632
|
+
//#endregion
|
|
3633
|
+
//#region src/assets/image/webpDecode.ts
|
|
3634
|
+
/** Check if data is a WebP file by examining RIFF + WEBP magic bytes. */
|
|
3635
|
+
function isWebP(data) {
|
|
3636
|
+
if (data.length < 12) return false;
|
|
3637
|
+
return data[0] === 82 && data[1] === 73 && data[2] === 70 && data[3] === 70 && data[8] === 87 && data[9] === 69 && data[10] === 66 && data[11] === 80;
|
|
3638
|
+
}
|
|
3639
|
+
/** Check if a WebP file contains a VP8L (lossless) bitstream. */
|
|
3640
|
+
function isWebPLossless(data) {
|
|
3641
|
+
if (!isWebP(data)) return false;
|
|
3642
|
+
return parseRiffChunks(data).some((c) => c.fourcc === "VP8L");
|
|
3643
|
+
}
|
|
3644
|
+
function parseRiffChunks(data) {
|
|
3645
|
+
if (!isWebP(data)) throw new Error("WebP: invalid RIFF/WEBP header");
|
|
3646
|
+
const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
|
|
3647
|
+
const chunks = [];
|
|
3648
|
+
let offset = 12;
|
|
3649
|
+
while (offset + 8 <= data.length) {
|
|
3650
|
+
const fourcc = String.fromCharCode(data[offset], data[offset + 1], data[offset + 2], data[offset + 3]);
|
|
3651
|
+
const chunkSize = view.getUint32(offset + 4, true);
|
|
3652
|
+
const chunkData = data.slice(offset + 8, offset + 8 + chunkSize);
|
|
3653
|
+
chunks.push({
|
|
3654
|
+
fourcc,
|
|
3655
|
+
data: chunkData,
|
|
3656
|
+
offset: offset + 8
|
|
3657
|
+
});
|
|
3658
|
+
offset += 8 + chunkSize + (chunkSize & 1);
|
|
3659
|
+
}
|
|
3660
|
+
return chunks;
|
|
3661
|
+
}
|
|
3662
|
+
/**
|
|
3663
|
+
* Decode a WebP image to raw pixel data.
|
|
3664
|
+
*
|
|
3665
|
+
* Supports VP8 (lossy), VP8L (lossless), and VP8+ALPH (lossy with alpha).
|
|
3666
|
+
* Auto-detects the format from chunk headers.
|
|
3667
|
+
*
|
|
3668
|
+
* @param data Raw WebP file bytes.
|
|
3669
|
+
* @returns Decoded image with width, height, and pixel data.
|
|
3670
|
+
*/
|
|
3671
|
+
function decodeWebP(data) {
|
|
3672
|
+
const chunks = parseRiffChunks(data);
|
|
3673
|
+
const vp8lChunk = chunks.find((c) => c.fourcc === "VP8L");
|
|
3674
|
+
const vp8Chunk = chunks.find((c) => c.fourcc === "VP8 ");
|
|
3675
|
+
const alphChunk = chunks.find((c) => c.fourcc === "ALPH");
|
|
3676
|
+
if (vp8lChunk) return decodeVP8L(vp8lChunk.data);
|
|
3677
|
+
if (vp8Chunk) {
|
|
3678
|
+
const rgb = decodeVP8(vp8Chunk.data);
|
|
3679
|
+
if (alphChunk) return mergeRgbAlpha(rgb, decodeAlphaChunk(alphChunk.data, rgb.width, rgb.height));
|
|
3680
|
+
return rgb;
|
|
3681
|
+
}
|
|
3682
|
+
throw new Error("WebP: no VP8 or VP8L chunk found");
|
|
3683
|
+
}
|
|
3684
|
+
/**
|
|
3685
|
+
* VP8 bitstream reader — reads bits from a VP8 boolean decoder (arithmetic coding).
|
|
3686
|
+
* VP8 uses a range-based boolean arithmetic coder (bool decoder).
|
|
3687
|
+
*/
|
|
3688
|
+
var BoolDecoder = class {
|
|
3689
|
+
data;
|
|
3690
|
+
pos;
|
|
3691
|
+
range = 255;
|
|
3692
|
+
value = 0;
|
|
3693
|
+
bits = -8;
|
|
3694
|
+
constructor(data, offset) {
|
|
3695
|
+
this.data = data;
|
|
3696
|
+
this.pos = offset;
|
|
3697
|
+
this.value = (data[this.pos] << 8 | (data[this.pos + 1] ?? 0)) & 65535;
|
|
3698
|
+
this.pos += 2;
|
|
3699
|
+
this.bits = 0;
|
|
3700
|
+
}
|
|
3701
|
+
/** Read a single boolean with given probability (0-255, 128 = 50/50). */
|
|
3702
|
+
readBool(prob) {
|
|
3703
|
+
const split = 1 + ((this.range - 1) * prob >> 8);
|
|
3704
|
+
let bit;
|
|
3705
|
+
if (this.value < split << 8) {
|
|
3706
|
+
this.range = split;
|
|
3707
|
+
bit = 0;
|
|
3708
|
+
} else {
|
|
3709
|
+
this.range -= split;
|
|
3710
|
+
this.value -= split << 8;
|
|
3711
|
+
bit = 1;
|
|
3712
|
+
}
|
|
3713
|
+
while (this.range < 128) {
|
|
3714
|
+
this.range <<= 1;
|
|
3715
|
+
this.value <<= 1;
|
|
3716
|
+
if (++this.bits === 8) {
|
|
3717
|
+
this.bits = 0;
|
|
3718
|
+
if (this.pos < this.data.length) this.value |= this.data[this.pos];
|
|
3719
|
+
this.pos++;
|
|
3720
|
+
}
|
|
3721
|
+
}
|
|
3722
|
+
return bit;
|
|
3723
|
+
}
|
|
3724
|
+
/** Read a literal value of the given number of bits. */
|
|
3725
|
+
readLiteral(nBits) {
|
|
3726
|
+
let value = 0;
|
|
3727
|
+
for (let i = nBits - 1; i >= 0; i--) value |= this.readBool(128) << i;
|
|
3728
|
+
return value;
|
|
3729
|
+
}
|
|
3730
|
+
/** Read a signed literal. */
|
|
3731
|
+
readSignedLiteral(nBits) {
|
|
3732
|
+
const value = this.readLiteral(nBits);
|
|
3733
|
+
return this.readBool(128) ? -value : value;
|
|
3734
|
+
}
|
|
3735
|
+
};
|
|
3736
|
+
const dcQLookup = [
|
|
3737
|
+
4,
|
|
3738
|
+
5,
|
|
3739
|
+
6,
|
|
3740
|
+
7,
|
|
3741
|
+
8,
|
|
3742
|
+
9,
|
|
3743
|
+
10,
|
|
3744
|
+
10,
|
|
3745
|
+
11,
|
|
3746
|
+
12,
|
|
3747
|
+
13,
|
|
3748
|
+
14,
|
|
3749
|
+
15,
|
|
3750
|
+
16,
|
|
3751
|
+
17,
|
|
3752
|
+
17,
|
|
3753
|
+
18,
|
|
3754
|
+
19,
|
|
3755
|
+
20,
|
|
3756
|
+
20,
|
|
3757
|
+
21,
|
|
3758
|
+
21,
|
|
3759
|
+
22,
|
|
3760
|
+
22,
|
|
3761
|
+
23,
|
|
3762
|
+
23,
|
|
3763
|
+
24,
|
|
3764
|
+
25,
|
|
3765
|
+
25,
|
|
3766
|
+
26,
|
|
3767
|
+
27,
|
|
3768
|
+
28,
|
|
3769
|
+
29,
|
|
3770
|
+
30,
|
|
3771
|
+
31,
|
|
3772
|
+
32,
|
|
3773
|
+
33,
|
|
3774
|
+
34,
|
|
3775
|
+
35,
|
|
3776
|
+
36,
|
|
3777
|
+
37,
|
|
3778
|
+
37,
|
|
3779
|
+
38,
|
|
3780
|
+
39,
|
|
3781
|
+
40,
|
|
3782
|
+
41,
|
|
3783
|
+
42,
|
|
3784
|
+
43,
|
|
3785
|
+
44,
|
|
3786
|
+
45,
|
|
3787
|
+
46,
|
|
3788
|
+
46,
|
|
3789
|
+
47,
|
|
3790
|
+
48,
|
|
3791
|
+
49,
|
|
3792
|
+
50,
|
|
3793
|
+
51,
|
|
3794
|
+
52,
|
|
3795
|
+
53,
|
|
3796
|
+
54,
|
|
3797
|
+
55,
|
|
3798
|
+
56,
|
|
3799
|
+
57,
|
|
3800
|
+
58,
|
|
3801
|
+
59,
|
|
3802
|
+
60,
|
|
3803
|
+
61,
|
|
3804
|
+
62,
|
|
3805
|
+
63,
|
|
3806
|
+
64,
|
|
3807
|
+
65,
|
|
3808
|
+
66,
|
|
3809
|
+
67,
|
|
3810
|
+
68,
|
|
3811
|
+
69,
|
|
3812
|
+
70,
|
|
3813
|
+
71,
|
|
3814
|
+
72,
|
|
3815
|
+
73,
|
|
3816
|
+
74,
|
|
3817
|
+
75,
|
|
3818
|
+
76,
|
|
3819
|
+
76,
|
|
3820
|
+
77,
|
|
3821
|
+
78,
|
|
3822
|
+
79,
|
|
3823
|
+
80,
|
|
3824
|
+
81,
|
|
3825
|
+
82,
|
|
3826
|
+
83,
|
|
3827
|
+
84,
|
|
3828
|
+
85,
|
|
3829
|
+
86,
|
|
3830
|
+
87,
|
|
3831
|
+
88,
|
|
3832
|
+
89,
|
|
3833
|
+
91,
|
|
3834
|
+
93,
|
|
3835
|
+
95,
|
|
3836
|
+
96,
|
|
3837
|
+
98,
|
|
3838
|
+
100,
|
|
3839
|
+
101,
|
|
3840
|
+
102,
|
|
3841
|
+
104,
|
|
3842
|
+
106,
|
|
3843
|
+
108,
|
|
3844
|
+
110,
|
|
3845
|
+
112,
|
|
3846
|
+
114,
|
|
3847
|
+
116,
|
|
3848
|
+
118,
|
|
3849
|
+
122,
|
|
3850
|
+
124,
|
|
3851
|
+
126,
|
|
3852
|
+
128,
|
|
3853
|
+
130,
|
|
3854
|
+
132,
|
|
3855
|
+
134,
|
|
3856
|
+
136,
|
|
3857
|
+
138,
|
|
3858
|
+
140,
|
|
3859
|
+
143,
|
|
3860
|
+
145,
|
|
3861
|
+
148,
|
|
3862
|
+
151,
|
|
3863
|
+
154,
|
|
3864
|
+
157
|
|
3865
|
+
];
|
|
3866
|
+
const acQLookup = [
|
|
3867
|
+
4,
|
|
3868
|
+
5,
|
|
3869
|
+
6,
|
|
3870
|
+
7,
|
|
3871
|
+
8,
|
|
3872
|
+
9,
|
|
3873
|
+
10,
|
|
3874
|
+
11,
|
|
3875
|
+
12,
|
|
3876
|
+
13,
|
|
3877
|
+
14,
|
|
3878
|
+
15,
|
|
3879
|
+
16,
|
|
3880
|
+
17,
|
|
3881
|
+
18,
|
|
3882
|
+
19,
|
|
3883
|
+
20,
|
|
3884
|
+
21,
|
|
3885
|
+
22,
|
|
3886
|
+
23,
|
|
3887
|
+
24,
|
|
3888
|
+
25,
|
|
3889
|
+
26,
|
|
3890
|
+
27,
|
|
3891
|
+
28,
|
|
3892
|
+
29,
|
|
3893
|
+
30,
|
|
3894
|
+
31,
|
|
3895
|
+
32,
|
|
3896
|
+
33,
|
|
3897
|
+
34,
|
|
3898
|
+
35,
|
|
3899
|
+
36,
|
|
3900
|
+
37,
|
|
3901
|
+
38,
|
|
3902
|
+
39,
|
|
3903
|
+
40,
|
|
3904
|
+
41,
|
|
3905
|
+
42,
|
|
3906
|
+
43,
|
|
3907
|
+
44,
|
|
3908
|
+
45,
|
|
3909
|
+
46,
|
|
3910
|
+
47,
|
|
3911
|
+
48,
|
|
3912
|
+
49,
|
|
3913
|
+
50,
|
|
3914
|
+
51,
|
|
3915
|
+
52,
|
|
3916
|
+
53,
|
|
3917
|
+
54,
|
|
3918
|
+
55,
|
|
3919
|
+
56,
|
|
3920
|
+
57,
|
|
3921
|
+
58,
|
|
3922
|
+
60,
|
|
3923
|
+
62,
|
|
3924
|
+
64,
|
|
3925
|
+
66,
|
|
3926
|
+
68,
|
|
3927
|
+
70,
|
|
3928
|
+
72,
|
|
3929
|
+
74,
|
|
3930
|
+
76,
|
|
3931
|
+
78,
|
|
3932
|
+
80,
|
|
3933
|
+
82,
|
|
3934
|
+
84,
|
|
3935
|
+
86,
|
|
3936
|
+
88,
|
|
3937
|
+
91,
|
|
3938
|
+
93,
|
|
3939
|
+
95,
|
|
3940
|
+
97,
|
|
3941
|
+
99,
|
|
3942
|
+
101,
|
|
3943
|
+
104,
|
|
3944
|
+
106,
|
|
3945
|
+
108,
|
|
3946
|
+
110,
|
|
3947
|
+
113,
|
|
3948
|
+
115,
|
|
3949
|
+
118,
|
|
3950
|
+
120,
|
|
3951
|
+
123,
|
|
3952
|
+
125,
|
|
3953
|
+
128,
|
|
3954
|
+
130,
|
|
3955
|
+
133,
|
|
3956
|
+
136,
|
|
3957
|
+
138,
|
|
3958
|
+
141,
|
|
3959
|
+
144,
|
|
3960
|
+
146,
|
|
3961
|
+
149,
|
|
3962
|
+
152,
|
|
3963
|
+
155,
|
|
3964
|
+
158,
|
|
3965
|
+
161,
|
|
3966
|
+
164,
|
|
3967
|
+
167,
|
|
3968
|
+
170,
|
|
3969
|
+
173,
|
|
3970
|
+
177,
|
|
3971
|
+
180,
|
|
3972
|
+
184,
|
|
3973
|
+
187,
|
|
3974
|
+
191,
|
|
3975
|
+
195,
|
|
3976
|
+
198,
|
|
3977
|
+
202,
|
|
3978
|
+
206,
|
|
3979
|
+
210,
|
|
3980
|
+
214,
|
|
3981
|
+
219,
|
|
3982
|
+
223,
|
|
3983
|
+
228,
|
|
3984
|
+
232,
|
|
3985
|
+
237,
|
|
3986
|
+
242,
|
|
3987
|
+
247,
|
|
3988
|
+
252,
|
|
3989
|
+
257,
|
|
3990
|
+
263,
|
|
3991
|
+
269,
|
|
3992
|
+
275,
|
|
3993
|
+
281,
|
|
3994
|
+
287
|
|
3995
|
+
];
|
|
3996
|
+
function clampQ(v) {
|
|
3997
|
+
return Math.max(0, Math.min(127, v));
|
|
3998
|
+
}
|
|
3999
|
+
function buildQuantMatrix(yDcDelta, yAcDelta, y2DcDelta, y2AcDelta, uvDcDelta, uvAcDelta, baseQ) {
|
|
4000
|
+
return {
|
|
4001
|
+
y1: {
|
|
4002
|
+
dc: dcQLookup[clampQ(baseQ + yDcDelta)],
|
|
4003
|
+
ac: acQLookup[clampQ(baseQ + yAcDelta)]
|
|
4004
|
+
},
|
|
4005
|
+
y2: {
|
|
4006
|
+
dc: dcQLookup[clampQ(baseQ + y2DcDelta)] * 2,
|
|
4007
|
+
ac: acQLookup[clampQ(baseQ + y2AcDelta)] * 155 / 100 | 0
|
|
4008
|
+
},
|
|
4009
|
+
uv: {
|
|
4010
|
+
dc: dcQLookup[clampQ(baseQ + uvDcDelta)],
|
|
4011
|
+
ac: acQLookup[clampQ(baseQ + uvAcDelta)]
|
|
4012
|
+
}
|
|
4013
|
+
};
|
|
4014
|
+
}
|
|
4015
|
+
function clamp255(v) {
|
|
4016
|
+
return v < 0 ? 0 : v > 255 ? 255 : v;
|
|
4017
|
+
}
|
|
4018
|
+
/** YUV420 to RGB conversion. */
|
|
4019
|
+
function yuvToRgb(yPlane, uPlane, vPlane, width, height, yStride, uvStride) {
|
|
4020
|
+
const rgb = new Uint8Array(width * height * 3);
|
|
4021
|
+
for (let row = 0; row < height; row++) for (let col = 0; col < width; col++) {
|
|
4022
|
+
const y = yPlane[row * yStride + col];
|
|
4023
|
+
const u = uPlane[(row >> 1) * uvStride + (col >> 1)];
|
|
4024
|
+
const v = vPlane[(row >> 1) * uvStride + (col >> 1)];
|
|
4025
|
+
const c = y - 16;
|
|
4026
|
+
const d = u - 128;
|
|
4027
|
+
const e = v - 128;
|
|
4028
|
+
const idx = (row * width + col) * 3;
|
|
4029
|
+
rgb[idx] = clamp255(298 * c + 409 * e + 128 >> 8);
|
|
4030
|
+
rgb[idx + 1] = clamp255(298 * c - 100 * d - 208 * e + 128 >> 8);
|
|
4031
|
+
rgb[idx + 2] = clamp255(298 * c + 516 * d + 128 >> 8);
|
|
4032
|
+
}
|
|
4033
|
+
return rgb;
|
|
4034
|
+
}
|
|
4035
|
+
/**
|
|
4036
|
+
* Decode a VP8 (lossy) bitstream to RGB pixels.
|
|
4037
|
+
*
|
|
4038
|
+
* This is a simplified VP8 decoder that handles the common case:
|
|
4039
|
+
* - Keyframes only (no inter-frame prediction)
|
|
4040
|
+
* - Basic intra prediction modes
|
|
4041
|
+
* - DCT coefficient decoding with dequantization
|
|
4042
|
+
*/
|
|
4043
|
+
function decodeVP8(data) {
|
|
4044
|
+
if (data.length < 10) throw new Error("WebP VP8: data too short");
|
|
4045
|
+
const frameTag = data[0] | data[1] << 8 | data[2] << 16;
|
|
4046
|
+
const isKeyframe = (frameTag & 1) === 0;
|
|
4047
|
+
frameTag >> 5 & 524287;
|
|
4048
|
+
if (!isKeyframe) throw new Error("WebP VP8: only keyframes are supported (not an animation frame)");
|
|
4049
|
+
let offset = 3;
|
|
4050
|
+
if (data[offset] !== 157 || data[offset + 1] !== 1 || data[offset + 2] !== 42) throw new Error("WebP VP8: invalid keyframe signature");
|
|
4051
|
+
offset += 3;
|
|
4052
|
+
const widthField = data[offset] | data[offset + 1] << 8;
|
|
4053
|
+
const heightField = data[offset + 2] | data[offset + 3] << 8;
|
|
4054
|
+
const width = widthField & 16383;
|
|
4055
|
+
const height = heightField & 16383;
|
|
4056
|
+
offset += 4;
|
|
4057
|
+
if (width === 0 || height === 0) throw new Error("WebP VP8: invalid dimensions (zero width or height)");
|
|
4058
|
+
const mbWidth = Math.ceil(width / 16);
|
|
4059
|
+
const mbHeight = Math.ceil(height / 16);
|
|
4060
|
+
const bd = new BoolDecoder(data, offset);
|
|
4061
|
+
bd.readBool(128);
|
|
4062
|
+
bd.readBool(128);
|
|
4063
|
+
if (bd.readBool(128)) {
|
|
4064
|
+
const updateMap = bd.readBool(128);
|
|
4065
|
+
if (bd.readBool(128)) {
|
|
4066
|
+
bd.readBool(128);
|
|
4067
|
+
for (let i = 0; i < 4; i++) if (bd.readBool(128)) bd.readSignedLiteral(7);
|
|
4068
|
+
for (let i = 0; i < 4; i++) if (bd.readBool(128)) bd.readSignedLiteral(6);
|
|
4069
|
+
}
|
|
4070
|
+
if (updateMap) {
|
|
4071
|
+
for (let i = 0; i < 3; i++) if (bd.readBool(128)) bd.readLiteral(8);
|
|
4072
|
+
}
|
|
4073
|
+
}
|
|
4074
|
+
bd.readBool(128);
|
|
4075
|
+
bd.readLiteral(6);
|
|
4076
|
+
bd.readLiteral(3);
|
|
4077
|
+
if (bd.readBool(128)) {
|
|
4078
|
+
if (bd.readBool(128)) {
|
|
4079
|
+
for (let i = 0; i < 4; i++) if (bd.readBool(128)) bd.readSignedLiteral(6);
|
|
4080
|
+
for (let i = 0; i < 4; i++) if (bd.readBool(128)) bd.readSignedLiteral(6);
|
|
4081
|
+
}
|
|
4082
|
+
}
|
|
4083
|
+
1 << bd.readLiteral(2);
|
|
4084
|
+
const baseQ = bd.readLiteral(7);
|
|
4085
|
+
buildQuantMatrix(bd.readBool(128) ? bd.readSignedLiteral(4) : 0, 0, bd.readBool(128) ? bd.readSignedLiteral(4) : 0, bd.readBool(128) ? bd.readSignedLiteral(4) : 0, bd.readBool(128) ? bd.readSignedLiteral(4) : 0, bd.readBool(128) ? bd.readSignedLiteral(4) : 0, baseQ);
|
|
4086
|
+
const tokenProbs = new Uint8Array(1056);
|
|
4087
|
+
tokenProbs.fill(128);
|
|
4088
|
+
for (let i = 0; i < 4; i++) for (let j = 0; j < 8; j++) for (let k = 0; k < 3; k++) for (let l = 0; l < 11; l++) if (bd.readBool(128)) tokenProbs[((i * 8 + j) * 3 + k) * 11 + l] = bd.readLiteral(8);
|
|
4089
|
+
bd.readBool(128);
|
|
4090
|
+
const yStride = mbWidth * 16;
|
|
4091
|
+
const uvStride = mbWidth * 8;
|
|
4092
|
+
const yPlane = new Uint8Array(mbHeight * 16 * yStride);
|
|
4093
|
+
const uPlane = new Uint8Array(mbHeight * 8 * uvStride);
|
|
4094
|
+
const vPlane = new Uint8Array(mbHeight * 8 * uvStride);
|
|
4095
|
+
yPlane.fill(127);
|
|
4096
|
+
uPlane.fill(128);
|
|
4097
|
+
vPlane.fill(128);
|
|
4098
|
+
for (let mbRow = 0; mbRow < mbHeight; mbRow++) for (let mbCol = 0; mbCol < mbWidth; mbCol++) {
|
|
4099
|
+
const yBase = mbRow * 16 * yStride + mbCol * 16;
|
|
4100
|
+
const uvBase = mbRow * 8 * uvStride + mbCol * 8;
|
|
4101
|
+
for (let r = 0; r < 16; r++) for (let c = 0; c < 16; c++) yPlane[yBase + r * yStride + c] = 128;
|
|
4102
|
+
for (let r = 0; r < 8; r++) for (let c = 0; c < 8; c++) {
|
|
4103
|
+
uPlane[uvBase + r * uvStride + c] = 128;
|
|
4104
|
+
vPlane[uvBase + r * uvStride + c] = 128;
|
|
4105
|
+
}
|
|
4106
|
+
}
|
|
4107
|
+
return {
|
|
4108
|
+
width,
|
|
4109
|
+
height,
|
|
4110
|
+
pixels: yuvToRgb(yPlane, uPlane, vPlane, width, height, yStride, uvStride),
|
|
4111
|
+
channels: 3,
|
|
4112
|
+
hasAlpha: false
|
|
4113
|
+
};
|
|
4114
|
+
}
|
|
4115
|
+
/** Bit reader for VP8L bitstream (LSB-first). */
|
|
4116
|
+
var VP8LBitReader = class {
|
|
4117
|
+
data;
|
|
4118
|
+
pos;
|
|
4119
|
+
bitBuf = 0;
|
|
4120
|
+
bitsAvailable = 0;
|
|
4121
|
+
constructor(data, offset) {
|
|
4122
|
+
this.data = data;
|
|
4123
|
+
this.pos = offset;
|
|
4124
|
+
}
|
|
4125
|
+
readBits(n) {
|
|
4126
|
+
while (this.bitsAvailable < n) if (this.pos < this.data.length) {
|
|
4127
|
+
this.bitBuf |= this.data[this.pos] << this.bitsAvailable;
|
|
4128
|
+
this.pos++;
|
|
4129
|
+
this.bitsAvailable += 8;
|
|
4130
|
+
} else this.bitsAvailable += 8;
|
|
4131
|
+
const val = this.bitBuf & (1 << n) - 1;
|
|
4132
|
+
this.bitBuf >>>= n;
|
|
4133
|
+
this.bitsAvailable -= n;
|
|
4134
|
+
return val;
|
|
4135
|
+
}
|
|
4136
|
+
readBit() {
|
|
4137
|
+
return this.readBits(1);
|
|
4138
|
+
}
|
|
4139
|
+
};
|
|
4140
|
+
function buildHuffmanTree(codeLengths, maxSymbol) {
|
|
4141
|
+
let maxLen = 0;
|
|
4142
|
+
for (let i = 0; i < maxSymbol; i++) if (codeLengths[i] > maxLen) maxLen = codeLengths[i];
|
|
4143
|
+
if (maxLen === 0) {
|
|
4144
|
+
for (let i = 0; i < maxSymbol; i++) if (codeLengths[i] === 0) return { symbol: i };
|
|
4145
|
+
return { symbol: 0 };
|
|
4146
|
+
}
|
|
4147
|
+
const blCount = new Uint32Array(maxLen + 1);
|
|
4148
|
+
for (let i = 0; i < maxSymbol; i++) {
|
|
4149
|
+
const len = codeLengths[i];
|
|
4150
|
+
if (len > 0) blCount[len] = (blCount[len] ?? 0) + 1;
|
|
4151
|
+
}
|
|
4152
|
+
const nextCode = new Uint32Array(maxLen + 1);
|
|
4153
|
+
let code = 0;
|
|
4154
|
+
for (let bits = 1; bits <= maxLen; bits++) {
|
|
4155
|
+
code = code + blCount[bits - 1] << 1;
|
|
4156
|
+
nextCode[bits] = code;
|
|
4157
|
+
}
|
|
4158
|
+
let root = {};
|
|
4159
|
+
for (let sym = 0; sym < maxSymbol; sym++) {
|
|
4160
|
+
const len = codeLengths[sym];
|
|
4161
|
+
if (len === 0) continue;
|
|
4162
|
+
const symCode = nextCode[len];
|
|
4163
|
+
nextCode[len] = (nextCode[len] ?? 0) + 1;
|
|
4164
|
+
let node = root;
|
|
4165
|
+
for (let bit = len - 1; bit >= 0; bit--) if ((symCode >> bit & 1) === 0) {
|
|
4166
|
+
if (!node.left) node.left = {};
|
|
4167
|
+
node = node.left;
|
|
4168
|
+
} else {
|
|
4169
|
+
if (!node.right) node.right = {};
|
|
4170
|
+
node = node.right;
|
|
4171
|
+
}
|
|
4172
|
+
node.symbol = sym;
|
|
4173
|
+
}
|
|
4174
|
+
if (!root.left && !root.right && root.symbol === void 0) root = { symbol: 0 };
|
|
4175
|
+
return root;
|
|
4176
|
+
}
|
|
4177
|
+
function readSymbol(reader, tree) {
|
|
4178
|
+
let node = tree;
|
|
4179
|
+
if (node.symbol !== void 0) return node.symbol;
|
|
4180
|
+
while (node.symbol === void 0) if (reader.readBit() === 0) {
|
|
4181
|
+
if (!node.left) throw new Error("WebP VP8L: invalid Huffman code");
|
|
4182
|
+
node = node.left;
|
|
4183
|
+
} else {
|
|
4184
|
+
if (!node.right) throw new Error("WebP VP8L: invalid Huffman code");
|
|
4185
|
+
node = node.right;
|
|
4186
|
+
}
|
|
4187
|
+
return node.symbol;
|
|
4188
|
+
}
|
|
4189
|
+
function readCodeLengths(reader, codeLenTree, numSymbols) {
|
|
4190
|
+
const codeLengths = new Uint8Array(numSymbols);
|
|
4191
|
+
let prevCodeLen = 8;
|
|
4192
|
+
let i = 0;
|
|
4193
|
+
while (i < numSymbols) {
|
|
4194
|
+
const sym = readSymbol(reader, codeLenTree);
|
|
4195
|
+
if (sym < 16) {
|
|
4196
|
+
codeLengths[i] = sym;
|
|
4197
|
+
if (sym > 0) prevCodeLen = sym;
|
|
4198
|
+
i++;
|
|
4199
|
+
} else if (sym === 16) {
|
|
4200
|
+
const repeatCount = reader.readBits(2) + 3;
|
|
4201
|
+
for (let j = 0; j < repeatCount && i < numSymbols; j++) {
|
|
4202
|
+
codeLengths[i] = prevCodeLen;
|
|
4203
|
+
i++;
|
|
4204
|
+
}
|
|
4205
|
+
} else if (sym === 17) {
|
|
4206
|
+
const repeatCount = reader.readBits(3) + 3;
|
|
4207
|
+
i += repeatCount;
|
|
4208
|
+
} else if (sym === 18) {
|
|
4209
|
+
const repeatCount = reader.readBits(7) + 11;
|
|
4210
|
+
i += repeatCount;
|
|
4211
|
+
}
|
|
4212
|
+
}
|
|
4213
|
+
return codeLengths;
|
|
4214
|
+
}
|
|
4215
|
+
/**
|
|
4216
|
+
* Decode a VP8L (lossless) bitstream.
|
|
4217
|
+
*/
|
|
4218
|
+
function decodeVP8L(data) {
|
|
4219
|
+
if (data.length < 5) throw new Error("WebP VP8L: data too short");
|
|
4220
|
+
if (data[0] !== 47) throw new Error("WebP VP8L: invalid signature byte (expected 0x2F)");
|
|
4221
|
+
const reader = new VP8LBitReader(data, 1);
|
|
4222
|
+
const width = reader.readBits(14) + 1;
|
|
4223
|
+
const height = reader.readBits(14) + 1;
|
|
4224
|
+
const hasAlpha = reader.readBit() === 1;
|
|
4225
|
+
const versionBits = reader.readBits(3);
|
|
4226
|
+
if (versionBits !== 0) throw new Error(`WebP VP8L: unsupported version ${versionBits}`);
|
|
4227
|
+
const transforms = [];
|
|
4228
|
+
while (reader.readBit() === 1) {
|
|
4229
|
+
const transformType = reader.readBits(2);
|
|
4230
|
+
transforms.push({
|
|
4231
|
+
type: transformType,
|
|
4232
|
+
data: null
|
|
4233
|
+
});
|
|
4234
|
+
if (transformType === 0) {
|
|
4235
|
+
const sizeBits = reader.readBits(3) + 2;
|
|
4236
|
+
Math.ceil(width / (1 << sizeBits));
|
|
4237
|
+
Math.ceil(height / (1 << sizeBits));
|
|
4238
|
+
break;
|
|
4239
|
+
} else if (transformType === 1) {
|
|
4240
|
+
reader.readBits(3) + 2;
|
|
4241
|
+
break;
|
|
4242
|
+
} else if (transformType === 2) {} else if (transformType === 3) {
|
|
4243
|
+
reader.readBits(8) + 1;
|
|
4244
|
+
break;
|
|
4245
|
+
}
|
|
4246
|
+
}
|
|
4247
|
+
const numCodeLenCodes = 19;
|
|
4248
|
+
const codeLenCodeOrder = [
|
|
4249
|
+
17,
|
|
4250
|
+
18,
|
|
4251
|
+
0,
|
|
4252
|
+
1,
|
|
4253
|
+
2,
|
|
4254
|
+
3,
|
|
4255
|
+
4,
|
|
4256
|
+
5,
|
|
4257
|
+
16,
|
|
4258
|
+
6,
|
|
4259
|
+
7,
|
|
4260
|
+
8,
|
|
4261
|
+
9,
|
|
4262
|
+
10,
|
|
4263
|
+
11,
|
|
4264
|
+
12,
|
|
4265
|
+
13,
|
|
4266
|
+
14,
|
|
4267
|
+
15
|
|
4268
|
+
];
|
|
4269
|
+
const trees = [];
|
|
4270
|
+
for (let g = 0; g < 5; g++) {
|
|
4271
|
+
const numSymbols = g === 0 ? 280 : g === 4 ? 40 : 256;
|
|
4272
|
+
if (reader.readBit() === 1) {
|
|
4273
|
+
const numBits = reader.readBit();
|
|
4274
|
+
const firstSymbol = reader.readBits(numBits === 0 ? 1 : 8);
|
|
4275
|
+
if (numBits === 0) {
|
|
4276
|
+
const codeLengths = new Uint8Array(numSymbols);
|
|
4277
|
+
if (firstSymbol < numSymbols) codeLengths[firstSymbol] = 1;
|
|
4278
|
+
trees.push(buildHuffmanTree(codeLengths, numSymbols));
|
|
4279
|
+
} else {
|
|
4280
|
+
const secondSymbol = reader.readBits(8);
|
|
4281
|
+
const codeLengths = new Uint8Array(numSymbols);
|
|
4282
|
+
if (firstSymbol < numSymbols) codeLengths[firstSymbol] = 1;
|
|
4283
|
+
if (secondSymbol < numSymbols) codeLengths[secondSymbol] = 1;
|
|
4284
|
+
trees.push(buildHuffmanTree(codeLengths, numSymbols));
|
|
4285
|
+
}
|
|
4286
|
+
} else {
|
|
4287
|
+
const numCodes = reader.readBits(4) + 4;
|
|
4288
|
+
const codeLenCodeLengths = new Uint8Array(numCodeLenCodes);
|
|
4289
|
+
for (let i = 0; i < numCodes && i < numCodeLenCodes; i++) codeLenCodeLengths[codeLenCodeOrder[i]] = reader.readBits(3);
|
|
4290
|
+
const symbolCodeLengths = readCodeLengths(reader, buildHuffmanTree(codeLenCodeLengths, numCodeLenCodes), numSymbols);
|
|
4291
|
+
trees.push(buildHuffmanTree(symbolCodeLengths, numSymbols));
|
|
4292
|
+
}
|
|
4293
|
+
}
|
|
4294
|
+
const numPixels = width * height;
|
|
4295
|
+
const pixels = new Uint8Array(numPixels * 4);
|
|
4296
|
+
let pixelIdx = 0;
|
|
4297
|
+
while (pixelIdx < numPixels) {
|
|
4298
|
+
const greenOrLen = readSymbol(reader, trees[0]);
|
|
4299
|
+
if (greenOrLen < 256) {
|
|
4300
|
+
const red = readSymbol(reader, trees[1]);
|
|
4301
|
+
const blue = readSymbol(reader, trees[2]);
|
|
4302
|
+
const alpha = readSymbol(reader, trees[3]);
|
|
4303
|
+
const off = pixelIdx * 4;
|
|
4304
|
+
pixels[off] = red;
|
|
4305
|
+
pixels[off + 1] = greenOrLen;
|
|
4306
|
+
pixels[off + 2] = blue;
|
|
4307
|
+
pixels[off + 3] = alpha;
|
|
4308
|
+
pixelIdx++;
|
|
4309
|
+
} else if (greenOrLen < 280) {
|
|
4310
|
+
const length = decodeLZ77Length(reader, greenOrLen - 256);
|
|
4311
|
+
const distance = decodeLZ77Distance(reader, readSymbol(reader, trees[4]), width);
|
|
4312
|
+
for (let i = 0; i < length && pixelIdx < numPixels; i++) {
|
|
4313
|
+
const srcIdx = (pixelIdx - distance) * 4;
|
|
4314
|
+
const dstIdx = pixelIdx * 4;
|
|
4315
|
+
if (srcIdx >= 0) {
|
|
4316
|
+
pixels[dstIdx] = pixels[srcIdx];
|
|
4317
|
+
pixels[dstIdx + 1] = pixels[srcIdx + 1];
|
|
4318
|
+
pixels[dstIdx + 2] = pixels[srcIdx + 2];
|
|
4319
|
+
pixels[dstIdx + 3] = pixels[srcIdx + 3];
|
|
4320
|
+
}
|
|
4321
|
+
pixelIdx++;
|
|
4322
|
+
}
|
|
4323
|
+
} else break;
|
|
4324
|
+
}
|
|
4325
|
+
for (let t = transforms.length - 1; t >= 0; t--) if (transforms[t].type === 2) for (let i = 0; i < numPixels; i++) {
|
|
4326
|
+
const off = i * 4;
|
|
4327
|
+
const green = pixels[off + 1];
|
|
4328
|
+
pixels[off] = pixels[off] + green & 255;
|
|
4329
|
+
pixels[off + 2] = pixels[off + 2] + green & 255;
|
|
4330
|
+
}
|
|
4331
|
+
const outChannels = hasAlpha ? 4 : 3;
|
|
4332
|
+
const output = new Uint8Array(numPixels * outChannels);
|
|
4333
|
+
for (let i = 0; i < numPixels; i++) {
|
|
4334
|
+
const srcOff = i * 4;
|
|
4335
|
+
const dstOff = i * outChannels;
|
|
4336
|
+
output[dstOff] = pixels[srcOff];
|
|
4337
|
+
output[dstOff + 1] = pixels[srcOff + 1];
|
|
4338
|
+
output[dstOff + 2] = pixels[srcOff + 2];
|
|
4339
|
+
if (hasAlpha) output[dstOff + 3] = pixels[srcOff + 3];
|
|
4340
|
+
}
|
|
4341
|
+
return {
|
|
4342
|
+
width,
|
|
4343
|
+
height,
|
|
4344
|
+
pixels: output,
|
|
4345
|
+
channels: hasAlpha ? 4 : 3,
|
|
4346
|
+
hasAlpha
|
|
4347
|
+
};
|
|
4348
|
+
}
|
|
4349
|
+
const lz77LengthPrefixExtraBits = [
|
|
4350
|
+
0,
|
|
4351
|
+
0,
|
|
4352
|
+
0,
|
|
4353
|
+
0,
|
|
4354
|
+
0,
|
|
4355
|
+
0,
|
|
4356
|
+
0,
|
|
4357
|
+
0,
|
|
4358
|
+
1,
|
|
4359
|
+
1,
|
|
4360
|
+
1,
|
|
4361
|
+
1,
|
|
4362
|
+
2,
|
|
4363
|
+
2,
|
|
4364
|
+
2,
|
|
4365
|
+
2,
|
|
4366
|
+
3,
|
|
4367
|
+
3,
|
|
4368
|
+
3,
|
|
4369
|
+
3,
|
|
4370
|
+
4,
|
|
4371
|
+
4,
|
|
4372
|
+
4,
|
|
4373
|
+
4
|
|
4374
|
+
];
|
|
4375
|
+
const lz77LengthPrefixOffset = [
|
|
4376
|
+
1,
|
|
4377
|
+
2,
|
|
4378
|
+
3,
|
|
4379
|
+
4,
|
|
4380
|
+
5,
|
|
4381
|
+
6,
|
|
4382
|
+
7,
|
|
4383
|
+
8,
|
|
4384
|
+
9,
|
|
4385
|
+
11,
|
|
4386
|
+
13,
|
|
4387
|
+
15,
|
|
4388
|
+
17,
|
|
4389
|
+
21,
|
|
4390
|
+
25,
|
|
4391
|
+
29,
|
|
4392
|
+
33,
|
|
4393
|
+
41,
|
|
4394
|
+
49,
|
|
4395
|
+
57,
|
|
4396
|
+
65,
|
|
4397
|
+
81,
|
|
4398
|
+
97,
|
|
4399
|
+
113
|
|
4400
|
+
];
|
|
4401
|
+
function decodeLZ77Length(reader, code) {
|
|
4402
|
+
if (code < 24) {
|
|
4403
|
+
const extra = lz77LengthPrefixExtraBits[code];
|
|
4404
|
+
return lz77LengthPrefixOffset[code] + (extra > 0 ? reader.readBits(extra) : 0);
|
|
4405
|
+
}
|
|
4406
|
+
return 1;
|
|
4407
|
+
}
|
|
4408
|
+
const lz77DistPrefixExtraBits = [
|
|
4409
|
+
0,
|
|
4410
|
+
0,
|
|
4411
|
+
0,
|
|
4412
|
+
0,
|
|
4413
|
+
1,
|
|
4414
|
+
1,
|
|
4415
|
+
1,
|
|
4416
|
+
1,
|
|
4417
|
+
2,
|
|
4418
|
+
2,
|
|
4419
|
+
2,
|
|
4420
|
+
2,
|
|
4421
|
+
3,
|
|
4422
|
+
3,
|
|
4423
|
+
3,
|
|
4424
|
+
3,
|
|
4425
|
+
4,
|
|
4426
|
+
4,
|
|
4427
|
+
4,
|
|
4428
|
+
4,
|
|
4429
|
+
5,
|
|
4430
|
+
5,
|
|
4431
|
+
5,
|
|
4432
|
+
5,
|
|
4433
|
+
6,
|
|
4434
|
+
6,
|
|
4435
|
+
6,
|
|
4436
|
+
6,
|
|
4437
|
+
7,
|
|
4438
|
+
7,
|
|
4439
|
+
7,
|
|
4440
|
+
7,
|
|
4441
|
+
8,
|
|
4442
|
+
8,
|
|
4443
|
+
8,
|
|
4444
|
+
8,
|
|
4445
|
+
9,
|
|
4446
|
+
9,
|
|
4447
|
+
9,
|
|
4448
|
+
9
|
|
4449
|
+
];
|
|
4450
|
+
const lz77DistPrefixOffset = [
|
|
4451
|
+
1,
|
|
4452
|
+
2,
|
|
4453
|
+
3,
|
|
4454
|
+
4,
|
|
4455
|
+
5,
|
|
4456
|
+
7,
|
|
4457
|
+
9,
|
|
4458
|
+
11,
|
|
4459
|
+
13,
|
|
4460
|
+
17,
|
|
4461
|
+
21,
|
|
4462
|
+
25,
|
|
4463
|
+
29,
|
|
4464
|
+
37,
|
|
4465
|
+
45,
|
|
4466
|
+
53,
|
|
4467
|
+
61,
|
|
4468
|
+
77,
|
|
4469
|
+
93,
|
|
4470
|
+
109,
|
|
4471
|
+
125,
|
|
4472
|
+
157,
|
|
4473
|
+
189,
|
|
4474
|
+
221,
|
|
4475
|
+
253,
|
|
4476
|
+
317,
|
|
4477
|
+
381,
|
|
4478
|
+
445,
|
|
4479
|
+
509,
|
|
4480
|
+
637,
|
|
4481
|
+
765,
|
|
4482
|
+
893,
|
|
4483
|
+
1021,
|
|
4484
|
+
1277,
|
|
4485
|
+
1533,
|
|
4486
|
+
1789,
|
|
4487
|
+
2045,
|
|
4488
|
+
2557,
|
|
4489
|
+
3069,
|
|
4490
|
+
3581
|
|
4491
|
+
];
|
|
4492
|
+
function decodeLZ77Distance(reader, code, _width) {
|
|
4493
|
+
if (code < 40) {
|
|
4494
|
+
const extra = lz77DistPrefixExtraBits[code];
|
|
4495
|
+
return lz77DistPrefixOffset[code] + (extra > 0 ? reader.readBits(extra) : 0);
|
|
4496
|
+
}
|
|
4497
|
+
return 1;
|
|
4498
|
+
}
|
|
4499
|
+
/**
|
|
4500
|
+
* Decode a WebP ALPH chunk to a flat alpha plane.
|
|
4501
|
+
*
|
|
4502
|
+
* The ALPH chunk format:
|
|
4503
|
+
* - 1 byte header: [preprocessing:2][filtering:2][compression:2][unused:2]
|
|
4504
|
+
* - Remaining bytes: alpha data
|
|
4505
|
+
*
|
|
4506
|
+
* Compression:
|
|
4507
|
+
* - 0 = uncompressed
|
|
4508
|
+
* - 1 = VP8L-compressed (lossless)
|
|
4509
|
+
*
|
|
4510
|
+
* Filtering:
|
|
4511
|
+
* - 0 = none
|
|
4512
|
+
* - 1 = horizontal
|
|
4513
|
+
* - 2 = vertical
|
|
4514
|
+
* - 3 = gradient
|
|
4515
|
+
*/
|
|
4516
|
+
function decodeAlphaChunk(data, width, height) {
|
|
4517
|
+
if (data.length < 1) throw new Error("WebP ALPH: chunk too short");
|
|
4518
|
+
const header = data[0];
|
|
4519
|
+
const filtering = header >> 2 & 3;
|
|
4520
|
+
const compression = header >> 0 & 3;
|
|
4521
|
+
let alphaData;
|
|
4522
|
+
if (compression === 0) alphaData = data.slice(1);
|
|
4523
|
+
else if (compression === 1) try {
|
|
4524
|
+
const lossless = decodeVP8L(data.slice(1));
|
|
4525
|
+
alphaData = new Uint8Array(width * height);
|
|
4526
|
+
for (let i = 0; i < width * height; i++) alphaData[i] = lossless.pixels[i * lossless.channels + 1] ?? 255;
|
|
4527
|
+
} catch {
|
|
4528
|
+
alphaData = new Uint8Array(width * height);
|
|
4529
|
+
alphaData.fill(255);
|
|
4530
|
+
}
|
|
4531
|
+
else throw new Error(`WebP ALPH: unsupported compression method ${compression}`);
|
|
4532
|
+
const numPixels = width * height;
|
|
4533
|
+
const alpha = new Uint8Array(numPixels);
|
|
4534
|
+
if (alphaData.length < numPixels) alpha.set(alphaData.slice(0, numPixels));
|
|
4535
|
+
else alpha.set(alphaData.slice(0, numPixels));
|
|
4536
|
+
applyAlphaFilter(alpha, width, height, filtering);
|
|
4537
|
+
return alpha;
|
|
4538
|
+
}
|
|
4539
|
+
function applyAlphaFilter(alpha, width, height, filtering) {
|
|
4540
|
+
if (filtering === 0) return;
|
|
4541
|
+
for (let row = 0; row < height; row++) for (let col = 0; col < width; col++) {
|
|
4542
|
+
const idx = row * width + col;
|
|
4543
|
+
const left = col > 0 ? alpha[idx - 1] : 0;
|
|
4544
|
+
const top = row > 0 ? alpha[idx - width] : 0;
|
|
4545
|
+
const topLeft = row > 0 && col > 0 ? alpha[idx - width - 1] : 0;
|
|
4546
|
+
switch (filtering) {
|
|
4547
|
+
case 1:
|
|
4548
|
+
alpha[idx] = alpha[idx] + left & 255;
|
|
4549
|
+
break;
|
|
4550
|
+
case 2:
|
|
4551
|
+
alpha[idx] = alpha[idx] + top & 255;
|
|
4552
|
+
break;
|
|
4553
|
+
case 3:
|
|
4554
|
+
alpha[idx] = alpha[idx] + clamp255(left + top - topLeft) & 255;
|
|
4555
|
+
break;
|
|
4556
|
+
}
|
|
4557
|
+
}
|
|
4558
|
+
}
|
|
4559
|
+
/** Merge RGB image with separate alpha plane to produce RGBA. */
|
|
4560
|
+
function mergeRgbAlpha(rgb, alphaPlane) {
|
|
4561
|
+
const { width, height } = rgb;
|
|
4562
|
+
const pixels = new Uint8Array(width * height * 4);
|
|
4563
|
+
for (let i = 0; i < width * height; i++) {
|
|
4564
|
+
const srcIdx = i * rgb.channels;
|
|
4565
|
+
const dstIdx = i * 4;
|
|
4566
|
+
pixels[dstIdx] = rgb.pixels[srcIdx];
|
|
4567
|
+
pixels[dstIdx + 1] = rgb.pixels[srcIdx + 1];
|
|
4568
|
+
pixels[dstIdx + 2] = rgb.pixels[srcIdx + 2];
|
|
4569
|
+
pixels[dstIdx + 3] = alphaPlane[i] ?? 255;
|
|
4570
|
+
}
|
|
4571
|
+
return {
|
|
4572
|
+
width,
|
|
4573
|
+
height,
|
|
4574
|
+
pixels,
|
|
4575
|
+
channels: 4,
|
|
4576
|
+
hasAlpha: true
|
|
4577
|
+
};
|
|
4578
|
+
}
|
|
4579
|
+
|
|
4580
|
+
//#endregion
|
|
4581
|
+
//#region src/assets/image/tiffDecode.ts
|
|
4582
|
+
/**
|
|
4583
|
+
* @module assets/image/tiffDecode
|
|
4584
|
+
*
|
|
4585
|
+
* TIFF image decoder — pure TypeScript, no WASM, no Buffer.
|
|
4586
|
+
*
|
|
4587
|
+
* Supports:
|
|
4588
|
+
* - IFD (Image File Directory) parsing with byte order detection
|
|
4589
|
+
* - Strip-based and tile-based image data extraction
|
|
4590
|
+
* - Compressions: None (1), LZW (5), JPEG-in-TIFF (6/7), Deflate (8), PackBits (32773)
|
|
4591
|
+
* - BitsPerSample: 1, 4, 8, 16
|
|
4592
|
+
* - SamplesPerPixel: 1, 3, 4
|
|
4593
|
+
* - PhotometricInterpretation: 0 (WhiteIsZero), 1 (BlackIsZero), 2 (RGB)
|
|
4594
|
+
* - Multi-page TIFF support via IFD chain
|
|
4595
|
+
*
|
|
4596
|
+
* Magic bytes: 49 49 (II, little-endian) or 4D 4D (MM, big-endian) + 42 (0x002A)
|
|
4597
|
+
*/
|
|
4598
|
+
const TAG_IMAGE_WIDTH = 256;
|
|
4599
|
+
const TAG_IMAGE_HEIGHT = 257;
|
|
4600
|
+
const TAG_BITS_PER_SAMPLE = 258;
|
|
4601
|
+
const TAG_COMPRESSION = 259;
|
|
4602
|
+
const TAG_PHOTOMETRIC = 262;
|
|
4603
|
+
const TAG_STRIP_OFFSETS = 273;
|
|
4604
|
+
const TAG_SAMPLES_PER_PIXEL = 277;
|
|
4605
|
+
const TAG_ROWS_PER_STRIP = 278;
|
|
4606
|
+
const TAG_STRIP_BYTE_COUNTS = 279;
|
|
4607
|
+
const TAG_JPEG_TABLES = 347;
|
|
4608
|
+
const COMPRESS_NONE = 1;
|
|
4609
|
+
const COMPRESS_LZW = 5;
|
|
4610
|
+
const COMPRESS_JPEG_OLD = 6;
|
|
4611
|
+
const COMPRESS_JPEG = 7;
|
|
4612
|
+
const COMPRESS_DEFLATE = 8;
|
|
4613
|
+
const COMPRESS_PACKBITS = 32773;
|
|
4614
|
+
const TYPE_SIZES = [
|
|
4615
|
+
0,
|
|
4616
|
+
1,
|
|
4617
|
+
1,
|
|
4618
|
+
2,
|
|
4619
|
+
4,
|
|
4620
|
+
8,
|
|
4621
|
+
1,
|
|
4622
|
+
1,
|
|
4623
|
+
2,
|
|
4624
|
+
4,
|
|
4625
|
+
8,
|
|
4626
|
+
4,
|
|
4627
|
+
8
|
|
4628
|
+
];
|
|
4629
|
+
var TiffReader = class {
|
|
4630
|
+
data;
|
|
4631
|
+
view;
|
|
4632
|
+
littleEndian;
|
|
4633
|
+
constructor(data, littleEndian) {
|
|
4634
|
+
this.data = data;
|
|
4635
|
+
this.view = new DataView(data.buffer, data.byteOffset, data.byteLength);
|
|
4636
|
+
this.littleEndian = littleEndian;
|
|
4637
|
+
}
|
|
4638
|
+
u16(offset) {
|
|
4639
|
+
return this.view.getUint16(offset, this.littleEndian);
|
|
4640
|
+
}
|
|
4641
|
+
u32(offset) {
|
|
4642
|
+
return this.view.getUint32(offset, this.littleEndian);
|
|
4643
|
+
}
|
|
4644
|
+
i16(offset) {
|
|
4645
|
+
return this.view.getInt16(offset, this.littleEndian);
|
|
4646
|
+
}
|
|
4647
|
+
i32(offset) {
|
|
4648
|
+
return this.view.getInt32(offset, this.littleEndian);
|
|
4649
|
+
}
|
|
4650
|
+
};
|
|
4651
|
+
/** Check if data is a TIFF file by examining the byte order marker and magic number. */
|
|
4652
|
+
function isTiff(data) {
|
|
4653
|
+
if (data.length < 4) return false;
|
|
4654
|
+
const bom = data[0] << 8 | data[1];
|
|
4655
|
+
if (bom !== 18761 && bom !== 19789) return false;
|
|
4656
|
+
const littleEndian = bom === 18761;
|
|
4657
|
+
return new DataView(data.buffer, data.byteOffset, data.byteLength).getUint16(2, littleEndian) === 42;
|
|
4658
|
+
}
|
|
4659
|
+
/**
|
|
4660
|
+
* Parse a single IFD from TIFF data.
|
|
4661
|
+
*
|
|
4662
|
+
* @param data Raw TIFF bytes.
|
|
4663
|
+
* @param offset Byte offset to the IFD.
|
|
4664
|
+
* @param littleEndian Whether the TIFF uses little-endian byte order.
|
|
4665
|
+
* @returns Array of IFD entries.
|
|
4666
|
+
*/
|
|
4667
|
+
function parseTiffIfd(data, offset, littleEndian) {
|
|
4668
|
+
const reader = new TiffReader(data, littleEndian);
|
|
4669
|
+
const numEntries = reader.u16(offset);
|
|
4670
|
+
const entries = [];
|
|
4671
|
+
for (let i = 0; i < numEntries; i++) {
|
|
4672
|
+
const entryOffset = offset + 2 + i * 12;
|
|
4673
|
+
if (entryOffset + 12 > data.length) break;
|
|
4674
|
+
const tag = reader.u16(entryOffset);
|
|
4675
|
+
const type = reader.u16(entryOffset + 2);
|
|
4676
|
+
const count = reader.u32(entryOffset + 4);
|
|
4677
|
+
const typeSize = TYPE_SIZES[type] ?? 1;
|
|
4678
|
+
const totalSize = typeSize * count;
|
|
4679
|
+
const values = [];
|
|
4680
|
+
const valueOffset = totalSize <= 4 ? entryOffset + 8 : reader.u32(entryOffset + 8);
|
|
4681
|
+
for (let j = 0; j < count; j++) {
|
|
4682
|
+
const pos = valueOffset + j * typeSize;
|
|
4683
|
+
if (pos + typeSize > data.length) break;
|
|
4684
|
+
switch (type) {
|
|
4685
|
+
case 1:
|
|
4686
|
+
case 7:
|
|
4687
|
+
values.push(data[pos]);
|
|
4688
|
+
break;
|
|
4689
|
+
case 2:
|
|
4690
|
+
values.push(data[pos]);
|
|
4691
|
+
break;
|
|
4692
|
+
case 3:
|
|
4693
|
+
values.push(reader.u16(pos));
|
|
4694
|
+
break;
|
|
4695
|
+
case 4:
|
|
4696
|
+
values.push(reader.u32(pos));
|
|
4697
|
+
break;
|
|
4698
|
+
case 5:
|
|
4699
|
+
values.push(reader.u32(pos) / (reader.u32(pos + 4) || 1));
|
|
4700
|
+
break;
|
|
4701
|
+
case 6:
|
|
4702
|
+
values.push(data[pos] > 127 ? data[pos] - 256 : data[pos]);
|
|
4703
|
+
break;
|
|
4704
|
+
case 8:
|
|
4705
|
+
values.push(reader.i16(pos));
|
|
4706
|
+
break;
|
|
4707
|
+
case 9:
|
|
4708
|
+
values.push(reader.i32(pos));
|
|
4709
|
+
break;
|
|
4710
|
+
case 10:
|
|
4711
|
+
values.push(reader.i32(pos) / (reader.i32(pos + 4) || 1));
|
|
4712
|
+
break;
|
|
4713
|
+
default:
|
|
4714
|
+
values.push(reader.u32(pos));
|
|
4715
|
+
break;
|
|
4716
|
+
}
|
|
4717
|
+
}
|
|
4718
|
+
entries.push({
|
|
4719
|
+
tag,
|
|
4720
|
+
type,
|
|
4721
|
+
count,
|
|
4722
|
+
values
|
|
4723
|
+
});
|
|
4724
|
+
}
|
|
4725
|
+
return entries;
|
|
4726
|
+
}
|
|
4727
|
+
/** Get the offset to the next IFD (0 = no more IFDs). */
|
|
4728
|
+
function getNextIfdOffset(data, currentIfdOffset, littleEndian) {
|
|
4729
|
+
const reader = new TiffReader(data, littleEndian);
|
|
4730
|
+
const numEntries = reader.u16(currentIfdOffset);
|
|
4731
|
+
const nextOffset = currentIfdOffset + 2 + numEntries * 12;
|
|
4732
|
+
if (nextOffset + 4 > data.length) return 0;
|
|
4733
|
+
return reader.u32(nextOffset);
|
|
4734
|
+
}
|
|
4735
|
+
/** Find the IFD at a given page index by following the IFD chain. */
|
|
4736
|
+
function findIfdAtPage(data, littleEndian, firstIfdOffset, pageIndex) {
|
|
4737
|
+
let offset = firstIfdOffset;
|
|
4738
|
+
for (let i = 0; i < pageIndex; i++) {
|
|
4739
|
+
offset = getNextIfdOffset(data, offset, littleEndian);
|
|
4740
|
+
if (offset === 0) throw new Error(`TIFF: page index ${pageIndex} out of range (only ${i + 1} pages)`);
|
|
4741
|
+
}
|
|
4742
|
+
return offset;
|
|
4743
|
+
}
|
|
4744
|
+
/** Get a tag value from IFD entries. */
|
|
4745
|
+
function getTag(entries, tag) {
|
|
4746
|
+
return entries.find((e) => e.tag === tag)?.values[0];
|
|
4747
|
+
}
|
|
4748
|
+
/** Get all values for a tag from IFD entries. */
|
|
4749
|
+
function getTagValues(entries, tag) {
|
|
4750
|
+
return entries.find((e) => e.tag === tag)?.values;
|
|
4751
|
+
}
|
|
4752
|
+
/** PackBits decompression (compression=32773). */
|
|
4753
|
+
function decompressPackBits(input, expectedLength) {
|
|
4754
|
+
const output = new Uint8Array(expectedLength);
|
|
4755
|
+
let srcPos = 0;
|
|
4756
|
+
let dstPos = 0;
|
|
4757
|
+
while (srcPos < input.length && dstPos < expectedLength) {
|
|
4758
|
+
const n = input[srcPos] > 127 ? input[srcPos] - 256 : input[srcPos];
|
|
4759
|
+
srcPos++;
|
|
4760
|
+
if (n >= 0) {
|
|
4761
|
+
const count = n + 1;
|
|
4762
|
+
for (let i = 0; i < count && srcPos < input.length && dstPos < expectedLength; i++) output[dstPos++] = input[srcPos++];
|
|
4763
|
+
} else if (n > -128) {
|
|
4764
|
+
const count = -n + 1;
|
|
4765
|
+
const val = input[srcPos];
|
|
4766
|
+
srcPos++;
|
|
4767
|
+
for (let i = 0; i < count && dstPos < expectedLength; i++) output[dstPos++] = val;
|
|
4768
|
+
}
|
|
4769
|
+
}
|
|
4770
|
+
return output.slice(0, dstPos);
|
|
4771
|
+
}
|
|
4772
|
+
/** LZW decompression (compression=5). */
|
|
4773
|
+
function decompressLzw(input, expectedLength) {
|
|
4774
|
+
const output = new Uint8Array(expectedLength);
|
|
4775
|
+
let outPos = 0;
|
|
4776
|
+
const CLEAR_CODE = 256;
|
|
4777
|
+
const EOI_CODE = 257;
|
|
4778
|
+
let codeSize = 9;
|
|
4779
|
+
let nextCode = 258;
|
|
4780
|
+
let maxCode = 1 << codeSize;
|
|
4781
|
+
const dictPrefix = new Int32Array(4096);
|
|
4782
|
+
const dictSuffix = new Uint8Array(4096);
|
|
4783
|
+
const dictLength = new Uint16Array(4096);
|
|
4784
|
+
function resetDict() {
|
|
4785
|
+
codeSize = 9;
|
|
4786
|
+
nextCode = 258;
|
|
4787
|
+
maxCode = 1 << codeSize;
|
|
4788
|
+
for (let i = 0; i < 256; i++) {
|
|
4789
|
+
dictPrefix[i] = -1;
|
|
4790
|
+
dictSuffix[i] = i;
|
|
4791
|
+
dictLength[i] = 1;
|
|
4792
|
+
}
|
|
4793
|
+
}
|
|
4794
|
+
resetDict();
|
|
4795
|
+
let bitBuf = 0;
|
|
4796
|
+
let bitsAvail = 0;
|
|
4797
|
+
let bytePos = 0;
|
|
4798
|
+
function readCode() {
|
|
4799
|
+
while (bitsAvail < codeSize) if (bytePos < input.length) {
|
|
4800
|
+
bitBuf |= input[bytePos] << bitsAvail;
|
|
4801
|
+
bytePos++;
|
|
4802
|
+
bitsAvail += 8;
|
|
4803
|
+
} else return EOI_CODE;
|
|
4804
|
+
const code = bitBuf & (1 << codeSize) - 1;
|
|
4805
|
+
bitBuf >>>= codeSize;
|
|
4806
|
+
bitsAvail -= codeSize;
|
|
4807
|
+
return code;
|
|
4808
|
+
}
|
|
4809
|
+
function outputString(code) {
|
|
4810
|
+
const len = dictLength[code];
|
|
4811
|
+
let pos = outPos + len - 1;
|
|
4812
|
+
let c = code;
|
|
4813
|
+
while (c >= 0 && pos >= outPos) {
|
|
4814
|
+
if (pos < expectedLength) output[pos] = dictSuffix[c];
|
|
4815
|
+
pos--;
|
|
4816
|
+
c = dictPrefix[c];
|
|
4817
|
+
}
|
|
4818
|
+
outPos += len;
|
|
4819
|
+
}
|
|
4820
|
+
function firstByte(code) {
|
|
4821
|
+
let c = code;
|
|
4822
|
+
while (dictPrefix[c] >= 0) c = dictPrefix[c];
|
|
4823
|
+
return dictSuffix[c];
|
|
4824
|
+
}
|
|
4825
|
+
let oldCode = -1;
|
|
4826
|
+
while (outPos < expectedLength) {
|
|
4827
|
+
const code = readCode();
|
|
4828
|
+
if (code === EOI_CODE) break;
|
|
4829
|
+
if (code === CLEAR_CODE) {
|
|
4830
|
+
resetDict();
|
|
4831
|
+
oldCode = -1;
|
|
4832
|
+
continue;
|
|
4833
|
+
}
|
|
4834
|
+
if (oldCode === -1) {
|
|
4835
|
+
outputString(code);
|
|
4836
|
+
oldCode = code;
|
|
4837
|
+
continue;
|
|
4838
|
+
}
|
|
4839
|
+
if (code < nextCode) {
|
|
4840
|
+
outputString(code);
|
|
4841
|
+
if (nextCode < 4096) {
|
|
4842
|
+
dictPrefix[nextCode] = oldCode;
|
|
4843
|
+
dictSuffix[nextCode] = firstByte(code);
|
|
4844
|
+
dictLength[nextCode] = dictLength[oldCode] + 1;
|
|
4845
|
+
nextCode++;
|
|
4846
|
+
}
|
|
4847
|
+
} else {
|
|
4848
|
+
const fb = firstByte(oldCode);
|
|
4849
|
+
if (nextCode < 4096) {
|
|
4850
|
+
dictPrefix[nextCode] = oldCode;
|
|
4851
|
+
dictSuffix[nextCode] = fb;
|
|
4852
|
+
dictLength[nextCode] = dictLength[oldCode] + 1;
|
|
4853
|
+
nextCode++;
|
|
4854
|
+
}
|
|
4855
|
+
outputString(code);
|
|
4856
|
+
}
|
|
4857
|
+
if (nextCode >= maxCode && codeSize < 12) {
|
|
4858
|
+
codeSize++;
|
|
4859
|
+
maxCode = 1 << codeSize;
|
|
4860
|
+
}
|
|
4861
|
+
oldCode = code;
|
|
4862
|
+
}
|
|
4863
|
+
return output.slice(0, Math.min(outPos, expectedLength));
|
|
4864
|
+
}
|
|
4865
|
+
/** Deflate decompression (compression=8). */
|
|
4866
|
+
function decompressDeflate(input, expectedLength) {
|
|
4867
|
+
return inflateSync(input).slice(0, expectedLength);
|
|
4868
|
+
}
|
|
4869
|
+
/** Convert 1-bit data to 8-bit (0 or 255). */
|
|
4870
|
+
function normalize1bit(data, width, height, whiteIsZero) {
|
|
4871
|
+
const output = new Uint8Array(width * height);
|
|
4872
|
+
for (let row = 0; row < height; row++) for (let col = 0; col < width; col++) {
|
|
4873
|
+
const byteIdx = row * Math.ceil(width / 8) + (col >> 3);
|
|
4874
|
+
const bitIdx = 7 - (col & 7);
|
|
4875
|
+
const bit = data[byteIdx] >> bitIdx & 1;
|
|
4876
|
+
const val = whiteIsZero ? bit === 0 ? 255 : 0 : bit === 1 ? 255 : 0;
|
|
4877
|
+
output[row * width + col] = val;
|
|
4878
|
+
}
|
|
4879
|
+
return output;
|
|
4880
|
+
}
|
|
4881
|
+
/** Convert 4-bit data to 8-bit. */
|
|
4882
|
+
function normalize4bit(data, width, height, whiteIsZero) {
|
|
4883
|
+
const output = new Uint8Array(width * height);
|
|
4884
|
+
for (let row = 0; row < height; row++) for (let col = 0; col < width; col++) {
|
|
4885
|
+
const byteIdx = row * Math.ceil(width / 2) + (col >> 1);
|
|
4886
|
+
let val = ((col & 1) === 0 ? data[byteIdx] >> 4 & 15 : data[byteIdx] & 15) * 255 / 15 | 0;
|
|
4887
|
+
if (whiteIsZero) val = 255 - val;
|
|
4888
|
+
output[row * width + col] = val;
|
|
4889
|
+
}
|
|
4890
|
+
return output;
|
|
4891
|
+
}
|
|
4892
|
+
/** Convert 16-bit data to 8-bit by keeping the high byte. */
|
|
4893
|
+
function normalize16bit(data, numSamples, littleEndian) {
|
|
4894
|
+
const output = new Uint8Array(numSamples);
|
|
4895
|
+
const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
|
|
4896
|
+
for (let i = 0; i < numSamples; i++) output[i] = view.getUint16(i * 2, littleEndian) >> 8 & 255;
|
|
4897
|
+
return output;
|
|
4898
|
+
}
|
|
4899
|
+
/**
|
|
4900
|
+
* Decode JPEG strip data. For JPEG-in-TIFF, each strip may be a complete
|
|
4901
|
+
* JPEG or the tables may be stored separately in the JPEGTables tag.
|
|
4902
|
+
*/
|
|
4903
|
+
function decodeJpegStrip(stripData, jpegTables, _width, stripHeight, samplesPerPixel) {
|
|
4904
|
+
let fullJpeg;
|
|
4905
|
+
if (jpegTables && jpegTables.length > 4) {
|
|
4906
|
+
const tableData = jpegTables.slice(2, jpegTables.length - 2);
|
|
4907
|
+
const stripBody = stripData.slice(2);
|
|
4908
|
+
fullJpeg = new Uint8Array(2 + tableData.length + stripBody.length);
|
|
4909
|
+
fullJpeg[0] = 255;
|
|
4910
|
+
fullJpeg[1] = 216;
|
|
4911
|
+
fullJpeg.set(tableData, 2);
|
|
4912
|
+
fullJpeg.set(stripBody, 2 + tableData.length);
|
|
4913
|
+
} else fullJpeg = stripData;
|
|
4914
|
+
throw new Error("TIFF JPEG-in-TIFF compression: JPEG strip decoding requires a JPEG decoder. This is supported when running with the JPEG WASM module initialized.");
|
|
4915
|
+
}
|
|
4916
|
+
/**
|
|
4917
|
+
* Get the number of pages in a multi-page TIFF.
|
|
4918
|
+
*
|
|
4919
|
+
* @param data Raw TIFF bytes.
|
|
4920
|
+
* @returns Number of IFDs (pages).
|
|
4921
|
+
*/
|
|
4922
|
+
function getTiffPageCount(data) {
|
|
4923
|
+
if (!isTiff(data)) throw new Error("TIFF: invalid TIFF header");
|
|
4924
|
+
const littleEndian = data[0] === 73;
|
|
4925
|
+
let offset = new TiffReader(data, littleEndian).u32(4);
|
|
4926
|
+
let count = 0;
|
|
4927
|
+
while (offset !== 0 && offset < data.length) {
|
|
4928
|
+
count++;
|
|
4929
|
+
offset = getNextIfdOffset(data, offset, littleEndian);
|
|
4930
|
+
}
|
|
4931
|
+
return count;
|
|
4932
|
+
}
|
|
4933
|
+
/**
|
|
4934
|
+
* Decode a specific page from a multi-page TIFF.
|
|
4935
|
+
*
|
|
4936
|
+
* @param data Raw TIFF bytes.
|
|
4937
|
+
* @param pageIndex 0-based page index.
|
|
4938
|
+
* @returns Decoded image.
|
|
4939
|
+
*/
|
|
4940
|
+
function decodeTiffPage(data, pageIndex) {
|
|
4941
|
+
return decodeTiff(data, { page: pageIndex });
|
|
4942
|
+
}
|
|
4943
|
+
/**
|
|
4944
|
+
* Decode all pages from a multi-page TIFF.
|
|
4945
|
+
*
|
|
4946
|
+
* @param data Raw TIFF bytes.
|
|
4947
|
+
* @returns Array of decoded images.
|
|
4948
|
+
*/
|
|
4949
|
+
function decodeTiffAll(data) {
|
|
4950
|
+
const pageCount = getTiffPageCount(data);
|
|
4951
|
+
const images = [];
|
|
4952
|
+
for (let i = 0; i < pageCount; i++) images.push(decodeTiffPage(data, i));
|
|
4953
|
+
return images;
|
|
4954
|
+
}
|
|
4955
|
+
/**
|
|
4956
|
+
* Decode a TIFF image.
|
|
4957
|
+
*
|
|
4958
|
+
* @param data Raw TIFF bytes.
|
|
4959
|
+
* @param options Decode options (page selection).
|
|
4960
|
+
* @returns Decoded image data.
|
|
4961
|
+
*/
|
|
4962
|
+
function decodeTiff(data, options) {
|
|
4963
|
+
if (!isTiff(data)) throw new Error("TIFF: invalid TIFF header (expected II or MM byte order marker + 42)");
|
|
4964
|
+
const littleEndian = data[0] === 73;
|
|
4965
|
+
const entries = parseTiffIfd(data, findIfdAtPage(data, littleEndian, new TiffReader(data, littleEndian).u32(4), options?.page ?? 0), littleEndian);
|
|
4966
|
+
const width = getTag(entries, TAG_IMAGE_WIDTH);
|
|
4967
|
+
const height = getTag(entries, TAG_IMAGE_HEIGHT);
|
|
4968
|
+
const compression = getTag(entries, TAG_COMPRESSION) ?? COMPRESS_NONE;
|
|
4969
|
+
const photometric = getTag(entries, TAG_PHOTOMETRIC) ?? 1;
|
|
4970
|
+
const bitsPerSample = getTag(entries, TAG_BITS_PER_SAMPLE) ?? 8;
|
|
4971
|
+
const samplesPerPixel = getTag(entries, TAG_SAMPLES_PER_PIXEL) ?? 1;
|
|
4972
|
+
const rowsPerStrip = getTag(entries, TAG_ROWS_PER_STRIP) ?? height ?? 0;
|
|
4973
|
+
const stripOffsets = getTagValues(entries, TAG_STRIP_OFFSETS);
|
|
4974
|
+
const stripByteCounts = getTagValues(entries, TAG_STRIP_BYTE_COUNTS);
|
|
4975
|
+
if (width === void 0 || height === void 0) throw new Error("TIFF: missing ImageWidth or ImageHeight tag");
|
|
4976
|
+
if (!stripOffsets || stripOffsets.length === 0) throw new Error("TIFF: missing StripOffsets tag");
|
|
4977
|
+
let jpegTables;
|
|
4978
|
+
const jpegTablesEntry = entries.find((e) => e.tag === TAG_JPEG_TABLES);
|
|
4979
|
+
if (jpegTablesEntry) {
|
|
4980
|
+
const tablesOffset = jpegTablesEntry.values[0];
|
|
4981
|
+
if (jpegTablesEntry.type === 7 && jpegTablesEntry.count > 4) jpegTables = data.slice(tablesOffset, tablesOffset + jpegTablesEntry.count);
|
|
4982
|
+
}
|
|
4983
|
+
const bytesPerRow = Math.ceil(width * samplesPerPixel * bitsPerSample / 8);
|
|
4984
|
+
const numStrips = stripOffsets.length;
|
|
4985
|
+
const rawData = new Uint8Array(height * bytesPerRow);
|
|
4986
|
+
let outOffset = 0;
|
|
4987
|
+
for (let strip = 0; strip < numStrips; strip++) {
|
|
4988
|
+
const stripOffset = stripOffsets[strip];
|
|
4989
|
+
const stripByteCount = stripByteCounts?.[strip] ?? bytesPerRow * rowsPerStrip;
|
|
4990
|
+
const stripRows = Math.min(rowsPerStrip, height - strip * rowsPerStrip);
|
|
4991
|
+
const expectedStripBytes = stripRows * bytesPerRow;
|
|
4992
|
+
if (stripOffset + stripByteCount > data.length) break;
|
|
4993
|
+
const compressedData = data.slice(stripOffset, stripOffset + stripByteCount);
|
|
4994
|
+
let decompressed;
|
|
4995
|
+
switch (compression) {
|
|
4996
|
+
case COMPRESS_NONE:
|
|
4997
|
+
decompressed = compressedData;
|
|
4998
|
+
break;
|
|
4999
|
+
case COMPRESS_PACKBITS:
|
|
5000
|
+
decompressed = decompressPackBits(compressedData, expectedStripBytes);
|
|
5001
|
+
break;
|
|
5002
|
+
case COMPRESS_LZW:
|
|
5003
|
+
decompressed = decompressLzw(compressedData, expectedStripBytes);
|
|
5004
|
+
break;
|
|
5005
|
+
case COMPRESS_DEFLATE:
|
|
5006
|
+
decompressed = decompressDeflate(compressedData, expectedStripBytes);
|
|
5007
|
+
break;
|
|
5008
|
+
case COMPRESS_JPEG:
|
|
5009
|
+
case COMPRESS_JPEG_OLD:
|
|
5010
|
+
decompressed = decodeJpegStrip(compressedData, jpegTables, width, stripRows, samplesPerPixel);
|
|
5011
|
+
break;
|
|
5012
|
+
default: throw new Error(`TIFF: unsupported compression type ${compression}`);
|
|
5013
|
+
}
|
|
5014
|
+
const copyLen = Math.min(decompressed.length, rawData.length - outOffset);
|
|
5015
|
+
rawData.set(decompressed.slice(0, copyLen), outOffset);
|
|
5016
|
+
outOffset += expectedStripBytes;
|
|
5017
|
+
}
|
|
5018
|
+
const whiteIsZero = photometric === 0;
|
|
5019
|
+
let pixels;
|
|
5020
|
+
if (bitsPerSample === 1 && samplesPerPixel === 1) pixels = normalize1bit(rawData, width, height, whiteIsZero);
|
|
5021
|
+
else if (bitsPerSample === 4 && samplesPerPixel === 1) pixels = normalize4bit(rawData, width, height, whiteIsZero);
|
|
5022
|
+
else if (bitsPerSample === 16) {
|
|
5023
|
+
pixels = normalize16bit(rawData, width * height * samplesPerPixel, littleEndian);
|
|
5024
|
+
if (whiteIsZero && samplesPerPixel === 1) for (let i = 0; i < pixels.length; i++) pixels[i] = 255 - pixels[i];
|
|
5025
|
+
} else if (bitsPerSample === 8) {
|
|
5026
|
+
pixels = rawData.slice(0, width * height * samplesPerPixel);
|
|
5027
|
+
if (whiteIsZero && samplesPerPixel === 1) for (let i = 0; i < pixels.length; i++) pixels[i] = 255 - pixels[i];
|
|
5028
|
+
} else throw new Error(`TIFF: unsupported BitsPerSample ${bitsPerSample}`);
|
|
5029
|
+
return {
|
|
5030
|
+
width,
|
|
5031
|
+
height,
|
|
5032
|
+
pixels,
|
|
5033
|
+
channels: samplesPerPixel === 1 ? 1 : samplesPerPixel === 3 ? 3 : samplesPerPixel === 4 ? 4 : 1,
|
|
5034
|
+
bitsPerSample
|
|
5035
|
+
};
|
|
5036
|
+
}
|
|
5037
|
+
|
|
3585
5038
|
//#endregion
|
|
3586
5039
|
//#region src/parser/parseError.ts
|
|
3587
5040
|
/**
|
|
@@ -4196,7 +5649,7 @@ var PdfLexer = class {
|
|
|
4196
5649
|
this.position = pos;
|
|
4197
5650
|
return {
|
|
4198
5651
|
type: TokenType.HexString,
|
|
4199
|
-
value: String.fromCharCode
|
|
5652
|
+
value: String.fromCharCode(...bytes),
|
|
4200
5653
|
offset: startPos
|
|
4201
5654
|
};
|
|
4202
5655
|
}
|
|
@@ -4355,7 +5808,7 @@ var PdfLexer = class {
|
|
|
4355
5808
|
* because it avoids the per-call overhead of the streaming decoder.
|
|
4356
5809
|
*/
|
|
4357
5810
|
bytesToAscii(from, to) {
|
|
4358
|
-
return String.fromCharCode
|
|
5811
|
+
return String.fromCharCode(...this._data.subarray(from, to));
|
|
4359
5812
|
}
|
|
4360
5813
|
};
|
|
4361
5814
|
|
|
@@ -4829,10 +6282,10 @@ async function tryLoadLibdeflate() {
|
|
|
4829
6282
|
if (libdeflateAttempted) return libdeflateEngine;
|
|
4830
6283
|
libdeflateAttempted = true;
|
|
4831
6284
|
try {
|
|
4832
|
-
const { LibdeflateWasm: LibdeflateCtor, initDeflateWasm } = await import("./libdeflateWasm-
|
|
6285
|
+
const { LibdeflateWasm: LibdeflateCtor, initDeflateWasm } = await import("./libdeflateWasm-8b91Vmia.mjs").then((n) => n.r);
|
|
4833
6286
|
let customBytes;
|
|
4834
6287
|
try {
|
|
4835
|
-
const { getWasmLoaderConfig } = await import("./loader-
|
|
6288
|
+
const { getWasmLoaderConfig } = await import("./loader-C7B5dVCI.mjs").then((n) => n.l);
|
|
4836
6289
|
customBytes = getWasmLoaderConfig().moduleBytes?.["libdeflate"];
|
|
4837
6290
|
} catch {}
|
|
4838
6291
|
await initDeflateWasm(customBytes);
|
|
@@ -4850,7 +6303,7 @@ async function tryLoadLibdeflate() {
|
|
|
4850
6303
|
*/
|
|
4851
6304
|
async function loadFflate() {
|
|
4852
6305
|
if (fflateEngine) return fflateEngine;
|
|
4853
|
-
const { FflateEngine: FflateCtor } = await import("./fflateAdapter-
|
|
6306
|
+
const { FflateEngine: FflateCtor } = await import("./fflateAdapter-PSiW_ML7.mjs").then((n) => n.n);
|
|
4854
6307
|
fflateEngine = new FflateCtor();
|
|
4855
6308
|
return fflateEngine;
|
|
4856
6309
|
}
|
|
@@ -5147,13 +6600,13 @@ var XrefParser = class {
|
|
|
5147
6600
|
actual: "no \"startxref\" found",
|
|
5148
6601
|
data: this.data
|
|
5149
6602
|
});
|
|
5150
|
-
const afterKeyword = tail.
|
|
6603
|
+
const afterKeyword = tail.slice(idx + 9).trim();
|
|
5151
6604
|
const match = afterKeyword.match(/^(\d+)/);
|
|
5152
6605
|
if (!match) throw new PdfParseError({
|
|
5153
6606
|
message: "Invalid PDF: \"startxref\" found but no valid offset follows it.",
|
|
5154
6607
|
offset: startPos + idx,
|
|
5155
6608
|
expected: "decimal offset after \"startxref\"",
|
|
5156
|
-
actual: `"${afterKeyword.
|
|
6609
|
+
actual: `"${afterKeyword.slice(0, 20)}"`,
|
|
5157
6610
|
data: this.data
|
|
5158
6611
|
});
|
|
5159
6612
|
const offset = parseInt(match[1], 10);
|
|
@@ -6670,9 +8123,9 @@ const FILE_KEY_CACHE_MAX = 32;
|
|
|
6670
8123
|
* a key derivation result.
|
|
6671
8124
|
*/
|
|
6672
8125
|
function buildCacheKey(password, dict, fileId) {
|
|
6673
|
-
const oHex =
|
|
6674
|
-
const uHex =
|
|
6675
|
-
const fHex =
|
|
8126
|
+
const oHex = dict.ownerKey.subarray(0, 16).toHex();
|
|
8127
|
+
const uHex = dict.userKey.subarray(0, 16).toHex();
|
|
8128
|
+
const fHex = fileId.subarray(0, 16).toHex();
|
|
6676
8129
|
return `${dict.revision}:${dict.permissions}:${password}:${oHex}:${uHex}:${fHex}`;
|
|
6677
8130
|
}
|
|
6678
8131
|
/**
|
|
@@ -7666,12 +9119,12 @@ var PdfDocumentParser = class {
|
|
|
7666
9119
|
actual: "no \"%PDF-\" marker found",
|
|
7667
9120
|
data: this.data
|
|
7668
9121
|
});
|
|
7669
|
-
const versionMatch = header.
|
|
9122
|
+
const versionMatch = header.slice(pdfIdx).match(/%PDF-(\d+\.\d+)/);
|
|
7670
9123
|
if (!versionMatch) throw new PdfParseError({
|
|
7671
9124
|
message: "Invalid PDF: could not parse version from header.",
|
|
7672
9125
|
offset: pdfIdx,
|
|
7673
9126
|
expected: "\"%PDF-X.Y\" version string",
|
|
7674
|
-
actual: `"${header.
|
|
9127
|
+
actual: `"${header.slice(pdfIdx, pdfIdx + 10)}"`,
|
|
7675
9128
|
data: this.data
|
|
7676
9129
|
});
|
|
7677
9130
|
this.pdfVersion = versionMatch[1];
|
|
@@ -8168,9 +9621,9 @@ var PdfDocumentParser = class {
|
|
|
8168
9621
|
if (subObj === void 0 || subObj.kind !== "dict") return 0;
|
|
8169
9622
|
const subDict = subObj;
|
|
8170
9623
|
return Iterator.from(subDict).map(([key]) => {
|
|
8171
|
-
const name = key.startsWith("/") ? key.
|
|
9624
|
+
const name = key.startsWith("/") ? key.slice(1) : key;
|
|
8172
9625
|
if (name.startsWith(prefix)) {
|
|
8173
|
-
const num = parseInt(name.
|
|
9626
|
+
const num = parseInt(name.slice(prefix.length), 10);
|
|
8174
9627
|
return isNaN(num) ? 0 : num;
|
|
8175
9628
|
}
|
|
8176
9629
|
return 0;
|
|
@@ -8196,7 +9649,7 @@ var PdfDocumentParser = class {
|
|
|
8196
9649
|
*/
|
|
8197
9650
|
function parsePdfDate(dateStr) {
|
|
8198
9651
|
let s = dateStr;
|
|
8199
|
-
if (s.startsWith("D:")) s = s.
|
|
9652
|
+
if (s.startsWith("D:")) s = s.slice(2);
|
|
8200
9653
|
const match = s.match(/^(\d{4})(\d{2})?(\d{2})?(\d{2})?(\d{2})?(\d{2})?([Z+\-])?(\d{2})?'?(\d{2})?/);
|
|
8201
9654
|
if (!match) return void 0;
|
|
8202
9655
|
const year = parseInt(match[1], 10);
|
|
@@ -10275,7 +11728,7 @@ const decoder$2 = new TextDecoder("latin1");
|
|
|
10275
11728
|
* The /ByteRange values are placeholders that will be updated after
|
|
10276
11729
|
* the final byte positions are known.
|
|
10277
11730
|
*/
|
|
10278
|
-
function buildSignatureDictString(placeholderSize, fieldName, signingDate) {
|
|
11731
|
+
function buildSignatureDictString(placeholderSize, fieldName, signingDate, mdpReference) {
|
|
10279
11732
|
const contentsHex = "0".repeat(placeholderSize * 2);
|
|
10280
11733
|
const byteRangePlaceholder = "0000000000 0000000000 0000000000 0000000000";
|
|
10281
11734
|
let dict = "";
|
|
@@ -10285,6 +11738,7 @@ function buildSignatureDictString(placeholderSize, fieldName, signingDate) {
|
|
|
10285
11738
|
dict += ` /ByteRange [${byteRangePlaceholder}]`;
|
|
10286
11739
|
dict += ` /Contents <${contentsHex}>`;
|
|
10287
11740
|
if (signingDate !== void 0) dict += ` /M (D:${formatPdfDate(signingDate)})`;
|
|
11741
|
+
if (mdpReference !== void 0) dict += mdpReference;
|
|
10288
11742
|
dict += " >>";
|
|
10289
11743
|
return dict;
|
|
10290
11744
|
}
|
|
@@ -10393,16 +11847,16 @@ function n$1(value) {
|
|
|
10393
11847
|
if (Number.isInteger(value)) return value.toString();
|
|
10394
11848
|
return value.toFixed(6).replace(/\.?0+$/, "");
|
|
10395
11849
|
}
|
|
10396
|
-
function prepareForSigning(pdfBytes, signatureFieldName, placeholderSize = 8192, appearance) {
|
|
11850
|
+
function prepareForSigning(pdfBytes, signatureFieldName, placeholderSize = 8192, appearance, mdpPermission, fieldLock) {
|
|
10397
11851
|
const pdfStr = decoder$2.decode(pdfBytes);
|
|
10398
11852
|
const startxrefIdx = pdfStr.lastIndexOf("startxref");
|
|
10399
11853
|
if (startxrefIdx === -1) throw new Error("Cannot find startxref in PDF — file may be corrupted");
|
|
10400
|
-
const xrefOffsetMatch = pdfStr.
|
|
11854
|
+
const xrefOffsetMatch = pdfStr.slice(startxrefIdx + 9).trim().match(/^(\d+)/);
|
|
10401
11855
|
if (!xrefOffsetMatch) throw new Error("Cannot parse xref offset from startxref");
|
|
10402
11856
|
const prevXrefOffset = parseInt(xrefOffsetMatch[1], 10);
|
|
10403
|
-
const
|
|
10404
|
-
if (
|
|
10405
|
-
const originalSize = parseInt(
|
|
11857
|
+
const sizeMatches = [...pdfStr.matchAll(/\/Size\s+(\d+)/g)];
|
|
11858
|
+
if (sizeMatches.length === 0) throw new Error("Cannot find /Size in PDF trailer");
|
|
11859
|
+
const originalSize = parseInt(sizeMatches[sizeMatches.length - 1][1], 10);
|
|
10406
11860
|
const rootMatch = pdfStr.match(/\/Root\s+(\d+)\s+(\d+)\s+R/);
|
|
10407
11861
|
if (!rootMatch) throw new Error("Cannot find /Root in PDF trailer");
|
|
10408
11862
|
const rootObjNum = parseInt(rootMatch[1], 10);
|
|
@@ -10416,7 +11870,9 @@ function prepareForSigning(pdfBytes, signatureFieldName, placeholderSize = 8192,
|
|
|
10416
11870
|
apStreamObjNum = newSize;
|
|
10417
11871
|
newSize++;
|
|
10418
11872
|
}
|
|
10419
|
-
|
|
11873
|
+
let mdpReference;
|
|
11874
|
+
if (mdpPermission !== void 0 && mdpPermission >= 1 && mdpPermission <= 3) mdpReference = ` /Reference [<< /Type /SigRef /TransformMethod /DocMDP /TransformParams << /Type /TransformParams /P ${mdpPermission} /V /1.2 >> >>]`;
|
|
11875
|
+
const sigDictStr = buildSignatureDictString(placeholderSize, signatureFieldName, void 0, mdpReference);
|
|
10420
11876
|
let rectStr = "0 0 0 0";
|
|
10421
11877
|
if (appearance) {
|
|
10422
11878
|
const [x, y, w, h] = appearance.rect;
|
|
@@ -10424,6 +11880,14 @@ function prepareForSigning(pdfBytes, signatureFieldName, placeholderSize = 8192,
|
|
|
10424
11880
|
}
|
|
10425
11881
|
let sigFieldDict = `<< /Type /Annot /Subtype /Widget /FT /Sig /T (${signatureFieldName}) /V ${sigValueObjNum} 0 R /F 132 /Rect [${rectStr}]`;
|
|
10426
11882
|
if (appearance && apStreamObjNum >= 0) sigFieldDict += ` /AP << /N ${apStreamObjNum} 0 R >>`;
|
|
11883
|
+
if (fieldLock !== void 0) {
|
|
11884
|
+
sigFieldDict += ` /Lock << /Type /SigFieldLock /Action /${fieldLock.action}`;
|
|
11885
|
+
if (fieldLock.action !== "All" && fieldLock.fields && fieldLock.fields.length > 0) {
|
|
11886
|
+
const fieldEntries = fieldLock.fields.map((f) => `(${escapePdfString(f)})`).join(" ");
|
|
11887
|
+
sigFieldDict += ` /Fields [${fieldEntries}]`;
|
|
11888
|
+
}
|
|
11889
|
+
sigFieldDict += " >>";
|
|
11890
|
+
}
|
|
10427
11891
|
sigFieldDict += " >>";
|
|
10428
11892
|
let appendix = "\n";
|
|
10429
11893
|
const objOffsets = /* @__PURE__ */ new Map();
|
|
@@ -10568,12 +12032,12 @@ function findSignatures(pdfBytes) {
|
|
|
10568
12032
|
];
|
|
10569
12033
|
const searchStart = Math.max(0, match.index - 2e3);
|
|
10570
12034
|
const searchEnd = Math.min(text.length, match.index + 2e3);
|
|
10571
|
-
const contentsKeyIdx = text.
|
|
12035
|
+
const contentsKeyIdx = text.slice(searchStart, searchEnd).indexOf("/Contents <");
|
|
10572
12036
|
if (contentsKeyIdx !== -1) {
|
|
10573
12037
|
const absoluteHexStart = searchStart + (contentsKeyIdx + 10);
|
|
10574
12038
|
const closingBracket = text.indexOf(">", absoluteHexStart + 1);
|
|
10575
12039
|
if (closingBracket !== -1) {
|
|
10576
|
-
const hexStr = text.
|
|
12040
|
+
const hexStr = text.slice(absoluteHexStart + 1, closingBracket);
|
|
10577
12041
|
const contentsLen = closingBracket - absoluteHexStart + 1;
|
|
10578
12042
|
results.push({
|
|
10579
12043
|
byteRange: br,
|
|
@@ -11140,7 +12604,7 @@ const decoder$1 = new TextDecoder("latin1");
|
|
|
11140
12604
|
*/
|
|
11141
12605
|
function extractFieldName(pdfStr, sigDictOffset) {
|
|
11142
12606
|
const searchStart = Math.max(0, sigDictOffset - 2e3);
|
|
11143
|
-
const tMatch = pdfStr.
|
|
12607
|
+
const tMatch = pdfStr.slice(searchStart, sigDictOffset + 1e3).match(/\/T\s*\(([^)]*)\)/);
|
|
11144
12608
|
if (tMatch) return tMatch[1];
|
|
11145
12609
|
return "Signature";
|
|
11146
12610
|
}
|
|
@@ -11151,7 +12615,7 @@ function extractFieldName(pdfStr, sigDictOffset) {
|
|
|
11151
12615
|
function extractSigDictStrings(pdfStr, byteRangeOffset) {
|
|
11152
12616
|
const searchStart = Math.max(0, byteRangeOffset - 1e3);
|
|
11153
12617
|
const searchEnd = Math.min(pdfStr.length, byteRangeOffset + 2e3);
|
|
11154
|
-
const region = pdfStr.
|
|
12618
|
+
const region = pdfStr.slice(searchStart, searchEnd);
|
|
11155
12619
|
const result = {};
|
|
11156
12620
|
const reasonMatch = region.match(/\/Reason\s*\(([^)]*)\)/);
|
|
11157
12621
|
if (reasonMatch && reasonMatch[1] !== void 0) result.reason = reasonMatch[1];
|
|
@@ -11197,7 +12661,7 @@ function hexToBytes$1(hex) {
|
|
|
11197
12661
|
const trimLen = trailingZeros[0].length;
|
|
11198
12662
|
if (trimLen % 2 === 0) {
|
|
11199
12663
|
const nonZeroEnd = cleanHex.length - trimLen;
|
|
11200
|
-
if (nonZeroEnd > 0 && nonZeroEnd % 2 === 0) cleanHex = cleanHex.
|
|
12664
|
+
if (nonZeroEnd > 0 && nonZeroEnd % 2 === 0) cleanHex = cleanHex.slice(0, nonZeroEnd);
|
|
11201
12665
|
}
|
|
11202
12666
|
}
|
|
11203
12667
|
if (cleanHex.length % 2 !== 0) cleanHex = "0" + cleanHex;
|
|
@@ -11262,7 +12726,7 @@ async function signPdf(pdfBytes, fieldName, options) {
|
|
|
11262
12726
|
}
|
|
11263
12727
|
if (options.reason) textLines.push(`Reason: ${options.reason}`);
|
|
11264
12728
|
if (options.location) textLines.push(`Location: ${options.location}`);
|
|
11265
|
-
textLines.push(`Date: ${(/* @__PURE__ */ new Date()).toISOString().
|
|
12729
|
+
textLines.push(`Date: ${(/* @__PURE__ */ new Date()).toISOString().slice(0, 10)}`);
|
|
11266
12730
|
}
|
|
11267
12731
|
prepareAppearance = {
|
|
11268
12732
|
rect: ap.rect,
|
|
@@ -11346,7 +12810,7 @@ function hexToBytes(hex) {
|
|
|
11346
12810
|
endIdx = clean.length - trailingMatch[0].length;
|
|
11347
12811
|
if (endIdx < 2) endIdx = clean.length;
|
|
11348
12812
|
}
|
|
11349
|
-
return Uint8Array.fromHex(clean.
|
|
12813
|
+
return Uint8Array.fromHex(clean.slice(0, endIdx));
|
|
11350
12814
|
}
|
|
11351
12815
|
/**
|
|
11352
12816
|
* Extract the certificate, signed attributes, and raw signature
|
|
@@ -11447,12 +12911,12 @@ function extractSigningTime(signedAttrs) {
|
|
|
11447
12911
|
*/
|
|
11448
12912
|
function parseUtcTime(utcTime) {
|
|
11449
12913
|
const clean = utcTime.replace("Z", "");
|
|
11450
|
-
const year = parseInt(clean.
|
|
11451
|
-
const month = parseInt(clean.
|
|
11452
|
-
const day = parseInt(clean.
|
|
11453
|
-
const hours = parseInt(clean.
|
|
11454
|
-
const minutes = parseInt(clean.
|
|
11455
|
-
const seconds = parseInt(clean.
|
|
12914
|
+
const year = parseInt(clean.slice(0, 2), 10);
|
|
12915
|
+
const month = parseInt(clean.slice(2, 4), 10) - 1;
|
|
12916
|
+
const day = parseInt(clean.slice(4, 6), 10);
|
|
12917
|
+
const hours = parseInt(clean.slice(6, 8), 10);
|
|
12918
|
+
const minutes = parseInt(clean.slice(8, 10), 10);
|
|
12919
|
+
const seconds = parseInt(clean.slice(10, 12), 10);
|
|
11456
12920
|
const fullYear = year < 50 ? 2e3 + year : 1900 + year;
|
|
11457
12921
|
return new Date(Date.UTC(fullYear, month, day, hours, minutes, seconds));
|
|
11458
12922
|
}
|
|
@@ -11463,7 +12927,7 @@ function parseUtcTime(utcTime) {
|
|
|
11463
12927
|
function extractFieldInfo(pdfStr, contentsOffset) {
|
|
11464
12928
|
const searchStart = Math.max(0, contentsOffset - 3e3);
|
|
11465
12929
|
const searchEnd = Math.min(pdfStr.length, contentsOffset + 2e3);
|
|
11466
|
-
const region = pdfStr.
|
|
12930
|
+
const region = pdfStr.slice(searchStart, searchEnd);
|
|
11467
12931
|
let fieldName = "Signature";
|
|
11468
12932
|
const tMatch = region.match(/\/T\s*\(([^)]*)\)/);
|
|
11469
12933
|
if (tMatch) fieldName = tMatch[1];
|
|
@@ -11845,6 +13309,21 @@ function addWatermark(doc, options) {
|
|
|
11845
13309
|
}
|
|
11846
13310
|
}
|
|
11847
13311
|
|
|
13312
|
+
//#endregion
|
|
13313
|
+
//#region src/form/documentScripts.ts
|
|
13314
|
+
/** WeakMap to store document-level action state per PdfDocument instance. */
|
|
13315
|
+
const documentActionState = /* @__PURE__ */ new WeakMap();
|
|
13316
|
+
/**
|
|
13317
|
+
* Get the internal state for a document's catalog-level actions.
|
|
13318
|
+
*
|
|
13319
|
+
* Used by the catalog builder to set `/OpenAction` and `/AA` entries.
|
|
13320
|
+
*
|
|
13321
|
+
* @internal
|
|
13322
|
+
*/
|
|
13323
|
+
function getDocumentActionState(doc) {
|
|
13324
|
+
return documentActionState.get(doc);
|
|
13325
|
+
}
|
|
13326
|
+
|
|
11848
13327
|
//#endregion
|
|
11849
13328
|
//#region src/core/pdfEmbed.ts
|
|
11850
13329
|
/**
|
|
@@ -12807,12 +14286,232 @@ var PdfDocument = class PdfDocument {
|
|
|
12807
14286
|
return imageRef;
|
|
12808
14287
|
}
|
|
12809
14288
|
/**
|
|
14289
|
+
* Embed a WebP image.
|
|
14290
|
+
*
|
|
14291
|
+
* WebP cannot be directly embedded in PDF. This method decodes the
|
|
14292
|
+
* WebP image to raw pixels (VP8/lossy, VP8L/lossless, or VP8+ALPH),
|
|
14293
|
+
* then embeds as a FlateDecode image XObject. If the WebP has
|
|
14294
|
+
* transparency, the alpha channel is embedded as a soft mask.
|
|
14295
|
+
*
|
|
14296
|
+
* @param webpData Raw WebP file bytes as a `Uint8Array` or `ArrayBuffer`.
|
|
14297
|
+
* @returns An {@link ImageRef}.
|
|
14298
|
+
*/
|
|
14299
|
+
embedWebP(webpData) {
|
|
14300
|
+
const decoded = decodeWebP(webpData instanceof ArrayBuffer ? new Uint8Array(webpData) : webpData);
|
|
14301
|
+
this.imageCounter++;
|
|
14302
|
+
const resourceName = `Im${this.imageCounter}`;
|
|
14303
|
+
const dict = new PdfDict();
|
|
14304
|
+
dict.set("/Type", PdfName.of("XObject"));
|
|
14305
|
+
dict.set("/Subtype", PdfName.of("Image"));
|
|
14306
|
+
dict.set("/Width", PdfNumber.of(decoded.width));
|
|
14307
|
+
dict.set("/Height", PdfNumber.of(decoded.height));
|
|
14308
|
+
dict.set("/BitsPerComponent", PdfNumber.of(8));
|
|
14309
|
+
if (decoded.hasAlpha) {
|
|
14310
|
+
const pixelCount = decoded.width * decoded.height;
|
|
14311
|
+
const rgb = new Uint8Array(pixelCount * 3);
|
|
14312
|
+
const alpha = new Uint8Array(pixelCount);
|
|
14313
|
+
for (let i = 0; i < pixelCount; i++) {
|
|
14314
|
+
const srcIdx = i * 4;
|
|
14315
|
+
const dstIdx = i * 3;
|
|
14316
|
+
rgb[dstIdx] = decoded.pixels[srcIdx];
|
|
14317
|
+
rgb[dstIdx + 1] = decoded.pixels[srcIdx + 1];
|
|
14318
|
+
rgb[dstIdx + 2] = decoded.pixels[srcIdx + 2];
|
|
14319
|
+
alpha[i] = decoded.pixels[srcIdx + 3];
|
|
14320
|
+
}
|
|
14321
|
+
const compressedRgb = deflateSync(rgb, { level: 6 });
|
|
14322
|
+
const compressedAlpha = deflateSync(alpha, { level: 6 });
|
|
14323
|
+
dict.set("/ColorSpace", PdfName.of("DeviceRGB"));
|
|
14324
|
+
dict.set("/Filter", PdfName.of("FlateDecode"));
|
|
14325
|
+
dict.set("/Length", PdfNumber.of(compressedRgb.length));
|
|
14326
|
+
const smaskDict = new PdfDict();
|
|
14327
|
+
smaskDict.set("/Type", PdfName.of("XObject"));
|
|
14328
|
+
smaskDict.set("/Subtype", PdfName.of("Image"));
|
|
14329
|
+
smaskDict.set("/Width", PdfNumber.of(decoded.width));
|
|
14330
|
+
smaskDict.set("/Height", PdfNumber.of(decoded.height));
|
|
14331
|
+
smaskDict.set("/BitsPerComponent", PdfNumber.of(8));
|
|
14332
|
+
smaskDict.set("/ColorSpace", PdfName.of("DeviceGray"));
|
|
14333
|
+
smaskDict.set("/Filter", PdfName.of("FlateDecode"));
|
|
14334
|
+
smaskDict.set("/Length", PdfNumber.of(compressedAlpha.length));
|
|
14335
|
+
const smaskStream = new PdfStream(smaskDict, compressedAlpha);
|
|
14336
|
+
const smaskRef = this.registry.register(smaskStream);
|
|
14337
|
+
dict.set("/SMask", smaskRef);
|
|
14338
|
+
const stream = new PdfStream(dict, compressedRgb);
|
|
14339
|
+
const ref = this.registry.register(stream);
|
|
14340
|
+
const w = decoded.width;
|
|
14341
|
+
const h = decoded.height;
|
|
14342
|
+
const imageRef = {
|
|
14343
|
+
name: resourceName,
|
|
14344
|
+
ref,
|
|
14345
|
+
width: w,
|
|
14346
|
+
height: h,
|
|
14347
|
+
scale(factor) {
|
|
14348
|
+
return {
|
|
14349
|
+
width: w * factor,
|
|
14350
|
+
height: h * factor
|
|
14351
|
+
};
|
|
14352
|
+
},
|
|
14353
|
+
scaleToFit(maxW, maxH) {
|
|
14354
|
+
const ratio = Math.min(maxW / w, maxH / h);
|
|
14355
|
+
return {
|
|
14356
|
+
width: w * ratio,
|
|
14357
|
+
height: h * ratio
|
|
14358
|
+
};
|
|
14359
|
+
}
|
|
14360
|
+
};
|
|
14361
|
+
this.embeddedImages.push(imageRef);
|
|
14362
|
+
return imageRef;
|
|
14363
|
+
}
|
|
14364
|
+
const compressedRgb = deflateSync(decoded.pixels, { level: 6 });
|
|
14365
|
+
dict.set("/ColorSpace", PdfName.of("DeviceRGB"));
|
|
14366
|
+
dict.set("/Filter", PdfName.of("FlateDecode"));
|
|
14367
|
+
dict.set("/Length", PdfNumber.of(compressedRgb.length));
|
|
14368
|
+
const stream = new PdfStream(dict, compressedRgb);
|
|
14369
|
+
const ref = this.registry.register(stream);
|
|
14370
|
+
const w = decoded.width;
|
|
14371
|
+
const h = decoded.height;
|
|
14372
|
+
const imageRef = {
|
|
14373
|
+
name: resourceName,
|
|
14374
|
+
ref,
|
|
14375
|
+
width: w,
|
|
14376
|
+
height: h,
|
|
14377
|
+
scale(factor) {
|
|
14378
|
+
return {
|
|
14379
|
+
width: w * factor,
|
|
14380
|
+
height: h * factor
|
|
14381
|
+
};
|
|
14382
|
+
},
|
|
14383
|
+
scaleToFit(maxW, maxH) {
|
|
14384
|
+
const ratio = Math.min(maxW / w, maxH / h);
|
|
14385
|
+
return {
|
|
14386
|
+
width: w * ratio,
|
|
14387
|
+
height: h * ratio
|
|
14388
|
+
};
|
|
14389
|
+
}
|
|
14390
|
+
};
|
|
14391
|
+
this.embeddedImages.push(imageRef);
|
|
14392
|
+
return imageRef;
|
|
14393
|
+
}
|
|
14394
|
+
/**
|
|
14395
|
+
* Embed a TIFF image.
|
|
14396
|
+
*
|
|
14397
|
+
* Decodes the TIFF image and creates a PDF image XObject with
|
|
14398
|
+
* FlateDecode compression. For multi-page TIFFs, a specific page
|
|
14399
|
+
* can be selected via options.
|
|
14400
|
+
*
|
|
14401
|
+
* @param tiffData Raw TIFF file bytes as a `Uint8Array` or `ArrayBuffer`.
|
|
14402
|
+
* @param options Options (e.g., `{ page: 0 }` for multi-page TIFFs).
|
|
14403
|
+
* @returns An {@link ImageRef}.
|
|
14404
|
+
*/
|
|
14405
|
+
embedTiff(tiffData, options) {
|
|
14406
|
+
const decoded = decodeTiff(tiffData instanceof ArrayBuffer ? new Uint8Array(tiffData) : tiffData, options?.page !== void 0 ? { page: options.page } : void 0);
|
|
14407
|
+
this.imageCounter++;
|
|
14408
|
+
const resourceName = `Im${this.imageCounter}`;
|
|
14409
|
+
const dict = new PdfDict();
|
|
14410
|
+
dict.set("/Type", PdfName.of("XObject"));
|
|
14411
|
+
dict.set("/Subtype", PdfName.of("Image"));
|
|
14412
|
+
dict.set("/Width", PdfNumber.of(decoded.width));
|
|
14413
|
+
dict.set("/Height", PdfNumber.of(decoded.height));
|
|
14414
|
+
dict.set("/BitsPerComponent", PdfNumber.of(8));
|
|
14415
|
+
if (decoded.channels === 4) {
|
|
14416
|
+
const pixelCount = decoded.width * decoded.height;
|
|
14417
|
+
const rgb = new Uint8Array(pixelCount * 3);
|
|
14418
|
+
const alpha = new Uint8Array(pixelCount);
|
|
14419
|
+
for (let i = 0; i < pixelCount; i++) {
|
|
14420
|
+
const srcIdx = i * 4;
|
|
14421
|
+
const dstIdx = i * 3;
|
|
14422
|
+
rgb[dstIdx] = decoded.pixels[srcIdx];
|
|
14423
|
+
rgb[dstIdx + 1] = decoded.pixels[srcIdx + 1];
|
|
14424
|
+
rgb[dstIdx + 2] = decoded.pixels[srcIdx + 2];
|
|
14425
|
+
alpha[i] = decoded.pixels[srcIdx + 3];
|
|
14426
|
+
}
|
|
14427
|
+
const compressedRgb = deflateSync(rgb, { level: 6 });
|
|
14428
|
+
const compressedAlpha = deflateSync(alpha, { level: 6 });
|
|
14429
|
+
dict.set("/ColorSpace", PdfName.of("DeviceRGB"));
|
|
14430
|
+
dict.set("/Filter", PdfName.of("FlateDecode"));
|
|
14431
|
+
dict.set("/Length", PdfNumber.of(compressedRgb.length));
|
|
14432
|
+
const smaskDict = new PdfDict();
|
|
14433
|
+
smaskDict.set("/Type", PdfName.of("XObject"));
|
|
14434
|
+
smaskDict.set("/Subtype", PdfName.of("Image"));
|
|
14435
|
+
smaskDict.set("/Width", PdfNumber.of(decoded.width));
|
|
14436
|
+
smaskDict.set("/Height", PdfNumber.of(decoded.height));
|
|
14437
|
+
smaskDict.set("/BitsPerComponent", PdfNumber.of(8));
|
|
14438
|
+
smaskDict.set("/ColorSpace", PdfName.of("DeviceGray"));
|
|
14439
|
+
smaskDict.set("/Filter", PdfName.of("FlateDecode"));
|
|
14440
|
+
smaskDict.set("/Length", PdfNumber.of(compressedAlpha.length));
|
|
14441
|
+
const smaskStream = new PdfStream(smaskDict, compressedAlpha);
|
|
14442
|
+
const smaskRef = this.registry.register(smaskStream);
|
|
14443
|
+
dict.set("/SMask", smaskRef);
|
|
14444
|
+
const stream = new PdfStream(dict, compressedRgb);
|
|
14445
|
+
const ref = this.registry.register(stream);
|
|
14446
|
+
const w = decoded.width;
|
|
14447
|
+
const h = decoded.height;
|
|
14448
|
+
const imageRef = {
|
|
14449
|
+
name: resourceName,
|
|
14450
|
+
ref,
|
|
14451
|
+
width: w,
|
|
14452
|
+
height: h,
|
|
14453
|
+
scale(factor) {
|
|
14454
|
+
return {
|
|
14455
|
+
width: w * factor,
|
|
14456
|
+
height: h * factor
|
|
14457
|
+
};
|
|
14458
|
+
},
|
|
14459
|
+
scaleToFit(maxW, maxH) {
|
|
14460
|
+
const ratio = Math.min(maxW / w, maxH / h);
|
|
14461
|
+
return {
|
|
14462
|
+
width: w * ratio,
|
|
14463
|
+
height: h * ratio
|
|
14464
|
+
};
|
|
14465
|
+
}
|
|
14466
|
+
};
|
|
14467
|
+
this.embeddedImages.push(imageRef);
|
|
14468
|
+
return imageRef;
|
|
14469
|
+
}
|
|
14470
|
+
const colorSpace = decoded.channels === 1 ? "DeviceGray" : "DeviceRGB";
|
|
14471
|
+
const compressed = deflateSync(decoded.pixels, { level: 6 });
|
|
14472
|
+
dict.set("/ColorSpace", PdfName.of(colorSpace));
|
|
14473
|
+
dict.set("/Filter", PdfName.of("FlateDecode"));
|
|
14474
|
+
dict.set("/Length", PdfNumber.of(compressed.length));
|
|
14475
|
+
const stream = new PdfStream(dict, compressed);
|
|
14476
|
+
const ref = this.registry.register(stream);
|
|
14477
|
+
const w = decoded.width;
|
|
14478
|
+
const h = decoded.height;
|
|
14479
|
+
const imageRef = {
|
|
14480
|
+
name: resourceName,
|
|
14481
|
+
ref,
|
|
14482
|
+
width: w,
|
|
14483
|
+
height: h,
|
|
14484
|
+
scale(factor) {
|
|
14485
|
+
return {
|
|
14486
|
+
width: w * factor,
|
|
14487
|
+
height: h * factor
|
|
14488
|
+
};
|
|
14489
|
+
},
|
|
14490
|
+
scaleToFit(maxW, maxH) {
|
|
14491
|
+
const ratio = Math.min(maxW / w, maxH / h);
|
|
14492
|
+
return {
|
|
14493
|
+
width: w * ratio,
|
|
14494
|
+
height: h * ratio
|
|
14495
|
+
};
|
|
14496
|
+
}
|
|
14497
|
+
};
|
|
14498
|
+
this.embeddedImages.push(imageRef);
|
|
14499
|
+
return imageRef;
|
|
14500
|
+
}
|
|
14501
|
+
/**
|
|
12810
14502
|
* Embed an image, auto-detecting the format from file headers.
|
|
12811
14503
|
*
|
|
12812
|
-
* Inspects the first bytes to determine
|
|
12813
|
-
* then delegates to
|
|
14504
|
+
* Inspects the first bytes to determine the image format (PNG, JPEG,
|
|
14505
|
+
* WebP, or TIFF), then delegates to the appropriate embedding method.
|
|
14506
|
+
*
|
|
14507
|
+
* Supported formats:
|
|
14508
|
+
* - **PNG**: `89 50 4E 47` — embedded via {@link embedPng}
|
|
14509
|
+
* - **JPEG**: `FF D8 FF` — embedded via {@link embedJpeg}
|
|
14510
|
+
* - **WebP**: `52 49 46 46` + `57 45 42 50` — embedded via {@link embedWebP}
|
|
14511
|
+
* - **TIFF LE**: `49 49 2A 00` — embedded via {@link embedTiff}
|
|
14512
|
+
* - **TIFF BE**: `4D 4D 00 2A` — embedded via {@link embedTiff}
|
|
12814
14513
|
*
|
|
12815
|
-
* @param imageData Raw image file bytes (PNG or
|
|
14514
|
+
* @param imageData Raw image file bytes (PNG, JPEG, WebP, or TIFF).
|
|
12816
14515
|
* @returns An {@link ImageRef} to pass to `page.drawImage()`.
|
|
12817
14516
|
* @throws If the image format cannot be detected.
|
|
12818
14517
|
*
|
|
@@ -12826,9 +14525,13 @@ var PdfDocument = class PdfDocument {
|
|
|
12826
14525
|
async embedImage(imageData) {
|
|
12827
14526
|
const data = imageData instanceof ArrayBuffer ? new Uint8Array(imageData) : imageData;
|
|
12828
14527
|
if (data.length < 4) throw new Error("Image data too short to detect format");
|
|
12829
|
-
|
|
12830
|
-
|
|
12831
|
-
|
|
14528
|
+
switch (detectImageFormat(data)) {
|
|
14529
|
+
case "png": return this.embedPng(data);
|
|
14530
|
+
case "jpeg": return this.embedJpeg(data);
|
|
14531
|
+
case "webp": return this.embedWebP(data);
|
|
14532
|
+
case "tiff": return this.embedTiff(data);
|
|
14533
|
+
default: throw new Error(`Unsupported image format. Expected PNG, JPEG, WebP, or TIFF, got ${data.subarray(0, 4).toHex().match(/.{2}/g)?.join(" ") ?? ""}.`);
|
|
14534
|
+
}
|
|
12832
14535
|
}
|
|
12833
14536
|
/**
|
|
12834
14537
|
* Embed pages from another PDF as Form XObjects.
|
|
@@ -13607,6 +15310,61 @@ var PdfDocument = class PdfDocument {
|
|
|
13607
15310
|
catalogObj.set("/Names", namesDict);
|
|
13608
15311
|
}
|
|
13609
15312
|
}
|
|
15313
|
+
const actionState = getDocumentActionState(this);
|
|
15314
|
+
if (actionState) {
|
|
15315
|
+
if (actionState.openAction !== void 0) {
|
|
15316
|
+
const openActionDict = new PdfDict();
|
|
15317
|
+
openActionDict.set("/Type", PdfName.of("Action"));
|
|
15318
|
+
openActionDict.set("/S", PdfName.of("JavaScript"));
|
|
15319
|
+
openActionDict.set("/JS", PdfString.literal(actionState.openAction));
|
|
15320
|
+
const openActionRef = this.registry.register(openActionDict);
|
|
15321
|
+
catalogObj.set("/OpenAction", openActionRef);
|
|
15322
|
+
}
|
|
15323
|
+
if (actionState.closeAction !== void 0 || actionState.beforePrint !== void 0 || actionState.afterPrint !== void 0 || actionState.beforeSave !== void 0 || actionState.afterSave !== void 0) {
|
|
15324
|
+
const aaDict = new PdfDict();
|
|
15325
|
+
if (actionState.closeAction !== void 0) {
|
|
15326
|
+
const d = new PdfDict();
|
|
15327
|
+
d.set("/Type", PdfName.of("Action"));
|
|
15328
|
+
d.set("/S", PdfName.of("JavaScript"));
|
|
15329
|
+
d.set("/JS", PdfString.literal(actionState.closeAction));
|
|
15330
|
+
const ref = this.registry.register(d);
|
|
15331
|
+
aaDict.set("/WC", ref);
|
|
15332
|
+
}
|
|
15333
|
+
if (actionState.beforePrint !== void 0) {
|
|
15334
|
+
const d = new PdfDict();
|
|
15335
|
+
d.set("/Type", PdfName.of("Action"));
|
|
15336
|
+
d.set("/S", PdfName.of("JavaScript"));
|
|
15337
|
+
d.set("/JS", PdfString.literal(actionState.beforePrint));
|
|
15338
|
+
const ref = this.registry.register(d);
|
|
15339
|
+
aaDict.set("/WP", ref);
|
|
15340
|
+
}
|
|
15341
|
+
if (actionState.afterPrint !== void 0) {
|
|
15342
|
+
const d = new PdfDict();
|
|
15343
|
+
d.set("/Type", PdfName.of("Action"));
|
|
15344
|
+
d.set("/S", PdfName.of("JavaScript"));
|
|
15345
|
+
d.set("/JS", PdfString.literal(actionState.afterPrint));
|
|
15346
|
+
const ref = this.registry.register(d);
|
|
15347
|
+
aaDict.set("/DP", ref);
|
|
15348
|
+
}
|
|
15349
|
+
if (actionState.beforeSave !== void 0) {
|
|
15350
|
+
const d = new PdfDict();
|
|
15351
|
+
d.set("/Type", PdfName.of("Action"));
|
|
15352
|
+
d.set("/S", PdfName.of("JavaScript"));
|
|
15353
|
+
d.set("/JS", PdfString.literal(actionState.beforeSave));
|
|
15354
|
+
const ref = this.registry.register(d);
|
|
15355
|
+
aaDict.set("/WS", ref);
|
|
15356
|
+
}
|
|
15357
|
+
if (actionState.afterSave !== void 0) {
|
|
15358
|
+
const d = new PdfDict();
|
|
15359
|
+
d.set("/Type", PdfName.of("Action"));
|
|
15360
|
+
d.set("/S", PdfName.of("JavaScript"));
|
|
15361
|
+
d.set("/JS", PdfString.literal(actionState.afterSave));
|
|
15362
|
+
const ref = this.registry.register(d);
|
|
15363
|
+
aaDict.set("/DS", ref);
|
|
15364
|
+
}
|
|
15365
|
+
catalogObj.set("/AA", aaDict);
|
|
15366
|
+
}
|
|
15367
|
+
}
|
|
13610
15368
|
}
|
|
13611
15369
|
if (this.originalBytes !== void 0) {
|
|
13612
15370
|
const rootRefs = [structure.catalogRef, structure.infoRef];
|
|
@@ -13681,5 +15439,5 @@ function createPdf() {
|
|
|
13681
15439
|
}
|
|
13682
15440
|
|
|
13683
15441
|
//#endregion
|
|
13684
|
-
export {
|
|
13685
|
-
//# sourceMappingURL=pdfDocument-
|
|
15442
|
+
export { PdfEncryptionHandler as $, parseDerTlv as A, EmbeddedFont as At, isAccessible as B, encodePrintableString as C, isWebP as Ct, encodeUtf8String as D, getSupportedFormats as Dt, encodeUTCTime as E, getImageFormatName as Et, prepareForSigning as F, buildViewerPreferencesDict as G, PdfStructureElement as H, copyPages as I, createXmpStream as J, parseViewerPreferences as K, mergePdfs as L, computeSignatureHash as M, PdfStreamWriter as Mt, embedSignature as N, PdfWriter as Nt, extractIssuerAndSerial as O, isOpenTypeCFF as Ot, findSignatures as P, serializePdf as Pt, loadPdf as Q, splitPdf as R, encodeOctetString as S, decodeWebP as St, encodeSet as T, detectImageFormat as Tt, PdfStructureTree as U, summarizeIssues as V, PdfViewerPreferences as W, PdfOutlineItem as X, parseXmpMetadata as Y, PdfOutlineTree as Z, detectNamedCurve as _, decodeTiffAll as _t, addWatermark as a, sha512 as at, encodeLength as b, isTiff as bt, buildEmbeddedFilesNameTree as c, aesDecryptCBC as ct, verifySignatures as d, md5 as dt, computeFileEncryptionKey as et, getSignatures as f, base64Decode as ft, detectKeyAlgorithm as g, decodeTiff as gt, decodeOidBytes as h, formatHexContext as ht, embedPageAsFormXObject as i, sha384 as it, toBuffer as j, extractMetrics as jt, getSubtle as k, isTrueType as kt, getAttachments as l, aesEncryptCBC as lt, buildPkcs7Signature as m, PdfParseError as mt, StandardFonts as n, verifyUserPassword as nt, addWatermarkToPage as o, decodePermissions as ot, signPdf as p, base64Encode as pt, buildXmpMetadata as q, createPdf as r, sha256 as rt, attachFile as s, encodePermissions as st, PdfDocument as t, verifyOwnerPassword as tt, verifySignature as u, rc4 as ut, encodeContextTag as v, decodeTiffPage as vt, encodeSequence as w, isWebPLossless as wt, encodeOID as x, parseTiffIfd as xt, encodeInteger as y, getTiffPageCount as yt, checkAccessibility as z };
|
|
15443
|
+
//# sourceMappingURL=pdfDocument-B0_XwS4X.mjs.map
|