kordoc 4.12.0 → 4.12.2

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 (127) hide show
  1. package/README.md +26 -5
  2. package/dist/{-B3EUUBPV.js → -EZH6NKTE.js} +8 -8
  3. package/dist/{chunk-HGUNWAXK.js → chunk-2B7OIEJJ.js} +9 -5
  4. package/dist/chunk-2B7OIEJJ.js.map +1 -0
  5. package/dist/{chunk-QKFCGJJY.js → chunk-2HBYWA5X.js} +2 -2
  6. package/dist/{chunk-HFJWCAJ2.cjs → chunk-2LKYJ4G3.cjs} +6 -6
  7. package/dist/chunk-2LKYJ4G3.cjs.map +1 -0
  8. package/dist/{chunk-COGFPMEB.js → chunk-2LO525TL.js} +2 -2
  9. package/dist/{chunk-TROD36DK.cjs → chunk-2LPBIW2B.cjs} +3 -3
  10. package/dist/chunk-2LPBIW2B.cjs.map +1 -0
  11. package/dist/{chunk-BQSWSMRG.cjs → chunk-62IU4NV3.cjs} +2 -2
  12. package/dist/chunk-62IU4NV3.cjs.map +1 -0
  13. package/dist/{chunk-SMGSCP57.js → chunk-7XUXSDH5.js} +308 -53
  14. package/dist/chunk-7XUXSDH5.js.map +1 -0
  15. package/dist/{chunk-YASFVKDT.js → chunk-DBGEUDBY.js} +2 -2
  16. package/dist/chunk-DCZVOIEO.cjs.map +1 -1
  17. package/dist/{chunk-4PKNWBXJ.js → chunk-FBLMVHDK.js} +2 -2
  18. package/dist/{chunk-I2OETEEL.js → chunk-FVBR7SC3.js} +2 -2
  19. package/dist/chunk-GS7T56RP.cjs.map +1 -1
  20. package/dist/{chunk-46UDI56N.js → chunk-IOJX4Q63.js} +3 -3
  21. package/dist/{chunk-BQE7AMFS.js → chunk-JCMBQC5N.js} +2 -2
  22. package/dist/{chunk-TF5DS74F.js → chunk-KX7IYCCZ.js} +3 -3
  23. package/dist/{chunk-PFUW4I4I.js → chunk-LD2K4K5V.js} +170 -18
  24. package/dist/chunk-LD2K4K5V.js.map +1 -0
  25. package/dist/{chunk-MJPRWCI5.js → chunk-MUYU4JI5.js} +2 -2
  26. package/dist/{chunk-SKGYPM6L.js → chunk-OEYECZGV.js} +308 -53
  27. package/dist/chunk-OEYECZGV.js.map +1 -0
  28. package/dist/{chunk-AT23GFSK.cjs → chunk-ONNAHSPV.cjs} +323 -68
  29. package/dist/chunk-ONNAHSPV.cjs.map +1 -0
  30. package/dist/{chunk-GHVXLV52.js → chunk-Q6ETEJFK.js} +9 -5
  31. package/dist/chunk-Q6ETEJFK.js.map +1 -0
  32. package/dist/{chunk-2IR3PEZU.js → chunk-QBCMFNHS.js} +2 -2
  33. package/dist/{chunk-DRLKMFDV.js → chunk-QM2RSJ2S.js} +3 -3
  34. package/dist/chunk-T7GRHAIW.cjs.map +1 -1
  35. package/dist/{chunk-TJYZY2O6.js → chunk-UL2MDA5C.js} +3 -3
  36. package/dist/{chunk-TYYJSOZS.cjs → chunk-V5BUPZ6I.cjs} +23 -19
  37. package/dist/chunk-V5BUPZ6I.cjs.map +1 -0
  38. package/dist/{chunk-7JQ2ITT2.js → chunk-XXDZ2KTW.js} +2 -2
  39. package/dist/chunks-TT7ZE5QQ.js +10 -0
  40. package/dist/cli.js +21 -21
  41. package/dist/cli.js.map +1 -1
  42. package/dist/{formula-WGKKR5OQ.js → formula-6RQX4NMJ.js} +4 -4
  43. package/dist/{formula-YMGXXRBQ.cjs → formula-NBEO6CFC.cjs} +9 -9
  44. package/dist/formula-NBEO6CFC.cjs.map +1 -0
  45. package/dist/{formula-G4CD5KQ6.js → formula-NW33S4TA.js} +3 -3
  46. package/dist/{image-ocr-MBWZK3ZT.js → image-ocr-CAVU2TNZ.js} +5 -5
  47. package/dist/{image-ocr-M4RP4INO.js → image-ocr-SO56VETS.js} +7 -7
  48. package/dist/{image-ocr-BE2HHMVN.cjs → image-ocr-V4KY373B.cjs} +11 -11
  49. package/dist/image-ocr-V4KY373B.cjs.map +1 -0
  50. package/dist/index.cjs +547 -395
  51. package/dist/index.cjs.map +1 -1
  52. package/dist/index.d.cts +5 -2
  53. package/dist/index.d.ts +5 -2
  54. package/dist/index.js +166 -14
  55. package/dist/index.js.map +1 -1
  56. package/dist/mcp.js +15 -15
  57. package/dist/{model-bundle-BIMP5DIH.js → model-bundle-EIJF34DR.js} +5 -5
  58. package/dist/{models-QPOEGHVA.js → models-6V4CPQXC.js} +5 -5
  59. package/dist/page-range-P7SDW6LR.cjs.map +1 -1
  60. package/dist/{parser-CMNHBVTL.js → parser-GIREMEPD.js} +307 -6
  61. package/dist/parser-GIREMEPD.js.map +1 -0
  62. package/dist/{parser-NR3XO4SM.js → parser-OBHNXEQM.js} +307 -6
  63. package/dist/parser-OBHNXEQM.js.map +1 -0
  64. package/dist/{parser-QJY5W47V.cjs → parser-OMT25WH2.cjs} +334 -33
  65. package/dist/parser-OMT25WH2.cjs.map +1 -0
  66. package/dist/pdf-ocr-IRXQ5GBM.js +13 -0
  67. package/dist/pdf-ocr-SL4GCOGS.js +16 -0
  68. package/dist/pdf-ocr-TFYVK4FI.cjs +14 -0
  69. package/dist/pdf-ocr-TFYVK4FI.cjs.map +1 -0
  70. package/dist/{profile-io-UZ43CLWX.js → profile-io-PZVMM66V.js} +3 -3
  71. package/dist/{rasterize-JC2JVVOF.js → rasterize-NRWP2C6L.js} +2 -2
  72. package/dist/render-V5ZPTZQ7.js +10 -0
  73. package/dist/seal-LVG2DGAB.js +10 -0
  74. package/dist/{setup-WDUVPFX3.js → setup-V55AJP6H.js} +3 -3
  75. package/dist/{watch-UI7OXIES.js → watch-7CQSMDDP.js} +9 -9
  76. package/package.json +1 -1
  77. package/dist/chunk-AT23GFSK.cjs.map +0 -1
  78. package/dist/chunk-BQSWSMRG.cjs.map +0 -1
  79. package/dist/chunk-GHVXLV52.js.map +0 -1
  80. package/dist/chunk-HFJWCAJ2.cjs.map +0 -1
  81. package/dist/chunk-HGUNWAXK.js.map +0 -1
  82. package/dist/chunk-PFUW4I4I.js.map +0 -1
  83. package/dist/chunk-SKGYPM6L.js.map +0 -1
  84. package/dist/chunk-SMGSCP57.js.map +0 -1
  85. package/dist/chunk-TROD36DK.cjs.map +0 -1
  86. package/dist/chunk-TYYJSOZS.cjs.map +0 -1
  87. package/dist/chunks-Q4GCPRRU.js +0 -10
  88. package/dist/formula-YMGXXRBQ.cjs.map +0 -1
  89. package/dist/image-ocr-BE2HHMVN.cjs.map +0 -1
  90. package/dist/parser-CMNHBVTL.js.map +0 -1
  91. package/dist/parser-NR3XO4SM.js.map +0 -1
  92. package/dist/parser-QJY5W47V.cjs.map +0 -1
  93. package/dist/pdf-ocr-DF3OTBKH.js +0 -13
  94. package/dist/pdf-ocr-R4DIKF4Q.js +0 -16
  95. package/dist/pdf-ocr-T3RHIZKY.cjs +0 -14
  96. package/dist/pdf-ocr-T3RHIZKY.cjs.map +0 -1
  97. package/dist/render-KSQ6UGCP.js +0 -10
  98. package/dist/seal-EOFFQ4JR.js +0 -10
  99. /package/dist/{-B3EUUBPV.js.map → -EZH6NKTE.js.map} +0 -0
  100. /package/dist/{chunk-QKFCGJJY.js.map → chunk-2HBYWA5X.js.map} +0 -0
  101. /package/dist/{chunk-COGFPMEB.js.map → chunk-2LO525TL.js.map} +0 -0
  102. /package/dist/{chunk-YASFVKDT.js.map → chunk-DBGEUDBY.js.map} +0 -0
  103. /package/dist/{chunk-4PKNWBXJ.js.map → chunk-FBLMVHDK.js.map} +0 -0
  104. /package/dist/{chunk-I2OETEEL.js.map → chunk-FVBR7SC3.js.map} +0 -0
  105. /package/dist/{chunk-46UDI56N.js.map → chunk-IOJX4Q63.js.map} +0 -0
  106. /package/dist/{chunk-BQE7AMFS.js.map → chunk-JCMBQC5N.js.map} +0 -0
  107. /package/dist/{chunk-TF5DS74F.js.map → chunk-KX7IYCCZ.js.map} +0 -0
  108. /package/dist/{chunk-MJPRWCI5.js.map → chunk-MUYU4JI5.js.map} +0 -0
  109. /package/dist/{chunk-2IR3PEZU.js.map → chunk-QBCMFNHS.js.map} +0 -0
  110. /package/dist/{chunk-DRLKMFDV.js.map → chunk-QM2RSJ2S.js.map} +0 -0
  111. /package/dist/{chunk-TJYZY2O6.js.map → chunk-UL2MDA5C.js.map} +0 -0
  112. /package/dist/{chunk-7JQ2ITT2.js.map → chunk-XXDZ2KTW.js.map} +0 -0
  113. /package/dist/{chunks-Q4GCPRRU.js.map → chunks-TT7ZE5QQ.js.map} +0 -0
  114. /package/dist/{formula-WGKKR5OQ.js.map → formula-6RQX4NMJ.js.map} +0 -0
  115. /package/dist/{formula-G4CD5KQ6.js.map → formula-NW33S4TA.js.map} +0 -0
  116. /package/dist/{image-ocr-MBWZK3ZT.js.map → image-ocr-CAVU2TNZ.js.map} +0 -0
  117. /package/dist/{image-ocr-M4RP4INO.js.map → image-ocr-SO56VETS.js.map} +0 -0
  118. /package/dist/{model-bundle-BIMP5DIH.js.map → model-bundle-EIJF34DR.js.map} +0 -0
  119. /package/dist/{models-QPOEGHVA.js.map → models-6V4CPQXC.js.map} +0 -0
  120. /package/dist/{pdf-ocr-DF3OTBKH.js.map → pdf-ocr-IRXQ5GBM.js.map} +0 -0
  121. /package/dist/{pdf-ocr-R4DIKF4Q.js.map → pdf-ocr-SL4GCOGS.js.map} +0 -0
  122. /package/dist/{profile-io-UZ43CLWX.js.map → profile-io-PZVMM66V.js.map} +0 -0
  123. /package/dist/{rasterize-JC2JVVOF.js.map → rasterize-NRWP2C6L.js.map} +0 -0
  124. /package/dist/{render-KSQ6UGCP.js.map → render-V5ZPTZQ7.js.map} +0 -0
  125. /package/dist/{seal-EOFFQ4JR.js.map → seal-LVG2DGAB.js.map} +0 -0
  126. /package/dist/{setup-WDUVPFX3.js.map → setup-V55AJP6H.js.map} +0 -0
  127. /package/dist/{watch-UI7OXIES.js.map → watch-7CQSMDDP.js.map} +0 -0
