kordoc 4.3.0 → 4.4.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 (169) hide show
  1. package/NOTICE +176 -0
  2. package/README.md +936 -922
  3. package/THIRD_PARTY/apache-2.0.LICENSE +201 -0
  4. package/THIRD_PARTY/claw-hwp.LICENSE +21 -0
  5. package/THIRD_PARTY/hml-equation-parser.LICENSE +201 -0
  6. package/THIRD_PARTY/hml-equation-parser.txt +35 -0
  7. package/THIRD_PARTY/opendataloader-pdf.txt +32 -0
  8. package/THIRD_PARTY/pix2text.txt +17 -0
  9. package/THIRD_PARTY/rhwp-forms.txt +35 -0
  10. package/dist/{-DYHT3HWH.js → -R6NUMPRV.js} +11 -11
  11. package/dist/{chunk-3FFPHTXD.js → chunk-3H5X63BQ.js} +1 -1
  12. package/dist/chunk-3H5X63BQ.js.map +1 -0
  13. package/dist/{chunk-EFVAY5VE.js → chunk-5KLE3U23.js} +7 -4
  14. package/dist/chunk-5KLE3U23.js.map +1 -0
  15. package/dist/{chunk-6GBLFQMA.js → chunk-5RPYBC2Q.js} +1 -1
  16. package/dist/chunk-5RPYBC2Q.js.map +1 -0
  17. package/dist/{chunk-VXMLENCV.js → chunk-65DYCXGC.js} +4 -4
  18. package/dist/chunk-65DYCXGC.js.map +1 -0
  19. package/dist/{chunk-W34QSJFF.js → chunk-7FCNOKOR.js} +1 -1
  20. package/dist/chunk-7FCNOKOR.js.map +1 -0
  21. package/dist/{chunk-ZSL6MQVE.js → chunk-A73RMJ6B.js} +1 -1
  22. package/dist/chunk-A73RMJ6B.js.map +1 -0
  23. package/dist/{chunk-AL34DGS5.cjs → chunk-AB322K3O.cjs} +6 -6
  24. package/dist/chunk-AB322K3O.cjs.map +1 -0
  25. package/dist/chunk-DCZVOIEO.cjs.map +1 -1
  26. package/dist/{chunk-O5EK52TG.js → chunk-DZIXKL7E.js} +1 -1
  27. package/dist/chunk-DZIXKL7E.js.map +1 -0
  28. package/dist/{chunk-EZHW3RNQ.js → chunk-GHGZU2LQ.js} +2 -2
  29. package/dist/chunk-GHGZU2LQ.js.map +1 -0
  30. package/dist/chunk-GS7T56RP.cjs.map +1 -1
  31. package/dist/{chunk-6XPXIG57.js → chunk-GZDOWYJM.js} +2 -2
  32. package/dist/chunk-GZDOWYJM.js.map +1 -0
  33. package/dist/{chunk-JFV7QT5T.cjs → chunk-H2KMJOB6.cjs} +2 -2
  34. package/dist/chunk-H2KMJOB6.cjs.map +1 -0
  35. package/dist/{chunk-6RAYTNQT.js → chunk-HFJF477F.js} +3 -3
  36. package/dist/chunk-HFJF477F.js.map +1 -0
  37. package/dist/{chunk-SPKBIFSK.js → chunk-HNRULYUH.js} +2 -2
  38. package/dist/chunk-HNRULYUH.js.map +1 -0
  39. package/dist/chunk-MOL7MDBG.js +0 -0
  40. package/dist/{chunk-7MFO4TKP.js → chunk-ONS7JKVA.js} +2 -2
  41. package/dist/chunk-ONS7JKVA.js.map +1 -0
  42. package/dist/{chunk-DRQUAYNT.js → chunk-P7YPCK7C.js} +2 -2
  43. package/dist/chunk-P7YPCK7C.js.map +1 -0
  44. package/dist/{chunk-GSMPTZC7.js → chunk-QXYB74QF.js} +24 -3
  45. package/dist/chunk-QXYB74QF.js.map +1 -0
  46. package/dist/{chunk-NMWXBJ5B.js → chunk-RZURSA4N.js} +6 -4
  47. package/dist/chunk-RZURSA4N.js.map +1 -0
  48. package/dist/{chunk-LPQWKQQQ.cjs → chunk-SKM6KZUH.cjs} +9 -9
  49. package/dist/chunk-SKM6KZUH.cjs.map +1 -0
  50. package/dist/{chunk-YNJDNP44.js → chunk-UAFSPWU7.js} +1 -1
  51. package/dist/chunk-UAFSPWU7.js.map +1 -0
  52. package/dist/{chunk-AUOEZKJE.cjs → chunk-UDGPM5R5.cjs} +26 -5
  53. package/dist/chunk-UDGPM5R5.cjs.map +1 -0
  54. package/dist/{chunk-WFOQFJI4.js → chunk-V35DRUJC.js} +2 -2
  55. package/dist/chunk-V35DRUJC.js.map +1 -0
  56. package/dist/{chunk-4DBCUF3C.js → chunk-W6CUD7V3.js} +3 -3
  57. package/dist/chunk-W6CUD7V3.js.map +1 -0
  58. package/dist/{chunk-US4WFSIQ.js → chunk-XCIC7U6Y.js} +24 -3
  59. package/dist/chunk-XCIC7U6Y.js.map +1 -0
  60. package/dist/{chunk-DRZUJ2RK.js → chunk-YOZ7ZRCD.js} +366 -51
  61. package/dist/chunk-YOZ7ZRCD.js.map +1 -0
  62. package/dist/{chunk-ARXNKC5D.js → chunk-YVYU267E.js} +3 -3
  63. package/dist/chunk-YVYU267E.js.map +1 -0
  64. package/dist/{chunk-MGGU3ZRK.cjs → chunk-ZV5OKQ3Z.cjs} +1 -1
  65. package/dist/chunk-ZV5OKQ3Z.cjs.map +1 -0
  66. package/dist/chunks-QMGCI5DG.js +10 -0
  67. package/dist/cli.js +28 -24
  68. package/dist/cli.js.map +1 -1
  69. package/dist/{formula-IWV5IJ3Y.cjs → formula-CG4S7TWO.cjs} +8 -8
  70. package/dist/formula-CG4S7TWO.cjs.map +1 -0
  71. package/dist/{formula-7ZPC54JO.js → formula-JLYS73QL.js} +2 -2
  72. package/dist/formula-JLYS73QL.js.map +1 -0
  73. package/dist/{formula-EQRHSMYW.js → formula-LIVQSCMM.js} +2 -2
  74. package/dist/formula-LIVQSCMM.js.map +1 -0
  75. package/dist/{image-ocr-XEJFXH6M.cjs → image-ocr-F2BQA26V.cjs} +11 -11
  76. package/dist/image-ocr-F2BQA26V.cjs.map +1 -0
  77. package/dist/{image-ocr-G2LYTEUH.js → image-ocr-U2DRRFL3.js} +5 -5
  78. package/dist/image-ocr-U2DRRFL3.js.map +1 -0
  79. package/dist/{image-ocr-R44RYDBU.js → image-ocr-XC52AQDV.js} +7 -7
  80. package/dist/image-ocr-XC52AQDV.js.map +1 -0
  81. package/dist/index.cjs +858 -537
  82. package/dist/index.cjs.map +1 -1
  83. package/dist/index.d.cts +7 -0
  84. package/dist/index.d.ts +7 -0
  85. package/dist/index.js +373 -52
  86. package/dist/index.js.map +1 -1
  87. package/dist/mcp.js +24 -22
  88. package/dist/mcp.js.map +1 -1
  89. package/dist/{models-6VMAVZR6.js → models-6SIKLKRH.js} +3 -3
  90. package/dist/page-range-737B4EZW.js +0 -0
  91. package/dist/page-range-P7SDW6LR.cjs.map +1 -1
  92. package/dist/{parser-W3POOYQQ.js → parser-ESGFWWCT.js} +8 -8
  93. package/dist/parser-ESGFWWCT.js.map +1 -0
  94. package/dist/{parser-W3ZGQUCQ.cjs → parser-FFQN67O6.cjs} +25 -25
  95. package/dist/parser-FFQN67O6.cjs.map +1 -0
  96. package/dist/{parser-2G245MMN.js → parser-TFPGQWCC.js} +6 -6
  97. package/dist/parser-TFPGQWCC.js.map +1 -0
  98. package/dist/pdf-ocr-4R646OO6.js +12 -0
  99. package/dist/pdf-ocr-UQBZH3HE.cjs +13 -0
  100. package/dist/pdf-ocr-UQBZH3HE.cjs.map +1 -0
  101. package/dist/pdf-ocr-WE5TMRFU.js +15 -0
  102. package/dist/{profile-io-72R3B7KO.js → profile-io-O6BDCUJN.js} +3 -3
  103. package/dist/{rasterize-GNJQVURE.js → rasterize-V22VCCE7.js} +2 -2
  104. package/dist/rasterize-V22VCCE7.js.map +1 -0
  105. package/dist/{redact-W5K3ILQH.js → redact-7ILEAUTS.js} +2 -2
  106. package/dist/render-3BSVTTZE.js +10 -0
  107. package/dist/seal-6ZSEJVOD.js +10 -0
  108. package/dist/{setup-I4I75QJU.js → setup-GUQT5ELJ.js} +1 -1
  109. package/dist/setup-GUQT5ELJ.js.map +1 -0
  110. package/dist/{watch-2ADAJLIJ.js → watch-HGKBICED.js} +11 -11
  111. package/dist/watch-HGKBICED.js.map +1 -0
  112. package/package.json +103 -101
  113. package/templates//352/260/204/354/235/264/352/270/260/354/225/210/353/254/270_/354/230/210/354/213/234/352/260/222.json +9 -9
  114. package/templates//354/235/274/353/260/230/352/270/260/354/225/210/353/254/270_/354/204/234/354/213/235.hwpx +0 -0
  115. package/templates//354/235/274/353/260/230/352/270/260/354/225/210/353/254/270_/354/230/210/354/213/234/352/260/222.json +24 -24
  116. package/dist/chunk-3FFPHTXD.js.map +0 -1
  117. package/dist/chunk-4DBCUF3C.js.map +0 -1
  118. package/dist/chunk-6GBLFQMA.js.map +0 -1
  119. package/dist/chunk-6RAYTNQT.js.map +0 -1
  120. package/dist/chunk-6XPXIG57.js.map +0 -1
  121. package/dist/chunk-7MFO4TKP.js.map +0 -1
  122. package/dist/chunk-AL34DGS5.cjs.map +0 -1
  123. package/dist/chunk-ARXNKC5D.js.map +0 -1
  124. package/dist/chunk-AUOEZKJE.cjs.map +0 -1
  125. package/dist/chunk-DRQUAYNT.js.map +0 -1
  126. package/dist/chunk-DRZUJ2RK.js.map +0 -1
  127. package/dist/chunk-EFVAY5VE.js.map +0 -1
  128. package/dist/chunk-EZHW3RNQ.js.map +0 -1
  129. package/dist/chunk-GSMPTZC7.js.map +0 -1
  130. package/dist/chunk-JFV7QT5T.cjs.map +0 -1
  131. package/dist/chunk-LPQWKQQQ.cjs.map +0 -1
  132. package/dist/chunk-MGGU3ZRK.cjs.map +0 -1
  133. package/dist/chunk-NMWXBJ5B.js.map +0 -1
  134. package/dist/chunk-O5EK52TG.js.map +0 -1
  135. package/dist/chunk-SPKBIFSK.js.map +0 -1
  136. package/dist/chunk-US4WFSIQ.js.map +0 -1
  137. package/dist/chunk-VXMLENCV.js.map +0 -1
  138. package/dist/chunk-W34QSJFF.js.map +0 -1
  139. package/dist/chunk-WFOQFJI4.js.map +0 -1
  140. package/dist/chunk-YNJDNP44.js.map +0 -1
  141. package/dist/chunk-ZSL6MQVE.js.map +0 -1
  142. package/dist/chunks-Q7T4SQGW.js +0 -10
  143. package/dist/formula-7ZPC54JO.js.map +0 -1
  144. package/dist/formula-EQRHSMYW.js.map +0 -1
  145. package/dist/formula-IWV5IJ3Y.cjs.map +0 -1
  146. package/dist/image-ocr-G2LYTEUH.js.map +0 -1
  147. package/dist/image-ocr-R44RYDBU.js.map +0 -1
  148. package/dist/image-ocr-XEJFXH6M.cjs.map +0 -1
  149. package/dist/parser-2G245MMN.js.map +0 -1
  150. package/dist/parser-W3POOYQQ.js.map +0 -1
  151. package/dist/parser-W3ZGQUCQ.cjs.map +0 -1
  152. package/dist/pdf-ocr-3GDTJSHF.js +0 -15
  153. package/dist/pdf-ocr-N7IU23UV.cjs +0 -13
  154. package/dist/pdf-ocr-N7IU23UV.cjs.map +0 -1
  155. package/dist/pdf-ocr-U7MEGNBZ.js +0 -12
  156. package/dist/rasterize-GNJQVURE.js.map +0 -1
  157. package/dist/render-6OW6U4L4.js +0 -10
  158. package/dist/seal-GTNLVBJX.js +0 -10
  159. package/dist/setup-I4I75QJU.js.map +0 -1
  160. package/dist/watch-2ADAJLIJ.js.map +0 -1
  161. /package/dist/{-DYHT3HWH.js.map → -R6NUMPRV.js.map} +0 -0
  162. /package/dist/{chunks-Q7T4SQGW.js.map → chunks-QMGCI5DG.js.map} +0 -0
  163. /package/dist/{models-6VMAVZR6.js.map → models-6SIKLKRH.js.map} +0 -0
  164. /package/dist/{pdf-ocr-3GDTJSHF.js.map → pdf-ocr-4R646OO6.js.map} +0 -0
  165. /package/dist/{pdf-ocr-U7MEGNBZ.js.map → pdf-ocr-WE5TMRFU.js.map} +0 -0
  166. /package/dist/{profile-io-72R3B7KO.js.map → profile-io-O6BDCUJN.js.map} +0 -0
  167. /package/dist/{redact-W5K3ILQH.js.map → redact-7ILEAUTS.js.map} +0 -0
  168. /package/dist/{render-6OW6U4L4.js.map → render-3BSVTTZE.js.map} +0 -0
  169. /package/dist/{seal-GTNLVBJX.js.map → seal-6ZSEJVOD.js.map} +0 -0
