kordoc 4.23.0 → 4.24.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 (126) hide show
  1. package/README.md +3 -1
  2. package/dist/{-YNDBG7XO.js → -VEOO4BOM.js} +13 -11
  3. package/dist/{chunk-4AI32TFY.js → chunk-3M2BIQLZ.js} +2 -2
  4. package/dist/{chunk-2EVBM5NZ.js → chunk-4VPN5MHS.js} +2 -2
  5. package/dist/{chunk-VRIHNSYF.js → chunk-55DOBEGD.js} +2 -2
  6. package/dist/{chunk-2URKNOOQ.js → chunk-6YDPTTIY.js} +2 -2
  7. package/dist/{chunk-4XLDFJGY.js → chunk-CKCJU42Y.js} +6 -6
  8. package/dist/{chunk-5NBUT7GX.js → chunk-DBPDAG7N.js} +2 -2
  9. package/dist/{chunk-M6QZYQRH.cjs → chunk-DKWEZWCF.cjs} +3 -3
  10. package/dist/{chunk-M6QZYQRH.cjs.map → chunk-DKWEZWCF.cjs.map} +1 -1
  11. package/dist/{chunk-QO4PT3NN.js → chunk-DM5YPONO.js} +2 -2
  12. package/dist/{chunk-KLVN3EW7.cjs → chunk-DZT3CHNZ.cjs} +3 -3
  13. package/dist/{chunk-KLVN3EW7.cjs.map → chunk-DZT3CHNZ.cjs.map} +1 -1
  14. package/dist/{chunk-LJSKKGWU.js → chunk-EN3PK6OT.js} +2 -2
  15. package/dist/{chunk-6BUW4PJ3.js → chunk-ENCAYXDI.js} +4 -4
  16. package/dist/{chunk-I22NWH6W.cjs → chunk-FHFPQ6MX.cjs} +24 -24
  17. package/dist/{chunk-I22NWH6W.cjs.map → chunk-FHFPQ6MX.cjs.map} +1 -1
  18. package/dist/{chunk-W67KQWMU.js → chunk-G4HWEM6Q.js} +4 -4
  19. package/dist/{chunk-MSUBZT53.js → chunk-GEY6ZMXH.js} +177 -17
  20. package/dist/chunk-GEY6ZMXH.js.map +1 -0
  21. package/dist/{chunk-MA4NIAYO.js → chunk-H5PDQ66I.js} +3 -3
  22. package/dist/{chunk-5KLO56CJ.cjs → chunk-HOXHBG4C.cjs} +2 -2
  23. package/dist/{chunk-5KLO56CJ.cjs.map → chunk-HOXHBG4C.cjs.map} +1 -1
  24. package/dist/{chunk-IOBVLL5A.cjs → chunk-IHNIPVPJ.cjs} +6 -6
  25. package/dist/{chunk-IOBVLL5A.cjs.map → chunk-IHNIPVPJ.cjs.map} +1 -1
  26. package/dist/{chunk-YFCE7J37.js → chunk-IZYYKE5F.js} +2 -2
  27. package/dist/{chunk-2CHTPRDU.js → chunk-O2SLKE4L.js} +2 -2
  28. package/dist/{chunk-QXIZ5XSB.cjs → chunk-OHDGEGDJ.cjs} +23 -23
  29. package/dist/{chunk-QXIZ5XSB.cjs.map → chunk-OHDGEGDJ.cjs.map} +1 -1
  30. package/dist/{chunk-TFXWP5QI.js → chunk-OJISMR2Z.js} +2 -2
  31. package/dist/{chunk-67DVZL4W.js → chunk-P35SCAJN.js} +2 -2
  32. package/dist/{chunk-GZFL6UOS.js → chunk-P6FNIU5P.js} +2 -2
  33. package/dist/{chunk-A5HW2Z5T.js → chunk-PX6X4YOR.js} +4 -4
  34. package/dist/{chunk-XCSWST2A.js → chunk-SRUBDR3Z.js} +3 -3
  35. package/dist/{chunk-Y3LLNEU3.js → chunk-XLY5ZJRP.js} +2 -2
  36. package/dist/{chunk-UIZYWW3J.js → chunk-Z5G6QNVH.js} +2 -2
  37. package/dist/{chunk-S7B74SOM.js → chunk-Z62PHCSU.js} +3 -3
  38. package/dist/{chunk-6O2AUXFB.js → chunk-ZGNHJCDR.js} +2 -2
  39. package/dist/chunks-LV5EJBZ4.js +10 -0
  40. package/dist/cli.js +57 -30
  41. package/dist/cli.js.map +1 -1
  42. package/dist/{formula-ZULPRKJR.js → formula-4GRNVWIQ.js} +4 -4
  43. package/dist/{formula-JASN5ELL.js → formula-6Y467S4R.js} +3 -3
  44. package/dist/{formula-Z7JPTFAV.cjs → formula-R3OWFGCZ.cjs} +9 -9
  45. package/dist/{formula-Z7JPTFAV.cjs.map → formula-R3OWFGCZ.cjs.map} +1 -1
  46. package/dist/{image-ocr-IYFAHFQH.js → image-ocr-CGJJCD4E.js} +9 -9
  47. package/dist/{image-ocr-MJVZFMXW.cjs → image-ocr-EBKG45KF.cjs} +17 -17
  48. package/dist/{image-ocr-MJVZFMXW.cjs.map → image-ocr-EBKG45KF.cjs.map} +1 -1
  49. package/dist/{image-ocr-2ZWU6O5O.js → image-ocr-ZLL66KNO.js} +6 -6
  50. package/dist/index.cjs +847 -687
  51. package/dist/index.cjs.map +1 -1
  52. package/dist/index.d.cts +47 -1
  53. package/dist/index.d.ts +47 -1
  54. package/dist/index.js +172 -12
  55. package/dist/index.js.map +1 -1
  56. package/dist/mcp.js +40 -27
  57. package/dist/mcp.js.map +1 -1
  58. package/dist/{model-bundle-FD5ORLT4.js → model-bundle-RD4KSNTB.js} +5 -5
  59. package/dist/{models-CNXDF4BB.js → models-OLGCZBXI.js} +5 -5
  60. package/dist/{parse-worker-v2-GZIAKU6M.js → parse-worker-v2-YZY2GQY5.js} +51 -15
  61. package/dist/parse-worker-v2-YZY2GQY5.js.map +1 -0
  62. package/dist/{parser-MTUCDPDX.cjs → parser-DX4I3GT7.cjs} +157 -157
  63. package/dist/{parser-MTUCDPDX.cjs.map → parser-DX4I3GT7.cjs.map} +1 -1
  64. package/dist/{parser-OQ4FLNBE.js → parser-IHCFHLHN.js} +12 -12
  65. package/dist/{parser-WTOPH3C2.js → parser-J22QBXPP.js} +9 -9
  66. package/dist/{pdf-ocr-V346MK77.js → pdf-ocr-H2247ZZH.js} +6 -6
  67. package/dist/{pdf-ocr-IBZRT65M.js → pdf-ocr-TYIH23RP.js} +9 -9
  68. package/dist/pdf-ocr-WU7SZHWM.cjs +31 -0
  69. package/dist/{pdf-ocr-EW34T34R.cjs.map → pdf-ocr-WU7SZHWM.cjs.map} +1 -1
  70. package/dist/{profile-io-UFTSKMWU.js → profile-io-GH3TYTNJ.js} +3 -3
  71. package/dist/{rasterize-SXIF5J5K.js → rasterize-7TFTVFMB.js} +3 -3
  72. package/dist/{redact-doc-P6XQ6B6U.js → redact-doc-DSSQOO3A.js} +11 -11
  73. package/dist/{render-QW56QMUE.js → render-NKXZQINI.js} +7 -7
  74. package/dist/seal-JIVSSI3Z.js +10 -0
  75. package/dist/{setup-RIZUPKAE.js → setup-JVLMX2TH.js} +3 -3
  76. package/dist/visual-BKVZN6HP.js +21 -0
  77. package/dist/{watch-TAHHXY7Z.js → watch-5RZVHAKI.js} +12 -12
  78. package/package.json +1 -1
  79. package/dist/chunk-MSUBZT53.js.map +0 -1
  80. package/dist/chunks-LX2EPPSD.js +0 -10
  81. package/dist/parse-worker-v2-GZIAKU6M.js.map +0 -1
  82. package/dist/pdf-ocr-EW34T34R.cjs +0 -31
  83. package/dist/seal-HMXHWFBA.js +0 -10
  84. package/dist/visual-X7OFXY4M.js +0 -21
  85. /package/dist/{-YNDBG7XO.js.map → -VEOO4BOM.js.map} +0 -0
  86. /package/dist/{chunk-4AI32TFY.js.map → chunk-3M2BIQLZ.js.map} +0 -0
  87. /package/dist/{chunk-2EVBM5NZ.js.map → chunk-4VPN5MHS.js.map} +0 -0
  88. /package/dist/{chunk-VRIHNSYF.js.map → chunk-55DOBEGD.js.map} +0 -0
  89. /package/dist/{chunk-2URKNOOQ.js.map → chunk-6YDPTTIY.js.map} +0 -0
  90. /package/dist/{chunk-4XLDFJGY.js.map → chunk-CKCJU42Y.js.map} +0 -0
  91. /package/dist/{chunk-5NBUT7GX.js.map → chunk-DBPDAG7N.js.map} +0 -0
  92. /package/dist/{chunk-QO4PT3NN.js.map → chunk-DM5YPONO.js.map} +0 -0
  93. /package/dist/{chunk-LJSKKGWU.js.map → chunk-EN3PK6OT.js.map} +0 -0
  94. /package/dist/{chunk-6BUW4PJ3.js.map → chunk-ENCAYXDI.js.map} +0 -0
  95. /package/dist/{chunk-W67KQWMU.js.map → chunk-G4HWEM6Q.js.map} +0 -0
  96. /package/dist/{chunk-MA4NIAYO.js.map → chunk-H5PDQ66I.js.map} +0 -0
  97. /package/dist/{chunk-YFCE7J37.js.map → chunk-IZYYKE5F.js.map} +0 -0
  98. /package/dist/{chunk-2CHTPRDU.js.map → chunk-O2SLKE4L.js.map} +0 -0
  99. /package/dist/{chunk-TFXWP5QI.js.map → chunk-OJISMR2Z.js.map} +0 -0
  100. /package/dist/{chunk-67DVZL4W.js.map → chunk-P35SCAJN.js.map} +0 -0
  101. /package/dist/{chunk-GZFL6UOS.js.map → chunk-P6FNIU5P.js.map} +0 -0
  102. /package/dist/{chunk-A5HW2Z5T.js.map → chunk-PX6X4YOR.js.map} +0 -0
  103. /package/dist/{chunk-XCSWST2A.js.map → chunk-SRUBDR3Z.js.map} +0 -0
  104. /package/dist/{chunk-Y3LLNEU3.js.map → chunk-XLY5ZJRP.js.map} +0 -0
  105. /package/dist/{chunk-UIZYWW3J.js.map → chunk-Z5G6QNVH.js.map} +0 -0
  106. /package/dist/{chunk-S7B74SOM.js.map → chunk-Z62PHCSU.js.map} +0 -0
  107. /package/dist/{chunk-6O2AUXFB.js.map → chunk-ZGNHJCDR.js.map} +0 -0
  108. /package/dist/{chunks-LX2EPPSD.js.map → chunks-LV5EJBZ4.js.map} +0 -0
  109. /package/dist/{formula-ZULPRKJR.js.map → formula-4GRNVWIQ.js.map} +0 -0
  110. /package/dist/{formula-JASN5ELL.js.map → formula-6Y467S4R.js.map} +0 -0
  111. /package/dist/{image-ocr-IYFAHFQH.js.map → image-ocr-CGJJCD4E.js.map} +0 -0
  112. /package/dist/{image-ocr-2ZWU6O5O.js.map → image-ocr-ZLL66KNO.js.map} +0 -0
  113. /package/dist/{model-bundle-FD5ORLT4.js.map → model-bundle-RD4KSNTB.js.map} +0 -0
  114. /package/dist/{models-CNXDF4BB.js.map → models-OLGCZBXI.js.map} +0 -0
  115. /package/dist/{parser-OQ4FLNBE.js.map → parser-IHCFHLHN.js.map} +0 -0
  116. /package/dist/{parser-WTOPH3C2.js.map → parser-J22QBXPP.js.map} +0 -0
  117. /package/dist/{pdf-ocr-IBZRT65M.js.map → pdf-ocr-H2247ZZH.js.map} +0 -0
  118. /package/dist/{pdf-ocr-V346MK77.js.map → pdf-ocr-TYIH23RP.js.map} +0 -0
  119. /package/dist/{profile-io-UFTSKMWU.js.map → profile-io-GH3TYTNJ.js.map} +0 -0
  120. /package/dist/{rasterize-SXIF5J5K.js.map → rasterize-7TFTVFMB.js.map} +0 -0
  121. /package/dist/{redact-doc-P6XQ6B6U.js.map → redact-doc-DSSQOO3A.js.map} +0 -0
  122. /package/dist/{render-QW56QMUE.js.map → render-NKXZQINI.js.map} +0 -0
  123. /package/dist/{seal-HMXHWFBA.js.map → seal-JIVSSI3Z.js.map} +0 -0
  124. /package/dist/{setup-RIZUPKAE.js.map → setup-JVLMX2TH.js.map} +0 -0
  125. /package/dist/{visual-X7OFXY4M.js.map → visual-BKVZN6HP.js.map} +0 -0
  126. /package/dist/{watch-TAHHXY7Z.js.map → watch-5RZVHAKI.js.map} +0 -0
