kordoc 3.13.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 +14 -1
  2. package/dist/{-MRPDPE2X.js → -EYPKRLMN.js} +13 -7
  3. package/dist/{chunk-33CIKNL4.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-MEXUFQVC.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-TWACKS3K.js → chunk-Q3IHI3E6.js} +309 -29
  10. package/dist/chunk-Q3IHI3E6.js.map +1 -0
  11. package/dist/{chunk-YXOT7P5B.js → chunk-Q5ZPWQID.js} +5 -5
  12. package/dist/{chunk-YXOT7P5B.js.map → chunk-Q5ZPWQID.js.map} +1 -1
  13. package/dist/{chunk-Q6UXVJXI.js → chunk-SCFFABCT.js} +2 -2
  14. package/dist/{chunk-O4WFPOFN.js → chunk-ZRNYYEE6.js} +5 -5
  15. package/dist/{chunk-O4WFPOFN.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 +1002 -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 +881 -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-XUNGOH76.js → parser-76JSTEP3.js} +55 -7
  29. package/dist/parser-76JSTEP3.js.map +1 -0
  30. package/dist/{parser-DZBI3EA2.cjs → parser-O2SD4DFR.cjs} +66 -18
  31. package/dist/parser-O2SD4DFR.cjs.map +1 -0
  32. package/dist/{parser-Z3IL2ESY.js → parser-SNBQZO5E.js} +54 -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-G5NFIT5L.js → watch-LEK5U5P6.js} +5 -5
  37. package/package.json +4 -3
  38. package/dist/chunk-33CIKNL4.js.map +0 -1
  39. package/dist/chunk-MEXUFQVC.cjs.map +0 -1
  40. package/dist/chunk-TWACKS3K.js.map +0 -1
  41. package/dist/parser-DZBI3EA2.cjs.map +0 -1
  42. package/dist/parser-XUNGOH76.js.map +0 -1
  43. package/dist/parser-Z3IL2ESY.js.map +0 -1
  44. package/dist/render-VZ4OGHZU.js +0 -9
  45. /package/dist/{-MRPDPE2X.js.map → -EYPKRLMN.js.map} +0 -0
  46. /package/dist/{chunk-Q6UXVJXI.js.map → chunk-SCFFABCT.js.map} +0 -0
  47. /package/dist/{render-VZ4OGHZU.js.map → render-LDI3LO2P.js.map} +0 -0
  48. /package/dist/{watch-G5NFIT5L.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-33CIKNL4.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-YXOT7P5B.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-Q6UXVJXI.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;
@@ -18588,6 +18623,81 @@ function countSections(body) {
18588
18623
  }
18589
18624
 
18590
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
+ }
18591
18701
  var ValueCursor = class {
18592
18702
  constructor(values) {
18593
18703
  this.values = values;
@@ -18739,11 +18849,32 @@ function padInsertion(text, pos, value) {
18739
18849
  }
18740
18850
  function normalizeValues(values) {
18741
18851
  const map = /* @__PURE__ */ new Map();
18742
- for (const [label, value] of Object.entries(values)) {
18743
- 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));
18744
18855
  }
18745
18856
  return map;
18746
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
+ }
18747
18878
  function resolveUnmatched(normalizedValues, matchedLabels, originalValues) {
18748
18879
  return [...normalizedValues.keys()].filter((k) => !matchedLabels.has(k)).map((k) => {
18749
18880
  for (const orig of Object.keys(originalValues)) {
@@ -19013,7 +19144,7 @@ function fillFormFields(blocks, values) {
19013
19144
  cell.text = result.text;
19014
19145
  patternFilledCells.add(cell);
19015
19146
  for (const m of result.matches) {
19016
- 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 });
19017
19148
  }
19018
19149
  }
19019
19150
  }
@@ -19093,7 +19224,8 @@ function fillTable(table, values, filled, matchedLabels, patternFilledCells) {
19093
19224
  label: labelCell.text.trim().replace(/[::]\s*$/, ""),
19094
19225
  value: newValue,
19095
19226
  row: r,
19096
- col: c
19227
+ col: c,
19228
+ key: matchKey
19097
19229
  });
19098
19230
  }
19099
19231
  }
@@ -19122,7 +19254,8 @@ function fillTable(table, values, filled, matchedLabels, patternFilledCells) {
19122
19254
  label: headerCell.text.trim(),
19123
19255
  value: newValue,
19124
19256
  row: r,
19125
- col: c
19257
+ col: c,
19258
+ key: matchKey
19126
19259
  });
