kordoc 4.8.1 → 4.9.1

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 (122) hide show
  1. package/README.md +14 -0
  2. package/dist/{-BPSOA7WX.js → -KIYHEVUW.js} +14 -8
  3. package/dist/{chunk-FZA5HW4K.cjs → chunk-2ULV2DX4.cjs} +4 -4
  4. package/dist/chunk-2ULV2DX4.cjs.map +1 -0
  5. package/dist/{chunk-I6Q5K2YF.js → chunk-3PY5YCJP.js} +2 -2
  6. package/dist/{chunk-D6NDAA72.cjs → chunk-4R73ASZF.cjs} +2 -2
  7. package/dist/chunk-4R73ASZF.cjs.map +1 -0
  8. package/dist/{chunk-C5IU7Q3W.js → chunk-55Q76S6S.js} +2 -2
  9. package/dist/{chunk-YCSXG6SY.js → chunk-5CAKNM2K.js} +2 -2
  10. package/dist/{chunk-DVEJRBFI.cjs → chunk-65TL66UD.cjs} +3 -3
  11. package/dist/chunk-65TL66UD.cjs.map +1 -0
  12. package/dist/{chunk-H25XAYE4.cjs → chunk-D7EMDOX7.cjs} +6 -6
  13. package/dist/chunk-D7EMDOX7.cjs.map +1 -0
  14. package/dist/chunk-DCZVOIEO.cjs.map +1 -1
  15. package/dist/{chunk-YOFMKKCA.js → chunk-GNRBCV4R.js} +3 -3
  16. package/dist/chunk-GS7T56RP.cjs.map +1 -1
  17. package/dist/{chunk-KTAWKNYX.js → chunk-GU4NCNCT.js} +2 -2
  18. package/dist/{chunk-I6EWYT3Y.js → chunk-HFZIYAQQ.js} +2 -2
  19. package/dist/{chunk-MYOGZQMD.js → chunk-HG7GXBVP.js} +2 -2
  20. package/dist/{chunk-XZEDL6B4.cjs → chunk-HOSNMNKU.cjs} +7 -7
  21. package/dist/chunk-HOSNMNKU.cjs.map +1 -0
  22. package/dist/{chunk-ETMMB6O2.js → chunk-INV3W43Y.js} +2 -2
  23. package/dist/{chunk-5EWPDL7S.js → chunk-JHXAQDXS.js} +288 -21
  24. package/dist/chunk-JHXAQDXS.js.map +1 -0
  25. package/dist/{chunk-DIB2I2HP.js → chunk-L27YEW3T.js} +3 -3
  26. package/dist/chunk-L5CDTOC3.cjs.map +1 -1
  27. package/dist/{chunk-N7HKWVGP.js → chunk-Q3IVAWOK.js} +3 -3
  28. package/dist/{chunk-A6EAUP2J.js → chunk-SFSB3FAL.js} +2 -2
  29. package/dist/{chunk-66BZATFV.js → chunk-SGTGSV4A.js} +2 -2
  30. package/dist/{chunk-Y4H75LKS.js → chunk-TC7EDYZD.js} +2 -2
  31. package/dist/{chunk-4CUEYYQF.js → chunk-TN6AALZG.js} +2 -2
  32. package/dist/{chunk-NJMWYRLY.js → chunk-UIQWUYOJ.js} +5 -4
  33. package/dist/{chunk-NJMWYRLY.js.map → chunk-UIQWUYOJ.js.map} +1 -1
  34. package/dist/{chunk-XGXKICRN.js → chunk-VGRIWMTQ.js} +2 -2
  35. package/dist/{chunk-6KFW772K.js → chunk-Z2SX4E7K.js} +2 -2
  36. package/dist/chunks-FB344JZT.js +10 -0
  37. package/dist/cli.js +47 -25
  38. package/dist/cli.js.map +1 -1
  39. package/dist/{formula-T4AIR2LX.js → formula-5DQQ4MFT.js} +4 -4
  40. package/dist/{formula-ODJ7GSUH.cjs → formula-I5D3UNLV.cjs} +9 -9
  41. package/dist/formula-I5D3UNLV.cjs.map +1 -0
  42. package/dist/{formula-7XQLPNSO.js → formula-S4O7SBN3.js} +3 -3
  43. package/dist/{image-ocr-WMTQ357I.cjs → image-ocr-BKUYR4WU.cjs} +11 -11
  44. package/dist/image-ocr-BKUYR4WU.cjs.map +1 -0
  45. package/dist/{image-ocr-TZU64JJF.js → image-ocr-LIH2E7WC.js} +5 -5
  46. package/dist/{image-ocr-EJLWGR3B.js → image-ocr-SQXFTNYR.js} +7 -7
  47. package/dist/index.cjs +457 -189
  48. package/dist/index.cjs.map +1 -1
  49. package/dist/index.d.cts +46 -1
  50. package/dist/index.d.ts +46 -1
  51. package/dist/index.js +286 -18
  52. package/dist/index.js.map +1 -1
  53. package/dist/mcp.js +19 -16
  54. package/dist/mcp.js.map +1 -1
  55. package/dist/{model-bundle-OT2OTSGH.js → model-bundle-4JMY5E37.js} +5 -5
  56. package/dist/{models-MUVFWN7M.js → models-D7X5YBWX.js} +5 -5
  57. package/dist/page-range-P7SDW6LR.cjs.map +1 -1
  58. package/dist/{parser-ERSNBILY.js → parser-GHRXWFJN.js} +6 -6
  59. package/dist/{parser-E6NQ666P.js → parser-MY7JJISJ.js} +6 -6
  60. package/dist/{parser-XVUFA35I.cjs → parser-ZLHOAZAA.cjs} +26 -26
  61. package/dist/parser-ZLHOAZAA.cjs.map +1 -0
  62. package/dist/pdf-ocr-FESGFQYN.cjs +14 -0
  63. package/dist/pdf-ocr-FESGFQYN.cjs.map +1 -0
  64. package/dist/pdf-ocr-IEAXGG3D.js +13 -0
  65. package/dist/pdf-ocr-Z6CHIYEM.js +16 -0
  66. package/dist/{profile-io-IRW6XCXH.js → profile-io-EAPI6M7O.js} +3 -3
  67. package/dist/{rasterize-BNF6QTLY.js → rasterize-UIDZAEWB.js} +2 -2
  68. package/dist/render-XL7ZVAHK.js +10 -0
  69. package/dist/seal-CY7EIWDJ.js +10 -0
  70. package/dist/{setup-BXOXVBU3.js → setup-UHX3HHWY.js} +3 -3
  71. package/dist/{watch-7PYYGQUJ.js → watch-23MV3PBX.js} +9 -9
  72. package/package.json +1 -1
  73. package/dist/chunk-5EWPDL7S.js.map +0 -1
  74. package/dist/chunk-D6NDAA72.cjs.map +0 -1
  75. package/dist/chunk-DVEJRBFI.cjs.map +0 -1
  76. package/dist/chunk-FZA5HW4K.cjs.map +0 -1
  77. package/dist/chunk-H25XAYE4.cjs.map +0 -1
  78. package/dist/chunk-XZEDL6B4.cjs.map +0 -1
  79. package/dist/chunks-HYXNV3WP.js +0 -10
  80. package/dist/formula-ODJ7GSUH.cjs.map +0 -1
  81. package/dist/image-ocr-WMTQ357I.cjs.map +0 -1
  82. package/dist/parser-XVUFA35I.cjs.map +0 -1
  83. package/dist/pdf-ocr-KG7X7WRC.js +0 -13
  84. package/dist/pdf-ocr-KMM24YFF.cjs +0 -14
  85. package/dist/pdf-ocr-KMM24YFF.cjs.map +0 -1
  86. package/dist/pdf-ocr-YTZL2UL4.js +0 -16
  87. package/dist/render-T7QXFTZA.js +0 -10
  88. package/dist/seal-ITE2NNNH.js +0 -10
  89. /package/dist/{-BPSOA7WX.js.map → -KIYHEVUW.js.map} +0 -0
  90. /package/dist/{chunk-I6Q5K2YF.js.map → chunk-3PY5YCJP.js.map} +0 -0
  91. /package/dist/{chunk-C5IU7Q3W.js.map → chunk-55Q76S6S.js.map} +0 -0
  92. /package/dist/{chunk-YCSXG6SY.js.map → chunk-5CAKNM2K.js.map} +0 -0
  93. /package/dist/{chunk-YOFMKKCA.js.map → chunk-GNRBCV4R.js.map} +0 -0
  94. /package/dist/{chunk-KTAWKNYX.js.map → chunk-GU4NCNCT.js.map} +0 -0
  95. /package/dist/{chunk-I6EWYT3Y.js.map → chunk-HFZIYAQQ.js.map} +0 -0
  96. /package/dist/{chunk-MYOGZQMD.js.map → chunk-HG7GXBVP.js.map} +0 -0
  97. /package/dist/{chunk-ETMMB6O2.js.map → chunk-INV3W43Y.js.map} +0 -0
  98. /package/dist/{chunk-DIB2I2HP.js.map → chunk-L27YEW3T.js.map} +0 -0
  99. /package/dist/{chunk-N7HKWVGP.js.map → chunk-Q3IVAWOK.js.map} +0 -0
  100. /package/dist/{chunk-A6EAUP2J.js.map → chunk-SFSB3FAL.js.map} +0 -0
  101. /package/dist/{chunk-66BZATFV.js.map → chunk-SGTGSV4A.js.map} +0 -0
  102. /package/dist/{chunk-Y4H75LKS.js.map → chunk-TC7EDYZD.js.map} +0 -0
  103. /package/dist/{chunk-4CUEYYQF.js.map → chunk-TN6AALZG.js.map} +0 -0
  104. /package/dist/{chunk-XGXKICRN.js.map → chunk-VGRIWMTQ.js.map} +0 -0
  105. /package/dist/{chunk-6KFW772K.js.map → chunk-Z2SX4E7K.js.map} +0 -0
  106. /package/dist/{chunks-HYXNV3WP.js.map → chunks-FB344JZT.js.map} +0 -0
  107. /package/dist/{formula-T4AIR2LX.js.map → formula-5DQQ4MFT.js.map} +0 -0
  108. /package/dist/{formula-7XQLPNSO.js.map → formula-S4O7SBN3.js.map} +0 -0
  109. /package/dist/{image-ocr-TZU64JJF.js.map → image-ocr-LIH2E7WC.js.map} +0 -0
  110. /package/dist/{image-ocr-EJLWGR3B.js.map → image-ocr-SQXFTNYR.js.map} +0 -0
  111. /package/dist/{model-bundle-OT2OTSGH.js.map → model-bundle-4JMY5E37.js.map} +0 -0
  112. /package/dist/{models-MUVFWN7M.js.map → models-D7X5YBWX.js.map} +0 -0
  113. /package/dist/{parser-ERSNBILY.js.map → parser-GHRXWFJN.js.map} +0 -0
  114. /package/dist/{parser-E6NQ666P.js.map → parser-MY7JJISJ.js.map} +0 -0
  115. /package/dist/{pdf-ocr-KG7X7WRC.js.map → pdf-ocr-IEAXGG3D.js.map} +0 -0
  116. /package/dist/{pdf-ocr-YTZL2UL4.js.map → pdf-ocr-Z6CHIYEM.js.map} +0 -0
  117. /package/dist/{profile-io-IRW6XCXH.js.map → profile-io-EAPI6M7O.js.map} +0 -0
  118. /package/dist/{rasterize-BNF6QTLY.js.map → rasterize-UIDZAEWB.js.map} +0 -0
  119. /package/dist/{render-T7QXFTZA.js.map → render-XL7ZVAHK.js.map} +0 -0
  120. /package/dist/{seal-ITE2NNNH.js.map → seal-CY7EIWDJ.js.map} +0 -0
  121. /package/dist/{setup-BXOXVBU3.js.map → setup-UHX3HHWY.js.map} +0 -0
  122. /package/dist/{watch-7PYYGQUJ.js.map → watch-23MV3PBX.js.map} +0 -0