@@ -1,12 +1,12 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  inlineImagesIntoMarkdown
4
- } from "./chunk-W34QSJFF.js";
4
+ } from "./chunk-7FCNOKOR.js";
5
5
  import {
6
6
  HEADING_RATIO_H1,
7
7
  HEADING_RATIO_H2,
8
8
  HEADING_RATIO_H3
9
- } from "./chunk-ZSL6MQVE.js";
9
+ } from "./chunk-A73RMJ6B.js";
10
10
  import {
11
11
  parsePageRange
12
12
  } from "./chunk-MOL7MDBG.js";
@@ -22,7 +22,7 @@ import {
22
22
  paraTextPureT,
23
23
  patchZipEntries,
24
24
  scanSectionXml
25
- } from "./chunk-NMWXBJ5B.js";
25
+ } from "./chunk-RZURSA4N.js";
26
26
  import {
27
27
  MAX_COLS,
28
28
  MAX_ROWS,
@@ -32,14 +32,14 @@ import {
32
32
  dedupeRunningHeaders,
33
33
  flattenLayoutTables,
34
34
  mapPuaText
35
- } from "./chunk-GSMPTZC7.js";
35
+ } from "./chunk-QXYB74QF.js";
36
36
  import {
37
37
  SPACE_EM_FIXED,
38
38
  charWidthEm1000,
39
39
  fitRatioForFewerLines,
40
40
  measureTextWidth,
41
41
  simulateWrap
42
- } from "./chunk-VXMLENCV.js";
42
+ } from "./chunk-65DYCXGC.js";
43
43
  import {
44
44
  MAX_DECOMPRESS_SIZE,
45
45
  MAX_XML_DEPTH,
@@ -55,7 +55,7 @@ import {
55
55
  findChildByLocalName,
56
56
  localName,
57
57
  rawTextContent
58
- } from "./chunk-EFVAY5VE.js";
58
+ } from "./chunk-5KLE3U23.js";
59
59
  import {
60
60
  KordocError,
61
61
  classifyError,
@@ -67,7 +67,7 @@ import {
67
67
  sanitizeHref,
68
68
  stripDtd,
69
69
  toArrayBuffer
70
- } from "./chunk-DRQUAYNT.js";
70
+ } from "./chunk-P7YPCK7C.js";
71
71
 