19127
19260
  }
19128
19261
  }
@@ -19139,7 +19272,7 @@ function fillInlineFields(text, values, filled, matchedLabels) {
19139
19272
  const newValue = values.consume(matchKey);
19140
19273
  if (newValue === void 0) continue;
19141
19274
  matchedLabels.add(matchKey);
19142
- 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 });
19143
19276
  out += text.slice(pos, seg.valueStart);
19144
19277
  out += seg.valueStart === seg.valueEnd ? padInsertion(text, seg.valueStart, newValue) : newValue;
19145
19278
  pos = seg.valueEnd;
@@ -19976,7 +20109,8 @@ async function fillHwpx(hwpxBuffer, values) {
19976
20109
  label: cellLabelText(headerCells[colIdx]).trim(),
19977
20110
  value: newValue,
19978
20111
  row: rowIdx,
19979
- col: colIdx
20112
+ col: colIdx,
20113
+ key: matchKey
19980
20114
  });
19981
20115
  }
19982
20116
  }
@@ -21487,6 +21621,9 @@ function readCommand(input, idx, depth) {
21487
21621
  const body = readGroupOrToken(input, name.next, depth);
21488
21622
  return { value: `${ACCENT_COMMANDS[command]}{${body.value}}`, next: body.next };
21489
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 };
21490
21627
  if (command === "mathrm" || command === "text") {
21491
21628
  const start = skipSpaces(input, name.next);
21492
21629
  if (input[start] === "{") {
@@ -23489,10 +23626,11 @@ function handleModified(orig, edited, ctx) {
23489
23626
  function patchParagraph(block, orig, edited, ctx, skip) {
23490
23627
  const mapping = ctx.paraMap.get(orig.blockIdx);
23491
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)");
23492
- if (block.text && block.text.includes("\n")) {
23493
- 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)");
23494
23633
  }
23495
- let newPlain = textUnitToPlain(edited.raw, block);
23496
23634
  if (block.footnoteText) {
23497
23635
  const noteMatch = newPlain.match(/\s*\(주: ([\s\S]*)\)$/);
23498
23636
  if (noteMatch) {
@@ -23514,7 +23652,7 @@ function patchParagraph(block, orig, edited, ctx, skip) {
23514
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) });
23515
23653
  }
23516
23654
  }
23517
- const origPlain = textUnitToPlain(orig.raw, block);
23655
+ const origPlain = restoreBr(textUnitToPlain(orig.raw, block));
23518
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");
23519
23657
  if (sanitizeText(newPlain) !== newPlain) {
23520
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");
@@ -23542,6 +23680,28 @@ function patchGfmCells(scanTable, orig, edited, ctx, skip) {
23542
23680
  }
23543
23681
  const beforeParts = origRows[r][c].split(/<br\s*\/?>/i);
23544
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
+ }
23545
23705
  if (beforeParts.length !== cell.paras.length || afterParts.length !== cell.paras.length) {
23546
23706
  cellSkip("\uC140 \uBB38\uB2E8 \uC218 \uBCC0\uACBD \u2014 \uBBF8\uC9C0\uC6D0 (\uBB38\uB2E8 \uCD94\uAC00/\uC0AD\uC81C)");
23547
23707
  continue;
@@ -23686,13 +23846,13 @@ function applyCellEdit5(table, scanTable, gridR, gridC, newLines, ctx, before, a
23686
23846
  const assigned = [];
23687
23847
  for (let i = 0; i < targets.length; i++) {
23688
23848
  if (i < newLines.length) {
23689
- 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]);
23690
23850
  } else {
23691
23851
  assigned.push("");
23692
23852
  }
23693
23853
  }
23694
23854
  if (newLines.length > targets.length) {
23695
- 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 });
23696
23856
  } else if (newLines.length < nonEmpty.length && nonEmpty.length > 1) {
23697
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 });
23698
23858
  }
@@ -23750,8 +23910,7 @@ function splitParaText(data) {
23750
23910
  i += 16;
23751
23911
  toks.push({ start, end: i, units: 1, plain: false, visible: false });
23752
23912
  } else {
23753
- if (i + 14 <= data.length) i += 14;
23754
- toks.push({ start, end: i, units: 1, plain: false, visible: true });
23913
+ toks.push({ start, end: i, units: 1, plain: true, visible: true });
23755
23914
  }
23756
23915
  break;