@@ -1,5 +1,5 @@
1
1
  // src/utils.ts
2
- var VERSION = true ? "4.8.1" : "0.0.0-dev";
2
+ var VERSION = true ? "4.9.1" : "0.0.0-dev";
3
3
  function toArrayBuffer(buf) {
4
4
  if (buf.byteOffset === 0 && buf.byteLength === buf.buffer.byteLength) {
5
5
  return buf.buffer;
@@ -121,4 +121,4 @@ export {
121
121
  safeMax,
122
122
  classifyError
123
123
  };
124
- //# sourceMappingURL=chunk-ETMMB6O2.js.map
124
+ //# sourceMappingURL=chunk-INV3W43Y.js.map
@@ -22,7 +22,7 @@ import {
22
22
  paraTextPureT,
23
23
  patchZipEntries,
24
24
  scanSectionXml
25
- } from "./chunk-N7HKWVGP.js";
25
+ } from "./chunk-Q3IVAWOK.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-MYOGZQMD.js";
35
+ } from "./chunk-HG7GXBVP.js";
36
36
  import {
37
37
  SPACE_EM_FIXED,
38
38
  charWidthEm1000,
39
39
  fitRatioForFewerLines,
40
40
  measureTextWidth,
41
41
  simulateWrap
42
- } from "./chunk-NJMWYRLY.js";
42
+ } from "./chunk-UIQWUYOJ.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-Y4H75LKS.js";
58
+ } from "./chunk-TC7EDYZD.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-I6Q5K2YF.js";
70
+ } from "./chunk-3PY5YCJP.js";
71
71
 
