kordoc 3.0.1 → 3.2.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.
- package/README.md +48 -0
- package/dist/{-5BWAV4ZY.js → -MSRLLJNM.js} +23 -5
- package/dist/{chunk-X3SCCO5Q.js → chunk-7KFBGOXZ.js} +2 -2
- package/dist/{chunk-X3SCCO5Q.js.map → chunk-7KFBGOXZ.js.map} +1 -1
- package/dist/{chunk-X7VQVMXQ.js → chunk-CYLOXVYY.js} +2245 -1604
- package/dist/chunk-CYLOXVYY.js.map +1 -0
- package/dist/{chunk-MUOQXDZ4.cjs → chunk-DCZVOIEO.cjs} +1 -1
- package/dist/{chunk-MUOQXDZ4.cjs.map → chunk-DCZVOIEO.cjs.map} +1 -1
- package/dist/{chunk-SBVRCJFH.js → chunk-GE43BE46.js} +1 -1
- package/dist/chunk-GS7T56RP.cjs +8 -0
- package/dist/chunk-GS7T56RP.cjs.map +1 -0
- package/dist/{chunk-NBJB6TJB.cjs → chunk-SWEBVX5K.cjs} +2 -2
- package/dist/{chunk-NBJB6TJB.cjs.map → chunk-SWEBVX5K.cjs.map} +1 -1
- package/dist/{chunk-O5P6EG5L.js → chunk-ZWVTR2HQ.js} +2 -2
- package/dist/{chunk-O5P6EG5L.js.map → chunk-ZWVTR2HQ.js.map} +1 -1
- package/dist/cli.js +71 -4
- package/dist/cli.js.map +1 -1
- package/dist/{formula-XGG6ZP42.cjs → formula-5NKVS2LR.cjs} +4 -2
- package/dist/formula-5NKVS2LR.cjs.map +1 -0
- package/dist/{formula-3AQUUIRF.js → formula-RXVSQPXI.js} +1 -1
- package/dist/index.cjs +2400 -1756
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +306 -6
- package/dist/index.d.ts +306 -6
- package/dist/index.js +2248 -1607
- package/dist/index.js.map +1 -1
- package/dist/mcp.js +3 -3
- package/dist/page-range-LNMXJU6W.js +7 -0
- package/dist/page-range-P7SDW6LR.cjs +8 -0
- package/dist/page-range-P7SDW6LR.cjs.map +1 -0
- package/dist/{parser-LZH7ZELV.js → parser-HAWIRFS4.js} +5 -5
- package/dist/{parser-5FZJVLQL.cjs → parser-LKIUKZJV.cjs} +19 -18
- package/dist/parser-LKIUKZJV.cjs.map +1 -0
- package/dist/{parser-3N6FZSKU.js → parser-U6KRTEB7.js} +2 -2
- package/dist/{provider-2SEHU2FM.js → provider-4ZJKV3DC.js} +1 -1
- package/dist/{provider-SNONEZNW.cjs → provider-G4C2V2PD.cjs} +4 -2
- package/dist/provider-G4C2V2PD.cjs.map +1 -0
- package/dist/{watch-4FMRS7QU.js → watch-OKZAU3I5.js} +3 -3
- package/package.json +1 -1
- package/dist/chunk-X7VQVMXQ.js.map +0 -1
- package/dist/formula-XGG6ZP42.cjs.map +0 -1
- package/dist/page-range-3C7UGGEK.cjs +0 -7
- package/dist/page-range-3C7UGGEK.cjs.map +0 -1
- package/dist/page-range-H35FN3OQ.js +0 -7
- package/dist/parser-5FZJVLQL.cjs.map +0 -1
- package/dist/provider-SNONEZNW.cjs.map +0 -1
- /package/dist/{-5BWAV4ZY.js.map → -MSRLLJNM.js.map} +0 -0
- /package/dist/{chunk-SBVRCJFH.js.map → chunk-GE43BE46.js.map} +0 -0
- /package/dist/{formula-3AQUUIRF.js.map → formula-RXVSQPXI.js.map} +0 -0
- /package/dist/{page-range-H35FN3OQ.js.map → page-range-LNMXJU6W.js.map} +0 -0
- /package/dist/{parser-LZH7ZELV.js.map → parser-HAWIRFS4.js.map} +0 -0
- /package/dist/{parser-3N6FZSKU.js.map → parser-U6KRTEB7.js.map} +0 -0
- /package/dist/{provider-2SEHU2FM.js.map → provider-4ZJKV3DC.js.map} +0 -0
- /package/dist/{watch-4FMRS7QU.js.map → watch-OKZAU3I5.js.map} +0 -0
|
@@ -23,7 +23,7 @@ import {
|
|
|
23
23
|
sanitizeHref,
|
|
24
24
|
stripDtd,
|
|
25
25
|
toArrayBuffer
|
|
26
|
-
} from "./chunk-
|
|
26
|
+
} from "./chunk-ZWVTR2HQ.js";
|
|
27
27
|
import {
|
|
28
28
|
parsePageRange
|
|
29
29
|
} from "./chunk-MOL7MDBG.js";
|
|
@@ -17750,21 +17750,21 @@ function isDisplayMath(el) {
|
|
|
17750
17750
|
|
|
17751
17751
|
// src/docx/parser.ts
|
|
17752
17752
|
var MAX_DECOMPRESS_SIZE4 = 100 * 1024 * 1024;
|
|
17753
|
-
function getChildElements(parent,
|
|
17753
|
+
function getChildElements(parent, localName2) {
|
|
17754
17754
|
const result = [];
|
|
17755
17755
|
const children = parent.childNodes;
|
|
17756
17756
|
for (let i = 0; i < children.length; i++) {
|
|
17757
17757
|
const node = children[i];
|
|
17758
17758
|
if (node.nodeType === 1) {
|
|
17759
17759
|
const el = node;
|
|
17760
|
-
if (el.localName ===
|
|
17760
|
+
if (el.localName === localName2 || el.tagName?.endsWith(`:${localName2}`)) {
|
|
17761
17761
|
result.push(el);
|
|
17762
17762
|
}
|
|
17763
17763
|
}
|
|
17764
17764
|
}
|
|
17765
17765
|
return result;
|
|
17766
17766
|
}
|
|
17767
|
-
function findElements(parent,
|
|
17767
|
+
function findElements(parent, localName2) {
|
|
17768
17768
|
const result = [];
|
|
17769
17769
|
const walk = (node) => {
|
|
17770
17770
|
const children = node.childNodes;
|
|
@@ -17772,7 +17772,7 @@ function findElements(parent, localName3) {
|
|
|
17772
17772
|
const child = children[i];
|
|
17773
17773
|
if (child.nodeType === 1) {
|
|
17774
17774
|
const el = child;
|
|
17775
|
-
if (el.localName ===
|
|
17775
|
+
if (el.localName === localName2 || el.tagName?.endsWith(`:${localName2}`)) {
|
|
17776
17776
|
result.push(el);
|
|
17777
17777
|
}
|
|
17778
17778
|
walk(el);
|
|
@@ -17782,11 +17782,11 @@ function findElements(parent, localName3) {
|
|
|
17782
17782
|
walk(parent);
|
|
17783
17783
|
return result;
|
|
17784
17784
|
}
|
|
17785
|
-
function getAttr(el,
|
|
17785
|
+
function getAttr(el, localName2) {
|
|
17786
17786
|
const attrs = el.attributes;
|
|
17787
17787
|
for (let i = 0; i < attrs.length; i++) {
|
|
17788
17788
|
const attr = attrs[i];
|
|
17789
|
-
if (attr.localName ===
|
|
17789
|
+
if (attr.localName === localName2 || attr.name === localName2) return attr.value;
|
|
17790
17790
|
}
|
|
17791
17791
|
return null;
|
|
17792
17792
|
}
|
|
@@ -18159,11 +18159,11 @@ async function parseDocxDocument(buffer, options) {
|
|
|
18159
18159
|
const node = children[i];
|
|
18160
18160
|
if (node.nodeType !== 1) continue;
|
|
18161
18161
|
const el = node;
|
|
18162
|
-
const
|
|
18163
|
-
if (
|
|
18162
|
+
const localName2 = el.localName ?? el.tagName?.split(":").pop();
|
|
18163
|
+
if (localName2 === "p") {
|
|
18164
18164
|
const block = parseParagraph2(el, styles, numbering, footnotes, rels);
|
|
18165
18165
|
if (block) blocks.push(block);
|
|
18166
|
-
} else if (
|
|
18166
|
+
} else if (localName2 === "tbl") {
|
|
18167
18167
|
const block = parseTable(el, styles, numbering, footnotes, rels);
|
|
18168
18168
|
if (block) blocks.push(block);
|
|
18169
18169
|
}
|
|
@@ -18454,6 +18454,132 @@ function countSections(body) {
|
|
|
18454
18454
|
return count;
|
|
18455
18455
|
}
|
|
18456
18456
|
|
|
18457
|
+
// src/form/match.ts
|
|
18458
|
+
function normalizeLabel(label) {
|
|
18459
|
+
return label.trim().replace(/[::\s()()·]/g, "");
|
|
18460
|
+
}
|
|
18461
|
+
function findMatchingKey(cellLabel, values) {
|
|
18462
|
+
if (values.has(cellLabel)) return cellLabel;
|
|
18463
|
+
let bestKey;
|
|
18464
|
+
let bestLen = 0;
|
|
18465
|
+
for (const key of values.keys()) {
|
|
18466
|
+
if (cellLabel.startsWith(key)) {
|
|
18467
|
+
if (key.length >= cellLabel.length * 0.6 && key.length > bestLen) {
|
|
18468
|
+
bestLen = key.length;
|
|
18469
|
+
bestKey = key;
|
|
18470
|
+
}
|
|
18471
|
+
} else if (key.startsWith(cellLabel)) {
|
|
18472
|
+
if (cellLabel.length >= key.length * 0.6 && cellLabel.length > bestLen) {
|
|
18473
|
+
bestLen = cellLabel.length;
|
|
18474
|
+
bestKey = key;
|
|
18475
|
+
}
|
|
18476
|
+
}
|
|
18477
|
+
}
|
|
18478
|
+
return bestKey;
|
|
18479
|
+
}
|
|
18480
|
+
function isKeywordLabel(text) {
|
|
18481
|
+
const trimmed = text.trim().replace(/[¹²³⁴⁵⁶⁷⁸⁹⁰*※]+$/g, "").trim();
|
|
18482
|
+
if (!trimmed || trimmed.length > 15) return false;
|
|
18483
|
+
for (const kw of LABEL_KEYWORDS) {
|
|
18484
|
+
if (trimmed.includes(kw)) return true;
|
|
18485
|
+
}
|
|
18486
|
+
return false;
|
|
18487
|
+
}
|
|
18488
|
+
function fillInCellPatterns(cellText, values, matchedLabels) {
|
|
18489
|
+
let text = cellText;
|
|
18490
|
+
const matches = [];
|
|
18491
|
+
text = text.replace(
|
|
18492
|
+
/([가-힣A-Za-z]+)\(\s{1,}\)([가-힣A-Za-z]*)/g,
|
|
18493
|
+
(match, prefix, suffix) => {
|
|
18494
|
+
const label = prefix + suffix;
|
|
18495
|
+
const normalizedLabel = normalizeLabel(label);
|
|
18496
|
+
const matchKey = values.has(normalizedLabel) ? normalizedLabel : values.has(normalizeLabel(prefix)) ? normalizeLabel(prefix) : void 0;
|
|
18497
|
+
if (matchKey === void 0) return match;
|
|
18498
|
+
const newValue = values.get(matchKey);
|
|
18499
|
+
matchedLabels.add(matchKey);
|
|
18500
|
+
matches.push({ key: matchKey, label, value: newValue });
|
|
18501
|
+
return `${prefix}(${newValue})${suffix}`;
|
|
18502
|
+
}
|
|
18503
|
+
);
|
|
18504
|
+
text = text.replace(
|
|
18505
|
+
/□([가-힣A-Za-z]+)/g,
|
|
18506
|
+
(match, keyword) => {
|
|
18507
|
+
const normalizedKw = normalizeLabel(keyword);
|
|
18508
|
+
const matchKey = values.has(normalizedKw) ? normalizedKw : void 0;
|
|
18509
|
+
if (matchKey === void 0) return match;
|
|
18510
|
+
const val = values.get(matchKey);
|
|
18511
|
+
const isTruthy = ["\u2611", "\u2713", "\u2714", "v", "V", "true", "1", "yes", "o", "O"].includes(val.trim()) || val.trim() === "";
|
|
18512
|
+
if (!isTruthy) return match;
|
|
18513
|
+
matchedLabels.add(matchKey);
|
|
18514
|
+
matches.push({ key: matchKey, label: `\u25A1${keyword}`, value: "\u2611" });
|
|
18515
|
+
return `\u2611${keyword}`;
|
|
18516
|
+
}
|
|
18517
|
+
);
|
|
18518
|
+
text = text.replace(
|
|
18519
|
+
/\(([가-힣A-Za-z]+)[::]\s{1,}\)/g,
|
|
18520
|
+
(match, keyword) => {
|
|
18521
|
+
const normalizedKw = normalizeLabel(keyword);
|
|
18522
|
+
const matchKey = values.has(normalizedKw) ? normalizedKw : void 0;
|
|
18523
|
+
if (matchKey === void 0) return match;
|
|
18524
|
+
const newValue = values.get(matchKey);
|
|
18525
|
+
matchedLabels.add(matchKey);
|
|
18526
|
+
matches.push({ key: matchKey, label: keyword, value: newValue });
|
|
18527
|
+
return `(${keyword}\uFF1A${newValue})`;
|
|
18528
|
+
}
|
|
18529
|
+
);
|
|
18530
|
+
return matches.length > 0 ? { text, matches } : null;
|
|
18531
|
+
}
|
|
18532
|
+
var INLINE_LABEL_RE = /([가-힣A-Za-z]{2,10})\s*[::]/g;
|
|
18533
|
+
function scanInlineSegments(text) {
|
|
18534
|
+
const labels = [];
|
|
18535
|
+
INLINE_LABEL_RE.lastIndex = 0;
|
|
18536
|
+
let m;
|
|
18537
|
+
while ((m = INLINE_LABEL_RE.exec(text)) !== null) {
|
|
18538
|
+
if (text[INLINE_LABEL_RE.lastIndex] === "/") continue;
|
|
18539
|
+
labels.push({ label: m[1], start: m.index, end: INLINE_LABEL_RE.lastIndex });
|
|
18540
|
+
}
|
|
18541
|
+
const segments = [];
|
|
18542
|
+
for (let i = 0; i < labels.length; i++) {
|
|
18543
|
+
const cur = labels[i];
|
|
18544
|
+
let vs = cur.end;
|
|
18545
|
+
while (vs < text.length && (text[vs] === " " || text[vs] === " ")) vs++;
|
|
18546
|
+
let ve = i + 1 < labels.length ? labels[i + 1].start : text.length;
|
|
18547
|
+
if (ve < vs) ve = vs;
|
|
18548
|
+
const sep = text.slice(vs, ve).search(/[\n,;]/);
|
|
18549
|
+
if (sep !== -1) ve = vs + sep;
|
|
18550
|
+
if (ve - vs > 100) ve = vs + 100;
|
|
18551
|
+
while (ve > vs && /\s/.test(text[ve - 1])) ve--;
|
|
18552
|
+
segments.push({
|
|
18553
|
+
label: cur.label,
|
|
18554
|
+
labelStart: cur.start,
|
|
18555
|
+
valueStart: vs,
|
|
18556
|
+
valueEnd: ve,
|
|
18557
|
+
value: text.slice(vs, ve)
|
|
18558
|
+
});
|
|
18559
|
+
}
|
|
18560
|
+
return segments;
|
|
18561
|
+
}
|
|
18562
|
+
function padInsertion(text, pos, value) {
|
|
18563
|
+
const lead = pos > 0 && !/\s/.test(text[pos - 1]) ? " " : "";
|
|
18564
|
+
const trail = pos < text.length && !/\s/.test(text[pos]) ? " " : "";
|
|
18565
|
+
return lead + value + trail;
|
|
18566
|
+
}
|
|
18567
|
+
function normalizeValues(values) {
|
|
18568
|
+
const map = /* @__PURE__ */ new Map();
|
|
18569
|
+
for (const [label, value] of Object.entries(values)) {
|
|
18570
|
+
map.set(normalizeLabel(label), value);
|
|
18571
|
+
}
|
|
18572
|
+
return map;
|
|
18573
|
+
}
|
|
18574
|
+
function resolveUnmatched(normalizedValues, matchedLabels, originalValues) {
|
|
18575
|
+
return [...normalizedValues.keys()].filter((k) => !matchedLabels.has(k)).map((k) => {
|
|
18576
|
+
for (const orig of Object.keys(originalValues)) {
|
|
18577
|
+
if (normalizeLabel(orig) === k) return orig;
|
|
18578
|
+
}
|
|
18579
|
+
return k;
|
|
18580
|
+
});
|
|
18581
|
+
}
|
|
18582
|
+
|
|
18457
18583
|
// src/form/recognize.ts
|
|
18458
18584
|
var LABEL_KEYWORDS = /* @__PURE__ */ new Set([
|
|
18459
18585
|
"\uC131\uBA85",
|
|
@@ -18573,107 +18699,73 @@ function extractFromTable(table) {
|
|
|
18573
18699
|
}
|
|
18574
18700
|
function extractInlineFields(text) {
|
|
18575
18701
|
const fields = [];
|
|
18576
|
-
const
|
|
18577
|
-
|
|
18578
|
-
|
|
18579
|
-
const label = match[1].trim();
|
|
18580
|
-
const value = match[2].trim();
|
|
18581
|
-
if (value) {
|
|
18582
|
-
fields.push({ label, value, row: -1, col: -1 });
|
|
18702
|
+
for (const seg of scanInlineSegments(text)) {
|
|
18703
|
+
if (seg.value) {
|
|
18704
|
+
fields.push({ label: seg.label, value: seg.value, row: -1, col: -1 });
|
|
18583
18705
|
}
|
|
18584
18706
|
}
|
|
18585
18707
|
return fields;
|
|
18586
18708
|
}
|
|
18587
|
-
|
|
18588
|
-
|
|
18589
|
-
|
|
18590
|
-
|
|
18591
|
-
|
|
18592
|
-
|
|
18593
|
-
|
|
18594
|
-
|
|
18595
|
-
|
|
18596
|
-
|
|
18597
|
-
|
|
18598
|
-
|
|
18599
|
-
|
|
18600
|
-
|
|
18601
|
-
|
|
18602
|
-
|
|
18603
|
-
|
|
18604
|
-
|
|
18605
|
-
|
|
18606
|
-
|
|
18607
|
-
|
|
18608
|
-
}
|
|
18609
|
-
return
|
|
18610
|
-
}
|
|
18611
|
-
function
|
|
18612
|
-
|
|
18613
|
-
|
|
18614
|
-
|
|
18615
|
-
|
|
18616
|
-
|
|
18617
|
-
return
|
|
18618
|
-
}
|
|
18619
|
-
function
|
|
18620
|
-
|
|
18621
|
-
const
|
|
18622
|
-
|
|
18623
|
-
|
|
18624
|
-
(
|
|
18625
|
-
|
|
18626
|
-
|
|
18627
|
-
|
|
18628
|
-
|
|
18629
|
-
|
|
18630
|
-
|
|
18631
|
-
|
|
18632
|
-
|
|
18633
|
-
|
|
18634
|
-
|
|
18635
|
-
|
|
18636
|
-
|
|
18637
|
-
|
|
18638
|
-
|
|
18639
|
-
|
|
18640
|
-
|
|
18641
|
-
|
|
18642
|
-
|
|
18643
|
-
|
|
18644
|
-
matchedLabels.add(matchKey);
|
|
18645
|
-
matches.push({ key: matchKey, label: `\u25A1${keyword}`, value: "\u2611" });
|
|
18646
|
-
return `\u2611${keyword}`;
|
|
18647
|
-
}
|
|
18648
|
-
);
|
|
18649
|
-
text = text.replace(
|
|
18650
|
-
/\(([가-힣A-Za-z]+)[::]\s{1,}\)/g,
|
|
18651
|
-
(match, keyword) => {
|
|
18652
|
-
const normalizedKw = normalizeLabel(keyword);
|
|
18653
|
-
const matchKey = values.has(normalizedKw) ? normalizedKw : void 0;
|
|
18654
|
-
if (matchKey === void 0) return match;
|
|
18655
|
-
const newValue = values.get(matchKey);
|
|
18656
|
-
matchedLabels.add(matchKey);
|
|
18657
|
-
matches.push({ key: matchKey, label: keyword, value: newValue });
|
|
18658
|
-
return `(${keyword}\uFF1A${newValue})`;
|
|
18709
|
+
var LABEL_TYPE_RULES = [
|
|
18710
|
+
[/주민등록번호|외국인등록번호/, "idnum"],
|
|
18711
|
+
[/생년월일|일시|날짜|일자|기간|연월일|년월일|신청일|작성일|발급일|접수일/, "date"],
|
|
18712
|
+
[/전화|연락처|휴대폰|핸드폰|팩스/, "phone"],
|
|
18713
|
+
[/이메일|전자우편|email/i, "email"],
|
|
18714
|
+
[/금액|단가|수량|합계|소계|예산|비용|인원|급여|연봉/, "amount"]
|
|
18715
|
+
];
|
|
18716
|
+
function inferFieldType(label, value) {
|
|
18717
|
+
if (/[□☑✓✔]/.test(value) || /[□☑✓✔]/.test(label)) return "checkbox";
|
|
18718
|
+
const v = value.trim();
|
|
18719
|
+
if (v) {
|
|
18720
|
+
if (/^\d{6}[-\s]?[1-4]\d{6}$/.test(v)) return "idnum";
|
|
18721
|
+
if (/^\d{4}\s*[-./년]\s*\d{1,2}\s*[-./월]\s*\d{1,2}\s*일?\s*\.?$/.test(v)) return "date";
|
|
18722
|
+
if (/^0\d{1,2}[-.)\s]?\d{3,4}[-.\s]?\d{4}$/.test(v)) return "phone";
|
|
18723
|
+
if (/^[\w.+-]+@[\w-]+(?:\.[\w-]+)+$/.test(v)) return "email";
|
|
18724
|
+
if (/^[\d,.\s]+(?:원|명|건|개|회|부|매|%)$/.test(v) && /\d/.test(v)) return "amount";
|
|
18725
|
+
if (/^\d{1,3}(?:,\d{3})+$/.test(v)) return "amount";
|
|
18726
|
+
}
|
|
18727
|
+
const norm = label.replace(/\s/g, "");
|
|
18728
|
+
for (const [re, type] of LABEL_TYPE_RULES) {
|
|
18729
|
+
if (re.test(norm)) return type;
|
|
18730
|
+
}
|
|
18731
|
+
return "text";
|
|
18732
|
+
}
|
|
18733
|
+
function isRequiredLabel(label) {
|
|
18734
|
+
return /[*※★]|\(\s*필수\s*\)|(\s*필수\s*)/.test(label);
|
|
18735
|
+
}
|
|
18736
|
+
function isEmptyValue(value) {
|
|
18737
|
+
const v = value.trim();
|
|
18738
|
+
if (!v) return true;
|
|
18739
|
+
return /^[\s_()()\-—–~.·,]*$/.test(v);
|
|
18740
|
+
}
|
|
18741
|
+
function extractFormSchema(blocks) {
|
|
18742
|
+
const { fields, confidence } = extractFormFields(blocks);
|
|
18743
|
+
const schemaFields = fields.map((f) => ({
|
|
18744
|
+
...f,
|
|
18745
|
+
type: inferFieldType(f.label, f.value),
|
|
18746
|
+
required: isRequiredLabel(f.label) || void 0,
|
|
18747
|
+
empty: isEmptyValue(f.value)
|
|
18748
|
+
}));
|
|
18749
|
+
const seen = new Set(schemaFields.map((f) => normalizeLabel(f.label)));
|
|
18750
|
+
for (const block of blocks) {
|
|
18751
|
+
if (block.type !== "paragraph" || !block.text) continue;
|
|
18752
|
+
for (const seg of scanInlineSegments(block.text)) {
|
|
18753
|
+
if (seg.value) continue;
|
|
18754
|
+
const key = normalizeLabel(seg.label);
|
|
18755
|
+
if (seen.has(key)) continue;
|
|
18756
|
+
seen.add(key);
|
|
18757
|
+
schemaFields.push({
|
|
18758
|
+
label: seg.label,
|
|
18759
|
+
value: "",
|
|
18760
|
+
row: -1,
|
|
18761
|
+
col: -1,
|
|
18762
|
+
type: inferFieldType(seg.label, ""),
|
|
18763
|
+
required: isRequiredLabel(seg.label) || void 0,
|
|
18764
|
+
empty: true
|
|
18765
|
+
});
|
|
18659
18766
|
}
|
|
18660
|
-
);
|
|
18661
|
-
return matches.length > 0 ? { text, matches } : null;
|
|
18662
|
-
}
|
|
18663
|
-
function normalizeValues(values) {
|
|
18664
|
-
const map = /* @__PURE__ */ new Map();
|
|
18665
|
-
for (const [label, value] of Object.entries(values)) {
|
|
18666
|
-
map.set(normalizeLabel(label), value);
|
|
18667
18767
|
}
|
|
18668
|
-
return
|
|
18669
|
-
}
|
|
18670
|
-
function resolveUnmatched(normalizedValues, matchedLabels, originalValues) {
|
|
18671
|
-
return [...normalizedValues.keys()].filter((k) => !matchedLabels.has(k)).map((k) => {
|
|
18672
|
-
for (const orig of Object.keys(originalValues)) {
|
|
18673
|
-
if (normalizeLabel(orig) === k) return orig;
|
|
18674
|
-
}
|
|
18675
|
-
return k;
|
|
18676
|
-
});
|
|
18768
|
+
return { confidence, fields: schemaFields };
|
|
18677
18769
|
}
|
|
18678
18770
|
|
|
18679
18771
|
// src/form/filler.ts
|
|
@@ -18767,1579 +18859,1773 @@ function fillTable(table, values, filled, matchedLabels, patternFilledCells) {
|
|
|
18767
18859
|
}
|
|
18768
18860
|
}
|
|
18769
18861
|
function fillInlineFields(text, values, filled, matchedLabels) {
|
|
18770
|
-
|
|
18771
|
-
|
|
18772
|
-
|
|
18773
|
-
|
|
18774
|
-
|
|
18775
|
-
|
|
18776
|
-
|
|
18777
|
-
|
|
18778
|
-
|
|
18779
|
-
|
|
18780
|
-
|
|
18781
|
-
|
|
18782
|
-
|
|
18783
|
-
|
|
18784
|
-
|
|
18785
|
-
|
|
18786
|
-
);
|
|
18862
|
+
const segments = scanInlineSegments(text);
|
|
18863
|
+
if (segments.length === 0) return text;
|
|
18864
|
+
let out = "";
|
|
18865
|
+
let pos = 0;
|
|
18866
|
+
for (const seg of segments) {
|
|
18867
|
+
const matchKey = findMatchingKey(normalizeLabel(seg.label), values);
|
|
18868
|
+
if (matchKey === void 0) continue;
|
|
18869
|
+
const newValue = values.get(matchKey);
|
|
18870
|
+
matchedLabels.add(matchKey);
|
|
18871
|
+
filled.push({ label: seg.label.trim(), value: newValue, row: -1, col: -1 });
|
|
18872
|
+
out += text.slice(pos, seg.valueStart);
|
|
18873
|
+
out += seg.valueStart === seg.valueEnd ? padInsertion(text, seg.valueStart, newValue) : newValue;
|
|
18874
|
+
pos = seg.valueEnd;
|
|
18875
|
+
}
|
|
18876
|
+
out += text.slice(pos);
|
|
18877
|
+
return out;
|
|
18787
18878
|
}
|
|
18788
18879
|
|
|
18789
18880
|
// src/form/filler-hwpx.ts
|
|
18790
18881
|
import JSZip4 from "jszip";
|
|
18791
|
-
|
|
18792
|
-
|
|
18793
|
-
|
|
18794
|
-
|
|
18795
|
-
|
|
18796
|
-
|
|
18797
|
-
|
|
18798
|
-
|
|
18799
|
-
|
|
18800
|
-
|
|
18801
|
-
|
|
18802
|
-
|
|
18803
|
-
|
|
18804
|
-
|
|
18805
|
-
|
|
18806
|
-
|
|
18807
|
-
|
|
18808
|
-
|
|
18809
|
-
let modified = false;
|
|
18810
|
-
const tables = findAllElements(doc.documentElement, "tbl");
|
|
18811
|
-
const cellPatternApplied = /* @__PURE__ */ new Set();
|
|
18812
|
-
for (const tblEl of tables) {
|
|
18813
|
-
const allCells = findAllElements(tblEl, "tc");
|
|
18814
|
-
for (const tcEl of allCells) {
|
|
18815
|
-
const tNodes = collectCellTextNodes(tcEl);
|
|
18816
|
-
const fullText = tNodes.map((n) => n.text).join("");
|
|
18817
|
-
const result = fillInCellPatterns(fullText, normalizedValues, matchedLabels);
|
|
18818
|
-
if (!result) continue;
|
|
18819
|
-
applyTextReplacements(tNodes, fullText, result.text);
|
|
18820
|
-
cellPatternApplied.add(tcEl);
|
|
18821
|
-
for (const m of result.matches) {
|
|
18822
|
-
filled.push({ label: m.label, value: m.value, row: -1, col: -1 });
|
|
18823
|
-
}
|
|
18824
|
-
modified = true;
|
|
18825
|
-
}
|
|
18882
|
+
|
|
18883
|
+
// src/roundtrip/source-map.ts
|
|
18884
|
+
function escapeXmlText(text) {
|
|
18885
|
+
return text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
18886
|
+
}
|
|
18887
|
+
function decodeXmlEntities(text) {
|
|
18888
|
+
return text.replace(/&(lt|gt|amp|quot|apos|#x?[0-9a-fA-F]+);/g, (m, ent) => {
|
|
18889
|
+
switch (ent) {
|
|
18890
|
+
case "lt":
|
|
18891
|
+
return "<";
|
|
18892
|
+
case "gt":
|
|
18893
|
+
return ">";
|
|
18894
|
+
case "amp":
|
|
18895
|
+
return "&";
|
|
18896
|
+
case "quot":
|
|
18897
|
+
return '"';
|
|
18898
|
+
case "apos":
|
|
18899
|
+
return "'";
|
|
18826
18900
|
}
|
|
18827
|
-
|
|
18828
|
-
const
|
|
18829
|
-
|
|
18830
|
-
|
|
18831
|
-
const cells = findDirectChildren(trEl, "tc");
|
|
18832
|
-
for (let colIdx = 0; colIdx < cells.length - 1; colIdx++) {
|
|
18833
|
-
const labelText = extractCellText2(cells[colIdx]);
|
|
18834
|
-
if (!isLabelCell(labelText)) continue;
|
|
18835
|
-
const valueCell = cells[colIdx + 1];
|
|
18836
|
-
const valueText = extractCellText2(valueCell);
|
|
18837
|
-
if (isKeywordLabel(valueText)) continue;
|
|
18838
|
-
const normalizedCellLabel = normalizeLabel(labelText);
|
|
18839
|
-
if (!normalizedCellLabel) continue;
|
|
18840
|
-
const matchKey = findMatchingKey(normalizedCellLabel, normalizedValues);
|
|
18841
|
-
if (matchKey === void 0) continue;
|
|
18842
|
-
const newValue = normalizedValues.get(matchKey);
|
|
18843
|
-
if (cellPatternApplied.has(valueCell)) {
|
|
18844
|
-
prependCellText(valueCell, newValue);
|
|
18845
|
-
} else {
|
|
18846
|
-
replaceCellText(valueCell, newValue);
|
|
18847
|
-
}
|
|
18848
|
-
matchedLabels.add(matchKey);
|
|
18849
|
-
filled.push({
|
|
18850
|
-
label: labelText.trim().replace(/[::]\s*$/, ""),
|
|
18851
|
-
value: newValue,
|
|
18852
|
-
row: rowIdx,
|
|
18853
|
-
col: colIdx
|
|
18854
|
-
});
|
|
18855
|
-
modified = true;
|
|
18856
|
-
}
|
|
18857
|
-
}
|
|
18858
|
-
if (rows.length >= 2) {
|
|
18859
|
-
const headerCells = findDirectChildren(rows[0], "tc");
|
|
18860
|
-
const allLabels = headerCells.every((cell) => {
|
|
18861
|
-
const t = extractCellText2(cell).trim();
|
|
18862
|
-
return t.length > 0 && t.length <= 20 && isLabelCell(t);
|
|
18863
|
-
});
|
|
18864
|
-
if (allLabels) {
|
|
18865
|
-
for (let rowIdx = 1; rowIdx < rows.length; rowIdx++) {
|
|
18866
|
-
const dataCells = findDirectChildren(rows[rowIdx], "tc");
|
|
18867
|
-
for (let colIdx = 0; colIdx < Math.min(headerCells.length, dataCells.length); colIdx++) {
|
|
18868
|
-
const headerLabel = normalizeLabel(extractCellText2(headerCells[colIdx]));
|
|
18869
|
-
const matchKey = findMatchingKey(headerLabel, normalizedValues);
|
|
18870
|
-
if (matchKey === void 0) continue;
|
|
18871
|
-
if (matchedLabels.has(matchKey)) continue;
|
|
18872
|
-
const newValue = normalizedValues.get(matchKey);
|
|
18873
|
-
replaceCellText(dataCells[colIdx], newValue);
|
|
18874
|
-
matchedLabels.add(matchKey);
|
|
18875
|
-
filled.push({
|
|
18876
|
-
label: extractCellText2(headerCells[colIdx]).trim(),
|
|
18877
|
-
value: newValue,
|
|
18878
|
-
row: rowIdx,
|
|
18879
|
-
col: colIdx
|
|
18880
|
-
});
|
|
18881
|
-
modified = true;
|
|
18882
|
-
}
|
|
18883
|
-
}
|
|
18884
|
-
}
|
|
18885
|
-
}
|
|
18886
|
-
}
|
|
18887
|
-
const allParagraphs = findAllElements(doc.documentElement, "p");
|
|
18888
|
-
for (const pEl of allParagraphs) {
|
|
18889
|
-
if (isInsideTable(pEl)) continue;
|
|
18890
|
-
const tNodes = collectTextNodes(pEl);
|
|
18891
|
-
const fullText = tNodes.map((n) => n.text).join("");
|
|
18892
|
-
const pattern = /([가-힣A-Za-z]{2,10})\s*[::]\s*([^\n,;]{0,100})/g;
|
|
18893
|
-
let match;
|
|
18894
|
-
while ((match = pattern.exec(fullText)) !== null) {
|
|
18895
|
-
const rawLabel = match[1];
|
|
18896
|
-
const normalized = normalizeLabel(rawLabel);
|
|
18897
|
-
const matchKey = findMatchingKey(normalized, normalizedValues);
|
|
18898
|
-
if (matchKey === void 0) continue;
|
|
18899
|
-
const newValue = normalizedValues.get(matchKey);
|
|
18900
|
-
const valueStart = match.index + match[0].length - match[2].length;
|
|
18901
|
-
const valueEnd = match.index + match[0].length;
|
|
18902
|
-
replaceTextRange(tNodes, valueStart, valueEnd, newValue);
|
|
18903
|
-
matchedLabels.add(matchKey);
|
|
18904
|
-
filled.push({ label: rawLabel.trim(), value: newValue, row: -1, col: -1 });
|
|
18905
|
-
modified = true;
|
|
18906
|
-
break;
|
|
18907
|
-
}
|
|
18908
|
-
}
|
|
18909
|
-
if (modified) {
|
|
18910
|
-
const newXml = xmlSerializer.serializeToString(doc);
|
|
18911
|
-
zip.file(sectionPath, newXml);
|
|
18901
|
+
try {
|
|
18902
|
+
const code = ent[1] === "x" || ent[1] === "X" ? parseInt(ent.slice(2), 16) : parseInt(ent.slice(1), 10);
|
|
18903
|
+
if (!isNaN(code) && code >= 0 && code <= 1114111) return String.fromCodePoint(code);
|
|
18904
|
+
} catch {
|
|
18912
18905
|
}
|
|
18913
|
-
|
|
18914
|
-
|
|
18915
|
-
const buffer = await zip.generateAsync({ type: "arraybuffer" });
|
|
18916
|
-
return { buffer, filled, unmatched };
|
|
18917
|
-
}
|
|
18918
|
-
function localName2(el) {
|
|
18919
|
-
return (el.tagName || el.localName || "").replace(/^[^:]+:/, "");
|
|
18906
|
+
return m;
|
|
18907
|
+
});
|
|
18920
18908
|
}
|
|
18921
|
-
function
|
|
18922
|
-
|
|
18923
|
-
|
|
18924
|
-
|
|
18925
|
-
if (!children) return;
|
|
18926
|
-
for (let i = 0; i < children.length; i++) {
|
|
18927
|
-
const child = children[i];
|
|
18928
|
-
if (child.nodeType !== 1) continue;
|
|
18929
|
-
if (localName2(child) === tagLocalName) result.push(child);
|
|
18930
|
-
walk(child);
|
|
18931
|
-
}
|
|
18932
|
-
};
|
|
18933
|
-
walk(node);
|
|
18934
|
-
return result;
|
|
18909
|
+
function tContentToText(raw) {
|
|
18910
|
+
return decodeXmlEntities(
|
|
18911
|
+
raw.replace(/<\/?(?:[A-Za-z0-9_]+:)?(?:tab|fwSpace|hwSpace|br|lineBreak)(?:\s[^>]*)?\/?>/g, " ").replace(/<[^>]*>/g, "")
|
|
18912
|
+
);
|
|
18935
18913
|
}
|
|
18936
|
-
|
|
18937
|
-
|
|
18938
|
-
|
|
18939
|
-
|
|
18940
|
-
|
|
18941
|
-
|
|
18942
|
-
|
|
18943
|
-
|
|
18944
|
-
|
|
18945
|
-
|
|
18946
|
-
|
|
18914
|
+
var TAG_RE = /<!--[\s\S]*?-->|<!\[CDATA\[[\s\S]*?\]\]>|<\?[\s\S]*?\?>|<!(?:"[^"]*"|'[^']*'|[^>"'])*>|<\/([^\s>]+)\s*>|<([^\s/>!?]+)((?:"[^"]*"|'[^']*'|[^>"'])*?)(\/?)>/g;
|
|
18915
|
+
var T_BARRIER = /* @__PURE__ */ new Set([
|
|
18916
|
+
"tbl",
|
|
18917
|
+
"ctrl",
|
|
18918
|
+
"caption",
|
|
18919
|
+
"pic",
|
|
18920
|
+
"shape",
|
|
18921
|
+
"drawingObject",
|
|
18922
|
+
"drawText",
|
|
18923
|
+
"shapeComment",
|
|
18924
|
+
"memogroup",
|
|
18925
|
+
"memo",
|
|
18926
|
+
"hiddenComment",
|
|
18927
|
+
"equation",
|
|
18928
|
+
"parameters",
|
|
18929
|
+
"subList",
|
|
18930
|
+
"p"
|
|
18931
|
+
]);
|
|
18932
|
+
var PARA_CONTAINER = /* @__PURE__ */ new Set([
|
|
18933
|
+
"tc",
|
|
18934
|
+
"ctrl",
|
|
18935
|
+
"caption",
|
|
18936
|
+
"drawText",
|
|
18937
|
+
"pic",
|
|
18938
|
+
"shape",
|
|
18939
|
+
"drawingObject",
|
|
18940
|
+
"memogroup",
|
|
18941
|
+
"memo",
|
|
18942
|
+
"hiddenComment",
|
|
18943
|
+
"footNote",
|
|
18944
|
+
"endNote",
|
|
18945
|
+
"fn",
|
|
18946
|
+
"en"
|
|
18947
|
+
// 각주/미주 — 파서는 호스트 블록 footnoteText로만 흡수
|
|
18948
|
+
]);
|
|
18949
|
+
var TABLE_BARRIER = /* @__PURE__ */ new Set([
|
|
18950
|
+
"tbl",
|
|
18951
|
+
"ctrl",
|
|
18952
|
+
"caption",
|
|
18953
|
+
"memogroup",
|
|
18954
|
+
"memo",
|
|
18955
|
+
"hiddenComment"
|
|
18956
|
+
]);
|
|
18957
|
+
function localOf(qname) {
|
|
18958
|
+
const i = qname.indexOf(":");
|
|
18959
|
+
return i >= 0 ? qname.slice(i + 1) : qname;
|
|
18947
18960
|
}
|
|
18948
|
-
function
|
|
18949
|
-
|
|
18950
|
-
|
|
18951
|
-
if (parent.nodeType === 1 && localName2(parent) === "tbl") return true;
|
|
18952
|
-
parent = parent.parentNode;
|
|
18953
|
-
}
|
|
18954
|
-
return false;
|
|
18961
|
+
function prefixOf(qname) {
|
|
18962
|
+
const i = qname.indexOf(":");
|
|
18963
|
+
return i >= 0 ? qname.slice(0, i) : "";
|
|
18955
18964
|
}
|
|
18956
|
-
function
|
|
18957
|
-
const
|
|
18958
|
-
const
|
|
18959
|
-
|
|
18960
|
-
|
|
18961
|
-
|
|
18962
|
-
|
|
18963
|
-
|
|
18964
|
-
|
|
18965
|
-
|
|
18966
|
-
|
|
18967
|
-
|
|
18968
|
-
|
|
18969
|
-
|
|
18970
|
-
|
|
18965
|
+
function scanSectionXml(xml, sectionIndex) {
|
|
18966
|
+
const stack = [];
|
|
18967
|
+
const bodyParagraphs = [];
|
|
18968
|
+
const tables = [];
|
|
18969
|
+
const headerTexts = [];
|
|
18970
|
+
const footerTexts = [];
|
|
18971
|
+
const excludedParagraphs = [];
|
|
18972
|
+
const orphanTables = [];
|
|
18973
|
+
const paraStack = [];
|
|
18974
|
+
const tableStack = [];
|
|
18975
|
+
const rowStack = [];
|
|
18976
|
+
const cellStack = [];
|
|
18977
|
+
let pendingT = null;
|
|
18978
|
+
const ctrlSubStack = [];
|
|
18979
|
+
const classifyPara = () => {
|
|
18980
|
+
let sawDrawText = false;
|
|
18981
|
+
for (let i = stack.length - 1; i >= 0; i--) {
|
|
18982
|
+
const l = stack[i].local;
|
|
18983
|
+
if (l === "tc") return { kind: "cell", inTextbox: sawDrawText };
|
|
18984
|
+
if (l === "drawText") {
|
|
18985
|
+
sawDrawText = true;
|
|
18986
|
+
continue;
|
|
18971
18987
|
}
|
|
18988
|
+
if (PARA_CONTAINER.has(l)) return { kind: "excluded", inTextbox: sawDrawText };
|
|
18972
18989
|
}
|
|
18990
|
+
return sawDrawText ? { kind: "draw", inTextbox: true } : { kind: "body", inTextbox: false };
|
|
18973
18991
|
};
|
|
18974
|
-
|
|
18975
|
-
|
|
18976
|
-
|
|
18977
|
-
|
|
18978
|
-
|
|
18979
|
-
|
|
18980
|
-
const firstT = tElements[0];
|
|
18981
|
-
const existing = firstT.textContent || "";
|
|
18982
|
-
clearChildren(firstT);
|
|
18983
|
-
firstT.appendChild(firstT.ownerDocument.createTextNode(text + " " + existing));
|
|
18984
|
-
}
|
|
18985
|
-
function replaceCellText(tcEl, newValue) {
|
|
18986
|
-
const paragraphs = findAllElements(tcEl, "p");
|
|
18987
|
-
if (paragraphs.length === 0) return;
|
|
18988
|
-
const firstP = paragraphs[0];
|
|
18989
|
-
const runs = findAllElements(firstP, "run").concat(findAllElements(firstP, "r"));
|
|
18990
|
-
if (runs.length > 0) {
|
|
18991
|
-
setRunText(runs[0], newValue);
|
|
18992
|
-
for (let i = 1; i < runs.length; i++) {
|
|
18993
|
-
setRunText(runs[i], "");
|
|
18992
|
+
const owningPara = () => {
|
|
18993
|
+
if (paraStack.length === 0) return null;
|
|
18994
|
+
for (let i = stack.length - 1; i >= 0; i--) {
|
|
18995
|
+
const l = stack[i].local;
|
|
18996
|
+
if (l === "p") return paraStack[paraStack.length - 1];
|
|
18997
|
+
if (T_BARRIER.has(l)) return null;
|
|
18994
18998
|
}
|
|
18995
|
-
|
|
18996
|
-
|
|
18997
|
-
|
|
18998
|
-
|
|
18999
|
-
|
|
19000
|
-
for (let i = 1; i < tElements.length; i++) {
|
|
19001
|
-
clearChildren(tElements[i]);
|
|
19002
|
-
}
|
|
18999
|
+
return null;
|
|
19000
|
+
};
|
|
19001
|
+
const isTableTopLevel = () => {
|
|
19002
|
+
for (let i = stack.length - 1; i >= 0; i--) {
|
|
19003
|
+
if (TABLE_BARRIER.has(stack[i].local)) return false;
|
|
19003
19004
|
}
|
|
19004
|
-
|
|
19005
|
-
|
|
19006
|
-
|
|
19007
|
-
|
|
19008
|
-
|
|
19009
|
-
|
|
19010
|
-
|
|
19011
|
-
|
|
19005
|
+
return true;
|
|
19006
|
+
};
|
|
19007
|
+
const currentCtrlSub = () => ctrlSubStack.length > 0 ? ctrlSubStack[ctrlSubStack.length - 1] : null;
|
|
19008
|
+
TAG_RE.lastIndex = 0;
|
|
19009
|
+
let m;
|
|
19010
|
+
while ((m = TAG_RE.exec(xml)) !== null) {
|
|
19011
|
+
const [full, closeName, openName, , selfClose] = m;
|
|
19012
|
+
if (closeName === void 0 && openName === void 0) continue;
|
|
19013
|
+
if (closeName !== void 0) {
|
|
19014
|
+
const local2 = localOf(closeName);
|
|
19015
|
+
if (local2 === "t" && pendingT) {
|
|
19016
|
+
const { para, contentStart: contentStart2 } = pendingT;
|
|
19017
|
+
para.tRanges.push({ contentStart: contentStart2, contentEnd: m.index });
|
|
19018
|
+
para.text += tContentToText(xml.slice(contentStart2, m.index));
|
|
19019
|
+
pendingT = null;
|
|
19020
|
+
}
|
|
19021
|
+
for (let i = stack.length - 1; i >= 0; i--) {
|
|
19022
|
+
if (stack[i].local === local2) {
|
|
19023
|
+
stack.length = i;
|
|
19024
|
+
break;
|
|
19025
|
+
}
|
|
19026
|
+
}
|
|
19027
|
+
if (local2 === "p") {
|
|
19028
|
+
const para = paraStack.pop();
|
|
19029
|
+
if (para && para.kind === "excluded") {
|
|
19030
|
+
const sub = currentCtrlSub();
|
|
19031
|
+
if (sub && para.text.trim()) sub.texts.push(para.text);
|
|
19032
|
+
}
|
|
19033
|
+
} else if (local2 === "tc") {
|
|
19034
|
+
const cell = cellStack.pop();
|
|
19035
|
+
const row = rowStack[rowStack.length - 1];
|
|
19036
|
+
if (cell && row) row.push(cell);
|
|
19037
|
+
} else if (local2 === "tr") {
|
|
19038
|
+
const row = rowStack[rowStack.length - 1];
|
|
19039
|
+
const table = tableStack[tableStack.length - 1];
|
|
19040
|
+
if (row && table && row.length > 0) table.rows.push(row);
|
|
19041
|
+
if (rowStack.length > 0) rowStack[rowStack.length - 1] = [];
|
|
19042
|
+
} else if (local2 === "tbl") {
|
|
19043
|
+
const table = tableStack.pop();
|
|
19044
|
+
rowStack.pop();
|
|
19045
|
+
if (table) {
|
|
19046
|
+
finalizeTable(table);
|
|
19047
|
+
if (!table.topLevel) {
|
|
19048
|
+
const cell = cellStack[cellStack.length - 1];
|
|
19049
|
+
if (cell) cell.tables.push(table);
|
|
19050
|
+
else orphanTables.push(table);
|
|
19051
|
+
}
|
|
19052
|
+
}
|
|
19053
|
+
} else if (local2 === "header" || local2 === "footer") {
|
|
19054
|
+
const sub = ctrlSubStack[ctrlSubStack.length - 1];
|
|
19055
|
+
if (sub) {
|
|
19056
|
+
ctrlSubStack.pop();
|
|
19057
|
+
const joined = sub.texts.join("\n").trim();
|
|
19058
|
+
if (joined) (sub.kind === "header" ? headerTexts : footerTexts).push(joined);
|
|
19059
|
+
}
|
|
19060
|
+
}
|
|
19061
|
+
continue;
|
|
19012
19062
|
}
|
|
19013
|
-
|
|
19014
|
-
|
|
19015
|
-
|
|
19016
|
-
|
|
19017
|
-
|
|
19018
|
-
|
|
19019
|
-
|
|
19020
|
-
|
|
19021
|
-
|
|
19063
|
+
const qname = openName;
|
|
19064
|
+
const local = localOf(qname);
|
|
19065
|
+
const attrsRaw = m[3] || "";
|
|
19066
|
+
const isSelfClose = selfClose === "/";
|
|
19067
|
+
const contentStart = m.index + full.length;
|
|
19068
|
+
if (isSelfClose) {
|
|
19069
|
+
if (local === "t") {
|
|
19070
|
+
const para = owningPara();
|
|
19071
|
+
if (para) para.tRanges.push({ contentStart: m.index, contentEnd: m.index + full.length, selfClosing: true, prefix: prefixOf(qname) });
|
|
19072
|
+
} else if (local === "tab" || local === "fwSpace" || local === "hwSpace" || local === "br" || local === "lineBreak") {
|
|
19073
|
+
if (!pendingT) {
|
|
19074
|
+
const para = owningPara();
|
|
19075
|
+
if (para) para.text += " ";
|
|
19076
|
+
}
|
|
19077
|
+
} else if (local === "run" || local === "r") {
|
|
19078
|
+
const para = owningPara();
|
|
19079
|
+
if (para && !para.selfCloseRun) para.selfCloseRun = { start: m.index, end: m.index + full.length };
|
|
19080
|
+
} else if (local === "cellAddr") {
|
|
19081
|
+
const cell = cellStack[cellStack.length - 1];
|
|
19082
|
+
if (cell && insideCurrentTable(stack, tableStack)) {
|
|
19083
|
+
const ca = parseInt(getAttr2(attrsRaw, "colAddr") || "", 10);
|
|
19084
|
+
const ra = parseInt(getAttr2(attrsRaw, "rowAddr") || "", 10);
|
|
19085
|
+
if (!isNaN(ca)) cell.colAddr = ca;
|
|
19086
|
+
if (!isNaN(ra)) cell.rowAddr = ra;
|
|
19087
|
+
}
|
|
19088
|
+
} else if (local === "cellSpan") {
|
|
19089
|
+
const cell = cellStack[cellStack.length - 1];
|
|
19090
|
+
if (cell && insideCurrentTable(stack, tableStack)) {
|
|
19091
|
+
const cs = parseInt(getAttr2(attrsRaw, "colSpan") || "1", 10);
|
|
19092
|
+
const rs = parseInt(getAttr2(attrsRaw, "rowSpan") || "1", 10);
|
|
19093
|
+
cell.colSpan = isNaN(cs) || cs < 1 ? 1 : cs;
|
|
19094
|
+
cell.rowSpan = isNaN(rs) || rs < 1 ? 1 : rs;
|
|
19095
|
+
}
|
|
19096
|
+
}
|
|
19097
|
+
continue;
|
|
19022
19098
|
}
|
|
19023
|
-
|
|
19024
|
-
|
|
19025
|
-
|
|
19026
|
-
|
|
19027
|
-
|
|
19028
|
-
|
|
19029
|
-
|
|
19030
|
-
|
|
19031
|
-
|
|
19032
|
-
|
|
19033
|
-
|
|
19034
|
-
|
|
19035
|
-
|
|
19036
|
-
|
|
19037
|
-
|
|
19038
|
-
|
|
19039
|
-
|
|
19040
|
-
|
|
19041
|
-
|
|
19042
|
-
|
|
19043
|
-
|
|
19044
|
-
|
|
19045
|
-
|
|
19046
|
-
|
|
19047
|
-
|
|
19048
|
-
|
|
19049
|
-
|
|
19050
|
-
|
|
19051
|
-
|
|
19052
|
-
if (
|
|
19053
|
-
|
|
19054
|
-
|
|
19055
|
-
if (
|
|
19056
|
-
const
|
|
19057
|
-
|
|
19058
|
-
|
|
19059
|
-
|
|
19060
|
-
|
|
19061
|
-
|
|
19062
|
-
|
|
19063
|
-
|
|
19064
|
-
|
|
19065
|
-
|
|
19066
|
-
|
|
19067
|
-
|
|
19099
|
+
if (local === "t") {
|
|
19100
|
+
const para = owningPara();
|
|
19101
|
+
if (para) pendingT = { para, contentStart };
|
|
19102
|
+
stack.push({ local, qname, contentStart });
|
|
19103
|
+
continue;
|
|
19104
|
+
}
|
|
19105
|
+
stack.push({ local, qname, contentStart });
|
|
19106
|
+
if (local === "p") {
|
|
19107
|
+
const para = {
|
|
19108
|
+
sectionIndex,
|
|
19109
|
+
kind: "excluded",
|
|
19110
|
+
// 분류는 push 직후 스택 기준 (자기 자신 제외)
|
|
19111
|
+
start: m.index,
|
|
19112
|
+
tRanges: [],
|
|
19113
|
+
text: ""
|
|
19114
|
+
};
|
|
19115
|
+
stack.pop();
|
|
19116
|
+
const cls = classifyPara();
|
|
19117
|
+
para.kind = cls.kind;
|
|
19118
|
+
if (cls.inTextbox) para.inTextbox = true;
|
|
19119
|
+
stack.push({ local, qname, contentStart });
|
|
19120
|
+
paraStack.push(para);
|
|
19121
|
+
if (para.kind === "body" || para.kind === "draw") bodyParagraphs.push(para);
|
|
19122
|
+
else if (para.kind === "cell") {
|
|
19123
|
+
const cell = cellStack[cellStack.length - 1];
|
|
19124
|
+
if (cell) cell.paragraphs.push(para);
|
|
19125
|
+
} else if (para.kind === "excluded") {
|
|
19126
|
+
excludedParagraphs.push(para);
|
|
19127
|
+
}
|
|
19128
|
+
} else if (local === "run" || local === "r") {
|
|
19129
|
+
const para = owningPara();
|
|
19130
|
+
if (para && para.runPrefix === void 0) para.runPrefix = prefixOf(qname);
|
|
19131
|
+
} else if (local === "tbl") {
|
|
19132
|
+
const table = {
|
|
19133
|
+
sectionIndex,
|
|
19134
|
+
start: m.index,
|
|
19135
|
+
topLevel: false,
|
|
19136
|
+
rows: [],
|
|
19137
|
+
cellByAnchor: /* @__PURE__ */ new Map()
|
|
19138
|
+
};
|
|
19139
|
+
stack.pop();
|
|
19140
|
+
table.topLevel = isTableTopLevel();
|
|
19141
|
+
stack.push({ local, qname, contentStart });
|
|
19142
|
+
tableStack.push(table);
|
|
19143
|
+
rowStack.push([]);
|
|
19144
|
+
if (table.topLevel) tables.push(table);
|
|
19145
|
+
} else if (local === "tr") {
|
|
19146
|
+
if (rowStack.length > 0) rowStack[rowStack.length - 1] = [];
|
|
19147
|
+
} else if (local === "tc") {
|
|
19148
|
+
cellStack.push({ colSpan: 1, rowSpan: 1, paragraphs: [], tables: [] });
|
|
19149
|
+
} else if (local === "cellAddr" || local === "cellSpan") {
|
|
19150
|
+
const cell = cellStack[cellStack.length - 1];
|
|
19151
|
+
if (cell && insideCurrentTable(stack, tableStack)) {
|
|
19152
|
+
if (local === "cellAddr") {
|
|
19153
|
+
const ca = parseInt(getAttr2(attrsRaw, "colAddr") || "", 10);
|
|
19154
|
+
const ra = parseInt(getAttr2(attrsRaw, "rowAddr") || "", 10);
|
|
19155
|
+
if (!isNaN(ca)) cell.colAddr = ca;
|
|
19156
|
+
if (!isNaN(ra)) cell.rowAddr = ra;
|
|
19157
|
+
} else {
|
|
19158
|
+
const cs = parseInt(getAttr2(attrsRaw, "colSpan") || "1", 10);
|
|
19159
|
+
const rs = parseInt(getAttr2(attrsRaw, "rowSpan") || "1", 10);
|
|
19160
|
+
cell.colSpan = isNaN(cs) || cs < 1 ? 1 : cs;
|
|
19161
|
+
cell.rowSpan = isNaN(rs) || rs < 1 ? 1 : rs;
|
|
19162
|
+
}
|
|
19163
|
+
}
|
|
19164
|
+
} else if (local === "header" || local === "footer") {
|
|
19165
|
+
if (stack.some((f) => f.local === "ctrl")) {
|
|
19166
|
+
ctrlSubStack.push({ kind: local, texts: [] });
|
|
19167
|
+
}
|
|
19168
|
+
} else if (local === "tab" || local === "fwSpace" || local === "hwSpace" || local === "br" || local === "lineBreak") {
|
|
19169
|
+
const para = owningPara();
|
|
19170
|
+
if (para) para.text += " ";
|
|
19068
19171
|
}
|
|
19069
19172
|
}
|
|
19070
|
-
|
|
19071
|
-
|
|
19072
|
-
const
|
|
19073
|
-
|
|
19074
|
-
|
|
19075
|
-
|
|
19076
|
-
|
|
19077
|
-
|
|
19078
|
-
|
|
19079
|
-
|
|
19080
|
-
return result;
|
|
19081
|
-
}
|
|
19082
|
-
function applyTextReplacements(tNodes, originalFull, replacedFull) {
|
|
19083
|
-
if (originalFull === replacedFull) return;
|
|
19084
|
-
if (tNodes.length === 1) {
|
|
19085
|
-
clearChildren(tNodes[0].element);
|
|
19086
|
-
tNodes[0].element.appendChild(
|
|
19087
|
-
tNodes[0].element.ownerDocument.createTextNode(replacedFull)
|
|
19088
|
-
);
|
|
19089
|
-
return;
|
|
19090
|
-
}
|
|
19091
|
-
let diffStart = 0;
|
|
19092
|
-
while (diffStart < originalFull.length && diffStart < replacedFull.length && originalFull[diffStart] === replacedFull[diffStart]) {
|
|
19093
|
-
diffStart++;
|
|
19094
|
-
}
|
|
19095
|
-
let diffEndOrig = originalFull.length;
|
|
19096
|
-
let diffEndRepl = replacedFull.length;
|
|
19097
|
-
while (diffEndOrig > diffStart && diffEndRepl > diffStart && originalFull[diffEndOrig - 1] === replacedFull[diffEndRepl - 1]) {
|
|
19098
|
-
diffEndOrig--;
|
|
19099
|
-
diffEndRepl--;
|
|
19100
|
-
}
|
|
19101
|
-
const newPart = replacedFull.slice(diffStart, diffEndRepl);
|
|
19102
|
-
replaceTextRange(tNodes, diffStart, diffEndOrig, newPart);
|
|
19103
|
-
}
|
|
19104
|
-
|
|
19105
|
-
// src/hwpx/generator.ts
|
|
19106
|
-
import JSZip5 from "jszip";
|
|
19107
|
-
var NS_SECTION = "http://www.hancom.co.kr/hwpml/2011/section";
|
|
19108
|
-
var NS_PARA = "http://www.hancom.co.kr/hwpml/2011/paragraph";
|
|
19109
|
-
var NS_HEAD = "http://www.hancom.co.kr/hwpml/2011/head";
|
|
19110
|
-
var NS_OPF = "http://www.idpf.org/2007/opf/";
|
|
19111
|
-
var NS_HPF = "http://www.hancom.co.kr/schema/2011/hpf";
|
|
19112
|
-
var NS_OCF = "urn:oasis:names:tc:opendocument:xmlns:container";
|
|
19113
|
-
var CHAR_NORMAL = 0;
|
|
19114
|
-
var CHAR_BOLD = 1;
|
|
19115
|
-
var CHAR_ITALIC = 2;
|
|
19116
|
-
var CHAR_BOLD_ITALIC = 3;
|
|
19117
|
-
var CHAR_CODE = 4;
|
|
19118
|
-
var CHAR_H1 = 5;
|
|
19119
|
-
var CHAR_H2 = 6;
|
|
19120
|
-
var CHAR_H3 = 7;
|
|
19121
|
-
var CHAR_H4 = 8;
|
|
19122
|
-
var CHAR_TABLE_HEADER = 9;
|
|
19123
|
-
var CHAR_QUOTE = 10;
|
|
19124
|
-
var PARA_NORMAL = 0;
|
|
19125
|
-
var PARA_H1 = 1;
|
|
19126
|
-
var PARA_H2 = 2;
|
|
19127
|
-
var PARA_H3 = 3;
|
|
19128
|
-
var PARA_H4 = 4;
|
|
19129
|
-
var PARA_CODE = 5;
|
|
19130
|
-
var PARA_QUOTE = 6;
|
|
19131
|
-
var PARA_LIST = 7;
|
|
19132
|
-
var DEFAULT_TEXT_COLOR = "#000000";
|
|
19133
|
-
function resolveTheme(theme) {
|
|
19134
|
-
return {
|
|
19135
|
-
h1: theme?.headingColors?.[1] ?? DEFAULT_TEXT_COLOR,
|
|
19136
|
-
h2: theme?.headingColors?.[2] ?? DEFAULT_TEXT_COLOR,
|
|
19137
|
-
h3: theme?.headingColors?.[3] ?? DEFAULT_TEXT_COLOR,
|
|
19138
|
-
h4: theme?.headingColors?.[4] ?? theme?.headingColors?.[3] ?? DEFAULT_TEXT_COLOR,
|
|
19139
|
-
body: theme?.bodyColor ?? DEFAULT_TEXT_COLOR,
|
|
19140
|
-
quote: theme?.quoteColor ?? DEFAULT_TEXT_COLOR,
|
|
19141
|
-
/** quoteColor가 명시되었는지 — blockquote charPr 분기에 사용 (baseline 호환) */
|
|
19142
|
-
hasQuoteOption: theme?.quoteColor !== void 0,
|
|
19143
|
-
tableHeader: theme?.tableHeaderColor ?? theme?.bodyColor ?? DEFAULT_TEXT_COLOR,
|
|
19144
|
-
tableHeaderBold: !!theme?.tableHeaderBold
|
|
19173
|
+
for (const para of bodyParagraphs) fillRunInsertPos(para, xml);
|
|
19174
|
+
for (const para of excludedParagraphs) fillRunInsertPos(para, xml);
|
|
19175
|
+
const fillTableInsertPos = (table, depth = 0) => {
|
|
19176
|
+
if (depth > 16) return;
|
|
19177
|
+
for (const row of table.rows) {
|
|
19178
|
+
for (const cell of row) {
|
|
19179
|
+
for (const para of cell.paragraphs) fillRunInsertPos(para, xml);
|
|
19180
|
+
for (const nested of cell.tables) fillTableInsertPos(nested, depth + 1);
|
|
19181
|
+
}
|
|
19182
|
+
}
|
|
19145
19183
|
};
|
|
19184
|
+
for (const table of tables) fillTableInsertPos(table);
|
|
19185
|
+
for (const table of orphanTables) fillTableInsertPos(table);
|
|
19186
|
+
return { sectionIndex, xml, bodyParagraphs, tables, headerTexts, footerTexts, excludedParagraphs, orphanTables };
|
|
19146
19187
|
}
|
|
19147
|
-
|
|
19148
|
-
const
|
|
19149
|
-
const
|
|
19150
|
-
|
|
19151
|
-
const zip = new JSZip5();
|
|
19152
|
-
zip.file("mimetype", "application/hwp+zip", { compression: "STORE" });
|
|
19153
|
-
zip.file("META-INF/container.xml", generateContainerXml());
|
|
19154
|
-
zip.file("Contents/content.hpf", generateManifest());
|
|
19155
|
-
zip.file("Contents/header.xml", generateHeaderXml(theme));
|
|
19156
|
-
zip.file("Contents/section0.xml", sectionXml);
|
|
19157
|
-
zip.file("Preview/PrvText.txt", buildPrvText(blocks));
|
|
19158
|
-
return await zip.generateAsync({ type: "arraybuffer" });
|
|
19188
|
+
function getAttr2(attrsRaw, name) {
|
|
19189
|
+
const re = new RegExp(`(?:^|\\s)${name}\\s*=\\s*(?:"([^"]*)"|'([^']*)')`);
|
|
19190
|
+
const m = attrsRaw.match(re);
|
|
19191
|
+
return m ? m[1] ?? m[2] : void 0;
|
|
19159
19192
|
}
|
|
19160
|
-
function
|
|
19161
|
-
|
|
19162
|
-
let
|
|
19163
|
-
|
|
19164
|
-
|
|
19165
|
-
if (
|
|
19166
|
-
lines.push(text);
|
|
19167
|
-
bytes += text.length * 3;
|
|
19168
|
-
if (bytes > 1024) break;
|
|
19193
|
+
function insideCurrentTable(stack, tableStack) {
|
|
19194
|
+
if (tableStack.length === 0) return false;
|
|
19195
|
+
for (let i = stack.length - 1; i >= 0; i--) {
|
|
19196
|
+
const l = stack[i].local;
|
|
19197
|
+
if (l === "tc") return true;
|
|
19198
|
+
if (l === "tbl") return false;
|
|
19169
19199
|
}
|
|
19170
|
-
return
|
|
19200
|
+
return false;
|
|
19171
19201
|
}
|
|
19172
|
-
function
|
|
19173
|
-
|
|
19174
|
-
const
|
|
19175
|
-
|
|
19176
|
-
|
|
19177
|
-
|
|
19178
|
-
|
|
19179
|
-
|
|
19180
|
-
|
|
19202
|
+
function fillRunInsertPos(para, xml) {
|
|
19203
|
+
if (para.tRanges.length > 0) return;
|
|
19204
|
+
const pEnd = findElementEnd(xml, para.start);
|
|
19205
|
+
if (pEnd < 0) return;
|
|
19206
|
+
const slice = xml.slice(para.start, pEnd);
|
|
19207
|
+
const runOpen = slice.match(/<((?:[A-Za-z0-9_]+:)?run)(?:\s(?:"[^"]*"|'[^']*'|[^>"'])*?)?(\/?)>/);
|
|
19208
|
+
if (!runOpen || runOpen.index === void 0) return;
|
|
19209
|
+
if (runOpen[2] === "/") return;
|
|
19210
|
+
const qname = runOpen[1];
|
|
19211
|
+
const closeIdx = slice.indexOf(`</${qname}>`, runOpen.index);
|
|
19212
|
+
if (closeIdx < 0) return;
|
|
19213
|
+
para.runInsertPos = para.start + closeIdx;
|
|
19214
|
+
para.runPrefix = prefixOf(qname);
|
|
19215
|
+
}
|
|
19216
|
+
function findElementEnd(xml, start) {
|
|
19217
|
+
const open = xml.slice(start).match(/^<([^\s/>!?]+)/);
|
|
19218
|
+
if (!open) return -1;
|
|
19219
|
+
const qname = open[1];
|
|
19220
|
+
const re = new RegExp(`<${qname}(?=[\\s/>])(?:"[^"]*"|'[^']*'|[^>"'])*?(/?)>|</${qname}\\s*>`, "g");
|
|
19221
|
+
re.lastIndex = start;
|
|
19222
|
+
let depth = 0;
|
|
19223
|
+
let mm;
|
|
19224
|
+
while ((mm = re.exec(xml)) !== null) {
|
|
19225
|
+
if (mm[0].startsWith("</")) {
|
|
19226
|
+
depth--;
|
|
19227
|
+
if (depth === 0) return mm.index + mm[0].length;
|
|
19228
|
+
} else if (mm[1] !== "/") {
|
|
19229
|
+
depth++;
|
|
19181
19230
|
}
|
|
19182
|
-
|
|
19183
|
-
|
|
19184
|
-
|
|
19185
|
-
|
|
19186
|
-
|
|
19187
|
-
|
|
19188
|
-
|
|
19189
|
-
|
|
19190
|
-
|
|
19231
|
+
}
|
|
19232
|
+
return -1;
|
|
19233
|
+
}
|
|
19234
|
+
function finalizeTable(table) {
|
|
19235
|
+
const hasAddr = table.rows.some((row) => row.some((c) => c.colAddr !== void 0 && c.rowAddr !== void 0));
|
|
19236
|
+
if (hasAddr) {
|
|
19237
|
+
for (const row of table.rows) {
|
|
19238
|
+
for (const cell of row) {
|
|
19239
|
+
if (cell.rowAddr !== void 0 && cell.colAddr !== void 0) {
|
|
19240
|
+
table.cellByAnchor.set(`${cell.rowAddr},${cell.colAddr}`, cell);
|
|
19241
|
+
}
|
|
19191
19242
|
}
|
|
19192
|
-
if (i < lines.length) i++;
|
|
19193
|
-
blocks.push({ type: "code_block", text: codeLines.join("\n"), lang });
|
|
19194
|
-
continue;
|
|
19195
|
-
}
|
|
19196
|
-
if (/^(\*{3,}|-{3,}|_{3,})\s*$/.test(line.trim())) {
|
|
19197
|
-
blocks.push({ type: "hr" });
|
|
19198
|
-
i++;
|
|
19199
|
-
continue;
|
|
19200
|
-
}
|
|
19201
|
-
const headingMatch = line.match(/^(#{1,6})\s+(.+)$/);
|
|
19202
|
-
if (headingMatch) {
|
|
19203
|
-
blocks.push({ type: "heading", text: headingMatch[2].trim(), level: headingMatch[1].length });
|
|
19204
|
-
i++;
|
|
19205
|
-
continue;
|
|
19206
19243
|
}
|
|
19207
|
-
|
|
19208
|
-
|
|
19209
|
-
|
|
19210
|
-
|
|
19211
|
-
|
|
19212
|
-
|
|
19213
|
-
|
|
19244
|
+
return;
|
|
19245
|
+
}
|
|
19246
|
+
const numRows = table.rows.length;
|
|
19247
|
+
const occupied = Array.from({ length: numRows }, () => []);
|
|
19248
|
+
for (let rowIdx = 0; rowIdx < numRows; rowIdx++) {
|
|
19249
|
+
let colIdx = 0;
|
|
19250
|
+
for (const cell of table.rows[rowIdx]) {
|
|
19251
|
+
while (occupied[rowIdx][colIdx]) colIdx++;
|
|
19252
|
+
cell.rowAddr = rowIdx;
|
|
19253
|
+
cell.colAddr = colIdx;
|
|
19254
|
+
table.cellByAnchor.set(`${rowIdx},${colIdx}`, cell);
|
|
19255
|
+
for (let r = rowIdx; r < Math.min(rowIdx + cell.rowSpan, numRows); r++) {
|
|
19256
|
+
for (let c = colIdx; c < colIdx + cell.colSpan; c++) {
|
|
19257
|
+
occupied[r][c] = true;
|
|
19214
19258
|
}
|
|
19215
|
-
const cells = row.split("|").slice(1, -1).map((c) => c.trim());
|
|
19216
|
-
if (cells.length > 0) tableRows.push(cells);
|
|
19217
|
-
i++;
|
|
19218
19259
|
}
|
|
19219
|
-
|
|
19220
|
-
continue;
|
|
19260
|
+
colIdx += cell.colSpan;
|
|
19221
19261
|
}
|
|
19222
|
-
|
|
19223
|
-
|
|
19224
|
-
|
|
19225
|
-
|
|
19226
|
-
|
|
19262
|
+
}
|
|
19263
|
+
}
|
|
19264
|
+
function buildParagraphSplices(para, newText, xml) {
|
|
19265
|
+
if (newText && xml) {
|
|
19266
|
+
const orig = paraTText(para, xml);
|
|
19267
|
+
if (orig && orig.trim() !== "") {
|
|
19268
|
+
const lead = orig.match(/^\s*/)[0];
|
|
19269
|
+
const trail = orig.match(/\s*$/)[0];
|
|
19270
|
+
if ((lead || trail) && newText.trim() !== "") {
|
|
19271
|
+
newText = lead + newText.replace(/^\s+|\s+$/g, "") + trail;
|
|
19227
19272
|
}
|
|
19228
|
-
for (const ql of quoteLines) {
|
|
19229
|
-
blocks.push({ type: "blockquote", text: ql.trim() || "" });
|
|
19230
|
-
}
|
|
19231
|
-
continue;
|
|
19232
|
-
}
|
|
19233
|
-
const listMatch = line.match(/^(\s*)([-*+]|\d+[.)]) (.+)$/);
|
|
19234
|
-
if (listMatch) {
|
|
19235
|
-
const indent = Math.floor(listMatch[1].length / 2);
|
|
19236
|
-
const ordered = /\d/.test(listMatch[2]);
|
|
19237
|
-
blocks.push({ type: "list_item", text: listMatch[3].trim(), ordered, indent });
|
|
19238
|
-
i++;
|
|
19239
|
-
continue;
|
|
19240
19273
|
}
|
|
19241
|
-
blocks.push({ type: "paragraph", text: line.trim() });
|
|
19242
|
-
i++;
|
|
19243
19274
|
}
|
|
19244
|
-
|
|
19245
|
-
|
|
19246
|
-
|
|
19247
|
-
|
|
19248
|
-
|
|
19249
|
-
|
|
19250
|
-
|
|
19251
|
-
const regex = /(`[^`]+`|\*{3}[^*]+\*{3}|\*{2}[^*]+\*{2}|\*[^*]+\*|_{2}[^_]+_{2}|_[^_]+_)/g;
|
|
19252
|
-
let lastIdx = 0;
|
|
19253
|
-
for (const match of text.matchAll(regex)) {
|
|
19254
|
-
const idx = match.index;
|
|
19255
|
-
if (idx > lastIdx) {
|
|
19256
|
-
spans.push({ text: text.slice(lastIdx, idx), bold: false, italic: false, code: false });
|
|
19257
|
-
}
|
|
19258
|
-
const raw = match[0];
|
|
19259
|
-
if (raw.startsWith("`")) {
|
|
19260
|
-
spans.push({ text: raw.slice(1, -1), bold: false, italic: false, code: true });
|
|
19261
|
-
} else if (raw.startsWith("***") || raw.startsWith("___")) {
|
|
19262
|
-
spans.push({ text: raw.slice(3, -3), bold: true, italic: true, code: false });
|
|
19263
|
-
} else if (raw.startsWith("**") || raw.startsWith("__")) {
|
|
19264
|
-
spans.push({ text: raw.slice(2, -2), bold: true, italic: false, code: false });
|
|
19275
|
+
const escaped = escapeXmlText(newText);
|
|
19276
|
+
if (para.tRanges.length > 0) {
|
|
19277
|
+
const splices = [];
|
|
19278
|
+
const first = para.tRanges[0];
|
|
19279
|
+
if (first.selfClosing) {
|
|
19280
|
+
const prefix = first.prefix ? first.prefix + ":" : "";
|
|
19281
|
+
splices.push({ start: first.contentStart, end: first.contentEnd, replacement: `<${prefix}t>${escaped}</${prefix}t>` });
|
|
19265
19282
|
} else {
|
|
19266
|
-
|
|
19283
|
+
splices.push({ start: first.contentStart, end: first.contentEnd, replacement: escaped });
|
|
19267
19284
|
}
|
|
19268
|
-
|
|
19285
|
+
for (let i = 1; i < para.tRanges.length; i++) {
|
|
19286
|
+
const r = para.tRanges[i];
|
|
19287
|
+
if (!r.selfClosing && r.contentStart < r.contentEnd) {
|
|
19288
|
+
splices.push({ start: r.contentStart, end: r.contentEnd, replacement: "" });
|
|
19289
|
+
}
|
|
19290
|
+
}
|
|
19291
|
+
return splices;
|
|
19269
19292
|
}
|
|
19270
|
-
if (
|
|
19271
|
-
|
|
19293
|
+
if (para.runInsertPos !== void 0) {
|
|
19294
|
+
if (!newText) return [];
|
|
19295
|
+
const prefix = para.runPrefix ? para.runPrefix + ":" : "";
|
|
19296
|
+
return [{ start: para.runInsertPos, end: para.runInsertPos, replacement: `<${prefix}t>${escaped}</${prefix}t>` }];
|
|
19272
19297
|
}
|
|
19273
|
-
if (
|
|
19274
|
-
|
|
19298
|
+
if (para.selfCloseRun && xml) {
|
|
19299
|
+
if (!newText) return [];
|
|
19300
|
+
const { start, end } = para.selfCloseRun;
|
|
19301
|
+
const tag = xml.slice(start, end);
|
|
19302
|
+
const qm = tag.match(/^<([^\s/>]+)/);
|
|
19303
|
+
if (!qm || !tag.endsWith("/>")) return null;
|
|
19304
|
+
const qname = qm[1];
|
|
19305
|
+
const colon = qname.indexOf(":");
|
|
19306
|
+
const prefix = colon >= 0 ? qname.slice(0, colon) + ":" : "";
|
|
19307
|
+
const opened = tag.slice(0, tag.length - 2).trimEnd() + ">";
|
|
19308
|
+
return [{ start, end, replacement: `${opened}<${prefix}t>${escaped}</${prefix}t></${qname}>` }];
|
|
19275
19309
|
}
|
|
19276
|
-
return
|
|
19277
|
-
}
|
|
19278
|
-
function spanToCharPrId(span) {
|
|
19279
|
-
if (span.code) return CHAR_CODE;
|
|
19280
|
-
if (span.bold && span.italic) return CHAR_BOLD_ITALIC;
|
|
19281
|
-
if (span.bold) return CHAR_BOLD;
|
|
19282
|
-
if (span.italic) return CHAR_ITALIC;
|
|
19283
|
-
return CHAR_NORMAL;
|
|
19284
|
-
}
|
|
19285
|
-
function escapeXml(text) {
|
|
19286
|
-
return text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
19287
|
-
}
|
|
19288
|
-
function generateRuns(text, defaultCharPr = CHAR_NORMAL) {
|
|
19289
|
-
const spans = parseInlineMarkdown(text);
|
|
19290
|
-
return spans.map((span) => {
|
|
19291
|
-
const charId = span.code || span.bold || span.italic ? spanToCharPrId(span) : defaultCharPr;
|
|
19292
|
-
return `<hp:run charPrIDRef="${charId}"><hp:t>${escapeXml(span.text)}</hp:t></hp:run>`;
|
|
19293
|
-
}).join("");
|
|
19310
|
+
return newText ? null : [];
|
|
19294
19311
|
}
|
|
19295
|
-
function
|
|
19296
|
-
|
|
19297
|
-
|
|
19312
|
+
function paraTText(para, xml) {
|
|
19313
|
+
let text = "";
|
|
19314
|
+
for (const t of para.tRanges) {
|
|
19315
|
+
if (t.selfClosing) continue;
|
|
19316
|
+
const raw = xml.slice(t.contentStart, t.contentEnd);
|
|
19317
|
+
if (/[<&]/.test(raw)) return null;
|
|
19318
|
+
text += raw;
|
|
19298
19319
|
}
|
|
19299
|
-
|
|
19300
|
-
return `<hp:p paraPrIDRef="${paraPrId}" styleIDRef="0">${runs}</hp:p>`;
|
|
19320
|
+
return text;
|
|
19301
19321
|
}
|
|
19302
|
-
function
|
|
19303
|
-
|
|
19304
|
-
|
|
19305
|
-
|
|
19306
|
-
|
|
19322
|
+
function paraTextPureT(para, xml) {
|
|
19323
|
+
let len = 0;
|
|
19324
|
+
for (const t of para.tRanges) {
|
|
19325
|
+
if (t.selfClosing) continue;
|
|
19326
|
+
len += tContentToText(xml.slice(t.contentStart, t.contentEnd)).length;
|
|
19327
|
+
}
|
|
19328
|
+
return len === para.text.length;
|
|
19307
19329
|
}
|
|
19308
|
-
function
|
|
19309
|
-
if (
|
|
19310
|
-
|
|
19311
|
-
|
|
19312
|
-
|
|
19330
|
+
function buildRangeSplices(para, xml, start, end, replacement) {
|
|
19331
|
+
if (start < 0 || end < start) return null;
|
|
19332
|
+
const segs = [];
|
|
19333
|
+
let offset = 0;
|
|
19334
|
+
for (const t of para.tRanges) {
|
|
19335
|
+
if (t.selfClosing) continue;
|
|
19336
|
+
const raw = xml.slice(t.contentStart, t.contentEnd);
|
|
19337
|
+
if (/[<&]/.test(raw)) return null;
|
|
19338
|
+
segs.push({ contentStart: t.contentStart, from: offset, to: offset + raw.length });
|
|
19339
|
+
offset += raw.length;
|
|
19340
|
+
}
|
|
19341
|
+
if (segs.length === 0 || end > offset) return null;
|
|
19342
|
+
const escaped = escapeXmlText(replacement);
|
|
19343
|
+
if (start === end) {
|
|
19344
|
+
for (const seg of segs) {
|
|
19345
|
+
if (start >= seg.from && start <= seg.to) {
|
|
19346
|
+
const at = seg.contentStart + (start - seg.from);
|
|
19347
|
+
return [{ start: at, end: at, replacement: escaped }];
|
|
19348
|
+
}
|
|
19349
|
+
}
|
|
19350
|
+
return null;
|
|
19351
|
+
}
|
|
19352
|
+
const splices = [];
|
|
19353
|
+
let placed = false;
|
|
19354
|
+
for (const seg of segs) {
|
|
19355
|
+
if (seg.to <= start || seg.from >= end) continue;
|
|
19356
|
+
const localStart = Math.max(seg.from, start) - seg.from;
|
|
19357
|
+
const localEnd = Math.min(seg.to, end) - seg.from;
|
|
19358
|
+
splices.push({
|
|
19359
|
+
start: seg.contentStart + localStart,
|
|
19360
|
+
end: seg.contentStart + localEnd,
|
|
19361
|
+
replacement: placed ? "" : escaped
|
|
19362
|
+
});
|
|
19363
|
+
placed = true;
|
|
19364
|
+
}
|
|
19365
|
+
return placed ? splices : null;
|
|
19313
19366
|
}
|
|
19314
|
-
function
|
|
19315
|
-
|
|
19316
|
-
|
|
19317
|
-
|
|
19318
|
-
|
|
19319
|
-
|
|
19320
|
-
|
|
19367
|
+
function applySplices(xml, splices) {
|
|
19368
|
+
const sorted = [...splices].sort((a, b) => a.start - b.start);
|
|
19369
|
+
for (let i = 1; i < sorted.length; i++) {
|
|
19370
|
+
if (sorted[i].start < sorted[i - 1].end) {
|
|
19371
|
+
throw new Error("\uC18C\uC2A4\uB9F5 splice \uBC94\uC704 \uACB9\uCE68 \u2014 \uB0B4\uBD80 \uC624\uB958");
|
|
19372
|
+
}
|
|
19373
|
+
}
|
|
19374
|
+
let result = xml;
|
|
19375
|
+
for (let i = sorted.length - 1; i >= 0; i--) {
|
|
19376
|
+
const s = sorted[i];
|
|
19377
|
+
result = result.slice(0, s.start) + s.replacement + result.slice(s.end);
|
|
19378
|
+
}
|
|
19379
|
+
return result;
|
|
19321
19380
|
}
|
|
19322
|
-
|
|
19323
|
-
|
|
19324
|
-
|
|
19325
|
-
|
|
19326
|
-
|
|
19327
|
-
|
|
19328
|
-
|
|
19329
|
-
|
|
19330
|
-
|
|
19331
|
-
<opf:itemref idref="section0" linear="yes"/>
|
|
19332
|
-
</opf:spine>
|
|
19333
|
-
</opf:package>`;
|
|
19381
|
+
|
|
19382
|
+
// src/roundtrip/zip-patch.ts
|
|
19383
|
+
import { deflateRawSync } from "zlib";
|
|
19384
|
+
var EOCD_SIG = 101010256;
|
|
19385
|
+
var CD_SIG = 33639248;
|
|
19386
|
+
var LOCAL_SIG = 67324752;
|
|
19387
|
+
var ZIP64_EOCD_LOC_SIG = 117853008;
|
|
19388
|
+
function copyBytes(buf, start, end) {
|
|
19389
|
+
return new Uint8Array(buf.subarray(start, end));
|
|
19334
19390
|
}
|
|
19335
|
-
function
|
|
19336
|
-
const
|
|
19337
|
-
const
|
|
19338
|
-
|
|
19339
|
-
|
|
19340
|
-
|
|
19341
|
-
|
|
19342
|
-
|
|
19343
|
-
|
|
19344
|
-
|
|
19345
|
-
|
|
19391
|
+
function parseCentralDirectory(buf) {
|
|
19392
|
+
const view = new DataView(buf.buffer, buf.byteOffset, buf.byteLength);
|
|
19393
|
+
const minEocd = Math.max(0, buf.length - 22 - 65535);
|
|
19394
|
+
let eocdOffset = -1;
|
|
19395
|
+
for (let i = buf.length - 22; i >= minEocd; i--) {
|
|
19396
|
+
if (view.getUint32(i, true) === EOCD_SIG && i + 22 + view.getUint16(i + 20, true) === buf.length) {
|
|
19397
|
+
eocdOffset = i;
|
|
19398
|
+
break;
|
|
19399
|
+
}
|
|
19400
|
+
}
|
|
19401
|
+
if (eocdOffset < 0) {
|
|
19402
|
+
for (let i = buf.length - 22; i >= minEocd; i--) {
|
|
19403
|
+
if (view.getUint32(i, true) !== EOCD_SIG) continue;
|
|
19404
|
+
if (i + 22 + view.getUint16(i + 20, true) > buf.length) continue;
|
|
19405
|
+
const cand = view.getUint32(i + 16, true);
|
|
19406
|
+
if (cand < buf.length - 4 && view.getUint32(cand, true) === CD_SIG) {
|
|
19407
|
+
eocdOffset = i;
|
|
19408
|
+
break;
|
|
19409
|
+
}
|
|
19410
|
+
}
|
|
19411
|
+
}
|
|
19412
|
+
if (eocdOffset < 0) throw new KordocError("ZIP EOCD\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4");
|
|
19413
|
+
const totalEntries = view.getUint16(eocdOffset + 10, true);
|
|
19414
|
+
const cdSize = view.getUint32(eocdOffset + 12, true);
|
|
19415
|
+
const cdOffset = view.getUint32(eocdOffset + 16, true);
|
|
19416
|
+
if (cdOffset === 4294967295 || totalEntries === 65535) throw new KordocError("ZIP64\uB294 \uC9C0\uC6D0\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4");
|
|
19417
|
+
if (eocdOffset >= 20 && view.getUint32(eocdOffset - 20, true) === ZIP64_EOCD_LOC_SIG) {
|
|
19418
|
+
throw new KordocError("ZIP64\uB294 \uC9C0\uC6D0\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4");
|
|
19419
|
+
}
|
|
19420
|
+
const decoder = new TextDecoder("utf-8");
|
|
19421
|
+
const entries = [];
|
|
19422
|
+
let pos = cdOffset;
|
|
19423
|
+
for (let i = 0; i < totalEntries; i++) {
|
|
19424
|
+
if (view.getUint32(pos, true) !== CD_SIG) throw new KordocError("ZIP Central Directory \uC190\uC0C1");
|
|
19425
|
+
const flags = view.getUint16(pos + 8, true);
|
|
19426
|
+
const method = view.getUint16(pos + 10, true);
|
|
19427
|
+
const crc = view.getUint32(pos + 16, true);
|
|
19428
|
+
const compSize = view.getUint32(pos + 20, true);
|
|
19429
|
+
const uncompSize = view.getUint32(pos + 24, true);
|
|
19430
|
+
const nameLen = view.getUint16(pos + 28, true);
|
|
19431
|
+
const extraLen = view.getUint16(pos + 30, true);
|
|
19432
|
+
const commentLen = view.getUint16(pos + 32, true);
|
|
19433
|
+
const localOffset = view.getUint32(pos + 42, true);
|
|
19434
|
+
if (compSize === 4294967295 || uncompSize === 4294967295 || localOffset === 4294967295) {
|
|
19435
|
+
throw new KordocError("ZIP64\uB294 \uC9C0\uC6D0\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4");
|
|
19436
|
+
}
|
|
19437
|
+
const name = decoder.decode(buf.subarray(pos + 46, pos + 46 + nameLen));
|
|
19438
|
+
const cdEnd = pos + 46 + nameLen + extraLen + commentLen;
|
|
19439
|
+
entries.push({ cdStart: pos, cdEnd, name, flags, method, crc, compSize, uncompSize, localOffset });
|
|
19440
|
+
pos = cdEnd;
|
|
19441
|
+
}
|
|
19442
|
+
return { entries, cdOffset, cdSize, eocdOffset };
|
|
19346
19443
|
}
|
|
19347
|
-
|
|
19348
|
-
const
|
|
19349
|
-
|
|
19350
|
-
|
|
19351
|
-
|
|
19352
|
-
|
|
19353
|
-
|
|
19354
|
-
|
|
19355
|
-
|
|
19356
|
-
|
|
19357
|
-
|
|
19444
|
+
var CRC_TABLE = (() => {
|
|
19445
|
+
const table = new Uint32Array(256);
|
|
19446
|
+
for (let n = 0; n < 256; n++) {
|
|
19447
|
+
let c = n;
|
|
19448
|
+
for (let k = 0; k < 8; k++) c = c & 1 ? 3988292384 ^ c >>> 1 : c >>> 1;
|
|
19449
|
+
table[n] = c >>> 0;
|
|
19450
|
+
}
|
|
19451
|
+
return table;
|
|
19452
|
+
})();
|
|
19453
|
+
function crc32(data) {
|
|
19454
|
+
let crc = 4294967295;
|
|
19455
|
+
for (let i = 0; i < data.length; i++) {
|
|
19456
|
+
crc = CRC_TABLE[(crc ^ data[i]) & 255] ^ crc >>> 8;
|
|
19457
|
+
}
|
|
19458
|
+
return (crc ^ 4294967295) >>> 0;
|
|
19358
19459
|
}
|
|
19359
|
-
function
|
|
19360
|
-
|
|
19361
|
-
|
|
19362
|
-
|
|
19363
|
-
|
|
19364
|
-
<hh:fontfaces itemCnt="7">
|
|
19365
|
-
<hh:fontface lang="HANGUL" fontCnt="3">
|
|
19366
|
-
<hh:font id="0" face="\uD568\uCD08\uB86C\uBC14\uD0D5" type="TTF" isEmbedded="0">
|
|
19367
|
-
<hh:typeInfo familyType="FCAT_GOTHIC" weight="6" proportion="4" contrast="0" strokeVariation="1" armStyle="1" letterform="1" midline="1" xHeight="1"/>
|
|
19368
|
-
</hh:font>
|
|
19369
|
-
<hh:font id="1" face="\uD568\uCD08\uB86C\uB3CB\uC6C0" type="TTF" isEmbedded="0">
|
|
19370
|
-
<hh:typeInfo familyType="FCAT_GOTHIC" weight="6" proportion="4" contrast="0" strokeVariation="1" armStyle="1" letterform="1" midline="1" xHeight="1"/>
|
|
19371
|
-
</hh:font>
|
|
19372
|
-
<hh:font id="2" face="HY\uACAC\uACE0\uB515" type="TTF" isEmbedded="0">
|
|
19373
|
-
<hh:typeInfo familyType="FCAT_GOTHIC" weight="9" proportion="0" contrast="0" strokeVariation="1" armStyle="1" letterform="1" midline="1" xHeight="1"/>
|
|
19374
|
-
</hh:font>
|
|
19375
|
-
</hh:fontface>
|
|
19376
|
-
<hh:fontface lang="LATIN" fontCnt="3">
|
|
19377
|
-
<hh:font id="0" face="Times New Roman" type="TTF" isEmbedded="0">
|
|
19378
|
-
<hh:typeInfo familyType="FCAT_OLDSTYLE" weight="5" proportion="4" contrast="2" strokeVariation="0" armStyle="0" letterform="0" midline="0" xHeight="4"/>
|
|
19379
|
-
</hh:font>
|
|
19380
|
-
<hh:font id="1" face="Consolas" type="TTF" isEmbedded="0">
|
|
19381
|
-
<hh:typeInfo familyType="FCAT_MODERN" weight="5" proportion="0" contrast="0" strokeVariation="0" armStyle="0" letterform="0" midline="0" xHeight="0"/>
|
|
19382
|
-
</hh:font>
|
|
19383
|
-
<hh:font id="2" face="Arial Black" type="TTF" isEmbedded="0">
|
|
19384
|
-
<hh:typeInfo familyType="FCAT_GOTHIC" weight="9" proportion="0" contrast="0" strokeVariation="0" armStyle="0" letterform="0" midline="0" xHeight="0"/>
|
|
19385
|
-
</hh:font>
|
|
19386
|
-
</hh:fontface>
|
|
19387
|
-
<hh:fontface lang="HANJA" fontCnt="1">
|
|
19388
|
-
<hh:font id="0" face="\uD568\uCD08\uB86C\uBC14\uD0D5" type="TTF" isEmbedded="0">
|
|
19389
|
-
<hh:typeInfo familyType="FCAT_GOTHIC" weight="6" proportion="4" contrast="0" strokeVariation="1" armStyle="1" letterform="1" midline="1" xHeight="1"/>
|
|
19390
|
-
</hh:font>
|
|
19391
|
-
</hh:fontface>
|
|
19392
|
-
<hh:fontface lang="JAPANESE" fontCnt="1">
|
|
19393
|
-
<hh:font id="0" face="\uAD74\uB9BC" type="TTF" isEmbedded="0">
|
|
19394
|
-
<hh:typeInfo familyType="FCAT_GOTHIC" weight="6" proportion="0" contrast="0" strokeVariation="1" armStyle="1" letterform="1" midline="1" xHeight="1"/>
|
|
19395
|
-
</hh:font>
|
|
19396
|
-
</hh:fontface>
|
|
19397
|
-
<hh:fontface lang="OTHER" fontCnt="1">
|
|
19398
|
-
<hh:font id="0" face="\uAD74\uB9BC" type="TTF" isEmbedded="0">
|
|
19399
|
-
<hh:typeInfo familyType="FCAT_GOTHIC" weight="6" proportion="0" contrast="0" strokeVariation="1" armStyle="1" letterform="1" midline="1" xHeight="1"/>
|
|
19400
|
-
</hh:font>
|
|
19401
|
-
</hh:fontface>
|
|
19402
|
-
<hh:fontface lang="SYMBOL" fontCnt="1">
|
|
19403
|
-
<hh:font id="0" face="Symbol" type="TTF" isEmbedded="0">
|
|
19404
|
-
<hh:typeInfo familyType="FCAT_GOTHIC" weight="6" proportion="0" contrast="0" strokeVariation="1" armStyle="1" letterform="1" midline="1" xHeight="1"/>
|
|
19405
|
-
</hh:font>
|
|
19406
|
-
</hh:fontface>
|
|
19407
|
-
<hh:fontface lang="USER" fontCnt="1">
|
|
19408
|
-
<hh:font id="0" face="\uAD74\uB9BC" type="TTF" isEmbedded="0">
|
|
19409
|
-
<hh:typeInfo familyType="FCAT_GOTHIC" weight="6" proportion="0" contrast="0" strokeVariation="1" armStyle="1" letterform="1" midline="1" xHeight="1"/>
|
|
19410
|
-
</hh:font>
|
|
19411
|
-
</hh:fontface>
|
|
19412
|
-
</hh:fontfaces>
|
|
19413
|
-
<hh:borderFills itemCnt="2">
|
|
19414
|
-
<hh:borderFill id="0" threeD="0" shadow="0" centerLine="0" breakCellSeparateLine="0">
|
|
19415
|
-
<hh:slash type="NONE" Crooked="0" isCounter="0"/>
|
|
19416
|
-
<hh:backSlash type="NONE" Crooked="0" isCounter="0"/>
|
|
19417
|
-
<hh:leftBorder type="NONE" width="0.1 mm" color="#000000"/>
|
|
19418
|
-
<hh:rightBorder type="NONE" width="0.1 mm" color="#000000"/>
|
|
19419
|
-
<hh:topBorder type="NONE" width="0.1 mm" color="#000000"/>
|
|
19420
|
-
<hh:bottomBorder type="NONE" width="0.1 mm" color="#000000"/>
|
|
19421
|
-
<hh:diagonal type="NONE" width="0.1 mm" color="#000000"/>
|
|
19422
|
-
<hh:fillInfo/>
|
|
19423
|
-
</hh:borderFill>
|
|
19424
|
-
<hh:borderFill id="1" threeD="0" shadow="0" centerLine="0" breakCellSeparateLine="0">
|
|
19425
|
-
<hh:slash type="NONE" Crooked="0" isCounter="0"/>
|
|
19426
|
-
<hh:backSlash type="NONE" Crooked="0" isCounter="0"/>
|
|
19427
|
-
<hh:leftBorder type="SOLID" width="0.12 mm" color="#000000"/>
|
|
19428
|
-
<hh:rightBorder type="SOLID" width="0.12 mm" color="#000000"/>
|
|
19429
|
-
<hh:topBorder type="SOLID" width="0.12 mm" color="#000000"/>
|
|
19430
|
-
<hh:bottomBorder type="SOLID" width="0.12 mm" color="#000000"/>
|
|
19431
|
-
<hh:diagonal type="NONE" width="0.1 mm" color="#000000"/>
|
|
19432
|
-
<hh:fillInfo/>
|
|
19433
|
-
</hh:borderFill>
|
|
19434
|
-
</hh:borderFills>
|
|
19435
|
-
<hh:charProperties itemCnt="11">
|
|
19436
|
-
${charPr(0, 1e3, false, false, 0, theme.body)}
|
|
19437
|
-
${charPr(1, 1e3, true, false, 0, theme.body)}
|
|
19438
|
-
${charPr(2, 1e3, false, true, 0, theme.body)}
|
|
19439
|
-
${charPr(3, 1e3, true, true, 0, theme.body)}
|
|
19440
|
-
${charPr(4, 900, false, false, 1)}
|
|
19441
|
-
${charPr(5, 1800, true, false, 1, theme.h1)}
|
|
19442
|
-
${charPr(6, 1400, true, false, 1, theme.h2)}
|
|
19443
|
-
${charPr(7, 1200, true, false, 1, theme.h3)}
|
|
19444
|
-
${charPr(8, 1100, true, false, 1, theme.h4)}
|
|
19445
|
-
${charPr(CHAR_TABLE_HEADER, 1e3, theme.tableHeaderBold, false, 0, theme.tableHeader)}
|
|
19446
|
-
${charPr(CHAR_QUOTE, 1e3, false, true, 0, theme.quote)}
|
|
19447
|
-
</hh:charProperties>
|
|
19448
|
-
<hh:tabProperties itemCnt="0"/>
|
|
19449
|
-
<hh:numberings itemCnt="0"/>
|
|
19450
|
-
<hh:bullets itemCnt="0"/>
|
|
19451
|
-
<hh:paraProperties itemCnt="8">
|
|
19452
|
-
${paraPr(0)}
|
|
19453
|
-
${paraPr(1, { align: "LEFT", spaceBefore: 800, spaceAfter: 200, lineSpacing: 180 })}
|
|
19454
|
-
${paraPr(2, { align: "LEFT", spaceBefore: 600, spaceAfter: 150, lineSpacing: 170 })}
|
|
19455
|
-
${paraPr(3, { align: "LEFT", spaceBefore: 400, spaceAfter: 100, lineSpacing: 160 })}
|
|
19456
|
-
${paraPr(4, { align: "LEFT", spaceBefore: 300, spaceAfter: 100, lineSpacing: 160 })}
|
|
19457
|
-
${paraPr(5, { align: "LEFT", lineSpacing: 130, indent: 400 })}
|
|
19458
|
-
${paraPr(6, { align: "LEFT", lineSpacing: 150, indent: 600 })}
|
|
19459
|
-
${paraPr(7, { align: "LEFT", lineSpacing: 160, indent: 600 })}
|
|
19460
|
-
</hh:paraProperties>
|
|
19461
|
-
<hh:styles itemCnt="1">
|
|
19462
|
-
<hh:style id="0" type="PARA" name="\uBC14\uD0D5\uAE00" engName="Normal" paraPrIDRef="0" charPrIDRef="0" nextStyleIDRef="0" langIDRef="1042" lockForm="0"/>
|
|
19463
|
-
</hh:styles>
|
|
19464
|
-
</hh:refList>
|
|
19465
|
-
<hh:compatibleDocument targetProgram="HWP2018"/>
|
|
19466
|
-
</hh:head>`;
|
|
19467
|
-
}
|
|
19468
|
-
function generateSecPr() {
|
|
19469
|
-
return `<hp:secPr textDirection="HORIZONTAL" spaceColumns="1134" tabStop="8000" outlineShapeIDRef="0" memoShapeIDRef="0" textVerticalWidthHead="0" masterPageCnt="0"><hp:grid lineGrid="0" charGrid="0" wonggojiFormat="0"/><hp:startNum pageStartsOn="BOTH" page="0" pic="0" tbl="0" equation="0"/><hp:visibility hideFirstHeader="0" hideFirstFooter="0" hideFirstMasterPage="0" border="SHOW_ALL" fill="SHOW_ALL" hideFirstPageNum="0" hideFirstEmptyLine="0" showLineNumber="0"/><hp:pagePr landscape="WIDELY" width="59528" height="84188" gutterType="LEFT_ONLY"><hp:margin header="2835" footer="2835" gutter="0" left="5670" right="4252" top="8504" bottom="4252"/></hp:pagePr><hp:footNotePr><hp:autoNumFormat type="DIGIT" userChar="" prefixChar="" suffixChar=")" supscript="0"/><hp:noteLine length="-1" type="SOLID" width="0.12 mm" color="#000000"/><hp:noteSpacing betweenNotes="283" belowLine="567" aboveLine="850"/><hp:numbering type="CONTINUOUS" newNum="1"/><hp:placement place="EACH_COLUMN" beneathText="0"/></hp:footNotePr><hp:endNotePr><hp:autoNumFormat type="DIGIT" userChar="" prefixChar="" suffixChar=")" supscript="0"/><hp:noteLine length="14692344" type="SOLID" width="0.12 mm" color="#000000"/><hp:noteSpacing betweenNotes="0" belowLine="567" aboveLine="850"/><hp:numbering type="CONTINUOUS" newNum="1"/><hp:placement place="END_OF_DOCUMENT" beneathText="0"/></hp:endNotePr></hp:secPr>`;
|
|
19470
|
-
}
|
|
19471
|
-
var TABLE_ID_BASE = 1e3;
|
|
19472
|
-
var tableIdCounter = TABLE_ID_BASE;
|
|
19473
|
-
function nextTableId() {
|
|
19474
|
-
return ++tableIdCounter;
|
|
19475
|
-
}
|
|
19476
|
-
function generateTable(rows, theme) {
|
|
19477
|
-
const rowCnt = rows.length;
|
|
19478
|
-
const colCnt = Math.max(...rows.map((r) => r.length), 1);
|
|
19479
|
-
const cellW = Math.floor(44e3 / colCnt);
|
|
19480
|
-
const cellH = 1500;
|
|
19481
|
-
const tblW = cellW * colCnt;
|
|
19482
|
-
const tblH = cellH * rowCnt;
|
|
19483
|
-
const tblId = nextTableId();
|
|
19484
|
-
const useHeaderStyle = theme.tableHeader !== theme.body || theme.tableHeaderBold;
|
|
19485
|
-
const trElements = rows.map((row, rowIdx) => {
|
|
19486
|
-
const cells = row.length < colCnt ? [...row, ...Array(colCnt - row.length).fill("")] : row;
|
|
19487
|
-
const isHeaderRow = rowIdx === 0;
|
|
19488
|
-
const headerCharPr = isHeaderRow && useHeaderStyle ? CHAR_TABLE_HEADER : CHAR_NORMAL;
|
|
19489
|
-
const tdElements = cells.map((cell, colIdx) => {
|
|
19490
|
-
const runs = generateRuns(cell, headerCharPr);
|
|
19491
|
-
const p = `<hp:p paraPrIDRef="0" styleIDRef="0">${runs}</hp:p>`;
|
|
19492
|
-
return `<hp:tc name="" header="${isHeaderRow ? 1 : 0}" hasMargin="0" protect="0" editable="1" dirty="0" borderFillIDRef="1"><hp:subList id="" textDirection="HORIZONTAL" lineWrap="BREAK" vertAlign="TOP" linkListIDRef="0" linkListNextIDRef="0" textWidth="0" textHeight="0" hasTextRef="0" hasNumRef="0">${p}</hp:subList><hp:cellAddr colAddr="${colIdx}" rowAddr="${rowIdx}"/><hp:cellSpan colSpan="1" rowSpan="1"/><hp:cellSz width="${cellW}" height="${cellH}"/><hp:cellMargin left="141" right="141" top="141" bottom="141"/></hp:tc>`;
|
|
19493
|
-
}).join("");
|
|
19494
|
-
return `<hp:tr>${tdElements}</hp:tr>`;
|
|
19495
|
-
}).join("");
|
|
19496
|
-
const tblInner = `<hp:sz width="${tblW}" widthRelTo="ABSOLUTE" height="${tblH}" heightRelTo="ABSOLUTE" protect="0"/><hp:pos treatAsChar="1" affectLSpacing="0" flowWithText="0" allowOverlap="0" holdAnchorAndSO="0" vertRelTo="PARA" horzRelTo="PARA" vertAlign="TOP" horzAlign="LEFT" vertOffset="0" horzOffset="0"/><hp:outMargin left="0" right="0" top="0" bottom="0"/><hp:inMargin left="510" right="510" top="141" bottom="141"/>` + trElements;
|
|
19497
|
-
const tbl = `<hp:tbl id="${tblId}" zOrder="0" numberingType="TABLE" pageBreak="CELL" repeatHeader="0" rowCnt="${rowCnt}" colCnt="${colCnt}" cellSpacing="0" borderFillIDRef="1" noShading="0">${tblInner}</hp:tbl>`;
|
|
19498
|
-
return `<hp:p paraPrIDRef="0" styleIDRef="0"><hp:run charPrIDRef="0">${tbl}</hp:run></hp:p>`;
|
|
19499
|
-
}
|
|
19500
|
-
function blocksToSectionXml(blocks, theme) {
|
|
19501
|
-
const paraXmls = [];
|
|
19502
|
-
let isFirst = true;
|
|
19503
|
-
const orderedCounters = {};
|
|
19504
|
-
let prevWasOrdered = false;
|
|
19505
|
-
for (const block of blocks) {
|
|
19506
|
-
let xml = "";
|
|
19507
|
-
if (block.type !== "list_item" || !block.ordered) {
|
|
19508
|
-
if (prevWasOrdered) {
|
|
19509
|
-
for (const k of Object.keys(orderedCounters)) delete orderedCounters[+k];
|
|
19510
|
-
}
|
|
19511
|
-
prevWasOrdered = false;
|
|
19512
|
-
}
|
|
19513
|
-
switch (block.type) {
|
|
19514
|
-
case "heading": {
|
|
19515
|
-
const pId = headingParaPrId(block.level || 1);
|
|
19516
|
-
const cId = headingCharPrId(block.level || 1);
|
|
19517
|
-
xml = generateParagraph(block.text || "", pId, cId);
|
|
19518
|
-
break;
|
|
19519
|
-
}
|
|
19520
|
-
case "paragraph":
|
|
19521
|
-
xml = generateParagraph(block.text || "");
|
|
19522
|
-
break;
|
|
19523
|
-
case "code_block": {
|
|
19524
|
-
const codeLines = (block.text || "").split("\n");
|
|
19525
|
-
xml = codeLines.map((line) => generateParagraph(line || " ", PARA_CODE)).join("\n ");
|
|
19526
|
-
break;
|
|
19527
|
-
}
|
|
19528
|
-
case "blockquote":
|
|
19529
|
-
xml = generateParagraph(
|
|
19530
|
-
block.text || "",
|
|
19531
|
-
PARA_QUOTE,
|
|
19532
|
-
theme.hasQuoteOption ? CHAR_QUOTE : CHAR_NORMAL
|
|
19533
|
-
);
|
|
19534
|
-
break;
|
|
19535
|
-
case "list_item": {
|
|
19536
|
-
const indent = block.indent || 0;
|
|
19537
|
-
let marker;
|
|
19538
|
-
if (block.ordered) {
|
|
19539
|
-
orderedCounters[indent] = (orderedCounters[indent] || 0) + 1;
|
|
19540
|
-
for (const k of Object.keys(orderedCounters)) {
|
|
19541
|
-
if (+k > indent) delete orderedCounters[+k];
|
|
19542
|
-
}
|
|
19543
|
-
marker = `${orderedCounters[indent]}. `;
|
|
19544
|
-
prevWasOrdered = true;
|
|
19545
|
-
} else {
|
|
19546
|
-
marker = "\xB7 ";
|
|
19547
|
-
if (prevWasOrdered) {
|
|
19548
|
-
for (const k of Object.keys(orderedCounters)) delete orderedCounters[+k];
|
|
19549
|
-
}
|
|
19550
|
-
prevWasOrdered = false;
|
|
19551
|
-
}
|
|
19552
|
-
const indentPrefix = " ".repeat(indent);
|
|
19553
|
-
xml = generateParagraph(indentPrefix + marker + (block.text || ""), PARA_LIST);
|
|
19554
|
-
break;
|
|
19555
|
-
}
|
|
19556
|
-
case "hr":
|
|
19557
|
-
xml = `<hp:p paraPrIDRef="0" styleIDRef="0"><hp:run charPrIDRef="0"><hp:t>\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500</hp:t></hp:run></hp:p>`;
|
|
19558
|
-
break;
|
|
19559
|
-
case "table":
|
|
19560
|
-
if (block.rows) {
|
|
19561
|
-
if (isFirst) {
|
|
19562
|
-
const secRun = `<hp:run charPrIDRef="0">${generateSecPr()}<hp:t></hp:t></hp:run>`;
|
|
19563
|
-
paraXmls.push(`<hp:p paraPrIDRef="0" styleIDRef="0">${secRun}</hp:p>`);
|
|
19564
|
-
isFirst = false;
|
|
19565
|
-
}
|
|
19566
|
-
xml = generateTable(block.rows, theme);
|
|
19567
|
-
}
|
|
19568
|
-
break;
|
|
19569
|
-
}
|
|
19570
|
-
if (!xml) continue;
|
|
19571
|
-
if (isFirst && block.type !== "table") {
|
|
19572
|
-
xml = xml.replace(
|
|
19573
|
-
/<hp:run charPrIDRef="(\d+)">/,
|
|
19574
|
-
`<hp:run charPrIDRef="$1">${generateSecPr()}`
|
|
19575
|
-
);
|
|
19576
|
-
isFirst = false;
|
|
19577
|
-
}
|
|
19578
|
-
paraXmls.push(xml);
|
|
19579
|
-
}
|
|
19580
|
-
if (paraXmls.length === 0) {
|
|
19581
|
-
paraXmls.push(`<hp:p paraPrIDRef="0" styleIDRef="0"><hp:run charPrIDRef="0">${generateSecPr()}<hp:t></hp:t></hp:run></hp:p>`);
|
|
19582
|
-
}
|
|
19583
|
-
return `<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
|
|
19584
|
-
<hs:sec xmlns:hs="${NS_SECTION}" xmlns:hp="${NS_PARA}">
|
|
19585
|
-
${paraXmls.join("\n ")}
|
|
19586
|
-
</hs:sec>`;
|
|
19587
|
-
}
|
|
19588
|
-
|
|
19589
|
-
// src/diff/text-diff.ts
|
|
19590
|
-
function similarity(a, b) {
|
|
19591
|
-
if (a === b) return 1;
|
|
19592
|
-
if (!a || !b) return 0;
|
|
19593
|
-
const maxLen = Math.max(a.length, b.length);
|
|
19594
|
-
if (maxLen === 0) return 1;
|
|
19595
|
-
return 1 - levenshtein(a, b) / maxLen;
|
|
19596
|
-
}
|
|
19597
|
-
function normalizedSimilarity(a, b) {
|
|
19598
|
-
return similarity(normalize(a), normalize(b));
|
|
19599
|
-
}
|
|
19600
|
-
function normalize(s) {
|
|
19601
|
-
return s.replace(/\s+/g, " ").trim();
|
|
19602
|
-
}
|
|
19603
|
-
var MAX_LEVENSHTEIN_LEN = 1e4;
|
|
19604
|
-
function levenshtein(a, b) {
|
|
19605
|
-
if (a.length + b.length > MAX_LEVENSHTEIN_LEN) {
|
|
19606
|
-
const sampleLen = Math.min(500, a.length, b.length);
|
|
19607
|
-
let diffs = 0;
|
|
19608
|
-
for (let i = 0; i < sampleLen; i++) if (a[i] !== b[i]) diffs++;
|
|
19609
|
-
const sampleRate = sampleLen > 0 ? diffs / sampleLen : 1;
|
|
19610
|
-
return Math.abs(a.length - b.length) + Math.round(Math.min(a.length, b.length) * sampleRate);
|
|
19611
|
-
}
|
|
19612
|
-
if (a.length > b.length) [a, b] = [b, a];
|
|
19613
|
-
const m = a.length;
|
|
19614
|
-
const n = b.length;
|
|
19615
|
-
let prev = Array.from({ length: m + 1 }, (_, i) => i);
|
|
19616
|
-
let curr = new Array(m + 1);
|
|
19617
|
-
for (let j = 1; j <= n; j++) {
|
|
19618
|
-
curr[0] = j;
|
|
19619
|
-
for (let i = 1; i <= m; i++) {
|
|
19620
|
-
if (a[i - 1] === b[j - 1]) {
|
|
19621
|
-
curr[i] = prev[i - 1];
|
|
19622
|
-
} else {
|
|
19623
|
-
curr[i] = 1 + Math.min(prev[i - 1], prev[i], curr[i - 1]);
|
|
19624
|
-
}
|
|
19625
|
-
}
|
|
19626
|
-
;
|
|
19627
|
-
[prev, curr] = [curr, prev];
|
|
19628
|
-
}
|
|
19629
|
-
return prev[m];
|
|
19630
|
-
}
|
|
19631
|
-
|
|
19632
|
-
// src/diff/compare.ts
|
|
19633
|
-
var SIMILARITY_THRESHOLD = 0.4;
|
|
19634
|
-
async function compare(bufferA, bufferB, options) {
|
|
19635
|
-
const [resultA, resultB] = await Promise.all([
|
|
19636
|
-
parse(bufferA, options),
|
|
19637
|
-
parse(bufferB, options)
|
|
19638
|
-
]);
|
|
19639
|
-
if (!resultA.success) throw new Error(`\uBB38\uC11CA \uD30C\uC2F1 \uC2E4\uD328: ${resultA.error}`);
|
|
19640
|
-
if (!resultB.success) throw new Error(`\uBB38\uC11CB \uD30C\uC2F1 \uC2E4\uD328: ${resultB.error}`);
|
|
19641
|
-
return diffBlocks(resultA.blocks, resultB.blocks);
|
|
19642
|
-
}
|
|
19643
|
-
function diffBlocks(blocksA, blocksB) {
|
|
19644
|
-
const aligned = alignBlocks(blocksA, blocksB);
|
|
19645
|
-
const stats = { added: 0, removed: 0, modified: 0, unchanged: 0 };
|
|
19646
|
-
const diffs = [];
|
|
19647
|
-
for (const [a, b] of aligned) {
|
|
19648
|
-
if (a && b) {
|
|
19649
|
-
const sim = blockSimilarity(a, b);
|
|
19650
|
-
if (sim >= 0.99) {
|
|
19651
|
-
diffs.push({ type: "unchanged", before: a, after: b, similarity: 1 });
|
|
19652
|
-
stats.unchanged++;
|
|
19653
|
-
} else {
|
|
19654
|
-
const diff = { type: "modified", before: a, after: b, similarity: sim };
|
|
19655
|
-
if (a.type === "table" && b.type === "table" && a.table && b.table) {
|
|
19656
|
-
diff.cellDiffs = diffTableCells(a.table, b.table);
|
|
19657
|
-
}
|
|
19658
|
-
diffs.push(diff);
|
|
19659
|
-
stats.modified++;
|
|
19660
|
-
}
|
|
19661
|
-
} else if (a) {
|
|
19662
|
-
diffs.push({ type: "removed", before: a });
|
|
19663
|
-
stats.removed++;
|
|
19664
|
-
} else if (b) {
|
|
19665
|
-
diffs.push({ type: "added", after: b });
|
|
19666
|
-
stats.added++;
|
|
19667
|
-
}
|
|
19668
|
-
}
|
|
19669
|
-
return { stats, diffs };
|
|
19670
|
-
}
|
|
19671
|
-
function alignBlocks(a, b) {
|
|
19672
|
-
const m = a.length, n = b.length;
|
|
19673
|
-
if (m * n > 1e7) return fallbackAlign(a, b);
|
|
19674
|
-
const simCache = /* @__PURE__ */ new Map();
|
|
19675
|
-
const getSim = (i2, j2) => {
|
|
19676
|
-
const key = `${i2},${j2}`;
|
|
19677
|
-
let v = simCache.get(key);
|
|
19678
|
-
if (v === void 0) {
|
|
19679
|
-
v = blockSimilarity(a[i2], b[j2]);
|
|
19680
|
-
simCache.set(key, v);
|
|
19681
|
-
}
|
|
19682
|
-
return v;
|
|
19683
|
-
};
|
|
19684
|
-
const dp = Array.from({ length: m + 1 }, () => new Array(n + 1).fill(0));
|
|
19685
|
-
for (let i2 = 1; i2 <= m; i2++) {
|
|
19686
|
-
for (let j2 = 1; j2 <= n; j2++) {
|
|
19687
|
-
if (getSim(i2 - 1, j2 - 1) >= SIMILARITY_THRESHOLD) {
|
|
19688
|
-
dp[i2][j2] = dp[i2 - 1][j2 - 1] + 1;
|
|
19689
|
-
} else {
|
|
19690
|
-
dp[i2][j2] = Math.max(dp[i2 - 1][j2], dp[i2][j2 - 1]);
|
|
19691
|
-
}
|
|
19692
|
-
}
|
|
19693
|
-
}
|
|
19694
|
-
const pairs = [];
|
|
19695
|
-
let i = m, j = n;
|
|
19696
|
-
while (i > 0 && j > 0) {
|
|
19697
|
-
if (getSim(i - 1, j - 1) >= SIMILARITY_THRESHOLD && dp[i][j] === dp[i - 1][j - 1] + 1) {
|
|
19698
|
-
pairs.push([i - 1, j - 1]);
|
|
19699
|
-
i--;
|
|
19700
|
-
j--;
|
|
19701
|
-
} else if (dp[i - 1][j] >= dp[i][j - 1]) {
|
|
19702
|
-
i--;
|
|
19703
|
-
} else {
|
|
19704
|
-
j--;
|
|
19705
|
-
}
|
|
19706
|
-
}
|
|
19707
|
-
pairs.reverse();
|
|
19708
|
-
const result = [];
|
|
19709
|
-
let ai = 0, bi = 0;
|
|
19710
|
-
for (const [pi, pj] of pairs) {
|
|
19711
|
-
while (ai < pi) result.push([a[ai++], null]);
|
|
19712
|
-
while (bi < pj) result.push([null, b[bi++]]);
|
|
19713
|
-
result.push([a[ai++], b[bi++]]);
|
|
19714
|
-
}
|
|
19715
|
-
while (ai < m) result.push([a[ai++], null]);
|
|
19716
|
-
while (bi < n) result.push([null, b[bi++]]);
|
|
19717
|
-
return result;
|
|
19718
|
-
}
|
|
19719
|
-
function fallbackAlign(a, b) {
|
|
19720
|
-
const result = [];
|
|
19721
|
-
const len = Math.max(a.length, b.length);
|
|
19722
|
-
for (let i = 0; i < len; i++) {
|
|
19723
|
-
result.push([a[i] || null, b[i] || null]);
|
|
19724
|
-
}
|
|
19725
|
-
return result;
|
|
19726
|
-
}
|
|
19727
|
-
function blockSimilarity(a, b) {
|
|
19728
|
-
if (a.type !== b.type) return 0;
|
|
19729
|
-
if (a.text !== void 0 && b.text !== void 0) {
|
|
19730
|
-
return normalizedSimilarity(a.text || "", b.text || "");
|
|
19731
|
-
}
|
|
19732
|
-
if (a.type === "table" && a.table && b.table) {
|
|
19733
|
-
return tableSimilarity(a.table, b.table);
|
|
19460
|
+
function patchZipEntries(original, replacements) {
|
|
19461
|
+
const { entries, cdOffset, eocdOffset } = parseCentralDirectory(original);
|
|
19462
|
+
const view = new DataView(original.buffer, original.byteOffset, original.byteLength);
|
|
19463
|
+
for (const name of replacements.keys()) {
|
|
19464
|
+
if (!entries.some((e) => e.name === name)) throw new KordocError(`ZIP\uC5D0 \uC5C6\uB294 \uC5D4\uD2B8\uB9AC: ${name}`);
|
|
19734
19465
|
}
|
|
19735
|
-
|
|
19736
|
-
|
|
19737
|
-
|
|
19738
|
-
|
|
19739
|
-
|
|
19740
|
-
|
|
19741
|
-
|
|
19742
|
-
|
|
19743
|
-
|
|
19744
|
-
|
|
19745
|
-
|
|
19746
|
-
|
|
19747
|
-
|
|
19748
|
-
|
|
19749
|
-
|
|
19750
|
-
const row = [];
|
|
19751
|
-
for (let c = 0; c < maxCols; c++) {
|
|
19752
|
-
const cellA = r < a.rows && c < a.cols ? a.cells[r][c].text : void 0;
|
|
19753
|
-
const cellB = r < b.rows && c < b.cols ? b.cells[r][c].text : void 0;
|
|
19754
|
-
let type;
|
|
19755
|
-
if (cellA === void 0) type = "added";
|
|
19756
|
-
else if (cellB === void 0) type = "removed";
|
|
19757
|
-
else if (cellA === cellB) type = "unchanged";
|
|
19758
|
-
else type = "modified";
|
|
19759
|
-
row.push({ type, before: cellA, after: cellB });
|
|
19466
|
+
const byLocal = [...entries].sort((a, b) => a.localOffset - b.localOffset);
|
|
19467
|
+
const segments = [];
|
|
19468
|
+
const newLocalOffset = /* @__PURE__ */ new Map();
|
|
19469
|
+
const newMeta = /* @__PURE__ */ new Map();
|
|
19470
|
+
let offset = 0;
|
|
19471
|
+
for (let i = 0; i < byLocal.length; i++) {
|
|
19472
|
+
const e = byLocal[i];
|
|
19473
|
+
const segEnd = i + 1 < byLocal.length ? byLocal[i + 1].localOffset : cdOffset;
|
|
19474
|
+
newLocalOffset.set(e, offset);
|
|
19475
|
+
const newData = replacements.get(e.name);
|
|
19476
|
+
if (newData === void 0) {
|
|
19477
|
+
const seg = original.subarray(e.localOffset, segEnd);
|
|
19478
|
+
segments.push(seg);
|
|
19479
|
+
offset += seg.length;
|
|
19480
|
+
continue;
|
|
19760
19481
|
}
|
|
19761
|
-
|
|
19482
|
+
if (view.getUint32(e.localOffset, true) !== LOCAL_SIG) throw new KordocError("ZIP \uB85C\uCEEC \uD5E4\uB354 \uC2DC\uADF8\uB2C8\uCC98 \uBD88\uC77C\uCE58");
|
|
19483
|
+
const nameLen = view.getUint16(e.localOffset + 26, true);
|
|
19484
|
+
const extraLen = view.getUint16(e.localOffset + 28, true);
|
|
19485
|
+
const headerLen = 30 + nameLen + extraLen;
|
|
19486
|
+
const header = copyBytes(original, e.localOffset, e.localOffset + headerLen);
|
|
19487
|
+
const hview = new DataView(header.buffer, header.byteOffset, header.byteLength);
|
|
19488
|
+
const method = e.method;
|
|
19489
|
+
const compData = method === 0 ? newData : new Uint8Array(deflateRawSync(newData));
|
|
19490
|
+
const crc = crc32(newData);
|
|
19491
|
+
const flags = e.flags & ~8;
|
|
19492
|
+
hview.setUint16(6, flags, true);
|
|
19493
|
+
hview.setUint32(14, crc, true);
|
|
19494
|
+
hview.setUint32(18, compData.length, true);
|
|
19495
|
+
hview.setUint32(22, newData.length, true);
|
|
19496
|
+
segments.push(header, compData);
|
|
19497
|
+
offset += headerLen + compData.length;
|
|
19498
|
+
newMeta.set(e, { crc, compSize: compData.length, uncompSize: newData.length, flags });
|
|
19762
19499
|
}
|
|
19763
|
-
|
|
19764
|
-
|
|
19765
|
-
|
|
19766
|
-
|
|
19767
|
-
|
|
19768
|
-
|
|
19769
|
-
|
|
19770
|
-
|
|
19771
|
-
|
|
19772
|
-
|
|
19773
|
-
|
|
19774
|
-
return text.replace(/&(lt|gt|amp|quot|apos|#x?[0-9a-fA-F]+);/g, (m, ent) => {
|
|
19775
|
-
switch (ent) {
|
|
19776
|
-
case "lt":
|
|
19777
|
-
return "<";
|
|
19778
|
-
case "gt":
|
|
19779
|
-
return ">";
|
|
19780
|
-
case "amp":
|
|
19781
|
-
return "&";
|
|
19782
|
-
case "quot":
|
|
19783
|
-
return '"';
|
|
19784
|
-
case "apos":
|
|
19785
|
-
return "'";
|
|
19786
|
-
}
|
|
19787
|
-
try {
|
|
19788
|
-
const code = ent[1] === "x" || ent[1] === "X" ? parseInt(ent.slice(2), 16) : parseInt(ent.slice(1), 10);
|
|
19789
|
-
if (!isNaN(code) && code >= 0 && code <= 1114111) return String.fromCodePoint(code);
|
|
19790
|
-
} catch {
|
|
19791
|
-
}
|
|
19792
|
-
return m;
|
|
19793
|
-
});
|
|
19794
|
-
}
|
|
19795
|
-
function tContentToText(raw) {
|
|
19796
|
-
return decodeXmlEntities(
|
|
19797
|
-
raw.replace(/<\/?(?:[A-Za-z0-9_]+:)?(?:tab|fwSpace|hwSpace|br|lineBreak)(?:\s[^>]*)?\/?>/g, " ").replace(/<[^>]*>/g, "")
|
|
19798
|
-
);
|
|
19799
|
-
}
|
|
19800
|
-
var TAG_RE = /<!--[\s\S]*?-->|<!\[CDATA\[[\s\S]*?\]\]>|<\?[\s\S]*?\?>|<!(?:"[^"]*"|'[^']*'|[^>"'])*>|<\/([^\s>]+)\s*>|<([^\s/>!?]+)((?:"[^"]*"|'[^']*'|[^>"'])*?)(\/?)>/g;
|
|
19801
|
-
var T_BARRIER = /* @__PURE__ */ new Set([
|
|
19802
|
-
"tbl",
|
|
19803
|
-
"ctrl",
|
|
19804
|
-
"caption",
|
|
19805
|
-
"pic",
|
|
19806
|
-
"shape",
|
|
19807
|
-
"drawingObject",
|
|
19808
|
-
"drawText",
|
|
19809
|
-
"shapeComment",
|
|
19810
|
-
"memogroup",
|
|
19811
|
-
"memo",
|
|
19812
|
-
"hiddenComment",
|
|
19813
|
-
"equation",
|
|
19814
|
-
"parameters",
|
|
19815
|
-
"subList",
|
|
19816
|
-
"p"
|
|
19817
|
-
]);
|
|
19818
|
-
var PARA_CONTAINER = /* @__PURE__ */ new Set([
|
|
19819
|
-
"tc",
|
|
19820
|
-
"ctrl",
|
|
19821
|
-
"caption",
|
|
19822
|
-
"drawText",
|
|
19823
|
-
"pic",
|
|
19824
|
-
"shape",
|
|
19825
|
-
"drawingObject",
|
|
19826
|
-
"memogroup",
|
|
19827
|
-
"memo",
|
|
19828
|
-
"hiddenComment",
|
|
19829
|
-
"footNote",
|
|
19830
|
-
"endNote",
|
|
19831
|
-
"fn",
|
|
19832
|
-
"en"
|
|
19833
|
-
// 각주/미주 — 파서는 호스트 블록 footnoteText로만 흡수
|
|
19834
|
-
]);
|
|
19835
|
-
var TABLE_BARRIER = /* @__PURE__ */ new Set([
|
|
19836
|
-
"tbl",
|
|
19837
|
-
"ctrl",
|
|
19838
|
-
"caption",
|
|
19839
|
-
"memogroup",
|
|
19840
|
-
"memo",
|
|
19841
|
-
"hiddenComment"
|
|
19842
|
-
]);
|
|
19843
|
-
function localOf(qname) {
|
|
19844
|
-
const i = qname.indexOf(":");
|
|
19845
|
-
return i >= 0 ? qname.slice(i + 1) : qname;
|
|
19846
|
-
}
|
|
19847
|
-
function prefixOf(qname) {
|
|
19848
|
-
const i = qname.indexOf(":");
|
|
19849
|
-
return i >= 0 ? qname.slice(0, i) : "";
|
|
19850
|
-
}
|
|
19851
|
-
function scanSectionXml(xml, sectionIndex) {
|
|
19852
|
-
const stack = [];
|
|
19853
|
-
const bodyParagraphs = [];
|
|
19854
|
-
const tables = [];
|
|
19855
|
-
const headerTexts = [];
|
|
19856
|
-
const footerTexts = [];
|
|
19857
|
-
const paraStack = [];
|
|
19858
|
-
const tableStack = [];
|
|
19859
|
-
const rowStack = [];
|
|
19860
|
-
const cellStack = [];
|
|
19861
|
-
let pendingT = null;
|
|
19862
|
-
const ctrlSubStack = [];
|
|
19863
|
-
const classifyPara = () => {
|
|
19864
|
-
let sawDrawText = false;
|
|
19865
|
-
for (let i = stack.length - 1; i >= 0; i--) {
|
|
19866
|
-
const l = stack[i].local;
|
|
19867
|
-
if (l === "tc") return "cell";
|
|
19868
|
-
if (l === "drawText") {
|
|
19869
|
-
sawDrawText = true;
|
|
19870
|
-
continue;
|
|
19871
|
-
}
|
|
19872
|
-
if (PARA_CONTAINER.has(l)) return "excluded";
|
|
19873
|
-
}
|
|
19874
|
-
return sawDrawText ? "draw" : "body";
|
|
19875
|
-
};
|
|
19876
|
-
const owningPara = () => {
|
|
19877
|
-
if (paraStack.length === 0) return null;
|
|
19878
|
-
for (let i = stack.length - 1; i >= 0; i--) {
|
|
19879
|
-
const l = stack[i].local;
|
|
19880
|
-
if (l === "p") return paraStack[paraStack.length - 1];
|
|
19881
|
-
if (T_BARRIER.has(l)) return null;
|
|
19882
|
-
}
|
|
19883
|
-
return null;
|
|
19884
|
-
};
|
|
19885
|
-
const isTableTopLevel = () => {
|
|
19886
|
-
for (let i = stack.length - 1; i >= 0; i--) {
|
|
19887
|
-
if (TABLE_BARRIER.has(stack[i].local)) return false;
|
|
19500
|
+
const newCdOffset = offset;
|
|
19501
|
+
for (const e of entries) {
|
|
19502
|
+
const cd = copyBytes(original, e.cdStart, e.cdEnd);
|
|
19503
|
+
const cview = new DataView(cd.buffer, cd.byteOffset, cd.byteLength);
|
|
19504
|
+
cview.setUint32(42, newLocalOffset.get(e), true);
|
|
19505
|
+
const meta = newMeta.get(e);
|
|
19506
|
+
if (meta) {
|
|
19507
|
+
cview.setUint16(8, meta.flags, true);
|
|
19508
|
+
cview.setUint32(16, meta.crc, true);
|
|
19509
|
+
cview.setUint32(20, meta.compSize, true);
|
|
19510
|
+
cview.setUint32(24, meta.uncompSize, true);
|
|
19888
19511
|
}
|
|
19889
|
-
|
|
19890
|
-
|
|
19891
|
-
|
|
19892
|
-
|
|
19893
|
-
|
|
19894
|
-
|
|
19895
|
-
|
|
19896
|
-
|
|
19897
|
-
|
|
19898
|
-
|
|
19899
|
-
|
|
19900
|
-
|
|
19901
|
-
|
|
19902
|
-
|
|
19903
|
-
|
|
19512
|
+
segments.push(cd);
|
|
19513
|
+
offset += cd.length;
|
|
19514
|
+
}
|
|
19515
|
+
const newCdSize = offset - newCdOffset;
|
|
19516
|
+
const eocd = copyBytes(original, eocdOffset);
|
|
19517
|
+
const eview = new DataView(eocd.buffer, eocd.byteOffset, eocd.byteLength);
|
|
19518
|
+
eview.setUint32(12, newCdSize, true);
|
|
19519
|
+
eview.setUint32(16, newCdOffset, true);
|
|
19520
|
+
segments.push(eocd);
|
|
19521
|
+
offset += eocd.length;
|
|
19522
|
+
const result = new Uint8Array(offset);
|
|
19523
|
+
let pos = 0;
|
|
19524
|
+
for (const seg of segments) {
|
|
19525
|
+
result.set(seg, pos);
|
|
19526
|
+
pos += seg.length;
|
|
19527
|
+
}
|
|
19528
|
+
return result;
|
|
19529
|
+
}
|
|
19530
|
+
|
|
19531
|
+
// src/form/filler-hwpx.ts
|
|
19532
|
+
async function fillHwpx(hwpxBuffer, values) {
|
|
19533
|
+
const u8 = new Uint8Array(hwpxBuffer);
|
|
19534
|
+
const zip = await JSZip4.loadAsync(hwpxBuffer);
|
|
19535
|
+
const sectionPaths = Object.keys(zip.files).filter((name) => /[Ss]ection\d+\.xml$/i.test(name)).sort();
|
|
19536
|
+
if (sectionPaths.length === 0) {
|
|
19537
|
+
throw new KordocError("HWPX\uC5D0\uC11C \uC139\uC158 \uD30C\uC77C\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4");
|
|
19538
|
+
}
|
|
19539
|
+
const normalizedValues = normalizeValues(values);
|
|
19540
|
+
const matchedLabels = /* @__PURE__ */ new Set();
|
|
19541
|
+
const filled = [];
|
|
19542
|
+
const failedKeys = /* @__PURE__ */ new Set();
|
|
19543
|
+
const succeededKeys = /* @__PURE__ */ new Set();
|
|
19544
|
+
const replacements = /* @__PURE__ */ new Map();
|
|
19545
|
+
const encoder = new TextEncoder();
|
|
19546
|
+
for (let si = 0; si < sectionPaths.length; si++) {
|
|
19547
|
+
const xml = await zip.file(sectionPaths[si]).async("text");
|
|
19548
|
+
const scan = scanSectionXml(xml, si);
|
|
19549
|
+
const ledger = /* @__PURE__ */ new Map();
|
|
19550
|
+
const led = (p) => {
|
|
19551
|
+
let l = ledger.get(p);
|
|
19552
|
+
if (!l) ledger.set(p, l = { ranges: [], filledIdx: [], matchKeys: [] });
|
|
19553
|
+
return l;
|
|
19554
|
+
};
|
|
19555
|
+
const matchText = (p) => paraTText(p, xml) ?? p.text;
|
|
19556
|
+
const cellLabelText = (cell) => cell.paragraphs.filter((p) => !p.inTextbox).map((p) => matchText(p)).join("");
|
|
19557
|
+
const allTables = [];
|
|
19558
|
+
const collectTables = (tables, depth) => {
|
|
19559
|
+
if (depth > 16) return;
|
|
19560
|
+
for (const t of tables) {
|
|
19561
|
+
allTables.push(t);
|
|
19562
|
+
for (const row of t.rows) {
|
|
19563
|
+
for (const cell of row) collectTables(cell.tables, depth + 1);
|
|
19564
|
+
}
|
|
19904
19565
|
}
|
|
19905
|
-
|
|
19906
|
-
|
|
19907
|
-
|
|
19908
|
-
|
|
19566
|
+
};
|
|
19567
|
+
collectTables(scan.tables, 0);
|
|
19568
|
+
collectTables(scan.orphanTables, 0);
|
|
19569
|
+
const patternApplied = /* @__PURE__ */ new Set();
|
|
19570
|
+
for (const table of allTables) {
|
|
19571
|
+
for (const row of table.rows) {
|
|
19572
|
+
for (const cell of row) {
|
|
19573
|
+
for (const para of cell.paragraphs) {
|
|
19574
|
+
const text = matchText(para);
|
|
19575
|
+
const result = fillInCellPatterns(text, normalizedValues, matchedLabels);
|
|
19576
|
+
if (!result) continue;
|
|
19577
|
+
const l = led(para);
|
|
19578
|
+
if (l.fullText !== void 0) continue;
|
|
19579
|
+
const newT = result.text;
|
|
19580
|
+
let s = 0;
|
|
19581
|
+
while (s < text.length && s < newT.length && text[s] === newT[s]) s++;
|
|
19582
|
+
let eo = text.length;
|
|
19583
|
+
let en = newT.length;
|
|
19584
|
+
while (eo > s && en > s && text[eo - 1] === newT[en - 1]) {
|
|
19585
|
+
eo--;
|
|
19586
|
+
en--;
|
|
19587
|
+
}
|
|
19588
|
+
l.ranges.push({ start: s, end: eo, replacement: newT.slice(s, en) });
|
|
19589
|
+
patternApplied.add(cell);
|
|
19590
|
+
for (const m of result.matches) {
|
|
19591
|
+
l.filledIdx.push(filled.length);
|
|
19592
|
+
l.matchKeys.push(m.key);
|
|
19593
|
+
filled.push({ label: m.label, value: m.value, row: -1, col: -1 });
|
|
19594
|
+
}
|
|
19595
|
+
}
|
|
19909
19596
|
}
|
|
19910
19597
|
}
|
|
19911
|
-
|
|
19912
|
-
|
|
19913
|
-
|
|
19914
|
-
|
|
19915
|
-
|
|
19598
|
+
}
|
|
19599
|
+
for (const table of allTables) {
|
|
19600
|
+
for (let rowIdx = 0; rowIdx < table.rows.length; rowIdx++) {
|
|
19601
|
+
const cells = table.rows[rowIdx];
|
|
19602
|
+
for (let colIdx = 0; colIdx < cells.length - 1; colIdx++) {
|
|
19603
|
+
const labelText = cellLabelText(cells[colIdx]);
|
|
19604
|
+
if (!isLabelCell(labelText)) continue;
|
|
19605
|
+
const valueCell = cells[colIdx + 1];
|
|
19606
|
+
if (isKeywordLabel(cellLabelText(valueCell))) continue;
|
|
19607
|
+
const normalizedCellLabel = normalizeLabel(labelText);
|
|
19608
|
+
if (!normalizedCellLabel) continue;
|
|
19609
|
+
const matchKey = findMatchingKey(normalizedCellLabel, normalizedValues);
|
|
19610
|
+
if (matchKey === void 0) continue;
|
|
19611
|
+
const newValue = normalizedValues.get(matchKey);
|
|
19612
|
+
if (patternApplied.has(valueCell)) {
|
|
19613
|
+
const target = valueCell.paragraphs.find((p) => p.tRanges.length > 0) ?? valueCell.paragraphs[0];
|
|
19614
|
+
if (!target) continue;
|
|
19615
|
+
const l = led(target);
|
|
19616
|
+
if (l.fullText === void 0) {
|
|
19617
|
+
l.ranges.push({ start: 0, end: 0, replacement: newValue + " " });
|
|
19618
|
+
l.filledIdx.push(filled.length);
|
|
19619
|
+
l.matchKeys.push(matchKey);
|
|
19620
|
+
}
|
|
19621
|
+
} else {
|
|
19622
|
+
const paras = valueCell.paragraphs;
|
|
19623
|
+
if (paras.length === 0) continue;
|
|
19624
|
+
const l0 = led(paras[0]);
|
|
19625
|
+
l0.fullText = newValue;
|
|
19626
|
+
l0.ranges = [];
|
|
19627
|
+
l0.filledIdx.push(filled.length);
|
|
19628
|
+
l0.matchKeys.push(matchKey);
|
|
19629
|
+
for (let k = 1; k < paras.length; k++) {
|
|
19630
|
+
const lk = led(paras[k]);
|
|
19631
|
+
lk.fullText = "";
|
|
19632
|
+
lk.ranges = [];
|
|
19633
|
+
}
|
|
19634
|
+
}
|
|
19635
|
+
matchedLabels.add(matchKey);
|
|
19636
|
+
filled.push({
|
|
19637
|
+
label: labelText.trim().replace(/[::]\s*$/, ""),
|
|
19638
|
+
value: newValue,
|
|
19639
|
+
row: rowIdx,
|
|
19640
|
+
col: colIdx
|
|
19641
|
+
});
|
|
19916
19642
|
}
|
|
19917
|
-
}
|
|
19918
|
-
|
|
19919
|
-
const
|
|
19920
|
-
|
|
19921
|
-
|
|
19922
|
-
|
|
19923
|
-
|
|
19924
|
-
if (
|
|
19925
|
-
|
|
19926
|
-
|
|
19927
|
-
|
|
19928
|
-
|
|
19929
|
-
|
|
19930
|
-
|
|
19931
|
-
|
|
19932
|
-
|
|
19933
|
-
|
|
19643
|
+
}
|
|
19644
|
+
if (table.rows.length >= 2) {
|
|
19645
|
+
const headerCells = table.rows[0];
|
|
19646
|
+
const allLabels = headerCells.length > 0 && headerCells.every((cell) => {
|
|
19647
|
+
const t = cellLabelText(cell).trim();
|
|
19648
|
+
return t.length > 0 && t.length <= 20 && isLabelCell(t);
|
|
19649
|
+
});
|
|
19650
|
+
if (allLabels) {
|
|
19651
|
+
for (let rowIdx = 1; rowIdx < table.rows.length; rowIdx++) {
|
|
19652
|
+
const dataCells = table.rows[rowIdx];
|
|
19653
|
+
for (let colIdx = 0; colIdx < Math.min(headerCells.length, dataCells.length); colIdx++) {
|
|
19654
|
+
const headerLabel = normalizeLabel(cellLabelText(headerCells[colIdx]));
|
|
19655
|
+
const matchKey = findMatchingKey(headerLabel, normalizedValues);
|
|
19656
|
+
if (matchKey === void 0) continue;
|
|
19657
|
+
if (matchedLabels.has(matchKey)) continue;
|
|
19658
|
+
const newValue = normalizedValues.get(matchKey);
|
|
19659
|
+
const paras = dataCells[colIdx].paragraphs;
|
|
19660
|
+
if (paras.length === 0) continue;
|
|
19661
|
+
const l0 = led(paras[0]);
|
|
19662
|
+
l0.fullText = newValue;
|
|
19663
|
+
l0.ranges = [];
|
|
19664
|
+
l0.filledIdx.push(filled.length);
|
|
19665
|
+
l0.matchKeys.push(matchKey);
|
|
19666
|
+
for (let k = 1; k < paras.length; k++) {
|
|
19667
|
+
const lk = led(paras[k]);
|
|
19668
|
+
lk.fullText = "";
|
|
19669
|
+
lk.ranges = [];
|
|
19670
|
+
}
|
|
19671
|
+
matchedLabels.add(matchKey);
|
|
19672
|
+
filled.push({
|
|
19673
|
+
label: cellLabelText(headerCells[colIdx]).trim(),
|
|
19674
|
+
value: newValue,
|
|
19675
|
+
row: rowIdx,
|
|
19676
|
+
col: colIdx
|
|
19677
|
+
});
|
|
19678
|
+
}
|
|
19934
19679
|
}
|
|
19935
19680
|
}
|
|
19936
|
-
}
|
|
19937
|
-
|
|
19938
|
-
|
|
19939
|
-
|
|
19940
|
-
|
|
19941
|
-
|
|
19681
|
+
}
|
|
19682
|
+
}
|
|
19683
|
+
for (const para of [...scan.bodyParagraphs, ...scan.excludedParagraphs]) {
|
|
19684
|
+
const existing = ledger.get(para);
|
|
19685
|
+
if (existing?.fullText !== void 0) continue;
|
|
19686
|
+
const text = matchText(para);
|
|
19687
|
+
for (const seg of scanInlineSegments(text)) {
|
|
19688
|
+
const matchKey = findMatchingKey(normalizeLabel(seg.label), normalizedValues);
|
|
19689
|
+
if (matchKey === void 0) continue;
|
|
19690
|
+
const newValue = normalizedValues.get(matchKey);
|
|
19691
|
+
const replacement = seg.valueStart === seg.valueEnd ? padInsertion(text, seg.valueStart, newValue) : newValue;
|
|
19692
|
+
const l = led(para);
|
|
19693
|
+
l.ranges.push({ start: seg.valueStart, end: seg.valueEnd, replacement });
|
|
19694
|
+
matchedLabels.add(matchKey);
|
|
19695
|
+
l.filledIdx.push(filled.length);
|
|
19696
|
+
l.matchKeys.push(matchKey);
|
|
19697
|
+
filled.push({ label: seg.label.trim(), value: newValue, row: -1, col: -1 });
|
|
19698
|
+
}
|
|
19699
|
+
}
|
|
19700
|
+
const splices = [];
|
|
19701
|
+
for (const [para, l] of ledger) {
|
|
19702
|
+
let paraSplices = null;
|
|
19703
|
+
if (l.fullText !== void 0) {
|
|
19704
|
+
paraSplices = buildParagraphSplices(para, l.fullText, xml);
|
|
19705
|
+
} else if (l.ranges.length > 0) {
|
|
19706
|
+
const sorted = [...l.ranges].sort((a, b) => a.start - b.start || a.end - b.end);
|
|
19707
|
+
const merged = [];
|
|
19708
|
+
for (const r of sorted) {
|
|
19709
|
+
const prev = merged[merged.length - 1];
|
|
19710
|
+
if (prev && r.start < prev.end) continue;
|
|
19711
|
+
merged.push(r);
|
|
19712
|
+
}
|
|
19713
|
+
if (paraTText(para, xml) !== null) {
|
|
19714
|
+
const precise = [];
|
|
19715
|
+
let ok = true;
|
|
19716
|
+
for (const r of merged) {
|
|
19717
|
+
const sp = buildRangeSplices(para, xml, r.start, r.end, r.replacement);
|
|
19718
|
+
if (!sp) {
|
|
19719
|
+
ok = false;
|
|
19720
|
+
break;
|
|
19721
|
+
}
|
|
19722
|
+
precise.push(...sp);
|
|
19723
|
+
}
|
|
19724
|
+
paraSplices = ok ? precise : null;
|
|
19725
|
+
} else if (paraTextPureT(para, xml)) {
|
|
19726
|
+
let text = para.text;
|
|
19727
|
+
for (let k = merged.length - 1; k >= 0; k--) {
|
|
19728
|
+
const r = merged[k];
|
|
19729
|
+
text = text.slice(0, r.start) + r.replacement + text.slice(r.end);
|
|
19730
|
+
}
|
|
19731
|
+
paraSplices = buildParagraphSplices(para, text, xml);
|
|
19732
|
+
} else {
|
|
19733
|
+
paraSplices = null;
|
|
19942
19734
|
}
|
|
19943
19735
|
}
|
|
19736
|
+
if (paraSplices === null) {
|
|
19737
|
+
for (const idx of l.filledIdx) filled[idx] = null;
|
|
19738
|
+
for (const k of l.matchKeys) failedKeys.add(k);
|
|
19739
|
+
continue;
|
|
19740
|
+
}
|
|
19741
|
+
for (const k of l.matchKeys) succeededKeys.add(k);
|
|
19742
|
+
splices.push(...paraSplices);
|
|
19743
|
+
}
|
|
19744
|
+
if (splices.length > 0) {
|
|
19745
|
+
replacements.set(sectionPaths[si], encoder.encode(applySplices(xml, splices)));
|
|
19746
|
+
}
|
|
19747
|
+
}
|
|
19748
|
+
for (const k of failedKeys) {
|
|
19749
|
+
if (!succeededKeys.has(k)) matchedLabels.delete(k);
|
|
19750
|
+
}
|
|
19751
|
+
const cleanFilled = filled.filter((f) => f !== null);
|
|
19752
|
+
const unmatched = resolveUnmatched(normalizedValues, matchedLabels, values);
|
|
19753
|
+
const out = replacements.size > 0 ? patchZipEntries(u8, replacements) : new Uint8Array(u8);
|
|
19754
|
+
return {
|
|
19755
|
+
buffer: out.buffer.slice(out.byteOffset, out.byteOffset + out.byteLength),
|
|
19756
|
+
filled: cleanFilled,
|
|
19757
|
+
unmatched
|
|
19758
|
+
};
|
|
19759
|
+
}
|
|
19760
|
+
|
|
19761
|
+
// src/hwpx/generator.ts
|
|
19762
|
+
import JSZip5 from "jszip";
|
|
19763
|
+
|
|
19764
|
+
// src/hwpx/gongmun.ts
|
|
19765
|
+
var OFFICIAL_MARGINS = { top: 20, bottom: 10, left: 20, right: 20 };
|
|
19766
|
+
var PRESET_DEFAULTS = {
|
|
19767
|
+
official: { bodyPt: 15, lineSpacing: 160, numbering: "standard" },
|
|
19768
|
+
report: { bodyPt: 15, lineSpacing: 160, numbering: "report" },
|
|
19769
|
+
plan: { bodyPt: 15, lineSpacing: 160, numbering: "standard" },
|
|
19770
|
+
notice: { bodyPt: 15, lineSpacing: 160, numbering: "standard" },
|
|
19771
|
+
minutes: { bodyPt: 14, lineSpacing: 130, numbering: "standard" }
|
|
19772
|
+
};
|
|
19773
|
+
function resolveGongmun(opts) {
|
|
19774
|
+
const preset = opts.preset ?? "official";
|
|
19775
|
+
const d = PRESET_DEFAULTS[preset];
|
|
19776
|
+
const bodyPt = opts.bodyPt ?? d.bodyPt;
|
|
19777
|
+
return {
|
|
19778
|
+
preset,
|
|
19779
|
+
bodyFont: opts.bodyFont ?? "myeongjo",
|
|
19780
|
+
bodyHeight: Math.round(bodyPt * 100),
|
|
19781
|
+
lineSpacing: opts.lineSpacing ?? d.lineSpacing,
|
|
19782
|
+
numbering: opts.numbering ?? d.numbering,
|
|
19783
|
+
margins: opts.margins ?? OFFICIAL_MARGINS,
|
|
19784
|
+
centerTitle: opts.centerTitle ?? true
|
|
19785
|
+
};
|
|
19786
|
+
}
|
|
19787
|
+
var HANGUL_INITIALS = [0, 2, 3, 5, 6, 7, 9, 11, 12, 14, 15, 16, 17, 18];
|
|
19788
|
+
var HANGUL_MEDIALS = [0, 4, 8, 13, 18, 20];
|
|
19789
|
+
function hangulOrdinal(n) {
|
|
19790
|
+
const cols = HANGUL_INITIALS.length;
|
|
19791
|
+
const vowel = HANGUL_MEDIALS[Math.min(Math.floor(n / cols), HANGUL_MEDIALS.length - 1)];
|
|
19792
|
+
const init = HANGUL_INITIALS[n % cols];
|
|
19793
|
+
return String.fromCodePoint(44032 + init * 588 + vowel * 28);
|
|
19794
|
+
}
|
|
19795
|
+
function circledNumber(n) {
|
|
19796
|
+
return String.fromCodePoint(9312 + n % 20);
|
|
19797
|
+
}
|
|
19798
|
+
function circledHangul(n) {
|
|
19799
|
+
return String.fromCodePoint(12910 + n % 14);
|
|
19800
|
+
}
|
|
19801
|
+
var REPORT_BULLETS = ["\u25A1", "\u25CB", "-", "\u318D"];
|
|
19802
|
+
function standardMarker(depth, n) {
|
|
19803
|
+
switch (depth) {
|
|
19804
|
+
case 0:
|
|
19805
|
+
return `${n + 1}.`;
|
|
19806
|
+
case 1:
|
|
19807
|
+
return `${hangulOrdinal(n)}.`;
|
|
19808
|
+
case 2:
|
|
19809
|
+
return `${n + 1})`;
|
|
19810
|
+
case 3:
|
|
19811
|
+
return `${hangulOrdinal(n)})`;
|
|
19812
|
+
case 4:
|
|
19813
|
+
return `(${n + 1})`;
|
|
19814
|
+
case 5:
|
|
19815
|
+
return `(${hangulOrdinal(n)})`;
|
|
19816
|
+
case 6:
|
|
19817
|
+
return circledNumber(n);
|
|
19818
|
+
case 7:
|
|
19819
|
+
return circledHangul(n);
|
|
19820
|
+
default:
|
|
19821
|
+
return circledHangul(n);
|
|
19822
|
+
}
|
|
19823
|
+
}
|
|
19824
|
+
function reportMarker(depth) {
|
|
19825
|
+
return REPORT_BULLETS[Math.min(depth, REPORT_BULLETS.length - 1)];
|
|
19826
|
+
}
|
|
19827
|
+
function levelIndent(depth, bodyHeight, numbering) {
|
|
19828
|
+
const ta = bodyHeight / 2;
|
|
19829
|
+
const markerTa = numbering === "standard" && (depth === 4 || depth === 5) ? 3 : 2;
|
|
19830
|
+
const hang = Math.round((markerTa + 1) * ta);
|
|
19831
|
+
return { left: Math.round(depth * bodyHeight), indent: -hang };
|
|
19832
|
+
}
|
|
19833
|
+
function computeSuppression(depths) {
|
|
19834
|
+
const counts = /* @__PURE__ */ new Map();
|
|
19835
|
+
const keys = [];
|
|
19836
|
+
const path = [];
|
|
19837
|
+
for (const depth of depths) {
|
|
19838
|
+
path.length = depth + 1;
|
|
19839
|
+
path[depth] = (path[depth] ?? 0) + 1;
|
|
19840
|
+
const parentKey = path.slice(0, depth).join(".") + "|" + depth;
|
|
19841
|
+
keys.push(parentKey);
|
|
19842
|
+
counts.set(parentKey, (counts.get(parentKey) ?? 0) + 1);
|
|
19843
|
+
}
|
|
19844
|
+
return keys.map((k) => (counts.get(k) ?? 0) <= 1);
|
|
19845
|
+
}
|
|
19846
|
+
var GongmunNumberer = class {
|
|
19847
|
+
constructor(numbering) {
|
|
19848
|
+
this.numbering = numbering;
|
|
19849
|
+
}
|
|
19850
|
+
counts = [];
|
|
19851
|
+
/** depth 항목 하나에 대한 마커. suppress=true면 빈 문자열(부호 없음) */
|
|
19852
|
+
next(depth, suppress) {
|
|
19853
|
+
this.counts.length = depth + 1;
|
|
19854
|
+
const n = this.counts[depth] ?? 0;
|
|
19855
|
+
this.counts[depth] = n + 1;
|
|
19856
|
+
if (suppress) return "";
|
|
19857
|
+
return this.numbering === "report" ? reportMarker(depth) : standardMarker(depth, n);
|
|
19858
|
+
}
|
|
19859
|
+
reset() {
|
|
19860
|
+
this.counts = [];
|
|
19861
|
+
}
|
|
19862
|
+
};
|
|
19863
|
+
function mmToHwpunit(mm) {
|
|
19864
|
+
return Math.round(mm * 7200 / 25.4);
|
|
19865
|
+
}
|
|
19866
|
+
|
|
19867
|
+
// src/hwpx/generator.ts
|
|
19868
|
+
var NS_SECTION = "http://www.hancom.co.kr/hwpml/2011/section";
|
|
19869
|
+
var NS_PARA = "http://www.hancom.co.kr/hwpml/2011/paragraph";
|
|
19870
|
+
var NS_HEAD = "http://www.hancom.co.kr/hwpml/2011/head";
|
|
19871
|
+
var NS_CORE = "http://www.hancom.co.kr/hwpml/2011/core";
|
|
19872
|
+
var NS_OPF = "http://www.idpf.org/2007/opf/";
|
|
19873
|
+
var NS_HPF = "http://www.hancom.co.kr/schema/2011/hpf";
|
|
19874
|
+
var NS_OCF = "urn:oasis:names:tc:opendocument:xmlns:container";
|
|
19875
|
+
var CHAR_NORMAL = 0;
|
|
19876
|
+
var CHAR_BOLD = 1;
|
|
19877
|
+
var CHAR_ITALIC = 2;
|
|
19878
|
+
var CHAR_BOLD_ITALIC = 3;
|
|
19879
|
+
var CHAR_CODE = 4;
|
|
19880
|
+
var CHAR_H1 = 5;
|
|
19881
|
+
var CHAR_H2 = 6;
|
|
19882
|
+
var CHAR_H3 = 7;
|
|
19883
|
+
var CHAR_H4 = 8;
|
|
19884
|
+
var CHAR_TABLE_HEADER = 9;
|
|
19885
|
+
var CHAR_QUOTE = 10;
|
|
19886
|
+
var PARA_NORMAL = 0;
|
|
19887
|
+
var PARA_H1 = 1;
|
|
19888
|
+
var PARA_H2 = 2;
|
|
19889
|
+
var PARA_H3 = 3;
|
|
19890
|
+
var PARA_H4 = 4;
|
|
19891
|
+
var PARA_CODE = 5;
|
|
19892
|
+
var PARA_QUOTE = 6;
|
|
19893
|
+
var PARA_LIST = 7;
|
|
19894
|
+
var DEFAULT_TEXT_COLOR = "#000000";
|
|
19895
|
+
function resolveTheme(theme) {
|
|
19896
|
+
return {
|
|
19897
|
+
h1: theme?.headingColors?.[1] ?? DEFAULT_TEXT_COLOR,
|
|
19898
|
+
h2: theme?.headingColors?.[2] ?? DEFAULT_TEXT_COLOR,
|
|
19899
|
+
h3: theme?.headingColors?.[3] ?? DEFAULT_TEXT_COLOR,
|
|
19900
|
+
h4: theme?.headingColors?.[4] ?? theme?.headingColors?.[3] ?? DEFAULT_TEXT_COLOR,
|
|
19901
|
+
body: theme?.bodyColor ?? DEFAULT_TEXT_COLOR,
|
|
19902
|
+
quote: theme?.quoteColor ?? DEFAULT_TEXT_COLOR,
|
|
19903
|
+
/** quoteColor가 명시되었는지 — blockquote charPr 분기에 사용 (baseline 호환) */
|
|
19904
|
+
hasQuoteOption: theme?.quoteColor !== void 0,
|
|
19905
|
+
tableHeader: theme?.tableHeaderColor ?? theme?.bodyColor ?? DEFAULT_TEXT_COLOR,
|
|
19906
|
+
tableHeaderBold: !!theme?.tableHeaderBold
|
|
19907
|
+
};
|
|
19908
|
+
}
|
|
19909
|
+
async function markdownToHwpx(markdown, options) {
|
|
19910
|
+
const theme = resolveTheme(options?.theme);
|
|
19911
|
+
const gongmun = options?.gongmun ? resolveGongmun(options.gongmun) : null;
|
|
19912
|
+
const blocks = parseMarkdownToBlocks(markdown);
|
|
19913
|
+
const sectionXml = blocksToSectionXml(blocks, theme, gongmun);
|
|
19914
|
+
const zip = new JSZip5();
|
|
19915
|
+
zip.file("mimetype", "application/hwp+zip", { compression: "STORE" });
|
|
19916
|
+
zip.file("META-INF/container.xml", generateContainerXml());
|
|
19917
|
+
zip.file("Contents/content.hpf", generateManifest());
|
|
19918
|
+
zip.file("Contents/header.xml", generateHeaderXml(theme, gongmun));
|
|
19919
|
+
zip.file("Contents/section0.xml", sectionXml);
|
|
19920
|
+
zip.file("Preview/PrvText.txt", buildPrvText(blocks));
|
|
19921
|
+
return await zip.generateAsync({ type: "arraybuffer" });
|
|
19922
|
+
}
|
|
19923
|
+
function buildPrvText(blocks) {
|
|
19924
|
+
const lines = [];
|
|
19925
|
+
let bytes = 0;
|
|
19926
|
+
for (const b of blocks) {
|
|
19927
|
+
const text = b.text || (b.rows ? b.rows.map((r) => r.join(" ")).join("\n") : "");
|
|
19928
|
+
if (!text) continue;
|
|
19929
|
+
lines.push(text);
|
|
19930
|
+
bytes += text.length * 3;
|
|
19931
|
+
if (bytes > 1024) break;
|
|
19932
|
+
}
|
|
19933
|
+
return lines.join("\n").slice(0, 1024);
|
|
19934
|
+
}
|
|
19935
|
+
function parseMarkdownToBlocks(md2) {
|
|
19936
|
+
const lines = md2.split("\n");
|
|
19937
|
+
const blocks = [];
|
|
19938
|
+
let i = 0;
|
|
19939
|
+
while (i < lines.length) {
|
|
19940
|
+
const line = lines[i];
|
|
19941
|
+
if (!line.trim()) {
|
|
19942
|
+
i++;
|
|
19943
|
+
continue;
|
|
19944
|
+
}
|
|
19945
|
+
const fenceMatch = line.match(/^(`{3,}|~{3,})(.*)$/);
|
|
19946
|
+
if (fenceMatch) {
|
|
19947
|
+
const fence = fenceMatch[1];
|
|
19948
|
+
const lang = fenceMatch[2].trim();
|
|
19949
|
+
const codeLines = [];
|
|
19950
|
+
i++;
|
|
19951
|
+
while (i < lines.length && !lines[i].startsWith(fence)) {
|
|
19952
|
+
codeLines.push(lines[i]);
|
|
19953
|
+
i++;
|
|
19954
|
+
}
|
|
19955
|
+
if (i < lines.length) i++;
|
|
19956
|
+
blocks.push({ type: "code_block", text: codeLines.join("\n"), lang });
|
|
19944
19957
|
continue;
|
|
19945
19958
|
}
|
|
19946
|
-
|
|
19947
|
-
|
|
19948
|
-
|
|
19949
|
-
const isSelfClose = selfClose === "/";
|
|
19950
|
-
const contentStart = m.index + full.length;
|
|
19951
|
-
if (isSelfClose) {
|
|
19952
|
-
if (local === "t") {
|
|
19953
|
-
const para = owningPara();
|
|
19954
|
-
if (para) para.tRanges.push({ contentStart: m.index, contentEnd: m.index + full.length, selfClosing: true, prefix: prefixOf(qname) });
|
|
19955
|
-
} else if (local === "tab" || local === "fwSpace" || local === "hwSpace" || local === "br" || local === "lineBreak") {
|
|
19956
|
-
if (!pendingT) {
|
|
19957
|
-
const para = owningPara();
|
|
19958
|
-
if (para) para.text += " ";
|
|
19959
|
-
}
|
|
19960
|
-
} else if (local === "run" || local === "r") {
|
|
19961
|
-
const para = owningPara();
|
|
19962
|
-
if (para && !para.selfCloseRun) para.selfCloseRun = { start: m.index, end: m.index + full.length };
|
|
19963
|
-
} else if (local === "cellAddr") {
|
|
19964
|
-
const cell = cellStack[cellStack.length - 1];
|
|
19965
|
-
if (cell && insideCurrentTable(stack, tableStack)) {
|
|
19966
|
-
const ca = parseInt(getAttr2(attrsRaw, "colAddr") || "", 10);
|
|
19967
|
-
const ra = parseInt(getAttr2(attrsRaw, "rowAddr") || "", 10);
|
|
19968
|
-
if (!isNaN(ca)) cell.colAddr = ca;
|
|
19969
|
-
if (!isNaN(ra)) cell.rowAddr = ra;
|
|
19970
|
-
}
|
|
19971
|
-
} else if (local === "cellSpan") {
|
|
19972
|
-
const cell = cellStack[cellStack.length - 1];
|
|
19973
|
-
if (cell && insideCurrentTable(stack, tableStack)) {
|
|
19974
|
-
const cs = parseInt(getAttr2(attrsRaw, "colSpan") || "1", 10);
|
|
19975
|
-
const rs = parseInt(getAttr2(attrsRaw, "rowSpan") || "1", 10);
|
|
19976
|
-
cell.colSpan = isNaN(cs) || cs < 1 ? 1 : cs;
|
|
19977
|
-
cell.rowSpan = isNaN(rs) || rs < 1 ? 1 : rs;
|
|
19978
|
-
}
|
|
19979
|
-
}
|
|
19959
|
+
if (/^(\*{3,}|-{3,}|_{3,})\s*$/.test(line.trim())) {
|
|
19960
|
+
blocks.push({ type: "hr" });
|
|
19961
|
+
i++;
|
|
19980
19962
|
continue;
|
|
19981
19963
|
}
|
|
19982
|
-
|
|
19983
|
-
|
|
19984
|
-
|
|
19985
|
-
|
|
19964
|
+
const headingMatch = line.match(/^(#{1,6})\s+(.+)$/);
|
|
19965
|
+
if (headingMatch) {
|
|
19966
|
+
blocks.push({ type: "heading", text: headingMatch[2].trim(), level: headingMatch[1].length });
|
|
19967
|
+
i++;
|
|
19986
19968
|
continue;
|
|
19987
19969
|
}
|
|
19988
|
-
|
|
19989
|
-
|
|
19990
|
-
|
|
19991
|
-
|
|
19992
|
-
|
|
19993
|
-
|
|
19994
|
-
|
|
19995
|
-
tRanges: [],
|
|
19996
|
-
text: ""
|
|
19997
|
-
};
|
|
19998
|
-
stack.pop();
|
|
19999
|
-
para.kind = classifyPara();
|
|
20000
|
-
stack.push({ local, qname, contentStart });
|
|
20001
|
-
paraStack.push(para);
|
|
20002
|
-
if (para.kind === "body" || para.kind === "draw") bodyParagraphs.push(para);
|
|
20003
|
-
else if (para.kind === "cell") {
|
|
20004
|
-
const cell = cellStack[cellStack.length - 1];
|
|
20005
|
-
if (cell) cell.paragraphs.push(para);
|
|
20006
|
-
}
|
|
20007
|
-
} else if (local === "run" || local === "r") {
|
|
20008
|
-
const para = owningPara();
|
|
20009
|
-
if (para && para.runPrefix === void 0) para.runPrefix = prefixOf(qname);
|
|
20010
|
-
} else if (local === "tbl") {
|
|
20011
|
-
const table = {
|
|
20012
|
-
sectionIndex,
|
|
20013
|
-
start: m.index,
|
|
20014
|
-
topLevel: false,
|
|
20015
|
-
rows: [],
|
|
20016
|
-
cellByAnchor: /* @__PURE__ */ new Map()
|
|
20017
|
-
};
|
|
20018
|
-
stack.pop();
|
|
20019
|
-
table.topLevel = isTableTopLevel();
|
|
20020
|
-
stack.push({ local, qname, contentStart });
|
|
20021
|
-
tableStack.push(table);
|
|
20022
|
-
rowStack.push([]);
|
|
20023
|
-
if (table.topLevel) tables.push(table);
|
|
20024
|
-
} else if (local === "tr") {
|
|
20025
|
-
if (rowStack.length > 0) rowStack[rowStack.length - 1] = [];
|
|
20026
|
-
} else if (local === "tc") {
|
|
20027
|
-
cellStack.push({ colSpan: 1, rowSpan: 1, paragraphs: [], tables: [] });
|
|
20028
|
-
} else if (local === "cellAddr" || local === "cellSpan") {
|
|
20029
|
-
const cell = cellStack[cellStack.length - 1];
|
|
20030
|
-
if (cell && insideCurrentTable(stack, tableStack)) {
|
|
20031
|
-
if (local === "cellAddr") {
|
|
20032
|
-
const ca = parseInt(getAttr2(attrsRaw, "colAddr") || "", 10);
|
|
20033
|
-
const ra = parseInt(getAttr2(attrsRaw, "rowAddr") || "", 10);
|
|
20034
|
-
if (!isNaN(ca)) cell.colAddr = ca;
|
|
20035
|
-
if (!isNaN(ra)) cell.rowAddr = ra;
|
|
20036
|
-
} else {
|
|
20037
|
-
const cs = parseInt(getAttr2(attrsRaw, "colSpan") || "1", 10);
|
|
20038
|
-
const rs = parseInt(getAttr2(attrsRaw, "rowSpan") || "1", 10);
|
|
20039
|
-
cell.colSpan = isNaN(cs) || cs < 1 ? 1 : cs;
|
|
20040
|
-
cell.rowSpan = isNaN(rs) || rs < 1 ? 1 : rs;
|
|
19970
|
+
if (line.trimStart().startsWith("|")) {
|
|
19971
|
+
const tableRows = [];
|
|
19972
|
+
while (i < lines.length && lines[i].trimStart().startsWith("|")) {
|
|
19973
|
+
const row = lines[i];
|
|
19974
|
+
if (/^[\s|:\-]+$/.test(row)) {
|
|
19975
|
+
i++;
|
|
19976
|
+
continue;
|
|
20041
19977
|
}
|
|
19978
|
+
const cells = row.split("|").slice(1, -1).map((c) => c.trim());
|
|
19979
|
+
if (cells.length > 0) tableRows.push(cells);
|
|
19980
|
+
i++;
|
|
20042
19981
|
}
|
|
20043
|
-
|
|
20044
|
-
|
|
20045
|
-
|
|
19982
|
+
if (tableRows.length > 0) blocks.push({ type: "table", rows: tableRows });
|
|
19983
|
+
continue;
|
|
19984
|
+
}
|
|
19985
|
+
if (line.trimStart().startsWith("> ")) {
|
|
19986
|
+
const quoteLines = [];
|
|
19987
|
+
while (i < lines.length && (lines[i].trimStart().startsWith("> ") || lines[i].trimStart().startsWith(">"))) {
|
|
19988
|
+
quoteLines.push(lines[i].replace(/^>\s?/, ""));
|
|
19989
|
+
i++;
|
|
20046
19990
|
}
|
|
20047
|
-
|
|
20048
|
-
|
|
20049
|
-
|
|
19991
|
+
for (const ql of quoteLines) {
|
|
19992
|
+
blocks.push({ type: "blockquote", text: ql.trim() || "" });
|
|
19993
|
+
}
|
|
19994
|
+
continue;
|
|
19995
|
+
}
|
|
19996
|
+
const listMatch = line.match(/^(\s*)([-*+]|\d+[.)]) (.+)$/);
|
|
19997
|
+
if (listMatch) {
|
|
19998
|
+
const indent = Math.floor(listMatch[1].length / 2);
|
|
19999
|
+
const ordered = /\d/.test(listMatch[2]);
|
|
20000
|
+
blocks.push({ type: "list_item", text: listMatch[3].trim(), ordered, indent });
|
|
20001
|
+
i++;
|
|
20002
|
+
continue;
|
|
20050
20003
|
}
|
|
20004
|
+
blocks.push({ type: "paragraph", text: line.trim() });
|
|
20005
|
+
i++;
|
|
20051
20006
|
}
|
|
20052
|
-
|
|
20053
|
-
|
|
20054
|
-
|
|
20055
|
-
|
|
20056
|
-
|
|
20057
|
-
|
|
20058
|
-
|
|
20059
|
-
|
|
20007
|
+
return blocks;
|
|
20008
|
+
}
|
|
20009
|
+
function parseInlineMarkdown(text) {
|
|
20010
|
+
text = text.replace(/!\[([^\]]*)\]\([^)]*\)/g, "$1");
|
|
20011
|
+
text = text.replace(/\[([^\]]*)\]\(([^)]*)\)/g, (_, t, u) => t || u);
|
|
20012
|
+
text = text.replace(/~~([^~]+)~~/g, "$1");
|
|
20013
|
+
const spans = [];
|
|
20014
|
+
const regex = /(`[^`]+`|\*{3}[^*]+\*{3}|\*{2}[^*]+\*{2}|\*[^*]+\*|_{2}[^_]+_{2}|_[^_]+_)/g;
|
|
20015
|
+
let lastIdx = 0;
|
|
20016
|
+
for (const match of text.matchAll(regex)) {
|
|
20017
|
+
const idx = match.index;
|
|
20018
|
+
if (idx > lastIdx) {
|
|
20019
|
+
spans.push({ text: text.slice(lastIdx, idx), bold: false, italic: false, code: false });
|
|
20060
20020
|
}
|
|
20061
|
-
|
|
20062
|
-
|
|
20063
|
-
|
|
20021
|
+
const raw = match[0];
|
|
20022
|
+
if (raw.startsWith("`")) {
|
|
20023
|
+
spans.push({ text: raw.slice(1, -1), bold: false, italic: false, code: true });
|
|
20024
|
+
} else if (raw.startsWith("***") || raw.startsWith("___")) {
|
|
20025
|
+
spans.push({ text: raw.slice(3, -3), bold: true, italic: true, code: false });
|
|
20026
|
+
} else if (raw.startsWith("**") || raw.startsWith("__")) {
|
|
20027
|
+
spans.push({ text: raw.slice(2, -2), bold: true, italic: false, code: false });
|
|
20028
|
+
} else {
|
|
20029
|
+
spans.push({ text: raw.slice(1, -1), bold: false, italic: true, code: false });
|
|
20030
|
+
}
|
|
20031
|
+
lastIdx = idx + raw.length;
|
|
20032
|
+
}
|
|
20033
|
+
if (lastIdx < text.length) {
|
|
20034
|
+
spans.push({ text: text.slice(lastIdx), bold: false, italic: false, code: false });
|
|
20035
|
+
}
|
|
20036
|
+
if (spans.length === 0) {
|
|
20037
|
+
spans.push({ text, bold: false, italic: false, code: false });
|
|
20038
|
+
}
|
|
20039
|
+
return spans;
|
|
20040
|
+
}
|
|
20041
|
+
function spanToCharPrId(span) {
|
|
20042
|
+
if (span.code) return CHAR_CODE;
|
|
20043
|
+
if (span.bold && span.italic) return CHAR_BOLD_ITALIC;
|
|
20044
|
+
if (span.bold) return CHAR_BOLD;
|
|
20045
|
+
if (span.italic) return CHAR_ITALIC;
|
|
20046
|
+
return CHAR_NORMAL;
|
|
20047
|
+
}
|
|
20048
|
+
function escapeXml(text) {
|
|
20049
|
+
return text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
20050
|
+
}
|
|
20051
|
+
function generateRuns(text, defaultCharPr = CHAR_NORMAL) {
|
|
20052
|
+
const spans = parseInlineMarkdown(text);
|
|
20053
|
+
return spans.map((span) => {
|
|
20054
|
+
const charId = span.code || span.bold || span.italic ? spanToCharPrId(span) : defaultCharPr;
|
|
20055
|
+
return `<hp:run charPrIDRef="${charId}"><hp:t>${escapeXml(span.text)}</hp:t></hp:run>`;
|
|
20056
|
+
}).join("");
|
|
20057
|
+
}
|
|
20058
|
+
function generateParagraph(text, paraPrId = PARA_NORMAL, charPrId = CHAR_NORMAL) {
|
|
20059
|
+
if (paraPrId === PARA_CODE) {
|
|
20060
|
+
return `<hp:p paraPrIDRef="${paraPrId}" styleIDRef="0"><hp:run charPrIDRef="${CHAR_CODE}"><hp:t>${escapeXml(text)}</hp:t></hp:run></hp:p>`;
|
|
20061
|
+
}
|
|
20062
|
+
const runs = generateRuns(text, charPrId);
|
|
20063
|
+
return `<hp:p paraPrIDRef="${paraPrId}" styleIDRef="0">${runs}</hp:p>`;
|
|
20064
|
+
}
|
|
20065
|
+
function headingParaPrId(level) {
|
|
20066
|
+
if (level === 1) return PARA_H1;
|
|
20067
|
+
if (level === 2) return PARA_H2;
|
|
20068
|
+
if (level === 3) return PARA_H3;
|
|
20069
|
+
return PARA_H4;
|
|
20070
|
+
}
|
|
20071
|
+
function headingCharPrId(level) {
|
|
20072
|
+
if (level === 1) return CHAR_H1;
|
|
20073
|
+
if (level === 2) return CHAR_H2;
|
|
20074
|
+
if (level === 3) return CHAR_H3;
|
|
20075
|
+
return CHAR_H4;
|
|
20076
|
+
}
|
|
20077
|
+
function generateContainerXml() {
|
|
20078
|
+
return `<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
|
|
20079
|
+
<ocf:container xmlns:ocf="${NS_OCF}" xmlns:hpf="${NS_HPF}">
|
|
20080
|
+
<ocf:rootfiles>
|
|
20081
|
+
<ocf:rootfile full-path="Contents/content.hpf" media-type="application/hwpml-package+xml"/>
|
|
20082
|
+
</ocf:rootfiles>
|
|
20083
|
+
</ocf:container>`;
|
|
20084
|
+
}
|
|
20085
|
+
function generateManifest() {
|
|
20086
|
+
return `<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
|
|
20087
|
+
<opf:package xmlns:opf="${NS_OPF}" xmlns:hpf="${NS_HPF}" xmlns:hh="${NS_HEAD}">
|
|
20088
|
+
<opf:manifest>
|
|
20089
|
+
<opf:item id="header" href="Contents/header.xml" media-type="application/xml"/>
|
|
20090
|
+
<opf:item id="section0" href="Contents/section0.xml" media-type="application/xml"/>
|
|
20091
|
+
</opf:manifest>
|
|
20092
|
+
<opf:spine>
|
|
20093
|
+
<opf:itemref idref="header" linear="no"/>
|
|
20094
|
+
<opf:itemref idref="section0" linear="yes"/>
|
|
20095
|
+
</opf:spine>
|
|
20096
|
+
</opf:package>`;
|
|
20097
|
+
}
|
|
20098
|
+
function charPr(id, height, bold, italic, fontId = 0, textColor = DEFAULT_TEXT_COLOR) {
|
|
20099
|
+
const boldAttr = bold ? ` bold="1"` : "";
|
|
20100
|
+
const italicAttr = italic ? ` italic="1"` : "";
|
|
20101
|
+
const effFont = bold ? 2 : fontId;
|
|
20102
|
+
return ` <hh:charPr id="${id}" height="${height}" textColor="${textColor}" shadeColor="none" useFontSpace="0" useKerning="0" symMark="NONE" borderFillIDRef="0"${boldAttr}${italicAttr}>
|
|
20103
|
+
<hh:fontRef hangul="${effFont}" latin="${effFont}" hanja="${effFont}" japanese="${effFont}" other="${effFont}" symbol="${effFont}" user="${effFont}"/>
|
|
20104
|
+
<hh:ratio hangul="100" latin="100" hanja="100" japanese="100" other="100" symbol="100" user="100"/>
|
|
20105
|
+
<hh:spacing hangul="0" latin="0" hanja="0" japanese="0" other="0" symbol="0" user="0"/>
|
|
20106
|
+
<hh:relSz hangul="100" latin="100" hanja="100" japanese="100" other="100" symbol="100" user="100"/>
|
|
20107
|
+
<hh:offset hangul="0" latin="0" hanja="0" japanese="0" other="0" symbol="0" user="0"/>
|
|
20108
|
+
</hh:charPr>`;
|
|
20109
|
+
}
|
|
20110
|
+
function paraPr(id, opts = {}) {
|
|
20111
|
+
const { align = "JUSTIFY", spaceBefore = 0, spaceAfter = 0, lineSpacing = 160, indent = 0, left = 0 } = opts;
|
|
20112
|
+
return ` <hh:paraPr id="${id}" tabPrIDRef="0" condense="0" fontLineHeight="0" snapToGrid="1" suppressLineNumbers="0" checked="0" textDir="AUTO">
|
|
20113
|
+
<hh:align horizontal="${align}" vertical="BASELINE"/>
|
|
20114
|
+
<hh:heading type="NONE" idRef="0" level="0"/>
|
|
20115
|
+
<hh:breakSetting breakLatinWord="KEEP_WORD" breakNonLatinWord="BREAK_WORD" widowOrphan="0" keepWithNext="0" keepLines="0" pageBreakBefore="0" lineWrap="BREAK"/>
|
|
20116
|
+
<hh:autoSpacing eAsianEng="0" eAsianNum="0"/>
|
|
20117
|
+
<hh:margin><hc:intent value="${indent}" unit="HWPUNIT"/><hc:left value="${left}" unit="HWPUNIT"/><hc:right value="0" unit="HWPUNIT"/><hc:prev value="${spaceBefore}" unit="HWPUNIT"/><hc:next value="${spaceAfter}" unit="HWPUNIT"/></hh:margin>
|
|
20118
|
+
<hh:lineSpacing type="PERCENT" value="${lineSpacing}"/>
|
|
20119
|
+
<hh:border borderFillIDRef="0" offsetLeft="0" offsetRight="0" offsetTop="0" offsetBottom="0" connect="0" ignoreMargin="0"/>
|
|
20120
|
+
</hh:paraPr>`;
|
|
20064
20121
|
}
|
|
20065
|
-
|
|
20066
|
-
|
|
20067
|
-
|
|
20068
|
-
|
|
20122
|
+
var GONGMUN_LIST_BASE = 8;
|
|
20123
|
+
var GONGMUN_LIST_LEVELS = 8;
|
|
20124
|
+
var GONGMUN_CENTER = GONGMUN_LIST_BASE + GONGMUN_LIST_LEVELS;
|
|
20125
|
+
function buildCharProperties(theme, gongmun) {
|
|
20126
|
+
let body = 1e3, code = 900, h1 = 1800, h2 = 1400, h3 = 1200, h4 = 1100;
|
|
20127
|
+
if (gongmun) {
|
|
20128
|
+
body = gongmun.bodyHeight;
|
|
20129
|
+
code = Math.max(body - 200, 900);
|
|
20130
|
+
h1 = gongmun.preset === "report" || gongmun.preset === "plan" ? 2e3 : 1700;
|
|
20131
|
+
h2 = 1600;
|
|
20132
|
+
h3 = body;
|
|
20133
|
+
h4 = Math.max(body - 100, 1300);
|
|
20134
|
+
}
|
|
20135
|
+
const rows = [
|
|
20136
|
+
charPr(0, body, false, false, 0, theme.body),
|
|
20137
|
+
charPr(1, body, true, false, 0, theme.body),
|
|
20138
|
+
charPr(2, body, false, true, 0, theme.body),
|
|
20139
|
+
charPr(3, body, true, true, 0, theme.body),
|
|
20140
|
+
charPr(4, code, false, false, 1),
|
|
20141
|
+
charPr(5, h1, true, false, 1, theme.h1),
|
|
20142
|
+
charPr(6, h2, true, false, 1, theme.h2),
|
|
20143
|
+
charPr(7, h3, true, false, 1, theme.h3),
|
|
20144
|
+
charPr(8, h4, true, false, 1, theme.h4),
|
|
20145
|
+
charPr(CHAR_TABLE_HEADER, body, theme.tableHeaderBold, false, 0, theme.tableHeader),
|
|
20146
|
+
charPr(CHAR_QUOTE, body, false, true, 0, theme.quote)
|
|
20147
|
+
];
|
|
20148
|
+
return `<hh:charProperties itemCnt="${rows.length}">
|
|
20149
|
+
${rows.join("\n")}
|
|
20150
|
+
</hh:charProperties>`;
|
|
20151
|
+
}
|
|
20152
|
+
function buildParaProperties(gongmun) {
|
|
20153
|
+
if (!gongmun) {
|
|
20154
|
+
const base2 = [
|
|
20155
|
+
paraPr(0),
|
|
20156
|
+
paraPr(1, { align: "LEFT", spaceBefore: 800, spaceAfter: 200, lineSpacing: 180 }),
|
|
20157
|
+
paraPr(2, { align: "LEFT", spaceBefore: 600, spaceAfter: 150, lineSpacing: 170 }),
|
|
20158
|
+
paraPr(3, { align: "LEFT", spaceBefore: 400, spaceAfter: 100, lineSpacing: 160 }),
|
|
20159
|
+
paraPr(4, { align: "LEFT", spaceBefore: 300, spaceAfter: 100, lineSpacing: 160 }),
|
|
20160
|
+
paraPr(5, { align: "LEFT", lineSpacing: 130, indent: 400 }),
|
|
20161
|
+
paraPr(6, { align: "LEFT", lineSpacing: 150, indent: 600 }),
|
|
20162
|
+
paraPr(7, { align: "LEFT", lineSpacing: 160, indent: 600 })
|
|
20163
|
+
];
|
|
20164
|
+
return `<hh:paraProperties itemCnt="${base2.length}">
|
|
20165
|
+
${base2.join("\n")}
|
|
20166
|
+
</hh:paraProperties>`;
|
|
20167
|
+
}
|
|
20168
|
+
const ls = gongmun.lineSpacing;
|
|
20169
|
+
const titleAlign = gongmun.centerTitle ? "CENTER" : "LEFT";
|
|
20170
|
+
const base = [
|
|
20171
|
+
paraPr(0, { lineSpacing: ls }),
|
|
20172
|
+
paraPr(1, { align: titleAlign, spaceBefore: 400, spaceAfter: 400, lineSpacing: ls }),
|
|
20173
|
+
paraPr(2, { align: "LEFT", spaceBefore: 600, spaceAfter: 150, lineSpacing: ls }),
|
|
20174
|
+
paraPr(3, { align: "LEFT", spaceBefore: 400, spaceAfter: 100, lineSpacing: ls }),
|
|
20175
|
+
paraPr(4, { align: "LEFT", spaceBefore: 300, spaceAfter: 100, lineSpacing: ls }),
|
|
20176
|
+
paraPr(5, { align: "LEFT", lineSpacing: 130, indent: 400 }),
|
|
20177
|
+
paraPr(6, { align: "LEFT", lineSpacing: ls, indent: 600 }),
|
|
20178
|
+
paraPr(7, { align: "LEFT", lineSpacing: ls, indent: 600 })
|
|
20179
|
+
];
|
|
20180
|
+
for (let d = 0; d < GONGMUN_LIST_LEVELS; d++) {
|
|
20181
|
+
const { left, indent } = levelIndent(d, gongmun.bodyHeight, gongmun.numbering);
|
|
20182
|
+
base.push(paraPr(GONGMUN_LIST_BASE + d, { align: "JUSTIFY", lineSpacing: ls, left, indent }));
|
|
20183
|
+
}
|
|
20184
|
+
base.push(paraPr(GONGMUN_CENTER, { align: "CENTER", lineSpacing: ls }));
|
|
20185
|
+
return `<hh:paraProperties itemCnt="${base.length}">
|
|
20186
|
+
${base.join("\n")}
|
|
20187
|
+
</hh:paraProperties>`;
|
|
20188
|
+
}
|
|
20189
|
+
function generateHeaderXml(theme, gongmun) {
|
|
20190
|
+
const bodyFace = gongmun?.bodyFont === "gothic" ? "\uB9D1\uC740 \uACE0\uB515" : "\uD568\uCD08\uB86C\uBC14\uD0D5";
|
|
20191
|
+
const charPropsXml = buildCharProperties(theme, gongmun);
|
|
20192
|
+
const paraPropsXml = buildParaProperties(gongmun);
|
|
20193
|
+
return `<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
|
|
20194
|
+
<hh:head xmlns:hh="${NS_HEAD}" xmlns:hp="${NS_PARA}" xmlns:hc="${NS_CORE}" version="1.4" secCnt="1">
|
|
20195
|
+
<hh:beginNum page="1" footnote="1" endnote="1" pic="1" tbl="1" equation="1"/>
|
|
20196
|
+
<hh:refList>
|
|
20197
|
+
<hh:fontfaces itemCnt="7">
|
|
20198
|
+
<hh:fontface lang="HANGUL" fontCnt="3">
|
|
20199
|
+
<hh:font id="0" face="${bodyFace}" type="TTF" isEmbedded="0">
|
|
20200
|
+
<hh:typeInfo familyType="FCAT_GOTHIC" weight="6" proportion="4" contrast="0" strokeVariation="1" armStyle="1" letterform="1" midline="1" xHeight="1"/>
|
|
20201
|
+
</hh:font>
|
|
20202
|
+
<hh:font id="1" face="\uD568\uCD08\uB86C\uB3CB\uC6C0" type="TTF" isEmbedded="0">
|
|
20203
|
+
<hh:typeInfo familyType="FCAT_GOTHIC" weight="6" proportion="4" contrast="0" strokeVariation="1" armStyle="1" letterform="1" midline="1" xHeight="1"/>
|
|
20204
|
+
</hh:font>
|
|
20205
|
+
<hh:font id="2" face="HY\uACAC\uACE0\uB515" type="TTF" isEmbedded="0">
|
|
20206
|
+
<hh:typeInfo familyType="FCAT_GOTHIC" weight="9" proportion="0" contrast="0" strokeVariation="1" armStyle="1" letterform="1" midline="1" xHeight="1"/>
|
|
20207
|
+
</hh:font>
|
|
20208
|
+
</hh:fontface>
|
|
20209
|
+
<hh:fontface lang="LATIN" fontCnt="3">
|
|
20210
|
+
<hh:font id="0" face="Times New Roman" type="TTF" isEmbedded="0">
|
|
20211
|
+
<hh:typeInfo familyType="FCAT_OLDSTYLE" weight="5" proportion="4" contrast="2" strokeVariation="0" armStyle="0" letterform="0" midline="0" xHeight="4"/>
|
|
20212
|
+
</hh:font>
|
|
20213
|
+
<hh:font id="1" face="Consolas" type="TTF" isEmbedded="0">
|
|
20214
|
+
<hh:typeInfo familyType="FCAT_MODERN" weight="5" proportion="0" contrast="0" strokeVariation="0" armStyle="0" letterform="0" midline="0" xHeight="0"/>
|
|
20215
|
+
</hh:font>
|
|
20216
|
+
<hh:font id="2" face="Arial Black" type="TTF" isEmbedded="0">
|
|
20217
|
+
<hh:typeInfo familyType="FCAT_GOTHIC" weight="9" proportion="0" contrast="0" strokeVariation="0" armStyle="0" letterform="0" midline="0" xHeight="0"/>
|
|
20218
|
+
</hh:font>
|
|
20219
|
+
</hh:fontface>
|
|
20220
|
+
<hh:fontface lang="HANJA" fontCnt="1">
|
|
20221
|
+
<hh:font id="0" face="\uD568\uCD08\uB86C\uBC14\uD0D5" type="TTF" isEmbedded="0">
|
|
20222
|
+
<hh:typeInfo familyType="FCAT_GOTHIC" weight="6" proportion="4" contrast="0" strokeVariation="1" armStyle="1" letterform="1" midline="1" xHeight="1"/>
|
|
20223
|
+
</hh:font>
|
|
20224
|
+
</hh:fontface>
|
|
20225
|
+
<hh:fontface lang="JAPANESE" fontCnt="1">
|
|
20226
|
+
<hh:font id="0" face="\uAD74\uB9BC" type="TTF" isEmbedded="0">
|
|
20227
|
+
<hh:typeInfo familyType="FCAT_GOTHIC" weight="6" proportion="0" contrast="0" strokeVariation="1" armStyle="1" letterform="1" midline="1" xHeight="1"/>
|
|
20228
|
+
</hh:font>
|
|
20229
|
+
</hh:fontface>
|
|
20230
|
+
<hh:fontface lang="OTHER" fontCnt="1">
|
|
20231
|
+
<hh:font id="0" face="\uAD74\uB9BC" type="TTF" isEmbedded="0">
|
|
20232
|
+
<hh:typeInfo familyType="FCAT_GOTHIC" weight="6" proportion="0" contrast="0" strokeVariation="1" armStyle="1" letterform="1" midline="1" xHeight="1"/>
|
|
20233
|
+
</hh:font>
|
|
20234
|
+
</hh:fontface>
|
|
20235
|
+
<hh:fontface lang="SYMBOL" fontCnt="1">
|
|
20236
|
+
<hh:font id="0" face="Symbol" type="TTF" isEmbedded="0">
|
|
20237
|
+
<hh:typeInfo familyType="FCAT_GOTHIC" weight="6" proportion="0" contrast="0" strokeVariation="1" armStyle="1" letterform="1" midline="1" xHeight="1"/>
|
|
20238
|
+
</hh:font>
|
|
20239
|
+
</hh:fontface>
|
|
20240
|
+
<hh:fontface lang="USER" fontCnt="1">
|
|
20241
|
+
<hh:font id="0" face="\uAD74\uB9BC" type="TTF" isEmbedded="0">
|
|
20242
|
+
<hh:typeInfo familyType="FCAT_GOTHIC" weight="6" proportion="0" contrast="0" strokeVariation="1" armStyle="1" letterform="1" midline="1" xHeight="1"/>
|
|
20243
|
+
</hh:font>
|
|
20244
|
+
</hh:fontface>
|
|
20245
|
+
</hh:fontfaces>
|
|
20246
|
+
<hh:borderFills itemCnt="2">
|
|
20247
|
+
<hh:borderFill id="0" threeD="0" shadow="0" centerLine="0" breakCellSeparateLine="0">
|
|
20248
|
+
<hh:slash type="NONE" Crooked="0" isCounter="0"/>
|
|
20249
|
+
<hh:backSlash type="NONE" Crooked="0" isCounter="0"/>
|
|
20250
|
+
<hh:leftBorder type="NONE" width="0.1 mm" color="#000000"/>
|
|
20251
|
+
<hh:rightBorder type="NONE" width="0.1 mm" color="#000000"/>
|
|
20252
|
+
<hh:topBorder type="NONE" width="0.1 mm" color="#000000"/>
|
|
20253
|
+
<hh:bottomBorder type="NONE" width="0.1 mm" color="#000000"/>
|
|
20254
|
+
<hh:diagonal type="NONE" width="0.1 mm" color="#000000"/>
|
|
20255
|
+
<hh:fillInfo/>
|
|
20256
|
+
</hh:borderFill>
|
|
20257
|
+
<hh:borderFill id="1" threeD="0" shadow="0" centerLine="0" breakCellSeparateLine="0">
|
|
20258
|
+
<hh:slash type="NONE" Crooked="0" isCounter="0"/>
|
|
20259
|
+
<hh:backSlash type="NONE" Crooked="0" isCounter="0"/>
|
|
20260
|
+
<hh:leftBorder type="SOLID" width="0.12 mm" color="#000000"/>
|
|
20261
|
+
<hh:rightBorder type="SOLID" width="0.12 mm" color="#000000"/>
|
|
20262
|
+
<hh:topBorder type="SOLID" width="0.12 mm" color="#000000"/>
|
|
20263
|
+
<hh:bottomBorder type="SOLID" width="0.12 mm" color="#000000"/>
|
|
20264
|
+
<hh:diagonal type="NONE" width="0.1 mm" color="#000000"/>
|
|
20265
|
+
<hh:fillInfo/>
|
|
20266
|
+
</hh:borderFill>
|
|
20267
|
+
</hh:borderFills>
|
|
20268
|
+
${charPropsXml}
|
|
20269
|
+
<hh:tabProperties itemCnt="0"/>
|
|
20270
|
+
<hh:numberings itemCnt="0"/>
|
|
20271
|
+
<hh:bullets itemCnt="0"/>
|
|
20272
|
+
${paraPropsXml}
|
|
20273
|
+
<hh:styles itemCnt="1">
|
|
20274
|
+
<hh:style id="0" type="PARA" name="\uBC14\uD0D5\uAE00" engName="Normal" paraPrIDRef="0" charPrIDRef="0" nextStyleIDRef="0" langIDRef="1042" lockForm="0"/>
|
|
20275
|
+
</hh:styles>
|
|
20276
|
+
</hh:refList>
|
|
20277
|
+
<hh:compatibleDocument targetProgram="HWP2018"/>
|
|
20278
|
+
</hh:head>`;
|
|
20069
20279
|
}
|
|
20070
|
-
function
|
|
20071
|
-
|
|
20072
|
-
|
|
20073
|
-
|
|
20074
|
-
|
|
20075
|
-
|
|
20076
|
-
|
|
20077
|
-
|
|
20280
|
+
function generateSecPr(gongmun) {
|
|
20281
|
+
const m = gongmun ? {
|
|
20282
|
+
top: mmToHwpunit(gongmun.margins.top),
|
|
20283
|
+
bottom: mmToHwpunit(gongmun.margins.bottom),
|
|
20284
|
+
left: mmToHwpunit(gongmun.margins.left),
|
|
20285
|
+
right: mmToHwpunit(gongmun.margins.right),
|
|
20286
|
+
header: 0,
|
|
20287
|
+
footer: 0
|
|
20288
|
+
} : { top: 8504, bottom: 4252, left: 5670, right: 4252, header: 2835, footer: 2835 };
|
|
20289
|
+
return `<hp:secPr textDirection="HORIZONTAL" spaceColumns="1134" tabStop="8000" outlineShapeIDRef="0" memoShapeIDRef="0" textVerticalWidthHead="0" masterPageCnt="0"><hp:grid lineGrid="0" charGrid="0" wonggojiFormat="0"/><hp:startNum pageStartsOn="BOTH" page="0" pic="0" tbl="0" equation="0"/><hp:visibility hideFirstHeader="0" hideFirstFooter="0" hideFirstMasterPage="0" border="SHOW_ALL" fill="SHOW_ALL" hideFirstPageNum="0" hideFirstEmptyLine="0" showLineNumber="0"/><hp:pagePr landscape="WIDELY" width="59528" height="84188" gutterType="LEFT_ONLY"><hp:margin header="${m.header}" footer="${m.footer}" gutter="0" left="${m.left}" right="${m.right}" top="${m.top}" bottom="${m.bottom}"/></hp:pagePr><hp:footNotePr><hp:autoNumFormat type="DIGIT" userChar="" prefixChar="" suffixChar=")" supscript="0"/><hp:noteLine length="-1" type="SOLID" width="0.12 mm" color="#000000"/><hp:noteSpacing betweenNotes="283" belowLine="567" aboveLine="850"/><hp:numbering type="CONTINUOUS" newNum="1"/><hp:placement place="EACH_COLUMN" beneathText="0"/></hp:footNotePr><hp:endNotePr><hp:autoNumFormat type="DIGIT" userChar="" prefixChar="" suffixChar=")" supscript="0"/><hp:noteLine length="14692344" type="SOLID" width="0.12 mm" color="#000000"/><hp:noteSpacing betweenNotes="0" belowLine="567" aboveLine="850"/><hp:numbering type="CONTINUOUS" newNum="1"/><hp:placement place="END_OF_DOCUMENT" beneathText="0"/></hp:endNotePr></hp:secPr>`;
|
|
20078
20290
|
}
|
|
20079
|
-
|
|
20080
|
-
|
|
20081
|
-
|
|
20082
|
-
|
|
20083
|
-
const slice = xml.slice(para.start, pEnd);
|
|
20084
|
-
const runOpen = slice.match(/<((?:[A-Za-z0-9_]+:)?run)(?:\s(?:"[^"]*"|'[^']*'|[^>"'])*?)?(\/?)>/);
|
|
20085
|
-
if (!runOpen || runOpen.index === void 0) return;
|
|
20086
|
-
if (runOpen[2] === "/") return;
|
|
20087
|
-
const qname = runOpen[1];
|
|
20088
|
-
const closeIdx = slice.indexOf(`</${qname}>`, runOpen.index);
|
|
20089
|
-
if (closeIdx < 0) return;
|
|
20090
|
-
para.runInsertPos = para.start + closeIdx;
|
|
20091
|
-
para.runPrefix = prefixOf(qname);
|
|
20291
|
+
var TABLE_ID_BASE = 1e3;
|
|
20292
|
+
var tableIdCounter = TABLE_ID_BASE;
|
|
20293
|
+
function nextTableId() {
|
|
20294
|
+
return ++tableIdCounter;
|
|
20092
20295
|
}
|
|
20093
|
-
function
|
|
20094
|
-
const
|
|
20095
|
-
|
|
20096
|
-
const
|
|
20097
|
-
const
|
|
20098
|
-
|
|
20099
|
-
|
|
20100
|
-
|
|
20101
|
-
|
|
20102
|
-
|
|
20103
|
-
|
|
20104
|
-
|
|
20105
|
-
|
|
20106
|
-
|
|
20296
|
+
function generateTable(rows, theme) {
|
|
20297
|
+
const rowCnt = rows.length;
|
|
20298
|
+
const colCnt = Math.max(...rows.map((r) => r.length), 1);
|
|
20299
|
+
const cellW = Math.floor(44e3 / colCnt);
|
|
20300
|
+
const cellH = 1500;
|
|
20301
|
+
const tblW = cellW * colCnt;
|
|
20302
|
+
const tblH = cellH * rowCnt;
|
|
20303
|
+
const tblId = nextTableId();
|
|
20304
|
+
const useHeaderStyle = theme.tableHeader !== theme.body || theme.tableHeaderBold;
|
|
20305
|
+
const trElements = rows.map((row, rowIdx) => {
|
|
20306
|
+
const cells = row.length < colCnt ? [...row, ...Array(colCnt - row.length).fill("")] : row;
|
|
20307
|
+
const isHeaderRow = rowIdx === 0;
|
|
20308
|
+
const headerCharPr = isHeaderRow && useHeaderStyle ? CHAR_TABLE_HEADER : CHAR_NORMAL;
|
|
20309
|
+
const tdElements = cells.map((cell, colIdx) => {
|
|
20310
|
+
const runs = generateRuns(cell, headerCharPr);
|
|
20311
|
+
const p = `<hp:p paraPrIDRef="0" styleIDRef="0">${runs}</hp:p>`;
|
|
20312
|
+
return `<hp:tc name="" header="${isHeaderRow ? 1 : 0}" hasMargin="0" protect="0" editable="1" dirty="0" borderFillIDRef="1"><hp:subList id="" textDirection="HORIZONTAL" lineWrap="BREAK" vertAlign="TOP" linkListIDRef="0" linkListNextIDRef="0" textWidth="0" textHeight="0" hasTextRef="0" hasNumRef="0">${p}</hp:subList><hp:cellAddr colAddr="${colIdx}" rowAddr="${rowIdx}"/><hp:cellSpan colSpan="1" rowSpan="1"/><hp:cellSz width="${cellW}" height="${cellH}"/><hp:cellMargin left="141" right="141" top="141" bottom="141"/></hp:tc>`;
|
|
20313
|
+
}).join("");
|
|
20314
|
+
return `<hp:tr>${tdElements}</hp:tr>`;
|
|
20315
|
+
}).join("");
|
|
20316
|
+
const tblInner = `<hp:sz width="${tblW}" widthRelTo="ABSOLUTE" height="${tblH}" heightRelTo="ABSOLUTE" protect="0"/><hp:pos treatAsChar="1" affectLSpacing="0" flowWithText="0" allowOverlap="0" holdAnchorAndSO="0" vertRelTo="PARA" horzRelTo="PARA" vertAlign="TOP" horzAlign="LEFT" vertOffset="0" horzOffset="0"/><hp:outMargin left="0" right="0" top="0" bottom="0"/><hp:inMargin left="510" right="510" top="141" bottom="141"/>` + trElements;
|
|
20317
|
+
const tbl = `<hp:tbl id="${tblId}" zOrder="0" numberingType="TABLE" pageBreak="CELL" repeatHeader="0" rowCnt="${rowCnt}" colCnt="${colCnt}" cellSpacing="0" borderFillIDRef="1" noShading="0">${tblInner}</hp:tbl>`;
|
|
20318
|
+
return `<hp:p paraPrIDRef="0" styleIDRef="0"><hp:run charPrIDRef="0">${tbl}</hp:run></hp:p>`;
|
|
20319
|
+
}
|
|
20320
|
+
function precomputeGongmunList(blocks, gongmun) {
|
|
20321
|
+
const result = /* @__PURE__ */ new Map();
|
|
20322
|
+
let i = 0;
|
|
20323
|
+
while (i < blocks.length) {
|
|
20324
|
+
if (blocks[i].type !== "list_item") {
|
|
20325
|
+
i++;
|
|
20326
|
+
continue;
|
|
20107
20327
|
}
|
|
20328
|
+
const run = [];
|
|
20329
|
+
while (i < blocks.length && blocks[i].type === "list_item") {
|
|
20330
|
+
run.push(i);
|
|
20331
|
+
i++;
|
|
20332
|
+
}
|
|
20333
|
+
const depths = run.map((bi) => Math.min(Math.max(blocks[bi].indent || 0, 0), GONGMUN_LIST_LEVELS - 1));
|
|
20334
|
+
const suppress = gongmun.numbering === "standard" ? computeSuppression(depths) : depths.map(() => false);
|
|
20335
|
+
const numberer = new GongmunNumberer(gongmun.numbering);
|
|
20336
|
+
run.forEach((bi, k) => {
|
|
20337
|
+
const marker = numberer.next(depths[k], suppress[k]);
|
|
20338
|
+
result.set(bi, { marker, depth: depths[k] });
|
|
20339
|
+
});
|
|
20108
20340
|
}
|
|
20109
|
-
return
|
|
20341
|
+
return result;
|
|
20110
20342
|
}
|
|
20111
|
-
function
|
|
20112
|
-
const
|
|
20113
|
-
|
|
20114
|
-
|
|
20115
|
-
|
|
20116
|
-
|
|
20117
|
-
|
|
20118
|
-
|
|
20343
|
+
function blocksToSectionXml(blocks, theme, gongmun) {
|
|
20344
|
+
const paraXmls = [];
|
|
20345
|
+
let isFirst = true;
|
|
20346
|
+
const orderedCounters = {};
|
|
20347
|
+
let prevWasOrdered = false;
|
|
20348
|
+
const gongmunList = gongmun ? precomputeGongmunList(blocks, gongmun) : null;
|
|
20349
|
+
for (let blockIdx = 0; blockIdx < blocks.length; blockIdx++) {
|
|
20350
|
+
const block = blocks[blockIdx];
|
|
20351
|
+
let xml = "";
|
|
20352
|
+
if (block.type !== "list_item" || !block.ordered) {
|
|
20353
|
+
if (prevWasOrdered) {
|
|
20354
|
+
for (const k of Object.keys(orderedCounters)) delete orderedCounters[+k];
|
|
20119
20355
|
}
|
|
20356
|
+
prevWasOrdered = false;
|
|
20120
20357
|
}
|
|
20121
|
-
|
|
20122
|
-
|
|
20123
|
-
|
|
20124
|
-
|
|
20125
|
-
|
|
20126
|
-
|
|
20127
|
-
|
|
20128
|
-
|
|
20129
|
-
|
|
20130
|
-
|
|
20131
|
-
|
|
20132
|
-
|
|
20133
|
-
|
|
20134
|
-
occupied[r][c] = true;
|
|
20358
|
+
switch (block.type) {
|
|
20359
|
+
case "heading": {
|
|
20360
|
+
const pId = headingParaPrId(block.level || 1);
|
|
20361
|
+
const cId = headingCharPrId(block.level || 1);
|
|
20362
|
+
xml = generateParagraph(block.text || "", pId, cId);
|
|
20363
|
+
break;
|
|
20364
|
+
}
|
|
20365
|
+
case "paragraph": {
|
|
20366
|
+
const ctr = gongmun && /^<center>([\s\S]*)<\/center>$/i.exec((block.text || "").trim());
|
|
20367
|
+
if (ctr) {
|
|
20368
|
+
xml = generateParagraph(ctr[1].trim(), GONGMUN_CENTER);
|
|
20369
|
+
} else {
|
|
20370
|
+
xml = generateParagraph(block.text || "");
|
|
20135
20371
|
}
|
|
20372
|
+
break;
|
|
20136
20373
|
}
|
|
20137
|
-
|
|
20138
|
-
|
|
20139
|
-
|
|
20140
|
-
|
|
20141
|
-
|
|
20142
|
-
|
|
20143
|
-
|
|
20144
|
-
|
|
20145
|
-
|
|
20146
|
-
|
|
20147
|
-
|
|
20148
|
-
|
|
20149
|
-
|
|
20150
|
-
|
|
20151
|
-
|
|
20152
|
-
|
|
20153
|
-
|
|
20154
|
-
|
|
20155
|
-
|
|
20374
|
+
case "code_block": {
|
|
20375
|
+
const codeLines = (block.text || "").split("\n");
|
|
20376
|
+
xml = codeLines.map((line) => generateParagraph(line || " ", PARA_CODE)).join("\n ");
|
|
20377
|
+
break;
|
|
20378
|
+
}
|
|
20379
|
+
case "blockquote":
|
|
20380
|
+
xml = generateParagraph(
|
|
20381
|
+
block.text || "",
|
|
20382
|
+
PARA_QUOTE,
|
|
20383
|
+
theme.hasQuoteOption ? CHAR_QUOTE : CHAR_NORMAL
|
|
20384
|
+
);
|
|
20385
|
+
break;
|
|
20386
|
+
case "list_item": {
|
|
20387
|
+
if (gongmun && gongmunList) {
|
|
20388
|
+
const info = gongmunList.get(blockIdx);
|
|
20389
|
+
const depth = info?.depth ?? 0;
|
|
20390
|
+
const marker2 = info?.marker ?? "";
|
|
20391
|
+
const content = block.text || "";
|
|
20392
|
+
const text = marker2 ? `${marker2} ${content}` : content;
|
|
20393
|
+
xml = generateParagraph(text, GONGMUN_LIST_BASE + depth);
|
|
20394
|
+
break;
|
|
20395
|
+
}
|
|
20396
|
+
const indent = block.indent || 0;
|
|
20397
|
+
let marker;
|
|
20398
|
+
if (block.ordered) {
|
|
20399
|
+
orderedCounters[indent] = (orderedCounters[indent] || 0) + 1;
|
|
20400
|
+
for (const k of Object.keys(orderedCounters)) {
|
|
20401
|
+
if (+k > indent) delete orderedCounters[+k];
|
|
20402
|
+
}
|
|
20403
|
+
marker = `${orderedCounters[indent]}. `;
|
|
20404
|
+
prevWasOrdered = true;
|
|
20405
|
+
} else {
|
|
20406
|
+
marker = "\xB7 ";
|
|
20407
|
+
if (prevWasOrdered) {
|
|
20408
|
+
for (const k of Object.keys(orderedCounters)) delete orderedCounters[+k];
|
|
20409
|
+
}
|
|
20410
|
+
prevWasOrdered = false;
|
|
20411
|
+
}
|
|
20412
|
+
const indentPrefix = " ".repeat(indent);
|
|
20413
|
+
xml = generateParagraph(indentPrefix + marker + (block.text || ""), PARA_LIST);
|
|
20414
|
+
break;
|
|
20156
20415
|
}
|
|
20416
|
+
case "hr":
|
|
20417
|
+
xml = `<hp:p paraPrIDRef="0" styleIDRef="0"><hp:run charPrIDRef="0"><hp:t>\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500</hp:t></hp:run></hp:p>`;
|
|
20418
|
+
break;
|
|
20419
|
+
case "table":
|
|
20420
|
+
if (block.rows) {
|
|
20421
|
+
if (isFirst) {
|
|
20422
|
+
const secRun = `<hp:run charPrIDRef="0">${generateSecPr(gongmun)}<hp:t></hp:t></hp:run>`;
|
|
20423
|
+
paraXmls.push(`<hp:p paraPrIDRef="0" styleIDRef="0">${secRun}</hp:p>`);
|
|
20424
|
+
isFirst = false;
|
|
20425
|
+
}
|
|
20426
|
+
xml = generateTable(block.rows, theme);
|
|
20427
|
+
}
|
|
20428
|
+
break;
|
|
20157
20429
|
}
|
|
20158
|
-
|
|
20159
|
-
|
|
20160
|
-
|
|
20161
|
-
|
|
20162
|
-
|
|
20163
|
-
|
|
20430
|
+
if (!xml) continue;
|
|
20431
|
+
if (isFirst && block.type !== "table") {
|
|
20432
|
+
xml = xml.replace(
|
|
20433
|
+
/<hp:run charPrIDRef="(\d+)">/,
|
|
20434
|
+
`<hp:run charPrIDRef="$1">${generateSecPr(gongmun)}`
|
|
20435
|
+
);
|
|
20436
|
+
isFirst = false;
|
|
20437
|
+
}
|
|
20438
|
+
paraXmls.push(xml);
|
|
20164
20439
|
}
|
|
20165
|
-
if (
|
|
20166
|
-
|
|
20167
|
-
const { start, end } = para.selfCloseRun;
|
|
20168
|
-
const tag = xml.slice(start, end);
|
|
20169
|
-
const qm = tag.match(/^<([^\s/>]+)/);
|
|
20170
|
-
if (!qm || !tag.endsWith("/>")) return null;
|
|
20171
|
-
const qname = qm[1];
|
|
20172
|
-
const colon = qname.indexOf(":");
|
|
20173
|
-
const prefix = colon >= 0 ? qname.slice(0, colon) + ":" : "";
|
|
20174
|
-
const opened = tag.slice(0, tag.length - 2).trimEnd() + ">";
|
|
20175
|
-
return [{ start, end, replacement: `${opened}<${prefix}t>${escaped}</${prefix}t></${qname}>` }];
|
|
20440
|
+
if (paraXmls.length === 0) {
|
|
20441
|
+
paraXmls.push(`<hp:p paraPrIDRef="0" styleIDRef="0"><hp:run charPrIDRef="0">${generateSecPr(gongmun)}<hp:t></hp:t></hp:run></hp:p>`);
|
|
20176
20442
|
}
|
|
20177
|
-
return
|
|
20443
|
+
return `<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
|
|
20444
|
+
<hs:sec xmlns:hs="${NS_SECTION}" xmlns:hp="${NS_PARA}">
|
|
20445
|
+
${paraXmls.join("\n ")}
|
|
20446
|
+
</hs:sec>`;
|
|
20178
20447
|
}
|
|
20179
|
-
|
|
20180
|
-
|
|
20181
|
-
|
|
20182
|
-
|
|
20183
|
-
|
|
20184
|
-
|
|
20448
|
+
|
|
20449
|
+
// src/diff/text-diff.ts
|
|
20450
|
+
function similarity(a, b) {
|
|
20451
|
+
if (a === b) return 1;
|
|
20452
|
+
if (!a || !b) return 0;
|
|
20453
|
+
const maxLen = Math.max(a.length, b.length);
|
|
20454
|
+
if (maxLen === 0) return 1;
|
|
20455
|
+
return 1 - levenshtein(a, b) / maxLen;
|
|
20456
|
+
}
|
|
20457
|
+
function normalizedSimilarity(a, b) {
|
|
20458
|
+
return similarity(normalize(a), normalize(b));
|
|
20459
|
+
}
|
|
20460
|
+
function normalize(s) {
|
|
20461
|
+
return s.replace(/\s+/g, " ").trim();
|
|
20462
|
+
}
|
|
20463
|
+
var MAX_LEVENSHTEIN_LEN = 1e4;
|
|
20464
|
+
function levenshtein(a, b) {
|
|
20465
|
+
if (a.length + b.length > MAX_LEVENSHTEIN_LEN) {
|
|
20466
|
+
const sampleLen = Math.min(500, a.length, b.length);
|
|
20467
|
+
let diffs = 0;
|
|
20468
|
+
for (let i = 0; i < sampleLen; i++) if (a[i] !== b[i]) diffs++;
|
|
20469
|
+
const sampleRate = sampleLen > 0 ? diffs / sampleLen : 1;
|
|
20470
|
+
return Math.abs(a.length - b.length) + Math.round(Math.min(a.length, b.length) * sampleRate);
|
|
20185
20471
|
}
|
|
20186
|
-
|
|
20187
|
-
|
|
20188
|
-
|
|
20189
|
-
|
|
20472
|
+
if (a.length > b.length) [a, b] = [b, a];
|
|
20473
|
+
const m = a.length;
|
|
20474
|
+
const n = b.length;
|
|
20475
|
+
let prev = Array.from({ length: m + 1 }, (_, i) => i);
|
|
20476
|
+
let curr = new Array(m + 1);
|
|
20477
|
+
for (let j = 1; j <= n; j++) {
|
|
20478
|
+
curr[0] = j;
|
|
20479
|
+
for (let i = 1; i <= m; i++) {
|
|
20480
|
+
if (a[i - 1] === b[j - 1]) {
|
|
20481
|
+
curr[i] = prev[i - 1];
|
|
20482
|
+
} else {
|
|
20483
|
+
curr[i] = 1 + Math.min(prev[i - 1], prev[i], curr[i - 1]);
|
|
20484
|
+
}
|
|
20485
|
+
}
|
|
20486
|
+
;
|
|
20487
|
+
[prev, curr] = [curr, prev];
|
|
20190
20488
|
}
|
|
20191
|
-
return
|
|
20489
|
+
return prev[m];
|
|
20192
20490
|
}
|
|
20193
20491
|
|
|
20194
|
-
// src/
|
|
20195
|
-
|
|
20196
|
-
|
|
20197
|
-
|
|
20198
|
-
|
|
20199
|
-
|
|
20200
|
-
|
|
20201
|
-
|
|
20492
|
+
// src/diff/compare.ts
|
|
20493
|
+
var SIMILARITY_THRESHOLD = 0.4;
|
|
20494
|
+
async function compare(bufferA, bufferB, options) {
|
|
20495
|
+
const [resultA, resultB] = await Promise.all([
|
|
20496
|
+
parse(bufferA, options),
|
|
20497
|
+
parse(bufferB, options)
|
|
20498
|
+
]);
|
|
20499
|
+
if (!resultA.success) throw new Error(`\uBB38\uC11CA \uD30C\uC2F1 \uC2E4\uD328: ${resultA.error}`);
|
|
20500
|
+
if (!resultB.success) throw new Error(`\uBB38\uC11CB \uD30C\uC2F1 \uC2E4\uD328: ${resultB.error}`);
|
|
20501
|
+
return diffBlocks(resultA.blocks, resultB.blocks);
|
|
20202
20502
|
}
|
|
20203
|
-
function
|
|
20204
|
-
const
|
|
20205
|
-
const
|
|
20206
|
-
|
|
20207
|
-
for (
|
|
20208
|
-
if (
|
|
20209
|
-
|
|
20210
|
-
|
|
20503
|
+
function diffBlocks(blocksA, blocksB) {
|
|
20504
|
+
const aligned = alignBlocks(blocksA, blocksB);
|
|
20505
|
+
const stats = { added: 0, removed: 0, modified: 0, unchanged: 0 };
|
|
20506
|
+
const diffs = [];
|
|
20507
|
+
for (const [a, b] of aligned) {
|
|
20508
|
+
if (a && b) {
|
|
20509
|
+
const sim = blockSimilarity(a, b);
|
|
20510
|
+
if (sim >= 0.99) {
|
|
20511
|
+
diffs.push({ type: "unchanged", before: a, after: b, similarity: 1 });
|
|
20512
|
+
stats.unchanged++;
|
|
20513
|
+
} else {
|
|
20514
|
+
const diff = { type: "modified", before: a, after: b, similarity: sim };
|
|
20515
|
+
if (a.type === "table" && b.type === "table" && a.table && b.table) {
|
|
20516
|
+
diff.cellDiffs = diffTableCells(a.table, b.table);
|
|
20517
|
+
}
|
|
20518
|
+
diffs.push(diff);
|
|
20519
|
+
stats.modified++;
|
|
20520
|
+
}
|
|
20521
|
+
} else if (a) {
|
|
20522
|
+
diffs.push({ type: "removed", before: a });
|
|
20523
|
+
stats.removed++;
|
|
20524
|
+
} else if (b) {
|
|
20525
|
+
diffs.push({ type: "added", after: b });
|
|
20526
|
+
stats.added++;
|
|
20211
20527
|
}
|
|
20212
20528
|
}
|
|
20213
|
-
|
|
20214
|
-
|
|
20215
|
-
|
|
20216
|
-
|
|
20217
|
-
|
|
20218
|
-
|
|
20219
|
-
|
|
20220
|
-
|
|
20529
|
+
return { stats, diffs };
|
|
20530
|
+
}
|
|
20531
|
+
function alignBlocks(a, b) {
|
|
20532
|
+
const m = a.length, n = b.length;
|
|
20533
|
+
if (m * n > 1e7) return fallbackAlign(a, b);
|
|
20534
|
+
const simCache = /* @__PURE__ */ new Map();
|
|
20535
|
+
const getSim = (i2, j2) => {
|
|
20536
|
+
const key = `${i2},${j2}`;
|
|
20537
|
+
let v = simCache.get(key);
|
|
20538
|
+
if (v === void 0) {
|
|
20539
|
+
v = blockSimilarity(a[i2], b[j2]);
|
|
20540
|
+
simCache.set(key, v);
|
|
20541
|
+
}
|
|
20542
|
+
return v;
|
|
20543
|
+
};
|
|
20544
|
+
const dp = Array.from({ length: m + 1 }, () => new Array(n + 1).fill(0));
|
|
20545
|
+
for (let i2 = 1; i2 <= m; i2++) {
|
|
20546
|
+
for (let j2 = 1; j2 <= n; j2++) {
|
|
20547
|
+
if (getSim(i2 - 1, j2 - 1) >= SIMILARITY_THRESHOLD) {
|
|
20548
|
+
dp[i2][j2] = dp[i2 - 1][j2 - 1] + 1;
|
|
20549
|
+
} else {
|
|
20550
|
+
dp[i2][j2] = Math.max(dp[i2 - 1][j2], dp[i2][j2 - 1]);
|
|
20221
20551
|
}
|
|
20222
20552
|
}
|
|
20223
20553
|
}
|
|
20224
|
-
|
|
20225
|
-
|
|
20226
|
-
|
|
20227
|
-
|
|
20228
|
-
|
|
20229
|
-
|
|
20230
|
-
|
|
20231
|
-
|
|
20232
|
-
|
|
20233
|
-
|
|
20234
|
-
|
|
20235
|
-
for (let i = 0; i < totalEntries; i++) {
|
|
20236
|
-
if (view.getUint32(pos, true) !== CD_SIG) throw new KordocError("ZIP Central Directory \uC190\uC0C1");
|
|
20237
|
-
const flags = view.getUint16(pos + 8, true);
|
|
20238
|
-
const method = view.getUint16(pos + 10, true);
|
|
20239
|
-
const crc = view.getUint32(pos + 16, true);
|
|
20240
|
-
const compSize = view.getUint32(pos + 20, true);
|
|
20241
|
-
const uncompSize = view.getUint32(pos + 24, true);
|
|
20242
|
-
const nameLen = view.getUint16(pos + 28, true);
|
|
20243
|
-
const extraLen = view.getUint16(pos + 30, true);
|
|
20244
|
-
const commentLen = view.getUint16(pos + 32, true);
|
|
20245
|
-
const localOffset = view.getUint32(pos + 42, true);
|
|
20246
|
-
if (compSize === 4294967295 || uncompSize === 4294967295 || localOffset === 4294967295) {
|
|
20247
|
-
throw new KordocError("ZIP64\uB294 \uC9C0\uC6D0\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4");
|
|
20554
|
+
const pairs = [];
|
|
20555
|
+
let i = m, j = n;
|
|
20556
|
+
while (i > 0 && j > 0) {
|
|
20557
|
+
if (getSim(i - 1, j - 1) >= SIMILARITY_THRESHOLD && dp[i][j] === dp[i - 1][j - 1] + 1) {
|
|
20558
|
+
pairs.push([i - 1, j - 1]);
|
|
20559
|
+
i--;
|
|
20560
|
+
j--;
|
|
20561
|
+
} else if (dp[i - 1][j] >= dp[i][j - 1]) {
|
|
20562
|
+
i--;
|
|
20563
|
+
} else {
|
|
20564
|
+
j--;
|
|
20248
20565
|
}
|
|
20249
|
-
const name = decoder.decode(buf.subarray(pos + 46, pos + 46 + nameLen));
|
|
20250
|
-
const cdEnd = pos + 46 + nameLen + extraLen + commentLen;
|
|
20251
|
-
entries.push({ cdStart: pos, cdEnd, name, flags, method, crc, compSize, uncompSize, localOffset });
|
|
20252
|
-
pos = cdEnd;
|
|
20253
20566
|
}
|
|
20254
|
-
|
|
20255
|
-
|
|
20256
|
-
|
|
20257
|
-
const
|
|
20258
|
-
|
|
20259
|
-
|
|
20260
|
-
|
|
20261
|
-
table[n] = c >>> 0;
|
|
20567
|
+
pairs.reverse();
|
|
20568
|
+
const result = [];
|
|
20569
|
+
let ai = 0, bi = 0;
|
|
20570
|
+
for (const [pi, pj] of pairs) {
|
|
20571
|
+
while (ai < pi) result.push([a[ai++], null]);
|
|
20572
|
+
while (bi < pj) result.push([null, b[bi++]]);
|
|
20573
|
+
result.push([a[ai++], b[bi++]]);
|
|
20262
20574
|
}
|
|
20263
|
-
|
|
20264
|
-
|
|
20265
|
-
|
|
20266
|
-
|
|
20267
|
-
|
|
20268
|
-
|
|
20575
|
+
while (ai < m) result.push([a[ai++], null]);
|
|
20576
|
+
while (bi < n) result.push([null, b[bi++]]);
|
|
20577
|
+
return result;
|
|
20578
|
+
}
|
|
20579
|
+
function fallbackAlign(a, b) {
|
|
20580
|
+
const result = [];
|
|
20581
|
+
const len = Math.max(a.length, b.length);
|
|
20582
|
+
for (let i = 0; i < len; i++) {
|
|
20583
|
+
result.push([a[i] || null, b[i] || null]);
|
|
20269
20584
|
}
|
|
20270
|
-
return
|
|
20585
|
+
return result;
|
|
20271
20586
|
}
|
|
20272
|
-
function
|
|
20273
|
-
|
|
20274
|
-
|
|
20275
|
-
|
|
20276
|
-
if (!entries.some((e) => e.name === name)) throw new KordocError(`ZIP\uC5D0 \uC5C6\uB294 \uC5D4\uD2B8\uB9AC: ${name}`);
|
|
20587
|
+
function blockSimilarity(a, b) {
|
|
20588
|
+
if (a.type !== b.type) return 0;
|
|
20589
|
+
if (a.text !== void 0 && b.text !== void 0) {
|
|
20590
|
+
return normalizedSimilarity(a.text || "", b.text || "");
|
|
20277
20591
|
}
|
|
20278
|
-
|
|
20279
|
-
|
|
20280
|
-
const newLocalOffset = /* @__PURE__ */ new Map();
|
|
20281
|
-
const newMeta = /* @__PURE__ */ new Map();
|
|
20282
|
-
let offset = 0;
|
|
20283
|
-
for (let i = 0; i < byLocal.length; i++) {
|
|
20284
|
-
const e = byLocal[i];
|
|
20285
|
-
const segEnd = i + 1 < byLocal.length ? byLocal[i + 1].localOffset : cdOffset;
|
|
20286
|
-
newLocalOffset.set(e, offset);
|
|
20287
|
-
const newData = replacements.get(e.name);
|
|
20288
|
-
if (newData === void 0) {
|
|
20289
|
-
const seg = original.subarray(e.localOffset, segEnd);
|
|
20290
|
-
segments.push(seg);
|
|
20291
|
-
offset += seg.length;
|
|
20292
|
-
continue;
|
|
20293
|
-
}
|
|
20294
|
-
if (view.getUint32(e.localOffset, true) !== LOCAL_SIG) throw new KordocError("ZIP \uB85C\uCEEC \uD5E4\uB354 \uC2DC\uADF8\uB2C8\uCC98 \uBD88\uC77C\uCE58");
|
|
20295
|
-
const nameLen = view.getUint16(e.localOffset + 26, true);
|
|
20296
|
-
const extraLen = view.getUint16(e.localOffset + 28, true);
|
|
20297
|
-
const headerLen = 30 + nameLen + extraLen;
|
|
20298
|
-
const header = copyBytes(original, e.localOffset, e.localOffset + headerLen);
|
|
20299
|
-
const hview = new DataView(header.buffer, header.byteOffset, header.byteLength);
|
|
20300
|
-
const method = e.method;
|
|
20301
|
-
const compData = method === 0 ? newData : new Uint8Array(deflateRawSync(newData));
|
|
20302
|
-
const crc = crc32(newData);
|
|
20303
|
-
const flags = e.flags & ~8;
|
|
20304
|
-
hview.setUint16(6, flags, true);
|
|
20305
|
-
hview.setUint32(14, crc, true);
|
|
20306
|
-
hview.setUint32(18, compData.length, true);
|
|
20307
|
-
hview.setUint32(22, newData.length, true);
|
|
20308
|
-
segments.push(header, compData);
|
|
20309
|
-
offset += headerLen + compData.length;
|
|
20310
|
-
newMeta.set(e, { crc, compSize: compData.length, uncompSize: newData.length, flags });
|
|
20592
|
+
if (a.type === "table" && a.table && b.table) {
|
|
20593
|
+
return tableSimilarity(a.table, b.table);
|
|
20311
20594
|
}
|
|
20312
|
-
|
|
20313
|
-
|
|
20314
|
-
|
|
20315
|
-
|
|
20316
|
-
|
|
20317
|
-
|
|
20318
|
-
|
|
20319
|
-
|
|
20320
|
-
|
|
20321
|
-
|
|
20322
|
-
|
|
20595
|
+
if (a.type === b.type) return 1;
|
|
20596
|
+
return 0;
|
|
20597
|
+
}
|
|
20598
|
+
function tableSimilarity(a, b) {
|
|
20599
|
+
const dimSim = 1 - Math.abs(a.rows * a.cols - b.rows * b.cols) / Math.max(a.rows * a.cols, b.rows * b.cols, 1);
|
|
20600
|
+
const textsA = a.cells.flat().map((c) => c.text).join(" ");
|
|
20601
|
+
const textsB = b.cells.flat().map((c) => c.text).join(" ");
|
|
20602
|
+
const contentSim = normalizedSimilarity(textsA, textsB);
|
|
20603
|
+
return dimSim * 0.3 + contentSim * 0.7;
|
|
20604
|
+
}
|
|
20605
|
+
function diffTableCells(a, b) {
|
|
20606
|
+
const maxRows = Math.max(a.rows, b.rows);
|
|
20607
|
+
const maxCols = Math.max(a.cols, b.cols);
|
|
20608
|
+
const result = [];
|
|
20609
|
+
for (let r = 0; r < maxRows; r++) {
|
|
20610
|
+
const row = [];
|
|
20611
|
+
for (let c = 0; c < maxCols; c++) {
|
|
20612
|
+
const cellA = r < a.rows && c < a.cols ? a.cells[r][c].text : void 0;
|
|
20613
|
+
const cellB = r < b.rows && c < b.cols ? b.cells[r][c].text : void 0;
|
|
20614
|
+
let type;
|
|
20615
|
+
if (cellA === void 0) type = "added";
|
|
20616
|
+
else if (cellB === void 0) type = "removed";
|
|
20617
|
+
else if (cellA === cellB) type = "unchanged";
|
|
20618
|
+
else type = "modified";
|
|
20619
|
+
row.push({ type, before: cellA, after: cellB });
|
|
20323
20620
|
}
|
|
20324
|
-
|
|
20325
|
-
offset += cd.length;
|
|
20326
|
-
}
|
|
20327
|
-
const newCdSize = offset - newCdOffset;
|
|
20328
|
-
const eocd = copyBytes(original, eocdOffset);
|
|
20329
|
-
const eview = new DataView(eocd.buffer, eocd.byteOffset, eocd.byteLength);
|
|
20330
|
-
eview.setUint32(12, newCdSize, true);
|
|
20331
|
-
eview.setUint32(16, newCdOffset, true);
|
|
20332
|
-
segments.push(eocd);
|
|
20333
|
-
offset += eocd.length;
|
|
20334
|
-
const result = new Uint8Array(offset);
|
|
20335
|
-
let pos = 0;
|
|
20336
|
-
for (const seg of segments) {
|
|
20337
|
-
result.set(seg, pos);
|
|
20338
|
-
pos += seg.length;
|
|
20621
|
+
result.push(row);
|
|
20339
20622
|
}
|
|
20340
20623
|
return result;
|
|
20341
20624
|
}
|
|
20342
20625
|
|
|
20626
|
+
// src/roundtrip/patcher.ts
|
|
20627
|
+
import JSZip6 from "jszip";
|
|
20628
|
+
|
|
20343
20629
|
// src/roundtrip/markdown-units.ts
|
|
20344
20630
|
function splitMarkdownUnits(md2) {
|
|
20345
20631
|
const lines = md2.split("\n");
|
|
@@ -20855,6 +21141,41 @@ function applyCellEdit(table, scanTable, gridR, gridC, newLines, ctx, before, af
|
|
|
20855
21141
|
return 1;
|
|
20856
21142
|
}
|
|
20857
21143
|
|
|
21144
|
+
// src/roundtrip/hwpx-entries.ts
|
|
21145
|
+
async function resolveSectionEntryNames(zip) {
|
|
21146
|
+
for (const mp of ["Contents/content.hpf", "content.hpf"]) {
|
|
21147
|
+
const f = zip.file(mp);
|
|
21148
|
+
if (!f) continue;
|
|
21149
|
+
const xml = await f.async("text");
|
|
21150
|
+
const paths = sectionPathsFromManifest(xml).filter((p) => zip.file(p) !== null);
|
|
21151
|
+
if (paths.length > 0) return paths;
|
|
21152
|
+
}
|
|
21153
|
+
return Object.keys(zip.files).filter((n) => /[Ss]ection\d+\.xml$/.test(n)).sort();
|
|
21154
|
+
}
|
|
21155
|
+
function sectionPathsFromManifest(xml) {
|
|
21156
|
+
const isSectionId = (id) => /^s/i.test(id) || id.toLowerCase().includes("section");
|
|
21157
|
+
const attr = (tag, name) => {
|
|
21158
|
+
const m = tag.match(new RegExp(`(?:^|\\s)${name}\\s*=\\s*(?:"([^"]*)"|'([^']*)')`));
|
|
21159
|
+
return m ? m[1] ?? m[2] : "";
|
|
21160
|
+
};
|
|
21161
|
+
const idToHref = /* @__PURE__ */ new Map();
|
|
21162
|
+
for (const m of xml.matchAll(/<opf:item(\s(?:"[^"]*"|'[^']*'|[^>"'])*?)\/?>/g)) {
|
|
21163
|
+
const id = attr(m[1], "id");
|
|
21164
|
+
let href = attr(m[1], "href");
|
|
21165
|
+
const mediaType = attr(m[1], "media-type");
|
|
21166
|
+
if (!isSectionId(id) && !mediaType.includes("xml")) continue;
|
|
21167
|
+
if (!href.startsWith("/") && !href.startsWith("Contents/") && isSectionId(id)) href = "Contents/" + href;
|
|
21168
|
+
if (id) idToHref.set(id, href);
|
|
21169
|
+
}
|
|
21170
|
+
const ordered = [];
|
|
21171
|
+
for (const m of xml.matchAll(/<opf:itemref(\s(?:"[^"]*"|'[^']*'|[^>"'])*?)\/?>/g)) {
|
|
21172
|
+
const href = idToHref.get(attr(m[1], "idref"));
|
|
21173
|
+
if (href) ordered.push(href);
|
|
21174
|
+
}
|
|
21175
|
+
if (ordered.length > 0) return ordered;
|
|
21176
|
+
return Array.from(idToHref.entries()).filter(([id]) => isSectionId(id)).sort((a, b) => a[0].localeCompare(b[0])).map(([, href]) => href);
|
|
21177
|
+
}
|
|
21178
|
+
|
|
20858
21179
|
// src/roundtrip/patcher.ts
|
|
20859
21180
|
async function patchHwpx(original, editedMarkdown, options) {
|
|
20860
21181
|
const skipped = [];
|
|
@@ -21213,39 +21534,6 @@ function unitToBlock(u) {
|
|
|
21213
21534
|
function u8ToArrayBuffer(u8) {
|
|
21214
21535
|
return u8.buffer.slice(u8.byteOffset, u8.byteOffset + u8.byteLength);
|
|
21215
21536
|
}
|
|
21216
|
-
async function resolveSectionEntryNames(zip) {
|
|
21217
|
-
for (const mp of ["Contents/content.hpf", "content.hpf"]) {
|
|
21218
|
-
const f = zip.file(mp);
|
|
21219
|
-
if (!f) continue;
|
|
21220
|
-
const xml = await f.async("text");
|
|
21221
|
-
const paths = sectionPathsFromManifest(xml).filter((p) => zip.file(p) !== null);
|
|
21222
|
-
if (paths.length > 0) return paths;
|
|
21223
|
-
}
|
|
21224
|
-
return Object.keys(zip.files).filter((n) => /[Ss]ection\d+\.xml$/.test(n)).sort();
|
|
21225
|
-
}
|
|
21226
|
-
function sectionPathsFromManifest(xml) {
|
|
21227
|
-
const isSectionId = (id) => /^s/i.test(id) || id.toLowerCase().includes("section");
|
|
21228
|
-
const attr = (tag, name) => {
|
|
21229
|
-
const m = tag.match(new RegExp(`(?:^|\\s)${name}\\s*=\\s*(?:"([^"]*)"|'([^']*)')`));
|
|
21230
|
-
return m ? m[1] ?? m[2] : "";
|
|
21231
|
-
};
|
|
21232
|
-
const idToHref = /* @__PURE__ */ new Map();
|
|
21233
|
-
for (const m of xml.matchAll(/<opf:item(\s(?:"[^"]*"|'[^']*'|[^>"'])*?)\/?>/g)) {
|
|
21234
|
-
const id = attr(m[1], "id");
|
|
21235
|
-
let href = attr(m[1], "href");
|
|
21236
|
-
const mediaType = attr(m[1], "media-type");
|
|
21237
|
-
if (!isSectionId(id) && !mediaType.includes("xml")) continue;
|
|
21238
|
-
if (!href.startsWith("/") && !href.startsWith("Contents/") && isSectionId(id)) href = "Contents/" + href;
|
|
21239
|
-
if (id) idToHref.set(id, href);
|
|
21240
|
-
}
|
|
21241
|
-
const ordered = [];
|
|
21242
|
-
for (const m of xml.matchAll(/<opf:itemref(\s(?:"[^"]*"|'[^']*'|[^>"'])*?)\/?>/g)) {
|
|
21243
|
-
const href = idToHref.get(attr(m[1], "idref"));
|
|
21244
|
-
if (href) ordered.push(href);
|
|
21245
|
-
}
|
|
21246
|
-
if (ordered.length > 0) return ordered;
|
|
21247
|
-
return Array.from(idToHref.entries()).filter(([id]) => isSectionId(id)).sort((a, b) => a[0].localeCompare(b[0])).map(([, href]) => href);
|
|
21248
|
-
}
|
|
21249
21537
|
|
|
21250
21538
|
// src/roundtrip/hwp5-patch.ts
|
|
21251
21539
|
import { deflateRawSync as deflateRawSync2 } from "zlib";
|
|
@@ -22079,6 +22367,350 @@ function stageParaPatch(scan, para, newPlain, skip) {
|
|
|
22079
22367
|
return 1;
|
|
22080
22368
|
}
|
|
22081
22369
|
|
|
22370
|
+
// src/roundtrip/session.ts
|
|
22371
|
+
import JSZip7 from "jszip";
|
|
22372
|
+
async function buildState(bytes) {
|
|
22373
|
+
const parsed = await parseHwpxDocument(u8ToArrayBuffer2(bytes));
|
|
22374
|
+
const zip = await JSZip7.loadAsync(bytes);
|
|
22375
|
+
const sectionPaths = await resolveSectionEntryNames(zip);
|
|
22376
|
+
if (sectionPaths.length === 0) {
|
|
22377
|
+
throw new Error("HWPX \uC139\uC158 \uD30C\uC77C\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4");
|
|
22378
|
+
}
|
|
22379
|
+
const scans = [];
|
|
22380
|
+
for (let i = 0; i < sectionPaths.length; i++) {
|
|
22381
|
+
const xml = await zip.file(sectionPaths[i]).async("text");
|
|
22382
|
+
scans.push(scanSectionXml(xml, i));
|
|
22383
|
+
}
|
|
22384
|
+
const paraMap = resolveParagraphMappings(parsed.blocks, scans);
|
|
22385
|
+
const scanTables = scans.flatMap((s) => s.tables.filter((t) => t.rows.length > 0));
|
|
22386
|
+
const tableOrdinals = buildTableOrdinals(parsed.blocks);
|
|
22387
|
+
const fragmentBlocks = /* @__PURE__ */ new Set();
|
|
22388
|
+
const unitBlocks = /* @__PURE__ */ new Set();
|
|
22389
|
+
for (const u of buildOrigUnits(parsed.blocks)) {
|
|
22390
|
+
unitBlocks.add(u.blockIdx);
|
|
22391
|
+
if (u.fragment) fragmentBlocks.add(u.blockIdx);
|
|
22392
|
+
}
|
|
22393
|
+
for (let i = 0; i < parsed.blocks.length; i++) {
|
|
22394
|
+
const b = parsed.blocks[i];
|
|
22395
|
+
if ((b.type === "paragraph" || b.type === "heading") && b.text && !unitBlocks.has(i)) {
|
|
22396
|
+
fragmentBlocks.add(i);
|
|
22397
|
+
}
|
|
22398
|
+
}
|
|
22399
|
+
return {
|
|
22400
|
+
bytes,
|
|
22401
|
+
blocks: parsed.blocks,
|
|
22402
|
+
markdown: parsed.markdown,
|
|
22403
|
+
sectionPaths,
|
|
22404
|
+
scans,
|
|
22405
|
+
paraMap,
|
|
22406
|
+
scanTables,
|
|
22407
|
+
tableOrdinals,
|
|
22408
|
+
fragmentBlocks
|
|
22409
|
+
};
|
|
22410
|
+
}
|
|
22411
|
+
function u8ToArrayBuffer2(u8) {
|
|
22412
|
+
return u8.buffer.slice(u8.byteOffset, u8.byteOffset + u8.byteLength);
|
|
22413
|
+
}
|
|
22414
|
+
function irCellLines(text) {
|
|
22415
|
+
return stripCellTokens(sanitizeText(text)).split("\n").map((s) => s.trim()).filter(Boolean);
|
|
22416
|
+
}
|
|
22417
|
+
var HwpxSession = class _HwpxSession {
|
|
22418
|
+
state;
|
|
22419
|
+
constructor(state) {
|
|
22420
|
+
this.state = state;
|
|
22421
|
+
}
|
|
22422
|
+
/** HWPX 바이트로 세션을 연다 (입력은 복사되어 외부 변이와 격리) */
|
|
22423
|
+
static async open(input) {
|
|
22424
|
+
const bytes = input instanceof Uint8Array ? new Uint8Array(input) : new Uint8Array(input.slice(0));
|
|
22425
|
+
return new _HwpxSession(await buildState(bytes));
|
|
22426
|
+
}
|
|
22427
|
+
/** 현재 문서의 IR 블록 — patchBlocks 후 갱신되므로 호출마다 다시 읽을 것 */
|
|
22428
|
+
get blocks() {
|
|
22429
|
+
return this.state.blocks;
|
|
22430
|
+
}
|
|
22431
|
+
/** 현재 문서의 마크다운 */
|
|
22432
|
+
get markdown() {
|
|
22433
|
+
return this.state.markdown;
|
|
22434
|
+
}
|
|
22435
|
+
/** 현재 문서 바이트 (복사본) */
|
|
22436
|
+
get bytes() {
|
|
22437
|
+
return new Uint8Array(this.state.bytes);
|
|
22438
|
+
}
|
|
22439
|
+
/** 블록 → 원본 위치 참조. 매핑 실패 시 undefined */
|
|
22440
|
+
sourceRef(blockIndex) {
|
|
22441
|
+
const st = this.state;
|
|
22442
|
+
const block = st.blocks[blockIndex];
|
|
22443
|
+
if (!block) return void 0;
|
|
22444
|
+
if (block.type === "paragraph" || block.type === "heading") {
|
|
22445
|
+
const para = st.paraMap.get(blockIndex)?.para;
|
|
22446
|
+
if (!para) return void 0;
|
|
22447
|
+
return { kind: "paragraph", sectionIndex: para.sectionIndex, xmlStart: para.start };
|
|
22448
|
+
}
|
|
22449
|
+
if (block.type === "table" && block.table) {
|
|
22450
|
+
if (st.tableOrdinals.size !== st.scanTables.length) return void 0;
|
|
22451
|
+
const ordinal = st.tableOrdinals.get(blockIndex);
|
|
22452
|
+
const t = ordinal !== void 0 ? st.scanTables[ordinal] : void 0;
|
|
22453
|
+
if (!t) return void 0;
|
|
22454
|
+
return { kind: "table", sectionIndex: t.sectionIndex, xmlStart: t.start };
|
|
22455
|
+
}
|
|
22456
|
+
return void 0;
|
|
22457
|
+
}
|
|
22458
|
+
/** 블록 편집 가능성 사전 판정 — patcher graceful-skip 게이트의 사전 버전 */
|
|
22459
|
+
capability(blockIndex) {
|
|
22460
|
+
const st = this.state;
|
|
22461
|
+
const block = st.blocks[blockIndex];
|
|
22462
|
+
if (!block) return { capability: "locked", reason: "\uBE14\uB85D \uC778\uB371\uC2A4 \uBC94\uC704 \uBC16" };
|
|
22463
|
+
if (block.type === "paragraph" || block.type === "heading") {
|
|
22464
|
+
if (st.fragmentBlocks.has(blockIndex)) {
|
|
22465
|
+
return { capability: "locked", reason: "\uBB38\uB2E8 \uBD84\uC808(\uAC15\uC81C \uC904\uBC14\uAFC8/\uBCD1\uD569 \uC720\uB2DB) \u2014 \uBD80\uBD84 \uC218\uC815\uC740 \uBBF8\uC9C0\uC6D0 (v1)" };
|
|
22466
|
+
}
|
|
22467
|
+
if (block.text && block.text.includes("\n")) {
|
|
22468
|
+
return { capability: "locked", reason: "\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)" };
|
|
22469
|
+
}
|
|
22470
|
+
if (!st.paraMap.get(blockIndex)?.para) {
|
|
22471
|
+
return { capability: "locked", reason: "\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)" };
|
|
22472
|
+
}
|
|
22473
|
+
return { capability: "text" };
|
|
22474
|
+
}
|
|
22475
|
+
if (block.type === "table" && block.table) {
|
|
22476
|
+
if (st.tableOrdinals.size !== st.scanTables.length) {
|
|
22477
|
+
return { capability: "locked", reason: "\uD45C \uAC1C\uC218 \uBD88\uC77C\uCE58 \u2014 \uC18C\uC2A4\uB9F5 \uC2E0\uB8B0 \uBD88\uAC00" };
|
|
22478
|
+
}
|
|
22479
|
+
const ordinal = st.tableOrdinals.get(blockIndex);
|
|
22480
|
+
const scanTable = ordinal !== void 0 ? st.scanTables[ordinal] : void 0;
|
|
22481
|
+
if (!scanTable) {
|
|
22482
|
+
return { capability: "locked", reason: "\uD45C \uC18C\uC2A4\uB9F5 \uB9E4\uD551 \uC2E4\uD328" };
|
|
22483
|
+
}
|
|
22484
|
+
const table = block.table;
|
|
22485
|
+
const cells = [];
|
|
22486
|
+
let anyEditable = false;
|
|
22487
|
+
for (let r = 0; r < table.rows; r++) {
|
|
22488
|
+
const row = [];
|
|
22489
|
+
for (let c = 0; c < table.cols; c++) {
|
|
22490
|
+
const info = cellStaticCheck(table, scanTable, r, c);
|
|
22491
|
+
if (info.editable) anyEditable = true;
|
|
22492
|
+
row.push(info);
|
|
22493
|
+
}
|
|
22494
|
+
cells.push(row);
|
|
22495
|
+
}
|
|
22496
|
+
if (!anyEditable) return { capability: "locked", reason: "\uD3B8\uC9D1 \uAC00\uB2A5\uD55C \uC140 \uC5C6\uC74C", cells };
|
|
22497
|
+
return { capability: "cell-text", cells };
|
|
22498
|
+
}
|
|
22499
|
+
return { capability: "locked", reason: `${block.type} \uBE14\uB85D \uD3B8\uC9D1\uC740 \uBBF8\uC9C0\uC6D0 (v1)` };
|
|
22500
|
+
}
|
|
22501
|
+
/** 전 블록의 편집 가능성 */
|
|
22502
|
+
capabilities() {
|
|
22503
|
+
return this.state.blocks.map((_, i) => this.capability(i));
|
|
22504
|
+
}
|
|
22505
|
+
/**
|
|
22506
|
+
* 블록 단위 증분 패치 — 적용 후 세션 상태가 새 바이트로 갱신된다.
|
|
22507
|
+
*
|
|
22508
|
+
* - 호출은 내부적으로 직렬화된다 (동시 호출 시 도착 순서대로 누적 적용)
|
|
22509
|
+
* - 무변경 편집(현재 텍스트와 동일)은 조용히 건너뜀 (applied/skipped 모두 제외)
|
|
22510
|
+
* - 변경이 하나도 적용되지 않으면 반환 data는 현재 문서와 바이트 동일
|
|
22511
|
+
* - changes는 "패치 전 → 후" 문서 diff — modified 수가 기대 편집 수와
|
|
22512
|
+
* 일치하는지 확인 용도. patchHwpx의 verification(잔차 검증)과 의미가 다르다.
|
|
22513
|
+
*/
|
|
22514
|
+
async patchBlocks(edits, options) {
|
|
22515
|
+
const run = this.opQueue.then(() => this.patchBlocksInner(edits, options));
|
|
22516
|
+
this.opQueue = run.then(() => void 0, () => void 0);
|
|
22517
|
+
return run;
|
|
22518
|
+
}
|
|
22519
|
+
opQueue = Promise.resolve();
|
|
22520
|
+
async patchBlocksInner(edits, options) {
|
|
22521
|
+
const st = this.state;
|
|
22522
|
+
const skipped = [];
|
|
22523
|
+
let applied = 0;
|
|
22524
|
+
const sectionSplices = st.scans.map(() => []);
|
|
22525
|
+
const cellCtx = { scans: st.scans, sectionSplices, skipped };
|
|
22526
|
+
const seenParas = /* @__PURE__ */ new Set();
|
|
22527
|
+
const seenCells = /* @__PURE__ */ new Set();
|
|
22528
|
+
for (const edit of edits) {
|
|
22529
|
+
const i = edit.blockIndex;
|
|
22530
|
+
const block = st.blocks[i];
|
|
22531
|
+
if (!block) {
|
|
22532
|
+
skipped.push({ reason: `\uBE14\uB85D \uC778\uB371\uC2A4 \uBC94\uC704 \uBC16: ${i}` });
|
|
22533
|
+
continue;
|
|
22534
|
+
}
|
|
22535
|
+
if (block.type === "table" && block.table) {
|
|
22536
|
+
if (!edit.cells?.length) {
|
|
22537
|
+
skipped.push({ reason: "\uD45C \uBE14\uB85D\uC5D0\uB294 cells \uD3B8\uC9D1\uB9CC \uC9C0\uC6D0", before: summarize(block.table.caption ?? "(\uD45C)") });
|
|
22538
|
+
continue;
|
|
22539
|
+
}
|
|
22540
|
+
if (st.tableOrdinals.size !== st.scanTables.length) {
|
|
22541
|
+
skipped.push({ reason: "\uD45C \uAC1C\uC218 \uBD88\uC77C\uCE58 \u2014 \uC18C\uC2A4\uB9F5 \uC2E0\uB8B0 \uBD88\uAC00" });
|
|
22542
|
+
continue;
|
|
22543
|
+
}
|
|
22544
|
+
const ordinal = st.tableOrdinals.get(i);
|
|
22545
|
+
const scanTable = ordinal !== void 0 ? st.scanTables[ordinal] : void 0;
|
|
22546
|
+
if (!scanTable) {
|
|
22547
|
+
skipped.push({ reason: "\uD45C \uC18C\uC2A4\uB9F5 \uB9E4\uD551 \uC2E4\uD328" });
|
|
22548
|
+
continue;
|
|
22549
|
+
}
|
|
22550
|
+
for (const cellEdit of edit.cells) {
|
|
22551
|
+
const key = `${i}:${cellEdit.row},${cellEdit.col}`;
|
|
22552
|
+
if (seenCells.has(key)) {
|
|
22553
|
+
skipped.push({ reason: "\uAC19\uC740 \uC140\uC5D0 \uC911\uBCF5 \uD3B8\uC9D1 \u2014 \uBA3C\uC800 \uC801\uC6A9\uB41C \uD3B8\uC9D1 \uC720\uC9C0", after: summarize(cellEdit.text) });
|
|
22554
|
+
continue;
|
|
22555
|
+
}
|
|
22556
|
+
const irCell = block.table.cells[cellEdit.row]?.[cellEdit.col];
|
|
22557
|
+
if (!irCell) {
|
|
22558
|
+
skipped.push({ reason: `\uC140 \uC88C\uD45C \uBC94\uC704 \uBC16: ${cellEdit.row},${cellEdit.col}`, after: summarize(cellEdit.text) });
|
|
22559
|
+
continue;
|
|
22560
|
+
}
|
|
22561
|
+
if (extractCellTokens(irCell.text) !== extractCellTokens(cellEdit.text)) {
|
|
22562
|
+
skipped.push({ reason: "\uC140 \uB0B4 \uC774\uBBF8\uC9C0 \uBCC0\uACBD\uC740 \uBBF8\uC9C0\uC6D0", before: summarize(irCell.text), after: summarize(cellEdit.text) });
|
|
22563
|
+
continue;
|
|
22564
|
+
}
|
|
22565
|
+
const newLines = stripCellTokens(cellEdit.text).split("\n").map((s) => s.trim()).filter(Boolean);
|
|
22566
|
+
const origLines = irCellLines(irCell.text);
|
|
22567
|
+
if (newLines.join("\n") === origLines.join("\n")) continue;
|
|
22568
|
+
const n = applyCellEdit(
|
|
22569
|
+
block.table,
|
|
22570
|
+
scanTable,
|
|
22571
|
+
cellEdit.row,
|
|
22572
|
+
cellEdit.col,
|
|
22573
|
+
newLines,
|
|
22574
|
+
cellCtx,
|
|
22575
|
+
irCell.text,
|
|
22576
|
+
cellEdit.text,
|
|
22577
|
+
origLines.length
|
|
22578
|
+
);
|
|
22579
|
+
if (n > 0) seenCells.add(key);
|
|
22580
|
+
applied += n;
|
|
22581
|
+
}
|
|
22582
|
+
continue;
|
|
22583
|
+
}
|
|
22584
|
+
if ((block.type === "paragraph" || block.type === "heading") && edit.newText !== void 0) {
|
|
22585
|
+
if (seenParas.has(i)) {
|
|
22586
|
+
skipped.push({ reason: "\uAC19\uC740 \uBE14\uB85D\uC5D0 \uC911\uBCF5 \uD3B8\uC9D1 \u2014 \uBA3C\uC800 \uC801\uC6A9\uB41C \uD3B8\uC9D1 \uC720\uC9C0", after: summarize(edit.newText) });
|
|
22587
|
+
continue;
|
|
22588
|
+
}
|
|
22589
|
+
const n = this.patchParagraphPlain(i, block, edit.newText, sectionSplices, skipped);
|
|
22590
|
+
if (n > 0) seenParas.add(i);
|
|
22591
|
+
applied += n;
|
|
22592
|
+
continue;
|
|
22593
|
+
}
|
|
22594
|
+
skipped.push({
|
|
22595
|
+
reason: `\uC9C0\uC6D0\uD558\uC9C0 \uC54A\uB294 \uBE14\uB85D \uC720\uD615(${block.type}) \uB610\uB294 \uD3B8\uC9D1 \uD615\uC2DD`,
|
|
22596
|
+
before: summarize(block.text ?? "")
|
|
22597
|
+
});
|
|
22598
|
+
}
|
|
22599
|
+
const replacements = /* @__PURE__ */ new Map();
|
|
22600
|
+
const encoder = new TextEncoder();
|
|
22601
|
+
try {
|
|
22602
|
+
for (let s = 0; s < st.scans.length; s++) {
|
|
22603
|
+
if (sectionSplices[s].length === 0) continue;
|
|
22604
|
+
replacements.set(st.sectionPaths[s], encoder.encode(applySplices(st.scans[s].xml, sectionSplices[s])));
|
|
22605
|
+
}
|
|
22606
|
+
} catch (err) {
|
|
22607
|
+
return { success: false, applied: 0, skipped, error: `\uC18C\uC2A4\uB9F5 splice \uC2E4\uD328: ${err instanceof Error ? err.message : String(err)}` };
|
|
22608
|
+
}
|
|
22609
|
+
if (replacements.size === 0) {
|
|
22610
|
+
let changes2;
|
|
22611
|
+
if (options?.verify !== false) {
|
|
22612
|
+
const units = splitMarkdownUnits(st.markdown);
|
|
22613
|
+
changes2 = diffUnitLists(units, units);
|
|
22614
|
+
}
|
|
22615
|
+
return { success: true, data: new Uint8Array(st.bytes), applied, skipped, changes: changes2 };
|
|
22616
|
+
}
|
|
22617
|
+
let data;
|
|
22618
|
+
try {
|
|
22619
|
+
data = patchZipEntries(st.bytes, replacements);
|
|
22620
|
+
} catch (err) {
|
|
22621
|
+
return { success: false, applied: 0, skipped, error: `ZIP \uC7AC\uC870\uB9BD \uC2E4\uD328: ${err instanceof Error ? err.message : String(err)}` };
|
|
22622
|
+
}
|
|
22623
|
+
const beforeMarkdown = st.markdown;
|
|
22624
|
+
let newState;
|
|
22625
|
+
try {
|
|
22626
|
+
newState = await buildState(data);
|
|
22627
|
+
} catch (err) {
|
|
22628
|
+
return { success: false, applied, skipped, error: `\uD328\uCE58\uBCF8 \uC7AC\uD30C\uC2F1 \uC2E4\uD328 \u2014 \uD328\uCE58 \uC911\uB2E8: ${err instanceof Error ? err.message : String(err)}` };
|
|
22629
|
+
}
|
|
22630
|
+
this.state = newState;
|
|
22631
|
+
let changes;
|
|
22632
|
+
if (options?.verify !== false) {
|
|
22633
|
+
changes = diffUnitLists(splitMarkdownUnits(beforeMarkdown), splitMarkdownUnits(newState.markdown));
|
|
22634
|
+
}
|
|
22635
|
+
return { success: true, data: new Uint8Array(data), applied, skipped, changes };
|
|
22636
|
+
}
|
|
22637
|
+
/** 문단/헤딩 평문 편집 — patcher.patchParagraphUnit의 평문 입력 버전 */
|
|
22638
|
+
patchParagraphPlain(blockIndex, block, newTextRaw, sectionSplices, skipped) {
|
|
22639
|
+
const skip = (reason) => {
|
|
22640
|
+
skipped.push({ reason, before: summarize(block.text ?? ""), after: summarize(newTextRaw) });
|
|
22641
|
+
return 0;
|
|
22642
|
+
};
|
|
22643
|
+
const st = this.state;
|
|
22644
|
+
if (newTextRaw === (block.text ?? "")) return 0;
|
|
22645
|
+
if (st.fragmentBlocks.has(blockIndex)) {
|
|
22646
|
+
return skip("\uBB38\uB2E8 \uBD84\uC808(\uAC15\uC81C \uC904\uBC14\uAFC8/\uBCD1\uD569 \uC720\uB2DB) \u2014 \uBD80\uBD84 \uC218\uC815\uC740 \uBBF8\uC9C0\uC6D0 (v1)");
|
|
22647
|
+
}
|
|
22648
|
+
if (block.text && block.text.includes("\n")) {
|
|
22649
|
+
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)");
|
|
22650
|
+
}
|
|
22651
|
+
const mapping = st.paraMap.get(blockIndex);
|
|
22652
|
+
if (!mapping?.para) {
|
|
22653
|
+
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)");
|
|
22654
|
+
}
|
|
22655
|
+
let newPlain = newTextRaw.split("\n").map((l) => l.trim()).filter(Boolean).join(" ");
|
|
22656
|
+
if (mapping.prefixStripped) {
|
|
22657
|
+
const origPrefix = block.text.split(" ", 1)[0];
|
|
22658
|
+
const sp = newPlain.indexOf(" ");
|
|
22659
|
+
const newFirst = sp > 0 ? newPlain.slice(0, sp) : newPlain;
|
|
22660
|
+
if (newFirst === origPrefix || /^(?:[0-90-9a-zA-Z가-힣]{1,6}[.)\]:]|[([][0-90-9a-zA-Z가-힣]{1,6}[)\]][.:]?|[ⅰ-ⅹⅠ-Ⅹ①-⑮][.)\]:]?)$/u.test(newFirst)) {
|
|
22661
|
+
newPlain = sp > 0 ? newPlain.slice(sp + 1) : "";
|
|
22662
|
+
} else {
|
|
22663
|
+
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) });
|
|
22664
|
+
}
|
|
22665
|
+
}
|
|
22666
|
+
if (newPlain === "") {
|
|
22667
|
+
return skip("\uBE14\uB85D \uBE44\uC6B0\uAE30/\uC0AD\uC81C\uB294 \uBBF8\uC9C0\uC6D0 (v1) \u2014 \uC6D0\uBCF8 \uC720\uC9C0");
|
|
22668
|
+
}
|
|
22669
|
+
if (sanitizeText(newPlain) !== newPlain) {
|
|
22670
|
+
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");
|
|
22671
|
+
}
|
|
22672
|
+
const splices = buildParagraphSplices(mapping.para, newPlain, st.scans[mapping.para.sectionIndex]?.xml);
|
|
22673
|
+
if (splices === null) return skip("\uBB38\uB2E8\uC5D0 \uD14D\uC2A4\uD2B8 \uB178\uB4DC\uB97C \uB9CC\uB4E4 \uC218 \uC5C6\uC74C");
|
|
22674
|
+
sectionSplices[mapping.para.sectionIndex].push(...splices);
|
|
22675
|
+
return 1;
|
|
22676
|
+
}
|
|
22677
|
+
};
|
|
22678
|
+
function cellStaticCheck(table, scanTable, r, c) {
|
|
22679
|
+
const irCell = table.cells[r]?.[c];
|
|
22680
|
+
if (!irCell) return { editable: false, reason: "\uC140 \uC88C\uD45C \uBC94\uC704 \uBC16" };
|
|
22681
|
+
const cell = scanTable.cellByAnchor.get(`${r},${c}`);
|
|
22682
|
+
if (!cell) return { editable: false, reason: "\uBCD1\uD569 \uC601\uC5ED\uC758 \uBE48 \uCE78\uC774\uAC70\uB098 \uC88C\uD45C \uBD88\uC77C\uCE58" };
|
|
22683
|
+
const scanJoined = cell.paragraphs.map((p) => p.text).filter((t) => normForMatch(t)).join("\n");
|
|
22684
|
+
if (normForMatch(scanJoined) !== normForMatch(stripCellTokens(irCell.text))) {
|
|
22685
|
+
if (normForMatch(irCell.text) !== "" || normForMatch(scanJoined) !== "") {
|
|
22686
|
+
const flatBlocks = (irCell.blocks ?? []).filter((b) => b.type === "paragraph" || b.type === "heading");
|
|
22687
|
+
const flatJoined = flatBlocks.map((b) => b.text ?? "").join("\n");
|
|
22688
|
+
if (normForMatch(scanJoined) !== normForMatch(flatJoined)) {
|
|
22689
|
+
return { editable: false, reason: "\uC140 \uCF58\uD150\uCE20 \uAD6C\uC870 \uBCF5\uC7A1 (\uC911\uCCA9\uD45C/\uAE00\uC0C1\uC790) \u2014 \uB9E4\uD551 \uC2E0\uB8B0 \uBD88\uAC00" };
|
|
22690
|
+
}
|
|
22691
|
+
}
|
|
22692
|
+
}
|
|
22693
|
+
const nonEmpty = cell.paragraphs.filter((p) => normForMatch(p.text) !== "");
|
|
22694
|
+
if (nonEmpty.length === 0) {
|
|
22695
|
+
if (cell.paragraphs.length === 0) {
|
|
22696
|
+
return { editable: false, reason: "\uBE48 \uC140\uC5D0 \uBB38\uB2E8\uC774 \uC5C6\uC5B4 \uD14D\uC2A4\uD2B8 \uC0BD\uC785 \uBD88\uAC00" };
|
|
22697
|
+
}
|
|
22698
|
+
return { editable: true };
|
|
22699
|
+
}
|
|
22700
|
+
const lines = irCellLines(irCell.text);
|
|
22701
|
+
if (lines.length !== nonEmpty.length) {
|
|
22702
|
+
return { editable: false, reason: "\uC140 \uC904 \uACBD\uACC4 \uB9E4\uD551 \uBAA8\uD638 (\uB9AC\uD130\uB7F4 <br>/\uBB38\uB2E8 \uB0B4 \uC904\uBC14\uAFC8) \u2014 \uBBF8\uC9C0\uC6D0" };
|
|
22703
|
+
}
|
|
22704
|
+
return { editable: true };
|
|
22705
|
+
}
|
|
22706
|
+
async function openHwpxDocument(input) {
|
|
22707
|
+
return HwpxSession.open(input);
|
|
22708
|
+
}
|
|
22709
|
+
async function patchHwpxBlocks(original, edits, options) {
|
|
22710
|
+
const session = await HwpxSession.open(original);
|
|
22711
|
+
return session.patchBlocks(edits, options);
|
|
22712
|
+
}
|
|
22713
|
+
|
|
22082
22714
|
// src/print/renderer.ts
|
|
22083
22715
|
import { existsSync } from "fs";
|
|
22084
22716
|
import MarkdownIt from "markdown-it";
|
|
@@ -22319,7 +22951,7 @@ async function parseHwp(buffer, options) {
|
|
|
22319
22951
|
async function parsePdf(buffer, options) {
|
|
22320
22952
|
let parsePdfDocument;
|
|
22321
22953
|
try {
|
|
22322
|
-
const mod = await import("./parser-
|
|
22954
|
+
const mod = await import("./parser-U6KRTEB7.js");
|
|
22323
22955
|
parsePdfDocument = mod.parsePdfDocument;
|
|
22324
22956
|
} catch {
|
|
22325
22957
|
return {
|
|
@@ -22414,13 +23046,22 @@ export {
|
|
|
22414
23046
|
extractHwp5MetadataOnly,
|
|
22415
23047
|
isLabelCell,
|
|
22416
23048
|
extractFormFields,
|
|
23049
|
+
inferFieldType,
|
|
23050
|
+
extractFormSchema,
|
|
22417
23051
|
fillFormFields,
|
|
23052
|
+
scanSectionXml,
|
|
23053
|
+
buildParagraphSplices,
|
|
23054
|
+
buildRangeSplices,
|
|
23055
|
+
applySplices,
|
|
22418
23056
|
fillHwpx,
|
|
22419
23057
|
markdownToHwpx,
|
|
22420
23058
|
compare,
|
|
22421
23059
|
diffBlocks,
|
|
22422
23060
|
patchHwpx,
|
|
22423
23061
|
patchHwp,
|
|
23062
|
+
HwpxSession,
|
|
23063
|
+
openHwpxDocument,
|
|
23064
|
+
patchHwpxBlocks,
|
|
22424
23065
|
renderHtml,
|
|
22425
23066
|
markdownToPdf,
|
|
22426
23067
|
blocksToPdf,
|
|
@@ -22435,4 +23076,4 @@ export {
|
|
|
22435
23076
|
parseHwpml,
|
|
22436
23077
|
fillForm
|
|
22437
23078
|
};
|
|
22438
|
-
//# sourceMappingURL=chunk-
|
|
23079
|
+
//# sourceMappingURL=chunk-CYLOXVYY.js.map
|