@@ -10,14 +10,14 @@ import {
10
10
  ocrSourcePath,
11
11
  parseCharacterDict,
12
12
  resolveOcrModels
13
- } from "./chunk-A5HW2Z5T.js";
13
+ } from "./chunk-PX6X4YOR.js";
14
14
  import {
15
15
  validateOcrOptions
16
- } from "./chunk-2URKNOOQ.js";
16
+ } from "./chunk-6YDPTTIY.js";
17
17
  import {
18
18
  KordocError,
19
19
  OPTIONAL_DEP_INSTALL_HINT
20
- } from "./chunk-Y3LLNEU3.js";
20
+ } from "./chunk-XLY5ZJRP.js";
21
21
 
22
22
  // src/ocr/ruling-lines.ts
23
23
  var INK_LUMA_MAX = 205;
@@ -2212,4 +2212,4 @@ export {
2212
2212
  groupOcrLines,
2213
2213
  ocrItemsToBlocks
2214
2214
  };
2215
- //# sourceMappingURL=chunk-W67KQWMU.js.map
2215
+ //# sourceMappingURL=chunk-G4HWEM6Q.js.map
@@ -51,10 +51,10 @@ import {
51
51
  resolveParaHeading,
52
52
  simulateWrap,
53
53
  takeTocLeaders
54
- } from "./chunk-4XLDFJGY.js";
54
+ } from "./chunk-CKCJU42Y.js";
55
55
  import {
56
56
  blocksToPages
57
- } from "./chunk-LJSKKGWU.js";
57
+ } from "./chunk-EN3PK6OT.js";
58
58
  import {
59
59
  bmpToPng,
60
60
  inlineImagesIntoMarkdown
@@ -76,7 +76,7 @@ import {
76
76
  tidyScriptTags,
77
77
  unframeLayoutTables,
78
78
  wrapScript
79
- } from "./chunk-GZFL6UOS.js";
79
+ } from "./chunk-P6FNIU5P.js";
80
80
  import {
81
81
  MAX_COLS,
82
82
  MAX_ROWS,
@@ -99,7 +99,7 @@ import {
99
99
  noteTableHtml,
100
100
  sanitizeText,
101
101
  visibleText
102
- } from "./chunk-5NBUT7GX.js";
102
+ } from "./chunk-DBPDAG7N.js";
103
103
  import {
104
104
  ANNOTATION_BLANK_RE,
105
105
  ValueCursor,
@@ -132,7 +132,7 @@ import {
132
132
  resolveUnmatched,
133
133
  scanInlineSegments,
134
134
  scanSectionXml
135
- } from "./chunk-S7B74SOM.js";
135
+ } from "./chunk-Z62PHCSU.js";
136
136
  import {
137
137
  MAX_DECOMPRESS_SIZE,
138
138
  MAX_XML_DEPTH,
@@ -149,7 +149,7 @@ import {
149
149
  localName,
150
150
  rawTextContent,
151
151
  readZipEntry
152
- } from "./chunk-VRIHNSYF.js";
152
+ } from "./chunk-55DOBEGD.js";
153
153
  import {
154
154
  KordocError,
155
155
  classifyError,
@@ -165,7 +165,7 @@ import {
165
165
  toArrayBuffer,
166
166
  unescapeHtml,
167
167
  unzipLimitBytes
168
- } from "./chunk-UIZYWW3J.js";
168
+ } from "./chunk-Z5G6QNVH.js";
169
169
 