72
72
  // src/index.ts
73
73
  import { readFile } from "fs/promises";
@@ -502,9 +502,9 @@ function decryptEntry(cipher, entry, startKey) {
502
502
  const decipher = createDecipheriv("aes-256-cbc", key, entry.iv);
503
503
  decipher.setAutoPadding(false);
504
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;
505
+ const plain2 = inflateRawSync(raw, { maxOutputLength: MAX_DECOMPRESS_SIZE });
506
+ const sum = createHash("sha256").update(plain2.subarray(0, Math.min(CHECKSUM_PREFIX_BYTES, plain2.length))).digest();
507
+ if (sum.length === entry.checksum.length && timingSafeEqual(sum, entry.checksum)) return plain2;
508
508
  } catch {
509
509
  }
510
510
  }
@@ -520,13 +520,13 @@ async function decryptHwpxInPlace(zip, manifestXml, password) {
520
520
  if (!file) {
521
521
  throw new KordocError(`\uC554\uD638 \uBAA9\uB85D\uC5D0 \uC788\uB294 \uD30C\uC77C\uC774 \uBB38\uC11C\uC5D0 \uC5C6\uC2B5\uB2C8\uB2E4: ${entry.path}`);
522
522
  }
523
- const plain = decryptEntry(await file.async("nodebuffer"), entry, startKey);
524
- if (!plain) {
523
+ const plain2 = decryptEntry(await file.async("nodebuffer"), entry, startKey);
524
+ if (!plain2) {
525
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
526
  }
527
- decrypted.push({ path: entry.path, plain });
527
+ decrypted.push({ path: entry.path, plain: plain2 });
528
528
  }
529
- for (const { path, plain } of decrypted) zip.file(path, plain);
529
+ for (const { path, plain: plain2 } of decrypted) zip.file(path, plain2);
530
530
  zip.file("META-INF/manifest.xml", stripEncryptionData(manifestXml));
531
531
  return decrypted.length;
532
532
  }
