@cj-tech-master/excelts 9.1.0 → 9.2.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 +16 -1
- package/dist/browser/modules/archive/compression/crc32.js +1 -1
- package/dist/browser/modules/archive/crypto/aes.d.ts +0 -8
- package/dist/browser/modules/archive/crypto/aes.js +1 -20
- package/dist/browser/modules/archive/crypto/index.d.ts +2 -1
- package/dist/browser/modules/archive/crypto/index.js +3 -1
- package/dist/browser/modules/csv/parse/row-processor.d.ts +1 -1
- package/dist/browser/modules/csv/worker/worker-script.generated.js +1 -1
- package/dist/browser/modules/excel/utils/cell-matrix.js +1 -0
- package/dist/browser/modules/excel/utils/encryptor.browser.d.ts +4 -5
- package/dist/browser/modules/excel/utils/encryptor.browser.js +7 -12
- package/dist/browser/modules/excel/utils/encryptor.d.ts +1 -1
- package/dist/browser/modules/excel/utils/encryptor.js +4 -7
- package/dist/browser/modules/pdf/builder/document-builder.d.ts +517 -0
- package/dist/browser/modules/pdf/builder/document-builder.js +1493 -0
- package/dist/browser/modules/pdf/builder/form-appearance.d.ts +56 -0
- package/dist/browser/modules/pdf/builder/form-appearance.js +140 -0
- package/dist/browser/modules/pdf/builder/image-utils.d.ts +39 -0
- package/dist/browser/modules/pdf/builder/image-utils.js +129 -0
- package/dist/browser/modules/pdf/builder/pdf-editor.d.ts +230 -0
- package/dist/browser/modules/pdf/builder/pdf-editor.js +1574 -0
- package/dist/browser/modules/pdf/builder/resource-merger.d.ts +41 -0
- package/dist/browser/modules/pdf/builder/resource-merger.js +258 -0
- package/dist/browser/modules/pdf/core/digital-signature.d.ts +109 -0
- package/dist/browser/modules/pdf/core/digital-signature.js +659 -0
- package/dist/browser/modules/pdf/core/encryption.js +8 -7
- package/dist/browser/modules/pdf/core/pdf-object.d.ts +11 -0
- package/dist/browser/modules/pdf/core/pdf-object.js +38 -0
- package/dist/browser/modules/pdf/core/pdf-stream.d.ts +32 -0
- package/dist/browser/modules/pdf/core/pdf-stream.js +66 -0
- package/dist/browser/modules/pdf/core/pdf-writer.d.ts +55 -1
- package/dist/browser/modules/pdf/core/pdf-writer.js +271 -6
- package/dist/browser/modules/pdf/core/pdfa.d.ts +62 -0
- package/dist/browser/modules/pdf/core/pdfa.js +261 -0
- package/dist/browser/modules/pdf/index.d.ts +11 -0
- package/dist/browser/modules/pdf/index.js +9 -0
- package/dist/browser/modules/pdf/reader/bookmark-extractor.d.ts +35 -0
- package/dist/browser/modules/pdf/reader/bookmark-extractor.js +324 -0
- package/dist/browser/modules/pdf/reader/pdf-decrypt.js +6 -5
- package/dist/browser/modules/pdf/reader/pdf-reader.d.ts +17 -0
- package/dist/browser/modules/pdf/reader/pdf-reader.js +26 -2
- package/dist/browser/modules/pdf/reader/table-extractor.d.ts +69 -0
- package/dist/browser/modules/pdf/reader/table-extractor.js +365 -0
- package/dist/browser/modules/pdf/render/layout-engine.d.ts +21 -1
- package/dist/browser/modules/pdf/render/layout-engine.js +112 -5
- package/dist/browser/modules/pdf/render/page-renderer.d.ts +2 -9
- package/dist/browser/modules/pdf/render/page-renderer.js +62 -103
- package/dist/browser/modules/pdf/render/pdf-exporter.js +2 -61
- package/dist/browser/modules/pdf/render/style-converter.d.ts +4 -0
- package/dist/browser/modules/pdf/render/style-converter.js +1 -1
- package/dist/browser/modules/pdf/types.d.ts +14 -1
- package/dist/browser/modules/stream/browser/readable.js +8 -2
- package/dist/browser/utils/crypto.browser.d.ts +64 -0
- package/dist/browser/{modules/pdf/core/crypto.js → utils/crypto.browser.js} +91 -101
- package/dist/browser/utils/crypto.d.ts +97 -0
- package/dist/browser/utils/crypto.js +209 -0
- package/dist/cjs/modules/archive/compression/crc32.js +1 -1
- package/dist/cjs/modules/archive/crypto/aes.js +2 -23
- package/dist/cjs/modules/archive/crypto/index.js +3 -1
- package/dist/cjs/modules/csv/worker/worker-script.generated.js +1 -1
- package/dist/cjs/modules/excel/utils/cell-matrix.js +1 -0
- package/dist/cjs/modules/excel/utils/encryptor.browser.js +7 -12
- package/dist/cjs/modules/excel/utils/encryptor.js +4 -10
- package/dist/cjs/modules/pdf/builder/document-builder.js +1532 -0
- package/dist/cjs/modules/pdf/builder/form-appearance.js +145 -0
- package/dist/cjs/modules/pdf/builder/image-utils.js +135 -0
- package/dist/cjs/modules/pdf/builder/pdf-editor.js +1612 -0
- package/dist/cjs/modules/pdf/builder/resource-merger.js +263 -0
- package/dist/cjs/modules/pdf/core/digital-signature.js +667 -0
- package/dist/cjs/modules/pdf/core/encryption.js +8 -7
- package/dist/cjs/modules/pdf/core/pdf-object.js +38 -0
- package/dist/cjs/modules/pdf/core/pdf-stream.js +66 -0
- package/dist/cjs/modules/pdf/core/pdf-writer.js +272 -6
- package/dist/cjs/modules/pdf/core/pdfa.js +266 -0
- package/dist/cjs/modules/pdf/index.js +19 -1
- package/dist/cjs/modules/pdf/reader/bookmark-extractor.js +327 -0
- package/dist/cjs/modules/pdf/reader/pdf-decrypt.js +6 -5
- package/dist/cjs/modules/pdf/reader/pdf-reader.js +26 -2
- package/dist/cjs/modules/pdf/reader/table-extractor.js +368 -0
- package/dist/cjs/modules/pdf/render/layout-engine.js +113 -4
- package/dist/cjs/modules/pdf/render/page-renderer.js +63 -105
- package/dist/cjs/modules/pdf/render/pdf-exporter.js +3 -62
- package/dist/cjs/modules/pdf/render/style-converter.js +1 -0
- package/dist/cjs/modules/stream/browser/readable.js +8 -2
- package/dist/cjs/{modules/pdf/core/crypto.js → utils/crypto.browser.js} +95 -102
- package/dist/cjs/utils/crypto.js +228 -0
- package/dist/esm/modules/archive/compression/crc32.js +1 -1
- package/dist/esm/modules/archive/crypto/aes.js +1 -20
- package/dist/esm/modules/archive/crypto/index.js +3 -1
- package/dist/esm/modules/csv/worker/worker-script.generated.js +1 -1
- package/dist/esm/modules/excel/utils/cell-matrix.js +1 -0
- package/dist/esm/modules/excel/utils/encryptor.browser.js +7 -12
- package/dist/esm/modules/excel/utils/encryptor.js +4 -7
- package/dist/esm/modules/pdf/builder/document-builder.js +1493 -0
- package/dist/esm/modules/pdf/builder/form-appearance.js +140 -0
- package/dist/esm/modules/pdf/builder/image-utils.js +129 -0
- package/dist/esm/modules/pdf/builder/pdf-editor.js +1574 -0
- package/dist/esm/modules/pdf/builder/resource-merger.js +258 -0
- package/dist/esm/modules/pdf/core/digital-signature.js +659 -0
- package/dist/esm/modules/pdf/core/encryption.js +8 -7
- package/dist/esm/modules/pdf/core/pdf-object.js +38 -0
- package/dist/esm/modules/pdf/core/pdf-stream.js +66 -0
- package/dist/esm/modules/pdf/core/pdf-writer.js +271 -6
- package/dist/esm/modules/pdf/core/pdfa.js +261 -0
- package/dist/esm/modules/pdf/index.js +9 -0
- package/dist/esm/modules/pdf/reader/bookmark-extractor.js +324 -0
- package/dist/esm/modules/pdf/reader/pdf-decrypt.js +6 -5
- package/dist/esm/modules/pdf/reader/pdf-reader.js +26 -2
- package/dist/esm/modules/pdf/reader/table-extractor.js +365 -0
- package/dist/esm/modules/pdf/render/layout-engine.js +112 -5
- package/dist/esm/modules/pdf/render/page-renderer.js +62 -103
- package/dist/esm/modules/pdf/render/pdf-exporter.js +2 -61
- package/dist/esm/modules/pdf/render/style-converter.js +1 -1
- package/dist/esm/modules/stream/browser/readable.js +8 -2
- package/dist/esm/{modules/pdf/core/crypto.js → utils/crypto.browser.js} +91 -101
- package/dist/esm/utils/crypto.js +209 -0
- package/dist/iife/excelts.iife.js +1248 -1074
- package/dist/iife/excelts.iife.js.map +1 -1
- package/dist/iife/excelts.iife.min.js +53 -54
- package/dist/types/modules/archive/crypto/aes.d.ts +0 -8
- package/dist/types/modules/archive/crypto/index.d.ts +2 -1
- package/dist/types/modules/csv/parse/row-processor.d.ts +1 -1
- package/dist/types/modules/excel/utils/encryptor.browser.d.ts +4 -5
- package/dist/types/modules/excel/utils/encryptor.d.ts +1 -1
- package/dist/types/modules/pdf/builder/document-builder.d.ts +517 -0
- package/dist/types/modules/pdf/builder/form-appearance.d.ts +56 -0
- package/dist/types/modules/pdf/builder/image-utils.d.ts +39 -0
- package/dist/types/modules/pdf/builder/pdf-editor.d.ts +230 -0
- package/dist/types/modules/pdf/builder/resource-merger.d.ts +41 -0
- package/dist/types/modules/pdf/core/digital-signature.d.ts +109 -0
- package/dist/types/modules/pdf/core/pdf-object.d.ts +11 -0
- package/dist/types/modules/pdf/core/pdf-stream.d.ts +32 -0
- package/dist/types/modules/pdf/core/pdf-writer.d.ts +55 -1
- package/dist/types/modules/pdf/core/pdfa.d.ts +62 -0
- package/dist/types/modules/pdf/index.d.ts +11 -0
- package/dist/types/modules/pdf/reader/bookmark-extractor.d.ts +35 -0
- package/dist/types/modules/pdf/reader/pdf-reader.d.ts +17 -0
- package/dist/types/modules/pdf/reader/table-extractor.d.ts +69 -0
- package/dist/types/modules/pdf/render/layout-engine.d.ts +21 -1
- package/dist/types/modules/pdf/render/page-renderer.d.ts +2 -9
- package/dist/types/modules/pdf/render/style-converter.d.ts +4 -0
- package/dist/types/modules/pdf/types.d.ts +14 -1
- package/dist/types/utils/crypto.browser.d.ts +64 -0
- package/dist/types/utils/crypto.d.ts +97 -0
- package/package.json +110 -111
- package/dist/browser/modules/pdf/core/crypto.d.ts +0 -65
- package/dist/types/modules/pdf/core/crypto.d.ts +0 -65
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* @cj-tech-master/excelts v9.
|
|
2
|
+
* @cj-tech-master/excelts v9.2.0
|
|
3
3
|
* Zero-dependency TypeScript toolkit — Excel (XLSX), PDF, CSV, Markdown, XML, ZIP/TAR, and streaming.
|
|
4
4
|
* (c) 2026 cjnoname
|
|
5
5
|
* Released under the MIT License
|
|
6
6
|
*/
|
|
7
7
|
var ExcelTS = (function(exports) {
|
|
8
8
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
9
|
+
//#region \0rolldown/runtime.js
|
|
10
|
+
var __esmMin = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
11
|
+
//#endregion
|
|
9
12
|
//#region src/utils/errors.ts
|
|
10
13
|
/**
|
|
11
14
|
* Base class for all library errors.
|
|
@@ -4805,7 +4808,660 @@ var ExcelTS = (function(exports) {
|
|
|
4805
4808
|
return cell.text;
|
|
4806
4809
|
}
|
|
4807
4810
|
//#endregion
|
|
4811
|
+
//#region src/utils/crypto.browser.ts
|
|
4812
|
+
function gf2(a) {
|
|
4813
|
+
return a < 128 ? a << 1 : a << 1 ^ 283;
|
|
4814
|
+
}
|
|
4815
|
+
function aesKeyExpansion(key) {
|
|
4816
|
+
const nk = key.length / 4;
|
|
4817
|
+
const nr = nk + 6;
|
|
4818
|
+
const w = [];
|
|
4819
|
+
for (let i = 0; i < nk; i++) w.push(new Uint8Array([
|
|
4820
|
+
key[4 * i],
|
|
4821
|
+
key[4 * i + 1],
|
|
4822
|
+
key[4 * i + 2],
|
|
4823
|
+
key[4 * i + 3]
|
|
4824
|
+
]));
|
|
4825
|
+
for (let i = nk; i < 4 * (nr + 1); i++) {
|
|
4826
|
+
const temp = new Uint8Array(w[i - 1]);
|
|
4827
|
+
if (i % nk === 0) {
|
|
4828
|
+
const t0 = temp[0];
|
|
4829
|
+
temp[0] = SBOX[temp[1]] ^ RCON[i / nk - 1];
|
|
4830
|
+
temp[1] = SBOX[temp[2]];
|
|
4831
|
+
temp[2] = SBOX[temp[3]];
|
|
4832
|
+
temp[3] = SBOX[t0];
|
|
4833
|
+
} else if (nk > 6 && i % nk === 4) {
|
|
4834
|
+
temp[0] = SBOX[temp[0]];
|
|
4835
|
+
temp[1] = SBOX[temp[1]];
|
|
4836
|
+
temp[2] = SBOX[temp[2]];
|
|
4837
|
+
temp[3] = SBOX[temp[3]];
|
|
4838
|
+
}
|
|
4839
|
+
const word = new Uint8Array(4);
|
|
4840
|
+
for (let j = 0; j < 4; j++) word[j] = w[i - nk][j] ^ temp[j];
|
|
4841
|
+
w.push(word);
|
|
4842
|
+
}
|
|
4843
|
+
return w;
|
|
4844
|
+
}
|
|
4845
|
+
function aesEncryptBlock(block, roundKeys) {
|
|
4846
|
+
const nr = roundKeys.length / 4 - 1;
|
|
4847
|
+
const state = new Uint8Array(16);
|
|
4848
|
+
state.set(block);
|
|
4849
|
+
for (let c = 0; c < 4; c++) for (let r = 0; r < 4; r++) state[4 * c + r] ^= roundKeys[c][r];
|
|
4850
|
+
for (let round = 1; round < nr; round++) {
|
|
4851
|
+
for (let i = 0; i < 16; i++) state[i] = SBOX[state[i]];
|
|
4852
|
+
let tmp;
|
|
4853
|
+
tmp = state[1];
|
|
4854
|
+
state[1] = state[5];
|
|
4855
|
+
state[5] = state[9];
|
|
4856
|
+
state[9] = state[13];
|
|
4857
|
+
state[13] = tmp;
|
|
4858
|
+
tmp = state[2];
|
|
4859
|
+
state[2] = state[10];
|
|
4860
|
+
state[10] = tmp;
|
|
4861
|
+
tmp = state[6];
|
|
4862
|
+
state[6] = state[14];
|
|
4863
|
+
state[14] = tmp;
|
|
4864
|
+
tmp = state[15];
|
|
4865
|
+
state[15] = state[11];
|
|
4866
|
+
state[11] = state[7];
|
|
4867
|
+
state[7] = state[3];
|
|
4868
|
+
state[3] = tmp;
|
|
4869
|
+
for (let c = 0; c < 4; c++) {
|
|
4870
|
+
const s0 = state[4 * c];
|
|
4871
|
+
const s1 = state[4 * c + 1];
|
|
4872
|
+
const s2 = state[4 * c + 2];
|
|
4873
|
+
const s3 = state[4 * c + 3];
|
|
4874
|
+
state[4 * c] = gf2(s0) ^ gf2(s1) ^ s1 ^ s2 ^ s3;
|
|
4875
|
+
state[4 * c + 1] = s0 ^ gf2(s1) ^ gf2(s2) ^ s2 ^ s3;
|
|
4876
|
+
state[4 * c + 2] = s0 ^ s1 ^ gf2(s2) ^ gf2(s3) ^ s3;
|
|
4877
|
+
state[4 * c + 3] = gf2(s0) ^ s0 ^ s1 ^ s2 ^ gf2(s3);
|
|
4878
|
+
}
|
|
4879
|
+
const keyOffset = round * 4;
|
|
4880
|
+
for (let c = 0; c < 4; c++) for (let r = 0; r < 4; r++) state[4 * c + r] ^= roundKeys[keyOffset + c][r];
|
|
4881
|
+
}
|
|
4882
|
+
for (let i = 0; i < 16; i++) state[i] = SBOX[state[i]];
|
|
4883
|
+
let tmp;
|
|
4884
|
+
tmp = state[1];
|
|
4885
|
+
state[1] = state[5];
|
|
4886
|
+
state[5] = state[9];
|
|
4887
|
+
state[9] = state[13];
|
|
4888
|
+
state[13] = tmp;
|
|
4889
|
+
tmp = state[2];
|
|
4890
|
+
state[2] = state[10];
|
|
4891
|
+
state[10] = tmp;
|
|
4892
|
+
tmp = state[6];
|
|
4893
|
+
state[6] = state[14];
|
|
4894
|
+
state[14] = tmp;
|
|
4895
|
+
tmp = state[15];
|
|
4896
|
+
state[15] = state[11];
|
|
4897
|
+
state[11] = state[7];
|
|
4898
|
+
state[7] = state[3];
|
|
4899
|
+
state[3] = tmp;
|
|
4900
|
+
for (let c = 0; c < 4; c++) for (let r = 0; r < 4; r++) state[4 * c + r] ^= roundKeys[nr * 4 + c][r];
|
|
4901
|
+
return state;
|
|
4902
|
+
}
|
|
4903
|
+
function aesCbcEncrypt(plaintext, key, iv) {
|
|
4904
|
+
const padLen = 16 - plaintext.length % 16;
|
|
4905
|
+
const padded = new Uint8Array(plaintext.length + padLen);
|
|
4906
|
+
padded.set(plaintext);
|
|
4907
|
+
for (let i = plaintext.length; i < padded.length; i++) padded[i] = padLen;
|
|
4908
|
+
const roundKeys = aesKeyExpansion(key);
|
|
4909
|
+
const numBlocks = padded.length / 16;
|
|
4910
|
+
const output = new Uint8Array(padded.length);
|
|
4911
|
+
let prevBlock = iv;
|
|
4912
|
+
for (let b = 0; b < numBlocks; b++) {
|
|
4913
|
+
const block = new Uint8Array(16);
|
|
4914
|
+
for (let i = 0; i < 16; i++) block[i] = padded[b * 16 + i] ^ prevBlock[i];
|
|
4915
|
+
const encrypted = aesEncryptBlock(block, roundKeys);
|
|
4916
|
+
output.set(encrypted, b * 16);
|
|
4917
|
+
prevBlock = encrypted;
|
|
4918
|
+
}
|
|
4919
|
+
return output;
|
|
4920
|
+
}
|
|
4921
|
+
function aesCbcEncryptRaw(plaintext, key, iv) {
|
|
4922
|
+
if (plaintext.length % 16 !== 0) throw new Error("aesCbcEncryptRaw: plaintext length must be a multiple of 16");
|
|
4923
|
+
const roundKeys = aesKeyExpansion(key);
|
|
4924
|
+
const numBlocks = plaintext.length / 16;
|
|
4925
|
+
const output = new Uint8Array(plaintext.length);
|
|
4926
|
+
let prevBlock = iv;
|
|
4927
|
+
for (let b = 0; b < numBlocks; b++) {
|
|
4928
|
+
const block = new Uint8Array(16);
|
|
4929
|
+
for (let i = 0; i < 16; i++) block[i] = plaintext[b * 16 + i] ^ prevBlock[i];
|
|
4930
|
+
const encrypted = aesEncryptBlock(block, roundKeys);
|
|
4931
|
+
output.set(encrypted, b * 16);
|
|
4932
|
+
prevBlock = encrypted;
|
|
4933
|
+
}
|
|
4934
|
+
return output;
|
|
4935
|
+
}
|
|
4936
|
+
function aesEcbEncrypt(block, key) {
|
|
4937
|
+
return aesEncryptBlock(block, aesKeyExpansion(key));
|
|
4938
|
+
}
|
|
4939
|
+
function rotr32(x, n) {
|
|
4940
|
+
return (x >>> n | x << 32 - n) >>> 0;
|
|
4941
|
+
}
|
|
4942
|
+
function sha256(input) {
|
|
4943
|
+
const msgLen = input.length;
|
|
4944
|
+
const paddedLen = Math.ceil((msgLen + 9) / 64) * 64;
|
|
4945
|
+
const padded = new Uint8Array(paddedLen);
|
|
4946
|
+
padded.set(input);
|
|
4947
|
+
padded[msgLen] = 128;
|
|
4948
|
+
const bitLen = msgLen * 8;
|
|
4949
|
+
const view = new DataView(padded.buffer, padded.byteOffset, padded.byteLength);
|
|
4950
|
+
view.setUint32(paddedLen - 8, 0, false);
|
|
4951
|
+
view.setUint32(paddedLen - 4, bitLen, false);
|
|
4952
|
+
let h0 = SHA256_H[0];
|
|
4953
|
+
let h1 = SHA256_H[1];
|
|
4954
|
+
let h2 = SHA256_H[2];
|
|
4955
|
+
let h3 = SHA256_H[3];
|
|
4956
|
+
let h4 = SHA256_H[4];
|
|
4957
|
+
let h5 = SHA256_H[5];
|
|
4958
|
+
let h6 = SHA256_H[6];
|
|
4959
|
+
let h7 = SHA256_H[7];
|
|
4960
|
+
const w = new Uint32Array(64);
|
|
4961
|
+
for (let offset = 0; offset < paddedLen; offset += 64) {
|
|
4962
|
+
for (let i = 0; i < 16; i++) w[i] = view.getUint32(offset + i * 4, false);
|
|
4963
|
+
for (let i = 16; i < 64; i++) {
|
|
4964
|
+
const s0 = rotr32(w[i - 15], 7) ^ rotr32(w[i - 15], 18) ^ w[i - 15] >>> 3;
|
|
4965
|
+
const s1 = rotr32(w[i - 2], 17) ^ rotr32(w[i - 2], 19) ^ w[i - 2] >>> 10;
|
|
4966
|
+
w[i] = w[i - 16] + s0 + w[i - 7] + s1 >>> 0;
|
|
4967
|
+
}
|
|
4968
|
+
let a = h0;
|
|
4969
|
+
let b = h1;
|
|
4970
|
+
let c = h2;
|
|
4971
|
+
let d = h3;
|
|
4972
|
+
let e = h4;
|
|
4973
|
+
let f = h5;
|
|
4974
|
+
let g = h6;
|
|
4975
|
+
let h = h7;
|
|
4976
|
+
for (let i = 0; i < 64; i++) {
|
|
4977
|
+
const S1 = rotr32(e, 6) ^ rotr32(e, 11) ^ rotr32(e, 25);
|
|
4978
|
+
const ch = e & f ^ ~e & g;
|
|
4979
|
+
const temp1 = h + S1 + ch + SHA256_K[i] + w[i] >>> 0;
|
|
4980
|
+
const temp2 = (rotr32(a, 2) ^ rotr32(a, 13) ^ rotr32(a, 22)) + (a & b ^ a & c ^ b & c) >>> 0;
|
|
4981
|
+
h = g;
|
|
4982
|
+
g = f;
|
|
4983
|
+
f = e;
|
|
4984
|
+
e = d + temp1 >>> 0;
|
|
4985
|
+
d = c;
|
|
4986
|
+
c = b;
|
|
4987
|
+
b = a;
|
|
4988
|
+
a = temp1 + temp2 >>> 0;
|
|
4989
|
+
}
|
|
4990
|
+
h0 = h0 + a >>> 0;
|
|
4991
|
+
h1 = h1 + b >>> 0;
|
|
4992
|
+
h2 = h2 + c >>> 0;
|
|
4993
|
+
h3 = h3 + d >>> 0;
|
|
4994
|
+
h4 = h4 + e >>> 0;
|
|
4995
|
+
h5 = h5 + f >>> 0;
|
|
4996
|
+
h6 = h6 + g >>> 0;
|
|
4997
|
+
h7 = h7 + h >>> 0;
|
|
4998
|
+
}
|
|
4999
|
+
const result = new Uint8Array(32);
|
|
5000
|
+
const resultView = new DataView(result.buffer);
|
|
5001
|
+
resultView.setUint32(0, h0, false);
|
|
5002
|
+
resultView.setUint32(4, h1, false);
|
|
5003
|
+
resultView.setUint32(8, h2, false);
|
|
5004
|
+
resultView.setUint32(12, h3, false);
|
|
5005
|
+
resultView.setUint32(16, h4, false);
|
|
5006
|
+
resultView.setUint32(20, h5, false);
|
|
5007
|
+
resultView.setUint32(24, h6, false);
|
|
5008
|
+
resultView.setUint32(28, h7, false);
|
|
5009
|
+
return result;
|
|
5010
|
+
}
|
|
5011
|
+
/**
|
|
5012
|
+
* Generate cryptographically secure random bytes.
|
|
5013
|
+
* Uses crypto.getRandomValues (available in all modern browsers).
|
|
5014
|
+
*/
|
|
5015
|
+
function randomBytes(length) {
|
|
5016
|
+
const bytes = new Uint8Array(length);
|
|
5017
|
+
globalThis.crypto.getRandomValues(bytes);
|
|
5018
|
+
return bytes;
|
|
5019
|
+
}
|
|
5020
|
+
/**
|
|
5021
|
+
* Compute a hash digest using Web Crypto API.
|
|
5022
|
+
*
|
|
5023
|
+
* NOTE: In the browser, this is async. The Node.js version is sync.
|
|
5024
|
+
* For callers that need sync hashing, use `sha256()` or `md5()` directly.
|
|
5025
|
+
*
|
|
5026
|
+
* @param algorithm - Hash algorithm name (e.g., "SHA-256", "SHA-512", "SHA-1").
|
|
5027
|
+
* @param data - Data to hash
|
|
5028
|
+
* @returns The digest bytes
|
|
5029
|
+
*/
|
|
5030
|
+
async function hashAsync(algorithm, data) {
|
|
5031
|
+
const buf = await globalThis.crypto.subtle.digest(normalizeAlgorithmForWebCrypto(algorithm), data.buffer.slice(data.byteOffset, data.byteOffset + data.byteLength));
|
|
5032
|
+
return new Uint8Array(buf);
|
|
5033
|
+
}
|
|
5034
|
+
/**
|
|
5035
|
+
* Normalize a hash algorithm name to the format Web Crypto API expects.
|
|
5036
|
+
* Accepts: "sha256", "SHA-256", "sha-256", "SHA256" → "SHA-256"
|
|
5037
|
+
*/
|
|
5038
|
+
function normalizeAlgorithmForWebCrypto(algorithm) {
|
|
5039
|
+
switch (algorithm.toLowerCase().replace(/-/g, "")) {
|
|
5040
|
+
case "sha1": return "SHA-1";
|
|
5041
|
+
case "sha256": return "SHA-256";
|
|
5042
|
+
case "sha384": return "SHA-384";
|
|
5043
|
+
case "sha512": return "SHA-512";
|
|
5044
|
+
default: return algorithm;
|
|
5045
|
+
}
|
|
5046
|
+
}
|
|
5047
|
+
var SBOX, RCON, SHA256_H, SHA256_K;
|
|
5048
|
+
var init_crypto_browser = __esmMin((() => {
|
|
5049
|
+
SBOX = new Uint8Array([
|
|
5050
|
+
99,
|
|
5051
|
+
124,
|
|
5052
|
+
119,
|
|
5053
|
+
123,
|
|
5054
|
+
242,
|
|
5055
|
+
107,
|
|
5056
|
+
111,
|
|
5057
|
+
197,
|
|
5058
|
+
48,
|
|
5059
|
+
1,
|
|
5060
|
+
103,
|
|
5061
|
+
43,
|
|
5062
|
+
254,
|
|
5063
|
+
215,
|
|
5064
|
+
171,
|
|
5065
|
+
118,
|
|
5066
|
+
202,
|
|
5067
|
+
130,
|
|
5068
|
+
201,
|
|
5069
|
+
125,
|
|
5070
|
+
250,
|
|
5071
|
+
89,
|
|
5072
|
+
71,
|
|
5073
|
+
240,
|
|
5074
|
+
173,
|
|
5075
|
+
212,
|
|
5076
|
+
162,
|
|
5077
|
+
175,
|
|
5078
|
+
156,
|
|
5079
|
+
164,
|
|
5080
|
+
114,
|
|
5081
|
+
192,
|
|
5082
|
+
183,
|
|
5083
|
+
253,
|
|
5084
|
+
147,
|
|
5085
|
+
38,
|
|
5086
|
+
54,
|
|
5087
|
+
63,
|
|
5088
|
+
247,
|
|
5089
|
+
204,
|
|
5090
|
+
52,
|
|
5091
|
+
165,
|
|
5092
|
+
229,
|
|
5093
|
+
241,
|
|
5094
|
+
113,
|
|
5095
|
+
216,
|
|
5096
|
+
49,
|
|
5097
|
+
21,
|
|
5098
|
+
4,
|
|
5099
|
+
199,
|
|
5100
|
+
35,
|
|
5101
|
+
195,
|
|
5102
|
+
24,
|
|
5103
|
+
150,
|
|
5104
|
+
5,
|
|
5105
|
+
154,
|
|
5106
|
+
7,
|
|
5107
|
+
18,
|
|
5108
|
+
128,
|
|
5109
|
+
226,
|
|
5110
|
+
235,
|
|
5111
|
+
39,
|
|
5112
|
+
178,
|
|
5113
|
+
117,
|
|
5114
|
+
9,
|
|
5115
|
+
131,
|
|
5116
|
+
44,
|
|
5117
|
+
26,
|
|
5118
|
+
27,
|
|
5119
|
+
110,
|
|
5120
|
+
90,
|
|
5121
|
+
160,
|
|
5122
|
+
82,
|
|
5123
|
+
59,
|
|
5124
|
+
214,
|
|
5125
|
+
179,
|
|
5126
|
+
41,
|
|
5127
|
+
227,
|
|
5128
|
+
47,
|
|
5129
|
+
132,
|
|
5130
|
+
83,
|
|
5131
|
+
209,
|
|
5132
|
+
0,
|
|
5133
|
+
237,
|
|
5134
|
+
32,
|
|
5135
|
+
252,
|
|
5136
|
+
177,
|
|
5137
|
+
91,
|
|
5138
|
+
106,
|
|
5139
|
+
203,
|
|
5140
|
+
190,
|
|
5141
|
+
57,
|
|
5142
|
+
74,
|
|
5143
|
+
76,
|
|
5144
|
+
88,
|
|
5145
|
+
207,
|
|
5146
|
+
208,
|
|
5147
|
+
239,
|
|
5148
|
+
170,
|
|
5149
|
+
251,
|
|
5150
|
+
67,
|
|
5151
|
+
77,
|
|
5152
|
+
51,
|
|
5153
|
+
133,
|
|
5154
|
+
69,
|
|
5155
|
+
249,
|
|
5156
|
+
2,
|
|
5157
|
+
127,
|
|
5158
|
+
80,
|
|
5159
|
+
60,
|
|
5160
|
+
159,
|
|
5161
|
+
168,
|
|
5162
|
+
81,
|
|
5163
|
+
163,
|
|
5164
|
+
64,
|
|
5165
|
+
143,
|
|
5166
|
+
146,
|
|
5167
|
+
157,
|
|
5168
|
+
56,
|
|
5169
|
+
245,
|
|
5170
|
+
188,
|
|
5171
|
+
182,
|
|
5172
|
+
218,
|
|
5173
|
+
33,
|
|
5174
|
+
16,
|
|
5175
|
+
255,
|
|
5176
|
+
243,
|
|
5177
|
+
210,
|
|
5178
|
+
205,
|
|
5179
|
+
12,
|
|
5180
|
+
19,
|
|
5181
|
+
236,
|
|
5182
|
+
95,
|
|
5183
|
+
151,
|
|
5184
|
+
68,
|
|
5185
|
+
23,
|
|
5186
|
+
196,
|
|
5187
|
+
167,
|
|
5188
|
+
126,
|
|
5189
|
+
61,
|
|
5190
|
+
100,
|
|
5191
|
+
93,
|
|
5192
|
+
25,
|
|
5193
|
+
115,
|
|
5194
|
+
96,
|
|
5195
|
+
129,
|
|
5196
|
+
79,
|
|
5197
|
+
220,
|
|
5198
|
+
34,
|
|
5199
|
+
42,
|
|
5200
|
+
144,
|
|
5201
|
+
136,
|
|
5202
|
+
70,
|
|
5203
|
+
238,
|
|
5204
|
+
184,
|
|
5205
|
+
20,
|
|
5206
|
+
222,
|
|
5207
|
+
94,
|
|
5208
|
+
11,
|
|
5209
|
+
219,
|
|
5210
|
+
224,
|
|
5211
|
+
50,
|
|
5212
|
+
58,
|
|
5213
|
+
10,
|
|
5214
|
+
73,
|
|
5215
|
+
6,
|
|
5216
|
+
36,
|
|
5217
|
+
92,
|
|
5218
|
+
194,
|
|
5219
|
+
211,
|
|
5220
|
+
172,
|
|
5221
|
+
98,
|
|
5222
|
+
145,
|
|
5223
|
+
149,
|
|
5224
|
+
228,
|
|
5225
|
+
121,
|
|
5226
|
+
231,
|
|
5227
|
+
200,
|
|
5228
|
+
55,
|
|
5229
|
+
109,
|
|
5230
|
+
141,
|
|
5231
|
+
213,
|
|
5232
|
+
78,
|
|
5233
|
+
169,
|
|
5234
|
+
108,
|
|
5235
|
+
86,
|
|
5236
|
+
244,
|
|
5237
|
+
234,
|
|
5238
|
+
101,
|
|
5239
|
+
122,
|
|
5240
|
+
174,
|
|
5241
|
+
8,
|
|
5242
|
+
186,
|
|
5243
|
+
120,
|
|
5244
|
+
37,
|
|
5245
|
+
46,
|
|
5246
|
+
28,
|
|
5247
|
+
166,
|
|
5248
|
+
180,
|
|
5249
|
+
198,
|
|
5250
|
+
232,
|
|
5251
|
+
221,
|
|
5252
|
+
116,
|
|
5253
|
+
31,
|
|
5254
|
+
75,
|
|
5255
|
+
189,
|
|
5256
|
+
139,
|
|
5257
|
+
138,
|
|
5258
|
+
112,
|
|
5259
|
+
62,
|
|
5260
|
+
181,
|
|
5261
|
+
102,
|
|
5262
|
+
72,
|
|
5263
|
+
3,
|
|
5264
|
+
246,
|
|
5265
|
+
14,
|
|
5266
|
+
97,
|
|
5267
|
+
53,
|
|
5268
|
+
87,
|
|
5269
|
+
185,
|
|
5270
|
+
134,
|
|
5271
|
+
193,
|
|
5272
|
+
29,
|
|
5273
|
+
158,
|
|
5274
|
+
225,
|
|
5275
|
+
248,
|
|
5276
|
+
152,
|
|
5277
|
+
17,
|
|
5278
|
+
105,
|
|
5279
|
+
217,
|
|
5280
|
+
142,
|
|
5281
|
+
148,
|
|
5282
|
+
155,
|
|
5283
|
+
30,
|
|
5284
|
+
135,
|
|
5285
|
+
233,
|
|
5286
|
+
206,
|
|
5287
|
+
85,
|
|
5288
|
+
40,
|
|
5289
|
+
223,
|
|
5290
|
+
140,
|
|
5291
|
+
161,
|
|
5292
|
+
137,
|
|
5293
|
+
13,
|
|
5294
|
+
191,
|
|
5295
|
+
230,
|
|
5296
|
+
66,
|
|
5297
|
+
104,
|
|
5298
|
+
65,
|
|
5299
|
+
153,
|
|
5300
|
+
45,
|
|
5301
|
+
15,
|
|
5302
|
+
176,
|
|
5303
|
+
84,
|
|
5304
|
+
187,
|
|
5305
|
+
22
|
|
5306
|
+
]);
|
|
5307
|
+
RCON = [
|
|
5308
|
+
1,
|
|
5309
|
+
2,
|
|
5310
|
+
4,
|
|
5311
|
+
8,
|
|
5312
|
+
16,
|
|
5313
|
+
32,
|
|
5314
|
+
64,
|
|
5315
|
+
128,
|
|
5316
|
+
27,
|
|
5317
|
+
54
|
|
5318
|
+
];
|
|
5319
|
+
SHA256_H = new Uint32Array([
|
|
5320
|
+
1779033703,
|
|
5321
|
+
3144134277,
|
|
5322
|
+
1013904242,
|
|
5323
|
+
2773480762,
|
|
5324
|
+
1359893119,
|
|
5325
|
+
2600822924,
|
|
5326
|
+
528734635,
|
|
5327
|
+
1541459225
|
|
5328
|
+
]);
|
|
5329
|
+
SHA256_K = new Uint32Array([
|
|
5330
|
+
1116352408,
|
|
5331
|
+
1899447441,
|
|
5332
|
+
3049323471,
|
|
5333
|
+
3921009573,
|
|
5334
|
+
961987163,
|
|
5335
|
+
1508970993,
|
|
5336
|
+
2453635748,
|
|
5337
|
+
2870763221,
|
|
5338
|
+
3624381080,
|
|
5339
|
+
310598401,
|
|
5340
|
+
607225278,
|
|
5341
|
+
1426881987,
|
|
5342
|
+
1925078388,
|
|
5343
|
+
2162078206,
|
|
5344
|
+
2614888103,
|
|
5345
|
+
3248222580,
|
|
5346
|
+
3835390401,
|
|
5347
|
+
4022224774,
|
|
5348
|
+
264347078,
|
|
5349
|
+
604807628,
|
|
5350
|
+
770255983,
|
|
5351
|
+
1249150122,
|
|
5352
|
+
1555081692,
|
|
5353
|
+
1996064986,
|
|
5354
|
+
2554220882,
|
|
5355
|
+
2821834349,
|
|
5356
|
+
2952996808,
|
|
5357
|
+
3210313671,
|
|
5358
|
+
3336571891,
|
|
5359
|
+
3584528711,
|
|
5360
|
+
113926993,
|
|
5361
|
+
338241895,
|
|
5362
|
+
666307205,
|
|
5363
|
+
773529912,
|
|
5364
|
+
1294757372,
|
|
5365
|
+
1396182291,
|
|
5366
|
+
1695183700,
|
|
5367
|
+
1986661051,
|
|
5368
|
+
2177026350,
|
|
5369
|
+
2456956037,
|
|
5370
|
+
2730485921,
|
|
5371
|
+
2820302411,
|
|
5372
|
+
3259730800,
|
|
5373
|
+
3345764771,
|
|
5374
|
+
3516065817,
|
|
5375
|
+
3600352804,
|
|
5376
|
+
4094571909,
|
|
5377
|
+
275423344,
|
|
5378
|
+
430227734,
|
|
5379
|
+
506948616,
|
|
5380
|
+
659060556,
|
|
5381
|
+
883997877,
|
|
5382
|
+
958139571,
|
|
5383
|
+
1322822218,
|
|
5384
|
+
1537002063,
|
|
5385
|
+
1747873779,
|
|
5386
|
+
1955562222,
|
|
5387
|
+
2024104815,
|
|
5388
|
+
2227730452,
|
|
5389
|
+
2361852424,
|
|
5390
|
+
2428436474,
|
|
5391
|
+
2756734187,
|
|
5392
|
+
3204031479,
|
|
5393
|
+
3329325298
|
|
5394
|
+
]);
|
|
5395
|
+
new Uint32Array([
|
|
5396
|
+
3614090360,
|
|
5397
|
+
3905402710,
|
|
5398
|
+
606105819,
|
|
5399
|
+
3250441966,
|
|
5400
|
+
4118548399,
|
|
5401
|
+
1200080426,
|
|
5402
|
+
2821735955,
|
|
5403
|
+
4249261313,
|
|
5404
|
+
1770035416,
|
|
5405
|
+
2336552879,
|
|
5406
|
+
4294925233,
|
|
5407
|
+
2304563134,
|
|
5408
|
+
1804603682,
|
|
5409
|
+
4254626195,
|
|
5410
|
+
2792965006,
|
|
5411
|
+
1236535329,
|
|
5412
|
+
4129170786,
|
|
5413
|
+
3225465664,
|
|
5414
|
+
643717713,
|
|
5415
|
+
3921069994,
|
|
5416
|
+
3593408605,
|
|
5417
|
+
38016083,
|
|
5418
|
+
3634488961,
|
|
5419
|
+
3889429448,
|
|
5420
|
+
568446438,
|
|
5421
|
+
3275163606,
|
|
5422
|
+
4107603335,
|
|
5423
|
+
1163531501,
|
|
5424
|
+
2850285829,
|
|
5425
|
+
4243563512,
|
|
5426
|
+
1735328473,
|
|
5427
|
+
2368359562,
|
|
5428
|
+
4294588738,
|
|
5429
|
+
2272392833,
|
|
5430
|
+
1839030562,
|
|
5431
|
+
4259657740,
|
|
5432
|
+
2763975236,
|
|
5433
|
+
1272893353,
|
|
5434
|
+
4139469664,
|
|
5435
|
+
3200236656,
|
|
5436
|
+
681279174,
|
|
5437
|
+
3936430074,
|
|
5438
|
+
3572445317,
|
|
5439
|
+
76029189,
|
|
5440
|
+
3654602809,
|
|
5441
|
+
3873151461,
|
|
5442
|
+
530742520,
|
|
5443
|
+
3299628645,
|
|
5444
|
+
4096336452,
|
|
5445
|
+
1126891415,
|
|
5446
|
+
2878612391,
|
|
5447
|
+
4237533241,
|
|
5448
|
+
1700485571,
|
|
5449
|
+
2399980690,
|
|
5450
|
+
4293915773,
|
|
5451
|
+
2240044497,
|
|
5452
|
+
1873313359,
|
|
5453
|
+
4264355552,
|
|
5454
|
+
2734768916,
|
|
5455
|
+
1309151649,
|
|
5456
|
+
4149444226,
|
|
5457
|
+
3174756917,
|
|
5458
|
+
718787259,
|
|
5459
|
+
3951481745
|
|
5460
|
+
]);
|
|
5461
|
+
}));
|
|
5462
|
+
//#endregion
|
|
4808
5463
|
//#region src/utils/binary.ts
|
|
5464
|
+
init_crypto_browser();
|
|
4809
5465
|
/**
|
|
4810
5466
|
* Binary Utilities
|
|
4811
5467
|
*
|
|
@@ -5056,8 +5712,7 @@ var ExcelTS = (function(exports) {
|
|
|
5056
5712
|
//#endregion
|
|
5057
5713
|
//#region src/modules/excel/utils/encryptor.browser.ts
|
|
5058
5714
|
/**
|
|
5059
|
-
* Browser-only Encryptor
|
|
5060
|
-
* Uses Web Crypto API (hardware accelerated)
|
|
5715
|
+
* Browser-only Encryptor — uses shared crypto primitives from `@utils/crypto`.
|
|
5061
5716
|
*/
|
|
5062
5717
|
function uint32ToLe(num) {
|
|
5063
5718
|
const arr = new Uint8Array(4);
|
|
@@ -5069,9 +5724,7 @@ var ExcelTS = (function(exports) {
|
|
|
5069
5724
|
}
|
|
5070
5725
|
const Encryptor = {
|
|
5071
5726
|
async hash(algorithm, ...buffers) {
|
|
5072
|
-
|
|
5073
|
-
const hashBuffer = await crypto.subtle.digest(algorithm, new Uint8Array(data));
|
|
5074
|
-
return new Uint8Array(hashBuffer);
|
|
5727
|
+
return hashAsync(algorithm, concatUint8Arrays(buffers));
|
|
5075
5728
|
},
|
|
5076
5729
|
async convertPasswordToHash(password, hashAlgorithm, saltValue, spinCount) {
|
|
5077
5730
|
const passwordBuffer = stringToUtf16Le(password);
|
|
@@ -5081,9 +5734,7 @@ var ExcelTS = (function(exports) {
|
|
|
5081
5734
|
return uint8ArrayToBase64(key);
|
|
5082
5735
|
},
|
|
5083
5736
|
randomBytes(size) {
|
|
5084
|
-
|
|
5085
|
-
crypto.getRandomValues(bytes);
|
|
5086
|
-
return bytes;
|
|
5737
|
+
return randomBytes(size);
|
|
5087
5738
|
}
|
|
5088
5739
|
};
|
|
5089
5740
|
//#endregion
|
|
@@ -27863,6 +28514,7 @@ self.onmessage = async function(event) {
|
|
|
27863
28514
|
*
|
|
27864
28515
|
* Works in both Node.js and browsers using the Web Crypto API.
|
|
27865
28516
|
*/
|
|
28517
|
+
init_crypto_browser();
|
|
27866
28518
|
/**
|
|
27867
28519
|
* AES vendor ID for WinZip format.
|
|
27868
28520
|
*/
|
|
@@ -27911,22 +28563,6 @@ self.onmessage = async function(event) {
|
|
|
27911
28563
|
throw new Error("Web Crypto API not available");
|
|
27912
28564
|
}
|
|
27913
28565
|
/**
|
|
27914
|
-
* Get crypto.getRandomValues (works in both Node.js and browsers).
|
|
27915
|
-
*/
|
|
27916
|
-
function getRandomValues(array) {
|
|
27917
|
-
if (typeof globalThis.crypto?.getRandomValues !== "undefined") {
|
|
27918
|
-
globalThis.crypto.getRandomValues(array);
|
|
27919
|
-
return array;
|
|
27920
|
-
}
|
|
27921
|
-
throw new Error("crypto.getRandomValues not available");
|
|
27922
|
-
}
|
|
27923
|
-
/**
|
|
27924
|
-
* Generate random bytes.
|
|
27925
|
-
*/
|
|
27926
|
-
function randomBytes$1(length) {
|
|
27927
|
-
return getRandomValues(new Uint8Array(length));
|
|
27928
|
-
}
|
|
27929
|
-
/**
|
|
27930
28566
|
* Derive AES keys from password using PBKDF2.
|
|
27931
28567
|
*
|
|
27932
28568
|
* The derived key material is split into:
|
|
@@ -28077,7 +28713,7 @@ self.onmessage = async function(event) {
|
|
|
28077
28713
|
*/
|
|
28078
28714
|
async function aesEncrypt(data, password, keyStrength) {
|
|
28079
28715
|
const saltLen = AES_SALT_LENGTH[keyStrength];
|
|
28080
|
-
const salt = randomBytes
|
|
28716
|
+
const salt = randomBytes(saltLen);
|
|
28081
28717
|
const keys = await aesDerive(password, salt, keyStrength);
|
|
28082
28718
|
const ciphertext = await aesCtr(keys.encryptionKey, data, true);
|
|
28083
28719
|
const hmac = await aesComputeHmac(keys.hmacKey, ciphertext);
|
|
@@ -29017,7 +29653,7 @@ self.onmessage = async function(event) {
|
|
|
29017
29653
|
if (this._zipCryptoState || this._encryptionMethod !== "zipcrypto") return;
|
|
29018
29654
|
this._zipCryptoState = zipCryptoInitKeys(this._password);
|
|
29019
29655
|
const dosTimeForCheck = this.dosTime << 16 | this.dosDate;
|
|
29020
|
-
const header = zipCryptoCreateHeader(this._zipCryptoState, dosTimeForCheck, randomBytes
|
|
29656
|
+
const header = zipCryptoCreateHeader(this._zipCryptoState, dosTimeForCheck, randomBytes);
|
|
29021
29657
|
this._compressedSize += header.length;
|
|
29022
29658
|
this._enqueueData(header, false);
|
|
29023
29659
|
}
|
|
@@ -46284,15 +46920,31 @@ onmessage = async (ev) => {
|
|
|
46284
46920
|
* Builds a PDF dictionary object from key-value pairs.
|
|
46285
46921
|
* Values are already-serialized PDF strings.
|
|
46286
46922
|
*/
|
|
46287
|
-
var PdfDict = class {
|
|
46923
|
+
var PdfDict = class PdfDict {
|
|
46288
46924
|
constructor() {
|
|
46289
46925
|
this.entries = [];
|
|
46926
|
+
this._raw = null;
|
|
46927
|
+
}
|
|
46928
|
+
/**
|
|
46929
|
+
* Create a PdfDict that wraps a pre-serialized dictionary string.
|
|
46930
|
+
* toString() returns the raw string (with any set() overrides applied).
|
|
46931
|
+
*/
|
|
46932
|
+
static fromRawString(raw) {
|
|
46933
|
+
const d = new PdfDict();
|
|
46934
|
+
d._raw = raw;
|
|
46935
|
+
return d;
|
|
46290
46936
|
}
|
|
46291
46937
|
/**
|
|
46292
46938
|
* Set a dictionary entry. The key should NOT include the leading /.
|
|
46293
46939
|
* The value should be a pre-serialized PDF value string.
|
|
46294
46940
|
*/
|
|
46295
46941
|
set(key, value) {
|
|
46942
|
+
if (this._raw !== null) {
|
|
46943
|
+
const keyPattern = new RegExp(`(/${key})\\s+\\S+(?:\\s+\\d+\\s+R)?`);
|
|
46944
|
+
if (keyPattern.test(this._raw)) this._raw = this._raw.replace(keyPattern, `/${key} ${value}`);
|
|
46945
|
+
else this._raw = this._raw.replace(/>>$/, `\n${pdfName(key)} ${value}\n>>`);
|
|
46946
|
+
return this;
|
|
46947
|
+
}
|
|
46296
46948
|
const idx = this.entries.findIndex(([k]) => k === key);
|
|
46297
46949
|
if (idx >= 0) this.entries[idx] = [key, value];
|
|
46298
46950
|
else this.entries.push([key, value]);
|
|
@@ -46306,9 +46958,18 @@ onmessage = async (ev) => {
|
|
|
46306
46958
|
return this;
|
|
46307
46959
|
}
|
|
46308
46960
|
/**
|
|
46961
|
+
* Remove a dictionary entry by key.
|
|
46962
|
+
*/
|
|
46963
|
+
delete(key) {
|
|
46964
|
+
const idx = this.entries.findIndex(([k]) => k === key);
|
|
46965
|
+
if (idx >= 0) this.entries.splice(idx, 1);
|
|
46966
|
+
return this;
|
|
46967
|
+
}
|
|
46968
|
+
/**
|
|
46309
46969
|
* Serialize to a PDF dictionary string.
|
|
46310
46970
|
*/
|
|
46311
46971
|
toString() {
|
|
46972
|
+
if (this._raw !== null) return this._raw;
|
|
46312
46973
|
const parts = ["<<"];
|
|
46313
46974
|
for (const [key, value] of this.entries) parts.push(`${pdfName(key)} ${value}`);
|
|
46314
46975
|
parts.push(">>");
|
|
@@ -46363,702 +47024,8 @@ onmessage = async (ev) => {
|
|
|
46363
47024
|
return err instanceof PdfError;
|
|
46364
47025
|
}
|
|
46365
47026
|
//#endregion
|
|
46366
|
-
//#region src/modules/pdf/core/crypto.ts
|
|
46367
|
-
/**
|
|
46368
|
-
* Shared cryptographic primitives for PDF encryption/decryption.
|
|
46369
|
-
*
|
|
46370
|
-
* Zero-dependency, pure JavaScript implementations of:
|
|
46371
|
-
* - AES (128/256-bit) CBC encrypt and decrypt
|
|
46372
|
-
* - SHA-256
|
|
46373
|
-
* - MD5
|
|
46374
|
-
* - RC4 (for reading legacy PDFs only)
|
|
46375
|
-
*
|
|
46376
|
-
* @see FIPS 197 — AES
|
|
46377
|
-
* @see FIPS 180-4 — SHA-256
|
|
46378
|
-
* @see RFC 1321 — MD5
|
|
46379
|
-
*/
|
|
46380
|
-
/** AES S-Box */
|
|
46381
|
-
const SBOX = new Uint8Array([
|
|
46382
|
-
99,
|
|
46383
|
-
124,
|
|
46384
|
-
119,
|
|
46385
|
-
123,
|
|
46386
|
-
242,
|
|
46387
|
-
107,
|
|
46388
|
-
111,
|
|
46389
|
-
197,
|
|
46390
|
-
48,
|
|
46391
|
-
1,
|
|
46392
|
-
103,
|
|
46393
|
-
43,
|
|
46394
|
-
254,
|
|
46395
|
-
215,
|
|
46396
|
-
171,
|
|
46397
|
-
118,
|
|
46398
|
-
202,
|
|
46399
|
-
130,
|
|
46400
|
-
201,
|
|
46401
|
-
125,
|
|
46402
|
-
250,
|
|
46403
|
-
89,
|
|
46404
|
-
71,
|
|
46405
|
-
240,
|
|
46406
|
-
173,
|
|
46407
|
-
212,
|
|
46408
|
-
162,
|
|
46409
|
-
175,
|
|
46410
|
-
156,
|
|
46411
|
-
164,
|
|
46412
|
-
114,
|
|
46413
|
-
192,
|
|
46414
|
-
183,
|
|
46415
|
-
253,
|
|
46416
|
-
147,
|
|
46417
|
-
38,
|
|
46418
|
-
54,
|
|
46419
|
-
63,
|
|
46420
|
-
247,
|
|
46421
|
-
204,
|
|
46422
|
-
52,
|
|
46423
|
-
165,
|
|
46424
|
-
229,
|
|
46425
|
-
241,
|
|
46426
|
-
113,
|
|
46427
|
-
216,
|
|
46428
|
-
49,
|
|
46429
|
-
21,
|
|
46430
|
-
4,
|
|
46431
|
-
199,
|
|
46432
|
-
35,
|
|
46433
|
-
195,
|
|
46434
|
-
24,
|
|
46435
|
-
150,
|
|
46436
|
-
5,
|
|
46437
|
-
154,
|
|
46438
|
-
7,
|
|
46439
|
-
18,
|
|
46440
|
-
128,
|
|
46441
|
-
226,
|
|
46442
|
-
235,
|
|
46443
|
-
39,
|
|
46444
|
-
178,
|
|
46445
|
-
117,
|
|
46446
|
-
9,
|
|
46447
|
-
131,
|
|
46448
|
-
44,
|
|
46449
|
-
26,
|
|
46450
|
-
27,
|
|
46451
|
-
110,
|
|
46452
|
-
90,
|
|
46453
|
-
160,
|
|
46454
|
-
82,
|
|
46455
|
-
59,
|
|
46456
|
-
214,
|
|
46457
|
-
179,
|
|
46458
|
-
41,
|
|
46459
|
-
227,
|
|
46460
|
-
47,
|
|
46461
|
-
132,
|
|
46462
|
-
83,
|
|
46463
|
-
209,
|
|
46464
|
-
0,
|
|
46465
|
-
237,
|
|
46466
|
-
32,
|
|
46467
|
-
252,
|
|
46468
|
-
177,
|
|
46469
|
-
91,
|
|
46470
|
-
106,
|
|
46471
|
-
203,
|
|
46472
|
-
190,
|
|
46473
|
-
57,
|
|
46474
|
-
74,
|
|
46475
|
-
76,
|
|
46476
|
-
88,
|
|
46477
|
-
207,
|
|
46478
|
-
208,
|
|
46479
|
-
239,
|
|
46480
|
-
170,
|
|
46481
|
-
251,
|
|
46482
|
-
67,
|
|
46483
|
-
77,
|
|
46484
|
-
51,
|
|
46485
|
-
133,
|
|
46486
|
-
69,
|
|
46487
|
-
249,
|
|
46488
|
-
2,
|
|
46489
|
-
127,
|
|
46490
|
-
80,
|
|
46491
|
-
60,
|
|
46492
|
-
159,
|
|
46493
|
-
168,
|
|
46494
|
-
81,
|
|
46495
|
-
163,
|
|
46496
|
-
64,
|
|
46497
|
-
143,
|
|
46498
|
-
146,
|
|
46499
|
-
157,
|
|
46500
|
-
56,
|
|
46501
|
-
245,
|
|
46502
|
-
188,
|
|
46503
|
-
182,
|
|
46504
|
-
218,
|
|
46505
|
-
33,
|
|
46506
|
-
16,
|
|
46507
|
-
255,
|
|
46508
|
-
243,
|
|
46509
|
-
210,
|
|
46510
|
-
205,
|
|
46511
|
-
12,
|
|
46512
|
-
19,
|
|
46513
|
-
236,
|
|
46514
|
-
95,
|
|
46515
|
-
151,
|
|
46516
|
-
68,
|
|
46517
|
-
23,
|
|
46518
|
-
196,
|
|
46519
|
-
167,
|
|
46520
|
-
126,
|
|
46521
|
-
61,
|
|
46522
|
-
100,
|
|
46523
|
-
93,
|
|
46524
|
-
25,
|
|
46525
|
-
115,
|
|
46526
|
-
96,
|
|
46527
|
-
129,
|
|
46528
|
-
79,
|
|
46529
|
-
220,
|
|
46530
|
-
34,
|
|
46531
|
-
42,
|
|
46532
|
-
144,
|
|
46533
|
-
136,
|
|
46534
|
-
70,
|
|
46535
|
-
238,
|
|
46536
|
-
184,
|
|
46537
|
-
20,
|
|
46538
|
-
222,
|
|
46539
|
-
94,
|
|
46540
|
-
11,
|
|
46541
|
-
219,
|
|
46542
|
-
224,
|
|
46543
|
-
50,
|
|
46544
|
-
58,
|
|
46545
|
-
10,
|
|
46546
|
-
73,
|
|
46547
|
-
6,
|
|
46548
|
-
36,
|
|
46549
|
-
92,
|
|
46550
|
-
194,
|
|
46551
|
-
211,
|
|
46552
|
-
172,
|
|
46553
|
-
98,
|
|
46554
|
-
145,
|
|
46555
|
-
149,
|
|
46556
|
-
228,
|
|
46557
|
-
121,
|
|
46558
|
-
231,
|
|
46559
|
-
200,
|
|
46560
|
-
55,
|
|
46561
|
-
109,
|
|
46562
|
-
141,
|
|
46563
|
-
213,
|
|
46564
|
-
78,
|
|
46565
|
-
169,
|
|
46566
|
-
108,
|
|
46567
|
-
86,
|
|
46568
|
-
244,
|
|
46569
|
-
234,
|
|
46570
|
-
101,
|
|
46571
|
-
122,
|
|
46572
|
-
174,
|
|
46573
|
-
8,
|
|
46574
|
-
186,
|
|
46575
|
-
120,
|
|
46576
|
-
37,
|
|
46577
|
-
46,
|
|
46578
|
-
28,
|
|
46579
|
-
166,
|
|
46580
|
-
180,
|
|
46581
|
-
198,
|
|
46582
|
-
232,
|
|
46583
|
-
221,
|
|
46584
|
-
116,
|
|
46585
|
-
31,
|
|
46586
|
-
75,
|
|
46587
|
-
189,
|
|
46588
|
-
139,
|
|
46589
|
-
138,
|
|
46590
|
-
112,
|
|
46591
|
-
62,
|
|
46592
|
-
181,
|
|
46593
|
-
102,
|
|
46594
|
-
72,
|
|
46595
|
-
3,
|
|
46596
|
-
246,
|
|
46597
|
-
14,
|
|
46598
|
-
97,
|
|
46599
|
-
53,
|
|
46600
|
-
87,
|
|
46601
|
-
185,
|
|
46602
|
-
134,
|
|
46603
|
-
193,
|
|
46604
|
-
29,
|
|
46605
|
-
158,
|
|
46606
|
-
225,
|
|
46607
|
-
248,
|
|
46608
|
-
152,
|
|
46609
|
-
17,
|
|
46610
|
-
105,
|
|
46611
|
-
217,
|
|
46612
|
-
142,
|
|
46613
|
-
148,
|
|
46614
|
-
155,
|
|
46615
|
-
30,
|
|
46616
|
-
135,
|
|
46617
|
-
233,
|
|
46618
|
-
206,
|
|
46619
|
-
85,
|
|
46620
|
-
40,
|
|
46621
|
-
223,
|
|
46622
|
-
140,
|
|
46623
|
-
161,
|
|
46624
|
-
137,
|
|
46625
|
-
13,
|
|
46626
|
-
191,
|
|
46627
|
-
230,
|
|
46628
|
-
66,
|
|
46629
|
-
104,
|
|
46630
|
-
65,
|
|
46631
|
-
153,
|
|
46632
|
-
45,
|
|
46633
|
-
15,
|
|
46634
|
-
176,
|
|
46635
|
-
84,
|
|
46636
|
-
187,
|
|
46637
|
-
22
|
|
46638
|
-
]);
|
|
46639
|
-
/** AES round constants */
|
|
46640
|
-
const RCON = [
|
|
46641
|
-
1,
|
|
46642
|
-
2,
|
|
46643
|
-
4,
|
|
46644
|
-
8,
|
|
46645
|
-
16,
|
|
46646
|
-
32,
|
|
46647
|
-
64,
|
|
46648
|
-
128,
|
|
46649
|
-
27,
|
|
46650
|
-
54
|
|
46651
|
-
];
|
|
46652
|
-
/** GF(2^8) multiplication by 2 */
|
|
46653
|
-
function gf2(a) {
|
|
46654
|
-
return a < 128 ? a << 1 : a << 1 ^ 283;
|
|
46655
|
-
}
|
|
46656
|
-
/**
|
|
46657
|
-
* AES key expansion. Supports AES-128 (16-byte key) and AES-256 (32-byte key).
|
|
46658
|
-
*/
|
|
46659
|
-
function aesKeyExpansion(key) {
|
|
46660
|
-
const nk = key.length / 4;
|
|
46661
|
-
const nr = nk + 6;
|
|
46662
|
-
const w = [];
|
|
46663
|
-
for (let i = 0; i < nk; i++) w.push(new Uint8Array([
|
|
46664
|
-
key[4 * i],
|
|
46665
|
-
key[4 * i + 1],
|
|
46666
|
-
key[4 * i + 2],
|
|
46667
|
-
key[4 * i + 3]
|
|
46668
|
-
]));
|
|
46669
|
-
for (let i = nk; i < 4 * (nr + 1); i++) {
|
|
46670
|
-
const temp = new Uint8Array(w[i - 1]);
|
|
46671
|
-
if (i % nk === 0) {
|
|
46672
|
-
const t0 = temp[0];
|
|
46673
|
-
temp[0] = SBOX[temp[1]] ^ RCON[i / nk - 1];
|
|
46674
|
-
temp[1] = SBOX[temp[2]];
|
|
46675
|
-
temp[2] = SBOX[temp[3]];
|
|
46676
|
-
temp[3] = SBOX[t0];
|
|
46677
|
-
} else if (nk > 6 && i % nk === 4) {
|
|
46678
|
-
temp[0] = SBOX[temp[0]];
|
|
46679
|
-
temp[1] = SBOX[temp[1]];
|
|
46680
|
-
temp[2] = SBOX[temp[2]];
|
|
46681
|
-
temp[3] = SBOX[temp[3]];
|
|
46682
|
-
}
|
|
46683
|
-
const word = new Uint8Array(4);
|
|
46684
|
-
for (let j = 0; j < 4; j++) word[j] = w[i - nk][j] ^ temp[j];
|
|
46685
|
-
w.push(word);
|
|
46686
|
-
}
|
|
46687
|
-
return w;
|
|
46688
|
-
}
|
|
46689
|
-
/**
|
|
46690
|
-
* Encrypt a single AES block (16 bytes).
|
|
46691
|
-
* State layout: column-major per FIPS 197 §3.4.
|
|
46692
|
-
*/
|
|
46693
|
-
function aesEncryptBlock(block, roundKeys) {
|
|
46694
|
-
const nr = roundKeys.length / 4 - 1;
|
|
46695
|
-
const state = new Uint8Array(16);
|
|
46696
|
-
state.set(block);
|
|
46697
|
-
for (let c = 0; c < 4; c++) for (let r = 0; r < 4; r++) state[4 * c + r] ^= roundKeys[c][r];
|
|
46698
|
-
for (let round = 1; round < nr; round++) {
|
|
46699
|
-
for (let i = 0; i < 16; i++) state[i] = SBOX[state[i]];
|
|
46700
|
-
let tmp;
|
|
46701
|
-
tmp = state[1];
|
|
46702
|
-
state[1] = state[5];
|
|
46703
|
-
state[5] = state[9];
|
|
46704
|
-
state[9] = state[13];
|
|
46705
|
-
state[13] = tmp;
|
|
46706
|
-
tmp = state[2];
|
|
46707
|
-
state[2] = state[10];
|
|
46708
|
-
state[10] = tmp;
|
|
46709
|
-
tmp = state[6];
|
|
46710
|
-
state[6] = state[14];
|
|
46711
|
-
state[14] = tmp;
|
|
46712
|
-
tmp = state[15];
|
|
46713
|
-
state[15] = state[11];
|
|
46714
|
-
state[11] = state[7];
|
|
46715
|
-
state[7] = state[3];
|
|
46716
|
-
state[3] = tmp;
|
|
46717
|
-
for (let c = 0; c < 4; c++) {
|
|
46718
|
-
const s0 = state[4 * c];
|
|
46719
|
-
const s1 = state[4 * c + 1];
|
|
46720
|
-
const s2 = state[4 * c + 2];
|
|
46721
|
-
const s3 = state[4 * c + 3];
|
|
46722
|
-
state[4 * c] = gf2(s0) ^ gf2(s1) ^ s1 ^ s2 ^ s3;
|
|
46723
|
-
state[4 * c + 1] = s0 ^ gf2(s1) ^ gf2(s2) ^ s2 ^ s3;
|
|
46724
|
-
state[4 * c + 2] = s0 ^ s1 ^ gf2(s2) ^ gf2(s3) ^ s3;
|
|
46725
|
-
state[4 * c + 3] = gf2(s0) ^ s0 ^ s1 ^ s2 ^ gf2(s3);
|
|
46726
|
-
}
|
|
46727
|
-
const keyOffset = round * 4;
|
|
46728
|
-
for (let c = 0; c < 4; c++) for (let r = 0; r < 4; r++) state[4 * c + r] ^= roundKeys[keyOffset + c][r];
|
|
46729
|
-
}
|
|
46730
|
-
for (let i = 0; i < 16; i++) state[i] = SBOX[state[i]];
|
|
46731
|
-
let tmp;
|
|
46732
|
-
tmp = state[1];
|
|
46733
|
-
state[1] = state[5];
|
|
46734
|
-
state[5] = state[9];
|
|
46735
|
-
state[9] = state[13];
|
|
46736
|
-
state[13] = tmp;
|
|
46737
|
-
tmp = state[2];
|
|
46738
|
-
state[2] = state[10];
|
|
46739
|
-
state[10] = tmp;
|
|
46740
|
-
tmp = state[6];
|
|
46741
|
-
state[6] = state[14];
|
|
46742
|
-
state[14] = tmp;
|
|
46743
|
-
tmp = state[15];
|
|
46744
|
-
state[15] = state[11];
|
|
46745
|
-
state[11] = state[7];
|
|
46746
|
-
state[7] = state[3];
|
|
46747
|
-
state[3] = tmp;
|
|
46748
|
-
for (let c = 0; c < 4; c++) for (let r = 0; r < 4; r++) state[4 * c + r] ^= roundKeys[nr * 4 + c][r];
|
|
46749
|
-
return state;
|
|
46750
|
-
}
|
|
46751
|
-
/**
|
|
46752
|
-
* AES-CBC encryption with PKCS#7 padding.
|
|
46753
|
-
* Supports AES-128 (16-byte key) and AES-256 (32-byte key).
|
|
46754
|
-
*/
|
|
46755
|
-
function aesCbcEncrypt(plaintext, key, iv) {
|
|
46756
|
-
const padLen = 16 - plaintext.length % 16;
|
|
46757
|
-
const padded = new Uint8Array(plaintext.length + padLen);
|
|
46758
|
-
padded.set(plaintext);
|
|
46759
|
-
for (let i = plaintext.length; i < padded.length; i++) padded[i] = padLen;
|
|
46760
|
-
const roundKeys = aesKeyExpansion(key);
|
|
46761
|
-
const numBlocks = padded.length / 16;
|
|
46762
|
-
const output = new Uint8Array(padded.length);
|
|
46763
|
-
let prevBlock = iv;
|
|
46764
|
-
for (let b = 0; b < numBlocks; b++) {
|
|
46765
|
-
const block = new Uint8Array(16);
|
|
46766
|
-
for (let i = 0; i < 16; i++) block[i] = padded[b * 16 + i] ^ prevBlock[i];
|
|
46767
|
-
const encrypted = aesEncryptBlock(block, roundKeys);
|
|
46768
|
-
output.set(encrypted, b * 16);
|
|
46769
|
-
prevBlock = encrypted;
|
|
46770
|
-
}
|
|
46771
|
-
return output;
|
|
46772
|
-
}
|
|
46773
|
-
/**
|
|
46774
|
-
* AES-CBC encryption WITHOUT PKCS#7 padding.
|
|
46775
|
-
* Used when the plaintext is already block-aligned (e.g., encrypting
|
|
46776
|
-
* the 32-byte file encryption key in V=5).
|
|
46777
|
-
*
|
|
46778
|
-
* @throws if plaintext length is not a multiple of 16.
|
|
46779
|
-
*/
|
|
46780
|
-
function aesCbcEncryptRaw(plaintext, key, iv) {
|
|
46781
|
-
if (plaintext.length % 16 !== 0) throw new Error("aesCbcEncryptRaw: plaintext length must be a multiple of 16");
|
|
46782
|
-
const roundKeys = aesKeyExpansion(key);
|
|
46783
|
-
const numBlocks = plaintext.length / 16;
|
|
46784
|
-
const output = new Uint8Array(plaintext.length);
|
|
46785
|
-
let prevBlock = iv;
|
|
46786
|
-
for (let b = 0; b < numBlocks; b++) {
|
|
46787
|
-
const block = new Uint8Array(16);
|
|
46788
|
-
for (let i = 0; i < 16; i++) block[i] = plaintext[b * 16 + i] ^ prevBlock[i];
|
|
46789
|
-
const encrypted = aesEncryptBlock(block, roundKeys);
|
|
46790
|
-
output.set(encrypted, b * 16);
|
|
46791
|
-
prevBlock = encrypted;
|
|
46792
|
-
}
|
|
46793
|
-
return output;
|
|
46794
|
-
}
|
|
46795
|
-
/**
|
|
46796
|
-
* AES-ECB encryption of a single 16-byte block (no padding, no IV).
|
|
46797
|
-
* Used for the /Perms value in V=5 encryption.
|
|
46798
|
-
*/
|
|
46799
|
-
function aesEcbEncrypt(block, key) {
|
|
46800
|
-
return aesEncryptBlock(block, aesKeyExpansion(key));
|
|
46801
|
-
}
|
|
46802
|
-
/** SHA-256 initial hash values */
|
|
46803
|
-
const SHA256_H = new Uint32Array([
|
|
46804
|
-
1779033703,
|
|
46805
|
-
3144134277,
|
|
46806
|
-
1013904242,
|
|
46807
|
-
2773480762,
|
|
46808
|
-
1359893119,
|
|
46809
|
-
2600822924,
|
|
46810
|
-
528734635,
|
|
46811
|
-
1541459225
|
|
46812
|
-
]);
|
|
46813
|
-
/** SHA-256 round constants */
|
|
46814
|
-
const SHA256_K = new Uint32Array([
|
|
46815
|
-
1116352408,
|
|
46816
|
-
1899447441,
|
|
46817
|
-
3049323471,
|
|
46818
|
-
3921009573,
|
|
46819
|
-
961987163,
|
|
46820
|
-
1508970993,
|
|
46821
|
-
2453635748,
|
|
46822
|
-
2870763221,
|
|
46823
|
-
3624381080,
|
|
46824
|
-
310598401,
|
|
46825
|
-
607225278,
|
|
46826
|
-
1426881987,
|
|
46827
|
-
1925078388,
|
|
46828
|
-
2162078206,
|
|
46829
|
-
2614888103,
|
|
46830
|
-
3248222580,
|
|
46831
|
-
3835390401,
|
|
46832
|
-
4022224774,
|
|
46833
|
-
264347078,
|
|
46834
|
-
604807628,
|
|
46835
|
-
770255983,
|
|
46836
|
-
1249150122,
|
|
46837
|
-
1555081692,
|
|
46838
|
-
1996064986,
|
|
46839
|
-
2554220882,
|
|
46840
|
-
2821834349,
|
|
46841
|
-
2952996808,
|
|
46842
|
-
3210313671,
|
|
46843
|
-
3336571891,
|
|
46844
|
-
3584528711,
|
|
46845
|
-
113926993,
|
|
46846
|
-
338241895,
|
|
46847
|
-
666307205,
|
|
46848
|
-
773529912,
|
|
46849
|
-
1294757372,
|
|
46850
|
-
1396182291,
|
|
46851
|
-
1695183700,
|
|
46852
|
-
1986661051,
|
|
46853
|
-
2177026350,
|
|
46854
|
-
2456956037,
|
|
46855
|
-
2730485921,
|
|
46856
|
-
2820302411,
|
|
46857
|
-
3259730800,
|
|
46858
|
-
3345764771,
|
|
46859
|
-
3516065817,
|
|
46860
|
-
3600352804,
|
|
46861
|
-
4094571909,
|
|
46862
|
-
275423344,
|
|
46863
|
-
430227734,
|
|
46864
|
-
506948616,
|
|
46865
|
-
659060556,
|
|
46866
|
-
883997877,
|
|
46867
|
-
958139571,
|
|
46868
|
-
1322822218,
|
|
46869
|
-
1537002063,
|
|
46870
|
-
1747873779,
|
|
46871
|
-
1955562222,
|
|
46872
|
-
2024104815,
|
|
46873
|
-
2227730452,
|
|
46874
|
-
2361852424,
|
|
46875
|
-
2428436474,
|
|
46876
|
-
2756734187,
|
|
46877
|
-
3204031479,
|
|
46878
|
-
3329325298
|
|
46879
|
-
]);
|
|
46880
|
-
function rotr32(x, n) {
|
|
46881
|
-
return (x >>> n | x << 32 - n) >>> 0;
|
|
46882
|
-
}
|
|
46883
|
-
/**
|
|
46884
|
-
* SHA-256 hash function.
|
|
46885
|
-
* @returns 32-byte digest
|
|
46886
|
-
*/
|
|
46887
|
-
function sha256(input) {
|
|
46888
|
-
const msgLen = input.length;
|
|
46889
|
-
const paddedLen = Math.ceil((msgLen + 9) / 64) * 64;
|
|
46890
|
-
const padded = new Uint8Array(paddedLen);
|
|
46891
|
-
padded.set(input);
|
|
46892
|
-
padded[msgLen] = 128;
|
|
46893
|
-
const bitLen = msgLen * 8;
|
|
46894
|
-
const view = new DataView(padded.buffer, padded.byteOffset, padded.byteLength);
|
|
46895
|
-
view.setUint32(paddedLen - 8, 0, false);
|
|
46896
|
-
view.setUint32(paddedLen - 4, bitLen, false);
|
|
46897
|
-
let h0 = SHA256_H[0];
|
|
46898
|
-
let h1 = SHA256_H[1];
|
|
46899
|
-
let h2 = SHA256_H[2];
|
|
46900
|
-
let h3 = SHA256_H[3];
|
|
46901
|
-
let h4 = SHA256_H[4];
|
|
46902
|
-
let h5 = SHA256_H[5];
|
|
46903
|
-
let h6 = SHA256_H[6];
|
|
46904
|
-
let h7 = SHA256_H[7];
|
|
46905
|
-
const w = new Uint32Array(64);
|
|
46906
|
-
for (let offset = 0; offset < paddedLen; offset += 64) {
|
|
46907
|
-
for (let i = 0; i < 16; i++) w[i] = view.getUint32(offset + i * 4, false);
|
|
46908
|
-
for (let i = 16; i < 64; i++) {
|
|
46909
|
-
const s0 = rotr32(w[i - 15], 7) ^ rotr32(w[i - 15], 18) ^ w[i - 15] >>> 3;
|
|
46910
|
-
const s1 = rotr32(w[i - 2], 17) ^ rotr32(w[i - 2], 19) ^ w[i - 2] >>> 10;
|
|
46911
|
-
w[i] = w[i - 16] + s0 + w[i - 7] + s1 >>> 0;
|
|
46912
|
-
}
|
|
46913
|
-
let a = h0;
|
|
46914
|
-
let b = h1;
|
|
46915
|
-
let c = h2;
|
|
46916
|
-
let d = h3;
|
|
46917
|
-
let e = h4;
|
|
46918
|
-
let f = h5;
|
|
46919
|
-
let g = h6;
|
|
46920
|
-
let h = h7;
|
|
46921
|
-
for (let i = 0; i < 64; i++) {
|
|
46922
|
-
const S1 = rotr32(e, 6) ^ rotr32(e, 11) ^ rotr32(e, 25);
|
|
46923
|
-
const ch = e & f ^ ~e & g;
|
|
46924
|
-
const temp1 = h + S1 + ch + SHA256_K[i] + w[i] >>> 0;
|
|
46925
|
-
const temp2 = (rotr32(a, 2) ^ rotr32(a, 13) ^ rotr32(a, 22)) + (a & b ^ a & c ^ b & c) >>> 0;
|
|
46926
|
-
h = g;
|
|
46927
|
-
g = f;
|
|
46928
|
-
f = e;
|
|
46929
|
-
e = d + temp1 >>> 0;
|
|
46930
|
-
d = c;
|
|
46931
|
-
c = b;
|
|
46932
|
-
b = a;
|
|
46933
|
-
a = temp1 + temp2 >>> 0;
|
|
46934
|
-
}
|
|
46935
|
-
h0 = h0 + a >>> 0;
|
|
46936
|
-
h1 = h1 + b >>> 0;
|
|
46937
|
-
h2 = h2 + c >>> 0;
|
|
46938
|
-
h3 = h3 + d >>> 0;
|
|
46939
|
-
h4 = h4 + e >>> 0;
|
|
46940
|
-
h5 = h5 + f >>> 0;
|
|
46941
|
-
h6 = h6 + g >>> 0;
|
|
46942
|
-
h7 = h7 + h >>> 0;
|
|
46943
|
-
}
|
|
46944
|
-
const result = new Uint8Array(32);
|
|
46945
|
-
const resultView = new DataView(result.buffer);
|
|
46946
|
-
resultView.setUint32(0, h0, false);
|
|
46947
|
-
resultView.setUint32(4, h1, false);
|
|
46948
|
-
resultView.setUint32(8, h2, false);
|
|
46949
|
-
resultView.setUint32(12, h3, false);
|
|
46950
|
-
resultView.setUint32(16, h4, false);
|
|
46951
|
-
resultView.setUint32(20, h5, false);
|
|
46952
|
-
resultView.setUint32(24, h6, false);
|
|
46953
|
-
resultView.setUint32(28, h7, false);
|
|
46954
|
-
return result;
|
|
46955
|
-
}
|
|
46956
|
-
new Uint32Array([
|
|
46957
|
-
3614090360,
|
|
46958
|
-
3905402710,
|
|
46959
|
-
606105819,
|
|
46960
|
-
3250441966,
|
|
46961
|
-
4118548399,
|
|
46962
|
-
1200080426,
|
|
46963
|
-
2821735955,
|
|
46964
|
-
4249261313,
|
|
46965
|
-
1770035416,
|
|
46966
|
-
2336552879,
|
|
46967
|
-
4294925233,
|
|
46968
|
-
2304563134,
|
|
46969
|
-
1804603682,
|
|
46970
|
-
4254626195,
|
|
46971
|
-
2792965006,
|
|
46972
|
-
1236535329,
|
|
46973
|
-
4129170786,
|
|
46974
|
-
3225465664,
|
|
46975
|
-
643717713,
|
|
46976
|
-
3921069994,
|
|
46977
|
-
3593408605,
|
|
46978
|
-
38016083,
|
|
46979
|
-
3634488961,
|
|
46980
|
-
3889429448,
|
|
46981
|
-
568446438,
|
|
46982
|
-
3275163606,
|
|
46983
|
-
4107603335,
|
|
46984
|
-
1163531501,
|
|
46985
|
-
2850285829,
|
|
46986
|
-
4243563512,
|
|
46987
|
-
1735328473,
|
|
46988
|
-
2368359562,
|
|
46989
|
-
4294588738,
|
|
46990
|
-
2272392833,
|
|
46991
|
-
1839030562,
|
|
46992
|
-
4259657740,
|
|
46993
|
-
2763975236,
|
|
46994
|
-
1272893353,
|
|
46995
|
-
4139469664,
|
|
46996
|
-
3200236656,
|
|
46997
|
-
681279174,
|
|
46998
|
-
3936430074,
|
|
46999
|
-
3572445317,
|
|
47000
|
-
76029189,
|
|
47001
|
-
3654602809,
|
|
47002
|
-
3873151461,
|
|
47003
|
-
530742520,
|
|
47004
|
-
3299628645,
|
|
47005
|
-
4096336452,
|
|
47006
|
-
1126891415,
|
|
47007
|
-
2878612391,
|
|
47008
|
-
4237533241,
|
|
47009
|
-
1700485571,
|
|
47010
|
-
2399980690,
|
|
47011
|
-
4293915773,
|
|
47012
|
-
2240044497,
|
|
47013
|
-
1873313359,
|
|
47014
|
-
4264355552,
|
|
47015
|
-
2734768916,
|
|
47016
|
-
1309151649,
|
|
47017
|
-
4149444226,
|
|
47018
|
-
3174756917,
|
|
47019
|
-
718787259,
|
|
47020
|
-
3951481745
|
|
47021
|
-
]);
|
|
47022
|
-
/**
|
|
47023
|
-
* Generate pseudo-random bytes.
|
|
47024
|
-
* Uses Math.random — adequate for PDF IVs but not cryptographically secure.
|
|
47025
|
-
*/
|
|
47026
|
-
function randomBytes(length) {
|
|
47027
|
-
const bytes = new Uint8Array(length);
|
|
47028
|
-
for (let i = 0; i < length; i++) bytes[i] = Math.random() * 256 | 0;
|
|
47029
|
-
return bytes;
|
|
47030
|
-
}
|
|
47031
|
-
/**
|
|
47032
|
-
* Concatenate multiple Uint8Arrays.
|
|
47033
|
-
*/
|
|
47034
|
-
function concatArrays(...arrays) {
|
|
47035
|
-
let totalLen = 0;
|
|
47036
|
-
for (const arr of arrays) totalLen += arr.length;
|
|
47037
|
-
const result = new Uint8Array(totalLen);
|
|
47038
|
-
let offset = 0;
|
|
47039
|
-
for (const arr of arrays) {
|
|
47040
|
-
result.set(arr, offset);
|
|
47041
|
-
offset += arr.length;
|
|
47042
|
-
}
|
|
47043
|
-
return result;
|
|
47044
|
-
}
|
|
47045
|
-
//#endregion
|
|
47046
47027
|
//#region src/modules/pdf/core/encryption.ts
|
|
47047
|
-
|
|
47048
|
-
* PDF encryption support (Standard Security Handler, V=5, R=5).
|
|
47049
|
-
*
|
|
47050
|
-
* Implements AES-256 encryption compatible with PDF 2.0 (ISO 32000-2:2020).
|
|
47051
|
-
* Supports:
|
|
47052
|
-
* - User password (required to open the document)
|
|
47053
|
-
* - Owner password (grants full access)
|
|
47054
|
-
* - Permission flags (print, copy, modify, etc.)
|
|
47055
|
-
*
|
|
47056
|
-
* The file encryption key (FEK) is a random 256-bit key.
|
|
47057
|
-
* All streams and strings are encrypted using AES-256-CBC with a random
|
|
47058
|
-
* 16-byte IV prepended to each encrypted value.
|
|
47059
|
-
*
|
|
47060
|
-
* @see ISO 32000-2:2020, §7.6 — Encryption
|
|
47061
|
-
*/
|
|
47028
|
+
init_crypto_browser();
|
|
47062
47029
|
/**
|
|
47063
47030
|
* Initialize encryption state for AES-256 (V=5, R=5).
|
|
47064
47031
|
*/
|
|
@@ -47071,12 +47038,28 @@ onmessage = async (ev) => {
|
|
|
47071
47038
|
const uKeySalt = randomBytes(8);
|
|
47072
47039
|
const oValidationSalt = randomBytes(8);
|
|
47073
47040
|
const oKeySalt = randomBytes(8);
|
|
47074
|
-
const uValue =
|
|
47075
|
-
|
|
47041
|
+
const uValue = concatUint8Arrays([
|
|
47042
|
+
sha256(concatUint8Arrays([userPwd, uValidationSalt])),
|
|
47043
|
+
uValidationSalt,
|
|
47044
|
+
uKeySalt
|
|
47045
|
+
]);
|
|
47046
|
+
const ueKey = sha256(concatUint8Arrays([userPwd, uKeySalt]));
|
|
47076
47047
|
const zeroIv = new Uint8Array(16);
|
|
47077
47048
|
const ueValue = aesCbcEncryptRaw(encryptionKey, ueKey, zeroIv);
|
|
47078
|
-
const oValue =
|
|
47079
|
-
|
|
47049
|
+
const oValue = concatUint8Arrays([
|
|
47050
|
+
sha256(concatUint8Arrays([
|
|
47051
|
+
ownerPwd,
|
|
47052
|
+
oValidationSalt,
|
|
47053
|
+
uValue
|
|
47054
|
+
])),
|
|
47055
|
+
oValidationSalt,
|
|
47056
|
+
oKeySalt
|
|
47057
|
+
]);
|
|
47058
|
+
const oeValue = aesCbcEncryptRaw(encryptionKey, sha256(concatUint8Arrays([
|
|
47059
|
+
ownerPwd,
|
|
47060
|
+
oKeySalt,
|
|
47061
|
+
uValue
|
|
47062
|
+
])), zeroIv);
|
|
47080
47063
|
const permsBlock = new Uint8Array(16);
|
|
47081
47064
|
new DataView(permsBlock.buffer).setInt32(0, perms, true);
|
|
47082
47065
|
permsBlock[4] = 255;
|
|
@@ -47146,6 +47129,9 @@ onmessage = async (ev) => {
|
|
|
47146
47129
|
* 3. Cross-reference table
|
|
47147
47130
|
* 4. Trailer (with document catalog reference)
|
|
47148
47131
|
*
|
|
47132
|
+
* Also provides {@link buildIncremental} for appending incremental updates
|
|
47133
|
+
* to an existing PDF without rewriting the original bytes.
|
|
47134
|
+
*
|
|
47149
47135
|
* Encryption uses AES-256 (V=5, R=5) per ISO 32000-2:2020.
|
|
47150
47136
|
*
|
|
47151
47137
|
* @see ISO 32000-2:2020, Chapter 7.5 — File Structure
|
|
@@ -47165,6 +47151,14 @@ onmessage = async (ev) => {
|
|
|
47165
47151
|
this.catalogRef = 0;
|
|
47166
47152
|
this.infoRef = 0;
|
|
47167
47153
|
this.encryption = null;
|
|
47154
|
+
this.pdfVersion = "2.0";
|
|
47155
|
+
}
|
|
47156
|
+
/**
|
|
47157
|
+
* Set the PDF version string (e.g. "1.4", "1.7", "2.0").
|
|
47158
|
+
* Default is "2.0".
|
|
47159
|
+
*/
|
|
47160
|
+
setVersion(version) {
|
|
47161
|
+
this.pdfVersion = version;
|
|
47168
47162
|
}
|
|
47169
47163
|
/**
|
|
47170
47164
|
* Enable encryption for this document.
|
|
@@ -47191,9 +47185,9 @@ onmessage = async (ev) => {
|
|
|
47191
47185
|
content: typeof dict === "string" ? dict : dict.toString()
|
|
47192
47186
|
});
|
|
47193
47187
|
}
|
|
47194
|
-
addStreamObject(objectNumber, dict, data) {
|
|
47188
|
+
addStreamObject(objectNumber, dict, data, options) {
|
|
47195
47189
|
let streamData = data instanceof Uint8Array ? data : data.toUint8Array();
|
|
47196
|
-
if (streamData.length > 256 && !dict.toString().includes("/Filter")) {
|
|
47190
|
+
if ((options?.compress ?? true) && streamData.length > 256 && !dict.toString().includes("/Filter")) {
|
|
47197
47191
|
const compressed = zlibSync(streamData, { level: 6 });
|
|
47198
47192
|
if (compressed.length < streamData.length) {
|
|
47199
47193
|
dict.set("Filter", "/FlateDecode");
|
|
@@ -47209,6 +47203,20 @@ onmessage = async (ev) => {
|
|
|
47209
47203
|
});
|
|
47210
47204
|
}
|
|
47211
47205
|
/**
|
|
47206
|
+
* Return all stored objects for inspection (e.g., incremental update remapping).
|
|
47207
|
+
* Stream objects include their binary data.
|
|
47208
|
+
*/
|
|
47209
|
+
getObjects() {
|
|
47210
|
+
return this.objects.map((o) => {
|
|
47211
|
+
const result = {
|
|
47212
|
+
objectNumber: o.objectNumber,
|
|
47213
|
+
content: o.content
|
|
47214
|
+
};
|
|
47215
|
+
if ("streamData" in o) result.streamData = o.streamData;
|
|
47216
|
+
return result;
|
|
47217
|
+
});
|
|
47218
|
+
}
|
|
47219
|
+
/**
|
|
47212
47220
|
* Set the document catalog object number.
|
|
47213
47221
|
* This is required and references the root of the document structure.
|
|
47214
47222
|
*/
|
|
@@ -47245,14 +47253,19 @@ onmessage = async (ev) => {
|
|
|
47245
47253
|
}
|
|
47246
47254
|
/**
|
|
47247
47255
|
* Create and add the Catalog dictionary.
|
|
47256
|
+
*
|
|
47257
|
+
* @param pagesRef - Object number of the Pages tree root
|
|
47258
|
+
* @param optionsOrOutlinesRef - Either an outlinesRef number (legacy) or an options object
|
|
47248
47259
|
*/
|
|
47249
|
-
addCatalog(pagesRef,
|
|
47260
|
+
addCatalog(pagesRef, optionsOrOutlinesRef) {
|
|
47261
|
+
const resolvedOptions = typeof optionsOrOutlinesRef === "number" ? { outlinesRef: optionsOrOutlinesRef } : optionsOrOutlinesRef ?? {};
|
|
47250
47262
|
const objNum = this.allocObject();
|
|
47251
47263
|
const dict = new PdfDict().set("Type", "/Catalog").set("Pages", pdfRef(pagesRef));
|
|
47252
|
-
if (outlinesRef) {
|
|
47253
|
-
dict.set("Outlines", pdfRef(outlinesRef));
|
|
47264
|
+
if (resolvedOptions.outlinesRef) {
|
|
47265
|
+
dict.set("Outlines", pdfRef(resolvedOptions.outlinesRef));
|
|
47254
47266
|
dict.set("PageMode", "/UseOutlines");
|
|
47255
47267
|
}
|
|
47268
|
+
if (resolvedOptions.extraEntries) for (const [key, value] of resolvedOptions.extraEntries) dict.set(key, value);
|
|
47256
47269
|
this.addObject(objNum, dict);
|
|
47257
47270
|
this.setCatalog(objNum);
|
|
47258
47271
|
return objNum;
|
|
@@ -47265,7 +47278,8 @@ onmessage = async (ev) => {
|
|
|
47265
47278
|
const encoder = new TextEncoder();
|
|
47266
47279
|
const chunks = [];
|
|
47267
47280
|
let byteOffset = 0;
|
|
47268
|
-
const
|
|
47281
|
+
const headerStr = `%PDF-${this.pdfVersion}\n`;
|
|
47282
|
+
const headerStrBytes = encoder.encode(headerStr);
|
|
47269
47283
|
chunks.push(headerStrBytes);
|
|
47270
47284
|
byteOffset += headerStrBytes.length;
|
|
47271
47285
|
const binaryComment = new Uint8Array([
|
|
@@ -47604,6 +47618,30 @@ onmessage = async (ev) => {
|
|
|
47604
47618
|
return this;
|
|
47605
47619
|
}
|
|
47606
47620
|
/**
|
|
47621
|
+
* Append a cubic Bezier curve to the current path.
|
|
47622
|
+
* From current point to (x3, y3), with control points (x1, y1) and (x2, y2).
|
|
47623
|
+
*/
|
|
47624
|
+
curveTo(x1, y1, x2, y2, x3, y3) {
|
|
47625
|
+
this.parts.push(`${pdfNumber(x1)} ${pdfNumber(y1)} ${pdfNumber(x2)} ${pdfNumber(y2)} ${pdfNumber(x3)} ${pdfNumber(y3)} c`);
|
|
47626
|
+
return this;
|
|
47627
|
+
}
|
|
47628
|
+
/**
|
|
47629
|
+
* Append a cubic Bezier curve where the first control point is the current point.
|
|
47630
|
+
* From current point to (x3, y3), with control points (current, y1) and (x2, y2).
|
|
47631
|
+
*/
|
|
47632
|
+
curveToV(x2, y2, x3, y3) {
|
|
47633
|
+
this.parts.push(`${pdfNumber(x2)} ${pdfNumber(y2)} ${pdfNumber(x3)} ${pdfNumber(y3)} v`);
|
|
47634
|
+
return this;
|
|
47635
|
+
}
|
|
47636
|
+
/**
|
|
47637
|
+
* Append a cubic Bezier curve where the second control point equals (x3, y3).
|
|
47638
|
+
* From current point to (x3, y3), with control point (x1, y1).
|
|
47639
|
+
*/
|
|
47640
|
+
curveToY(x1, y1, x3, y3) {
|
|
47641
|
+
this.parts.push(`${pdfNumber(x1)} ${pdfNumber(y1)} ${pdfNumber(x3)} ${pdfNumber(y3)} y`);
|
|
47642
|
+
return this;
|
|
47643
|
+
}
|
|
47644
|
+
/**
|
|
47607
47645
|
* Stroke the current path.
|
|
47608
47646
|
*/
|
|
47609
47647
|
stroke() {
|
|
@@ -47805,6 +47843,47 @@ onmessage = async (ev) => {
|
|
|
47805
47843
|
return this.moveTo(x1, y1).lineTo(x2, y2).stroke().restore();
|
|
47806
47844
|
}
|
|
47807
47845
|
/**
|
|
47846
|
+
* Append an ellipse to the current path using 4 cubic Bezier curves.
|
|
47847
|
+
* (cx, cy) is the center; rx, ry are the radii.
|
|
47848
|
+
*
|
|
47849
|
+
* Uses the standard kappa = 4 * (sqrt(2) - 1) / 3 ≈ 0.5522847 approximation.
|
|
47850
|
+
*/
|
|
47851
|
+
ellipse(cx, cy, rx, ry) {
|
|
47852
|
+
const k = .5522847;
|
|
47853
|
+
const kx = k * rx;
|
|
47854
|
+
const ky = k * ry;
|
|
47855
|
+
this.moveTo(cx + rx, cy);
|
|
47856
|
+
this.curveTo(cx + rx, cy + ky, cx + kx, cy + ry, cx, cy + ry);
|
|
47857
|
+
this.curveTo(cx - kx, cy + ry, cx - rx, cy + ky, cx - rx, cy);
|
|
47858
|
+
this.curveTo(cx - rx, cy - ky, cx - kx, cy - ry, cx, cy - ry);
|
|
47859
|
+
this.curveTo(cx + kx, cy - ry, cx + rx, cy - ky, cx + rx, cy);
|
|
47860
|
+
return this;
|
|
47861
|
+
}
|
|
47862
|
+
/**
|
|
47863
|
+
* Append a circle to the current path.
|
|
47864
|
+
* (cx, cy) is the center; r is the radius.
|
|
47865
|
+
*/
|
|
47866
|
+
circle(cx, cy, r) {
|
|
47867
|
+
return this.ellipse(cx, cy, r, r);
|
|
47868
|
+
}
|
|
47869
|
+
/**
|
|
47870
|
+
* Append a rounded rectangle to the current path.
|
|
47871
|
+
* (x, y) is the lower-left corner; r is the corner radius.
|
|
47872
|
+
*/
|
|
47873
|
+
roundedRect(x, y, width, height, r) {
|
|
47874
|
+
const kr = .5522847 * r;
|
|
47875
|
+
this.moveTo(x + r, y);
|
|
47876
|
+
this.lineTo(x + width - r, y);
|
|
47877
|
+
this.curveTo(x + width - r + kr, y, x + width, y + r - kr, x + width, y + r);
|
|
47878
|
+
this.lineTo(x + width, y + height - r);
|
|
47879
|
+
this.curveTo(x + width, y + height - r + kr, x + width - r + kr, y + height, x + width - r, y + height);
|
|
47880
|
+
this.lineTo(x + r, y + height);
|
|
47881
|
+
this.curveTo(x + r - kr, y + height, x, y + height - r + kr, x, y + height - r);
|
|
47882
|
+
this.lineTo(x, y + r);
|
|
47883
|
+
this.curveTo(x, y + r - kr, x + r - kr, y, x + r, y);
|
|
47884
|
+
return this;
|
|
47885
|
+
}
|
|
47886
|
+
/**
|
|
47808
47887
|
* Get the content stream as a string.
|
|
47809
47888
|
*/
|
|
47810
47889
|
toString() {
|
|
@@ -49806,6 +49885,299 @@ onmessage = async (ev) => {
|
|
|
49806
49885
|
const LINE_HEIGHT_FACTOR = 1.2;
|
|
49807
49886
|
const PX_TO_PT = 72 / 96;
|
|
49808
49887
|
//#endregion
|
|
49888
|
+
//#region src/modules/pdf/render/png-decoder.ts
|
|
49889
|
+
/**
|
|
49890
|
+
* Minimal PNG decoder for PDF image embedding.
|
|
49891
|
+
*
|
|
49892
|
+
* Extracts raw RGB pixel data from a PNG file. Handles:
|
|
49893
|
+
* - Color types: RGB (2), RGBA (6), Grayscale (0), Grayscale+Alpha (4), Palette (3)
|
|
49894
|
+
* - Bit depth: 8 (most common)
|
|
49895
|
+
* - Interlacing: non-interlaced only (Adam7 interlacing is not supported)
|
|
49896
|
+
* - All 5 PNG filter types (None, Sub, Up, Average, Paeth)
|
|
49897
|
+
*
|
|
49898
|
+
* For RGBA images, produces separate RGB pixels and an alpha mask (for PDF SMask).
|
|
49899
|
+
*/
|
|
49900
|
+
/**
|
|
49901
|
+
* Maximum allowed pixel count for PNG decoding (default: 100 million pixels).
|
|
49902
|
+
* A 10000x10000 RGBA image at 100M pixels would need ~400MB for raw data alone.
|
|
49903
|
+
* This limit prevents memory exhaustion from malicious PNG files with
|
|
49904
|
+
* excessively large declared dimensions.
|
|
49905
|
+
*/
|
|
49906
|
+
const MAX_PNG_PIXELS = 1e8;
|
|
49907
|
+
/**
|
|
49908
|
+
* Decode a PNG file to raw RGB pixels for PDF embedding.
|
|
49909
|
+
* @throws on invalid or unsupported PNG data
|
|
49910
|
+
*/
|
|
49911
|
+
function decodePng(data) {
|
|
49912
|
+
if (data.length < 8 || data[0] !== 137 || data[1] !== 80 || data[2] !== 78 || data[3] !== 71) throw new Error("Invalid PNG signature");
|
|
49913
|
+
let offset = 8;
|
|
49914
|
+
let width = 0;
|
|
49915
|
+
let height = 0;
|
|
49916
|
+
let bitDepth = 0;
|
|
49917
|
+
let colorType = 0;
|
|
49918
|
+
const idatChunks = [];
|
|
49919
|
+
let palette = null;
|
|
49920
|
+
let trns = null;
|
|
49921
|
+
while (offset + 8 <= data.length) {
|
|
49922
|
+
const chunkLen = new DataView(data.buffer, data.byteOffset, data.byteLength).getUint32(offset, false);
|
|
49923
|
+
const chunkType = String.fromCharCode(data[offset + 4], data[offset + 5], data[offset + 6], data[offset + 7]);
|
|
49924
|
+
const chunkData = data.subarray(offset + 8, offset + 8 + chunkLen);
|
|
49925
|
+
offset += 8 + chunkLen + 4;
|
|
49926
|
+
switch (chunkType) {
|
|
49927
|
+
case "IHDR": {
|
|
49928
|
+
const hdr = new DataView(chunkData.buffer, chunkData.byteOffset, chunkData.byteLength);
|
|
49929
|
+
width = hdr.getUint32(0, false);
|
|
49930
|
+
height = hdr.getUint32(4, false);
|
|
49931
|
+
bitDepth = chunkData[8];
|
|
49932
|
+
colorType = chunkData[9];
|
|
49933
|
+
if (chunkData[12] !== 0) throw new Error("Interlaced PNG is not supported");
|
|
49934
|
+
if (bitDepth !== 8) throw new Error(`Unsupported PNG bit depth: ${bitDepth}. Only 8-bit PNGs are supported.`);
|
|
49935
|
+
if (width === 0 || height === 0) throw new Error(`Invalid PNG dimensions: ${width}x${height}`);
|
|
49936
|
+
const totalPixels = width * height;
|
|
49937
|
+
if (totalPixels > MAX_PNG_PIXELS) throw new Error(`PNG dimensions too large: ${width}x${height} (${totalPixels} pixels). Maximum allowed: ${MAX_PNG_PIXELS} pixels.`);
|
|
49938
|
+
break;
|
|
49939
|
+
}
|
|
49940
|
+
case "PLTE":
|
|
49941
|
+
palette = new Uint8Array(chunkData);
|
|
49942
|
+
break;
|
|
49943
|
+
case "tRNS":
|
|
49944
|
+
trns = new Uint8Array(chunkData);
|
|
49945
|
+
break;
|
|
49946
|
+
case "IDAT":
|
|
49947
|
+
idatChunks.push(chunkData);
|
|
49948
|
+
break;
|
|
49949
|
+
case "IEND": break;
|
|
49950
|
+
}
|
|
49951
|
+
}
|
|
49952
|
+
if (width === 0 || height === 0) throw new Error("PNG missing IHDR chunk");
|
|
49953
|
+
const compressedData = concatUint8Arrays(idatChunks);
|
|
49954
|
+
let rawCompressed;
|
|
49955
|
+
if (compressedData.length > 6 && (compressedData[0] & 15) === 8) rawCompressed = compressedData.subarray(2, compressedData.length - 4);
|
|
49956
|
+
else rawCompressed = compressedData;
|
|
49957
|
+
const rawData = decompressSync(rawCompressed);
|
|
49958
|
+
const channels = getChannelCount(colorType);
|
|
49959
|
+
const bytesPerPixel = Math.max(1, channels * bitDepth / 8);
|
|
49960
|
+
const scanlineLen = Math.ceil(width * channels * bitDepth / 8);
|
|
49961
|
+
return toRgb(applyFilters(rawData, width, height, scanlineLen, bytesPerPixel), width, height, colorType, bitDepth, palette, trns);
|
|
49962
|
+
}
|
|
49963
|
+
function applyFilters(data, _width, height, scanlineLen, bytesPerPixel) {
|
|
49964
|
+
const result = new Uint8Array(height * scanlineLen);
|
|
49965
|
+
const bpp = Math.max(1, Math.floor(bytesPerPixel));
|
|
49966
|
+
let srcOffset = 0;
|
|
49967
|
+
for (let y = 0; y < height; y++) {
|
|
49968
|
+
const filterType = data[srcOffset++];
|
|
49969
|
+
const dstOffset = y * scanlineLen;
|
|
49970
|
+
const prevRow = y > 0 ? (y - 1) * scanlineLen : -1;
|
|
49971
|
+
for (let x = 0; x < scanlineLen; x++) {
|
|
49972
|
+
const raw = data[srcOffset++] ?? 0;
|
|
49973
|
+
const a = x >= bpp ? result[dstOffset + x - bpp] : 0;
|
|
49974
|
+
const b = prevRow >= 0 ? result[prevRow + x] : 0;
|
|
49975
|
+
const c = prevRow >= 0 && x >= bpp ? result[prevRow + x - bpp] : 0;
|
|
49976
|
+
switch (filterType) {
|
|
49977
|
+
case 0:
|
|
49978
|
+
result[dstOffset + x] = raw;
|
|
49979
|
+
break;
|
|
49980
|
+
case 1:
|
|
49981
|
+
result[dstOffset + x] = raw + a & 255;
|
|
49982
|
+
break;
|
|
49983
|
+
case 2:
|
|
49984
|
+
result[dstOffset + x] = raw + b & 255;
|
|
49985
|
+
break;
|
|
49986
|
+
case 3:
|
|
49987
|
+
result[dstOffset + x] = raw + Math.floor((a + b) / 2) & 255;
|
|
49988
|
+
break;
|
|
49989
|
+
case 4:
|
|
49990
|
+
result[dstOffset + x] = raw + paethPredictor(a, b, c) & 255;
|
|
49991
|
+
break;
|
|
49992
|
+
default: result[dstOffset + x] = raw;
|
|
49993
|
+
}
|
|
49994
|
+
}
|
|
49995
|
+
}
|
|
49996
|
+
return result;
|
|
49997
|
+
}
|
|
49998
|
+
function paethPredictor(a, b, c) {
|
|
49999
|
+
const p = a + b - c;
|
|
50000
|
+
const pa = Math.abs(p - a);
|
|
50001
|
+
const pb = Math.abs(p - b);
|
|
50002
|
+
const pc = Math.abs(p - c);
|
|
50003
|
+
if (pa <= pb && pa <= pc) return a;
|
|
50004
|
+
if (pb <= pc) return b;
|
|
50005
|
+
return c;
|
|
50006
|
+
}
|
|
50007
|
+
function getChannelCount(colorType) {
|
|
50008
|
+
switch (colorType) {
|
|
50009
|
+
case 0: return 1;
|
|
50010
|
+
case 2: return 3;
|
|
50011
|
+
case 3: return 1;
|
|
50012
|
+
case 4: return 2;
|
|
50013
|
+
case 6: return 4;
|
|
50014
|
+
default: return 3;
|
|
50015
|
+
}
|
|
50016
|
+
}
|
|
50017
|
+
function toRgb(data, width, height, colorType, _bitDepth, palette, trns) {
|
|
50018
|
+
const totalPixels = width * height;
|
|
50019
|
+
const pixels = new Uint8Array(totalPixels * 3);
|
|
50020
|
+
let alpha = null;
|
|
50021
|
+
switch (colorType) {
|
|
50022
|
+
case 2:
|
|
50023
|
+
pixels.set(data.subarray(0, totalPixels * 3));
|
|
50024
|
+
if (trns && trns.length >= 6) {
|
|
50025
|
+
const trR = trns[1];
|
|
50026
|
+
const trG = trns[3];
|
|
50027
|
+
const trB = trns[5];
|
|
50028
|
+
alpha = new Uint8Array(totalPixels);
|
|
50029
|
+
alpha.fill(255);
|
|
50030
|
+
for (let i = 0; i < totalPixels; i++) if (data[i * 3] === trR && data[i * 3 + 1] === trG && data[i * 3 + 2] === trB) alpha[i] = 0;
|
|
50031
|
+
}
|
|
50032
|
+
break;
|
|
50033
|
+
case 6:
|
|
50034
|
+
alpha = new Uint8Array(totalPixels);
|
|
50035
|
+
for (let i = 0; i < totalPixels; i++) {
|
|
50036
|
+
pixels[i * 3] = data[i * 4];
|
|
50037
|
+
pixels[i * 3 + 1] = data[i * 4 + 1];
|
|
50038
|
+
pixels[i * 3 + 2] = data[i * 4 + 2];
|
|
50039
|
+
alpha[i] = data[i * 4 + 3];
|
|
50040
|
+
}
|
|
50041
|
+
break;
|
|
50042
|
+
case 0: {
|
|
50043
|
+
let trGray = -1;
|
|
50044
|
+
if (trns && trns.length >= 2) trGray = trns[1];
|
|
50045
|
+
if (trGray >= 0) {
|
|
50046
|
+
alpha = new Uint8Array(totalPixels);
|
|
50047
|
+
alpha.fill(255);
|
|
50048
|
+
}
|
|
50049
|
+
for (let i = 0; i < totalPixels; i++) {
|
|
50050
|
+
const g = data[i];
|
|
50051
|
+
pixels[i * 3] = g;
|
|
50052
|
+
pixels[i * 3 + 1] = g;
|
|
50053
|
+
pixels[i * 3 + 2] = g;
|
|
50054
|
+
if (alpha && g === trGray) alpha[i] = 0;
|
|
50055
|
+
}
|
|
50056
|
+
break;
|
|
50057
|
+
}
|
|
50058
|
+
case 4:
|
|
50059
|
+
alpha = new Uint8Array(totalPixels);
|
|
50060
|
+
for (let i = 0; i < totalPixels; i++) {
|
|
50061
|
+
const g = data[i * 2];
|
|
50062
|
+
pixels[i * 3] = g;
|
|
50063
|
+
pixels[i * 3 + 1] = g;
|
|
50064
|
+
pixels[i * 3 + 2] = g;
|
|
50065
|
+
alpha[i] = data[i * 2 + 1];
|
|
50066
|
+
}
|
|
50067
|
+
break;
|
|
50068
|
+
case 3:
|
|
50069
|
+
if (!palette) throw new Error("PNG palette color type (3) but missing PLTE chunk");
|
|
50070
|
+
if (trns && trns.length > 0) alpha = new Uint8Array(totalPixels);
|
|
50071
|
+
for (let i = 0; i < totalPixels; i++) {
|
|
50072
|
+
const idx = data[i];
|
|
50073
|
+
pixels[i * 3] = palette[idx * 3] ?? 0;
|
|
50074
|
+
pixels[i * 3 + 1] = palette[idx * 3 + 1] ?? 0;
|
|
50075
|
+
pixels[i * 3 + 2] = palette[idx * 3 + 2] ?? 0;
|
|
50076
|
+
if (alpha) alpha[i] = idx < trns.length ? trns[idx] : 255;
|
|
50077
|
+
}
|
|
50078
|
+
break;
|
|
50079
|
+
default: throw new Error(`Unsupported PNG color type: ${colorType}`);
|
|
50080
|
+
}
|
|
50081
|
+
if (alpha) {
|
|
50082
|
+
let fullyOpaque = true;
|
|
50083
|
+
for (let i = 0; i < alpha.length; i++) if (alpha[i] !== 255) {
|
|
50084
|
+
fullyOpaque = false;
|
|
50085
|
+
break;
|
|
50086
|
+
}
|
|
50087
|
+
if (fullyOpaque) alpha = null;
|
|
50088
|
+
}
|
|
50089
|
+
return {
|
|
50090
|
+
width,
|
|
50091
|
+
height,
|
|
50092
|
+
pixels,
|
|
50093
|
+
alpha,
|
|
50094
|
+
bitsPerComponent: 8
|
|
50095
|
+
};
|
|
50096
|
+
}
|
|
50097
|
+
//#endregion
|
|
50098
|
+
//#region src/modules/pdf/builder/image-utils.ts
|
|
50099
|
+
/**
|
|
50100
|
+
* Parse image dimensions from raw bytes.
|
|
50101
|
+
*/
|
|
50102
|
+
function parseImageDimensions(data, format) {
|
|
50103
|
+
if (format === "png") return parsePngDimensions(data);
|
|
50104
|
+
return parseJpegDimensions(data);
|
|
50105
|
+
}
|
|
50106
|
+
/**
|
|
50107
|
+
* Read width/height from a PNG IHDR chunk (bytes 16-23).
|
|
50108
|
+
*/
|
|
50109
|
+
function parsePngDimensions(data) {
|
|
50110
|
+
if (data.length >= 24 && data[12] === 73 && data[13] === 72 && data[14] === 68 && data[15] === 82) return {
|
|
50111
|
+
width: data[16] << 24 | data[17] << 16 | data[18] << 8 | data[19],
|
|
50112
|
+
height: data[20] << 24 | data[21] << 16 | data[22] << 8 | data[23]
|
|
50113
|
+
};
|
|
50114
|
+
return {
|
|
50115
|
+
width: 1,
|
|
50116
|
+
height: 1
|
|
50117
|
+
};
|
|
50118
|
+
}
|
|
50119
|
+
/**
|
|
50120
|
+
* Read width/height from JPEG SOF marker.
|
|
50121
|
+
*
|
|
50122
|
+
* Correctly excludes non-SOF markers in the 0xC0-0xCF range:
|
|
50123
|
+
* - 0xC4 = DHT (Define Huffman Table)
|
|
50124
|
+
* - 0xC8 = JPG (reserved)
|
|
50125
|
+
* - 0xCC = DAC (Define Arithmetic Coding)
|
|
50126
|
+
*/
|
|
50127
|
+
function parseJpegDimensions(data) {
|
|
50128
|
+
let offset = 2;
|
|
50129
|
+
while (offset < data.length - 1) {
|
|
50130
|
+
while (offset < data.length && data[offset] === 255 && data[offset + 1] === 255) offset++;
|
|
50131
|
+
if (offset >= data.length - 1 || data[offset] !== 255) break;
|
|
50132
|
+
const marker = data[offset + 1];
|
|
50133
|
+
if (marker >= 192 && marker <= 207 && marker !== 196 && marker !== 200 && marker !== 204 && offset + 8 < data.length) return {
|
|
50134
|
+
width: data[offset + 7] << 8 | data[offset + 8],
|
|
50135
|
+
height: data[offset + 5] << 8 | data[offset + 6]
|
|
50136
|
+
};
|
|
50137
|
+
if (offset + 3 >= data.length) break;
|
|
50138
|
+
const segLen = data[offset + 2] << 8 | data[offset + 3];
|
|
50139
|
+
offset += 2 + segLen;
|
|
50140
|
+
}
|
|
50141
|
+
return {
|
|
50142
|
+
width: 1,
|
|
50143
|
+
height: 1
|
|
50144
|
+
};
|
|
50145
|
+
}
|
|
50146
|
+
/**
|
|
50147
|
+
* Write an image XObject (JPEG or PNG) to the writer.
|
|
50148
|
+
* Returns the allocated object number.
|
|
50149
|
+
*/
|
|
50150
|
+
function writeImageXObject(writer, data, format) {
|
|
50151
|
+
if (format === "png") return writePngImageXObject(writer, data);
|
|
50152
|
+
return writeJpegImageXObject(writer, data);
|
|
50153
|
+
}
|
|
50154
|
+
/**
|
|
50155
|
+
* Write a JPEG image using DCTDecode (raw JPEG data embedded directly).
|
|
50156
|
+
*/
|
|
50157
|
+
function writeJpegImageXObject(writer, data) {
|
|
50158
|
+
const objNum = writer.allocObject();
|
|
50159
|
+
const dims = parseJpegDimensions(data);
|
|
50160
|
+
const dict = new PdfDict().set("Type", "/XObject").set("Subtype", "/Image").set("Width", pdfNumber(dims.width)).set("Height", pdfNumber(dims.height)).set("ColorSpace", "/DeviceRGB").set("BitsPerComponent", "8").set("Filter", "/DCTDecode");
|
|
50161
|
+
writer.addStreamObject(objNum, dict, data);
|
|
50162
|
+
return objNum;
|
|
50163
|
+
}
|
|
50164
|
+
/**
|
|
50165
|
+
* Write a PNG image: decode to raw RGB, create SMask for alpha if needed.
|
|
50166
|
+
*/
|
|
50167
|
+
function writePngImageXObject(writer, data) {
|
|
50168
|
+
const png = decodePng(data);
|
|
50169
|
+
const objNum = writer.allocObject();
|
|
50170
|
+
const dict = new PdfDict().set("Type", "/XObject").set("Subtype", "/Image").set("Width", pdfNumber(png.width)).set("Height", pdfNumber(png.height)).set("ColorSpace", "/DeviceRGB").set("BitsPerComponent", pdfNumber(png.bitsPerComponent));
|
|
50171
|
+
if (png.alpha) {
|
|
50172
|
+
const smaskObjNum = writer.allocObject();
|
|
50173
|
+
const smaskDict = new PdfDict().set("Type", "/XObject").set("Subtype", "/Image").set("Width", pdfNumber(png.width)).set("Height", pdfNumber(png.height)).set("ColorSpace", "/DeviceGray").set("BitsPerComponent", "8");
|
|
50174
|
+
writer.addStreamObject(smaskObjNum, smaskDict, png.alpha);
|
|
50175
|
+
dict.set("SMask", pdfRef(smaskObjNum));
|
|
50176
|
+
}
|
|
50177
|
+
writer.addStreamObject(objNum, dict, png.pixels);
|
|
50178
|
+
return objNum;
|
|
50179
|
+
}
|
|
50180
|
+
//#endregion
|
|
49809
50181
|
//#region src/modules/pdf/render/page-renderer.ts
|
|
49810
50182
|
/**
|
|
49811
50183
|
* Page renderer for PDF generation.
|
|
@@ -49818,6 +50190,14 @@ onmessage = async (ev) => {
|
|
|
49818
50190
|
* - Grid lines
|
|
49819
50191
|
* - Page headers (sheet names) and footers (page numbers)
|
|
49820
50192
|
*/
|
|
50193
|
+
function computeCellPadding(cell, scaleFactor = 1) {
|
|
50194
|
+
return {
|
|
50195
|
+
left: (3 + cell.borderInsets.left) * scaleFactor,
|
|
50196
|
+
right: (3 + cell.borderInsets.right) * scaleFactor,
|
|
50197
|
+
top: (2 + cell.borderInsets.top) * scaleFactor,
|
|
50198
|
+
bottom: (2 + cell.borderInsets.bottom) * scaleFactor
|
|
50199
|
+
};
|
|
50200
|
+
}
|
|
49821
50201
|
/**
|
|
49822
50202
|
* Render a single page to a PDF content stream.
|
|
49823
50203
|
*/
|
|
@@ -49923,10 +50303,9 @@ onmessage = async (ev) => {
|
|
|
49923
50303
|
function drawCellText(stream, cell, fontManager, alphaValues, scaleFactor = 1) {
|
|
49924
50304
|
const { rect, text, fontSize, horizontalAlign, verticalAlign, wrapText } = cell;
|
|
49925
50305
|
if (!text && !cell.richText) return;
|
|
49926
|
-
const
|
|
49927
|
-
const
|
|
49928
|
-
const
|
|
49929
|
-
const availHeight = rect.height - padV * 2;
|
|
50306
|
+
const pad = computeCellPadding(cell, scaleFactor);
|
|
50307
|
+
const availWidth = rect.width - pad.left - pad.right;
|
|
50308
|
+
const availHeight = rect.height - pad.top - pad.bottom;
|
|
49930
50309
|
if (availWidth <= 0 || availHeight <= 0) return;
|
|
49931
50310
|
const indentPts = cell.indent * 10 * scaleFactor;
|
|
49932
50311
|
const clipWidth = rect.width + (cell.textOverflowWidth || 0);
|
|
@@ -49967,28 +50346,27 @@ onmessage = async (ev) => {
|
|
|
49967
50346
|
const lines = wrapText ? wrapTextLines(text, measure, effectiveWidth) : text.split(/\r?\n/);
|
|
49968
50347
|
const lineHeight = fontSize * LINE_HEIGHT_FACTOR;
|
|
49969
50348
|
const ascent = fontManager.getFontAscent(resourceName, fontSize);
|
|
49970
|
-
const textStartY = computeTextStartY(verticalAlign, rect, lines.length * lineHeight, ascent,
|
|
50349
|
+
const textStartY = computeTextStartY(verticalAlign, rect, lines.length * lineHeight, ascent, pad.top, pad.bottom);
|
|
49971
50350
|
stream.setFillColor(cell.textColor);
|
|
49972
50351
|
stream.beginText();
|
|
49973
50352
|
stream.setFont(resourceName, fontSize);
|
|
49974
50353
|
for (let i = 0; i < lines.length; i++) {
|
|
49975
50354
|
const line = lines[i];
|
|
49976
50355
|
const lineY = textStartY - i * lineHeight;
|
|
49977
|
-
const textX = computeTextX(horizontalAlign, rect, measure(line), indentPts,
|
|
50356
|
+
const textX = computeTextX(horizontalAlign, rect, measure(line), indentPts, pad.left, pad.right);
|
|
49978
50357
|
stream.setTextMatrix(1, 0, 0, 1, textX, lineY);
|
|
49979
50358
|
const hexEncoded = fontManager.encodeText(line, resourceName);
|
|
49980
50359
|
if (hexEncoded) stream.showTextHex(hexEncoded);
|
|
49981
50360
|
else stream.showText(line);
|
|
49982
50361
|
}
|
|
49983
50362
|
stream.endText();
|
|
49984
|
-
drawTextDecorations(stream, cell, lines, lineHeight, textStartY, measure, resourceName, fontManager, indentPts);
|
|
50363
|
+
drawTextDecorations(stream, cell, lines, lineHeight, textStartY, measure, resourceName, fontManager, indentPts, pad);
|
|
49985
50364
|
stream.restore();
|
|
49986
50365
|
}
|
|
49987
50366
|
function drawRichText(stream, cell, fontManager, indentPts, scaleFactor = 1) {
|
|
49988
50367
|
const { rect, horizontalAlign, verticalAlign, wrapText } = cell;
|
|
49989
50368
|
const runs = cell.richText;
|
|
49990
|
-
const
|
|
49991
|
-
const padV = 2 * scaleFactor;
|
|
50369
|
+
const pad = computeCellPadding(cell, scaleFactor);
|
|
49992
50370
|
let maxFontSize = cell.fontSize;
|
|
49993
50371
|
for (const run of runs) if (run.fontSize > maxFontSize) maxFontSize = run.fontSize;
|
|
49994
50372
|
const primaryFontSize = maxFontSize;
|
|
@@ -49996,7 +50374,7 @@ onmessage = async (ev) => {
|
|
|
49996
50374
|
const isEmbedded = fontManager.hasEmbeddedFont();
|
|
49997
50375
|
const runResource = (run) => isEmbedded ? fontManager.getEmbeddedResourceName() : fontManager.ensureFont(resolvePdfFontName(run.fontFamily, run.bold, run.italic));
|
|
49998
50376
|
if (wrapText) {
|
|
49999
|
-
const availWidth = rect.width -
|
|
50377
|
+
const availWidth = rect.width - pad.left - pad.right - indentPts;
|
|
50000
50378
|
if (availWidth <= 0) return;
|
|
50001
50379
|
const fullText = runs.map((r) => r.text).join("");
|
|
50002
50380
|
const primaryResource = runResource(runs[0]);
|
|
@@ -50006,7 +50384,7 @@ onmessage = async (ev) => {
|
|
|
50006
50384
|
for (let ri = 0; ri < runs.length; ri++) for (let ci = 0; ci < runs[ri].text.length; ci++) runForChar.push(ri);
|
|
50007
50385
|
const primaryResourceName = runResource(runs[0]);
|
|
50008
50386
|
const ascent = fontManager.getFontAscent(primaryResourceName, primaryFontSize);
|
|
50009
|
-
const textStartY = computeTextStartY(verticalAlign, rect, lines.length * lineHeight, ascent,
|
|
50387
|
+
const textStartY = computeTextStartY(verticalAlign, rect, lines.length * lineHeight, ascent, pad.top, pad.bottom);
|
|
50010
50388
|
let charPos = 0;
|
|
50011
50389
|
for (let li = 0; li < lines.length; li++) {
|
|
50012
50390
|
const lineY = textStartY - li * lineHeight;
|
|
@@ -50034,7 +50412,7 @@ onmessage = async (ev) => {
|
|
|
50034
50412
|
}
|
|
50035
50413
|
let lineWidth = 0;
|
|
50036
50414
|
for (const seg of segments) lineWidth += fontManager.measureText(seg.text, seg.resourceName, seg.run.fontSize);
|
|
50037
|
-
let textX = computeTextX(horizontalAlign, rect, lineWidth, indentPts,
|
|
50415
|
+
let textX = computeTextX(horizontalAlign, rect, lineWidth, indentPts, pad.left, pad.right);
|
|
50038
50416
|
for (const seg of segments) {
|
|
50039
50417
|
const { run, text, resourceName } = seg;
|
|
50040
50418
|
const segWidth = fontManager.measureText(text, resourceName, run.fontSize);
|
|
@@ -50071,8 +50449,8 @@ onmessage = async (ev) => {
|
|
|
50071
50449
|
totalWidth += w;
|
|
50072
50450
|
}
|
|
50073
50451
|
const primaryResourceName = runMetrics[0]?.resourceName ?? "F1";
|
|
50074
|
-
const textStartY = computeTextStartY(verticalAlign, rect, lineHeight, fontManager.getFontAscent(primaryResourceName, primaryFontSize),
|
|
50075
|
-
let textX = computeTextX(horizontalAlign, rect, totalWidth, indentPts,
|
|
50452
|
+
const textStartY = computeTextStartY(verticalAlign, rect, lineHeight, fontManager.getFontAscent(primaryResourceName, primaryFontSize), pad.top, pad.bottom);
|
|
50453
|
+
let textX = computeTextX(horizontalAlign, rect, totalWidth, indentPts, pad.left, pad.right);
|
|
50076
50454
|
for (let i = 0; i < runs.length; i++) {
|
|
50077
50455
|
const run = runs[i];
|
|
50078
50456
|
const { resourceName } = runMetrics[i];
|
|
@@ -50099,8 +50477,7 @@ onmessage = async (ev) => {
|
|
|
50099
50477
|
function drawRotatedText(stream, cell, fontManager, indentPts, scaleFactor = 1) {
|
|
50100
50478
|
const { rect, wrapText } = cell;
|
|
50101
50479
|
let { fontSize } = cell;
|
|
50102
|
-
const
|
|
50103
|
-
const padV = 2 * scaleFactor;
|
|
50480
|
+
const pad = computeCellPadding(cell, scaleFactor);
|
|
50104
50481
|
const resourceName = fontManager.hasEmbeddedFont() ? fontManager.getEmbeddedResourceName() : fontManager.ensureFont(resolvePdfFontName(cell.fontFamily, cell.bold, cell.italic));
|
|
50105
50482
|
const degrees = excelRotationToDegrees(cell.textRotation);
|
|
50106
50483
|
const radians = degrees * Math.PI / 180;
|
|
@@ -50108,8 +50485,8 @@ onmessage = async (ev) => {
|
|
|
50108
50485
|
const sin = Math.sin(radians);
|
|
50109
50486
|
const absSin = Math.abs(sin);
|
|
50110
50487
|
const absCos = Math.abs(cos);
|
|
50111
|
-
const maxWidth = rect.width -
|
|
50112
|
-
const maxHeight = rect.height -
|
|
50488
|
+
const maxWidth = rect.width - pad.left - pad.right;
|
|
50489
|
+
const maxHeight = rect.height - pad.top - pad.bottom;
|
|
50113
50490
|
let availTextLength;
|
|
50114
50491
|
if (absSin > .01 && absCos > .01) availTextLength = Math.min(maxHeight / absSin, maxWidth / absCos);
|
|
50115
50492
|
else if (absSin > .01) availTextLength = maxHeight / absSin;
|
|
@@ -50138,27 +50515,27 @@ onmessage = async (ev) => {
|
|
|
50138
50515
|
const is90 = Math.abs(degrees - 90) < .01;
|
|
50139
50516
|
const isMinus90 = Math.abs(degrees + 90) < .01;
|
|
50140
50517
|
stream.setFillColor(cell.textColor);
|
|
50141
|
-
if (is90) drawRotated90(stream, cell, lines, fontManager, resourceName, fontSize, scaledLineHeight, ascent,
|
|
50142
|
-
else if (isMinus90) drawRotatedMinus90(stream, cell, lines, fontManager, resourceName, fontSize, scaledLineHeight, ascent,
|
|
50518
|
+
if (is90) drawRotated90(stream, cell, lines, fontManager, resourceName, fontSize, scaledLineHeight, ascent, pad);
|
|
50519
|
+
else if (isMinus90) drawRotatedMinus90(stream, cell, lines, fontManager, resourceName, fontSize, scaledLineHeight, ascent, pad);
|
|
50143
50520
|
else drawRotatedGeneral(stream, cell, lines, fontManager, resourceName, fontSize, scaledLineHeight, ascent, cos, sin, indentPts);
|
|
50144
50521
|
}
|
|
50145
50522
|
/** 90° CCW: text reads bottom-to-top, lines stack left-to-right. */
|
|
50146
|
-
function drawRotated90(stream, cell, lines, fontManager, resourceName, fontSize, lineHeight, ascent,
|
|
50523
|
+
function drawRotated90(stream, cell, lines, fontManager, resourceName, fontSize, lineHeight, ascent, pad) {
|
|
50147
50524
|
const { rect, horizontalAlign, verticalAlign } = cell;
|
|
50148
50525
|
const totalColumnsWidth = lines.length * lineHeight;
|
|
50149
50526
|
let startX;
|
|
50150
50527
|
if (horizontalAlign === "center") startX = rect.x + rect.width / 2 - totalColumnsWidth / 2 + ascent;
|
|
50151
|
-
else if (horizontalAlign === "right") startX = rect.x + rect.width -
|
|
50152
|
-
else startX = rect.x +
|
|
50528
|
+
else if (horizontalAlign === "right") startX = rect.x + rect.width - pad.right - totalColumnsWidth + ascent;
|
|
50529
|
+
else startX = rect.x + pad.left + ascent;
|
|
50153
50530
|
for (let i = 0; i < lines.length; i++) {
|
|
50154
50531
|
const line = lines[i];
|
|
50155
50532
|
const lineWidth = fontManager.measureText(line, resourceName, fontSize);
|
|
50156
50533
|
const colX = startX + i * lineHeight;
|
|
50157
50534
|
let ty;
|
|
50158
|
-
if (verticalAlign === "top") ty = rect.y + rect.height -
|
|
50535
|
+
if (verticalAlign === "top") ty = rect.y + rect.height - pad.top - lineWidth;
|
|
50159
50536
|
else if (verticalAlign === "middle") ty = rect.y + (rect.height - lineWidth) / 2;
|
|
50160
|
-
else ty = rect.y +
|
|
50161
|
-
ty = Math.max(ty, rect.y +
|
|
50537
|
+
else ty = rect.y + pad.bottom;
|
|
50538
|
+
ty = Math.max(ty, rect.y + pad.bottom);
|
|
50162
50539
|
stream.beginText();
|
|
50163
50540
|
stream.setFont(resourceName, fontSize);
|
|
50164
50541
|
stream.setTextMatrix(0, 1, -1, 0, colX, ty);
|
|
@@ -50167,22 +50544,22 @@ onmessage = async (ev) => {
|
|
|
50167
50544
|
}
|
|
50168
50545
|
}
|
|
50169
50546
|
/** -90° (270° CW): text reads top-to-bottom, lines stack right-to-left. */
|
|
50170
|
-
function drawRotatedMinus90(stream, cell, lines, fontManager, resourceName, fontSize, lineHeight, ascent,
|
|
50547
|
+
function drawRotatedMinus90(stream, cell, lines, fontManager, resourceName, fontSize, lineHeight, ascent, pad) {
|
|
50171
50548
|
const { rect, horizontalAlign, verticalAlign } = cell;
|
|
50172
50549
|
const totalColumnsWidth = lines.length * lineHeight;
|
|
50173
50550
|
let startX;
|
|
50174
50551
|
if (horizontalAlign === "center") startX = rect.x + rect.width / 2 + totalColumnsWidth / 2 - lineHeight + ascent;
|
|
50175
|
-
else if (horizontalAlign === "right") startX = rect.x + rect.width -
|
|
50176
|
-
else startX = rect.x +
|
|
50552
|
+
else if (horizontalAlign === "right") startX = rect.x + rect.width - pad.right - lineHeight + ascent;
|
|
50553
|
+
else startX = rect.x + pad.left + totalColumnsWidth - lineHeight + ascent;
|
|
50177
50554
|
for (let i = 0; i < lines.length; i++) {
|
|
50178
50555
|
const line = lines[i];
|
|
50179
50556
|
const lineWidth = fontManager.measureText(line, resourceName, fontSize);
|
|
50180
50557
|
const colX = startX - i * lineHeight;
|
|
50181
50558
|
let ty;
|
|
50182
|
-
if (verticalAlign === "top") ty = rect.y + rect.height -
|
|
50559
|
+
if (verticalAlign === "top") ty = rect.y + rect.height - pad.top;
|
|
50183
50560
|
else if (verticalAlign === "middle") ty = rect.y + (rect.height + lineWidth) / 2;
|
|
50184
|
-
else ty = rect.y +
|
|
50185
|
-
ty = Math.min(ty, rect.y + rect.height -
|
|
50561
|
+
else ty = rect.y + pad.bottom + lineWidth;
|
|
50562
|
+
ty = Math.min(ty, rect.y + rect.height - pad.top);
|
|
50186
50563
|
stream.beginText();
|
|
50187
50564
|
stream.setFont(resourceName, fontSize);
|
|
50188
50565
|
stream.setTextMatrix(0, -1, 1, 0, colX, ty);
|
|
@@ -50193,8 +50570,7 @@ onmessage = async (ev) => {
|
|
|
50193
50570
|
/** General rotation — center a multi-line text block in the cell. */
|
|
50194
50571
|
function drawRotatedGeneral(stream, cell, lines, fontManager, resourceName, fontSize, lineHeight, ascent, cos, sin, indentPts) {
|
|
50195
50572
|
const { rect, horizontalAlign, verticalAlign } = cell;
|
|
50196
|
-
const
|
|
50197
|
-
const padV = 2;
|
|
50573
|
+
const pad = computeCellPadding(cell);
|
|
50198
50574
|
let maxLineWidth = 0;
|
|
50199
50575
|
for (const line of lines) {
|
|
50200
50576
|
const w = fontManager.measureText(line, resourceName, fontSize);
|
|
@@ -50208,14 +50584,14 @@ onmessage = async (ev) => {
|
|
|
50208
50584
|
const slantShift = computeSlantOffset(cell.textRotation, rect.height) / 2;
|
|
50209
50585
|
const indentOffset = horizontalAlign === "left" ? indentPts / 2 : horizontalAlign === "right" ? -indentPts / 2 : 0;
|
|
50210
50586
|
let cy;
|
|
50211
|
-
if (verticalAlign === "top") cy = rect.y + rect.height -
|
|
50212
|
-
else if (verticalAlign === "bottom") cy = rect.y +
|
|
50587
|
+
if (verticalAlign === "top") cy = rect.y + rect.height - pad.top - rotatedHeight / 2;
|
|
50588
|
+
else if (verticalAlign === "bottom") cy = rect.y + pad.bottom + rotatedHeight / 2;
|
|
50213
50589
|
else cy = rect.y + rect.height / 2;
|
|
50214
50590
|
const verticalRatio = rect.height > 0 ? (cy - rect.y) / rect.height : .5;
|
|
50215
50591
|
const slantAtCy = slantShift * 2 * verticalRatio;
|
|
50216
50592
|
let cx;
|
|
50217
|
-
if (horizontalAlign === "right") cx = rect.x + rect.width -
|
|
50218
|
-
else if (horizontalAlign === "left") cx = rect.x +
|
|
50593
|
+
if (horizontalAlign === "right") cx = rect.x + rect.width - pad.right - rotatedWidth / 2 + indentOffset + slantAtCy;
|
|
50594
|
+
else if (horizontalAlign === "left") cx = rect.x + pad.left + rotatedWidth / 2 + indentOffset + slantAtCy;
|
|
50219
50595
|
else cx = rect.x + rect.width / 2 + indentOffset + slantAtCy;
|
|
50220
50596
|
for (let i = 0; i < lines.length; i++) {
|
|
50221
50597
|
const line = lines[i];
|
|
@@ -50244,8 +50620,7 @@ onmessage = async (ev) => {
|
|
|
50244
50620
|
*/
|
|
50245
50621
|
function drawVerticalStackedText(stream, cell, fontManager, _indentPts, scaleFactor = 1) {
|
|
50246
50622
|
const { rect, text, fontSize, horizontalAlign, verticalAlign } = cell;
|
|
50247
|
-
const
|
|
50248
|
-
const padV = 2 * scaleFactor;
|
|
50623
|
+
const pad = computeCellPadding(cell, scaleFactor);
|
|
50249
50624
|
const resourceName = fontManager.hasEmbeddedFont() ? fontManager.getEmbeddedResourceName() : fontManager.ensureFont(resolvePdfFontName(cell.fontFamily, cell.bold, cell.italic));
|
|
50250
50625
|
const charHeight = fontSize * 1.3;
|
|
50251
50626
|
const ascent = fontManager.getFontAscent(resourceName, fontSize);
|
|
@@ -50254,8 +50629,8 @@ onmessage = async (ev) => {
|
|
|
50254
50629
|
const totalColumnsWidth = columns.length * columnWidth;
|
|
50255
50630
|
let startX;
|
|
50256
50631
|
if (horizontalAlign === "center") startX = rect.x + rect.width / 2 - totalColumnsWidth / 2 + columnWidth / 2;
|
|
50257
|
-
else if (horizontalAlign === "right") startX = rect.x + rect.width -
|
|
50258
|
-
else startX = rect.x +
|
|
50632
|
+
else if (horizontalAlign === "right") startX = rect.x + rect.width - pad.right - totalColumnsWidth + columnWidth / 2;
|
|
50633
|
+
else startX = rect.x + pad.left + columnWidth / 2;
|
|
50259
50634
|
stream.setFillColor(cell.textColor);
|
|
50260
50635
|
for (let colIdx = 0; colIdx < columns.length; colIdx++) {
|
|
50261
50636
|
const colText = columns[colIdx];
|
|
@@ -50263,10 +50638,10 @@ onmessage = async (ev) => {
|
|
|
50263
50638
|
const totalTextHeight = colText.length * charHeight;
|
|
50264
50639
|
let currentY;
|
|
50265
50640
|
if (verticalAlign === "middle") currentY = rect.y + rect.height / 2 + totalTextHeight / 2 - ascent;
|
|
50266
|
-
else if (verticalAlign === "bottom") currentY = rect.y +
|
|
50267
|
-
else currentY = rect.y + rect.height -
|
|
50641
|
+
else if (verticalAlign === "bottom") currentY = rect.y + pad.bottom + totalTextHeight - ascent;
|
|
50642
|
+
else currentY = rect.y + rect.height - pad.top - ascent;
|
|
50268
50643
|
for (const ch of colText) {
|
|
50269
|
-
if (currentY < rect.y +
|
|
50644
|
+
if (currentY < rect.y + pad.bottom) break;
|
|
50270
50645
|
const charWidth = fontManager.measureText(ch, resourceName, fontSize);
|
|
50271
50646
|
stream.beginText();
|
|
50272
50647
|
stream.setFont(resourceName, fontSize);
|
|
@@ -50285,49 +50660,49 @@ onmessage = async (ev) => {
|
|
|
50285
50660
|
function alphaGsName(alpha) {
|
|
50286
50661
|
return `GS${Math.round(alpha * 1e4)}`;
|
|
50287
50662
|
}
|
|
50288
|
-
function computeTextStartY(verticalAlign, rect, totalTextHeight, ascent,
|
|
50663
|
+
function computeTextStartY(verticalAlign, rect, totalTextHeight, ascent, padVTop = 2, padVBottom = padVTop) {
|
|
50289
50664
|
let y;
|
|
50290
50665
|
switch (verticalAlign) {
|
|
50291
50666
|
case "top":
|
|
50292
|
-
y = rect.y + rect.height -
|
|
50667
|
+
y = rect.y + rect.height - padVTop - ascent;
|
|
50293
50668
|
break;
|
|
50294
50669
|
case "middle":
|
|
50295
50670
|
y = rect.y + rect.height / 2 + totalTextHeight / 2 - ascent;
|
|
50296
50671
|
break;
|
|
50297
50672
|
default:
|
|
50298
|
-
y = rect.y +
|
|
50673
|
+
y = rect.y + padVBottom + (totalTextHeight - ascent);
|
|
50299
50674
|
break;
|
|
50300
50675
|
}
|
|
50301
|
-
const maxY = rect.y + rect.height -
|
|
50676
|
+
const maxY = rect.y + rect.height - padVTop - ascent;
|
|
50302
50677
|
if (y > maxY) y = maxY;
|
|
50303
|
-
const minY = rect.y +
|
|
50678
|
+
const minY = rect.y + padVBottom;
|
|
50304
50679
|
if (y < minY) y = minY;
|
|
50305
50680
|
return y;
|
|
50306
50681
|
}
|
|
50307
|
-
function computeTextX(align, rect, textWidth, indentPts = 0,
|
|
50682
|
+
function computeTextX(align, rect, textWidth, indentPts = 0, padHLeft = 3, padHRight = padHLeft) {
|
|
50308
50683
|
let x;
|
|
50309
50684
|
switch (align) {
|
|
50310
50685
|
case "center":
|
|
50311
50686
|
x = rect.x + (rect.width - textWidth) / 2;
|
|
50312
50687
|
break;
|
|
50313
50688
|
case "right":
|
|
50314
|
-
x = rect.x + rect.width -
|
|
50689
|
+
x = rect.x + rect.width - padHRight - textWidth;
|
|
50315
50690
|
break;
|
|
50316
50691
|
default:
|
|
50317
|
-
x = rect.x +
|
|
50692
|
+
x = rect.x + padHLeft + indentPts;
|
|
50318
50693
|
break;
|
|
50319
50694
|
}
|
|
50320
|
-
const minX = rect.x +
|
|
50695
|
+
const minX = rect.x + padHLeft;
|
|
50321
50696
|
if (x < minX) x = minX;
|
|
50322
50697
|
return x;
|
|
50323
50698
|
}
|
|
50324
|
-
function drawTextDecorations(stream, cell, lines, lineHeight, textStartY, measure, resourceName, fontManager, indentPts) {
|
|
50699
|
+
function drawTextDecorations(stream, cell, lines, lineHeight, textStartY, measure, resourceName, fontManager, indentPts, pad) {
|
|
50325
50700
|
if (cell.strike) {
|
|
50326
50701
|
const strikeY = textStartY + fontManager.getFontDescent(resourceName, cell.fontSize) + cell.fontSize * .3;
|
|
50327
50702
|
for (let i = 0; i < lines.length; i++) {
|
|
50328
50703
|
const lineY = strikeY - i * lineHeight;
|
|
50329
50704
|
const lw = measure(lines[i]);
|
|
50330
|
-
const startX = computeTextX(cell.horizontalAlign, cell.rect, lw, indentPts);
|
|
50705
|
+
const startX = computeTextX(cell.horizontalAlign, cell.rect, lw, indentPts, pad?.left, pad?.right);
|
|
50331
50706
|
stream.drawLine(startX, lineY, startX + lw, lineY, cell.textColor, .5);
|
|
50332
50707
|
}
|
|
50333
50708
|
}
|
|
@@ -50336,7 +50711,7 @@ onmessage = async (ev) => {
|
|
|
50336
50711
|
for (let i = 0; i < lines.length; i++) {
|
|
50337
50712
|
const lineY = textStartY - i * lineHeight + underlineOffset;
|
|
50338
50713
|
const lw = measure(lines[i]);
|
|
50339
|
-
const startX = computeTextX(cell.horizontalAlign, cell.rect, lw, indentPts);
|
|
50714
|
+
const startX = computeTextX(cell.horizontalAlign, cell.rect, lw, indentPts, pad?.left, pad?.right);
|
|
50340
50715
|
stream.drawLine(startX, lineY, startX + lw, lineY, cell.textColor, .5);
|
|
50341
50716
|
}
|
|
50342
50717
|
}
|
|
@@ -50581,41 +50956,6 @@ onmessage = async (ev) => {
|
|
|
50581
50956
|
/**
|
|
50582
50957
|
* Parse image dimensions from raw JPEG or PNG data without a full decode.
|
|
50583
50958
|
*/
|
|
50584
|
-
function parseImageDimensions(data, format) {
|
|
50585
|
-
if (format === "png") return parsePngDimensions(data);
|
|
50586
|
-
return parseJpegDimensions(data);
|
|
50587
|
-
}
|
|
50588
|
-
/** Read width/height from a PNG IHDR chunk (bytes 16-23). */
|
|
50589
|
-
function parsePngDimensions(data) {
|
|
50590
|
-
if (data.length >= 24 && data[12] === 73 && data[13] === 72 && data[14] === 68 && data[15] === 82) return {
|
|
50591
|
-
width: data[16] << 24 | data[17] << 16 | data[18] << 8 | data[19],
|
|
50592
|
-
height: data[20] << 24 | data[21] << 16 | data[22] << 8 | data[23]
|
|
50593
|
-
};
|
|
50594
|
-
return {
|
|
50595
|
-
width: 1,
|
|
50596
|
-
height: 1
|
|
50597
|
-
};
|
|
50598
|
-
}
|
|
50599
|
-
/** Read width/height from JPEG SOF marker. */
|
|
50600
|
-
function parseJpegDimensions(data) {
|
|
50601
|
-
let offset = 2;
|
|
50602
|
-
while (offset < data.length - 1) {
|
|
50603
|
-
while (offset < data.length && data[offset] === 255 && data[offset + 1] === 255) offset++;
|
|
50604
|
-
if (offset >= data.length - 1 || data[offset] !== 255) break;
|
|
50605
|
-
const marker = data[offset + 1];
|
|
50606
|
-
if (marker >= 192 && marker <= 207 && marker !== 196 && marker !== 200 && marker !== 204 && offset + 8 < data.length) return {
|
|
50607
|
-
width: data[offset + 7] << 8 | data[offset + 8],
|
|
50608
|
-
height: data[offset + 5] << 8 | data[offset + 6]
|
|
50609
|
-
};
|
|
50610
|
-
if (offset + 3 >= data.length) break;
|
|
50611
|
-
const segLen = data[offset + 2] << 8 | data[offset + 3];
|
|
50612
|
-
offset += 2 + segLen;
|
|
50613
|
-
}
|
|
50614
|
-
return {
|
|
50615
|
-
width: 1,
|
|
50616
|
-
height: 1
|
|
50617
|
-
};
|
|
50618
|
-
}
|
|
50619
50959
|
/**
|
|
50620
50960
|
* Resolve the center position for a watermark on a given page.
|
|
50621
50961
|
*/
|
|
@@ -50791,6 +51131,7 @@ onmessage = async (ev) => {
|
|
|
50791
51131
|
cellGrid.set(`${ri}:${gci}`, layoutCell);
|
|
50792
51132
|
}
|
|
50793
51133
|
}
|
|
51134
|
+
resolveSharedBorders(cellGrid, rowPage.length, colGroup.length);
|
|
50794
51135
|
computeTextOverflows(cellGrid, rowPage, colGroup, visibleRows, visibleCols, groupColWidths, mergeMap, fontManager);
|
|
50795
51136
|
return {
|
|
50796
51137
|
pageNumber: currentPageCount + 1,
|
|
@@ -50930,7 +51271,9 @@ onmessage = async (ev) => {
|
|
|
50930
51271
|
const fontSize = getCellFontSize(cell);
|
|
50931
51272
|
const wrapLineCount = countWrapLines(cell, fontSize, scaleFactor, sheet, fontManager, options);
|
|
50932
51273
|
const lineHeight = fontSize * LINE_HEIGHT_FACTOR;
|
|
50933
|
-
const
|
|
51274
|
+
const borderTop = cell.style?.border?.top?.style ? borderStyleToLineWidth(cell.style.border.top.style) / 2 : 0;
|
|
51275
|
+
const borderBottom = cell.style?.border?.bottom?.style ? borderStyleToLineWidth(cell.style.border.bottom.style) / 2 : 0;
|
|
51276
|
+
const neededHeight = fontSize + (wrapLineCount - 1) * lineHeight + (2 + borderTop + borderBottom) * 2;
|
|
50934
51277
|
if (neededHeight > height) height = neededHeight;
|
|
50935
51278
|
}
|
|
50936
51279
|
}
|
|
@@ -50968,7 +51311,10 @@ onmessage = async (ev) => {
|
|
|
50968
51311
|
const lineCount = Math.max(1, (text.match(/\n/g) ?? []).length + 1);
|
|
50969
51312
|
if (!cell.style?.alignment?.wrapText || text.length === 0) return lineCount;
|
|
50970
51313
|
const scaledColPts = ((sheet.columns.get(cell.col)?.width ?? DEFAULT_COLUMN_WIDTH) * 7 + 5) * PX_TO_PT * scaleFactor;
|
|
50971
|
-
const
|
|
51314
|
+
const indent = cell.style.alignment.indent ?? 0;
|
|
51315
|
+
const borderLeft = cell.style?.border?.left?.style ? borderStyleToLineWidth(cell.style.border.left.style) / 2 : 0;
|
|
51316
|
+
const borderRight = cell.style?.border?.right?.style ? borderStyleToLineWidth(cell.style.border.right.style) / 2 : 0;
|
|
51317
|
+
const padding = 3 + borderLeft + (3 + borderRight) + indent * 10;
|
|
50972
51318
|
const effectiveWidth = Math.max(scaledColPts - padding, 1);
|
|
50973
51319
|
const scaledFontSize = fontSize * scaleFactor;
|
|
50974
51320
|
const fontProps = extractFontProperties(cell.style.font, options.defaultFontFamily, options.defaultFontSize);
|
|
@@ -51109,6 +51455,7 @@ onmessage = async (ev) => {
|
|
|
51109
51455
|
fontManager.ensureFont(pdfFontName);
|
|
51110
51456
|
}
|
|
51111
51457
|
const richText = buildRichTextRuns(cell, options, fontManager, scaleFactor);
|
|
51458
|
+
const borders = excelBordersToPdf(style.border);
|
|
51112
51459
|
return {
|
|
51113
51460
|
text,
|
|
51114
51461
|
rect: {
|
|
@@ -51128,7 +51475,13 @@ onmessage = async (ev) => {
|
|
|
51128
51475
|
horizontalAlign: resolveHorizontalAlign(style.alignment, cell?.type, cell?.result),
|
|
51129
51476
|
verticalAlign: excelVAlignToPdf(style.alignment),
|
|
51130
51477
|
wrapText: style.alignment?.wrapText ?? false,
|
|
51131
|
-
borders
|
|
51478
|
+
borders,
|
|
51479
|
+
borderInsets: {
|
|
51480
|
+
top: (borders.top?.width ?? 0) / 2,
|
|
51481
|
+
right: (borders.right?.width ?? 0) / 2,
|
|
51482
|
+
bottom: (borders.bottom?.width ?? 0) / 2,
|
|
51483
|
+
left: (borders.left?.width ?? 0) / 2
|
|
51484
|
+
},
|
|
51132
51485
|
colSpan,
|
|
51133
51486
|
rowSpan,
|
|
51134
51487
|
hyperlink: cell?.hyperlink ?? null,
|
|
@@ -51139,6 +51492,64 @@ onmessage = async (ev) => {
|
|
|
51139
51492
|
};
|
|
51140
51493
|
}
|
|
51141
51494
|
/**
|
|
51495
|
+
* Border precedence weight.
|
|
51496
|
+
*
|
|
51497
|
+
* When two adjacent cells both declare a border on a shared edge the winning
|
|
51498
|
+
* border is chosen by: 1. thicker wins, 2. solid beats dashed,
|
|
51499
|
+
* 3. double beats single, 4. darker colour wins (tie-break).
|
|
51500
|
+
*
|
|
51501
|
+
* Returns a numeric score – higher score wins.
|
|
51502
|
+
*/
|
|
51503
|
+
function borderPrecedence(b) {
|
|
51504
|
+
let score = b.width * 1e3;
|
|
51505
|
+
if (b.dashPattern.length === 0) score += 100;
|
|
51506
|
+
if (b.isDouble) score += 50;
|
|
51507
|
+
const brightness = b.color.r + b.color.g + b.color.b;
|
|
51508
|
+
score += (3 - brightness) * 10;
|
|
51509
|
+
return score;
|
|
51510
|
+
}
|
|
51511
|
+
/**
|
|
51512
|
+
* Resolve shared borders between adjacent cells.
|
|
51513
|
+
*
|
|
51514
|
+
* For each shared edge, determine the winning border (by precedence), then:
|
|
51515
|
+
* - The cell that "owns" the winning border keeps it in `borders` for drawing.
|
|
51516
|
+
* - The losing cell has that border side set to `null` (it won't draw).
|
|
51517
|
+
* - Both cells' `borderInsets` are updated to reflect the winning border's
|
|
51518
|
+
* half-width, so text padding accounts for the line that is actually there.
|
|
51519
|
+
*/
|
|
51520
|
+
function resolveSharedBorders(cellGrid, rowCount, colCount) {
|
|
51521
|
+
for (let ri = 0; ri < rowCount; ri++) for (let gci = 0; gci < colCount; gci++) {
|
|
51522
|
+
const cell = cellGrid.get(`${ri}:${gci}`);
|
|
51523
|
+
if (!cell) continue;
|
|
51524
|
+
if (cell.borders.right) {
|
|
51525
|
+
const rightNeighbor = cellGrid.get(`${ri}:${gci + 1}`);
|
|
51526
|
+
if (rightNeighbor?.borders.left) {
|
|
51527
|
+
const myScore = borderPrecedence(cell.borders.right);
|
|
51528
|
+
if (borderPrecedence(rightNeighbor.borders.left) > myScore) {
|
|
51529
|
+
cell.borderInsets.right = rightNeighbor.borders.left.width / 2;
|
|
51530
|
+
cell.borders.right = null;
|
|
51531
|
+
} else {
|
|
51532
|
+
rightNeighbor.borderInsets.left = cell.borders.right.width / 2;
|
|
51533
|
+
rightNeighbor.borders.left = null;
|
|
51534
|
+
}
|
|
51535
|
+
}
|
|
51536
|
+
}
|
|
51537
|
+
if (cell.borders.bottom) {
|
|
51538
|
+
const belowNeighbor = cellGrid.get(`${ri + 1}:${gci}`);
|
|
51539
|
+
if (belowNeighbor?.borders.top) {
|
|
51540
|
+
const myScore = borderPrecedence(cell.borders.bottom);
|
|
51541
|
+
if (borderPrecedence(belowNeighbor.borders.top) > myScore) {
|
|
51542
|
+
cell.borderInsets.bottom = belowNeighbor.borders.top.width / 2;
|
|
51543
|
+
cell.borders.bottom = null;
|
|
51544
|
+
} else {
|
|
51545
|
+
belowNeighbor.borderInsets.top = cell.borders.bottom.width / 2;
|
|
51546
|
+
belowNeighbor.borders.top = null;
|
|
51547
|
+
}
|
|
51548
|
+
}
|
|
51549
|
+
}
|
|
51550
|
+
}
|
|
51551
|
+
}
|
|
51552
|
+
/**
|
|
51142
51553
|
* Assign pre-collected images to the pages that contain their top-left anchor.
|
|
51143
51554
|
*/
|
|
51144
51555
|
function assignImagesToPages(images, layoutPages, scaleFactor) {
|
|
@@ -51199,7 +51610,10 @@ onmessage = async (ev) => {
|
|
|
51199
51610
|
const rightCellData = sheet.rows.get(wsRowNumber)?.cells.get(rightCol);
|
|
51200
51611
|
if (rightCellData?.style?.border?.right) {
|
|
51201
51612
|
const converted = excelBordersToPdf({ right: rightCellData.style.border.right });
|
|
51202
|
-
if (converted.right)
|
|
51613
|
+
if (converted.right) {
|
|
51614
|
+
layoutCell.borders.right = converted.right;
|
|
51615
|
+
layoutCell.borderInsets.right = converted.right.width / 2;
|
|
51616
|
+
}
|
|
51203
51617
|
}
|
|
51204
51618
|
}
|
|
51205
51619
|
if (mergeInfo.rowSpan > 1) {
|
|
@@ -51207,7 +51621,10 @@ onmessage = async (ev) => {
|
|
|
51207
51621
|
const bottomCellData = sheet.rows.get(bottomRowNum)?.cells.get(wsColNumber);
|
|
51208
51622
|
if (bottomCellData?.style?.border?.bottom) {
|
|
51209
51623
|
const converted = excelBordersToPdf({ bottom: bottomCellData.style.border.bottom });
|
|
51210
|
-
if (converted.bottom)
|
|
51624
|
+
if (converted.bottom) {
|
|
51625
|
+
layoutCell.borders.bottom = converted.bottom;
|
|
51626
|
+
layoutCell.borderInsets.bottom = converted.bottom.width / 2;
|
|
51627
|
+
}
|
|
51211
51628
|
}
|
|
51212
51629
|
}
|
|
51213
51630
|
}
|
|
@@ -51222,7 +51639,7 @@ onmessage = async (ev) => {
|
|
|
51222
51639
|
if (!cell || cell.wrapText || cell.colSpan > 1 || !cell.text || cell.richText || typeof cell.textRotation === "number" && cell.textRotation !== 0 || cell.textRotation === "vertical") continue;
|
|
51223
51640
|
const resourceName = fontManager.hasEmbeddedFont() ? fontManager.getEmbeddedResourceName() : fontManager.ensureFont(resolvePdfFontName(cell.fontFamily, cell.bold, cell.italic));
|
|
51224
51641
|
const textWidth = fontManager.measureText(cell.text, resourceName, cell.fontSize);
|
|
51225
|
-
const cellContentWidth = cell.rect.width -
|
|
51642
|
+
const cellContentWidth = cell.rect.width - (3 + cell.borderInsets.left) - (3 + cell.borderInsets.right);
|
|
51226
51643
|
if (textWidth <= cellContentWidth) continue;
|
|
51227
51644
|
const overflowNeeded = textWidth - cellContentWidth;
|
|
51228
51645
|
let overflowAvailable = 0;
|
|
@@ -51267,216 +51684,6 @@ onmessage = async (ev) => {
|
|
|
51267
51684
|
});
|
|
51268
51685
|
}
|
|
51269
51686
|
//#endregion
|
|
51270
|
-
//#region src/modules/pdf/render/png-decoder.ts
|
|
51271
|
-
/**
|
|
51272
|
-
* Minimal PNG decoder for PDF image embedding.
|
|
51273
|
-
*
|
|
51274
|
-
* Extracts raw RGB pixel data from a PNG file. Handles:
|
|
51275
|
-
* - Color types: RGB (2), RGBA (6), Grayscale (0), Grayscale+Alpha (4), Palette (3)
|
|
51276
|
-
* - Bit depth: 8 (most common)
|
|
51277
|
-
* - Interlacing: non-interlaced only (Adam7 interlacing is not supported)
|
|
51278
|
-
* - All 5 PNG filter types (None, Sub, Up, Average, Paeth)
|
|
51279
|
-
*
|
|
51280
|
-
* For RGBA images, produces separate RGB pixels and an alpha mask (for PDF SMask).
|
|
51281
|
-
*/
|
|
51282
|
-
/**
|
|
51283
|
-
* Maximum allowed pixel count for PNG decoding (default: 100 million pixels).
|
|
51284
|
-
* A 10000x10000 RGBA image at 100M pixels would need ~400MB for raw data alone.
|
|
51285
|
-
* This limit prevents memory exhaustion from malicious PNG files with
|
|
51286
|
-
* excessively large declared dimensions.
|
|
51287
|
-
*/
|
|
51288
|
-
const MAX_PNG_PIXELS = 1e8;
|
|
51289
|
-
/**
|
|
51290
|
-
* Decode a PNG file to raw RGB pixels for PDF embedding.
|
|
51291
|
-
* @throws on invalid or unsupported PNG data
|
|
51292
|
-
*/
|
|
51293
|
-
function decodePng(data) {
|
|
51294
|
-
if (data.length < 8 || data[0] !== 137 || data[1] !== 80 || data[2] !== 78 || data[3] !== 71) throw new Error("Invalid PNG signature");
|
|
51295
|
-
let offset = 8;
|
|
51296
|
-
let width = 0;
|
|
51297
|
-
let height = 0;
|
|
51298
|
-
let bitDepth = 0;
|
|
51299
|
-
let colorType = 0;
|
|
51300
|
-
const idatChunks = [];
|
|
51301
|
-
let palette = null;
|
|
51302
|
-
let trns = null;
|
|
51303
|
-
while (offset + 8 <= data.length) {
|
|
51304
|
-
const chunkLen = new DataView(data.buffer, data.byteOffset, data.byteLength).getUint32(offset, false);
|
|
51305
|
-
const chunkType = String.fromCharCode(data[offset + 4], data[offset + 5], data[offset + 6], data[offset + 7]);
|
|
51306
|
-
const chunkData = data.subarray(offset + 8, offset + 8 + chunkLen);
|
|
51307
|
-
offset += 8 + chunkLen + 4;
|
|
51308
|
-
switch (chunkType) {
|
|
51309
|
-
case "IHDR": {
|
|
51310
|
-
const hdr = new DataView(chunkData.buffer, chunkData.byteOffset, chunkData.byteLength);
|
|
51311
|
-
width = hdr.getUint32(0, false);
|
|
51312
|
-
height = hdr.getUint32(4, false);
|
|
51313
|
-
bitDepth = chunkData[8];
|
|
51314
|
-
colorType = chunkData[9];
|
|
51315
|
-
if (chunkData[12] !== 0) throw new Error("Interlaced PNG is not supported");
|
|
51316
|
-
if (bitDepth !== 8) throw new Error(`Unsupported PNG bit depth: ${bitDepth}. Only 8-bit PNGs are supported.`);
|
|
51317
|
-
if (width === 0 || height === 0) throw new Error(`Invalid PNG dimensions: ${width}x${height}`);
|
|
51318
|
-
const totalPixels = width * height;
|
|
51319
|
-
if (totalPixels > MAX_PNG_PIXELS) throw new Error(`PNG dimensions too large: ${width}x${height} (${totalPixels} pixels). Maximum allowed: ${MAX_PNG_PIXELS} pixels.`);
|
|
51320
|
-
break;
|
|
51321
|
-
}
|
|
51322
|
-
case "PLTE":
|
|
51323
|
-
palette = new Uint8Array(chunkData);
|
|
51324
|
-
break;
|
|
51325
|
-
case "tRNS":
|
|
51326
|
-
trns = new Uint8Array(chunkData);
|
|
51327
|
-
break;
|
|
51328
|
-
case "IDAT":
|
|
51329
|
-
idatChunks.push(chunkData);
|
|
51330
|
-
break;
|
|
51331
|
-
case "IEND": break;
|
|
51332
|
-
}
|
|
51333
|
-
}
|
|
51334
|
-
if (width === 0 || height === 0) throw new Error("PNG missing IHDR chunk");
|
|
51335
|
-
const compressedData = concatUint8Arrays(idatChunks);
|
|
51336
|
-
let rawCompressed;
|
|
51337
|
-
if (compressedData.length > 6 && (compressedData[0] & 15) === 8) rawCompressed = compressedData.subarray(2, compressedData.length - 4);
|
|
51338
|
-
else rawCompressed = compressedData;
|
|
51339
|
-
const rawData = decompressSync(rawCompressed);
|
|
51340
|
-
const channels = getChannelCount(colorType);
|
|
51341
|
-
const bytesPerPixel = Math.max(1, channels * bitDepth / 8);
|
|
51342
|
-
const scanlineLen = Math.ceil(width * channels * bitDepth / 8);
|
|
51343
|
-
return toRgb(applyFilters(rawData, width, height, scanlineLen, bytesPerPixel), width, height, colorType, bitDepth, palette, trns);
|
|
51344
|
-
}
|
|
51345
|
-
function applyFilters(data, _width, height, scanlineLen, bytesPerPixel) {
|
|
51346
|
-
const result = new Uint8Array(height * scanlineLen);
|
|
51347
|
-
const bpp = Math.max(1, Math.floor(bytesPerPixel));
|
|
51348
|
-
let srcOffset = 0;
|
|
51349
|
-
for (let y = 0; y < height; y++) {
|
|
51350
|
-
const filterType = data[srcOffset++];
|
|
51351
|
-
const dstOffset = y * scanlineLen;
|
|
51352
|
-
const prevRow = y > 0 ? (y - 1) * scanlineLen : -1;
|
|
51353
|
-
for (let x = 0; x < scanlineLen; x++) {
|
|
51354
|
-
const raw = data[srcOffset++] ?? 0;
|
|
51355
|
-
const a = x >= bpp ? result[dstOffset + x - bpp] : 0;
|
|
51356
|
-
const b = prevRow >= 0 ? result[prevRow + x] : 0;
|
|
51357
|
-
const c = prevRow >= 0 && x >= bpp ? result[prevRow + x - bpp] : 0;
|
|
51358
|
-
switch (filterType) {
|
|
51359
|
-
case 0:
|
|
51360
|
-
result[dstOffset + x] = raw;
|
|
51361
|
-
break;
|
|
51362
|
-
case 1:
|
|
51363
|
-
result[dstOffset + x] = raw + a & 255;
|
|
51364
|
-
break;
|
|
51365
|
-
case 2:
|
|
51366
|
-
result[dstOffset + x] = raw + b & 255;
|
|
51367
|
-
break;
|
|
51368
|
-
case 3:
|
|
51369
|
-
result[dstOffset + x] = raw + Math.floor((a + b) / 2) & 255;
|
|
51370
|
-
break;
|
|
51371
|
-
case 4:
|
|
51372
|
-
result[dstOffset + x] = raw + paethPredictor(a, b, c) & 255;
|
|
51373
|
-
break;
|
|
51374
|
-
default: result[dstOffset + x] = raw;
|
|
51375
|
-
}
|
|
51376
|
-
}
|
|
51377
|
-
}
|
|
51378
|
-
return result;
|
|
51379
|
-
}
|
|
51380
|
-
function paethPredictor(a, b, c) {
|
|
51381
|
-
const p = a + b - c;
|
|
51382
|
-
const pa = Math.abs(p - a);
|
|
51383
|
-
const pb = Math.abs(p - b);
|
|
51384
|
-
const pc = Math.abs(p - c);
|
|
51385
|
-
if (pa <= pb && pa <= pc) return a;
|
|
51386
|
-
if (pb <= pc) return b;
|
|
51387
|
-
return c;
|
|
51388
|
-
}
|
|
51389
|
-
function getChannelCount(colorType) {
|
|
51390
|
-
switch (colorType) {
|
|
51391
|
-
case 0: return 1;
|
|
51392
|
-
case 2: return 3;
|
|
51393
|
-
case 3: return 1;
|
|
51394
|
-
case 4: return 2;
|
|
51395
|
-
case 6: return 4;
|
|
51396
|
-
default: return 3;
|
|
51397
|
-
}
|
|
51398
|
-
}
|
|
51399
|
-
function toRgb(data, width, height, colorType, _bitDepth, palette, trns) {
|
|
51400
|
-
const totalPixels = width * height;
|
|
51401
|
-
const pixels = new Uint8Array(totalPixels * 3);
|
|
51402
|
-
let alpha = null;
|
|
51403
|
-
switch (colorType) {
|
|
51404
|
-
case 2:
|
|
51405
|
-
pixels.set(data.subarray(0, totalPixels * 3));
|
|
51406
|
-
if (trns && trns.length >= 6) {
|
|
51407
|
-
const trR = trns[1];
|
|
51408
|
-
const trG = trns[3];
|
|
51409
|
-
const trB = trns[5];
|
|
51410
|
-
alpha = new Uint8Array(totalPixels);
|
|
51411
|
-
alpha.fill(255);
|
|
51412
|
-
for (let i = 0; i < totalPixels; i++) if (data[i * 3] === trR && data[i * 3 + 1] === trG && data[i * 3 + 2] === trB) alpha[i] = 0;
|
|
51413
|
-
}
|
|
51414
|
-
break;
|
|
51415
|
-
case 6:
|
|
51416
|
-
alpha = new Uint8Array(totalPixels);
|
|
51417
|
-
for (let i = 0; i < totalPixels; i++) {
|
|
51418
|
-
pixels[i * 3] = data[i * 4];
|
|
51419
|
-
pixels[i * 3 + 1] = data[i * 4 + 1];
|
|
51420
|
-
pixels[i * 3 + 2] = data[i * 4 + 2];
|
|
51421
|
-
alpha[i] = data[i * 4 + 3];
|
|
51422
|
-
}
|
|
51423
|
-
break;
|
|
51424
|
-
case 0: {
|
|
51425
|
-
let trGray = -1;
|
|
51426
|
-
if (trns && trns.length >= 2) trGray = trns[1];
|
|
51427
|
-
if (trGray >= 0) {
|
|
51428
|
-
alpha = new Uint8Array(totalPixels);
|
|
51429
|
-
alpha.fill(255);
|
|
51430
|
-
}
|
|
51431
|
-
for (let i = 0; i < totalPixels; i++) {
|
|
51432
|
-
const g = data[i];
|
|
51433
|
-
pixels[i * 3] = g;
|
|
51434
|
-
pixels[i * 3 + 1] = g;
|
|
51435
|
-
pixels[i * 3 + 2] = g;
|
|
51436
|
-
if (alpha && g === trGray) alpha[i] = 0;
|
|
51437
|
-
}
|
|
51438
|
-
break;
|
|
51439
|
-
}
|
|
51440
|
-
case 4:
|
|
51441
|
-
alpha = new Uint8Array(totalPixels);
|
|
51442
|
-
for (let i = 0; i < totalPixels; i++) {
|
|
51443
|
-
const g = data[i * 2];
|
|
51444
|
-
pixels[i * 3] = g;
|
|
51445
|
-
pixels[i * 3 + 1] = g;
|
|
51446
|
-
pixels[i * 3 + 2] = g;
|
|
51447
|
-
alpha[i] = data[i * 2 + 1];
|
|
51448
|
-
}
|
|
51449
|
-
break;
|
|
51450
|
-
case 3:
|
|
51451
|
-
if (!palette) throw new Error("PNG palette color type (3) but missing PLTE chunk");
|
|
51452
|
-
if (trns && trns.length > 0) alpha = new Uint8Array(totalPixels);
|
|
51453
|
-
for (let i = 0; i < totalPixels; i++) {
|
|
51454
|
-
const idx = data[i];
|
|
51455
|
-
pixels[i * 3] = palette[idx * 3] ?? 0;
|
|
51456
|
-
pixels[i * 3 + 1] = palette[idx * 3 + 1] ?? 0;
|
|
51457
|
-
pixels[i * 3 + 2] = palette[idx * 3 + 2] ?? 0;
|
|
51458
|
-
if (alpha) alpha[i] = idx < trns.length ? trns[idx] : 255;
|
|
51459
|
-
}
|
|
51460
|
-
break;
|
|
51461
|
-
default: throw new Error(`Unsupported PNG color type: ${colorType}`);
|
|
51462
|
-
}
|
|
51463
|
-
if (alpha) {
|
|
51464
|
-
let fullyOpaque = true;
|
|
51465
|
-
for (let i = 0; i < alpha.length; i++) if (alpha[i] !== 255) {
|
|
51466
|
-
fullyOpaque = false;
|
|
51467
|
-
break;
|
|
51468
|
-
}
|
|
51469
|
-
if (fullyOpaque) alpha = null;
|
|
51470
|
-
}
|
|
51471
|
-
return {
|
|
51472
|
-
width,
|
|
51473
|
-
height,
|
|
51474
|
-
pixels,
|
|
51475
|
-
alpha,
|
|
51476
|
-
bitsPerComponent: 8
|
|
51477
|
-
};
|
|
51478
|
-
}
|
|
51479
|
-
//#endregion
|
|
51480
51687
|
//#region src/modules/pdf/render/pdf-exporter.ts
|
|
51481
51688
|
/**
|
|
51482
51689
|
* PDF Exporter - Main orchestrator for PDF document generation.
|
|
@@ -51822,39 +52029,6 @@ onmessage = async (ev) => {
|
|
|
51822
52029
|
}
|
|
51823
52030
|
return true;
|
|
51824
52031
|
}
|
|
51825
|
-
/**
|
|
51826
|
-
* Write a JPEG or PNG image as a PDF XObject Image.
|
|
51827
|
-
*/
|
|
51828
|
-
function writeImageXObject(writer, data, format) {
|
|
51829
|
-
if (format === "png") return writePngImageXObject(writer, data);
|
|
51830
|
-
return writeJpegImageXObject(writer, data);
|
|
51831
|
-
}
|
|
51832
|
-
/**
|
|
51833
|
-
* Write a JPEG image using DCTDecode (raw JPEG data embedded directly).
|
|
51834
|
-
*/
|
|
51835
|
-
function writeJpegImageXObject(writer, data) {
|
|
51836
|
-
const objNum = writer.allocObject();
|
|
51837
|
-
const dims = parseImageDimensions(data, "jpeg");
|
|
51838
|
-
const dict = new PdfDict().set("Type", "/XObject").set("Subtype", "/Image").set("Width", pdfNumber(dims.width)).set("Height", pdfNumber(dims.height)).set("ColorSpace", "/DeviceRGB").set("BitsPerComponent", "8").set("Filter", "/DCTDecode");
|
|
51839
|
-
writer.addStreamObject(objNum, dict, data);
|
|
51840
|
-
return objNum;
|
|
51841
|
-
}
|
|
51842
|
-
/**
|
|
51843
|
-
* Write a PNG image: decode to raw RGB, create SMask for alpha if needed.
|
|
51844
|
-
*/
|
|
51845
|
-
function writePngImageXObject(writer, data) {
|
|
51846
|
-
const png = decodePng(data);
|
|
51847
|
-
const objNum = writer.allocObject();
|
|
51848
|
-
const dict = new PdfDict().set("Type", "/XObject").set("Subtype", "/Image").set("Width", pdfNumber(png.width)).set("Height", pdfNumber(png.height)).set("ColorSpace", "/DeviceRGB").set("BitsPerComponent", pdfNumber(png.bitsPerComponent));
|
|
51849
|
-
if (png.alpha) {
|
|
51850
|
-
const smaskObjNum = writer.allocObject();
|
|
51851
|
-
const smaskDict = new PdfDict().set("Type", "/XObject").set("Subtype", "/Image").set("Width", pdfNumber(png.width)).set("Height", pdfNumber(png.height)).set("ColorSpace", "/DeviceGray").set("BitsPerComponent", "8");
|
|
51852
|
-
writer.addStreamObject(smaskObjNum, smaskDict, png.alpha);
|
|
51853
|
-
dict.set("SMask", pdfRef(smaskObjNum));
|
|
51854
|
-
}
|
|
51855
|
-
writer.addStreamObject(objNum, dict, png.pixels);
|
|
51856
|
-
return objNum;
|
|
51857
|
-
}
|
|
51858
52032
|
//#endregion
|
|
51859
52033
|
//#region src/modules/pdf/pdf.ts
|
|
51860
52034
|
/**
|