170
170
  // src/table/visual.ts
171
171
  import { readFile as readFile2 } from "fs/promises";
@@ -19329,7 +19329,7 @@ var MIN_SIDE = 24;
19329
19329
  async function ocrEmbeddedImages(blocks, options, warnings) {
19330
19330
  const ocr = options?.ocr;
19331
19331
  if (ocr !== true && ocr !== "force" && typeof ocr !== "function") return false;
19332
- const { parseImageDocument } = await import("./image-ocr-IYFAHFQH.js");
19332
+ const { parseImageDocument } = await import("./image-ocr-CGJJCD4E.js");
19333
19333
  let inserted = false, unreadable = 0;
19334
19334
  let failed = null;
19335
19335
  const walk = async (list, depth) => {
@@ -27216,27 +27216,35 @@ function gridFromHtml(html) {
27216
27216
  return { rowCnt: rows.length, colCnt, cells };
27217
27217
  }
27218
27218
  function splitContact(line) {
27219
- if (/[||]/.test(line)) return line.split(/\s*[||]\s*/).map((s) => s.trim()).filter(Boolean);
27220
27219
  const out = [];
27221
27220
  let rest = line.trim();
27222
27221
  const d = /^\(?\d{4}\.\s*\d{1,2}\.\s*\d{1,2}\.?(?:\s*\([^)]{0,20}\))?\)?[,,]?/u.exec(rest);
27223
27222
  if (d) {
27224
27223
  out.push(d[0].replace(/[,,]$/, ""));
27225
- rest = rest.slice(d[0].length).trim();
27224
+ rest = rest.slice(d[0].length).replace(/^\s*[||]?\s*/, "").trim();
27226
27225
  }
27226
+ if (/[||]/.test(rest)) return [...out, ...rest.split(/\s*[||]\s*/).map((s) => s.trim()).filter(Boolean)];
27227
27227
  out.push(...rest.split(/(?<=☎\s{0,3}[\d-]{1,20})[\s,,]+/u).map((s) => s.trim()).filter(Boolean));
27228
27228
  return out;
27229
27229
  }
27230
27230
  function fillCells(xml, edits) {
27231
- return applySplices(xml, edits.filter((e) => e.cell.paras.length).map(({ cell: cell2, lines, post }) => {
27231
+ return applySplices(xml, edits.filter((e) => e.cell.paras.length).map(({ cell: cell2, lines, post, raw }) => {
27232
27232
  const first = xml.slice(cell2.paras[0].start, cell2.paras[0].end);
27233
+ const direct = raw?.(first);
27233
27234
  return {
27234
27235
  start: cell2.paras[0].start,
27235
27236
  end: cell2.paras[cell2.paras.length - 1].end,
27236
- replacement: (lines.length ? lines : [""]).map((l) => post ? post(fillParagraph(first, l)) : fillParagraph(first, l)).join("")
27237
+ replacement: direct ?? (lines.length ? lines : [""]).map((l) => post ? post(fillParagraph(first, l)) : fillParagraph(first, l)).join("")
27237
27238
  };
27238
27239
  }));
27239
27240
  }
27241
+ function replaceCellValue(pXml, oldValue, newValue) {
27242
+ const needle = escapeTextXml(oldValue);
27243
+ const at = pXml.indexOf(needle);
27244
+ if (!oldValue || at < 0 || pXml.indexOf(needle, at + 1) >= 0) return null;
27245
+ if (pXml.lastIndexOf("<", at) > pXml.lastIndexOf(">", at)) return null;
27246
+ return pXml.slice(0, at) + bodyText(newValue) + pXml.slice(at + needle.length);
27247
+ }
27240
27248
  function defaultPara(text) {
27241
27249
  return `<hp:p id="0" paraPrIDRef="0" styleIDRef="0" pageBreak="0" columnBreak="0" merged="0"><hp:run charPrIDRef="0">${text ? `<hp:t>${bodyText(text)}</hp:t>` : ""}</hp:run></hp:p>`;
27242
27250
  }
@@ -27399,11 +27407,38 @@ function titleXml(ctx, title, contact) {
27399
27407
  }
27400
27408
  function assignContacts(ctx, slots, contact) {
27401
27409
  const texts = matchContactPieces(slots.map((c) => c.text), contact);
27402
- return slots.map((cell2, i) => ({ cell: cell2, lines: [texts[i]], post: (p) => undistribute(ctx, p) }));
27410
+ const kept = slots.filter((c, i) => contact && texts[i] && texts[i] === c.text).map((c) => c.text.trim());
27411
+ if (kept.length) ctx.warnings.push(`template: \uC6D0\uACE0\uC5D0 \uC5C6\uB294 \uB2F4\uB2F9 \uC904 \uCE78\uC740 \uACAC\uBCF8 \uADF8\uB300\uB85C \uB460 \u2014 ${kept.join(", ")}`);
27412
+ if (slots.length === 1) return [{ cell: slots[0], lines: [texts[0]], post: (p) => undistribute(ctx, p) }];
27413
+ return slots.flatMap((cell2, i) => {
27414
+ const next = texts[i];
27415
+ if (next === cell2.text) return [];
27416
+ const before = LABELLED_RE.exec(cell2.text.trim()), after = LABELLED_RE.exec(next.trim());
27417
+ const swap = before && after && before[1].trim() === after[1].trim() ? [before[3], after[3]] : !before && !after && DATE_IN_RE.test(cell2.text) && next ? [cell2.text.trim(), next.trim()] : null;
27418
+ return [{ cell: cell2, lines: [next], ...swap ? { raw: (first) => replaceCellValue(first, swap[0], swap[1]) } : {} }];
27419
+ });
27420
+ }
27421
+ var LABELLED_RE = /^(.+?)([\s ]*[::][\s ]*)(\S.*)$/u;
27422
+ function parseContactPiece(piece) {
27423
+ const m = LABELLED_RE.exec(piece.trim());
27424
+ if (m) {
27425
+ const kw = ROLE_KW_RE.exec(m[1])?.[1];
27426
+ return kw ? { kw, label: m[1].trim(), value: m[3].trim() } : null;
27427
+ }
27428
+ const k = ROLE_KW_RE.exec(piece);
27429
+ if (!k) return null;
27430
+ const end = (k.index ?? 0) + k[1].length;
27431
+ const value = piece.slice(end).trim();
27432
+ return value ? { kw: k[1], label: piece.slice(0, end).trim(), value } : null;
27403
27433
  }
27434
+ var STAFF_KW = /* @__PURE__ */ new Set(["\uB2F4\uB2F9", "\uC8FC\uBB34\uAD00"]);
27404
27435
  function matchContactPieces(slotTexts, contact) {
27405
27436
  const out = slotTexts.map(() => "");
27406
27437
  if (!contact || !slotTexts.length) return out;
27438
+ if (slotTexts.length > 1) {
27439
+ const done = matchRoleSlots(slotTexts, splitContact(contact));
27440
+ if (done) return done;
27441
+ }
27407
27442
  const pieces = slotTexts.length === 1 ? [splitContact(contact).join(" ")] : splitContact(contact);
27408
27443
  const free = slotTexts.map((_, i) => i);
27409
27444
  const take = (pred) => {
@@ -27424,6 +27459,28 @@ function matchContactPieces(slotTexts, contact) {
27424
27459
  }
27425
27460
  return out;
27426
27461
  }
27462
+ function matchRoleSlots(slotTexts, pieces) {
27463
+ const slots = slotTexts.map((t) => {
27464
+ const date = DATE_IN_RE.test(t) && !ROLE_KW_RE.test(t);
27465
+ const m = LABELLED_RE.exec(t.trim());
27466
+ return { date, kw: m ? ROLE_KW_RE.exec(m[1])?.[1] ?? null : null, label: m?.[1].trim() ?? "", sep: m?.[2] ?? " : " };
27467
+ });
27468
+ if (slots.some((s) => !s.date && !s.kw)) return null;
27469
+ const out = slotTexts.slice();
27470
+ for (const piece of pieces) {
27471
+ if (DATE_RE.test(piece)) {
27472
+ const i2 = slots.findIndex((s) => s.date);
27473
+ if (i2 >= 0) out[i2] = piece;
27474
+ continue;
27475
+ }
27476
+ const entry = parseContactPiece(piece);
27477
+ if (!entry) return null;
27478
+ const i = slots.findIndex((s) => s.kw === entry.kw);
27479
+ if (i < 0) return null;
27480
+ out[i] = `${STAFF_KW.has(entry.kw) ? slots[i].label : entry.label}${slots[i].sep}${entry.value}`;
27481
+ }
27482
+ return out;
27483
+ }
27427
27484
  function contactXml(ctx, contact) {
27428
27485
  const proto = ctx.scan.protos.get("contact");
27429
27486
  if (!proto) return bodyPara(ctx, contact);
@@ -28548,10 +28605,13 @@ var RULES = [
28548
28605
  // 숫자 런 첫머리에서만 시작 — 아니면 긴 숫자 줄에서 시작 자리마다 런을 다시 훑어 이차 폭주(숫자 5만 자 2.9초)
28549
28606
  // "359,600천원"·"1,200천원" 처럼 쉼표를 둔 수 뒤 천원은 예산서 단위 표기(단위: 천원)라 줄임말이 아니다 —
28550
28607
  // 쉼표 묶음 뒤(앞이 숫자·쉼표)는 건너뛴다. 걸리는 것은 "3천원"·"1500천원" 같은 쉼표 없는 수
28608
+ // 표 칸("금액(천원)" 열)·산출근거 곱셈 줄("× … = 900천원")의 천원도 예산서 단위 표기다 — 건너뛴다
28551
28609
  {
28552
28610
  code: "MONEY_CHEONWON",
28553
28611
  severity: "error",
28554
28612
  pattern: /(?<![\d,])\d+\s*천\s*원/g,
28613
+ skipTable: true,
28614
+ check: (m) => /[×✕]|\s=\s/.test(m.input ?? "") ? null : "",
28555
28615
  message: "\uAE08\uC561\uC740 '\uCC9C\uC6D0'\uC73C\uB85C \uC904\uC774\uC9C0 \uC54A\uACE0 \uC544\uB77C\uBE44\uC544 \uC22B\uC790\uB85C",
28556
28616
  suggest: "\uC608) 345,000\uC6D0"
28557
28617
  },
@@ -29526,7 +29586,8 @@ function checkCalc(line, no, out) {
29526
29586
  rule: "BUDGET_CALC_MISMATCH",
29527
29587
  severity: "error",
29528
29588
  message: "\uC0B0\uCD9C\uADFC\uAC70 \uACF1\uC148 \uACB0\uACFC\uAC00 \uC801\uC740 \uAE08\uC561\uACFC \uB2E4\uB984",
29529
- suggest: `${m[1].trim()} = ${fmt(expected)}${m[3]}`
29589
+ // "원래 식 → 바른 식" — 그대로 바꿔 넣을 수 있는 꼴(편집 도구가 바로 반영)
29590
+ suggest: `${m[0]} \u2192 ${m[1].trim()} = ${fmt(expected)}${m[3]}`
29530
29591
  });
29531
29592
  }
29532
29593
  }
@@ -29836,6 +29897,104 @@ function runLintSuite(text, opts = {}) {
29836
29897
  };
29837
29898
  }
29838
29899
 
29900
+ // src/hwpx/lint-blocks.ts
29901
+ var MARKDOWN_STRUCTURE = /^\s*(?:```|~~~|#|\||>)/;
29902
+ var safeLine = (text) => text.replace(/[\r\n]/g, " ");
29903
+ var safeCell = (text) => safeLine(text).replace(/\|/g, "\uFF5C");
29904
+ function blocksToLines(blocks) {
29905
+ const lines = [];
29906
+ const paragraph = (block) => {
29907
+ const text = safeLine(block.text);
29908
+ const prefix = MARKDOWN_STRUCTURE.test(text) ? "\\" : "";
29909
+ lines.push({ text: prefix + text, segments: [{ start: prefix.length, length: text.length, block: block.id }] });
29910
+ };
29911
+ const tables = /* @__PURE__ */ new Map();
29912
+ for (const b of blocks) if (b.table) (tables.get(b.table.id) ?? tables.set(b.table.id, []).get(b.table.id)).push(b);
29913
+ const emitted = /* @__PURE__ */ new Set();
29914
+ for (const b of blocks) {
29915
+ if (!b.table) {
29916
+ paragraph(b);
29917
+ continue;
29918
+ }
29919
+ if (emitted.has(b.table.id)) continue;
29920
+ emitted.add(b.table.id);
29921
+ const cells = tables.get(b.table.id);
29922
+ if (cells.length === 1) {
29923
+ paragraph(cells[0]);
29924
+ continue;
29925
+ }
29926
+ const rows = cells.reduce((n, c) => Math.max(n, c.table.row + 1), 0);
29927
+ const cols = cells.reduce((n, c) => Math.max(n, c.table.col + 1), 0);
29928
+ const at = new Map(cells.map((c) => [`${c.table.row},${c.table.col}`, c]));
29929
+ lines.push({ text: "", segments: [] });
29930
+ for (let r = 0; r < rows; r++) {
29931
+ let text = "|";
29932
+ const segments = [];
29933
+ for (let c = 0; c < cols; c++) {
29934
+ text += " ";
29935
+ const cell2 = at.get(`${r},${c}`);
29936
+ if (cell2) {
29937
+ const t = safeCell(cell2.text);
29938
+ segments.push({ start: text.length, length: t.length, block: cell2.id });
29939
+ text += t;
29940
+ }
29941
+ text += " |";
29942
+ }
29943
+ lines.push({ text, segments });
29944
+ if (r === 0) lines.push({ text: "|" + " --- |".repeat(cols), segments: [] });
29945
+ }
29946
+ lines.push({ text: "", segments: [] });
29947
+ }
29948
+ return lines;
29949
+ }
29950
+ function locate(lines, f) {
29951
+ const line = lines[f.line - 1];
29952
+ if (!line) return { block: null, offset: null };
29953
+ if (f.column !== void 0) {
29954
+ const at = f.column - 1;
29955
+ const seg2 = line.segments.find((s) => at >= s.start && at < s.start + Math.max(1, s.length)) ?? line.segments.find((s) => line.text.indexOf(f.match, s.start) >= s.start && line.text.indexOf(f.match, s.start) < s.start + s.length);
29956
+ if (seg2) {
29957
+ const found = line.text.indexOf(f.match, Math.max(seg2.start, at));
29958
+ const start = found >= seg2.start && found < seg2.start + seg2.length ? found : at;
29959
+ return { block: seg2.block, offset: Math.min(Math.max(0, start - seg2.start), seg2.length) };
29960
+ }
29961
+ return { block: null, offset: null };
29962
+ }
29963
+ if (line.segments.length !== 1) return { block: null, offset: null };
29964
+ const seg = line.segments[0];
29965
+ const body = line.text.slice(seg.start, seg.start + seg.length);
29966
+ const first = f.match ? body.indexOf(f.match) : -1;
29967
+ const unique = first >= 0 && body.indexOf(f.match, first + 1) < 0;
29968
+ return { block: seg.block, offset: unique ? first : null };
29969
+ }
29970
+ function lintBlocks(blocks, opts = {}) {
29971
+ const lines = blocksToLines(blocks);
29972
+ const suite = runLintSuite(lines.map((l) => l.text).join("\n") + "\n", opts);
29973
+ const findings = [];
29974
+ const take = (category, list) => {
29975
+ for (const f of list ?? []) {
29976
+ const { block, offset } = locate(lines, f);
29977
+ findings.push({
29978
+ block,
29979
+ offset,
29980
+ length: f.match.length,
29981
+ category,
29982
+ rule: f.rule,
29983
+ severity: f.severity,
29984
+ match: f.match,
29985
+ message: f.message,
29986
+ ...f.suggest ? { suggest: f.suggest } : {}
29987
+ });
29988
+ }
29989
+ };
29990
+ take("notation", suite.findings);
29991
+ take("munche", suite.munche);
29992
+ take("slop", suite.slop);
29993
+ take("logic", suite.logic);
29994
+ take("budget", suite.budget);
29995
+ return { findings, ...suite.numbers ? { numbers: suite.numbers } : {}, summary: suite.summary };
29996
+ }
29997
+
29839
29998
  // src/roundtrip/patcher.ts
29840
29999
  import JSZip11 from "jszip";
29841
30000
 
@@ -32880,7 +33039,7 @@ function scriptsOff(r, off) {
32880
33039
  }
32881
33040
  async function parseImage(buffer, options) {
32882
33041
  try {
32883
- const { parseImageDocument } = await import("./image-ocr-IYFAHFQH.js");
33042
+ const { parseImageDocument } = await import("./image-ocr-CGJJCD4E.js");
32884
33043
  const { blocks, warnings, lines } = await parseImageDocument(buffer, options);
32885
33044
  stripScriptTags({ blocks });
32886
33045
  return {
@@ -32944,7 +33103,7 @@ async function parseHwp(buffer, options) {
32944
33103
  async function parsePdf(buffer, options) {
32945
33104
  let parsePdfDocument;
32946
33105
  try {
32947
- const mod = await import("./parser-OQ4FLNBE.js");
33106
+ const mod = await import("./parser-IHCFHLHN.js");
32948
33107
  parsePdfDocument = mod.parsePdfDocument;
32949
33108
  } catch {
32950
33109
  return {
@@ -33153,6 +33312,7 @@ export {
33153
33312
  formatGateFinding,
33154
33313
  lintBudgetText,
33155
33314
  runLintSuite,
33315
+ lintBlocks,
33156
33316
  replaceOleStream,
33157
33317
  wipeFreeSectors,
33158
33318
  patchHwp,
@@ -33176,4 +33336,4 @@ export {
33176
33336
  parseHwpml,
33177
33337
  fillForm
33178
33338
  };
33179
- //# sourceMappingURL=chunk-MSUBZT53.js.map
33339
+ //# sourceMappingURL=chunk-GEY6ZMXH.js.map