@@ -21511,7 +21511,7 @@ function gaejosikSizes(bodyHeight, ov = {}) {
21511
21511
  }
21512
21512
  function gaejosikSpaceBefore(depth, bodyHeight) {
21513
21513
  const s = bodyHeight / 1500;
21514
- const ui = [3e3, 2e3, 1200, 600][Math.min(depth, 3)];
21514
+ const ui = [300, 0, 0, 0][Math.min(depth, 3)];
21515
21515
  return Math.round(ui * s);
21516
21516
  }
21517
21517
  function gaejosikLevelIndent(depth, bodyHeight, sizes = {}, bullet2 = "\u25CB") {
@@ -22028,6 +22028,7 @@ function buildPrvText(blocks) {
22028
22028
  }
22029
22029
  return lines.join("\n").slice(0, 1024);
22030
22030
  }
22031
+ var BUNIM_HEAD = /^붙\s*임(?:\s|:|$)/;
22031
22032
  function findMathDelim(s, from) {
22032
22033
  let i = s.indexOf("$$", from);
22033
22034
  while (i > 0) {
@@ -22043,9 +22044,11 @@ function parseMarkdownToBlocks(md2) {
22043
22044
  const blocks = [];
22044
22045
  let i = 0;
22045
22046
  const listStack = [];
22047
+ let inBunim = false;
22046
22048
  while (i < lines.length) {
22047
22049
  const line = lines[i];
22048
22050
  if (!line.trim()) {
22051
+ inBunim = false;
22049
22052
  i++;
22050
22053
  continue;
22051
22054
  }
@@ -22173,6 +22176,19 @@ function parseMarkdownToBlocks(md2) {
22173
22176
  if (joined.length) blocks.push({ type: "blockquote", text: joined.join("\n") });
22174
22177
  continue;
22175
22178
  }
22179
+ if (BUNIM_HEAD.test(line.trim())) inBunim = true;
22180
+ if (inBunim) {
22181
+ if (BUNIM_HEAD.test(line.trim())) {
22182
+ const prevBlock = blocks[blocks.length - 1];
22183
+ if (blocks.length && !(prevBlock.type === "paragraph" && !prevBlock.text)) {
22184
+ blocks.push({ type: "paragraph", text: "" });
22185
+ }
22186
+ }
22187
+ blocks.push({ type: "paragraph", text: line.replace(/\s+$/, "") });
22188
+ listStack.length = 0;
22189
+ i++;
22190
+ continue;
22191
+ }
22176
22192
  const listLine = line.replace(/^[\t ]+/, (ws) => ws.replace(/\t/g, " "));
22177
22193
  const listMatch = listLine.match(/^(\s*)([-*+]|\d+[.)]) (.+)$/);
22178
22194
  if (listMatch) {
@@ -22556,13 +22572,18 @@ ${base2.join("\n")}
22556
22572
  }
22557
22573
  const ls = gongmun.lineSpacing;
22558
22574
  const titleAlign = gongmun.centerTitle ? "CENTER" : "LEFT";
22559
- const h2Geom = gongmun.h2Marker !== "none" ? { spaceBefore: Math.round(gongmun.bodyHeight * 2), spaceAfter: 0, indent: -markerWidth(gongmun.h2Marker === "box" ? "\u25A1" : "1.", 1600) } : { spaceBefore: 600, spaceAfter: 150 };
22575
+ const h2MarkerWidth = markerWidth(gongmun.h2Marker === "box" ? "\u25A1" : "1.", 1600);
22576
+ const h2Geom = gongmun.h2Marker !== "none" ? { spaceBefore: Math.round(gongmun.bodyHeight * 2), spaceAfter: 0, indent: -h2MarkerWidth } : { spaceBefore: 600, spaceAfter: 150 };
22577
+ const h3Left = gongmun.h2Marker !== "none" ? h2MarkerWidth : 0;
22560
22578
  const base = [
22561
22579
  paraPr(0, { lineSpacing: ls, keepWord: true }),
22562
22580
  paraPr(1, { align: titleAlign, spaceBefore: 400, spaceAfter: 400, lineSpacing: ls, keepWord: true }),
22563
22581
  paraPr(2, { align: "LEFT", ...h2Geom, lineSpacing: ls, keepWord: true }),
22564
- paraPr(3, { align: "LEFT", spaceBefore: 400, spaceAfter: 100, lineSpacing: ls, keepWord: true }),
22565
- paraPr(4, { align: "LEFT", spaceBefore: 300, spaceAfter: 100, lineSpacing: ls, keepWord: true }),
22582
+ // h3·h4 소제목("가." "1)")은 h2(□) 대항목의 하위다 — 종전에는 left가 없어 h2 부호가
22583
+ // 내어쓰기로 왼쪽에 나간 뒤 소제목이 본문 경계에서 시작해, 실렌더상 소제목이 대항목보다
22584
+ // 왼쪽에 놓이며 계층이 사라졌다(실렌더 확인). h2 부호폭만큼 들여써 h2 제목 글자와 맞춘다.
22585
+ paraPr(3, { align: "LEFT", left: h3Left, spaceBefore: 400, spaceAfter: 100, lineSpacing: ls, keepWord: true }),
22586
+ paraPr(4, { align: "LEFT", left: h3Left + Math.round(gongmun.bodyHeight / 2), spaceBefore: 300, spaceAfter: 100, lineSpacing: ls, keepWord: true }),
22566
22587
  paraPr(5, { align: "LEFT", lineSpacing: 130, indent: 400, keepWord: true }),
22567
22588
  paraPr(6, { align: "LEFT", lineSpacing: ls, indent: 600, keepWord: true }),
22568
22589
  paraPr(7, { align: "LEFT", lineSpacing: ls, indent: 600, keepWord: true })
@@ -25269,8 +25290,8 @@ function renderHtmlTable(block, ctx) {
25269
25290
  ctx.opener.emitCarrier(ctx.paraXmls);
25270
25291
  return `<hp:p paraPrIDRef="${tableStyle && bfReg ? GONGMUN_RIGHT : 0}" styleIDRef="0"><hp:run charPrIDRef="0">${tbl2}</hp:run></hp:p>`;
25271
25292
  }
25272
- const plain = (block.text || "").replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
25273
- return plain ? generateParagraph(plain) : "";
25293
+ const plain2 = (block.text || "").replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
25294
+ return plain2 ? generateParagraph(plain2) : "";
25274
25295
  }
25275
25296
  function appendPostamble(blocks, ctx) {
25276
25297
  const { gongmun, dfIds, bfReg, tableStyle, paraXmls } = ctx;
@@ -26128,6 +26149,22 @@ var RULES = [
26128
26149
  pattern: /\S\s+:(?!\/\/)|\S:(?!\/\/)[^\s\d]/g,
26129
26150
  message: "\uC30D\uC810\uC740 \uC55E\uB9D0\uC5D0 \uBD99\uC774\uACE0 \uB4A4\uB294 \uD55C \uCE78 \uB744\uC6C0",
26130
26151
  suggest: "\uC608) \uC6D0\uC7A5: \uAE40\uAC11\uB3D9"
26152
+ },
26153
+ // ── AI 문체 흔적(슬롭) — 편람 원전 외 kordoc 자체 룰 (v4.9.0) ──────────────
26154
+ // 생성형 AI 초안이 공문서로 흘러들 때 남는 기계 문체를 걸러낸다. 조언용 warning.
26155
+ {
26156
+ code: "AI_EM_DASH",
26157
+ severity: "warning",
26158
+ pattern: /[—–―]/g,
26159
+ message: "\uC904\uD45C(\u2014 \u2013 \u2015)\uB294 \uACF5\uBB38\uC11C \uD45C\uAE30 \uAD00\uD589\uC5D0 \uB9DE\uC9C0 \uC54A\uC74C(\uC0DD\uC131\uD615 AI \uBB38\uCCB4 \uD754\uC801)",
26160
+ suggest: "\uC27C\uD45C\xB7\uAD04\uD638\xB7\uAC00\uC6B4\uB383\uC810(\xB7)\uC73C\uB85C \uD480\uC5B4\uC4F0\uAE30"
26161
+ },
26162
+ {
26163
+ code: "AI_BOLD_OVERUSE",
26164
+ severity: "warning",
26165
+ pattern: /(?:\*\*[^*\n]+\*\*[^*\n]*){3}/g,
26166
+ message: "\uD55C \uC904\uC5D0 \uAC15\uC870(**) 3\uD68C \uC774\uC0C1 \u2014 \uAC15\uC870 \uB0A8\uBC1C\uC740 \uC0DD\uC131\uD615 AI \uBB38\uCCB4 \uD754\uC801",
26167
+ suggest: "\uB9AC\uB4DC\uC5B4\xB7\uD575\uC2EC \uC218\uCE58 \uD55C \uACF3\uB9CC \uAC15\uC870"
26131
26168
  }
26132
26169
  ];
26133
26170
  function lintGongmunText(text) {
@@ -26169,6 +26206,233 @@ function gongmunLintWarnings(text, limit = 10) {
26169
26206
  return shown;
26170
26207
  }
26171
26208
 
26209
+ // src/hwpx/munche-lint.ts
26210
+ function usesGaejosikMunche(preset) {
26211
+ const p = normalizeGongmunPreset(preset);
26212
+ return p === "gaejosik" || p === "report" || p === "plan";
26213
+ }
26214
+ var ITEM_MAX = 70;
26215
+ var CONCL_MAX = 60;
26216
+ var LEAD_MAX = 140;
26217
+ function classify(raw) {
26218
+ const indent = raw.length - raw.trimStart().length;
26219
+ const t = raw.trim();
26220
+ if (!t) return null;
26221
+ if (t.startsWith("#") || t.startsWith("|") || t.startsWith("![") || /^[-=*_]{3,}$/.test(t)) return null;
26222
+ if (t.startsWith(">")) return { kind: "lead", body: t.slice(1).trim() };
26223
+ if (/^(?:⇒|=>|➡|→)/.test(t)) return { kind: "concl", body: t.replace(/^(?:⇒|=>|➡|→)\s*/, "") };
26224
+ if (/^(?:※|\*\s)/.test(t)) return { kind: "note", body: t.replace(/^(?:※|\*)\s*/, "") };
26225
+ if (t.startsWith("\u25A1")) return { kind: "dae", body: t.slice(1).trim() };
26226
+ if (/^(?:○|❍|ㅇ|◦)/.test(t)) return { kind: "item", body: t.slice(1).trim() };
26227
+ const li = /^(?:[-•ㆍ·]|\d+[.)])\s+(.*)$/.exec(t);
26228
+ if (li) return { kind: indent >= 2 ? "sub" : "item", body: li[1].trim() };
26229
+ return { kind: "para", body: t };
26230
+ }
26231
+ function plain(s) {
26232
+ return s.replace(/!?\[([^\]]*)\]\([^)]*\)/g, "$1").replace(/(\*\*|__|~~|`)/g, "").trim();
26233
+ }
26234
+ function stripQuoted(s) {
26235
+ return s.replace(/[“"'‘][^”"'’]{0,80}[”"'’]/g, "").replace(/\([^)]{0,80}\)/g, "");
26236
+ }
26237
+ var DA_ENDING = /[가-힣]다\.?$/;
26238
+ var DEONTIC = /(?:해야|하여야|되어야|돼야|필요가 있)\s*(?:한다|함|합니다|하겠음)\.?$|할 것\.?$/;
26239
+ var GEOSIDA = /것(?:이다|임)\.?$/;
26240
+ var LEAD_ENDING = /(?:하고자|하려|고자)\s*함\.?$/;
26241
+ var NOT_A_BUT = /(?:아니라|아닌|아니고|아니며)\s*,?\s*([가-힣]{1,12})/g;
26242
+ var ABSTRACT = [
26243
+ "\uC0AC\uB78C",
26244
+ "\uB9C8\uC74C",
26245
+ "\uBCF8\uC9C8",
26246
+ "\uC804\uD658",
26247
+ "\uD0DC\uB3C4",
26248
+ "\uBB38\uD654",
26249
+ "\uCCA0\uD559",
26250
+ "\uAC00\uCE58",
26251
+ "\uC815\uC2E0",
26252
+ "\uAD00\uACC4",
26253
+ "\uC2E0\uB8B0",
26254
+ "\uACFC\uC815",
26255
+ "\uC9C8\uBB38",
26256
+ "\uC774\uC57C\uAE30",
26257
+ "\uACBD\uD5D8",
26258
+ "\uC2DC\uAC04",
26259
+ "\uACF5\uAC04",
26260
+ "\uC5F0\uACB0",
26261
+ "\uBCC0\uD654",
26262
+ "\uBBF8\uB798",
26263
+ "\uC2DC\uC791",
26264
+ "\uC6A9\uAE30",
26265
+ "\uC758\uC9C0",
26266
+ "\uBC29\uBC95",
26267
+ "\uC790\uC138",
26268
+ "\uC758\uC2DD",
26269
+ "\uC5ED\uB7C9"
26270
+ ];
26271
+ var COUPLET = /[은는이가]\s*[^,]{2,15},\s*[^,]{2,15}[은는이가]\s*[^,]{2,15}$/;
26272
+ var DATE_KOREAN = /\d{4}년\s*\d{1,2}월/;
26273
+ function lintMuncheText(text) {
26274
+ const findings = [];
26275
+ const lines = text.split(/\r?\n/);
26276
+ let fenceMarker = null;
26277
+ let frontMatter = false;
26278
+ let leadBuf = [];
26279
+ const add = (line, kind, severity, rule, match, message, suggest) => {
26280
+ findings.push({ line, kind, severity, rule, match: match.slice(0, 60), message, suggest });
26281
+ };
26282
+ const flushLead = () => {
26283
+ if (!leadBuf.length) return;
26284
+ const block = leadBuf.map((l) => l.body).join(" ").trim();
26285
+ const at = leadBuf[0].line;
26286
+ leadBuf = [];
26287
+ if (block.length < 20) return;
26288
+ if (!LEAD_ENDING.test(block)) {
26289
+ add(
26290
+ at,
26291
+ "lead",
26292
+ "warning",
26293
+ "LEAD_ENDING",
26294
+ block,
26295
+ "\uB9AC\uB4DC\uBB38\uC740 '~\uD558\uACE0\uC790 \uD568.' \uD55C \uBB38\uC7A5",
26296
+ "[\uC218\uB2E8]\uD558\uACE0, [\uC218\uB2E8]\uD558\uC5EC [\uBAA9\uC801]\uD558\uACE0\uC790 \uD568."
26297
+ );
26298
+ }
26299
+ const sentences = (block.match(/\./g) ?? []).length;
26300
+ if (block.length > LEAD_MAX || sentences > 1) {
26301
+ add(
26302
+ at,
26303
+ "lead",
26304
+ "warning",
26305
+ "LEAD_LONG",
26306
+ block,
26307
+ `\uB9AC\uB4DC\uBB38 ${block.length}\uC790\xB7\uBB38\uC7A5 ${sentences}\uAC1C \u2014 \uC2E4\uCE21\uC740 \uD55C \uBB38\uC7A5 40~120\uC790`,
26308
+ "\uC218\uB2E8 \uB458\xB7\uBAA9\uC801 \uD558\uB098\uB85C \uC555\uCD95"
26309
+ );
26310
+ }
26311
+ };
26312
+ for (let i = 0; i < lines.length; i++) {
26313
+ const raw = lines[i];
26314
+ const no = i + 1;
26315
+ if (raw.trim() === "---" && (frontMatter || no <= 3)) {
26316
+ if (no <= 3 || frontMatter) frontMatter = !frontMatter;
26317
+ continue;
26318
+ }
26319
+ if (frontMatter) continue;
26320
+ const fence = raw.match(/^\s*(```+|~~~+)/);
26321
+ if (fence) {
26322
+ const kind = fence[1][0];
26323
+ if (fenceMarker === null) fenceMarker = kind;
26324
+ else if (kind === fenceMarker) fenceMarker = null;
26325
+ continue;
26326
+ }
26327
+ if (fenceMarker !== null) continue;
26328
+ const c = classify(raw);
26329
+ if (!c) {
26330
+ flushLead();
26331
+ continue;
26332
+ }
26333
+ if (c.kind === "lead") {
26334
+ leadBuf.push({ line: no, body: plain(c.body) });
26335
+ continue;
26336
+ }
26337
+ flushLead();
26338
+ const b = plain(c.body);
26339
+ if (!b) continue;
26340
+ const q = stripQuoted(b);
26341
+ if (c.kind === "item" || c.kind === "sub" || c.kind === "concl" || c.kind === "para") {
26342
+ if (DA_ENDING.test(q)) {
26343
+ add(
26344
+ no,
26345
+ c.kind,
26346
+ "error",
26347
+ "DA_ENDING",
26348
+ b,
26349
+ "'~\uB2E4' \uC11C\uC220\uD615 \uC885\uACB0 \u2014 \uC2E4\uCE21 0%(\uD56D\uBAA9 0/264)",
26350
+ "\uBA85\uC0AC\xB7\uBA85\uC0AC\uAD6C \uB610\uB294 '~\uD568/\uC788\uC74C'\uC73C\uB85C"
26351
+ );
26352
+ }
26353
+ if (GEOSIDA.test(q)) {
26354
+ add(no, c.kind, "warning", "GEOSIDA", b, "'~\uAC83\uC774\uB2E4/\uAC83\uC784' \u2014 \uC2E4\uCE21 1\uAC74\uBFD0", "\uBA85\uC0AC \uC885\uACB0\uB85C");
26355
+ }
26356
+ }
26357
+ if (DEONTIC.test(q)) {
26358
+ add(
26359
+ no,
26360
+ c.kind,
26361
+ "error",
26362
+ "DEONTIC",
26363
+ b,
26364
+ "'~\uD574\uC57C \uD55C\uB2E4' \uB2F9\uC704 \uC885\uACB0 \u2014 \uD310\uB2E8\uC740 \uBA85\uC0AC\uB85C \uB05D\uB9FA\uC74C",
26365
+ "\uC608) \u21D2 \uC9C0\uC5ED\uB9DE\uCDA4\uD615 \uBCF4\uAE09 \uD544\uC694"
26366
+ );
26367
+ }
26368
+ NOT_A_BUT.lastIndex = 0;
26369
+ for (const m of q.matchAll(NOT_A_BUT)) {
26370
+ const tail = m[1] ?? "";
26371
+ if (ABSTRACT.some((w) => tail.includes(w))) {
26372
+ add(
26373
+ no,
26374
+ c.kind,
26375
+ "error",
26376
+ "RHETORIC_CONTRAST",
26377
+ m[0].trim(),
26378
+ `'\uC544\uB2C8\uB77C ${tail}' \u2014 B\uAC00 \uCD94\uC0C1\uC5B4\xB7\uAC00\uCE58\uC5B4\uBA74 \uC218\uC0AC(\uC2E4\uCE21 0\uAC74)`,
26379
+ "\uB450 \uAD6C\uCCB4 \uC120\uD0DD\uC9C0\uB97C \uAC00\uB974\uB294 \uACBD\uC6B0\uB9CC \uD5C8\uC6A9"
26380
+ );
26381
+ } else {
26382
+ add(
26383
+ no,
26384
+ c.kind,
26385
+ "warning",
26386
+ "CONTRAST_CHECK",
26387
+ m[0].trim(),
26388
+ `'\uC544\uB2C8\uB77C ${tail}' \u2014 B\uAC00 \uAD6C\uCCB4 \uC120\uD0DD\uC9C0(\uC81C\uB3C4\xB7\uC7A5\uC18C\xB7\uC808\uCC28)\uC778\uC9C0 \uD655\uC778`
26389
+ );
26390
+ }
26391
+ }
26392
+ if (c.kind !== "note" && /[?!?!]/.test(q)) {
26393
+ add(no, c.kind, "error", "QUESTION_EXCLAIM", b, "\uBB3C\uC74C\uD45C\xB7\uB290\uB08C\uD45C \u2014 \uBCF8\uBB38 \uC2E4\uCE21 0\uAC74");
26394
+ }
26395
+ if ((c.kind === "item" || c.kind === "para" || c.kind === "dae") && COUPLET.test(q)) {
26396
+ add(no, c.kind, "warning", "COUPLET", b, "\uB300\uAD6C(\u5C0D\u53E5)\uB85C \uBCF4\uC784 \u2014 \uC2AC\uB85C\uAC74 \uBB38\uC7A5\uC740 \uC2E4\uCE21 0\uAC74", "\uBAA9\uD45C \uC218\uCE58\xB7\uBA85\uC0AC\uB85C");
26397
+ }
26398
+ if (c.kind === "item" && b.length > ITEM_MAX) {
26399
+ add(
26400
+ no,
26401
+ c.kind,
26402
+ "warning",
26403
+ "ITEM_LONG",
26404
+ b,
26405
+ `\uD56D\uBAA9 ${b.length}\uC790 \u2014 \uC2E4\uCE21 \uC911\uC559 31\uC790, 2\uC904 \uD55C\uACC4`,
26406
+ "\uADFC\uAC70\xB7\uC608\uC2DC\uB294 \uC138\uBD80(-)\uB85C \uB0B4\uB9BC"
26407
+ );
26408
+ }
26409
+ if (c.kind === "concl" && b.length > CONCL_MAX) {
26410
+ add(
26411
+ no,
26412
+ c.kind,
26413
+ "warning",
26414
+ "CONCL_LONG",
26415
+ b,
26416
+ `\uACB0\uB860 ${b.length}\uC790 \u2014 \uC2E4\uCE21 \uC911\uC559 30\uC790`,
26417
+ "\uD310\uB2E8 \uD558\uB098\uB9CC \uB0A8\uAE40"
26418
+ );
26419
+ }
26420
+ if (DATE_KOREAN.test(b)) {
26421
+ add(no, c.kind, "warning", "DATE_KOREAN", b, "\uC5F0\xB7\uC6D4\uC744 \uAE00\uC790\uB85C \uD45C\uAE30", "\uC608) 2026. 8. 22.");
26422
+ }
26423
+ }
26424
+ flushLead();
26425
+ return findings;
26426
+ }
26427
+ function muncheLintWarnings(text, limit = 10) {
26428
+ const findings = lintMuncheText(text);
26429
+ const shown = findings.slice(0, limit).map(
26430
+ (f) => `\uBB38\uCCB4 [${f.rule}] L${f.line} "${f.match}" \u2014 ${f.message}${f.suggest ? ` (${f.suggest})` : ""}`
26431
+ );
26432
+ if (findings.length > limit) shown.push(`\uBB38\uCCB4 \uACBD\uACE0 ${findings.length - limit}\uAC74 \uB354 \uC788\uC74C \u2014 kordoc lint --munche\uB85C \uC804\uCCB4 \uD655\uC778`);
26433
+ return shown;
26434
+ }
26435
+
26172
26436
  // src/roundtrip/patcher.ts