package/dist/index.d.cts CHANGED
@@ -1148,7 +1148,8 @@ declare function unknownFontWarnings(fonts: Record<string, string | undefined>):
1148
1148
  * 행정안전부 행정업무운영 편람의 날짜·시간·금액·기호 표기법을 정규식으로 검사.
1149
1149
  *
1150
1150
  * 원전: jkf87/hwpx-skill gonmun_lint.py(2025 편람 기준 13룰)를 kordoc에 맞게 이식
1151
- * (v4.0.1). URL 쌍점 오탐 가드 등 일부 보강. 검사는 조언용이다 — 생성은 막지 않고
1151
+ * (v4.0.1). URL 쌍점 오탐 가드 등 일부 보강. v4.12.1 에 금액 한글 병기·물결표·두음법칙·
1152
+ * 외래어·차별 표현·"끝." 누락 6룰 보강 (pyhwpxlib Gongmun 검사 항목 대조). 검사는 조언용이다 — 생성은 막지 않고
1152
1153
  * 경고만 낸다 (A2 폰트경고와 같은 원칙). 별도 CLI `kordoc lint`는 error 시 exit 1.
1153
1154
  */
1154
1155
  interface GongmunLintFinding {
@@ -1166,7 +1167,9 @@ interface GongmunLintFinding {
1166
1167
  * 텍스트(마크다운 포함) 표기법 검수. 마크다운 펜스 코드블록(``` ~ ```) 안은
1167
1168
  * 건너뛴다 — 코드·URL이 날짜/쌍점 규칙에 오탐되는 것 방지.
1168
1169
  */
1169
- declare function lintGongmunText(text: string): GongmunLintFinding[];
1170
+ declare function lintGongmunText(text: string, opts?: {
1171
+ document?: boolean;
1172
+ }): GongmunLintFinding[];
1170
1173
  /** 검수 결과를 사람이 읽는 경고 문자열 배열로 — generate 경고 채널(A2와 동일)용 */
1171
1174
  declare function gongmunLintWarnings(text: string, limit?: number): string[];
1172
1175
 
package/dist/index.d.ts CHANGED
@@ -1148,7 +1148,8 @@ declare function unknownFontWarnings(fonts: Record<string, string | undefined>):
1148
1148
  * 행정안전부 행정업무운영 편람의 날짜·시간·금액·기호 표기법을 정규식으로 검사.
1149
1149
  *
1150
1150
  * 원전: jkf87/hwpx-skill gonmun_lint.py(2025 편람 기준 13룰)를 kordoc에 맞게 이식
1151
- * (v4.0.1). URL 쌍점 오탐 가드 등 일부 보강. 검사는 조언용이다 — 생성은 막지 않고
1151
+ * (v4.0.1). URL 쌍점 오탐 가드 등 일부 보강. v4.12.1 에 금액 한글 병기·물결표·두음법칙·
1152
+ * 외래어·차별 표현·"끝." 누락 6룰 보강 (pyhwpxlib Gongmun 검사 항목 대조). 검사는 조언용이다 — 생성은 막지 않고
1152
1153
  * 경고만 낸다 (A2 폰트경고와 같은 원칙). 별도 CLI `kordoc lint`는 error 시 exit 1.
1153
1154
  */
1154
1155
  interface GongmunLintFinding {
@@ -1166,7 +1167,9 @@ interface GongmunLintFinding {
1166
1167
  * 텍스트(마크다운 포함) 표기법 검수. 마크다운 펜스 코드블록(``` ~ ```) 안은
1167
1168
  * 건너뛴다 — 코드·URL이 날짜/쌍점 규칙에 오탐되는 것 방지.
1168
1169
  */
1169
- declare function lintGongmunText(text: string): GongmunLintFinding[];
1170
+ declare function lintGongmunText(text: string, opts?: {
1171
+ document?: boolean;
1172
+ }): GongmunLintFinding[];
1170
1173
  /** 검수 결과를 사람이 읽는 경고 문자열 배열로 — generate 경고 채널(A2와 동일)용 */
1171
1174
  declare function gongmunLintWarnings(text: string, limit?: number): string[];
1172
1175
 
package/dist/index.js CHANGED
@@ -8,7 +8,7 @@ import {
8
8
  flattenLayoutTables,
9
9
  inlineImagesIntoMarkdown,
10
10
  mapPuaText
11
- } from "./chunk-HGUNWAXK.js";
11
+ } from "./chunk-2B7OIEJJ.js";
12
12
  import {
13
13
  parsePageRange
14
14
  } from "./chunk-GE43BE46.js";
@@ -29,7 +29,7 @@ import {
29
29
  sanitizeHref,
30
30
  stripDtd,
31
31
  toArrayBuffer
32
- } from "./chunk-MJPRWCI5.js";
32
+ } from "./chunk-MUYU4JI5.js";
33
33
 
34
34
  // src/index.ts
35
35
  import { readFile } from "fs/promises";
@@ -2748,6 +2748,9 @@ function parseDocInfo(records) {
2748
2748
  function createParaTextState() {
2749
2749
  return { text: "", ctrlIdx: 0, fieldStack: [], fieldRanges: [] };
2750
2750
  }
2751
+ var HANCOM_FOLDED_PUA_START = 40960;
2752
+ var HANCOM_FOLDED_PUA_END = 42124;
2753
+ var HANCOM_FOLDED_PUA_SHIFT = 983040 - 40960;
2751
2754
  function isExtendedOnlyCtrlChar(ch) {
2752
2755
  return ch >= 1 && ch <= 3 || ch >= 11 && ch <= 12 || ch >= 14 && ch <= 18 || ch >= 21 && ch <= 23;
2753
2756
  }
@@ -2815,6 +2818,10 @@ function appendParaText(state, data, resolveControl) {
2815
2818
  i += 14;
2816
2819
  }
2817
2820
  } else if (ch >= 32) {
2821
+ if (ch >= HANCOM_FOLDED_PUA_START && ch <= HANCOM_FOLDED_PUA_END) {
2822
+ result += String.fromCodePoint(ch + HANCOM_FOLDED_PUA_SHIFT);
2823
+ break;
2824
+ }
2818
2825
  if (ch >= 55296 && ch <= 56319 && i + 1 < data.length) {
2819
2826
  const lo = data.readUInt16LE(i);
2820
2827
  if (lo >= 56320 && lo <= 57343) {
@@ -21066,10 +21073,9 @@ function extractFormFields(blocks) {
21066
21073
  const fields = [];
21067
21074
  let totalTables = 0;
21068
21075
  let formTables = 0;
21069
- for (const block of blocks) {
21070
- if (block.type !== "table" || !block.table) continue;
21076
+ for (const table2 of collectTables(blocks)) {
21071
21077
  totalTables++;
21072
- const tableFields = extractFromTable(block.table);
21078
+ const tableFields = extractFromTable(table2);
21073
21079
  if (tableFields.length > 0) {
21074
21080
  formTables++;
21075
21081
  fields.push(...tableFields);
@@ -21084,6 +21090,17 @@ function extractFormFields(blocks) {
21084
21090
  const confidence = totalTables > 0 ? formTables / totalTables : fields.length > 0 ? 0.3 : 0;
21085
21091
  return { fields, confidence: Math.min(confidence, 1) };
21086
21092
  }
21093
+ function collectTables(blocks, out = [], depth = 0) {
21094
+ if (depth > 12) return out;
21095
+ for (const block of blocks) {
21096
+ if (block.type !== "table" || !block.table) continue;
21097
+ out.push(block.table);
21098
+ for (const row of block.table.cells) {
21099
+ for (const cell2 of row) if (cell2?.blocks?.length) collectTables(cell2.blocks, out, depth + 1);
21100
+ }
21101
+ }
21102
+ return out;
21103
+ }
21087
21104
  function extractFromTable(table2) {
21088
21105
  const fields = [];
21089
21106
  if (table2.cols >= 2) {
@@ -22282,17 +22299,17 @@ async function fillHwpx(hwpxBuffer, values, blockedLabels) {
22282
22299
  const matchText = (p) => paraTText(p, xml) ?? p.text;
22283
22300
  const cellLabelText = (cell2) => cell2.paragraphs.filter((p) => !p.inTextbox).map((p) => matchText(p)).join("");
22284
22301
  const allTables = [];
22285
- const collectTables = (tables, depth) => {
22302
+ const collectTables2 = (tables, depth) => {
22286
22303
  if (depth > 16) return;
22287
22304
  for (const t of tables) {
22288
22305
  allTables.push(t);
22289
22306
  for (const row of t.rows) {
22290
- for (const cell2 of row) collectTables(cell2.tables, depth + 1);
22307
+ for (const cell2 of row) collectTables2(cell2.tables, depth + 1);
22291
22308
  }
22292
22309
  }
22293
22310
  };
22294
- collectTables(scan.tables, 0);
22295
- collectTables(scan.orphanTables, 0);
22311
+ collectTables2(scan.tables, 0);
22312
+ collectTables2(scan.orphanTables, 0);
22296
22313
  const patternApplied = /* @__PURE__ */ new Set();
22297
22314
  for (const table2 of allTables) {
22298
22315
  for (const row of table2.rows) {
@@ -25976,10 +25993,13 @@ function footSep(g) {
25976
25993
  const bodyMm = 210 - g.margins.left - g.margins.right;
25977
25994
  return "\u2500".repeat(Math.max(10, Math.round(bodyMm * (46 / 175))));
25978
25995
  }
25996
+ function isInternalApproval(to) {
25997
+ return !!to && /^\s*내\s*부\s*결\s*재/.test(to);
25998
+ }
25979
25999
  function buildDocFoot(g, ids) {
25980
26000
  const f = g.docFoot;
25981
26001
  const out = [blank()];
25982
- if (f.sender) {
26002
+ if (f.sender && !isInternalApproval(g.docHead?.to)) {
25983
26003
  out.push(para(f.sender, GONGMUN_CENTER, ids.sender), blank());
25984
26004
  }
25985
26005
  const foot = (text) => para(text, GONGMUN_TBL_LEFT, ids.foot);
@@ -27930,6 +27950,77 @@ function unknownFontWarnings(fonts) {
27930
27950
  }
27931
27951
 
27932
27952
  // src/hwpx/gongmun-lint.ts
27953
+ var LOANWORD_FIXES = [
27954
+ ["\uCEE8\uD150\uCE20", "\uCF58\uD150\uCE20"],
27955
+ ["\uC5B4\uD50C\uB9AC\uCF00\uC774\uC158", "\uC560\uD50C\uB9AC\uCF00\uC774\uC158"],
27956
+ ["\uBA54\uC138\uC9C0", "\uBA54\uC2DC\uC9C0"],
27957
+ ["\uB9AC\uB354\uC27D", "\uB9AC\uB354\uC2ED"],
27958
+ ["\uC6CC\uD06C\uC0F5", "\uC6CC\uD06C\uC20D"],
27959
+ ["\uC2A4\uCF00\uC974", "\uC2A4\uCF00\uC904"],
27960
+ ["\uC545\uC138\uC0AC\uB9AC", "\uC561\uC138\uC11C\uB9AC"],
27961
+ ["\uB124\uBE44\uAC8C\uC774\uC158", "\uB0B4\uBE44\uAC8C\uC774\uC158"],
27962
+ ["\uD0C0\uAC9F", "\uD0C0\uAE43"],
27963
+ ["\uD654\uC774\uD305", "\uD30C\uC774\uD305"],
27964
+ ["\uBE44\uC9C0\uB2C8\uC2A4", "\uBE44\uC988\uB2C8\uC2A4"],
27965
+ ["\uD504\uB9AC\uC820\uD14C\uC774\uC158", "\uD504\uB808\uC820\uD14C\uC774\uC158"],
27966
+ ["\uB77C\uC774\uC13C\uC2A4", "\uB77C\uC774\uC120\uC2A4"],
27967
+ ["\uCE90\uBE44\uB137", "\uCE90\uBE44\uB2DB"],
27968
+ ["\uB80C\uD2B8\uCE74", "\uB80C\uD130\uCE74"],
27969
+ ["\uD50C\uB7AD\uCE74\uB4DC", "\uD50C\uB798\uCE74\uB4DC"],
27970
+ ["\uD50C\uB79C\uCE74\uB4DC", "\uD50C\uB798\uCE74\uB4DC"],
27971
+ ["\uCEE8\uC149", "\uCF58\uC149\uD2B8"],
27972
+ ["\uC2EC\uBCFC", "\uC2EC\uBC8C"],
27973
+ ["\uCE74\uB2EC\uB85C\uADF8", "\uCE74\uD0C8\uB85C\uADF8"],
27974
+ ["\uD31C\uD50C\uB81B", "\uD338\uD50C\uB9BF"],
27975
+ ["\uD31C\uD50C\uB9BF", "\uD338\uD50C\uB9BF"],
27976
+ ["\uB9AC\uBAA8\uCF58", "\uB9AC\uBAA8\uCEE8"],
27977
+ ["\uC5D0\uC5B4\uCF58", "\uC5D0\uC5B4\uCEE8"],
27978
+ ["\uC54C\uCF5C", "\uC54C\uCF54\uC62C"],
27979
+ ["\uBC1C\uB780\uC2A4", "\uBC38\uB7F0\uC2A4"],
27980
+ ["\uB9E4\uB2C8\uC544", "\uB9C8\uB2C8\uC544"],
27981
+ ["\uCF54\uBA54\uB514", "\uCF54\uBBF8\uB514"],
27982
+ ["\uD310\uB12C", "\uD328\uB110"],
27983
+ ["\uC575\uCF5C", "\uC559\uCF54\uB974"],
27984
+ ["\uB85C\uBCF4\uD2B8", "\uB85C\uBD07"],
27985
+ ["\uBC14\uBCA0\uD050", "\uBC14\uBE44\uD050"],
27986
+ ["\uBD80\uD398", "\uBDD4\uD398"],
27987
+ ["\uCD08\uCF54\uB81B", "\uCD08\uCF5C\uB9BF"],
27988
+ ["\uCE74\uD398\uD2B8", "\uCE74\uD3AB"],
27989
+ ["\uCF00\uC78C", "\uCF00\uC774\uD06C"],
27990
+ ["\uB3C4\uB108\uCE20", "\uB3C4\uB11B"],
27991
+ ["\uC288\uD37C\uB9C8\uCF19", "\uC288\uD37C\uB9C8\uCF13"],
27992
+ ["\uC368\uBE44\uC2A4", "\uC11C\uBE44\uC2A4"],
27993
+ ["\uC13C\uD0C0", "\uC13C\uD130"]
27994
+ ];
27995
+ var LOANWORD_RE = new RegExp(LOANWORD_FIXES.map(([w]) => w).sort((a, b) => b.length - a.length).join("|"), "g");
27996
+ var DISCRIM_FIXES = [
27997
+ ["\uC7A5\uC560\uC790", "\uC7A5\uC560\uC778"],
27998
+ ["\uC7A5\uC560\uC6B0", "\uC7A5\uC560\uC778"],
27999
+ ["\uBD88\uAD6C\uC790", "\uC7A5\uC560\uC778"],
28000
+ ["\uC815\uC2E0\uBC15\uC57D", "\uC9C0\uC801\uC7A5\uC560"],
28001
+ ["\uC815\uC0C1\uC778", "\uBE44\uC7A5\uC560\uC778"],
28002
+ ["\uD3B8\uBD80\uBAA8", "\uD55C\uBD80\uBAA8"],
28003
+ ["\uACB0\uC190\uAC00\uC815", "\uD55C\uBD80\uBAA8\uAC00\uC815"],
28004
+ ["\uBBF8\uB9DD\uC778", "\uACE0\uC778\uC758 \uBC30\uC6B0\uC790"],
28005
+ ["\uD559\uBD80\uD615", "\uD559\uBD80\uBAA8"],
28006
+ ["\uBD88\uC6B0\uC774\uC6C3", "\uC5B4\uB824\uC6B4 \uC774\uC6C3"],
28007
+ ["\uC720\uBAA8\uCC28", "\uC720\uC544\uCC28"],
28008
+ ["\uC800\uCD9C\uC0B0", "\uC800\uCD9C\uC0DD"],
28009
+ ["\uC7A1\uC0C1\uC778", "\uC774\uB3D9\uC0C1\uC778"],
28010
+ ["\uD30C\uCD9C\uBD80", "\uAC00\uC0AC\uB3C4\uC6B0\uBBF8"],
28011
+ ["\uCCAD\uC18C\uBD80", "\uD658\uACBD\uBBF8\uD654\uC6D0"],
28012
+ ["\uAC04\uD638\uC6D0", "\uAC04\uD638\uC0AC"],
28013
+ ["\uC6B4\uC804\uC218", "\uC6B4\uC804\uAE30\uC0AC"],
28014
+ ["\uB178\uAC00\uB2E4", "\uAC74\uC124\uB178\uB3D9\uC790"],
28015
+ ["\uC870\uC120\uC871", "\uC911\uAD6D\uB3D9\uD3EC"],
28016
+ ["\uB9E4\uB9E4\uCD98", "\uC131\uB9E4\uB9E4"],
28017
+ ["\uC0AC\uC0DD\uC544", "\uD63C\uC678\uC790"],
28018
+ ["\uBC99\uC5B4\uB9AC", "\uC5B8\uC5B4\uC7A5\uC560\uC778"],
28019
+ ["\uC808\uB984\uBC1C\uC774", "\uC9C0\uCCB4\uC7A5\uC560\uC778"],
28020
+ ["\uC560\uAFB8\uB208", "\uC2DC\uAC01\uC7A5\uC560\uC778"],
28021
+ ["\uADC0\uBA38\uAC70\uB9AC", "\uCCAD\uAC01\uC7A5\uC560\uC778"]
28022
+ ];
28023
+ var DISCRIM_RE = new RegExp(DISCRIM_FIXES.map(([w]) => w).sort((a, b) => b.length - a.length).join("|"), "g");
27933
28024
  var RULES = [
27934
28025
  // 날짜 ─ 온점 뒤 한 칸, 0 패딩 금지, 연도 4자리, 끝 마침표
27935
28026
  {
@@ -28028,6 +28119,51 @@ var RULES = [
28028
28119
  message: "\uC30D\uC810\uC740 \uC55E\uB9D0\uC5D0 \uBD99\uC774\uACE0 \uB4A4\uB294 \uD55C \uCE78 \uB744\uC6C0",
28029
28120
  suggest: "\uC608) \uC6D0\uC7A5: \uAE40\uAC11\uB3D9"
28030
28121
  },
28122
+ // ── 편람 보강 (v4.12.1) — pyhwpxlib Gongmun 검사·hwpx-skill gonmun_lint 대조로 빈 축 보충 ──
28123
+ // 금액 한글 병기 — 규정 시행규칙 제2조: 아라비아 숫자 다음 괄호 안에 한글로 적는다
28124
+ {
28125
+ code: "MONEY_NO_HANGUL",
28126
+ severity: "warning",
28127
+ pattern: /금\d[\d,]*원(?![\s]*[((])/g,
28128
+ message: "\uAE08\uC561\uC740 \uC22B\uC790 \uB2E4\uC74C \uAD04\uD638 \uC548\uC5D0 \uD55C\uAE00 \uBCD1\uAE30",
28129
+ suggest: "\uC608) \uAE08113,560\uC6D0(\uAE08\uC77C\uC2ED\uC77C\uB9CC\uC0BC\uCC9C\uC624\uBC31\uC721\uC2ED\uC6D0)"
28130
+ },
28131
+ // 물결표 앞뒤 붙여쓰기 — 기간·범위는 "2. 20.∼2. 24." 처럼 붙여 씀. 숫자·날짜·시각 범위(앞은 숫자·
28132
+ // 온점·괄호·년월일시분, 뒤는 숫자·연도 생략부호)에만 건다 — "기간( 부터 ~ 까지)" 기입란·"~ ※ 비고"·
28133
+ // "~ 수리완료시까지" 처럼 낱말이 붙는 물결표는 범위 표기가 아니다 (v4.12.2, 별지서식 47→30·기안문 32→27 실측)
28134
+ {
28135
+ code: "TILDE_SPACE",
28136
+ severity: "warning",
28137
+ pattern: /(?<=[\d.)일월년시분’'])[ \t]+[∼~~]|[∼~~][ \t]+(?=[\d'’])/g,
28138
+ message: "\uBB3C\uACB0\uD45C(\u223C) \uC55E\uB4A4\uB294 \uBD99\uC5EC \uC500",
28139
+ suggest: "\uC608) 2. 20.\u223C2. 24., 09:00\u223C18:00"
28140
+ },
28141
+ // 두음법칙 — 어두의 "년도·년간·년말…" 은 "연도·연간·연말" (숫자 뒤 '2026년도' 는 정상). 서식의
28142
+ // 기입란(" 년도", "( 년간)", "2026 년도")과 셀 하나가 "년도" 뿐인 표 머리글은 어두가 아니라
28143
+ // 빈칸 뒤 접미라 제외한다 (v4.12.2, 별지서식 실측 28건 전부 이 꼴·기안문 0건)
28144
+ {
28145
+ code: "DUEUM_ERROR",
28146
+ severity: "warning",
28147
+ pattern: /(?<![가-힣\d])(?<!\d[ \t]{1,3})(?<![((>|][ \t]*)(?<![ \t][ \t])년(?:도별|도|간|말|초|차|세|내)(?![가-힣])(?![ \t]*(?:<\/t[dh]>|\|))/g,
28148
+ message: "\uC5B4\uB450\uC758 '\uB144'\uC740 \uB450\uC74C\uBC95\uCE59\uC5D0 \uB530\uB77C '\uC5F0'\uC73C\uB85C \uC801\uC74C",
28149
+ suggest: "\uC608) \uC5F0\uB3C4, \uC5F0\uAC04, \uC5F0\uB9D0, \uC5F0\uCD08"
28150
+ },
28151
+ // 외래어 표기 — 국립국어원 표기법 기준 흔한 오기 (사전 매칭)
28152
+ {
28153
+ code: "LOANWORD_ERROR",
28154
+ severity: "warning",
28155
+ pattern: LOANWORD_RE,
28156
+ message: "\uC678\uB798\uC5B4 \uD45C\uAE30\uBC95\uC5D0 \uB9DE\uC9C0 \uC54A\uB294 \uD45C\uAE30",
28157
+ suggest: "\uC608) \uCF58\uD150\uCE20\xB7\uC560\uD50C\uB9AC\uCF00\uC774\uC158\xB7\uBA54\uC2DC\uC9C0\xB7\uC6CC\uD06C\uC20D\xB7\uC2A4\uCF00\uC904\xB7\uCF58\uC149\uD2B8"
28158
+ },
28159
+ // 차별·비하 표현 — 행안부 공문서 작성 지침·국립국어원 순화어
28160
+ {
28161
+ code: "DISCRIMINATORY_TERM",
28162
+ severity: "warning",
28163
+ pattern: DISCRIM_RE,
28164
+ message: "\uCC28\uBCC4\xB7\uBE44\uD558 \uD45C\uD604\uC740 \uC21C\uD654\uC5B4\uB85C",
28165
+ suggest: "\uC608) \uC7A5\uC560\uC790\u2192\uC7A5\uC560\uC778, \uD3B8\uBD80\uBAA8\u2192\uD55C\uBD80\uBAA8, \uD559\uBD80\uD615\u2192\uD559\uBD80\uBAA8, \uBBF8\uB9DD\uC778\u2192\uACE0\uC778\uC758 \uBC30\uC6B0\uC790"
28166
+ },
28031
28167
  // ── AI 문체 흔적(슬롭) — 편람 원전 외 kordoc 자체 룰 (v4.9.0) ──────────────
28032
28168
  // 생성형 AI 초안이 공문서로 흘러들 때 남는 기계 문체를 걸러낸다. 조언용 warning.
28033
28169
  {
@@ -28045,7 +28181,7 @@ var RULES = [
28045
28181
  suggest: "\uB9AC\uB4DC\uC5B4\xB7\uD575\uC2EC \uC218\uCE58 \uD55C \uACF3\uB9CC \uAC15\uC870"
28046
28182
  }
28047
28183
  ];
28048
- function lintGongmunText(text) {
28184
+ function lintGongmunText(text, opts) {
28049
28185
  const findings = [];
28050
28186
  let fenceMarker = null;
28051
28187
  const lines = text.split(/\r?\n/);
@@ -28068,13 +28204,29 @@ function lintGongmunText(text) {
28068
28204
  rule: r.code,
28069
28205
  severity: r.severity,
28070
28206
  message: r.message,
28071
- suggest: r.suggest
28207
+ suggest: dictSuggest(r.code, m[0]) ?? r.suggest
28072
28208
  });
28073
28209
  }
28074
28210
  }
28075
28211
  }
28212
+ if (opts?.document && /^\s*붙\s*임(?![가-힣])/m.test(text) && !/끝\.\s*$/m.test(text)) {
28213
+ findings.push({
28214
+ line: lines.length,
28215
+ match: "\uBD99\uC784",
28216
+ rule: "END_MARK_MISSING",
28217
+ severity: "warning",
28218
+ message: "\uBD99\uC784 \uD45C\uC2DC \uB4A4\uC5D0 '\uB05D.' \uD45C\uC2DC\uAC00 \uC5C6\uC74C",
28219
+ suggest: "\uC608) \uBD99\uC784 \uACC4\uD68D\uC11C 1\uBD80. \uB05D."
28220
+ });
28221
+ }
28076
28222
  return findings;
28077
28223
  }
28224
+ function dictSuggest(code, match) {
28225
+ const table2 = code === "LOANWORD_ERROR" ? LOANWORD_FIXES : code === "DISCRIMINATORY_TERM" ? DISCRIM_FIXES : null;
28226
+ if (!table2) return void 0;
28227
+ const hit = table2.find(([w]) => w === match.trim());
28228
+ return hit ? `${hit[0]} \u2192 ${hit[1]}` : void 0;
28229
+ }
28078
28230
  function gongmunLintWarnings(text, limit = 10) {
28079
28231
  const findings = lintGongmunText(text);
28080
28232
  const shown = findings.slice(0, limit).map(
@@ -32322,7 +32474,7 @@ async function dispatch(format, buffer, opts) {
32322
32474
  }
32323
32475
  async function parseImage(buffer, options) {
32324
32476
  try {
32325
- const { parseImageDocument } = await import("./image-ocr-MBWZK3ZT.js");
32477
+ const { parseImageDocument } = await import("./image-ocr-CAVU2TNZ.js");
32326
32478
  const { blocks, warnings } = await parseImageDocument(buffer, options);
32327
32479
  return {
32328
32480
  success: true,
@@ -32381,7 +32533,7 @@ async function parseHwp(buffer, options) {
32381
32533
  async function parsePdf(buffer, options) {
32382
32534
  let parsePdfDocument;
32383
32535
  try {
32384
- const mod = await import("./parser-CMNHBVTL.js");
32536
+ const mod = await import("./parser-GIREMEPD.js");
32385
32537
  parsePdfDocument = mod.parsePdfDocument;
32386
32538
  } catch {
32387
32539
  return {