23757
23916
  default: {
@@ -23814,13 +23973,31 @@ function rebuildCharShape(csData, coreStartUnit) {
23814
23973
  });
23815
23974
  return { buf, count: kept.length };
23816
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
+ }
23817
23994
  function stageParaPatch(scan, para, newPlain, skip) {
23818
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");
23819
23996
  if (para.textIdx === -2) return skip("\uBCF5\uC218 PARA_TEXT \uB808\uCF54\uB4DC \uBB38\uB2E8 \u2014 \uBBF8\uC9C0\uC6D0 (v1)");
23820
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)");
23821
23998
  if (para.charShapeIdx < 0 || para.lineSegIdx < 0) return skip("\uBB38\uB2E8 \uB808\uCF54\uB4DC \uAD6C\uC131 \uBE44\uC815\uD615 \u2014 \uBBF8\uC9C0\uC6D0");
23822
23999
  if (scan.repl.has(para.headerIdx)) return skip("\uB3D9\uC77C \uBB38\uB2E8 \uC911\uBCF5 \uC218\uC815 \u2014 \uCCAB \uC218\uC815\uB9CC \uC801\uC6A9");
23823
- 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");
23824
24001
  const records = scan.records;
23825
24002
  const headerRec = records[para.headerIdx];
23826
24003
  const charShapeRec = records[para.charShapeIdx];
@@ -23840,6 +24017,13 @@ function stageParaPatch(scan, para, newPlain, skip) {
23840
24017
  const cs2 = rebuildCharShape(charShapeRec.data, 0);
23841
24018
  scan.repl.set(para.charShapeIdx, cs2.buf);
23842
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
+ }
23843
24027
  scan.repl.set(para.headerIdx, newHeader2);
23844
24028
  return 1;
23845
24029
  }
@@ -23860,15 +24044,108 @@ function stageParaPatch(scan, para, newPlain, skip) {
23860
24044
  const cs = rebuildCharShape(charShapeRec.data, seg.prefixUnits);
23861
24045
  scan.repl.set(para.charShapeIdx, cs.buf);
23862
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
+ }
23863
24054
  scan.repl.set(para.headerIdx, newHeader);
23864
24055
  return 1;
23865
24056
  }
23866
24057
 
23867
- // src/roundtrip/session.ts
24058
+ // src/validate.ts
23868
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";
23869
24146
  async function buildState(bytes) {
23870
24147
  const parsed = await parseHwpxDocument(u8ToArrayBuffer2(bytes));
23871
- const zip = await JSZip8.loadAsync(bytes);
24148
+ const zip = await JSZip9.loadAsync(bytes);
23872
24149
  const sectionPaths = await resolveSectionEntryNames(zip);
23873
24150
  if (sectionPaths.length === 0) {
23874
24151
  throw new Error("HWPX \uC139\uC158 \uD30C\uC77C\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4");
@@ -24449,7 +24726,7 @@ async function parseHwp(buffer, options) {
24449
24726
  async function parsePdf(buffer, options) {
24450
24727
  let parsePdfDocument;
24451
24728
  try {
24452
- const mod = await import("./parser-XUNGOH76.js");
24729
+ const mod = await import("./parser-76JSTEP3.js");
24453
24730
  parsePdfDocument = mod.parsePdfDocument;
24454
24731
  } catch {
24455
24732
  return {
@@ -24542,7 +24819,9 @@ async function fillForm(input, values, outputFormat = "markdown") {
24542
24819
  export {
24543
24820
  extractHwpxMetadataOnly,
24544
24821
  extractHwp5MetadataOnly,
24822
+ formatFillValue,
24545
24823
  ValueCursor,
24824
+ fillWithUniqueGuard,
24546
24825
  isLabelCell,
24547
24826
  extractFormFields,
24548
24827
  inferFieldType,
@@ -24560,6 +24839,7 @@ export {
24560
24839
  diffBlocks,
24561
24840
  patchHwpx,
24562
24841
  patchHwp,
24842
+ validateHwpx,
24563
24843
  HwpxSession,
24564
24844
  openHwpxDocument,
24565
24845
  patchHwpxBlocks,
@@ -24577,4 +24857,4 @@ export {
24577
24857
  parseHwpml,
24578
24858
  fillForm
24579
24859
  };
24580
- //# sourceMappingURL=chunk-TWACKS3K.js.map
24860
+ //# sourceMappingURL=chunk-Q3IHI3E6.js.map