26173
26437
  import JSZip10 from "jszip";
26174
26438
 
@@ -28926,7 +29190,7 @@ async function dispatch(format, buffer, opts) {
28926
29190
  }
28927
29191
  async function parseImage(buffer, options) {
28928
29192
  try {
28929
- const { parseImageDocument } = await import("./image-ocr-EJLWGR3B.js");
29193
+ const { parseImageDocument } = await import("./image-ocr-SQXFTNYR.js");
28930
29194
  const { blocks, warnings } = await parseImageDocument(buffer, options);
28931
29195
  return {
28932
29196
  success: true,
@@ -28985,7 +29249,7 @@ async function parseHwp(buffer, options) {
28985
29249
  async function parsePdf(buffer, options) {
28986
29250
  let parsePdfDocument;
28987
29251
  try {
28988
- const mod = await import("./parser-ERSNBILY.js");
29252
+ const mod = await import("./parser-GHRXWFJN.js");
28989
29253
  parsePdfDocument = mod.parsePdfDocument;
28990
29254
  } catch {
28991
29255
  return {
@@ -29111,6 +29375,9 @@ export {
29111
29375
  unknownFontWarnings,
29112
29376
  lintGongmunText,
29113
29377
  gongmunLintWarnings,
29378
+ usesGaejosikMunche,
29379
+ lintMuncheText,
29380
+ muncheLintWarnings,
29114
29381
  patchHwpx,
29115
29382
  patchHwp,
29116
29383
  validateHwpx,
@@ -29132,4 +29399,4 @@ export {
29132
29399
  parseHwpml,
29133
29400
  fillForm
29134
29401
  };
29135
- //# sourceMappingURL=chunk-5EWPDL7S.js.map
29402
+ //# sourceMappingURL=chunk-JHXAQDXS.js.map