kordoc 3.12.0 → 3.15.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 (48) hide show
  1. package/README.md +21 -1
  2. package/dist/{-OPZIF3QT.js → -EYPKRLMN.js} +13 -7
  3. package/dist/{chunk-ILR7GF3W.js → chunk-734GJLXH.js} +522 -135
  4. package/dist/chunk-734GJLXH.js.map +1 -0
  5. package/dist/chunk-DCZVOIEO.cjs.map +1 -1
  6. package/dist/{chunk-2C5GCXPJ.cjs → chunk-DMBTOJK4.cjs} +5 -5
  7. package/dist/chunk-DMBTOJK4.cjs.map +1 -0
  8. package/dist/chunk-GS7T56RP.cjs.map +1 -1
  9. package/dist/{chunk-RUYSNNYZ.js → chunk-Q3IHI3E6.js} +325 -29
  10. package/dist/chunk-Q3IHI3E6.js.map +1 -0
  11. package/dist/{chunk-BUDM6ULT.js → chunk-Q5ZPWQID.js} +5 -5
  12. package/dist/{chunk-BUDM6ULT.js.map → chunk-Q5ZPWQID.js.map} +1 -1
  13. package/dist/{chunk-7Z43WEML.js → chunk-SCFFABCT.js} +2 -2
  14. package/dist/{chunk-NGKIAZ6G.js → chunk-ZRNYYEE6.js} +5 -5
  15. package/dist/{chunk-NGKIAZ6G.js.map → chunk-ZRNYYEE6.js.map} +1 -1
  16. package/dist/cli.js +71 -10
  17. package/dist/cli.js.map +1 -1
  18. package/dist/formula-5NKVS2LR.cjs.map +1 -1
  19. package/dist/index.cjs +1018 -335
  20. package/dist/index.cjs.map +1 -1
  21. package/dist/index.d.cts +80 -6
  22. package/dist/index.d.ts +80 -6
  23. package/dist/index.js +897 -214
  24. package/dist/index.js.map +1 -1
  25. package/dist/mcp.js +33 -10
  26. package/dist/mcp.js.map +1 -1
  27. package/dist/page-range-P7SDW6LR.cjs.map +1 -1
  28. package/dist/{parser-5FSEFR3R.js → parser-76JSTEP3.js} +107 -7
  29. package/dist/parser-76JSTEP3.js.map +1 -0
  30. package/dist/{parser-PCTX7LGN.cjs → parser-O2SD4DFR.cjs} +118 -18
  31. package/dist/parser-O2SD4DFR.cjs.map +1 -0
  32. package/dist/{parser-AWEGMHHV.js → parser-SNBQZO5E.js} +106 -6
  33. package/dist/parser-SNBQZO5E.js.map +1 -0
  34. package/dist/provider-G4C2V2PD.cjs.map +1 -1
  35. package/dist/render-LDI3LO2P.js +9 -0
  36. package/dist/{watch-Y7BAYXJE.js → watch-LEK5U5P6.js} +5 -5
  37. package/package.json +4 -3
  38. package/dist/chunk-2C5GCXPJ.cjs.map +0 -1
  39. package/dist/chunk-ILR7GF3W.js.map +0 -1
  40. package/dist/chunk-RUYSNNYZ.js.map +0 -1
  41. package/dist/parser-5FSEFR3R.js.map +0 -1
  42. package/dist/parser-AWEGMHHV.js.map +0 -1
  43. package/dist/parser-PCTX7LGN.cjs.map +0 -1
  44. package/dist/render-ATQNKOT2.js +0 -9
  45. /package/dist/{-OPZIF3QT.js.map → -EYPKRLMN.js.map} +0 -0
  46. /package/dist/{chunk-7Z43WEML.js.map → chunk-SCFFABCT.js.map} +0 -0
  47. /package/dist/{render-ATQNKOT2.js.map → render-LDI3LO2P.js.map} +0 -0
  48. /package/dist/{watch-Y7BAYXJE.js.map → watch-LEK5U5P6.js.map} +0 -0
@@ -18,7 +18,7 @@ import {
18
18
  extractTextFromNode,
19
19
  findChildByLocalName,
20
20
  fitRatioForFewerLines
21
- } from "./chunk-ILR7GF3W.js";
21
+ } from "./chunk-734GJLXH.js";
22
22
  import {
23
23
  HEADING_RATIO_H1,
24
24
  HEADING_RATIO_H2,
@@ -30,7 +30,7 @@ import {
30
30
  convertTableToText,
31
31
  flattenLayoutTables,
32
32
  mapPuaText
33
- } from "./chunk-BUDM6ULT.js";
33
+ } from "./chunk-Q5ZPWQID.js";
34
34
  import {
35
35
  parsePageRange
36
36
  } from "./chunk-MOL7MDBG.js";
@@ -44,7 +44,7 @@ import {
44
44
  sanitizeHref,
45
45
  stripDtd,
46
46
  toArrayBuffer
47
- } from "./chunk-7Z43WEML.js";
47
+ } from "./chunk-SCFFABCT.js";
48
48
 
49
49
  // src/index.ts
50
50
  import { readFile } from "fs/promises";
@@ -640,10 +640,22 @@ function findEnclosingBrackets(eqString, startIdx) {
640
640
  }
641
641
  return null;
642
642
  }
