@cj-tech-master/excelts 8.0.0 → 8.1.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.
Files changed (106) hide show
  1. package/README.md +14 -1
  2. package/README_zh.md +6 -0
  3. package/dist/browser/modules/archive/zip/stream.d.ts +4 -0
  4. package/dist/browser/modules/archive/zip/stream.js +53 -0
  5. package/dist/browser/modules/pdf/core/crypto.d.ts +65 -0
  6. package/dist/browser/modules/pdf/core/crypto.js +637 -0
  7. package/dist/browser/modules/pdf/core/encryption.d.ts +23 -20
  8. package/dist/browser/modules/pdf/core/encryption.js +88 -261
  9. package/dist/browser/modules/pdf/core/pdf-writer.d.ts +6 -4
  10. package/dist/browser/modules/pdf/core/pdf-writer.js +19 -10
  11. package/dist/browser/modules/pdf/index.d.ts +23 -2
  12. package/dist/browser/modules/pdf/index.js +21 -3
  13. package/dist/browser/modules/pdf/reader/annotation-extractor.d.ts +63 -0
  14. package/dist/browser/modules/pdf/reader/annotation-extractor.js +155 -0
  15. package/dist/browser/modules/pdf/reader/cmap-parser.d.ts +70 -0
  16. package/dist/browser/modules/pdf/reader/cmap-parser.js +321 -0
  17. package/dist/browser/modules/pdf/reader/content-interpreter.d.ts +57 -0
  18. package/dist/browser/modules/pdf/reader/content-interpreter.js +715 -0
  19. package/dist/browser/modules/pdf/reader/font-decoder.d.ts +58 -0
  20. package/dist/browser/modules/pdf/reader/font-decoder.js +1513 -0
  21. package/dist/browser/modules/pdf/reader/form-extractor.d.ts +48 -0
  22. package/dist/browser/modules/pdf/reader/form-extractor.js +355 -0
  23. package/dist/browser/modules/pdf/reader/image-extractor.d.ts +55 -0
  24. package/dist/browser/modules/pdf/reader/image-extractor.js +220 -0
  25. package/dist/browser/modules/pdf/reader/metadata-reader.d.ts +56 -0
  26. package/dist/browser/modules/pdf/reader/metadata-reader.js +275 -0
  27. package/dist/browser/modules/pdf/reader/pdf-decrypt.d.ts +26 -0
  28. package/dist/browser/modules/pdf/reader/pdf-decrypt.js +443 -0
  29. package/dist/browser/modules/pdf/reader/pdf-document.d.ts +191 -0
  30. package/dist/browser/modules/pdf/reader/pdf-document.js +818 -0
  31. package/dist/browser/modules/pdf/reader/pdf-parser.d.ts +65 -0
  32. package/dist/browser/modules/pdf/reader/pdf-parser.js +285 -0
  33. package/dist/browser/modules/pdf/reader/pdf-reader.d.ts +143 -0
  34. package/dist/browser/modules/pdf/reader/pdf-reader.js +200 -0
  35. package/dist/browser/modules/pdf/reader/pdf-tokenizer.d.ts +101 -0
  36. package/dist/browser/modules/pdf/reader/pdf-tokenizer.js +543 -0
  37. package/dist/browser/modules/pdf/reader/reader-utils.d.ts +15 -0
  38. package/dist/browser/modules/pdf/reader/reader-utils.js +27 -0
  39. package/dist/browser/modules/pdf/reader/stream-filters.d.ts +20 -0
  40. package/dist/browser/modules/pdf/reader/stream-filters.js +456 -0
  41. package/dist/browser/modules/pdf/reader/text-reconstruction.d.ts +44 -0
  42. package/dist/browser/modules/pdf/reader/text-reconstruction.js +463 -0
  43. package/dist/cjs/modules/archive/zip/stream.js +53 -0
  44. package/dist/cjs/modules/pdf/core/crypto.js +649 -0
  45. package/dist/cjs/modules/pdf/core/encryption.js +88 -263
  46. package/dist/cjs/modules/pdf/core/pdf-writer.js +19 -10
  47. package/dist/cjs/modules/pdf/index.js +23 -4
  48. package/dist/cjs/modules/pdf/reader/annotation-extractor.js +158 -0
  49. package/dist/cjs/modules/pdf/reader/cmap-parser.js +326 -0
  50. package/dist/cjs/modules/pdf/reader/content-interpreter.js +718 -0
  51. package/dist/cjs/modules/pdf/reader/font-decoder.js +1518 -0
  52. package/dist/cjs/modules/pdf/reader/form-extractor.js +358 -0
  53. package/dist/cjs/modules/pdf/reader/image-extractor.js +223 -0
  54. package/dist/cjs/modules/pdf/reader/metadata-reader.js +278 -0
  55. package/dist/cjs/modules/pdf/reader/pdf-decrypt.js +447 -0
  56. package/dist/cjs/modules/pdf/reader/pdf-document.js +822 -0
  57. package/dist/cjs/modules/pdf/reader/pdf-parser.js +301 -0
  58. package/dist/cjs/modules/pdf/reader/pdf-reader.js +203 -0
  59. package/dist/cjs/modules/pdf/reader/pdf-tokenizer.js +517 -0
  60. package/dist/cjs/modules/pdf/reader/reader-utils.js +30 -0
  61. package/dist/cjs/modules/pdf/reader/stream-filters.js +459 -0
  62. package/dist/cjs/modules/pdf/reader/text-reconstruction.js +467 -0
  63. package/dist/esm/modules/archive/zip/stream.js +53 -0
  64. package/dist/esm/modules/pdf/core/crypto.js +637 -0
  65. package/dist/esm/modules/pdf/core/encryption.js +88 -261
  66. package/dist/esm/modules/pdf/core/pdf-writer.js +19 -10
  67. package/dist/esm/modules/pdf/index.js +21 -3
  68. package/dist/esm/modules/pdf/reader/annotation-extractor.js +155 -0
  69. package/dist/esm/modules/pdf/reader/cmap-parser.js +321 -0
  70. package/dist/esm/modules/pdf/reader/content-interpreter.js +715 -0
  71. package/dist/esm/modules/pdf/reader/font-decoder.js +1513 -0
  72. package/dist/esm/modules/pdf/reader/form-extractor.js +355 -0
  73. package/dist/esm/modules/pdf/reader/image-extractor.js +220 -0
  74. package/dist/esm/modules/pdf/reader/metadata-reader.js +275 -0
  75. package/dist/esm/modules/pdf/reader/pdf-decrypt.js +443 -0
  76. package/dist/esm/modules/pdf/reader/pdf-document.js +818 -0
  77. package/dist/esm/modules/pdf/reader/pdf-parser.js +285 -0
  78. package/dist/esm/modules/pdf/reader/pdf-reader.js +200 -0
  79. package/dist/esm/modules/pdf/reader/pdf-tokenizer.js +543 -0
  80. package/dist/esm/modules/pdf/reader/reader-utils.js +27 -0
  81. package/dist/esm/modules/pdf/reader/stream-filters.js +456 -0
  82. package/dist/esm/modules/pdf/reader/text-reconstruction.js +463 -0
  83. package/dist/iife/excelts.iife.js +703 -267
  84. package/dist/iife/excelts.iife.js.map +1 -1
  85. package/dist/iife/excelts.iife.min.js +35 -35
  86. package/dist/types/modules/archive/zip/stream.d.ts +4 -0
  87. package/dist/types/modules/pdf/core/crypto.d.ts +65 -0
  88. package/dist/types/modules/pdf/core/encryption.d.ts +23 -20
  89. package/dist/types/modules/pdf/core/pdf-writer.d.ts +6 -4
  90. package/dist/types/modules/pdf/index.d.ts +23 -2
  91. package/dist/types/modules/pdf/reader/annotation-extractor.d.ts +63 -0
  92. package/dist/types/modules/pdf/reader/cmap-parser.d.ts +70 -0
  93. package/dist/types/modules/pdf/reader/content-interpreter.d.ts +57 -0
  94. package/dist/types/modules/pdf/reader/font-decoder.d.ts +58 -0
  95. package/dist/types/modules/pdf/reader/form-extractor.d.ts +48 -0
  96. package/dist/types/modules/pdf/reader/image-extractor.d.ts +55 -0
  97. package/dist/types/modules/pdf/reader/metadata-reader.d.ts +56 -0
  98. package/dist/types/modules/pdf/reader/pdf-decrypt.d.ts +26 -0
  99. package/dist/types/modules/pdf/reader/pdf-document.d.ts +191 -0
  100. package/dist/types/modules/pdf/reader/pdf-parser.d.ts +65 -0
  101. package/dist/types/modules/pdf/reader/pdf-reader.d.ts +143 -0
  102. package/dist/types/modules/pdf/reader/pdf-tokenizer.d.ts +101 -0
  103. package/dist/types/modules/pdf/reader/reader-utils.d.ts +15 -0
  104. package/dist/types/modules/pdf/reader/stream-filters.d.ts +20 -0
  105. package/dist/types/modules/pdf/reader/text-reconstruction.d.ts +44 -0
  106. package/package.json +1 -1
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * @cj-tech-master/excelts v8.0.0
2
+ * @cj-tech-master/excelts v8.1.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
@@ -27555,7 +27555,7 @@ self.onmessage = async function(event) {
27555
27555
  /**
27556
27556
  * Generate random bytes.
27557
27557
  */
27558
- function randomBytes(length) {
27558
+ function randomBytes$1(length) {
27559
27559
  return getRandomValues(new Uint8Array(length));
27560
27560
  }
27561
27561
  /**
@@ -27709,7 +27709,7 @@ self.onmessage = async function(event) {
27709
27709
  */
27710
27710
  async function aesEncrypt(data, password, keyStrength) {
27711
27711
  const saltLen = AES_SALT_LENGTH[keyStrength];
27712
- const salt = randomBytes(saltLen);
27712
+ const salt = randomBytes$1(saltLen);
27713
27713
  const keys = await aesDerive(password, salt, keyStrength);
27714
27714
  const ciphertext = await aesCtr(keys.encryptionKey, data, true);
27715
27715
  const hmac = await aesComputeHmac(keys.hmacKey, ciphertext);
@@ -28531,6 +28531,12 @@ self.onmessage = async function(event) {
28531
28531
  * - In browser builds the bundler aliases those imports to their browser variants.
28532
28532
  */
28533
28533
  const SMART_STORE_DECIDE_BYTES = 16 * 1024;
28534
+ /** Input batching threshold for push(). Small chunks are accumulated in an
28535
+ * internal buffer and flushed to the compression pipeline once this size is
28536
+ * reached. 64 KB matches the standard deflate window and keeps the number
28537
+ * of async push() calls — each of which creates a full Promise chain in the
28538
+ * browser CompressionStream path — down to a manageable level. */
28539
+ const INPUT_BATCH_BYTES = 65536;
28534
28540
  /**
28535
28541
  * True Streaming ZIP File - compresses chunk by chunk
28536
28542
  */
@@ -28566,6 +28572,8 @@ self.onmessage = async function(event) {
28566
28572
  this._dataQueue = [];
28567
28573
  this._finalQueued = false;
28568
28574
  this._pushChain = Promise.resolve();
28575
+ this._inputBuf = null;
28576
+ this._inputPos = 0;
28569
28577
  this._syncDeflater = null;
28570
28578
  this._syncZlibReady = false;
28571
28579
  const resolvedName = options?.path ? normalizeZipPath(name, options.path) : name;
@@ -28641,7 +28649,7 @@ self.onmessage = async function(event) {
28641
28649
  if (this._zipCryptoState || this._encryptionMethod !== "zipcrypto") return;
28642
28650
  this._zipCryptoState = zipCryptoInitKeys(this._password);
28643
28651
  const dosTimeForCheck = this.dosTime << 16 | this.dosDate;
28644
- const header = zipCryptoCreateHeader(this._zipCryptoState, dosTimeForCheck, randomBytes);
28652
+ const header = zipCryptoCreateHeader(this._zipCryptoState, dosTimeForCheck, randomBytes$1);
28645
28653
  this._compressedSize += header.length;
28646
28654
  this._enqueueData(header, false);
28647
28655
  }
@@ -28992,6 +29000,34 @@ self.onmessage = async function(event) {
28992
29000
  }
28993
29001
  return Promise.resolve();
28994
29002
  }
29003
+ if (!final && data.length > 0 && data.length < INPUT_BATCH_BYTES) {
29004
+ if (!this._inputBuf) {
29005
+ this._inputBuf = new Uint8Array(INPUT_BATCH_BYTES);
29006
+ this._inputPos = 0;
29007
+ }
29008
+ if (this._inputPos + data.length <= INPUT_BATCH_BYTES) {
29009
+ this._inputBuf.set(data, this._inputPos);
29010
+ this._inputPos += data.length;
29011
+ callback?.();
29012
+ return Promise.resolve();
29013
+ }
29014
+ const combined = new Uint8Array(this._inputPos + data.length);
29015
+ combined.set(this._inputBuf.subarray(0, this._inputPos));
29016
+ combined.set(data, this._inputPos);
29017
+ this._inputPos = 0;
29018
+ return this._pushAsync(combined, false, callback);
29019
+ }
29020
+ if (this._inputPos > 0) {
29021
+ const flushData = this._inputBuf.slice(0, this._inputPos);
29022
+ this._inputPos = 0;
29023
+ const promise = this._pushChain = this._pushAsync(flushData, false).then(() => this._pushUnchained(data, final, callback), () => this._pushUnchained(data, final, callback));
29024
+ promise.catch(() => {});
29025
+ return promise;
29026
+ }
29027
+ return this._pushAsync(data, final, callback);
29028
+ }
29029
+ /** Enqueue an async push through the _pushChain serialization. */
29030
+ _pushAsync(data, final, callback) {
28995
29031
  const promise = this._pushChain = this._pushChain.then(() => this._pushUnchained(data, final, callback), () => this._pushUnchained(data, final, callback));
28996
29032
  promise.catch(() => {});
28997
29033
  return promise;
@@ -44669,228 +44705,597 @@ onmessage = async (ev) => {
44669
44705
  return err instanceof PdfError;
44670
44706
  }
44671
44707
  //#endregion
44672
- //#region src/modules/pdf/core/encryption.ts
44673
- /** PDF password padding string (32 bytes) per PDF spec §3.5.2 */
44674
- const PASSWORD_PADDING = new Uint8Array([
44675
- 40,
44676
- 191,
44677
- 78,
44678
- 94,
44679
- 78,
44708
+ //#region src/modules/pdf/core/crypto.ts
44709
+ /**
44710
+ * Shared cryptographic primitives for PDF encryption/decryption.
44711
+ *
44712
+ * Zero-dependency, pure JavaScript implementations of:
44713
+ * - AES (128/256-bit) CBC encrypt and decrypt
44714
+ * - SHA-256
44715
+ * - MD5
44716
+ * - RC4 (for reading legacy PDFs only)
44717
+ *
44718
+ * @see FIPS 197 — AES
44719
+ * @see FIPS 180-4 — SHA-256
44720
+ * @see RFC 1321 — MD5
44721
+ */
44722
+ /** AES S-Box */
44723
+ const SBOX = new Uint8Array([
44724
+ 99,
44725
+ 124,
44726
+ 119,
44727
+ 123,
44728
+ 242,
44729
+ 107,
44730
+ 111,
44731
+ 197,
44732
+ 48,
44733
+ 1,
44734
+ 103,
44735
+ 43,
44736
+ 254,
44737
+ 215,
44738
+ 171,
44739
+ 118,
44740
+ 202,
44741
+ 130,
44742
+ 201,
44743
+ 125,
44744
+ 250,
44745
+ 89,
44746
+ 71,
44747
+ 240,
44748
+ 173,
44749
+ 212,
44750
+ 162,
44751
+ 175,
44752
+ 156,
44753
+ 164,
44754
+ 114,
44755
+ 192,
44756
+ 183,
44757
+ 253,
44758
+ 147,
44759
+ 38,
44760
+ 54,
44761
+ 63,
44762
+ 247,
44763
+ 204,
44764
+ 52,
44765
+ 165,
44766
+ 229,
44767
+ 241,
44768
+ 113,
44769
+ 216,
44770
+ 49,
44771
+ 21,
44772
+ 4,
44773
+ 199,
44774
+ 35,
44775
+ 195,
44776
+ 24,
44777
+ 150,
44778
+ 5,
44779
+ 154,
44780
+ 7,
44781
+ 18,
44782
+ 128,
44783
+ 226,
44784
+ 235,
44785
+ 39,
44786
+ 178,
44680
44787
  117,
44681
- 138,
44682
- 65,
44683
- 100,
44788
+ 9,
44789
+ 131,
44790
+ 44,
44791
+ 26,
44792
+ 27,
44793
+ 110,
44794
+ 90,
44795
+ 160,
44796
+ 82,
44797
+ 59,
44798
+ 214,
44799
+ 179,
44800
+ 41,
44801
+ 227,
44802
+ 47,
44803
+ 132,
44804
+ 83,
44805
+ 209,
44684
44806
  0,
44807
+ 237,
44808
+ 32,
44809
+ 252,
44810
+ 177,
44811
+ 91,
44812
+ 106,
44813
+ 203,
44814
+ 190,
44815
+ 57,
44816
+ 74,
44817
+ 76,
44818
+ 88,
44819
+ 207,
44820
+ 208,
44821
+ 239,
44822
+ 170,
44823
+ 251,
44824
+ 67,
44825
+ 77,
44826
+ 51,
44827
+ 133,
44828
+ 69,
44829
+ 249,
44830
+ 2,
44831
+ 127,
44832
+ 80,
44833
+ 60,
44834
+ 159,
44835
+ 168,
44836
+ 81,
44837
+ 163,
44838
+ 64,
44839
+ 143,
44840
+ 146,
44841
+ 157,
44842
+ 56,
44843
+ 245,
44844
+ 188,
44845
+ 182,
44846
+ 218,
44847
+ 33,
44848
+ 16,
44849
+ 255,
44850
+ 243,
44851
+ 210,
44852
+ 205,
44853
+ 12,
44854
+ 19,
44855
+ 236,
44856
+ 95,
44857
+ 151,
44858
+ 68,
44859
+ 23,
44860
+ 196,
44861
+ 167,
44862
+ 126,
44863
+ 61,
44864
+ 100,
44865
+ 93,
44866
+ 25,
44867
+ 115,
44868
+ 96,
44869
+ 129,
44870
+ 79,
44871
+ 220,
44872
+ 34,
44873
+ 42,
44874
+ 144,
44875
+ 136,
44876
+ 70,
44877
+ 238,
44878
+ 184,
44879
+ 20,
44880
+ 222,
44881
+ 94,
44882
+ 11,
44883
+ 219,
44884
+ 224,
44885
+ 50,
44886
+ 58,
44887
+ 10,
44888
+ 73,
44889
+ 6,
44890
+ 36,
44891
+ 92,
44892
+ 194,
44893
+ 211,
44894
+ 172,
44895
+ 98,
44896
+ 145,
44897
+ 149,
44898
+ 228,
44899
+ 121,
44900
+ 231,
44901
+ 200,
44902
+ 55,
44903
+ 109,
44904
+ 141,
44905
+ 213,
44685
44906
  78,
44907
+ 169,
44908
+ 108,
44686
44909
  86,
44687
- 255,
44688
- 250,
44689
- 1,
44910
+ 244,
44911
+ 234,
44912
+ 101,
44913
+ 122,
44914
+ 174,
44690
44915
  8,
44916
+ 186,
44917
+ 120,
44918
+ 37,
44691
44919
  46,
44692
- 46,
44693
- 0,
44694
- 182,
44695
- 208,
44696
- 104,
44920
+ 28,
44921
+ 166,
44922
+ 180,
44923
+ 198,
44924
+ 232,
44925
+ 221,
44926
+ 116,
44927
+ 31,
44928
+ 75,
44929
+ 189,
44930
+ 139,
44931
+ 138,
44932
+ 112,
44697
44933
  62,
44698
- 128,
44699
- 47,
44700
- 12,
44701
- 169,
44702
- 254,
44703
- 100,
44704
- 83,
44934
+ 181,
44935
+ 102,
44936
+ 72,
44937
+ 3,
44938
+ 246,
44939
+ 14,
44940
+ 97,
44941
+ 53,
44942
+ 87,
44943
+ 185,
44944
+ 134,
44945
+ 193,
44946
+ 29,
44947
+ 158,
44948
+ 225,
44949
+ 248,
44950
+ 152,
44951
+ 17,
44705
44952
  105,
44706
- 122
44953
+ 217,
44954
+ 142,
44955
+ 148,
44956
+ 155,
44957
+ 30,
44958
+ 135,
44959
+ 233,
44960
+ 206,
44961
+ 85,
44962
+ 40,
44963
+ 223,
44964
+ 140,
44965
+ 161,
44966
+ 137,
44967
+ 13,
44968
+ 191,
44969
+ 230,
44970
+ 66,
44971
+ 104,
44972
+ 65,
44973
+ 153,
44974
+ 45,
44975
+ 15,
44976
+ 176,
44977
+ 84,
44978
+ 187,
44979
+ 22
44707
44980
  ]);
44981
+ /** AES round constants */
44982
+ const RCON = [
44983
+ 1,
44984
+ 2,
44985
+ 4,
44986
+ 8,
44987
+ 16,
44988
+ 32,
44989
+ 64,
44990
+ 128,
44991
+ 27,
44992
+ 54
44993
+ ];
44994
+ /** GF(2^8) multiplication by 2 */
44995
+ function gf2(a) {
44996
+ return a < 128 ? a << 1 : a << 1 ^ 283;
44997
+ }
44998
+ /**
44999
+ * AES key expansion. Supports AES-128 (16-byte key) and AES-256 (32-byte key).
45000
+ */
45001
+ function aesKeyExpansion(key) {
45002
+ const nk = key.length / 4;
45003
+ const nr = nk + 6;
45004
+ const w = [];
45005
+ for (let i = 0; i < nk; i++) w.push(new Uint8Array([
45006
+ key[4 * i],
45007
+ key[4 * i + 1],
45008
+ key[4 * i + 2],
45009
+ key[4 * i + 3]
45010
+ ]));
45011
+ for (let i = nk; i < 4 * (nr + 1); i++) {
45012
+ const temp = new Uint8Array(w[i - 1]);
45013
+ if (i % nk === 0) {
45014
+ const t0 = temp[0];
45015
+ temp[0] = SBOX[temp[1]] ^ RCON[i / nk - 1];
45016
+ temp[1] = SBOX[temp[2]];
45017
+ temp[2] = SBOX[temp[3]];
45018
+ temp[3] = SBOX[t0];
45019
+ } else if (nk > 6 && i % nk === 4) {
45020
+ temp[0] = SBOX[temp[0]];
45021
+ temp[1] = SBOX[temp[1]];
45022
+ temp[2] = SBOX[temp[2]];
45023
+ temp[3] = SBOX[temp[3]];
45024
+ }
45025
+ const word = new Uint8Array(4);
45026
+ for (let j = 0; j < 4; j++) word[j] = w[i - nk][j] ^ temp[j];
45027
+ w.push(word);
45028
+ }
45029
+ return w;
45030
+ }
45031
+ /**
45032
+ * Encrypt a single AES block (16 bytes).
45033
+ * State layout: column-major per FIPS 197 §3.4.
45034
+ */
45035
+ function aesEncryptBlock(block, roundKeys) {
45036
+ const nr = roundKeys.length / 4 - 1;
45037
+ const state = new Uint8Array(16);
45038
+ state.set(block);
45039
+ for (let c = 0; c < 4; c++) for (let r = 0; r < 4; r++) state[4 * c + r] ^= roundKeys[c][r];
45040
+ for (let round = 1; round < nr; round++) {
45041
+ for (let i = 0; i < 16; i++) state[i] = SBOX[state[i]];
45042
+ let tmp;
45043
+ tmp = state[1];
45044
+ state[1] = state[5];
45045
+ state[5] = state[9];
45046
+ state[9] = state[13];
45047
+ state[13] = tmp;
45048
+ tmp = state[2];
45049
+ state[2] = state[10];
45050
+ state[10] = tmp;
45051
+ tmp = state[6];
45052
+ state[6] = state[14];
45053
+ state[14] = tmp;
45054
+ tmp = state[15];
45055
+ state[15] = state[11];
45056
+ state[11] = state[7];
45057
+ state[7] = state[3];
45058
+ state[3] = tmp;
45059
+ for (let c = 0; c < 4; c++) {
45060
+ const s0 = state[4 * c];
45061
+ const s1 = state[4 * c + 1];
45062
+ const s2 = state[4 * c + 2];
45063
+ const s3 = state[4 * c + 3];
45064
+ state[4 * c] = gf2(s0) ^ gf2(s1) ^ s1 ^ s2 ^ s3;
45065
+ state[4 * c + 1] = s0 ^ gf2(s1) ^ gf2(s2) ^ s2 ^ s3;
45066
+ state[4 * c + 2] = s0 ^ s1 ^ gf2(s2) ^ gf2(s3) ^ s3;
45067
+ state[4 * c + 3] = gf2(s0) ^ s0 ^ s1 ^ s2 ^ gf2(s3);
45068
+ }
45069
+ const keyOffset = round * 4;
45070
+ for (let c = 0; c < 4; c++) for (let r = 0; r < 4; r++) state[4 * c + r] ^= roundKeys[keyOffset + c][r];
45071
+ }
45072
+ for (let i = 0; i < 16; i++) state[i] = SBOX[state[i]];
45073
+ let tmp;
45074
+ tmp = state[1];
45075
+ state[1] = state[5];
45076
+ state[5] = state[9];
45077
+ state[9] = state[13];
45078
+ state[13] = tmp;
45079
+ tmp = state[2];
45080
+ state[2] = state[10];
45081
+ state[10] = tmp;
45082
+ tmp = state[6];
45083
+ state[6] = state[14];
45084
+ state[14] = tmp;
45085
+ tmp = state[15];
45086
+ state[15] = state[11];
45087
+ state[11] = state[7];
45088
+ state[7] = state[3];
45089
+ state[3] = tmp;
45090
+ for (let c = 0; c < 4; c++) for (let r = 0; r < 4; r++) state[4 * c + r] ^= roundKeys[nr * 4 + c][r];
45091
+ return state;
45092
+ }
44708
45093
  /**
44709
- * Initialize encryption state from the given options.
44710
- */
44711
- function initEncryption(options) {
44712
- const userPwd = options.userPassword ?? "";
44713
- const ownerPwd = options.ownerPassword;
44714
- const perms = computePermissions(options.permissions);
44715
- const fileId = generateFileId();
44716
- const oValue = computeOValue(ownerPwd, userPwd);
44717
- const encryptionKey = computeEncryptionKey(userPwd, oValue, perms, fileId);
44718
- return {
44719
- encryptionKey,
44720
- oValue,
44721
- uValue: computeUValue(encryptionKey, fileId),
44722
- permissions: perms,
44723
- fileId
44724
- };
45094
+ * AES-CBC encryption with PKCS#7 padding.
45095
+ * Supports AES-128 (16-byte key) and AES-256 (32-byte key).
45096
+ */
45097
+ function aesCbcEncrypt(plaintext, key, iv) {
45098
+ const padLen = 16 - plaintext.length % 16;
45099
+ const padded = new Uint8Array(plaintext.length + padLen);
45100
+ padded.set(plaintext);
45101
+ for (let i = plaintext.length; i < padded.length; i++) padded[i] = padLen;
45102
+ const roundKeys = aesKeyExpansion(key);
45103
+ const numBlocks = padded.length / 16;
45104
+ const output = new Uint8Array(padded.length);
45105
+ let prevBlock = iv;
45106
+ for (let b = 0; b < numBlocks; b++) {
45107
+ const block = new Uint8Array(16);
45108
+ for (let i = 0; i < 16; i++) block[i] = padded[b * 16 + i] ^ prevBlock[i];
45109
+ const encrypted = aesEncryptBlock(block, roundKeys);
45110
+ output.set(encrypted, b * 16);
45111
+ prevBlock = encrypted;
45112
+ }
45113
+ return output;
44725
45114
  }
44726
45115
  /**
44727
- * Encrypt a string or stream for a specific PDF object.
44728
- * Per-object encryption key = MD5(encryptionKey + objectNumber + generation).
45116
+ * AES-CBC encryption WITHOUT PKCS#7 padding.
45117
+ * Used when the plaintext is already block-aligned (e.g., encrypting
45118
+ * the 32-byte file encryption key in V=5).
45119
+ *
45120
+ * @throws if plaintext length is not a multiple of 16.
44729
45121
  */
44730
- function encryptData(data, objectNumber, generation, encryptionKey) {
44731
- const keyInput = new Uint8Array(encryptionKey.length + 5);
44732
- keyInput.set(encryptionKey);
44733
- keyInput[encryptionKey.length] = objectNumber & 255;
44734
- keyInput[encryptionKey.length + 1] = objectNumber >> 8 & 255;
44735
- keyInput[encryptionKey.length + 2] = objectNumber >> 16 & 255;
44736
- keyInput[encryptionKey.length + 3] = generation & 255;
44737
- keyInput[encryptionKey.length + 4] = generation >> 8 & 255;
44738
- const objKey = md5(keyInput);
44739
- const keyLen = Math.min(encryptionKey.length + 5, 16);
44740
- return rc4(objKey.subarray(0, keyLen), data);
45122
+ function aesCbcEncryptRaw(plaintext, key, iv) {
45123
+ if (plaintext.length % 16 !== 0) throw new Error("aesCbcEncryptRaw: plaintext length must be a multiple of 16");
45124
+ const roundKeys = aesKeyExpansion(key);
45125
+ const numBlocks = plaintext.length / 16;
45126
+ const output = new Uint8Array(plaintext.length);
45127
+ let prevBlock = iv;
45128
+ for (let b = 0; b < numBlocks; b++) {
45129
+ const block = new Uint8Array(16);
45130
+ for (let i = 0; i < 16; i++) block[i] = plaintext[b * 16 + i] ^ prevBlock[i];
45131
+ const encrypted = aesEncryptBlock(block, roundKeys);
45132
+ output.set(encrypted, b * 16);
45133
+ prevBlock = encrypted;
45134
+ }
45135
+ return output;
44741
45136
  }
44742
45137
  /**
44743
- * RC4 stream cipher implementation.
45138
+ * AES-ECB encryption of a single 16-byte block (no padding, no IV).
45139
+ * Used for the /Perms value in V=5 encryption.
44744
45140
  */
44745
- function rc4(key, data) {
44746
- const s = new Uint8Array(256);
44747
- for (let i = 0; i < 256; i++) s[i] = i;
44748
- let j = 0;
44749
- for (let i = 0; i < 256; i++) {
44750
- j = j + s[i] + key[i % key.length] & 255;
44751
- [s[i], s[j]] = [s[j], s[i]];
44752
- }
44753
- const result = new Uint8Array(data.length);
44754
- let ii = 0;
44755
- let jj = 0;
44756
- for (let k = 0; k < data.length; k++) {
44757
- ii = ii + 1 & 255;
44758
- jj = jj + s[ii] & 255;
44759
- [s[ii], s[jj]] = [s[jj], s[ii]];
44760
- result[k] = data[k] ^ s[s[ii] + s[jj] & 255];
44761
- }
44762
- return result;
45141
+ function aesEcbEncrypt(block, key) {
45142
+ return aesEncryptBlock(block, aesKeyExpansion(key));
45143
+ }
45144
+ /** SHA-256 initial hash values */
45145
+ const SHA256_H = new Uint32Array([
45146
+ 1779033703,
45147
+ 3144134277,
45148
+ 1013904242,
45149
+ 2773480762,
45150
+ 1359893119,
45151
+ 2600822924,
45152
+ 528734635,
45153
+ 1541459225
45154
+ ]);
45155
+ /** SHA-256 round constants */
45156
+ const SHA256_K = new Uint32Array([
45157
+ 1116352408,
45158
+ 1899447441,
45159
+ 3049323471,
45160
+ 3921009573,
45161
+ 961987163,
45162
+ 1508970993,
45163
+ 2453635748,
45164
+ 2870763221,
45165
+ 3624381080,
45166
+ 310598401,
45167
+ 607225278,
45168
+ 1426881987,
45169
+ 1925078388,
45170
+ 2162078206,
45171
+ 2614888103,
45172
+ 3248222580,
45173
+ 3835390401,
45174
+ 4022224774,
45175
+ 264347078,
45176
+ 604807628,
45177
+ 770255983,
45178
+ 1249150122,
45179
+ 1555081692,
45180
+ 1996064986,
45181
+ 2554220882,
45182
+ 2821834349,
45183
+ 2952996808,
45184
+ 3210313671,
45185
+ 3336571891,
45186
+ 3584528711,
45187
+ 113926993,
45188
+ 338241895,
45189
+ 666307205,
45190
+ 773529912,
45191
+ 1294757372,
45192
+ 1396182291,
45193
+ 1695183700,
45194
+ 1986661051,
45195
+ 2177026350,
45196
+ 2456956037,
45197
+ 2730485921,
45198
+ 2820302411,
45199
+ 3259730800,
45200
+ 3345764771,
45201
+ 3516065817,
45202
+ 3600352804,
45203
+ 4094571909,
45204
+ 275423344,
45205
+ 430227734,
45206
+ 506948616,
45207
+ 659060556,
45208
+ 883997877,
45209
+ 958139571,
45210
+ 1322822218,
45211
+ 1537002063,
45212
+ 1747873779,
45213
+ 1955562222,
45214
+ 2024104815,
45215
+ 2227730452,
45216
+ 2361852424,
45217
+ 2428436474,
45218
+ 2756734187,
45219
+ 3204031479,
45220
+ 3329325298
45221
+ ]);
45222
+ function rotr32(x, n) {
45223
+ return (x >>> n | x << 32 - n) >>> 0;
44763
45224
  }
44764
45225
  /**
44765
- * MD5 hash implementation (RFC 1321).
44766
- * Returns 16-byte digest.
45226
+ * SHA-256 hash function.
45227
+ * @returns 32-byte digest
44767
45228
  */
44768
- function md5(input) {
45229
+ function sha256(input) {
44769
45230
  const msgLen = input.length;
44770
- const bitLen = msgLen * 8;
44771
- const padLen = (56 - (msgLen + 1) % 64 + 64) % 64 + 1;
44772
- const padded = new Uint8Array(msgLen + padLen + 8);
45231
+ const paddedLen = Math.ceil((msgLen + 9) / 64) * 64;
45232
+ const padded = new Uint8Array(paddedLen);
44773
45233
  padded.set(input);
44774
45234
  padded[msgLen] = 128;
44775
- const view = new DataView(padded.buffer);
44776
- view.setUint32(padded.length - 8, bitLen >>> 0, true);
44777
- view.setUint32(padded.length - 4, 0, true);
44778
- let a0 = 1732584193;
44779
- let b0 = 4023233417;
44780
- let c0 = 2562383102;
44781
- let d0 = 271733878;
44782
- for (let i = 0; i < padded.length; i += 64) {
44783
- const M = new Uint32Array(16);
44784
- for (let j = 0; j < 16; j++) M[j] = view.getUint32(i + j * 4, true);
44785
- let A = a0;
44786
- let B = b0;
44787
- let C = c0;
44788
- let D = d0;
44789
- for (let j = 0; j < 64; j++) {
44790
- let F;
44791
- let g;
44792
- if (j < 16) {
44793
- F = B & C | ~B & D;
44794
- g = j;
44795
- } else if (j < 32) {
44796
- F = D & B | ~D & C;
44797
- g = (5 * j + 1) % 16;
44798
- } else if (j < 48) {
44799
- F = B ^ C ^ D;
44800
- g = (3 * j + 5) % 16;
44801
- } else {
44802
- F = C ^ (B | ~D);
44803
- g = 7 * j % 16;
44804
- }
44805
- F = F + A + K[j] + M[g] >>> 0;
44806
- A = D;
44807
- D = C;
44808
- C = B;
44809
- B = B + rotl(F, S[j]) >>> 0;
44810
- }
44811
- a0 = a0 + A >>> 0;
44812
- b0 = b0 + B >>> 0;
44813
- c0 = c0 + C >>> 0;
44814
- d0 = d0 + D >>> 0;
44815
- }
44816
- const digest = new Uint8Array(16);
44817
- const dv = new DataView(digest.buffer);
44818
- dv.setUint32(0, a0, true);
44819
- dv.setUint32(4, b0, true);
44820
- dv.setUint32(8, c0, true);
44821
- dv.setUint32(12, d0, true);
44822
- return digest;
44823
- }
44824
- function rotl(x, n) {
44825
- return (x << n | x >>> 32 - n) >>> 0;
44826
- }
44827
- const S = [
44828
- 7,
44829
- 12,
44830
- 17,
44831
- 22,
44832
- 7,
44833
- 12,
44834
- 17,
44835
- 22,
44836
- 7,
44837
- 12,
44838
- 17,
44839
- 22,
44840
- 7,
44841
- 12,
44842
- 17,
44843
- 22,
44844
- 5,
44845
- 9,
44846
- 14,
44847
- 20,
44848
- 5,
44849
- 9,
44850
- 14,
44851
- 20,
44852
- 5,
44853
- 9,
44854
- 14,
44855
- 20,
44856
- 5,
44857
- 9,
44858
- 14,
44859
- 20,
44860
- 4,
44861
- 11,
44862
- 16,
44863
- 23,
44864
- 4,
44865
- 11,
44866
- 16,
44867
- 23,
44868
- 4,
44869
- 11,
44870
- 16,
44871
- 23,
44872
- 4,
44873
- 11,
44874
- 16,
44875
- 23,
44876
- 6,
44877
- 10,
44878
- 15,
44879
- 21,
44880
- 6,
44881
- 10,
44882
- 15,
44883
- 21,
44884
- 6,
44885
- 10,
44886
- 15,
44887
- 21,
44888
- 6,
44889
- 10,
44890
- 15,
44891
- 21
44892
- ];
44893
- const K = new Uint32Array([
45235
+ const bitLen = msgLen * 8;
45236
+ const view = new DataView(padded.buffer, padded.byteOffset, padded.byteLength);
45237
+ view.setUint32(paddedLen - 8, 0, false);
45238
+ view.setUint32(paddedLen - 4, bitLen, false);
45239
+ let h0 = SHA256_H[0];
45240
+ let h1 = SHA256_H[1];
45241
+ let h2 = SHA256_H[2];
45242
+ let h3 = SHA256_H[3];
45243
+ let h4 = SHA256_H[4];
45244
+ let h5 = SHA256_H[5];
45245
+ let h6 = SHA256_H[6];
45246
+ let h7 = SHA256_H[7];
45247
+ const w = new Uint32Array(64);
45248
+ for (let offset = 0; offset < paddedLen; offset += 64) {
45249
+ for (let i = 0; i < 16; i++) w[i] = view.getUint32(offset + i * 4, false);
45250
+ for (let i = 16; i < 64; i++) {
45251
+ const s0 = rotr32(w[i - 15], 7) ^ rotr32(w[i - 15], 18) ^ w[i - 15] >>> 3;
45252
+ const s1 = rotr32(w[i - 2], 17) ^ rotr32(w[i - 2], 19) ^ w[i - 2] >>> 10;
45253
+ w[i] = w[i - 16] + s0 + w[i - 7] + s1 >>> 0;
45254
+ }
45255
+ let a = h0;
45256
+ let b = h1;
45257
+ let c = h2;
45258
+ let d = h3;
45259
+ let e = h4;
45260
+ let f = h5;
45261
+ let g = h6;
45262
+ let h = h7;
45263
+ for (let i = 0; i < 64; i++) {
45264
+ const S1 = rotr32(e, 6) ^ rotr32(e, 11) ^ rotr32(e, 25);
45265
+ const ch = e & f ^ ~e & g;
45266
+ const temp1 = h + S1 + ch + SHA256_K[i] + w[i] >>> 0;
45267
+ const temp2 = (rotr32(a, 2) ^ rotr32(a, 13) ^ rotr32(a, 22)) + (a & b ^ a & c ^ b & c) >>> 0;
45268
+ h = g;
45269
+ g = f;
45270
+ f = e;
45271
+ e = d + temp1 >>> 0;
45272
+ d = c;
45273
+ c = b;
45274
+ b = a;
45275
+ a = temp1 + temp2 >>> 0;
45276
+ }
45277
+ h0 = h0 + a >>> 0;
45278
+ h1 = h1 + b >>> 0;
45279
+ h2 = h2 + c >>> 0;
45280
+ h3 = h3 + d >>> 0;
45281
+ h4 = h4 + e >>> 0;
45282
+ h5 = h5 + f >>> 0;
45283
+ h6 = h6 + g >>> 0;
45284
+ h7 = h7 + h >>> 0;
45285
+ }
45286
+ const result = new Uint8Array(32);
45287
+ const resultView = new DataView(result.buffer);
45288
+ resultView.setUint32(0, h0, false);
45289
+ resultView.setUint32(4, h1, false);
45290
+ resultView.setUint32(8, h2, false);
45291
+ resultView.setUint32(12, h3, false);
45292
+ resultView.setUint32(16, h4, false);
45293
+ resultView.setUint32(20, h5, false);
45294
+ resultView.setUint32(24, h6, false);
45295
+ resultView.setUint32(28, h7, false);
45296
+ return result;
45297
+ }
45298
+ new Uint32Array([
44894
45299
  3614090360,
44895
45300
  3905402710,
44896
45301
  606105819,
@@ -44957,64 +45362,104 @@ onmessage = async (ev) => {
44957
45362
  3951481745
44958
45363
  ]);
44959
45364
  /**
44960
- * Pad or truncate a password to 32 bytes using the PDF password padding.
45365
+ * Generate pseudo-random bytes.
45366
+ * Uses Math.random — adequate for PDF IVs but not cryptographically secure.
44961
45367
  */
44962
- function padPassword(password) {
44963
- const result = new Uint8Array(32);
44964
- const bytes = new TextEncoder().encode(password);
44965
- const len = Math.min(bytes.length, 32);
44966
- result.set(bytes.subarray(0, len));
44967
- result.set(PASSWORD_PADDING.subarray(0, 32 - len), len);
44968
- return result;
45368
+ function randomBytes(length) {
45369
+ const bytes = new Uint8Array(length);
45370
+ for (let i = 0; i < length; i++) bytes[i] = Math.random() * 256 | 0;
45371
+ return bytes;
44969
45372
  }
44970
45373
  /**
44971
- * Compute the O (owner) value.
44972
- * Algorithm 3 from PDF spec §3.5.2.
45374
+ * Concatenate multiple Uint8Arrays.
44973
45375
  */
44974
- function computeOValue(ownerPassword, userPassword) {
44975
- let hash = md5(padPassword(ownerPassword));
44976
- for (let i = 0; i < 50; i++) hash = md5(hash);
44977
- const rc4Key = hash.subarray(0, 16);
44978
- let result = rc4(rc4Key, padPassword(userPassword));
44979
- for (let i = 1; i <= 19; i++) {
44980
- const modKey = new Uint8Array(16);
44981
- for (let j = 0; j < 16; j++) modKey[j] = rc4Key[j] ^ i;
44982
- result = rc4(modKey, result);
45376
+ function concatArrays(...arrays) {
45377
+ let totalLen = 0;
45378
+ for (const arr of arrays) totalLen += arr.length;
45379
+ const result = new Uint8Array(totalLen);
45380
+ let offset = 0;
45381
+ for (const arr of arrays) {
45382
+ result.set(arr, offset);
45383
+ offset += arr.length;
44983
45384
  }
44984
45385
  return result;
44985
45386
  }
45387
+ //#endregion
45388
+ //#region src/modules/pdf/core/encryption.ts
45389
+ /**
45390
+ * PDF encryption support (Standard Security Handler, V=5, R=5).
45391
+ *
45392
+ * Implements AES-256 encryption compatible with PDF 2.0 (ISO 32000-2:2020).
45393
+ * Supports:
45394
+ * - User password (required to open the document)
45395
+ * - Owner password (grants full access)
45396
+ * - Permission flags (print, copy, modify, etc.)
45397
+ *
45398
+ * The file encryption key (FEK) is a random 256-bit key.
45399
+ * All streams and strings are encrypted using AES-256-CBC with a random
45400
+ * 16-byte IV prepended to each encrypted value.
45401
+ *
45402
+ * @see ISO 32000-2:2020, §7.6 — Encryption
45403
+ */
44986
45404
  /**
44987
- * Compute the encryption key.
44988
- * Algorithm 2 from PDF spec §3.5.2.
45405
+ * Initialize encryption state for AES-256 (V=5, R=5).
44989
45406
  */
44990
- function computeEncryptionKey(userPassword, oValue, permissions, fileId) {
44991
- const paddedPwd = padPassword(userPassword);
44992
- const input = new Uint8Array(68 + fileId.length);
44993
- input.set(paddedPwd);
44994
- input.set(oValue, 32);
44995
- new DataView(input.buffer, input.byteOffset).setInt32(64, permissions, true);
44996
- input.set(fileId, 68);
44997
- let hash = md5(input);
44998
- for (let i = 0; i < 50; i++) hash = md5(hash.subarray(0, 16));
44999
- return hash.subarray(0, 16);
45407
+ function initEncryption(options) {
45408
+ const userPwd = truncatePassword(options.userPassword ?? "");
45409
+ const ownerPwd = truncatePassword(options.ownerPassword);
45410
+ const perms = computePermissions(options.permissions);
45411
+ const encryptionKey = randomBytes(32);
45412
+ const uValidationSalt = randomBytes(8);
45413
+ const uKeySalt = randomBytes(8);
45414
+ const oValidationSalt = randomBytes(8);
45415
+ const oKeySalt = randomBytes(8);
45416
+ const uValue = concatArrays(sha256(concatArrays(userPwd, uValidationSalt)), uValidationSalt, uKeySalt);
45417
+ const ueKey = sha256(concatArrays(userPwd, uKeySalt));
45418
+ const zeroIv = new Uint8Array(16);
45419
+ const ueValue = aesCbcEncryptRaw(encryptionKey, ueKey, zeroIv);
45420
+ const oValue = concatArrays(sha256(concatArrays(ownerPwd, oValidationSalt, uValue)), oValidationSalt, oKeySalt);
45421
+ const oeValue = aesCbcEncryptRaw(encryptionKey, sha256(concatArrays(ownerPwd, oKeySalt, uValue)), zeroIv);
45422
+ const permsBlock = new Uint8Array(16);
45423
+ new DataView(permsBlock.buffer).setInt32(0, perms, true);
45424
+ permsBlock[4] = 255;
45425
+ permsBlock[5] = 255;
45426
+ permsBlock[6] = 255;
45427
+ permsBlock[7] = 255;
45428
+ permsBlock[8] = 84;
45429
+ permsBlock[9] = 97;
45430
+ permsBlock[10] = 100;
45431
+ permsBlock[11] = 98;
45432
+ return {
45433
+ encryptionKey,
45434
+ oValue,
45435
+ uValue,
45436
+ oeValue,
45437
+ ueValue,
45438
+ permsValue: aesEcbEncrypt(permsBlock, encryptionKey),
45439
+ permissions: perms,
45440
+ fileId: randomBytes(16)
45441
+ };
45000
45442
  }
45001
45443
  /**
45002
- * Compute the U (user) value.
45003
- * Algorithm 5 from PDF spec §3.5.2 (revision 3).
45444
+ * Encrypt data for a PDF object using AES-256-CBC.
45445
+ *
45446
+ * For V=5/R=5, the file encryption key is used directly (no per-object key derivation).
45447
+ * A random 16-byte IV is prepended to the ciphertext.
45004
45448
  */
45005
- function computeUValue(encryptionKey, fileId) {
45006
- const hashInput = new Uint8Array(32 + fileId.length);
45007
- hashInput.set(PASSWORD_PADDING);
45008
- hashInput.set(fileId, 32);
45009
- let result = rc4(encryptionKey, md5(hashInput));
45010
- for (let i = 1; i <= 19; i++) {
45011
- const modKey = new Uint8Array(16);
45012
- for (let j = 0; j < 16; j++) modKey[j] = encryptionKey[j] ^ i;
45013
- result = rc4(modKey, result);
45014
- }
45015
- const uValue = new Uint8Array(32);
45016
- uValue.set(result);
45017
- return uValue;
45449
+ function encryptData(data, _objectNumber, _generation, encryptionKey) {
45450
+ const iv = randomBytes(16);
45451
+ const ciphertext = aesCbcEncrypt(data, encryptionKey, iv);
45452
+ const result = new Uint8Array(16 + ciphertext.length);
45453
+ result.set(iv);
45454
+ result.set(ciphertext, 16);
45455
+ return result;
45456
+ }
45457
+ /**
45458
+ * Truncate password to 127 bytes (UTF-8) per PDF 2.0 spec.
45459
+ */
45460
+ function truncatePassword(password) {
45461
+ const bytes = new TextEncoder().encode(password);
45462
+ return bytes.length > 127 ? bytes.subarray(0, 127) : bytes;
45018
45463
  }
45019
45464
  /**
45020
45465
  * Compute the permissions integer (P value) from permission flags.
@@ -45031,33 +45476,24 @@ onmessage = async (ev) => {
45031
45476
  if (perms?.printHighQuality) p |= 2048;
45032
45477
  return p | 0;
45033
45478
  }
45034
- /**
45035
- * Generate a random file identifier (16 bytes).
45036
- */
45037
- function generateFileId() {
45038
- const seed = new Uint8Array(16);
45039
- const now = Date.now();
45040
- const view = new DataView(seed.buffer);
45041
- view.setFloat64(0, now, true);
45042
- view.setFloat64(8, Math.random() * 0x38d7ea4c68000, true);
45043
- return md5(seed);
45044
- }
45045
45479
  //#endregion
45046
45480
  //#region src/modules/pdf/core/pdf-writer.ts
45047
45481
  /**
45048
45482
  * PDF file writer.
45049
45483
  *
45050
- * Assembles a complete PDF document from indirect objects.
45484
+ * Assembles a complete PDF 2.0 document from indirect objects.
45051
45485
  * Handles the four sections of a PDF file:
45052
- * 1. Header (%PDF-1.4)
45486
+ * 1. Header (%PDF-2.0)
45053
45487
  * 2. Body (indirect objects)
45054
45488
  * 3. Cross-reference table
45055
45489
  * 4. Trailer (with document catalog reference)
45056
45490
  *
45057
- * @see PDF Reference 1.7, Chapter 3.4 - File Structure
45491
+ * Encryption uses AES-256 (V=5, R=5) per ISO 32000-2:2020.
45492
+ *
45493
+ * @see ISO 32000-2:2020, Chapter 7.5 — File Structure
45058
45494
  */
45059
45495
  /**
45060
- * Constructs a valid PDF 1.4 file from a set of indirect objects.
45496
+ * Constructs a valid PDF 2.0 file from a set of indirect objects.
45061
45497
  *
45062
45498
  * Usage:
45063
45499
  * 1. Allocate object numbers with allocObject()
@@ -45170,7 +45606,7 @@ onmessage = async (ev) => {
45170
45606
  const encoder = new TextEncoder();
45171
45607
  const chunks = [];
45172
45608
  let byteOffset = 0;
45173
- const headerStrBytes = encoder.encode("%PDF-1.4\n");
45609
+ const headerStrBytes = encoder.encode("%PDF-2.0\n");
45174
45610
  chunks.push(headerStrBytes);
45175
45611
  byteOffset += headerStrBytes.length;
45176
45612
  const binaryComment = new Uint8Array([
@@ -45219,7 +45655,7 @@ onmessage = async (ev) => {
45219
45655
  byteOffset += objFooter.length;
45220
45656
  }
45221
45657
  if (this.encryption) {
45222
- const encContent = new PdfDict().set("Filter", "/Standard").set("V", "2").set("R", "3").set("Length", "128").set("P", String(this.encryption.permissions)).set("O", pdfHexString(this.encryption.oValue)).set("U", pdfHexString(this.encryption.uValue)).toString();
45658
+ const encContent = new PdfDict().set("Filter", "/Standard").set("V", "5").set("R", "5").set("Length", "256").set("P", String(this.encryption.permissions)).set("O", pdfHexString(this.encryption.oValue)).set("U", pdfHexString(this.encryption.uValue)).set("OE", pdfHexString(this.encryption.oeValue)).set("UE", pdfHexString(this.encryption.ueValue)).set("Perms", pdfHexString(this.encryption.permsValue)).set("EncryptMetadata", "true").set("CF", "<< /StdCF << /Type /CryptFilter /CFM /AESV3 /AuthEvent /DocOpen /Length 32 >> >>").set("StmF", "/StdCF").set("StrF", "/StdCF").toString();
45223
45659
  const encObj = {
45224
45660
  objectNumber: encryptObjNum,
45225
45661
  offset: byteOffset,