72
72
  // src/index.ts
73
73
  import { readFile } from "fs/promises";
@@ -446,6 +446,91 @@ function comResultToParseResult(pages, pageCount, warnings) {
446
446
  };
447
447
  }
448
448
 
449
+ // src/hwpx/crypto.ts
450
+ import { createDecipheriv, createHash, pbkdf2Sync, timingSafeEqual } from "crypto";
451
+ import { inflateRawSync } from "zlib";
452
+ var MAX_ITERATION_COUNT = 1e6;
453
+ var AES256_CBC = "http://www.w3.org/2001/04/xmlenc#aes256-cbc";
454
+ var START_KEY_SHA256 = "http://www.w3.org/2000/09/xmldsig#sha256";
455
+ var CHECKSUM_SHA256_1K = "sha256-1k";
456
+ var CHECKSUM_PREFIX_BYTES = 1024;
457
+ var attrOf = (xml, name) => xml.match(new RegExp(`${name}="([^"]*)"`))?.[1];
458
+ function parseEncryptionManifest(manifestXml) {
459
+ const entries = [];
460
+ const entryRe = /<[^:>\s]*:?file-entry\b([^>]*)>([\s\S]*?)<\/[^:>\s]*:?file-entry>/g;
461
+ let m;
462
+ while ((m = entryRe.exec(manifestXml)) !== null) {
463
+ const [, attrs, inner] = m;
464
+ if (!inner.includes("encryption-data")) continue;
465
+ const path = attrOf(attrs, "full-path");
466
+ if (!path) continue;
467
+ const algorithm = attrOf(inner, "algorithm-name");
468
+ const startKey = attrOf(inner, "start-key-generation-name");
469
+ const checksumType = attrOf(inner, "checksum-type");
470
+ if (algorithm !== AES256_CBC) {
471
+ throw new KordocError(`\uC9C0\uC6D0\uD558\uC9C0 \uC54A\uB294 HWPX \uC554\uD638\uD654 \uBC29\uC2DD\uC785\uB2C8\uB2E4: ${algorithm ?? "(\uBBF8\uC0C1)"}`);
472
+ }
473
+ if (startKey !== START_KEY_SHA256) {
474
+ throw new KordocError(`\uC9C0\uC6D0\uD558\uC9C0 \uC54A\uB294 HWPX \uC2DC\uC791\uD0A4 \uC0DD\uC131 \uBC29\uC2DD\uC785\uB2C8\uB2E4: ${startKey ?? "(\uBBF8\uC0C1)"}`);
475
+ }
476
+ if (!checksumType?.endsWith(CHECKSUM_SHA256_1K)) {
477
+ throw new KordocError(`\uC9C0\uC6D0\uD558\uC9C0 \uC54A\uB294 HWPX \uCCB4\uD06C\uC12C \uBC29\uC2DD\uC785\uB2C8\uB2E4: ${checksumType ?? "(\uBBF8\uC0C1)"}`);
478
+ }
479
+ const iterations = parseInt(attrOf(inner, "iteration-count") ?? "", 10);
480
+ const keySize = parseInt(attrOf(inner, "key-size") ?? "", 10);
481
+ const iv = Buffer.from(attrOf(inner, "initialisation-vector") ?? "", "base64");
482
+ const salt = Buffer.from(attrOf(inner, "salt") ?? "", "base64");
483
+ const checksum = Buffer.from(attrOf(inner, "checksum") ?? "", "base64");
484
+ if (!(iterations > 0 && iterations <= MAX_ITERATION_COUNT)) {
485
+ throw new KordocError(`HWPX \uC554\uD638\uD654 \uBC18\uBCF5 \uD69F\uC218\uAC00 \uD5C8\uC6A9 \uBC94\uC704\uB97C \uBC97\uC5B4\uB0AC\uC2B5\uB2C8\uB2E4: ${iterations}`);
486
+ }
487
+ if (keySize !== 32 || iv.length !== 16 || salt.length === 0 || checksum.length !== 32) {
488
+ throw new KordocError(`HWPX \uC554\uD638\uD654 \uD30C\uB77C\uBBF8\uD130\uAC00 \uC62C\uBC14\uB974\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4 (${path})`);
489
+ }
490
+ entries.push({ path, checksum, iv, salt, iterations, keySize });
491
+ }
492
+ return entries;
493
+ }
494
+ function stripEncryptionData(manifestXml) {
495
+ return manifestXml.replace(/<([^:>\s]*:?)encryption-data\b[\s\S]*?<\/\1encryption-data>/g, "");
496
+ }
497
+ function decryptEntry(cipher, entry, startKey) {
498
+ if (cipher.length === 0 || cipher.length % 16 !== 0) return void 0;
499
+ for (const prf of ["sha1", "sha256"]) {
500
+ try {
501
+ const key = pbkdf2Sync(startKey, entry.salt, entry.iterations, entry.keySize, prf);
502
+ const decipher = createDecipheriv("aes-256-cbc", key, entry.iv);
503
+ decipher.setAutoPadding(false);
504
+ const raw = Buffer.concat([decipher.update(cipher), decipher.final()]);
505
+ const plain = inflateRawSync(raw, { maxOutputLength: MAX_DECOMPRESS_SIZE });
506
+ const sum = createHash("sha256").update(plain.subarray(0, Math.min(CHECKSUM_PREFIX_BYTES, plain.length))).digest();
507
+ if (sum.length === entry.checksum.length && timingSafeEqual(sum, entry.checksum)) return plain;
508
+ } catch {
509
+ }
510
+ }
511
+ return void 0;
512
+ }
513
+ async function decryptHwpxInPlace(zip, manifestXml, password) {
514
+ const entries = parseEncryptionManifest(manifestXml);
515
+ if (entries.length === 0) return 0;
516
+ const startKey = createHash("sha256").update(Buffer.from(password, "utf8")).digest();
517
+ const decrypted = [];
518
+ for (const entry of entries) {
519
+ const file = zip.file(entry.path);
520
+ if (!file) {
521
+ throw new KordocError(`\uC554\uD638 \uBAA9\uB85D\uC5D0 \uC788\uB294 \uD30C\uC77C\uC774 \uBB38\uC11C\uC5D0 \uC5C6\uC2B5\uB2C8\uB2E4: ${entry.path}`);
522
+ }
523
+ const plain = decryptEntry(await file.async("nodebuffer"), entry, startKey);
524
+ if (!plain) {
525
+ throw new KordocError("\uBE44\uBC00\uBC88\uD638\uAC00 \uC77C\uCE58\uD558\uC9C0 \uC54A\uAC70\uB098 \uC554\uD638\uD654 \uB370\uC774\uD130\uAC00 \uC190\uC0C1\uB418\uC5C8\uC2B5\uB2C8\uB2E4.");
526
+ }
527
+ decrypted.push({ path: entry.path, plain });
528
+ }
529
+ for (const { path, plain } of decrypted) zip.file(path, plain);
530
+ zip.file("META-INF/manifest.xml", stripEncryptionData(manifestXml));
531
+ return decrypted.length;
532
+ }
533
+
449
534
  // src/hwpx/styles.ts