643
+ function maskLiteralSpans(eqString) {
644
+ return eqString.replace(/"[^"]*"/g, (m) => "\uFFFF".repeat(m.length)).replace(/\\text\{[^}]*\}/g, (m) => "\uFFFF".repeat(m.length));
645
+ }
646
+ function findKeywordToken(eqString, word, from = 0) {
647
+ const masked = maskLiteralSpans(eqString);
648
+ for (let i = masked.indexOf(word, from); i !== -1; i = masked.indexOf(word, i + 1)) {
649
+ const okL = i === 0 || masked[i - 1] === " ";
650
+ const okR = i + word.length === masked.length || masked[i + word.length] === " ";
651
+ if (okL && okR) return i;
652
+ }
653
+ return -1;
654
+ }
643
655
  function replaceFrac(eqString) {
644
656
  const hmlFrac = "over";
645
657
  while (true) {
646
- const cursor = eqString.indexOf(hmlFrac);
658
+ const cursor = findKeywordToken(eqString, hmlFrac);
647
659
  if (cursor === -1) break;
648
660
  try {
649
661
  const [numStart, numEnd] = findBrackets(eqString, cursor, 0);
@@ -659,12 +671,12 @@ function replaceFrac(eqString) {
659
671
  }
660
672
  function replaceRootOf(eqString) {
661
673
  while (true) {
662
- const rootCursor = eqString.indexOf("root");
674
+ const rootCursor = findKeywordToken(eqString, "root");
663
675
  if (rootCursor === -1) break;
664
676
  try {
665
- const ofCursor = eqString.indexOf("of");
666
- if (ofCursor === -1) return eqString;
667
677
  const elem1 = findBrackets(eqString, rootCursor, 1);
678
+ const ofCursor = findKeywordToken(eqString, "of", elem1[1]);
679
+ if (ofCursor === -1) return eqString;
668
680
  const elem2 = findBrackets(eqString, ofCursor, 1);
669
681
  const e1 = eqString.slice(elem1[0] + 1, elem1[1] - 1);
670
682
  const e2 = eqString.slice(elem2[0] + 1, elem2[1] - 1);
@@ -2082,7 +2094,6 @@ function appendParaText(state, data, resolveControl) {
2082
2094
  break;
2083
2095
  }
2084
2096
  result += "\n";
2085
- if (i + 14 <= data.length) i += 14;
2086
2097
  break;
2087
2098
  }
2088
2099
  case CHAR_PARA:
@@ -17955,6 +17966,7 @@ function collectOmmlRoots(p) {
17955
17966
  const tag = el.localName || el.tagName?.replace(/^[^:]+:/, "") || "";
17956
17967
  if (tag === "oMath" || tag === "oMathPara") {
17957
17968
  out.push(el);
17969
+ } else if (tag === "txbxContent" || tag === "Fallback") {
17958
17970
  } else {
17959
17971
  walk(el);
17960
17972
  }
@@ -18079,6 +18091,13 @@ function parseTable(tbl, styles, numbering, footnotes, rels) {
18079
18091
  for (const tr of trElements) {
18080
18092
  const row = [];
18081
18093
  let col = 0;
18094
+ const trPrEls = getChildElements(tr, "trPr");
18095
+ if (trPrEls.length > 0) {
18096
+ const gridBeforeEls = getChildElements(trPrEls[0], "gridBefore");
18097
+ if (gridBeforeEls.length > 0) {
18098
+ col = parseInt(getAttr(gridBeforeEls[0], "val") ?? "0", 10) || 0;
18099
+ }
18100
+ }
18082
18101
  for (const tc of getChildElements(tr, "tc")) {
18083
18102
  let colSpan = 1;
18084
18103
  let vMerge = null;
@@ -18093,12 +18112,28 @@ function parseTable(tbl, styles, numbering, footnotes, rels) {
18093
18112
  vMerge = getAttr(vMergeEls[0], "val") === "restart" ? "restart" : "continue";
18094
18113
  }
18095
18114
  }
18096
- const text = vMerge === "continue" ? "" : collectCellText(tc, styles, numbering, footnotes, rels, 0).join("\n");
18115
+ const text = collectCellText(tc, styles, numbering, footnotes, rels, 0).join("\n");
18097
18116
  row.push({ col, colSpan, vMerge, text });
18098
18117
  col += colSpan;
18099
18118
  }
18100
18119
  rawRows.push(row);
18101
18120
  }
18121
+ for (let r = 0; r < rawRows.length; r++) {
18122
+ for (const cell of rawRows[r]) {
18123
+ if (cell.vMerge !== "continue" || !cell.text) continue;
18124
+ let start;
18125
+ for (let pr = r - 1; pr >= 0 && !start; pr--) {
18126
+ start = rawRows[pr].find((pc) => pc.col === cell.col && pc.vMerge !== "continue");
18127
+ }
18128
+ if (start) {
18129
+ start.text = start.text ? `${start.text}
18130
+ ${cell.text}` : cell.text;
18131
+ cell.text = "";
18132
+ } else {
18133
+ cell.vMerge = null;
18134
+ }
18135
+ }
18136
+ }
18102
18137
  const cellRows = rawRows.map(
18103
18138
  (row, r) => row.filter((cell) => cell.vMerge !== "continue").map((cell) => {
18104
18139
  let rowSpan = 1;
@@ -18497,7 +18532,23 @@ function parseTable2(el, blocks, paraShapeMap, sectionNum, warnings) {
18497
18532
  (row) => row.map((cell) => cell ?? { text: "", colSpan: 1, rowSpan: 1 })
18498
18533
  );
18499
18534
  const table = buildTable(cellRows);
18535
+ const caption = extractShapeCaption(el);
18536
+ if (caption.text && caption.before) {
18537
+ blocks.push({ type: "paragraph", text: caption.text, pageNumber: sectionNum });
18538
+ }
18500
18539
  blocks.push({ type: "table", table, pageNumber: sectionNum });
18540
+ if (caption.text && !caption.before) {
18541
+ blocks.push({ type: "paragraph", text: caption.text, pageNumber: sectionNum });
18542
+ }
18543
+ }
18544
+ function extractShapeCaption(tableEl) {
18545
+ const shape = findChild(tableEl, "SHAPEOBJECT");
18546
+ const caption = shape && findChild(shape, "CAPTION");
18547
+ if (!caption) return { text: "", before: false };
18548
+ const parts = [];
18549
+ collectCellText2(caption, parts, 0);
18550
+ const side = caption.getAttribute("Side") ?? "";
18551
+ return { text: parts.filter(Boolean).join("\n").trim(), before: side === "Top" || side === "Left" };
18501
18552
  }
18502
18553
  function extractCellText(cellEl) {
18503
18554
  const textParts = [];
@@ -18572,6 +18623,81 @@ function countSections(body) {
18572
18623
  }
18573
18624
 
18574
18625
  // src/form/match.ts
18626
+ function parseYMD(v) {
18627
+ const d = v.replace(/\D/g, "");
18628
+ let y, m, day;
18629
+ if (d.length >= 8) {
18630
+ y = d.slice(0, 4);
18631
+ m = d.slice(4, 6);
18632
+ day = d.slice(6, 8);
18633
+ } else if (d.length === 6) {
18634
+ const yy = +d.slice(0, 2);
18635
+ y = String(yy <= 29 ? 2e3 + yy : 1900 + yy);
18636
+ m = d.slice(2, 4);
18637
+ day = d.slice(4, 6);
18638
+ } else return null;
18639
+ return { y, yy: y.slice(2), m, d: day };
18640
+ }
18641
+ function fmtDate(v, style) {
18642
+ const p = parseYMD(v);
18643
+ if (!p) return v;
18644
+ return (style || "yyyy-mm-dd").replace(/yyyy/g, p.y).replace(/yy/g, p.yy).replace(/mm/g, p.m).replace(/dd/g, p.d).replace(/m/g, String(+p.m)).replace(/d/g, String(+p.d));
18645
+ }
18646
+ function maskDigits(v, pattern) {
18647
+ const ds = v.replace(/\D/g, "");
18648
+ let i = 0;
18649
+ return pattern.replace(/#/g, () => ds[i++] ?? "");
18650
+ }
18651
+ function fmtPhone(v, style) {
18652
+ const d = v.replace(/\D/g, "");
18653
+ if (d.length < 9) return v;
18654
+ const a = d.slice(0, 3), b = d.slice(3, -4), c = d.slice(-4);
18655
+ switch (style) {
18656
+ case "digits":
18657
+ return d;
18658
+ case "dot":
18659
+ return `${a}.${b}.${c}`;
18660
+ case "space":
18661
+ return `${a} ${b} ${c}`;
18662
+ case "intl":
18663
+ return `+82-${d.slice(1, 3)}-${b}-${c}`;
18664
+ case "intl-paren":
18665
+ return `82)${d.slice(1, 3)}-${b}-${c}`;
18666
+ default:
18667
+ return `${a}-${b}-${c}`;
18668
+ }
18669
+ }
18670
+ function fmtRRN(v, style) {
18671
+ const d = v.replace(/\D/g, "");
18672
+ if (d.length !== 13) return v;
18673
+ switch (style) {
18674
+ case "digits":
18675
+ return d;
18676
+ case "front":
18677
+ return d.slice(0, 6);
18678
+ case "masked":
18679
+ return `${d.slice(0, 6)}-${d[6]}******`;
18680
+ default:
18681
+ return `${d.slice(0, 6)}-${d.slice(6)}`;
18682
+ }
18683
+ }
18684
+ function formatFillValue(value, format) {
18685
+ if (!format) return value;
18686
+ const ci = format.indexOf(":");
18687
+ const kind = ci >= 0 ? format.slice(0, ci) : format;
18688
+ const style = ci >= 0 ? format.slice(ci + 1) : "";
18689
+ if (kind === "date") return fmtDate(value, style);
18690
+ if (kind === "phone") return fmtPhone(value, style);
18691
+ if (kind === "rrn") return fmtRRN(value, style);
18692
+ if (kind === "mask") return maskDigits(value, style);
18693
+ if (kind === "digits") return value.replace(/\D/g, "");
18694
+ if (kind === "upper") return value.toUpperCase();
18695
+ if (kind === "lower") return value.toLowerCase();
18696
+ if (kind === "nospace") return value.replace(/\s+/g, "");
18697
+ if (format.includes("#")) return maskDigits(value, format);
18698
+ if (/(yyyy|yy|mm|dd)/.test(format)) return fmtDate(value, format);
18699
+ return value;
18700
+ }
18575
18701
  var ValueCursor = class {
18576
18702
  constructor(values) {
18577
18703
  this.values = values;
@@ -18723,11 +18849,32 @@ function padInsertion(text, pos, value) {
18723
18849
  }
18724
18850
  function normalizeValues(values) {
18725
18851
  const map = /* @__PURE__ */ new Map();
18726
- for (const [label, value] of Object.entries(values)) {
18727
- map.set(normalizeLabel(label), value);
18852
+ for (const [label, raw] of Object.entries(values)) {
18853
+ const { value, format } = typeof raw === "object" && !Array.isArray(raw) ? raw : { value: raw, format: void 0 };
18854
+ map.set(normalizeLabel(label), Array.isArray(value) ? value.map((v) => formatFillValue(v, format)) : formatFillValue(value, format));
18728
18855
  }
18729
18856
  return map;
18730
18857
  }
18858
+ async function fillWithUniqueGuard(values, run) {
18859
+ const first = await run(values);
18860
+ const counts = /* @__PURE__ */ new Map();
18861
+ for (const f of first.filled) {
18862
+ if (f.key) counts.set(f.key, (counts.get(f.key) ?? 0) + 1);
18863
+ }
18864
+ const isArrayValue = (normKey) => {
18865
+ for (const [label, raw] of Object.entries(values)) {
18866
+ if (normalizeLabel(label) !== normKey) continue;
18867
+ return Array.isArray(typeof raw === "object" && !Array.isArray(raw) ? raw.value : raw);
18868
+ }
18869
+ return false;
18870
+ };
18871
+ const dup = new Set([...counts].filter(([k, n]) => n >= 2 && !isArrayValue(k)).map(([k]) => k));
18872
+ if (dup.size === 0) return { ...first, rejected: [] };
18873
+ const filtered = Object.fromEntries(Object.entries(values).filter(([label]) => !dup.has(normalizeLabel(label))));
18874
+ const second = await run(filtered);
18875
+ const rejected = Object.keys(values).filter((label) => dup.has(normalizeLabel(label)));
18876
+ return { ...second, rejected };
18877
+ }
18731
18878
  function resolveUnmatched(normalizedValues, matchedLabels, originalValues) {
18732
18879
  return [...normalizedValues.keys()].filter((k) => !matchedLabels.has(k)).map((k) => {
18733
18880
  for (const orig of Object.keys(originalValues)) {
@@ -18997,7 +19144,7 @@ function fillFormFields(blocks, values) {
18997
19144
  cell.text = result.text;
18998
19145
  patternFilledCells.add(cell);
18999
19146
  for (const m of result.matches) {
19000
- filled.push({ label: m.label, value: m.value, row: r, col: c });
19147
+ filled.push({ label: m.label, value: m.value, row: r, col: c, key: m.key });
19001
19148
  }
19002
19149
  }
19003
19150
  }
@@ -19077,7 +19224,8 @@ function fillTable(table, values, filled, matchedLabels, patternFilledCells) {
19077
19224
  label: labelCell.text.trim().replace(/[::]\s*$/, ""),
19078
19225
  value: newValue,
19079
19226
  row: r,
19080
- col: c
19227
+ col: c,
19228
+ key: matchKey
19081
19229
  });
19082
19230
  }
19083
19231
  }
@@ -19106,7 +19254,8 @@ function fillTable(table, values, filled, matchedLabels, patternFilledCells) {
19106
19254
  label: headerCell.text.trim(),
19107
19255
  value: newValue,
19108
19256
  row: r,
19109
- col: c
19257
+ col: c,
19258
+ key: matchKey
19110
19259
  });
19111
19260
  }
19112
19261
  }
@@ -19123,7 +19272,7 @@ function fillInlineFields(text, values, filled, matchedLabels) {
19123
19272
  const newValue = values.consume(matchKey);
19124
19273
  if (newValue === void 0) continue;
19125
19274
  matchedLabels.add(matchKey);
19126
- filled.push({ label: seg.label.trim(), value: newValue, row: -1, col: -1 });
19275
+ filled.push({ label: seg.label.trim(), value: newValue, row: -1, col: -1, key: matchKey });
19127
19276
  out += text.slice(pos, seg.valueStart);
19128
19277
  out += seg.valueStart === seg.valueEnd ? padInsertion(text, seg.valueStart, newValue) : newValue;
19129
19278
  pos = seg.valueEnd;
@@ -19960,7 +20109,8 @@ async function fillHwpx(hwpxBuffer, values) {
19960
20109
  label: cellLabelText(headerCells[colIdx]).trim(),
19961
20110
  value: newValue,
19962
20111
  row: rowIdx,
19963
- col: colIdx
20112
+ col: colIdx,
20113
+ key: matchKey
19964
20114
  });
19965
20115
  }
19966
20116
  }
@@ -21471,6 +21621,9 @@ function readCommand(input, idx, depth) {
21471
21621
  const body = readGroupOrToken(input, name.next, depth);
21472
21622
  return { value: `${ACCENT_COMMANDS[command]}{${body.value}}`, next: body.next };
21473
21623
  }
21624
+ if (command === ",") return { value: "`", next: name.next };
21625
+ if (command === ";" || command === ":") return { value: "~", next: name.next };
21626
+ if (command === "!") return { value: "", next: name.next };
21474
21627
  if (command === "mathrm" || command === "text") {
21475
21628
  const start = skipSpaces(input, name.next);
21476
21629
  if (input[start] === "{") {
@@ -23473,10 +23626,11 @@ function handleModified(orig, edited, ctx) {
23473
23626
  function patchParagraph(block, orig, edited, ctx, skip) {
23474
23627
  const mapping = ctx.paraMap.get(orig.blockIdx);
23475
23628
  if (!mapping?.para) return skip("\uBB38\uB2E8 \uC18C\uC2A4\uB9F5 \uB9E4\uD551 \uC2E4\uD328 (\uBA38\uB9AC\uB9D0/\uAE00\uC0C1\uC790/\uCEA1\uC158 \uC601\uC5ED\uC774\uAC70\uB098 \uD14D\uC2A4\uD2B8 \uBD88\uC77C\uCE58)");
23476
- if (block.text && block.text.includes("\n")) {
23477
- return skip("\uBB38\uB2E8 \uB0B4 \uAC15\uC81C \uC904\uBC14\uAFC8 \uD3EC\uD568 \u2014 \uC218\uC815 \uC2DC \uC904\uBC14\uAFC8 \uBCF4\uC874 \uBD88\uAC00\uB85C \uBBF8\uC9C0\uC6D0 (v1)");
23629
+ const restoreBr = (s) => s.replace(/\s*<br\s*\/?\s*>\s*/gi, "\n");
23630
+ let newPlain = restoreBr(textUnitToPlain(edited.raw, block));
23631
+ if (block.text && block.text.includes("\n") && !newPlain.includes("\n")) {
23632
+ return skip("\uB2E4\uC911\uC904 \uBB38\uB2E8 \uC218\uC815\uC5D0 <br> \uC5C6\uC74C \u2014 \uC904\uBC14\uAFC8 \uC704\uCE58 \uBCF4\uC874 \uBD88\uAC00\uB85C \uBBF8\uC9C0\uC6D0 (\uC904\uBC14\uAFC8\uC740 <br>\uB85C \uD45C\uAE30)");
23478
23633
  }
23479
- let newPlain = textUnitToPlain(edited.raw, block);
23480
23634
  if (block.footnoteText) {
23481
23635
  const noteMatch = newPlain.match(/\s*\(주: ([\s\S]*)\)$/);
23482
23636
  if (noteMatch) {
@@ -23498,7 +23652,7 @@ function patchParagraph(block, orig, edited, ctx, skip) {
23498
23652
  ctx.skipped.push({ reason: "\uC790\uB3D9\uBC88\uD638 \uC811\uB450 \uC2DD\uBCC4 \uC2E4\uD328 \u2014 \uBC88\uD638 \uD3EC\uD568 \uD14D\uC2A4\uD2B8\uB85C \uC801\uC6A9 (\uBDF0\uC5B4\uC5D0\uC11C \uC911\uBCF5 \uD45C\uC2DC \uAC00\uB2A5)", after: summarize(newPlain) });
23499
23653
  }
23500
23654
  }
23501
- const origPlain = textUnitToPlain(orig.raw, block);
23655
+ const origPlain = restoreBr(textUnitToPlain(orig.raw, block));
23502
23656
  if (newPlain === origPlain) return skip("\uD14D\uC2A4\uD2B8 \uC678 \uBCC0\uACBD(\uD5E4\uB529 \uB808\uBCA8/\uC11C\uC2DD)\uB9CC \uAC10\uC9C0 \u2014 \uC2A4\uD0C0\uC77C \uBCC0\uACBD\uC740 \uBBF8\uC9C0\uC6D0");
23503
23657
  if (sanitizeText(newPlain) !== newPlain) {
23504
23658
  return skip("\uACF5\uBC31 \uC815\uADDC\uD654 \uBD88\uC548\uC815 \uD14D\uC2A4\uD2B8 \u2014 \uD328\uCE58 \uC2DC \uC6D0\uBB38 \uBCF4\uC874 \uBD88\uAC00\uB85C \uBBF8\uC9C0\uC6D0");
@@ -23526,6 +23680,28 @@ function patchGfmCells(scanTable, orig, edited, ctx, skip) {
23526
23680
  }
23527
23681
  const beforeParts = origRows[r][c].split(/<br\s*\/?>/i);
23528
23682
  const afterParts = editedRows[r][c].split(/<br\s*\/?>/i);
23683
+ if (cell.paras.length === 1) {
23684
+ const beforeEach = beforeParts.map(gfmCellToPlain);
23685
+ const afterEach = afterParts.map(gfmCellToPlain);
23686
+ if (beforeEach.some((p) => p === null) || afterEach.some((p) => p === null)) {
23687
+ cellSkip("\uC11C\uC2DD/\uB9C1\uD06C/\uC774\uBBF8\uC9C0 \uD3EC\uD568 \uC140 \uC218\uC815\uC740 \uBBF8\uC9C0\uC6D0 (v1)");
23688
+ continue;
23689
+ }
23690
+ const before = beforeEach.join("\n");
23691
+ const after = afterEach.join("\n");
23692
+ if (before === after) continue;
23693
+ const para = cell.paras[0];
23694
+ if (normForMatch(para.rawText) !== normForMatch(before)) {
23695
+ cellSkip("\uC140 \uD14D\uC2A4\uD2B8 \uBD88\uC77C\uCE58 \u2014 \uC18C\uC2A4\uB9F5 \uC2E0\uB8B0 \uBD88\uAC00");
23696
+ continue;
23697
+ }
23698
+ if (afterEach.some((l) => sanitizeText(l) !== l)) {
23699
+ cellSkip("\uACF5\uBC31 \uC815\uADDC\uD654 \uBD88\uC548\uC815 \uD14D\uC2A4\uD2B8 \u2014 \uBBF8\uC9C0\uC6D0");
23700
+ continue;
23701
+ }
23702
+ applied += stageParaPatch(ctx.scans[para.sectionIndex], para, after, cellSkip);
23703
+ continue;
23704
+ }
23529
23705
  if (beforeParts.length !== cell.paras.length || afterParts.length !== cell.paras.length) {
23530
23706
  cellSkip("\uC140 \uBB38\uB2E8 \uC218 \uBCC0\uACBD \u2014 \uBBF8\uC9C0\uC6D0 (\uBB38\uB2E8 \uCD94\uAC00/\uC0AD\uC81C)");
23531
23707
  continue;
@@ -23670,13 +23846,13 @@ function applyCellEdit5(table, scanTable, gridR, gridC, newLines, ctx, before, a
23670
23846
  const assigned = [];
23671
23847
  for (let i = 0; i < targets.length; i++) {
23672
23848
  if (i < newLines.length) {
23673
- assigned.push(i === targets.length - 1 && newLines.length > targets.length ? newLines.slice(i).join(" ") : newLines[i]);
23849
+ assigned.push(i === targets.length - 1 && newLines.length > targets.length ? newLines.slice(i).join("\n") : newLines[i]);
23674
23850
  } else {
23675
23851
  assigned.push("");
23676
23852
  }
23677
23853
  }
23678
23854
  if (newLines.length > targets.length) {
23679
- ctx.skipped.push({ reason: "\uC140 \uB0B4 \uC904 \uCD94\uAC00\uB294 \uBB38\uB2E8 \uC0DD\uC131 \uBBF8\uC9C0\uC6D0 \u2014 \uB9C8\uC9C0\uB9C9 \uBB38\uB2E8\uC5D0 \uBCD1\uD569 \uC801\uC6A9", after: summarize(after), partial: true });
23855
+ ctx.skipped.push({ reason: "\uC140 \uB0B4 \uCD94\uAC00 \uC904\uC744 \uB9C8\uC9C0\uB9C9 \uBB38\uB2E8\uC5D0 \uAC15\uC81C \uC904\uBC14\uAFC8\uC73C\uB85C \uBCD1\uD569(\uBB38\uB2E8 \uC0DD\uC131 \uB300\uC2E0)", after: summarize(after), partial: true });
23680
23856
  } else if (newLines.length < nonEmpty.length && nonEmpty.length > 1) {
23681
23857
  ctx.skipped.push({ reason: "\uC140 \uB0B4 \uC904 \uC0AD\uC81C\uB294 \uBB38\uB2E8 \uC81C\uAC70 \uBBF8\uC9C0\uC6D0 \u2014 \uBE48 \uBB38\uB2E8 \uC794\uC874(\uBDF0\uC5B4\uC5D0 \uBE48 \uC904 \uD45C\uC2DC \uAC00\uB2A5)", before: summarize(before), after: summarize(after), partial: true });
23682
23858
  }
@@ -23734,8 +23910,7 @@ function splitParaText(data) {
23734
23910
  i += 16;
23735
23911
  toks.push({ start, end: i, units: 1, plain: false, visible: false });
23736
23912
  } else {
23737
- if (i + 14 <= data.length) i += 14;
23738
- toks.push({ start, end: i, units: 1, plain: false, visible: true });
23913
+ toks.push({ start, end: i, units: 1, plain: true, visible: true });
23739
23914
  }
23740
23915
  break;
23741
23916
  default: {
@@ -23798,13 +23973,31 @@ function rebuildCharShape(csData, coreStartUnit) {
23798
23973
  });
23799
23974
  return { buf, count: kept.length };
23800
23975
  }
23976
+ function synthesizeLineSegs(lineSegData, newRaw, startUnits) {
23977
+ if (lineSegData.length < 36) return null;
23978
+ const seg0 = lineSegData.subarray(0, 36);
23979
+ const vPos0 = seg0.readInt32LE(4);
23980
+ const pitch = seg0.readInt32LE(8) + seg0.readInt32LE(20);
23981
+ if (pitch <= 0) return null;
23982
+ const lines = newRaw.split("\n");
23983
+ const segs = [];
23984
+ let pos = startUnits;
23985
+ for (let k = 0; k < lines.length; k++) {
23986
+ const s = Buffer.from(seg0);
23987
+ s.writeUInt32LE((k === 0 ? 0 : pos) >>> 0, 0);
23988
+ s.writeInt32LE(vPos0 + k * pitch, 4);
23989
+ segs.push(s);
23990
+ pos += lines[k].length + 1;
23991
+ }
23992
+ return { buf: Buffer.concat(segs), count: lines.length };
23993
+ }
23801
23994
  function stageParaPatch(scan, para, newPlain, skip) {
23802
23995
  if (!scan.safe) return skip("\uC139\uC158 \uB808\uCF54\uB4DC \uC7AC\uC9C1\uB82C\uD654 \uBD88\uC77C\uCE58 \u2014 \uC548\uC804\uC744 \uC704\uD574 \uC774 \uC139\uC158\uC740 \uBBF8\uC9C0\uC6D0");
23803
23996
  if (para.textIdx === -2) return skip("\uBCF5\uC218 PARA_TEXT \uB808\uCF54\uB4DC \uBB38\uB2E8 \u2014 \uBBF8\uC9C0\uC6D0 (v1)");
23804
23997
  if (para.rangeTagCount > 0) return skip("\uBC94\uC704 \uD0DC\uADF8(\uD615\uAD11\uD39C/\uAD50\uC815\uBD80\uD638) \uBB38\uB2E8 \u2014 \uBBF8\uC9C0\uC6D0 (v1)");
23805
23998
  if (para.charShapeIdx < 0 || para.lineSegIdx < 0) return skip("\uBB38\uB2E8 \uB808\uCF54\uB4DC \uAD6C\uC131 \uBE44\uC815\uD615 \u2014 \uBBF8\uC9C0\uC6D0");
23806
23999
  if (scan.repl.has(para.headerIdx)) return skip("\uB3D9\uC77C \uBB38\uB2E8 \uC911\uBCF5 \uC218\uC815 \u2014 \uCCAB \uC218\uC815\uB9CC \uC801\uC6A9");
23807
- if (/[\u0000-\u001f]/.test(newPlain)) return skip("\uC0C8 \uD14D\uC2A4\uD2B8\uC5D0 \uC81C\uC5B4\uBB38\uC790 \uD3EC\uD568 \u2014 \uBBF8\uC9C0\uC6D0");
24000
+ if (/[\u0000-\u0009\u000b-\u001f]/.test(newPlain)) return skip("\uC0C8 \uD14D\uC2A4\uD2B8\uC5D0 \uC81C\uC5B4\uBB38\uC790 \uD3EC\uD568 \u2014 \uBBF8\uC9C0\uC6D0");
23808
24001
  const records = scan.records;
23809
24002
  const headerRec = records[para.headerIdx];
23810
24003
  const charShapeRec = records[para.charShapeIdx];
@@ -23824,6 +24017,13 @@ function stageParaPatch(scan, para, newPlain, skip) {
23824
24017
  const cs2 = rebuildCharShape(charShapeRec.data, 0);
23825
24018
  scan.repl.set(para.charShapeIdx, cs2.buf);
23826
24019
  newHeader2.writeUInt16LE(cs2.count, 12);
24020
+ if (newPlain.includes("\n")) {
24021
+ const synth = synthesizeLineSegs(records[para.lineSegIdx].data, newPlain, 0);
24022
+ if (synth) {
24023
+ scan.repl.set(para.lineSegIdx, synth.buf);
24024
+ newHeader2.writeUInt16LE(synth.count, 16);
24025
+ }
24026
+ }
23827
24027
  scan.repl.set(para.headerIdx, newHeader2);
23828
24028
  return 1;
23829
24029
  }
@@ -23844,15 +24044,108 @@ function stageParaPatch(scan, para, newPlain, skip) {
23844
24044
  const cs = rebuildCharShape(charShapeRec.data, seg.prefixUnits);
23845
24045
  scan.repl.set(para.charShapeIdx, cs.buf);
23846
24046
  newHeader.writeUInt16LE(cs.count, 12);
24047
+ if (newRaw.includes("\n")) {
24048
+ const synth = synthesizeLineSegs(records[para.lineSegIdx].data, newRaw, seg.prefixUnits);
24049
+ if (synth) {
24050
+ scan.repl.set(para.lineSegIdx, synth.buf);
24051
+ newHeader.writeUInt16LE(synth.count, 16);
24052
+ }
24053
+ }
23847
24054
  scan.repl.set(para.headerIdx, newHeader);
23848
24055
  return 1;
23849
24056
  }
23850
24057
 
23851
- // src/roundtrip/session.ts
24058
+ // src/validate.ts
23852
24059
  import JSZip8 from "jszip";
24060
+ import { DOMParser as DOMParser4 } from "@xmldom/xmldom";
24061
+ var REQUIRED_FILES = [
24062
+ "mimetype",
24063
+ "META-INF/container.xml",
24064
+ "Contents/content.hpf",
24065
+ "Contents/header.xml",
24066
+ "Contents/section0.xml"
24067
+ ];
24068
+ var EXPECTED_MIMETYPE = "application/hwp+zip";
24069
+ var XML_SUFFIXES = [".xml", ".hpf", ".rdf"];
24070
+ var SECTION_FILE_RE = /^Contents\/section\d+\.xml$/;
24071
+ var SECCNT_RE = /<(?:\w+:)?head\b[^>]*?\bsecCnt="(\d+)"/;
24072
+ var OPF_HREF_RE = /<opf:item\b[^>]*?\bhref="([^"]*)"/g;
24073
+ async function validateHwpx(buffer) {
24074
+ const issues = [];
24075
+ let zip;
24076
+ try {
24077
+ zip = await JSZip8.loadAsync(buffer);
24078
+ } catch (err) {
24079
+ return {
24080
+ ok: false,
24081
+ issues: [{ message: `\uC720\uD6A8\uD55C ZIP\uC774 \uC544\uB2D8: ${err instanceof Error ? err.message : String(err)}` }],
24082
+ entryCount: 0
24083
+ };
24084
+ }
24085
+ const rawNames = Object.keys(zip.files);
24086
+ const names = rawNames.filter((n) => !zip.files[n].dir);
24087
+ if (names.length === 0) return { ok: false, issues: [{ message: "\uBE48 ZIP" }], entryCount: 0 };
24088
+ if (rawNames[0] !== "mimetype") {
24089
+ issues.push({ message: `\uCCAB zip \uC5D4\uD2B8\uB9AC\uAC00 '${rawNames[0]}' \u2014 'mimetype'\uC774\uC5B4\uC57C \uD568` });
24090
+ }
24091
+ const nameset = new Set(names);
24092
+ if (nameset.has("mimetype")) {
24093
+ const mt = (await zip.files["mimetype"].async("string")).trim();
24094
+ if (mt !== EXPECTED_MIMETYPE) {
24095
+ issues.push({ path: "mimetype", message: `\uB0B4\uC6A9\uC774 '${mt}' \u2014 '${EXPECTED_MIMETYPE}'\uC774\uC5B4\uC57C \uD568` });
24096
+ }
24097
+ }
24098
+ for (const req of REQUIRED_FILES) {
24099
+ if (!nameset.has(req)) issues.push({ message: `\uD544\uC218 \uD30C\uC77C \uB204\uB77D: ${req}` });
24100
+ }
24101
+ for (const name of names) {
24102
+ if (!XML_SUFFIXES.some((s) => name.endsWith(s))) continue;
24103
+ const text = await zip.files[name].async("string");
24104
+ let firstError = null;
24105
+ try {
24106
+ new DOMParser4({
24107
+ onError(level, msg2) {
24108
+ if (level !== "warning" && firstError === null) firstError = String(msg2);
24109
+ }
24110
+ }).parseFromString(text, "text/xml");
24111
+ } catch (err) {
24112
+ firstError ??= err instanceof Error ? err.message : String(err);
24113
+ }
24114
+ if (firstError !== null) {
24115
+ issues.push({ path: name, message: `XML \uC6F0\uD3FC\uB4DC \uC704\uBC18: ${firstError.split("\n")[0]}` });
24116
+ }
24117
+ }
24118
+ if (nameset.has("Contents/header.xml")) {
24119
+ const header = await zip.files["Contents/header.xml"].async("string");
24120
+ const m = SECCNT_RE.exec(header);
24121
+ if (m) {
24122
+ const declared = Number(m[1]);
24123
+ const actual = names.filter((n) => SECTION_FILE_RE.test(n)).length;
24124
+ if (declared !== actual) {
24125
+ issues.push({
24126
+ path: "Contents/header.xml",
24127
+ message: `secCnt=${declared}\uC778\uB370 \uC2E4\uC81C sectionN.xml\uC740 ${actual}\uAC1C \u2014 \uD55C\uCEF4\uB3C5\uC2A4\uAC00 \uC5F4\uAE30\uB97C \uAC70\uBD80\uD568`
24128
+ });
24129
+ }
24130
+ }
24131
+ }
24132
+ if (nameset.has("Contents/content.hpf")) {
24133
+ const hpf = await zip.files["Contents/content.hpf"].async("string");
24134
+ for (const m of hpf.matchAll(OPF_HREF_RE)) {
24135
+ const href = m[1];
24136
+ if (!nameset.has(href) && !nameset.has(`Contents/${href}`)) {
24137
+ issues.push({ path: "Contents/content.hpf", message: `manifest\uAC00 \uC5C6\uB294 \uD30C\uC77C\uC744 \uCC38\uC870: ${href}` });
24138
+ }
24139
+ }
24140
+ }
24141
+ return { ok: issues.length === 0, issues, entryCount: names.length };
24142
+ }
24143
+
24144
+ // src/roundtrip/session.ts
24145
+ import JSZip9 from "jszip";
23853
24146
  async function buildState(bytes) {
23854
24147
  const parsed = await parseHwpxDocument(u8ToArrayBuffer2(bytes));
23855
- const zip = await JSZip8.loadAsync(bytes);
24148
+ const zip = await JSZip9.loadAsync(bytes);
23856
24149
  const sectionPaths = await resolveSectionEntryNames(zip);
23857
24150
  if (sectionPaths.length === 0) {
23858
24151
  throw new Error("HWPX \uC139\uC158 \uD30C\uC77C\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4");
@@ -24433,7 +24726,7 @@ async function parseHwp(buffer, options) {
24433
24726
  async function parsePdf(buffer, options) {
24434
24727
  let parsePdfDocument;
24435
24728
  try {
24436
- const mod = await import("./parser-5FSEFR3R.js");
24729
+ const mod = await import("./parser-76JSTEP3.js");
24437
24730
  parsePdfDocument = mod.parsePdfDocument;
24438
24731
  } catch {
24439
24732
  return {
@@ -24526,7 +24819,9 @@ async function fillForm(input, values, outputFormat = "markdown") {
24526
24819
  export {
24527
24820
  extractHwpxMetadataOnly,
24528
24821
  extractHwp5MetadataOnly,
24822
+ formatFillValue,
24529
24823
  ValueCursor,
24824
+ fillWithUniqueGuard,
24530
24825
  isLabelCell,
24531
24826
  extractFormFields,
24532
24827
  inferFieldType,
@@ -24544,6 +24839,7 @@ export {
24544
24839
  diffBlocks,
24545
24840
  patchHwpx,
24546
24841
  patchHwp,
24842
+ validateHwpx,
24547
24843
  HwpxSession,
24548
24844
  openHwpxDocument,
24549
24845
  patchHwpxBlocks,
@@ -24561,4 +24857,4 @@ export {
24561
24857
  parseHwpml,
24562
24858
  fillForm
24563
24859
  };
24564
- //# sourceMappingURL=chunk-RUYSNNYZ.js.map
24860
+ //# sourceMappingURL=chunk-Q3IHI3E6.js.map