450
535
  function isRealStrikeShape(shape) {
451
536
  switch (shape) {
@@ -1140,8 +1225,8 @@ function hmlToLatex(hmlEqStr) {
1140
1225
  let tokens = s.split(" ");
1141
1226
  for (let i = 0; i < tokens.length; i++) {
1142
1227
  const t = tokens[i];
1143
- if (t in CONVERT_MAP) tokens[i] = CONVERT_MAP[t];
1144
- else if (t in MIDDLE_CONVERT_MAP) tokens[i] = MIDDLE_CONVERT_MAP[t];
1228
+ if (Object.hasOwn(CONVERT_MAP, t)) tokens[i] = CONVERT_MAP[t];
1229
+ else if (Object.hasOwn(MIDDLE_CONVERT_MAP, t)) tokens[i] = MIDDLE_CONVERT_MAP[t];
1145
1230
  else {
1146
1231
  const quoted = /^"(.+)"$/.exec(t);
1147
1232
  if (quoted) tokens[i] = `\\text{${quoted[1]}}`;
@@ -2002,7 +2087,7 @@ function extractParagraphInfo(para2, styleMap, ctx) {
2002
2087
  if (!children) return;
2003
2088
  for (let i = 0; i < children.length; i++) {
2004
2089
  const child = children[i];
2005
- if (child.nodeType === 3) {
2090
+ if (child.nodeType === 3 || child.nodeType === 4) {
2006
2091
  const t = child.textContent || "";
2007
2092
  if (isInDeletedRange(ctx)) {
2008
2093
  if (t && ctx && !ctx.shared.track.warned) {
@@ -2213,7 +2298,7 @@ function extractRunSpans(para2, ctx, mode, requireMixed) {
2213
2298
  const tkids = rc.childNodes;
2214
2299
  for (let k = 0; k < (tkids?.length ?? 0); k++) {
2215
2300
  const tk = tkids[k];
2216
- if (tk.nodeType === 3) text += tk.textContent || "";
2301
+ if (tk.nodeType === 3 || tk.nodeType === 4) text += tk.textContent || "";
2217
2302
  else if (tk.nodeType === 1) return null;
2218
2303
  }
2219
2304
  } else if (rtag === "secPr" || rtag === "colPr") {
@@ -2271,7 +2356,7 @@ function gongmunDepthFromIndent(para2, left, ctx) {
2271
2356
  }
2272
2357
 
2273
2358
  // src/hwp5/record.ts
2274
- import { inflateRawSync, inflateSync } from "zlib";
2359
+ import { inflateRawSync as inflateRawSync2, inflateSync } from "zlib";
2275
2360
  var TAG_PARA_HEADER = 66;
2276
2361
  var TAG_PARA_TEXT = 67;
2277
2362
  var TAG_CHAR_SHAPE = 68;
@@ -2329,7 +2414,7 @@ function decompressStream(data) {
2329
2414
  } catch {
2330
2415
  }
2331
2416
  }
2332
- return inflateRawSync(data, opts);
2417
+ return inflateRawSync2(data, opts);
2333
2418
  }
2334
2419
  function parseFileHeader(data) {
2335
2420
  if (data.length < 40) throw new KordocError("FileHeader\uAC00 \uB108\uBB34 \uC9E7\uC2B5\uB2C8\uB2E4 (\uCD5C\uC18C 40\uBC14\uC774\uD2B8)");
@@ -2850,7 +2935,7 @@ async function extractImagesFromZip(zip, blocks, decompressed, warnings, sweepUn
2850
2935
  import JSZip2 from "jszip";
2851
2936
 
2852
2937
  // src/hwpx/zip-sections.ts
2853
- import { inflateRawSync as inflateRawSync2 } from "zlib";
2938
+ import { inflateRawSync as inflateRawSync3 } from "zlib";
2854
2939
  function extractFromBrokenZip(buffer) {
2855
2940
  const data = new Uint8Array(buffer);
2856
2941
  const view = new DataView(buffer);
@@ -2901,7 +2986,7 @@ function extractFromBrokenZip(buffer) {
2901
2986
  if (method === 0) {
2902
2987
  content = new TextDecoder().decode(fileData);
2903
2988
  } else if (method === 8) {
2904
- const decompressed = inflateRawSync2(Buffer.from(fileData), { maxOutputLength: MAX_DECOMPRESS_SIZE });
2989
+ const decompressed = inflateRawSync3(Buffer.from(fileData), { maxOutputLength: MAX_DECOMPRESS_SIZE });
2905
2990
  content = new TextDecoder().decode(decompressed);
2906
2991
  } else {
2907
2992
  continue;
@@ -3039,13 +3124,19 @@ async function parseHwpxDocument(buffer, options) {
3039
3124
  if (manifestFile) {
3040
3125
  const manifestXml = await manifestFile.async("text");
3041
3126
  if (isEncryptedHwpx(manifestXml)) {
3042
- if (isComFallbackAvailable() && options?.filePath) {
3043
- const { pages, pageCount, warnings: warnings2 } = extractTextViaCom(options.filePath);
3044
- if (pages.some((p) => p && p.trim().length > 0)) {
3045
- return comResultToParseResult(pages, pageCount, warnings2);
3127
+ if (options?.password) {
3128
+ await decryptHwpxInPlace(zip, manifestXml, options.password);
3129
+ } else {
3130
+ if (isComFallbackAvailable() && options?.filePath) {
3131
+ const { pages, pageCount, warnings: warnings2 } = extractTextViaCom(options.filePath);
3132
+ if (pages.some((p) => p && p.trim().length > 0)) {
3133
+ return comResultToParseResult(pages, pageCount, warnings2);
3134
+ }
3046
3135
  }
3136
+ throw new KordocError(
3137
+ "\uC554\uD638\uB85C \uBCF4\uD638\uB41C HWPX \uD30C\uC77C\uC785\uB2C8\uB2E4. password \uC635\uC158\uC5D0 \uC5F4\uAE30 \uC554\uD638\uB97C \uC9C0\uC815\uD558\uC138\uC694."
3138
+ );
3047
3139
  }
3048
- throw new KordocError("DRM \uC554\uD638\uD654\uB41C HWPX \uD30C\uC77C\uC785\uB2C8\uB2E4. Windows + \uD55C\uCEF4 \uC624\uD53C\uC2A4 \uC124\uCE58 \uC2DC \uC790\uB3D9 \uCD94\uCD9C\uB429\uB2C8\uB2E4.");
3049
3140
  }
3050
3141
  }
3051
3142
  const decompressed = { total: 0 };
@@ -4000,11 +4091,11 @@ var WORD_ALIASES = {
4000
4091
  var STRUCTURAL_FOLD = /* @__PURE__ */ new Set(["over", "root", "of"]);
4001
4092
  function normalizeWords(input) {
4002
4093
  return input.replace(/(?<![\\A-Za-z0-9])([A-Za-z][A-Za-z0-9]*)(?![A-Za-z0-9])/g, (word) => {
4003
- if (word in CONVERT_MAP || word in MIDDLE_CONVERT_MAP) return word;
4094
+ if (Object.hasOwn(CONVERT_MAP, word) || Object.hasOwn(MIDDLE_CONVERT_MAP, word)) return word;
4004
4095
  const lower = word.toLowerCase();
4005
4096
  if (STRUCTURAL_FOLD.has(lower)) return lower;
4006
- if (lower in WORD_ALIASES) return WORD_ALIASES[lower];
4007
- if (lower !== word && (lower in CONVERT_MAP || lower in MIDDLE_CONVERT_MAP)) return lower;
4097
+ if (Object.hasOwn(WORD_ALIASES, lower)) return WORD_ALIASES[lower];
4098
+ if (lower !== word && (Object.hasOwn(CONVERT_MAP, lower) || Object.hasOwn(MIDDLE_CONVERT_MAP, lower))) return lower;
4008
4099
  return word;
4009
4100
  });
4010
4101
  }
@@ -4024,6 +4115,84 @@ function hwpEquationToLatex(equation) {
4024
4115
  return hmlToLatex(s).replace(/\s+/g, " ").trim();
4025
4116
  }
4026
4117
 
4118
+ // src/hwp5/pw-crypto.ts
4119
+ import { createCipheriv, createHash as createHash2 } from "crypto";
4120
+ var ENCRYPT_VERSION_OFFSET = 44;
4121
+ var SUPPORTED_ENCRYPT_VERSION = 4;
4122
+ var KEY_SEED = 236;
4123
+ function readEncryptVersion(fileHeader) {
4124
+ if (fileHeader.length < ENCRYPT_VERSION_OFFSET + 4) return 0;
4125
+ return (fileHeader[ENCRYPT_VERSION_OFFSET] | fileHeader[ENCRYPT_VERSION_OFFSET + 1] << 8 | fileHeader[ENCRYPT_VERSION_OFFSET + 2] << 16 | fileHeader[ENCRYPT_VERSION_OFFSET + 3] << 24) >>> 0;
4126
+ }
4127
+ function derivePasswordKey(password) {
4128
+ const buf = Buffer.alloc(password.length * 2);
4129
+ for (let i = 0; i < password.length; i++) {
4130
+ const prev = i === 0 ? KEY_SEED : password[i - 1];
4131
+ buf[i * 2] = (prev << 1 | prev >> 7) & 255;
4132
+ buf[i * 2 + 1] = password[i];
4133
+ }
4134
+ return createHash2("sha1").update(buf).digest().subarray(0, 16);
4135
+ }
4136
+ function padToBlock(data) {
4137
+ const rem = data.length % 16;
4138
+ if (rem === 0) return Buffer.from(data);
4139
+ const amount = 16 - rem;
4140
+ return Buffer.concat([Buffer.from(data), Buffer.alloc(amount, amount)]);
4141
+ }
4142
+ function shiftRegister(reg, feedbackBit) {
4143
+ for (let i = 0; i < 15; i++) {
4144
+ reg[i] = (reg[i] << 1 | reg[i + 1] >> 7) & 255;
4145
+ }
4146
+ reg[15] = (reg[15] << 1 | feedbackBit & 1) & 255;
4147
+ }
4148
+ function decryptAesCfb1(data, key) {
4149
+ const cipher = createCipheriv("aes-128-ecb", key, null);
4150
+ cipher.setAutoPadding(false);
4151
+ const register = new Uint8Array(16);
4152
+ const registerBuf = Buffer.alloc(16);
4153
+ const out = Buffer.alloc(data.length);
4154
+ for (let offset = 0; offset < data.length; offset += 16) {
4155
+ const len = Math.min(16, data.length - offset);
4156
+ const chunk = Buffer.alloc(16);
4157
+ data.copy(chunk, 0, offset, offset + len);
4158
+ for (let i = 0; i < 128; i++) {
4159
+ registerBuf.set(register);
4160
+ const keystreamMsb = cipher.update(registerBuf)[0];
4161
+ const byteIdx = i >> 3;
4162
+ const bitInByte = i & 7;
4163
+ const cipherBit = chunk[byteIdx] >> 7 - bitInByte & 1;
4164
+ shiftRegister(register, cipherBit);
4165
+ chunk[byteIdx] ^= (keystreamMsb & 128) >> bitInByte;
4166
+ }
4167
+ chunk.copy(out, offset, 0, len);
4168
+ }
4169
+ return out;
4170
+ }
4171
+ function decryptPasswordStream(raw, password) {
4172
+ const key = derivePasswordKey(Buffer.from(password, "utf8"));
4173
+ return decryptAesCfb1(padToBlock(raw), key).subarray(0, raw.length);
4174
+ }
4175
+ var TAG_DOCUMENT_PROPERTIES = 16;
4176
+ var TAG_ID_MAPPINGS = 17;
4177
+ function assertDecryptedDocInfo(data, readRecords3) {
4178
+ const wrong = () => new KordocError("\uBE44\uBC00\uBC88\uD638\uAC00 \uC77C\uCE58\uD558\uC9C0 \uC54A\uAC70\uB098 \uC554\uD638\uD654 \uB370\uC774\uD130\uAC00 \uC190\uC0C1\uB418\uC5C8\uC2B5\uB2C8\uB2E4.");
4179
+ let records;
4180
+ try {
4181
+ records = readRecords3(data);
4182
+ } catch {
4183
+ throw wrong();
4184
+ }
4185
+ if (records[0]?.tagId !== TAG_DOCUMENT_PROPERTIES) throw wrong();
4186
+ if (!records.some((r) => r.tagId === TAG_ID_MAPPINGS)) throw wrong();
4187
+ }
4188
+ function assertSupportedEncryptVersion(version) {
4189
+ if (version !== SUPPORTED_ENCRYPT_VERSION) {
4190
+ throw new KordocError(
4191
+ `\uC9C0\uC6D0\uD558\uC9C0 \uC54A\uB294 HWP \uC554\uD638\uD654 \uBC84\uC804\uC785\uB2C8\uB2E4 (EncryptVersion=${version}, \uC9C0\uC6D0: ${SUPPORTED_ENCRYPT_VERSION})`
4192
+ );
4193
+ }
4194
+ }
4195
+
4027
4196
  // src/hwp5/parser.ts
4028
4197
  import { createRequire } from "module";
4029
4198
  var require2 = createRequire(import.meta.url);
@@ -4104,27 +4273,50 @@ function parseHwp5Document(buffer, options) {
4104
4273
  throw new KordocError("CFB \uCEE8\uD14C\uC774\uB108 \uD30C\uC2F1 \uC2E4\uD328 (strict \uBC0F lenient \uBAA8\uB450)");
4105
4274
  }
4106
4275
  }
4107
- const findStream = (path) => {
4276
+ const readRawStream = (path) => {
4108
4277
  if (cfb) {
4109
4278
  const entry = CFB.find(cfb, path);
4110
4279
  return entry?.content ? Buffer.from(entry.content) : null;
4111
4280
  }
4112
4281
  return lenientCfb.findStream(path);
4113
4282
  };
4114
- const headerData = findStream("/FileHeader");
4283
+ const headerData = readRawStream("/FileHeader");
4115
4284
  if (!headerData) throw new KordocError("FileHeader \uC2A4\uD2B8\uB9BC \uC5C6\uC74C");
4116
4285
  const header = parseFileHeader(headerData);
4117
4286
  if (header.signature !== "HWP Document File") throw new KordocError("HWP \uC2DC\uADF8\uB2C8\uCC98 \uBD88\uC77C\uCE58");
4118
- if (header.flags & FLAG_ENCRYPTED) throw new KordocError("\uC554\uD638\uD654\uB41C HWP\uB294 \uC9C0\uC6D0\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4");
4119
4287
  if (header.flags & FLAG_DRM) throw new KordocError("DRM \uBCF4\uD638\uB41C HWP\uB294 \uC9C0\uC6D0\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4");
4288
+ const encrypted = (header.flags & FLAG_ENCRYPTED) !== 0;
4289
+ if (encrypted) {
4290
+ if (!options?.password) {
4291
+ throw new KordocError("\uC554\uD638\uB85C \uBCF4\uD638\uB41C HWP \uBB38\uC11C\uC785\uB2C8\uB2E4. password \uC635\uC158\uC5D0 \uC5F4\uAE30 \uC554\uD638\uB97C \uC9C0\uC815\uD558\uC138\uC694.");
4292
+ }
4293
+ assertSupportedEncryptVersion(readEncryptVersion(headerData));
4294
+ }
4295
+ const password = encrypted ? options.password : void 0;
4296
+ const findStream = (path) => {
4297
+ const raw = readRawStream(path);
4298
+ if (!raw || !password) return raw;
4299
+ return decryptPasswordStream(raw, password);
4300
+ };
4120
4301
  const compressed = (header.flags & FLAG_COMPRESSED) !== 0;
4121
4302
  const distribution = (header.flags & FLAG_DISTRIBUTION) !== 0;
4122
4303
  const metadata = {
4123
4304
  version: `${header.versionMajor}.x`
4124
4305
  };
4125
4306
  if (cfb) extractHwp5Metadata(cfb, metadata);
4126
- const docInfo = cfb ? parseDocInfoStream(cfb, compressed) : parseDocInfoFromStream(findStream("/DocInfo"), compressed);
4127
- const sections = distribution ? cfb ? findViewTextSections(cfb, compressed) : findViewTextSectionsLenient(lenientCfb, compressed) : cfb ? findSections(cfb) : findSectionsLenient(lenientCfb, compressed);
4307
+ if (encrypted) {
4308
+ const rawDocInfo = findStream("/DocInfo");
4309
+ if (!rawDocInfo) throw new KordocError("DocInfo \uC2A4\uD2B8\uB9BC \uC5C6\uC74C");
4310
+ let docInfoData;
4311
+ try {
4312
+ docInfoData = compressed ? decompressStream(rawDocInfo) : rawDocInfo;
4313
+ } catch {
4314
+ throw new KordocError("\uBE44\uBC00\uBC88\uD638\uAC00 \uC77C\uCE58\uD558\uC9C0 \uC54A\uAC70\uB098 \uC554\uD638\uD654 \uB370\uC774\uD130\uAC00 \uC190\uC0C1\uB418\uC5C8\uC2B5\uB2C8\uB2E4.");
4315
+ }
4316
+ assertDecryptedDocInfo(docInfoData, readRecords);
4317
+ }
4318
+ const docInfo = cfb && !encrypted ? parseDocInfoStream(cfb, compressed) : parseDocInfoFromStream(findStream("/DocInfo"), compressed);
4319
+ const sections = distribution ? cfb ? findViewTextSections(cfb, compressed) : findViewTextSectionsLenient(lenientCfb, compressed) : encrypted ? findSectionsVia(findStream) : cfb ? findSections(cfb) : findSectionsLenient(lenientCfb, compressed);
4128
4320
  if (sections.length === 0) throw new KordocError("\uC139\uC158 \uC2A4\uD2B8\uB9BC\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4");
4129
4321
  metadata.pageCount = sections.length;
4130
4322
  const pageFilter = options?.pages ? parsePageRange(options.pages, sections.length) : null;
@@ -4306,6 +4498,15 @@ function findViewTextSections(cfb, compressed) {
4306
4498
  }
4307
4499
  return sections.sort((a, b) => a.idx - b.idx).map((s) => s.content);
4308
4500
  }
4501
+ function findSectionsVia(read) {
4502
+ const sections = [];
4503
+ for (let i = 0; i < MAX_SECTIONS; i++) {
4504
+ const raw = read(`/BodyText/Section${i}`);
4505
+ if (!raw) break;
4506
+ sections.push(raw);
4507
+ }
4508
+ return sections;
4509
+ }
4309
4510
  function findSections(cfb) {
4310
4511
  const sections = [];
4311
4512
  for (let i = 0; i < MAX_SECTIONS; i++) {
@@ -4911,7 +5112,7 @@ function hasRealStrike(attrFlags) {
4911
5112
  }
4912
5113
 
4913
5114
  // src/hwp3/parser.ts
4914
- import { inflateRawSync as inflateRawSync3 } from "zlib";
5115
+ import { inflateRawSync as inflateRawSync5 } from "zlib";
4915
5116
 
4916
5117
  // src/hwp3/johab-symbols.ts
4917
5118
  var JOHAB_SYMBOLS = Object.freeze([
@@ -16819,6 +17020,23 @@ function lookupSymbol(ch) {
16819
17020
  return null;
16820
17021
  }
16821
17022
  var JOHAB_UNMAPPED = -1;
17023
+ var JUNG_ARAEA = 30;
17024
+ var ARAEA_JAMO = 4510;
17025
+ function decodeAraeaSyllable(ch) {
17026
+ if (ch < 32768) return null;
17027
+ if ((ch >> 5 & 31) !== JUNG_ARAEA) return null;
17028
+ const cho = CHO_MAP[ch >> 10 & 31];
17029
+ const jong = JONG_MAP[ch & 31];
17030
+ if (cho === -1 || jong === -1) return null;
17031
+ const out = String.fromCodePoint(4352 + cho) + String.fromCodePoint(ARAEA_JAMO);
17032
+ return jong === 0 ? out : out + String.fromCodePoint(4519 + jong);
17033
+ }
17034
+ function decodeJohabText(ch) {
17035
+ const araea = decodeAraeaSyllable(ch);
17036
+ if (araea) return araea;
17037
+ const cp = decodeJohab(ch);
17038
+ return cp === JOHAB_UNMAPPED ? null : String.fromCodePoint(cp);
17039
+ }
16822
17040
  function decodeJohab(ch) {
16823
17041
  if (ch < 128) return ch;
16824
17042
  if (ch >= 32768) {
@@ -16840,6 +17058,7 @@ function decodeJohab(ch) {
16840
17058
  function decodeHwp3Extra(ch) {
16841
17059
  if (ch >= 13712 && ch <= 13721) return 8544 + (ch - 13712);
16842
17060
  if (ch >= 14055 && ch <= 14064) return 9312 + (ch - 14055);
17061
+ if (ch >= 14272 && ch <= 14277) return "\uD55C\uAE00\uACFC\uCEF4\uD4E8\uD130".codePointAt(ch - 14272);
16843
17062
  switch (ch) {
16844
17063
  case 129:
16845
17064
  return 8220;
@@ -16874,6 +17093,9 @@ function decodeHwp3Extra(ch) {
16874
17093
  case 13434:
16875
17094
  return 9654;
16876
17095
  // ▶
17096
+ case 12135:
17097
+ return 9656;
17098
+ // ▸ 표 셀 글머리표 (rhwp 16db8260 — HWP5 변환본·한컴 PDF 대조)
16877
17099
  default:
16878
17100
  return JOHAB_UNMAPPED;
16879
17101
  }
@@ -16884,8 +17106,7 @@ function decodeHcharString(bytes) {
16884
17106
  while (i + 1 < bytes.length) {
16885
17107
  const ch = bytes[i] | bytes[i + 1] << 8;
16886
17108
  if (ch === 0) break;
16887
- const cp = decodeJohab(ch);
16888
- if (cp !== JOHAB_UNMAPPED) out += String.fromCodePoint(cp);
17109
+ out += decodeJohabText(ch) ?? "";
16889
17110
  i += 2;
16890
17111
  }
16891
17112
  return out;
@@ -16990,6 +17211,87 @@ function readHeader(reader) {
16990
17211
  return { compressed, encrypted, infoBlockLength, title, subject, author, date };
16991
17212
  }
16992
17213
 
17214
+ // src/hwp3/crypto.ts
17215
+ import { createDecipheriv as createDecipheriv2 } from "crypto";
17216
+ import { inflateRawSync as inflateRawSync4 } from "zlib";
17217
+ var FIXED_HEADER_BYTES = SIGNATURE_LEN + DOC_INFO_SIZE + DOC_SUMMARY_SIZE;
17218
+ var PASSWORD_FLAG_OFFSET = SIGNATURE_LEN + 96;
17219
+ var COMPRESSION_FLAG_OFFSET = SIGNATURE_LEN + 124;
17220
+ var INFO_BLOCK_LENGTH_OFFSET = SIGNATURE_LEN + 126;
17221
+ var PASSWORD_PREFIX_BYTES = 256;
17222
+ var MAX_DECOMPRESSED_BYTES = 100 * 1024 * 1024;
17223
+ function isEncryptedHwp3(data) {
17224
+ if (data.length < FIXED_HEADER_BYTES) return false;
17225
+ return (data[PASSWORD_FLAG_OFFSET] | data[PASSWORD_FLAG_OFFSET + 1] << 8) !== 0;
17226
+ }
17227
+ function deriveDesKey(password) {
17228
+ const rolling = new Uint8Array(7);
17229
+ let index = 0;
17230
+ for (let i = 0; i < password.length; i++) {
17231
+ const unit = password.charCodeAt(i);
17232
+ for (const byte of [unit & 255, unit >> 8 & 255]) {
17233
+ const slot = index % rolling.length;
17234
+ const mixed = rolling[slot] ^ byte;
17235
+ rolling[slot] = (mixed << 1 | mixed >> 7) & 255;
17236
+ index++;
17237
+ }
17238
+ }
17239
+ const key = Buffer.alloc(8);
17240
+ for (let bit = 0; bit < 56; bit++) {
17241
+ const sourceBit = rolling[Math.floor(bit / 8)] >> 7 - bit % 8 & 1;
17242
+ key[Math.floor(bit / 7)] |= sourceBit << 7 - bit % 7;
17243
+ }
17244
+ return key;
17245
+ }
17246
+ function desEcbDecrypt(data, key) {
17247
+ const decipher = createDecipheriv2("des-ede3-ecb", Buffer.concat([key, key, key]), null);
17248
+ decipher.setAutoPadding(false);
17249
+ return Buffer.concat([decipher.update(data), decipher.final()]);
17250
+ }
17251
+ function readLayout(data) {
17252
+ if (data.length < FIXED_HEADER_BYTES) {
17253
+ throw new KordocError("HWP3 \uC554\uD638 \uBB38\uC11C: \uD5E4\uB354\uAC00 \uC798\uB838\uC2B5\uB2C8\uB2E4");
17254
+ }
17255
+ const infoBlockLength = data[INFO_BLOCK_LENGTH_OFFSET] | data[INFO_BLOCK_LENGTH_OFFSET + 1] << 8;
17256
+ const payloadOffset = FIXED_HEADER_BYTES + infoBlockLength;
17257
+ if (payloadOffset >= data.length) {
17258
+ throw new KordocError("HWP3 \uC554\uD638 \uBB38\uC11C: \uBCF8\uBB38\uC774 \uC5C6\uC2B5\uB2C8\uB2E4");
17259
+ }
17260
+ return { payloadOffset, compressed: data[COMPRESSION_FLAG_OFFSET] !== 0 };
17261
+ }
17262
+ function decryptHwp3Document(input, password) {
17263
+ const { payloadOffset, compressed } = readLayout(input);
17264
+ if (!compressed) {
17265
+ throw new KordocError("\uC9C0\uC6D0\uD558\uC9C0 \uC54A\uB294 HWP3 \uC554\uD638\uD654 \uBC29\uC2DD\uC785\uB2C8\uB2E4 (\uC555\uCD95\uB418\uC9C0 \uC54A\uC740 \uC554\uD638 \uBCF8\uBB38)");
17266
+ }
17267
+ const source = Buffer.from(input);
17268
+ const payload = source.subarray(payloadOffset);
17269
+ const aligned = payload.subarray(0, payload.length - payload.length % 8);
17270
+ if (aligned.length === 0) {
17271
+ throw new KordocError("HWP3 \uC554\uD638 \uBB38\uC11C: \uBCF8\uBB38\uC774 \uB108\uBB34 \uC9E7\uC2B5\uB2C8\uB2E4");
17272
+ }
17273
+ const decrypted = desEcbDecrypt(aligned, deriveDesKey(password));
17274
+ let inflated;
17275
+ try {
17276
+ inflated = inflateRawSync4(decrypted, { maxOutputLength: MAX_DECOMPRESSED_BYTES });
17277
+ } catch (err) {
17278
+ if (err?.code === "ERR_BUFFER_TOO_LARGE") {
17279
+ throw new KordocError(
17280
+ `HWP3 \uC554\uD638 \uBCF8\uBB38\uC758 \uC555\uCD95 \uD574\uC81C \uACB0\uACFC\uAC00 \uCD5C\uB300 \uD5C8\uC6A9 \uD06C\uAE30(${MAX_DECOMPRESSED_BYTES / 1024 / 1024}MB)\uB97C \uCD08\uACFC\uD588\uC2B5\uB2C8\uB2E4`
17281
+ );
17282
+ }
17283
+ throw new KordocError("\uBE44\uBC00\uBC88\uD638\uAC00 \uC77C\uCE58\uD558\uC9C0 \uC54A\uAC70\uB098 \uC554\uD638\uD654 \uB370\uC774\uD130\uAC00 \uC190\uC0C1\uB418\uC5C8\uC2B5\uB2C8\uB2E4.");
17284
+ }
17285
+ if (inflated.length <= PASSWORD_PREFIX_BYTES) {
17286
+ throw new KordocError("\uBE44\uBC00\uBC88\uD638\uAC00 \uC77C\uCE58\uD558\uC9C0 \uC54A\uAC70\uB098 \uC554\uD638\uD654 \uB370\uC774\uD130\uAC00 \uC190\uC0C1\uB418\uC5C8\uC2B5\uB2C8\uB2E4.");
17287
+ }
17288
+ const body = inflated.subarray(PASSWORD_PREFIX_BYTES);
17289
+ const out = Buffer.concat([source.subarray(0, payloadOffset), body]);
17290
+ out.writeUInt16LE(0, PASSWORD_FLAG_OFFSET);
17291
+ out[COMPRESSION_FLAG_OFFSET] = 0;
17292
+ return out;
17293
+ }
17294
+
16993
17295
  // src/hwp3/parser.ts
16994
17296
  var MAX_DECOMPRESS_SIZE3 = 100 * 1024 * 1024;
16995
17297
  var PARA_SHAPE_SIZE = 187;
@@ -17012,19 +17314,25 @@ var SIMPLE_CTRL = /* @__PURE__ */ new Map([
17012
17314
  [30, { extraBytes: 2, extraHchar: 1, emit: "\xA0" }],
17013
17315
  [31, { extraBytes: 2, extraHchar: 1, emit: " " }]
17014
17316
  ]);
17015
- function parseHwp3Document(buffer, _options) {
17016
- const headReader = new Reader(Buffer.from(buffer));
17017
- const header = readHeader(headReader);
17018
- if (header.encrypted !== 0) {
17019
- throw new KordocError("\uC554\uD638\uD654\uB41C HWP3 \uBB38\uC11C \u2014 \uBCF8\uBB38\uC774 \uC554\uD638\uB85C \uBCF4\uD638\uB418\uC5B4 \uC788\uC5B4 \uCD94\uCD9C\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.");
17317
+ function parseHwp3Document(buffer, options) {
17318
+ let source = Buffer.from(buffer);
17319
+ if (isEncryptedHwp3(source)) {
17320
+ if (!options?.password) {
17321
+ throw new KordocError(
17322
+ "\uC554\uD638\uB85C \uBCF4\uD638\uB41C HWP3 \uBB38\uC11C\uC785\uB2C8\uB2E4. password \uC635\uC158\uC5D0 \uC5F4\uAE30 \uC554\uD638\uB97C \uC9C0\uC815\uD558\uC138\uC694."
17323
+ );
17324
+ }
17325
+ source = decryptHwp3Document(source, options.password);
17020
17326
  }
17327
+ const headReader = new Reader(source);
17328
+ const header = readHeader(headReader);
17021
17329
  headReader.skip(header.infoBlockLength);
17022
17330
  const tail = headReader.readToEnd();
17023
17331
  let body;
17024
17332
  const warnings = [];
17025
17333
  if (header.compressed !== 0) {
17026
17334
  try {
17027
- body = inflateRawSync3(tail, { maxOutputLength: MAX_DECOMPRESS_SIZE3 });
17335
+ body = inflateRawSync5(tail, { maxOutputLength: MAX_DECOMPRESS_SIZE3 });
17028
17336
  } catch (err) {
17029
17337
  if (err?.code === "ERR_BUFFER_TOO_LARGE") {
17030
17338
  throw new KordocError(`HWP3 \uC555\uCD95 \uD574\uC81C \uACB0\uACFC\uAC00 \uCD5C\uB300 \uD5C8\uC6A9 \uD06C\uAE30(${MAX_DECOMPRESS_SIZE3 / 1024 / 1024}MB)\uB97C \uCD08\uACFC\uD588\uC2B5\uB2C8\uB2E4`);
@@ -17127,8 +17435,7 @@ function parseCharStream(reader, charCount, ctx) {
17127
17435
  continue;
17128
17436
  }
17129
17437
  if (ch >= 32) {
17130
- const cp = decodeJohab(ch);
17131
- if (cp !== JOHAB_UNMAPPED) out += String.fromCodePoint(cp);
17438
+ out += decodeJohabText(ch) ?? "";
17132
17439
  continue;
17133
17440
  }
17134
17441
  const simple = SIMPLE_CTRL.get(ch);
@@ -17196,7 +17503,7 @@ function parseCharStream(reader, charCount, ctx) {
17196
17503
  function parseTableLike(reader, ctx) {
17197
17504
  const info = reader.readBytes(84);
17198
17505
  const cellCount = info.readUInt16LE(80) || 1;
17199
- if (cellCount > 256) {
17506
+ if (27 * cellCount > reader.remaining()) {
17200
17507
  ctx.warnings.push({
17201
17508
  code: "PARTIAL_PARSE",
17202
17509
  message: `HWP3 \uD45C cell_count=${cellCount} \uBE44\uC815\uC0C1 \u2014 \uD45C \uBCF8\uBB38 \uCD94\uCD9C \uD3EC\uAE30`
@@ -17210,10 +17517,11 @@ function parseTableLike(reader, ctx) {
17210
17517
  parseParagraphList2(reader, ctx);
17211
17518
  return "";
17212
17519
  }
17213
- function parsePicture(reader, _ctx) {
17520
+ function parsePicture(reader, ctx) {
17214
17521
  const info = reader.readBytes(348);
17215
17522
  const nExt = info.readUInt32LE(0);
17216
17523
  if (nExt > 0 && nExt < 100 * 1024 * 1024) reader.skip(nExt);
17524
+ parseParagraphList2(reader, ctx);
17217
17525
  }
17218
17526
 
17219
17527
  // src/hwp5/sentinel.ts
@@ -19922,7 +20230,9 @@ async function fillWithUniqueGuard(values, run) {
19922
20230
  const first = await run(values);
19923
20231
  const counts = /* @__PURE__ */ new Map();
19924
20232
  for (const f of first.filled) {
19925
- if (f.key) counts.set(f.key, (counts.get(f.key) ?? 0) + 1);
20233
+ if (f.key && f.source !== "clickhere") {
20234
+ counts.set(f.key, (counts.get(f.key) ?? 0) + 1);
20235
+ }
19926
20236
  }
19927
20237
  const isArrayValue = (normKey) => {
19928
20238
  for (const [label, raw] of Object.entries(values)) {
@@ -20383,7 +20693,7 @@ import JSZip7 from "jszip";
20383
20693
  // src/form/click-here.ts
20384
20694
  import JSZip6 from "jszip";
20385
20695
  var FIELD_BEGIN_RE = /<([A-Za-z0-9_]+:)?fieldBegin\b([^>]*?)(\/?)>/g;
20386
- function attrOf(attrs, name) {
20696
+ function attrOf2(attrs, name) {
20387
20697
  const m = attrs.match(new RegExp(`(?:^|\\s)${name}="([^"]*)"`));
20388
20698
  return m ? m[1] : void 0;
20389
20699
  }
@@ -20409,10 +20719,10 @@ function scanClickHereRegions(xml) {
20409
20719
  const prefix = m[1] ?? "";
20410
20720
  const attrs = m[2];
20411
20721
  const selfClosed = m[3] === "/";
20412
- if ((attrOf(attrs, "type") ?? "").toUpperCase() !== "CLICK_HERE") continue;
20413
- const name = attrOf(attrs, "name");
20722
+ if ((attrOf2(attrs, "type") ?? "").toUpperCase() !== "CLICK_HERE") continue;
20723
+ const name = attrOf2(attrs, "name");
20414
20724
  if (name === void 0) continue;
20415
- const id = attrOf(attrs, "id");
20725
+ const id = attrOf2(attrs, "id");
20416
20726
  let beginElemEnd;
20417
20727
  if (selfClosed) {
20418
20728
  beginElemEnd = FIELD_BEGIN_RE.lastIndex;
@@ -20435,7 +20745,7 @@ function scanClickHereRegions(xml) {
20435
20745
  let end = -1;
20436
20746
  let fe;
20437
20747
  while ((fe = feRe.exec(xml)) !== null) {
20438
- const ref = attrOf(fe[0], "beginIDRef");
20748
+ const ref = attrOf2(fe[0], "beginIDRef");
20439
20749
  if (id !== void 0 && ref !== void 0 && ref !== id) continue;
20440
20750
  end = fe.index;
20441
20751
  break;
@@ -20476,7 +20786,7 @@ function fillClickHereInXml(xml, cursor, blockedLabels) {
20476
20786
  const replacement = valueXml(value, region.prefix);
20477
20787
  const current = xml.slice(region.start, region.end);
20478
20788
  matchedKeys.add(key);
20479
- filled.push({ label: region.name, value, row: -1, col: -1, key });
20789
+ filled.push({ label: region.name, value, row: -1, col: -1, key, source: "clickhere" });
20480
20790
  if (current === replacement) continue;
20481
20791
  splices.push({ start: region.start, end: region.end, replacement });
20482
20792
  }
@@ -20487,6 +20797,7 @@ function fillClickHereInXml(xml, cursor, blockedLabels) {
20487
20797
  };
20488
20798
  }
20489
20799
  async function extractClickHereFields(hwpxBuffer) {
20800
+ precheckZipSize(hwpxBuffer);
20490
20801
  const zip = await JSZip6.loadAsync(hwpxBuffer);
20491
20802
  const sectionPaths = Object.keys(zip.files).filter((name) => /[Ss]ection\d+\.xml$/i.test(name)).sort();
20492
20803
  const fields = [];
@@ -20507,6 +20818,7 @@ async function extractClickHereFields(hwpxBuffer) {
20507
20818
  // src/form/filler-hwpx.ts
20508
20819
  async function fillHwpx(hwpxBuffer, values, blockedLabels) {
20509
20820
  const u8 = new Uint8Array(hwpxBuffer);
20821
+ precheckZipSize(hwpxBuffer);
20510
20822
  const zip = await JSZip7.loadAsync(hwpxBuffer);
20511
20823
  const sectionPaths = Object.keys(zip.files).filter((name) => /[Ss]ection\d+\.xml$/i.test(name)).sort();
20512
20824
  if (sectionPaths.length === 0) {
@@ -25770,6 +26082,9 @@ function applyCellEdit(table2, scanTable, gridR, gridC, newLines, ctx, before, a
25770
26082
  const xml = ctx.scans[target.sectionIndex]?.xml;
25771
26083
  const rawT = xml ? paraTText(target, xml) : null;
25772
26084
  let sp;
26085
+ if (xml && rawT === null) {
26086
+ return skip("\uBE48 \uC140 \uBB38\uB2E8\uC5D0 \uC5D4\uD2F0\uD2F0\xB7\uD0DC\uADF8\uAC00 \uC788\uC5B4 \uC0BD\uC785 \uACBD\uACC4\uB97C \uD2B9\uC815\uD560 \uC218 \uC5C6\uC74C \u2014 \uC6D0\uBB38 \uC190\uC2E4 \uBC29\uC9C0\uB85C \uBBF8\uC801\uC6A9");
26087
+ }
25773
26088
  if (xml && rawT !== null && rawT.length > 0 && rawT.trim() === "") {
25774
26089
  sp = buildRangeSplices(target, xml, 0, 0, value);
25775
26090
  if (sp === null) return skip("\uBE48 \uC140 \uACF5\uBC31 \uBCF4\uC874 \uC0BD\uC785 \uACBD\uACC4 \uBAA8\uD638 \u2014 \uC6D0\uBB38 \uC190\uC2E4 \uBC29\uC9C0\uB85C \uBBF8\uC801\uC6A9");
@@ -28012,7 +28327,7 @@ async function parse(input, options) {
28012
28327
  }
28013
28328
  async function parseImage(buffer, options) {
28014
28329
  try {
28015
- const { parseImageDocument } = await import("./image-ocr-R44RYDBU.js");
28330
+ const { parseImageDocument } = await import("./image-ocr-XC52AQDV.js");
28016
28331
  const { blocks, warnings } = await parseImageDocument(buffer, options);
28017
28332
  return {
28018
28333
  success: true,
@@ -28071,7 +28386,7 @@ async function parseHwp(buffer, options) {
28071
28386
  async function parsePdf(buffer, options) {
28072
28387
  let parsePdfDocument;
28073
28388
  try {
28074
- const mod = await import("./parser-W3POOYQQ.js");
28389
+ const mod = await import("./parser-ESGFWWCT.js");
28075
28390
  parsePdfDocument = mod.parsePdfDocument;
28076
28391
  } catch {
28077
28392
  return {
@@ -28217,4 +28532,4 @@ export {
28217
28532
  parseHwpml,
28218
28533
  fillForm
28219
28534
  };
28220
- //# sourceMappingURL=chunk-DRZUJ2RK.js.map
28535
+ //# sourceMappingURL=chunk-YOZ7ZRCD.js.map