kordoc 3.5.4 → 3.7.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 +16 -1
- package/dist/{-KX7VLNW5.js → -7UC4ZWBS.js} +21 -5
- package/dist/{chunk-WEAP42T3.js → chunk-IJJMVTU5.js} +2 -2
- package/dist/chunk-IJJMVTU5.js.map +1 -0
- package/dist/{chunk-VEB43O2L.js → chunk-MEVKYW55.js} +2 -2
- package/dist/chunk-MEVKYW55.js.map +1 -0
- package/dist/{chunk-DK2IE43H.js → chunk-NXRABCWW.js} +1584 -701
- package/dist/chunk-NXRABCWW.js.map +1 -0
- package/dist/{chunk-XZTDYOZV.cjs → chunk-QZCP3UWU.cjs} +2 -2
- package/dist/{chunk-XZTDYOZV.cjs.map → chunk-QZCP3UWU.cjs.map} +1 -1
- package/dist/cli.js +4 -4
- package/dist/index.cjs +1783 -900
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +130 -8
- package/dist/index.d.ts +130 -8
- package/dist/index.js +1584 -701
- package/dist/index.js.map +1 -1
- package/dist/mcp.js +3 -3
- package/dist/{parser-W5TBEAVX.js → parser-DR5CTZ74.js} +2 -2
- package/dist/{parser-CPTBEGQP.cjs → parser-RFLPUZ7P.cjs} +14 -14
- package/dist/{parser-CPTBEGQP.cjs.map → parser-RFLPUZ7P.cjs.map} +1 -1
- package/dist/{parser-XT65AEDJ.js → parser-ZHJFQR44.js} +2 -2
- package/dist/{watch-VOSNRZAY.js → watch-UIX447QV.js} +3 -3
- package/package.json +1 -1
- package/dist/chunk-DK2IE43H.js.map +0 -1
- package/dist/chunk-VEB43O2L.js.map +0 -1
- package/dist/chunk-WEAP42T3.js.map +0 -1
- /package/dist/{-KX7VLNW5.js.map → -7UC4ZWBS.js.map} +0 -0
- /package/dist/{parser-W5TBEAVX.js.map → parser-DR5CTZ74.js.map} +0 -0
- /package/dist/{parser-XT65AEDJ.js.map → parser-ZHJFQR44.js.map} +0 -0
- /package/dist/{watch-VOSNRZAY.js.map → watch-UIX447QV.js.map} +0 -0
|
@@ -25,7 +25,7 @@ import {
|
|
|
25
25
|
sanitizeHref,
|
|
26
26
|
stripDtd,
|
|
27
27
|
toArrayBuffer
|
|
28
|
-
} from "./chunk-
|
|
28
|
+
} from "./chunk-IJJMVTU5.js";
|
|
29
29
|
import {
|
|
30
30
|
parsePageRange
|
|
31
31
|
} from "./chunk-MOL7MDBG.js";
|
|
@@ -1218,9 +1218,32 @@ function detectHwpxHeadings(blocks, styleMap) {
|
|
|
1218
1218
|
function buildTableWithCellMeta(state) {
|
|
1219
1219
|
const table = buildTable(state.rows);
|
|
1220
1220
|
if (state.caption) table.caption = state.caption;
|
|
1221
|
+
const anchors = [];
|
|
1222
|
+
{
|
|
1223
|
+
const covered = /* @__PURE__ */ new Set();
|
|
1224
|
+
for (let r = 0; r < table.rows; r++) {
|
|
1225
|
+
for (let c = 0; c < table.cols; c++) {
|
|
1226
|
+
if (covered.has(`${r},${c}`)) continue;
|
|
1227
|
+
const cell = table.cells[r]?.[c];
|
|
1228
|
+
if (!cell) continue;
|
|
1229
|
+
for (let dr = 0; dr < cell.rowSpan; dr++) {
|
|
1230
|
+
for (let dc = 0; dc < cell.colSpan; dc++) {
|
|
1231
|
+
if (dr === 0 && dc === 0) continue;
|
|
1232
|
+
if (r + dr < table.rows && c + dc < table.cols) covered.add(`${r + dr},${c + dc}`);
|
|
1233
|
+
}
|
|
1234
|
+
}
|
|
1235
|
+
anchors.push(cell);
|
|
1236
|
+
c += cell.colSpan - 1;
|
|
1237
|
+
}
|
|
1238
|
+
}
|
|
1239
|
+
}
|
|
1240
|
+
const srcCount = state.rows.reduce((s, r) => s + r.length, 0);
|
|
1241
|
+
const ordinalReliable = anchors.length === srcCount;
|
|
1221
1242
|
const claimed = /* @__PURE__ */ new Set();
|
|
1243
|
+
let flatIdx = -1;
|
|
1222
1244
|
for (const row of state.rows) {
|
|
1223
1245
|
for (const src of row) {
|
|
1246
|
+
flatIdx++;
|
|
1224
1247
|
const needsBlocks = src.hasStructure && src.blocks && src.blocks.length > 0;
|
|
1225
1248
|
if (!needsBlocks && !src.isHeader) continue;
|
|
1226
1249
|
let target;
|
|
@@ -1239,6 +1262,10 @@ function buildTableWithCellMeta(state) {
|
|
|
1239
1262
|
}
|
|
1240
1263
|
}
|
|
1241
1264
|
}
|
|
1265
|
+
if (!target && ordinalReliable) {
|
|
1266
|
+
const cand = anchors[flatIdx];
|
|
1267
|
+
if (cand && !claimed.has(cand)) target = cand;
|
|
1268
|
+
}
|
|
1242
1269
|
if (!target) continue;
|
|
1243
1270
|
claimed.add(target);
|
|
1244
1271
|
if (needsBlocks) target.blocks = src.blocks;
|
|
@@ -18476,6 +18503,45 @@ function countSections(body) {
|
|
|
18476
18503
|
}
|
|
18477
18504
|
|
|
18478
18505
|
// src/form/match.ts
|
|
18506
|
+
var ValueCursor = class {
|
|
18507
|
+
constructor(values) {
|
|
18508
|
+
this.values = values;
|
|
18509
|
+
}
|
|
18510
|
+
nextIdx = /* @__PURE__ */ new Map();
|
|
18511
|
+
keys() {
|
|
18512
|
+
return this.values.keys();
|
|
18513
|
+
}
|
|
18514
|
+
has(key) {
|
|
18515
|
+
return this.values.has(key);
|
|
18516
|
+
}
|
|
18517
|
+
isArray(key) {
|
|
18518
|
+
return Array.isArray(this.values.get(key));
|
|
18519
|
+
}
|
|
18520
|
+
/** 남은 값이 있으면 true (스칼라는 항상 true) */
|
|
18521
|
+
available(key) {
|
|
18522
|
+
const v = this.values.get(key);
|
|
18523
|
+
if (v === void 0) return false;
|
|
18524
|
+
return typeof v === "string" || (this.nextIdx.get(key) ?? 0) < v.length;
|
|
18525
|
+
}
|
|
18526
|
+
/** 현재 값 미리보기 (소진 없음) */
|
|
18527
|
+
peek(key) {
|
|
18528
|
+
const v = this.values.get(key);
|
|
18529
|
+
if (v === void 0) return void 0;
|
|
18530
|
+
if (typeof v === "string") return v;
|
|
18531
|
+
const i = this.nextIdx.get(key) ?? 0;
|
|
18532
|
+
return i < v.length ? v[i] : void 0;
|
|
18533
|
+
}
|
|
18534
|
+
/** 값 소비 — 배열이면 커서 전진, 소진 시 undefined */
|
|
18535
|
+
consume(key) {
|
|
18536
|
+
const v = this.values.get(key);
|
|
18537
|
+
if (v === void 0) return void 0;
|
|
18538
|
+
if (typeof v === "string") return v;
|
|
18539
|
+
const i = this.nextIdx.get(key) ?? 0;
|
|
18540
|
+
if (i >= v.length) return void 0;
|
|
18541
|
+
this.nextIdx.set(key, i + 1);
|
|
18542
|
+
return v[i];
|
|
18543
|
+
}
|
|
18544
|
+
};
|
|
18479
18545
|
function normalizeLabel(label) {
|
|
18480
18546
|
return label.trim().replace(/[::\s()()·]/g, "");
|
|
18481
18547
|
}
|
|
@@ -18514,9 +18580,9 @@ function fillInCellPatterns(cellText, values, matchedLabels) {
|
|
|
18514
18580
|
(match, prefix, suffix) => {
|
|
18515
18581
|
const label = prefix + suffix;
|
|
18516
18582
|
const normalizedLabel = normalizeLabel(label);
|
|
18517
|
-
const matchKey = values.
|
|
18583
|
+
const matchKey = values.available(normalizedLabel) ? normalizedLabel : values.available(normalizeLabel(prefix)) ? normalizeLabel(prefix) : void 0;
|
|
18518
18584
|
if (matchKey === void 0) return match;
|
|
18519
|
-
const newValue = values.
|
|
18585
|
+
const newValue = values.consume(matchKey);
|
|
18520
18586
|
matchedLabels.add(matchKey);
|
|
18521
18587
|
matches.push({ key: matchKey, label, value: newValue });
|
|
18522
18588
|
return `${prefix}(${newValue})${suffix}`;
|
|
@@ -18526,11 +18592,12 @@ function fillInCellPatterns(cellText, values, matchedLabels) {
|
|
|
18526
18592
|
/□([가-힣A-Za-z]+)/g,
|
|
18527
18593
|
(match, keyword) => {
|
|
18528
18594
|
const normalizedKw = normalizeLabel(keyword);
|
|
18529
|
-
const matchKey = values.
|
|
18595
|
+
const matchKey = values.available(normalizedKw) ? normalizedKw : void 0;
|
|
18530
18596
|
if (matchKey === void 0) return match;
|
|
18531
|
-
const val = values.
|
|
18597
|
+
const val = values.peek(matchKey);
|
|
18532
18598
|
const isTruthy = ["\u2611", "\u2713", "\u2714", "v", "V", "true", "1", "yes", "o", "O"].includes(val.trim()) || val.trim() === "";
|
|
18533
18599
|
if (!isTruthy) return match;
|
|
18600
|
+
values.consume(matchKey);
|
|
18534
18601
|
matchedLabels.add(matchKey);
|
|
18535
18602
|
matches.push({ key: matchKey, label: `\u25A1${keyword}`, value: "\u2611" });
|
|
18536
18603
|
return `\u2611${keyword}`;
|
|
@@ -18540,9 +18607,9 @@ function fillInCellPatterns(cellText, values, matchedLabels) {
|
|
|
18540
18607
|
/\(([가-힣A-Za-z]+)[::]\s{1,}\)/g,
|
|
18541
18608
|
(match, keyword) => {
|
|
18542
18609
|
const normalizedKw = normalizeLabel(keyword);
|
|
18543
|
-
const matchKey = values.
|
|
18610
|
+
const matchKey = values.available(normalizedKw) ? normalizedKw : void 0;
|
|
18544
18611
|
if (matchKey === void 0) return match;
|
|
18545
|
-
const newValue = values.
|
|
18612
|
+
const newValue = values.consume(matchKey);
|
|
18546
18613
|
matchedLabels.add(matchKey);
|
|
18547
18614
|
matches.push({ key: matchKey, label: keyword, value: newValue });
|
|
18548
18615
|
return `(${keyword}\uFF1A${newValue})`;
|
|
@@ -18648,14 +18715,66 @@ var LABEL_KEYWORDS = /* @__PURE__ */ new Set([
|
|
|
18648
18715
|
"\uCCAD\uAD6C\uC0AC\uC720",
|
|
18649
18716
|
"\uC18C\uBA85\uC790\uB8CC"
|
|
18650
18717
|
]);
|
|
18718
|
+
var ENGLISH_LABEL_WORDS = /* @__PURE__ */ new Set([
|
|
18719
|
+
"name",
|
|
18720
|
+
"date",
|
|
18721
|
+
"address",
|
|
18722
|
+
"tel",
|
|
18723
|
+
"phone",
|
|
18724
|
+
"mobile",
|
|
18725
|
+
"fax",
|
|
18726
|
+
"email",
|
|
18727
|
+
"e-mail",
|
|
18728
|
+
"dept",
|
|
18729
|
+
"department",
|
|
18730
|
+
"division",
|
|
18731
|
+
"title",
|
|
18732
|
+
"position",
|
|
18733
|
+
"grade",
|
|
18734
|
+
"rank",
|
|
18735
|
+
"birth",
|
|
18736
|
+
"nationality",
|
|
18737
|
+
"sex",
|
|
18738
|
+
"gender",
|
|
18739
|
+
"signature",
|
|
18740
|
+
"sign",
|
|
18741
|
+
"seal",
|
|
18742
|
+
"remarks",
|
|
18743
|
+
"note",
|
|
18744
|
+
"period",
|
|
18745
|
+
"place",
|
|
18746
|
+
"purpose",
|
|
18747
|
+
"reason",
|
|
18748
|
+
"amount",
|
|
18749
|
+
"total",
|
|
18750
|
+
"sum",
|
|
18751
|
+
"qty",
|
|
18752
|
+
"quantity",
|
|
18753
|
+
"unit",
|
|
18754
|
+
"no",
|
|
18755
|
+
"id",
|
|
18756
|
+
"passport"
|
|
18757
|
+
]);
|
|
18758
|
+
var ENGLISH_STOPWORDS = /* @__PURE__ */ new Set(["of", "the", "and", "or", "in"]);
|
|
18759
|
+
var NUMERIC_VALUE_RE = /^제?\d+(?:[.,]\d+)*[십백천만억조]*(?:원|명|건|개|회|부|매|장|점|호|번|년|월|일|시|분|초|개월|주년|차례|퍼센트)?$/;
|
|
18760
|
+
var SENTENCE_ENDING_RE = /(?:입니다|합니다|습니다|하세요|십시오|시오|바랍니다|바람|할 것|할것|하며|하고|한다|된다|됨|음|임)$/;
|
|
18651
18761
|
function isLabelCell(text) {
|
|
18652
18762
|
const trimmed = text.trim().replace(/[¹²³⁴⁵⁶⁷⁸⁹⁰*※]+$/g, "").trim();
|
|
18653
18763
|
if (!trimmed || trimmed.length > 30) return false;
|
|
18654
18764
|
for (const kw of LABEL_KEYWORDS) {
|
|
18655
18765
|
if (trimmed.includes(kw)) return true;
|
|
18656
18766
|
}
|
|
18657
|
-
|
|
18767
|
+
const compact = trimmed.replace(/\s/g, "");
|
|
18768
|
+
if (/^[가-힣0-9()()·::\-]+$/.test(compact) && compact.length >= 2 && compact.length <= 12 && (compact.match(/[가-힣]/g) ?? []).length >= 2 && (compact.length <= 8 || trimmed.split(/\s+/).length <= 2) && !NUMERIC_VALUE_RE.test(compact) && !SENTENCE_ENDING_RE.test(trimmed) && !/^[((]주[))]|^주식회사/.test(compact)) {
|
|
18769
|
+
return true;
|
|
18770
|
+
}
|
|
18658
18771
|
if (/^[가-힣A-Za-z\s]+[::]$/.test(trimmed)) return true;
|
|
18772
|
+
if (/^[A-Za-z][A-Za-z\s./&-]*$/.test(trimmed) && trimmed.length <= 20) {
|
|
18773
|
+
const words = trimmed.toLowerCase().split(/[\s/&]+/).filter((w) => w && !ENGLISH_STOPWORDS.has(w));
|
|
18774
|
+
if (words.length >= 1 && words.length <= 3 && words.every((w) => ENGLISH_LABEL_WORDS.has(w.replace(/\.$/, "")))) {
|
|
18775
|
+
return true;
|
|
18776
|
+
}
|
|
18777
|
+
}
|
|
18659
18778
|
return false;
|
|
18660
18779
|
}
|
|
18661
18780
|
function extractFormFields(blocks) {
|
|
@@ -18796,17 +18915,18 @@ function fillFormFields(blocks, values) {
|
|
|
18796
18915
|
const filled = [];
|
|
18797
18916
|
const matchedLabels = /* @__PURE__ */ new Set();
|
|
18798
18917
|
const normalizedValues = normalizeValues(values);
|
|
18918
|
+
const cursor = new ValueCursor(normalizedValues);
|
|
18919
|
+
const allTables = collectIRTables(cloned, 0);
|
|
18799
18920
|
const patternFilledCells = /* @__PURE__ */ new Set();
|
|
18800
|
-
for (const
|
|
18801
|
-
|
|
18802
|
-
|
|
18803
|
-
|
|
18804
|
-
const cell = block.table.cells[r]?.[c];
|
|
18921
|
+
for (const table of allTables) {
|
|
18922
|
+
for (let r = 0; r < table.rows; r++) {
|
|
18923
|
+
for (let c = 0; c < table.cols; c++) {
|
|
18924
|
+
const cell = table.cells[r]?.[c];
|
|
18805
18925
|
if (!cell) continue;
|
|
18806
|
-
const result = fillInCellPatterns(cell.text,
|
|
18926
|
+
const result = fillInCellPatterns(cell.text, cursor, matchedLabels);
|
|
18807
18927
|
if (result) {
|
|
18808
18928
|
cell.text = result.text;
|
|
18809
|
-
patternFilledCells.add(
|
|
18929
|
+
patternFilledCells.add(cell);
|
|
18810
18930
|
for (const m of result.matches) {
|
|
18811
18931
|
filled.push({ label: m.label, value: m.value, row: r, col: c });
|
|
18812
18932
|
}
|
|
@@ -18814,33 +18934,71 @@ function fillFormFields(blocks, values) {
|
|
|
18814
18934
|
}
|
|
18815
18935
|
}
|
|
18816
18936
|
}
|
|
18817
|
-
for (const
|
|
18818
|
-
|
|
18819
|
-
fillTable(block.table, normalizedValues, filled, matchedLabels, patternFilledCells);
|
|
18937
|
+
for (const table of allTables) {
|
|
18938
|
+
fillTable(table, cursor, filled, matchedLabels, patternFilledCells);
|
|
18820
18939
|
}
|
|
18821
18940
|
for (const block of cloned) {
|
|
18822
18941
|
if (block.type !== "paragraph" || !block.text) continue;
|
|
18823
|
-
const newText = fillInlineFields(block.text,
|
|
18942
|
+
const newText = fillInlineFields(block.text, cursor, filled, matchedLabels);
|
|
18824
18943
|
if (newText !== block.text) block.text = newText;
|
|
18825
18944
|
}
|
|
18826
18945
|
const unmatched = resolveUnmatched(normalizedValues, matchedLabels, values);
|
|
18827
18946
|
return { blocks: cloned, filled, unmatched };
|
|
18828
18947
|
}
|
|
18948
|
+
function collectIRTables(blocks, depth) {
|
|
18949
|
+
if (depth > 16) return [];
|
|
18950
|
+
const out = [];
|
|
18951
|
+
for (const block of blocks) {
|
|
18952
|
+
if (block.type !== "table" || !block.table) continue;
|
|
18953
|
+
out.push(block.table);
|
|
18954
|
+
for (const row of block.table.cells) {
|
|
18955
|
+
for (const cell of row) {
|
|
18956
|
+
if (cell?.blocks?.length) out.push(...collectIRTables(cell.blocks, depth + 1));
|
|
18957
|
+
}
|
|
18958
|
+
}
|
|
18959
|
+
}
|
|
18960
|
+
return out;
|
|
18961
|
+
}
|
|
18962
|
+
function coveredPositions(table) {
|
|
18963
|
+
const covered = /* @__PURE__ */ new Set();
|
|
18964
|
+
for (let r = 0; r < table.rows; r++) {
|
|
18965
|
+
for (let c = 0; c < table.cols; c++) {
|
|
18966
|
+
if (covered.has(`${r},${c}`)) continue;
|
|
18967
|
+
const cell = table.cells[r]?.[c];
|
|
18968
|
+
if (!cell) continue;
|
|
18969
|
+
for (let dr = 0; dr < cell.rowSpan; dr++) {
|
|
18970
|
+
for (let dc = 0; dc < cell.colSpan; dc++) {
|
|
18971
|
+
if (dr === 0 && dc === 0) continue;
|
|
18972
|
+
if (r + dr < table.rows && c + dc < table.cols) covered.add(`${r + dr},${c + dc}`);
|
|
18973
|
+
}
|
|
18974
|
+
}
|
|
18975
|
+
c += cell.colSpan - 1;
|
|
18976
|
+
}
|
|
18977
|
+
}
|
|
18978
|
+
return covered;
|
|
18979
|
+
}
|
|
18829
18980
|
function fillTable(table, values, filled, matchedLabels, patternFilledCells) {
|
|
18830
18981
|
if (table.cols < 2) return;
|
|
18982
|
+
const covered = coveredPositions(table);
|
|
18831
18983
|
for (let r = 0; r < table.rows; r++) {
|
|
18832
|
-
for (let c = 0; c < table.cols
|
|
18984
|
+
for (let c = 0; c < table.cols; c++) {
|
|
18985
|
+
if (covered.has(`${r},${c}`)) continue;
|
|
18833
18986
|
const labelCell = table.cells[r][c];
|
|
18834
|
-
|
|
18835
|
-
if (!labelCell || !valueCell) continue;
|
|
18987
|
+
if (!labelCell) continue;
|
|
18836
18988
|
if (!isLabelCell(labelCell.text)) continue;
|
|
18989
|
+
let vc = c + labelCell.colSpan;
|
|
18990
|
+
while (vc < table.cols && covered.has(`${r},${vc}`)) vc++;
|
|
18991
|
+
if (vc >= table.cols) continue;
|
|
18992
|
+
const valueCell = table.cells[r][vc];
|
|
18993
|
+
if (!valueCell) continue;
|
|
18837
18994
|
if (isKeywordLabel(valueCell.text)) continue;
|
|
18838
18995
|
const normalizedCellLabel = normalizeLabel(labelCell.text);
|
|
18839
18996
|
if (!normalizedCellLabel) continue;
|
|
18840
18997
|
const matchKey = findMatchingKey(normalizedCellLabel, values);
|
|
18841
18998
|
if (matchKey === void 0) continue;
|
|
18842
|
-
const newValue = values.
|
|
18843
|
-
if (
|
|
18999
|
+
const newValue = values.consume(matchKey);
|
|
19000
|
+
if (newValue === void 0) continue;
|
|
19001
|
+
if (patternFilledCells?.has(valueCell)) {
|
|
18844
19002
|
valueCell.text = newValue + " " + valueCell.text;
|
|
18845
19003
|
} else {
|
|
18846
19004
|
valueCell.text = newValue;
|
|
@@ -18863,14 +19021,16 @@ function fillTable(table, values, filled, matchedLabels, patternFilledCells) {
|
|
|
18863
19021
|
if (!allLabels) return;
|
|
18864
19022
|
for (let r = 1; r < table.rows; r++) {
|
|
18865
19023
|
for (let c = 0; c < table.cols; c++) {
|
|
19024
|
+
if (covered.has(`${r},${c}`)) continue;
|
|
18866
19025
|
const headerCell = headerRow[c];
|
|
18867
19026
|
const valueCell = table.cells[r]?.[c];
|
|
18868
19027
|
if (!headerCell || !valueCell) continue;
|
|
18869
19028
|
const headerLabel = normalizeLabel(headerCell.text);
|
|
18870
19029
|
const matchKey = findMatchingKey(headerLabel, values);
|
|
18871
19030
|
if (matchKey === void 0) continue;
|
|
18872
|
-
if (matchedLabels.has(matchKey)) continue;
|
|
18873
|
-
const newValue = values.
|
|
19031
|
+
if (!values.isArray(matchKey) && matchedLabels.has(matchKey)) continue;
|
|
19032
|
+
const newValue = values.consume(matchKey);
|
|
19033
|
+
if (newValue === void 0) continue;
|
|
18874
19034
|
valueCell.text = newValue;
|
|
18875
19035
|
matchedLabels.add(matchKey);
|
|
18876
19036
|
filled.push({
|
|
@@ -18891,7 +19051,8 @@ function fillInlineFields(text, values, filled, matchedLabels) {
|
|
|
18891
19051
|
for (const seg of segments) {
|
|
18892
19052
|
const matchKey = findMatchingKey(normalizeLabel(seg.label), values);
|
|
18893
19053
|
if (matchKey === void 0) continue;
|
|
18894
|
-
const newValue = values.
|
|
19054
|
+
const newValue = values.consume(matchKey);
|
|
19055
|
+
if (newValue === void 0) continue;
|
|
18895
19056
|
matchedLabels.add(matchKey);
|
|
18896
19057
|
filled.push({ label: seg.label.trim(), value: newValue, row: -1, col: -1 });
|
|
18897
19058
|
out += text.slice(pos, seg.valueStart);
|
|
@@ -18998,6 +19159,7 @@ function scanSectionXml(xml, sectionIndex) {
|
|
|
18998
19159
|
const paraStack = [];
|
|
18999
19160
|
const tableStack = [];
|
|
19000
19161
|
const rowStack = [];
|
|
19162
|
+
const trStartStack = [];
|
|
19001
19163
|
const cellStack = [];
|
|
19002
19164
|
let pendingT = null;
|
|
19003
19165
|
const ctrlSubStack = [];
|
|
@@ -19062,11 +19224,17 @@ function scanSectionXml(xml, sectionIndex) {
|
|
|
19062
19224
|
} else if (local2 === "tr") {
|
|
19063
19225
|
const row = rowStack[rowStack.length - 1];
|
|
19064
19226
|
const table = tableStack[tableStack.length - 1];
|
|
19065
|
-
if (row && table && row.length > 0)
|
|
19227
|
+
if (row && table && row.length > 0) {
|
|
19228
|
+
table.rows.push(row);
|
|
19229
|
+
const trStart = trStartStack[trStartStack.length - 1];
|
|
19230
|
+
if (trStart >= 0) table.rowRanges.push({ start: trStart, end: m.index + full.length });
|
|
19231
|
+
}
|
|
19066
19232
|
if (rowStack.length > 0) rowStack[rowStack.length - 1] = [];
|
|
19233
|
+
if (trStartStack.length > 0) trStartStack[trStartStack.length - 1] = -1;
|
|
19067
19234
|
} else if (local2 === "tbl") {
|
|
19068
19235
|
const table = tableStack.pop();
|
|
19069
19236
|
rowStack.pop();
|
|
19237
|
+
trStartStack.pop();
|
|
19070
19238
|
if (table) {
|
|
19071
19239
|
finalizeTable(table);
|
|
19072
19240
|
if (!table.topLevel) {
|
|
@@ -19109,6 +19277,7 @@ function scanSectionXml(xml, sectionIndex) {
|
|
|
19109
19277
|
const ra = parseInt(getAttr2(attrsRaw, "rowAddr") || "", 10);
|
|
19110
19278
|
if (!isNaN(ca)) cell.colAddr = ca;
|
|
19111
19279
|
if (!isNaN(ra)) cell.rowAddr = ra;
|
|
19280
|
+
cell.addrTagRange = { start: m.index, end: m.index + full.length };
|
|
19112
19281
|
}
|
|
19113
19282
|
} else if (local === "cellSpan") {
|
|
19114
19283
|
const cell = cellStack[cellStack.length - 1];
|
|
@@ -19159,6 +19328,7 @@ function scanSectionXml(xml, sectionIndex) {
|
|
|
19159
19328
|
start: m.index,
|
|
19160
19329
|
topLevel: false,
|
|
19161
19330
|
rows: [],
|
|
19331
|
+
rowRanges: [],
|
|
19162
19332
|
cellByAnchor: /* @__PURE__ */ new Map()
|
|
19163
19333
|
};
|
|
19164
19334
|
stack.pop();
|
|
@@ -19166,9 +19336,11 @@ function scanSectionXml(xml, sectionIndex) {
|
|
|
19166
19336
|
stack.push({ local, qname, contentStart });
|
|
19167
19337
|
tableStack.push(table);
|
|
19168
19338
|
rowStack.push([]);
|
|
19339
|
+
trStartStack.push(-1);
|
|
19169
19340
|
if (table.topLevel) tables.push(table);
|
|
19170
19341
|
} else if (local === "tr") {
|
|
19171
19342
|
if (rowStack.length > 0) rowStack[rowStack.length - 1] = [];
|
|
19343
|
+
if (trStartStack.length > 0) trStartStack[trStartStack.length - 1] = m.index;
|
|
19172
19344
|
} else if (local === "tc") {
|
|
19173
19345
|
cellStack.push({ colSpan: 1, rowSpan: 1, paragraphs: [], tables: [] });
|
|
19174
19346
|
} else if (local === "cellAddr" || local === "cellSpan") {
|
|
@@ -19179,6 +19351,7 @@ function scanSectionXml(xml, sectionIndex) {
|
|
|
19179
19351
|
const ra = parseInt(getAttr2(attrsRaw, "rowAddr") || "", 10);
|
|
19180
19352
|
if (!isNaN(ca)) cell.colAddr = ca;
|
|
19181
19353
|
if (!isNaN(ra)) cell.rowAddr = ra;
|
|
19354
|
+
cell.addrTagRange = { start: m.index, end: contentStart };
|
|
19182
19355
|
} else {
|
|
19183
19356
|
const cs = parseInt(getAttr2(attrsRaw, "colSpan") || "1", 10);
|
|
19184
19357
|
const rs = parseInt(getAttr2(attrsRaw, "rowSpan") || "1", 10);
|
|
@@ -19571,6 +19744,7 @@ async function fillHwpx(hwpxBuffer, values) {
|
|
|
19571
19744
|
throw new KordocError("HWPX\uC5D0\uC11C \uC139\uC158 \uD30C\uC77C\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4");
|
|
19572
19745
|
}
|
|
19573
19746
|
const normalizedValues = normalizeValues(values);
|
|
19747
|
+
const cursor = new ValueCursor(normalizedValues);
|
|
19574
19748
|
const matchedLabels = /* @__PURE__ */ new Set();
|
|
19575
19749
|
const filled = [];
|
|
19576
19750
|
const failedKeys = /* @__PURE__ */ new Set();
|
|
@@ -19606,7 +19780,7 @@ async function fillHwpx(hwpxBuffer, values) {
|
|
|
19606
19780
|
for (const cell of row) {
|
|
19607
19781
|
for (const para of cell.paragraphs) {
|
|
19608
19782
|
const text = matchText(para);
|
|
19609
|
-
const result = fillInCellPatterns(text,
|
|
19783
|
+
const result = fillInCellPatterns(text, cursor, matchedLabels);
|
|
19610
19784
|
if (!result) continue;
|
|
19611
19785
|
const l = led(para);
|
|
19612
19786
|
if (l.fullText !== void 0) continue;
|
|
@@ -19640,21 +19814,30 @@ async function fillHwpx(hwpxBuffer, values) {
|
|
|
19640
19814
|
if (isKeywordLabel(cellLabelText(valueCell))) continue;
|
|
19641
19815
|
const normalizedCellLabel = normalizeLabel(labelText);
|
|
19642
19816
|
if (!normalizedCellLabel) continue;
|
|
19643
|
-
const matchKey = findMatchingKey(normalizedCellLabel,
|
|
19817
|
+
const matchKey = findMatchingKey(normalizedCellLabel, cursor);
|
|
19644
19818
|
if (matchKey === void 0) continue;
|
|
19645
|
-
const newValue = normalizedValues.get(matchKey);
|
|
19646
19819
|
if (patternApplied.has(valueCell)) {
|
|
19647
19820
|
const target = valueCell.paragraphs.find((p) => p.tRanges.length > 0) ?? valueCell.paragraphs[0];
|
|
19648
19821
|
if (!target) continue;
|
|
19649
19822
|
const l = led(target);
|
|
19650
|
-
if (l.fullText
|
|
19651
|
-
|
|
19652
|
-
|
|
19653
|
-
|
|
19654
|
-
|
|
19823
|
+
if (l.fullText !== void 0) continue;
|
|
19824
|
+
const newValue = cursor.consume(matchKey);
|
|
19825
|
+
if (newValue === void 0) continue;
|
|
19826
|
+
l.ranges.push({ start: 0, end: 0, replacement: newValue + " " });
|
|
19827
|
+
l.filledIdx.push(filled.length);
|
|
19828
|
+
l.matchKeys.push(matchKey);
|
|
19829
|
+
matchedLabels.add(matchKey);
|
|
19830
|
+
filled.push({
|
|
19831
|
+
label: labelText.trim().replace(/[::]\s*$/, ""),
|
|
19832
|
+
value: newValue,
|
|
19833
|
+
row: rowIdx,
|
|
19834
|
+
col: colIdx
|
|
19835
|
+
});
|
|
19655
19836
|
} else {
|
|
19656
19837
|
const paras = valueCell.paragraphs;
|
|
19657
19838
|
if (paras.length === 0) continue;
|
|
19839
|
+
const newValue = cursor.consume(matchKey);
|
|
19840
|
+
if (newValue === void 0) continue;
|
|
19658
19841
|
const l0 = led(paras[0]);
|
|
19659
19842
|
l0.fullText = newValue;
|
|
19660
19843
|
l0.ranges = [];
|
|
@@ -19665,14 +19848,14 @@ async function fillHwpx(hwpxBuffer, values) {
|
|
|
19665
19848
|
lk.fullText = "";
|
|
19666
19849
|
lk.ranges = [];
|
|
19667
19850
|
}
|
|
19851
|
+
matchedLabels.add(matchKey);
|
|
19852
|
+
filled.push({
|
|
19853
|
+
label: labelText.trim().replace(/[::]\s*$/, ""),
|
|
19854
|
+
value: newValue,
|
|
19855
|
+
row: rowIdx,
|
|
19856
|
+
col: colIdx
|
|
19857
|
+
});
|
|
19668
19858
|
}
|
|
19669
|
-
matchedLabels.add(matchKey);
|
|
19670
|
-
filled.push({
|
|
19671
|
-
label: labelText.trim().replace(/[::]\s*$/, ""),
|
|
19672
|
-
value: newValue,
|
|
19673
|
-
row: rowIdx,
|
|
19674
|
-
col: colIdx
|
|
19675
|
-
});
|
|
19676
19859
|
}
|
|
19677
19860
|
}
|
|
19678
19861
|
if (table.rows.length >= 2) {
|
|
@@ -19686,10 +19869,11 @@ async function fillHwpx(hwpxBuffer, values) {
|
|
|
19686
19869
|
const dataCells = table.rows[rowIdx];
|
|
19687
19870
|
for (let colIdx = 0; colIdx < Math.min(headerCells.length, dataCells.length); colIdx++) {
|
|
19688
19871
|
const headerLabel = normalizeLabel(cellLabelText(headerCells[colIdx]));
|
|
19689
|
-
const matchKey = findMatchingKey(headerLabel,
|
|
19872
|
+
const matchKey = findMatchingKey(headerLabel, cursor);
|
|
19690
19873
|
if (matchKey === void 0) continue;
|
|
19691
|
-
if (matchedLabels.has(matchKey)) continue;
|
|
19692
|
-
const newValue =
|
|
19874
|
+
if (!cursor.isArray(matchKey) && matchedLabels.has(matchKey)) continue;
|
|
19875
|
+
const newValue = cursor.consume(matchKey);
|
|
19876
|
+
if (newValue === void 0) continue;
|
|
19693
19877
|
const paras = dataCells[colIdx].paragraphs;
|
|
19694
19878
|
if (paras.length === 0) continue;
|
|
19695
19879
|
const l0 = led(paras[0]);
|
|
@@ -19719,9 +19903,10 @@ async function fillHwpx(hwpxBuffer, values) {
|
|
|
19719
19903
|
if (existing?.fullText !== void 0) continue;
|
|
19720
19904
|
const text = matchText(para);
|
|
19721
19905
|
for (const seg of scanInlineSegments(text)) {
|
|
19722
|
-
const matchKey = findMatchingKey(normalizeLabel(seg.label),
|
|
19906
|
+
const matchKey = findMatchingKey(normalizeLabel(seg.label), cursor);
|
|
19723
19907
|
if (matchKey === void 0) continue;
|
|
19724
|
-
const newValue =
|
|
19908
|
+
const newValue = cursor.consume(matchKey);
|
|
19909
|
+
if (newValue === void 0) continue;
|
|
19725
19910
|
const replacement = seg.valueStart === seg.valueEnd ? padInsertion(text, seg.valueStart, newValue) : newValue;
|
|
19726
19911
|
const l = led(para);
|
|
19727
19912
|
l.ranges.push({ start: seg.valueStart, end: seg.valueEnd, replacement });
|
|
@@ -19776,6 +19961,7 @@ async function fillHwpx(hwpxBuffer, values) {
|
|
|
19776
19961
|
splices.push(...paraSplices);
|
|
19777
19962
|
}
|
|
19778
19963
|
if (splices.length > 0) {
|
|
19964
|
+
splices.push(...allLinesegRemovalSplices(xml));
|
|
19779
19965
|
replacements.set(sectionPaths[si], encoder.encode(applySplices(xml, splices)));
|
|
19780
19966
|
}
|
|
19781
19967
|
}
|
|
@@ -19795,6 +19981,245 @@ async function fillHwpx(hwpxBuffer, values) {
|
|
|
19795
19981
|
// src/hwpx/generator.ts
|
|
19796
19982
|
import JSZip5 from "jszip";
|
|
19797
19983
|
|
|
19984
|
+
// src/hwpx/text-metrics.ts
|
|
19985
|
+
var ASCII_W = [
|
|
19986
|
+
300,
|
|
19987
|
+
320,
|
|
19988
|
+
320,
|
|
19989
|
+
610,
|
|
19990
|
+
610,
|
|
19991
|
+
830,
|
|
19992
|
+
724,
|
|
19993
|
+
320,
|
|
19994
|
+
320,
|
|
19995
|
+
320,
|
|
19996
|
+
550,
|
|
19997
|
+
550,
|
|
19998
|
+
320,
|
|
19999
|
+
550,
|
|
20000
|
+
320,
|
|
20001
|
+
550,
|
|
20002
|
+
// 0x20-0x2F
|
|
20003
|
+
550,
|
|
20004
|
+
550,
|
|
20005
|
+
550,
|
|
20006
|
+
550,
|
|
20007
|
+
550,
|
|
20008
|
+
550,
|
|
20009
|
+
550,
|
|
20010
|
+
550,
|
|
20011
|
+
550,
|
|
20012
|
+
550,
|
|
20013
|
+
320,
|
|
20014
|
+
320,
|
|
20015
|
+
550,
|
|
20016
|
+
550,
|
|
20017
|
+
550,
|
|
20018
|
+
550,
|
|
20019
|
+
// 0x30-0x3F
|
|
20020
|
+
830,
|
|
20021
|
+
706,
|
|
20022
|
+
605,
|
|
20023
|
+
685,
|
|
20024
|
+
719,
|
|
20025
|
+
627,
|
|
20026
|
+
617,
|
|
20027
|
+
683,
|
|
20028
|
+
734,
|
|
20029
|
+
305,
|
|
20030
|
+
315,
|
|
20031
|
+
660,
|
|
20032
|
+
605,
|
|
20033
|
+
839,
|
|
20034
|
+
734,
|
|
20035
|
+
732,
|
|
20036
|
+
// 0x40-0x4F
|
|
20037
|
+
603,
|
|
20038
|
+
705,
|
|
20039
|
+
660,
|
|
20040
|
+
627,
|
|
20041
|
+
664,
|
|
20042
|
+
731,
|
|
20043
|
+
706,
|
|
20044
|
+
910,
|
|
20045
|
+
705,
|
|
20046
|
+
705,
|
|
20047
|
+
626,
|
|
20048
|
+
320,
|
|
20049
|
+
550,
|
|
20050
|
+
320,
|
|
20051
|
+
550,
|
|
20052
|
+
550,
|
|
20053
|
+
// 0x50-0x5F
|
|
20054
|
+
320,
|
|
20055
|
+
569,
|
|
20056
|
+
597,
|
|
20057
|
+
552,
|
|
20058
|
+
597,
|
|
20059
|
+
536,
|
|
20060
|
+
356,
|
|
20061
|
+
562,
|
|
20062
|
+
635,
|
|
20063
|
+
287,
|
|
20064
|
+
288,
|
|
20065
|
+
582,
|
|
20066
|
+
287,
|
|
20067
|
+
907,
|
|
20068
|
+
635,
|
|
20069
|
+
588,
|
|
20070
|
+
// 0x60-0x6F
|
|
20071
|
+
597,
|
|
20072
|
+
579,
|
|
20073
|
+
478,
|
|
20074
|
+
496,
|
|
20075
|
+
356,
|
|
20076
|
+
635,
|
|
20077
|
+
563,
|
|
20078
|
+
720,
|
|
20079
|
+
542,
|
|
20080
|
+
543,
|
|
20081
|
+
486,
|
|
20082
|
+
320,
|
|
20083
|
+
320,
|
|
20084
|
+
320,
|
|
20085
|
+
550,
|
|
20086
|
+
0
|
|
20087
|
+
// 0x70-0x7E(+DEL)
|
|
20088
|
+
];
|
|
20089
|
+
var SYM_W = {
|
|
20090
|
+
160: 300,
|
|
20091
|
+
163: 568,
|
|
20092
|
+
165: 707,
|
|
20093
|
+
167: 498,
|
|
20094
|
+
171: 440,
|
|
20095
|
+
172: 564,
|
|
20096
|
+
176: 291,
|
|
20097
|
+
177: 798,
|
|
20098
|
+
182: 606,
|
|
20099
|
+
183: 320,
|
|
20100
|
+
187: 440,
|
|
20101
|
+
215: 617,
|
|
20102
|
+
247: 678,
|
|
20103
|
+
8211: 625,
|
|
20104
|
+
8212: 875,
|
|
20105
|
+
8213: 875,
|
|
20106
|
+
8216: 320,
|
|
20107
|
+
8217: 320,
|
|
20108
|
+
8220: 480,
|
|
20109
|
+
8221: 480,
|
|
20110
|
+
8224: 558,
|
|
20111
|
+
8225: 438,
|
|
20112
|
+
8229: 640,
|
|
20113
|
+
8230: 960,
|
|
20114
|
+
8240: 988,
|
|
20115
|
+
8242: 335,
|
|
20116
|
+
8243: 474,
|
|
20117
|
+
8251: 770,
|
|
20118
|
+
8364: 656,
|
|
20119
|
+
9756: 1012,
|
|
20120
|
+
9758: 1012
|
|
20121
|
+
};
|
|
20122
|
+
function charWidthEm1000(cp) {
|
|
20123
|
+
if (cp >= 32 && cp <= 126) return ASCII_W[cp - 32];
|
|
20124
|
+
const sym = SYM_W[cp];
|
|
20125
|
+
if (sym !== void 0) return sym;
|
|
20126
|
+
if (cp >= 44032 && cp <= 55203) return 970;
|
|
20127
|
+
if (cp >= 4352 && cp <= 4607) return 970;
|
|
20128
|
+
if (cp >= 12593 && cp <= 12686) return 970;
|
|
20129
|
+
if (cp >= 19968 && cp <= 40959 || cp >= 63744 && cp <= 64255) return 1e3;
|
|
20130
|
+
if (cp >= 12296 && cp <= 12305 || cp >= 12308 && cp <= 12315) return 500;
|
|
20131
|
+
if (cp === 12288) return 970;
|
|
20132
|
+
if (cp >= 8592 && cp <= 8959) return 970;
|
|
20133
|
+
if (cp >= 9312 && cp <= 9471) return 970;
|
|
20134
|
+
if (cp >= 9632 && cp <= 9983) return 970;
|
|
20135
|
+
if (cp >= 12800 && cp <= 13311) return 970;
|
|
20136
|
+
if (cp >= 65281 && cp <= 65376) return 970;
|
|
20137
|
+
return cp >= 11904 ? 970 : 550;
|
|
20138
|
+
}
|
|
20139
|
+
var SPACE_EM_FIXED = 500;
|
|
20140
|
+
var SPACE_EM_FONT = 300;
|
|
20141
|
+
function measureTextWidth(text, height, ratioPct, opts) {
|
|
20142
|
+
const spaceEm = opts?.spaceEm ?? SPACE_EM_FIXED;
|
|
20143
|
+
const spacing = opts?.spacingPct ?? 0;
|
|
20144
|
+
let em = 0;
|
|
20145
|
+
for (const ch of text) {
|
|
20146
|
+
const cp = ch.codePointAt(0);
|
|
20147
|
+
const w = cp === 32 ? spaceEm : charWidthEm1000(cp);
|
|
20148
|
+
em += w * (1 + spacing / 100);
|
|
20149
|
+
}
|
|
20150
|
+
return em / 1e3 * height * (ratioPct / 100);
|
|
20151
|
+
}
|
|
20152
|
+
var FORBID_START = /* @__PURE__ */ new Set([..."!%),.:;?]}\xA2\xB0\u2032\u2033\u2103\u3009\u300B\u300D\u300F\u3011\u3015!%),.:;?]}\u20A9~\u2026\xB7\u3001\u3002\u3003"]);
|
|
20153
|
+
var FORBID_END = /* @__PURE__ */ new Set([..."$([{\xA3\xA5\u3008\u300A\u300C\u300E\u3010\u3014$([{\u20A9"]);
|
|
20154
|
+
function simulateWrap(text, firstWidth, contWidth, height, ratioPct, mode = "keep", opts) {
|
|
20155
|
+
const EPS = 0.5;
|
|
20156
|
+
const spaceEm = opts?.spaceEm ?? SPACE_EM_FIXED;
|
|
20157
|
+
const spacing = opts?.spacingPct ?? 0;
|
|
20158
|
+
const k = height * ratioPct / 100 / 1e3;
|
|
20159
|
+
const cwCp = (cp) => (cp === 32 ? spaceEm : charWidthEm1000(cp)) * (1 + spacing / 100) * k;
|
|
20160
|
+
const charW = (ch) => cwCp(ch.codePointAt(0));
|
|
20161
|
+
const rangeW = (from, to) => {
|
|
20162
|
+
let w = 0;
|
|
20163
|
+
for (const ch of text.slice(from, to)) w += charW(ch);
|
|
20164
|
+
return w;
|
|
20165
|
+
};
|
|
20166
|
+
const units = text.match(mode === "keep" ? / +|[^ ]+/g : / +|[^ ]/g) ?? [];
|
|
20167
|
+
const starts = [0];
|
|
20168
|
+
let lineW = 0;
|
|
20169
|
+
let avail = firstWidth;
|
|
20170
|
+
let pos = 0;
|
|
20171
|
+
const lineStart = () => starts[starts.length - 1];
|
|
20172
|
+
const breakBefore = (unitPos, w) => {
|
|
20173
|
+
let bp = unitPos;
|
|
20174
|
+
const u = text[unitPos];
|
|
20175
|
+
if (u !== void 0 && FORBID_START.has(u) && bp - 1 > lineStart() && text[bp - 1] !== " ") bp--;
|
|
20176
|
+
while (bp - 1 > lineStart() && FORBID_END.has(text[bp - 1])) bp--;
|
|
20177
|
+
if (bp <= lineStart()) bp = unitPos;
|
|
20178
|
+
starts.push(bp);
|
|
20179
|
+
avail = contWidth;
|
|
20180
|
+
lineW = rangeW(bp, unitPos) + w;
|
|
20181
|
+
};
|
|
20182
|
+
for (const u of units) {
|
|
20183
|
+
if (u[0] === " ") {
|
|
20184
|
+
lineW += charW(" ") * u.length;
|
|
20185
|
+
pos += u.length;
|
|
20186
|
+
continue;
|
|
20187
|
+
}
|
|
20188
|
+
const w = rangeW(pos, pos + u.length);
|
|
20189
|
+
if (lineW + w <= avail + EPS) {
|
|
20190
|
+
lineW += w;
|
|
20191
|
+
pos += u.length;
|
|
20192
|
+
continue;
|
|
20193
|
+
}
|
|
20194
|
+
if (lineW === 0 || w > contWidth + EPS) {
|
|
20195
|
+
let sub = 0;
|
|
20196
|
+
for (const ch of u) {
|
|
20197
|
+
const c = charW(ch);
|
|
20198
|
+
if (lineW + c > avail + EPS && lineW > 0) breakBefore(pos + sub, 0);
|
|
20199
|
+
lineW += c;
|
|
20200
|
+
sub += ch.length;
|
|
20201
|
+
}
|
|
20202
|
+
pos += u.length;
|
|
20203
|
+
continue;
|
|
20204
|
+
}
|
|
20205
|
+
breakBefore(pos, w);
|
|
20206
|
+
pos += u.length;
|
|
20207
|
+
}
|
|
20208
|
+
return { lines: starts.length, starts, lastLineWidth: lineW };
|
|
20209
|
+
}
|
|
20210
|
+
function simulateWrapKeepWord(text, firstWidth, contWidth, height, ratioPct, opts) {
|
|
20211
|
+
return simulateWrap(text, firstWidth, contWidth, height, ratioPct, "keep", opts);
|
|
20212
|
+
}
|
|
20213
|
+
function fitRatioForFewerLines(text, firstWidth, contWidth, height, baseRatio, minRatio, opts) {
|
|
20214
|
+
const base = simulateWrap(text, firstWidth, contWidth, height, baseRatio, "keep", opts);
|
|
20215
|
+
if (base.lines < 2) return null;
|
|
20216
|
+
for (let r = baseRatio - 1; r >= minRatio; r--) {
|
|
20217
|
+
const sim = simulateWrap(text, firstWidth, contWidth, height, r, "keep", opts);
|
|
20218
|
+
if (sim.lines < base.lines) return r;
|
|
20219
|
+
}
|
|
20220
|
+
return null;
|
|
20221
|
+
}
|
|
20222
|
+
|
|
19798
20223
|
// src/hwpx/gongmun.ts
|
|
19799
20224
|
var OFFICIAL_MARGINS = { top: 20, bottom: 10, left: 20, right: 20 };
|
|
19800
20225
|
var PRESET_DEFAULTS = {
|
|
@@ -19830,6 +20255,7 @@ function resolveGongmun(opts) {
|
|
|
19830
20255
|
const preset = normalizeGongmunPreset(opts.preset);
|
|
19831
20256
|
const d = PRESET_DEFAULTS[preset];
|
|
19832
20257
|
const bodyPt = opts.bodyPt ?? d.bodyPt;
|
|
20258
|
+
const autoFitMinRatio = opts.autoFit === false ? null : typeof opts.autoFit === "object" ? Math.min(Math.max(opts.autoFit.minRatio ?? 90, 50), 99) : 90;
|
|
19833
20259
|
return {
|
|
19834
20260
|
preset,
|
|
19835
20261
|
bodyFont: opts.bodyFont ?? "myeongjo",
|
|
@@ -19837,7 +20263,8 @@ function resolveGongmun(opts) {
|
|
|
19837
20263
|
lineSpacing: opts.lineSpacing ?? d.lineSpacing,
|
|
19838
20264
|
numbering: opts.numbering ?? d.numbering,
|
|
19839
20265
|
margins: opts.margins ?? OFFICIAL_MARGINS,
|
|
19840
|
-
centerTitle: opts.centerTitle ?? true
|
|
20266
|
+
centerTitle: opts.centerTitle ?? true,
|
|
20267
|
+
autoFitMinRatio
|
|
19841
20268
|
};
|
|
19842
20269
|
}
|
|
19843
20270
|
var HANGUL_INITIALS = [0, 2, 3, 5, 6, 7, 9, 11, 12, 14, 15, 16, 17, 18];
|
|
@@ -19880,17 +20307,10 @@ function standardMarker(depth, n) {
|
|
|
19880
20307
|
function reportMarker(depth) {
|
|
19881
20308
|
return REPORT_BULLETS[Math.min(depth, REPORT_BULLETS.length - 1)];
|
|
19882
20309
|
}
|
|
19883
|
-
var MARKER_FULLWIDTH = "\u2460\u2461\u2462\u2463\u2464\u2465\u2466\u2467\u2468\u2469\u246A\u246B\u246C\u246D\u246E\u246F\u2470\u2471\u2472\u2473\u326E\u326F\u3270\u3271\u3272\u3273\u3274\u3275\u3276\u3277\u3278\u3279\u327A\u327B\u25A1\u25CB\u25E6\u25AA\u25C7\u25B3\u25B6\u318D";
|
|
19884
20310
|
function markerWidth(marker, bodyHeight) {
|
|
19885
|
-
|
|
19886
|
-
|
|
19887
|
-
|
|
19888
|
-
if (/[가-힣]/.test(c) || MARKER_FULLWIDTH.includes(c)) w += bodyHeight;
|
|
19889
|
-
else if (c === "." || c === ",") w += Math.round(bodyHeight * 0.25);
|
|
19890
|
-
else if (c === "(" || c === ")") w += Math.round(bodyHeight * 0.45);
|
|
19891
|
-
else w += Math.round(ta);
|
|
19892
|
-
}
|
|
19893
|
-
return Math.round(w + ta);
|
|
20311
|
+
let em = SPACE_EM_FIXED;
|
|
20312
|
+
for (const c of marker) em += charWidthEm1000(c.codePointAt(0));
|
|
20313
|
+
return Math.round(em / 1e3 * bodyHeight);
|
|
19894
20314
|
}
|
|
19895
20315
|
function levelIndent(depth, bodyHeight, numbering) {
|
|
19896
20316
|
const marker = numbering === "report" ? reportMarker(depth) : standardMarker(depth, 0);
|
|
@@ -19930,77 +20350,53 @@ function mmToHwpunit(mm) {
|
|
|
19930
20350
|
return Math.round(mm * 7200 / 25.4);
|
|
19931
20351
|
}
|
|
19932
20352
|
|
|
19933
|
-
// src/
|
|
19934
|
-
|
|
19935
|
-
|
|
19936
|
-
|
|
19937
|
-
|
|
19938
|
-
|
|
19939
|
-
|
|
19940
|
-
var NS_OCF = "urn:oasis:names:tc:opendocument:xmlns:container";
|
|
19941
|
-
var CHAR_NORMAL = 0;
|
|
19942
|
-
var CHAR_BOLD = 1;
|
|
19943
|
-
var CHAR_ITALIC = 2;
|
|
19944
|
-
var CHAR_BOLD_ITALIC = 3;
|
|
19945
|
-
var CHAR_CODE = 4;
|
|
19946
|
-
var CHAR_H1 = 5;
|
|
19947
|
-
var CHAR_H2 = 6;
|
|
19948
|
-
var CHAR_H3 = 7;
|
|
19949
|
-
var CHAR_H4 = 8;
|
|
19950
|
-
var CHAR_TABLE_HEADER = 9;
|
|
19951
|
-
var CHAR_QUOTE = 10;
|
|
19952
|
-
var PARA_NORMAL = 0;
|
|
19953
|
-
var PARA_H1 = 1;
|
|
19954
|
-
var PARA_H2 = 2;
|
|
19955
|
-
var PARA_H3 = 3;
|
|
19956
|
-
var PARA_H4 = 4;
|
|
19957
|
-
var PARA_CODE = 5;
|
|
19958
|
-
var PARA_QUOTE = 6;
|
|
19959
|
-
var PARA_LIST = 7;
|
|
19960
|
-
var DEFAULT_TEXT_COLOR = "#000000";
|
|
19961
|
-
function resolveTheme(theme) {
|
|
19962
|
-
return {
|
|
19963
|
-
h1: theme?.headingColors?.[1] ?? DEFAULT_TEXT_COLOR,
|
|
19964
|
-
h2: theme?.headingColors?.[2] ?? DEFAULT_TEXT_COLOR,
|
|
19965
|
-
h3: theme?.headingColors?.[3] ?? DEFAULT_TEXT_COLOR,
|
|
19966
|
-
h4: theme?.headingColors?.[4] ?? theme?.headingColors?.[3] ?? DEFAULT_TEXT_COLOR,
|
|
19967
|
-
body: theme?.bodyColor ?? DEFAULT_TEXT_COLOR,
|
|
19968
|
-
quote: theme?.quoteColor ?? DEFAULT_TEXT_COLOR,
|
|
19969
|
-
/** quoteColor가 명시되었는지 — blockquote charPr 분기에 사용 (baseline 호환) */
|
|
19970
|
-
hasQuoteOption: theme?.quoteColor !== void 0,
|
|
19971
|
-
tableHeader: theme?.tableHeaderColor ?? theme?.bodyColor ?? DEFAULT_TEXT_COLOR,
|
|
19972
|
-
tableHeaderBold: !!theme?.tableHeaderBold
|
|
19973
|
-
};
|
|
20353
|
+
// src/diff/text-diff.ts
|
|
20354
|
+
function similarity(a, b) {
|
|
20355
|
+
if (a === b) return 1;
|
|
20356
|
+
if (!a || !b) return 0;
|
|
20357
|
+
const maxLen = Math.max(a.length, b.length);
|
|
20358
|
+
if (maxLen === 0) return 1;
|
|
20359
|
+
return 1 - levenshtein(a, b) / maxLen;
|
|
19974
20360
|
}
|
|
19975
|
-
|
|
19976
|
-
|
|
19977
|
-
const gongmun = options?.gongmun ? resolveGongmun(options.gongmun) : null;
|
|
19978
|
-
const blocks = parseMarkdownToBlocks(markdown);
|
|
19979
|
-
const sectionXml = blocksToSectionXml(blocks, theme, gongmun);
|
|
19980
|
-
const zip = new JSZip5();
|
|
19981
|
-
zip.file("mimetype", "application/hwp+zip", { compression: "STORE" });
|
|
19982
|
-
zip.file("META-INF/container.xml", generateContainerXml());
|
|
19983
|
-
zip.file("Contents/content.hpf", generateManifest());
|
|
19984
|
-
zip.file("Contents/header.xml", generateHeaderXml(theme, gongmun));
|
|
19985
|
-
zip.file("Contents/section0.xml", sectionXml);
|
|
19986
|
-
zip.file("Preview/PrvText.txt", buildPrvText(blocks));
|
|
19987
|
-
return await zip.generateAsync({ type: "arraybuffer" });
|
|
20361
|
+
function normalizedSimilarity(a, b) {
|
|
20362
|
+
return similarity(normalize(a), normalize(b));
|
|
19988
20363
|
}
|
|
19989
|
-
function
|
|
19990
|
-
|
|
19991
|
-
|
|
19992
|
-
|
|
19993
|
-
|
|
19994
|
-
|
|
19995
|
-
|
|
19996
|
-
|
|
19997
|
-
if (
|
|
20364
|
+
function normalize(s) {
|
|
20365
|
+
return s.replace(/\s+/g, " ").trim();
|
|
20366
|
+
}
|
|
20367
|
+
var MAX_LEVENSHTEIN_LEN = 1e4;
|
|
20368
|
+
function levenshtein(a, b) {
|
|
20369
|
+
if (a.length + b.length > MAX_LEVENSHTEIN_LEN) {
|
|
20370
|
+
const sampleLen = Math.min(500, a.length, b.length);
|
|
20371
|
+
let diffs = 0;
|
|
20372
|
+
for (let i = 0; i < sampleLen; i++) if (a[i] !== b[i]) diffs++;
|
|
20373
|
+
const sampleRate = sampleLen > 0 ? diffs / sampleLen : 1;
|
|
20374
|
+
return Math.abs(a.length - b.length) + Math.round(Math.min(a.length, b.length) * sampleRate);
|
|
19998
20375
|
}
|
|
19999
|
-
|
|
20376
|
+
if (a.length > b.length) [a, b] = [b, a];
|
|
20377
|
+
const m = a.length;
|
|
20378
|
+
const n = b.length;
|
|
20379
|
+
let prev = Array.from({ length: m + 1 }, (_, i) => i);
|
|
20380
|
+
let curr = new Array(m + 1);
|
|
20381
|
+
for (let j = 1; j <= n; j++) {
|
|
20382
|
+
curr[0] = j;
|
|
20383
|
+
for (let i = 1; i <= m; i++) {
|
|
20384
|
+
if (a[i - 1] === b[j - 1]) {
|
|
20385
|
+
curr[i] = prev[i - 1];
|
|
20386
|
+
} else {
|
|
20387
|
+
curr[i] = 1 + Math.min(prev[i - 1], prev[i], curr[i - 1]);
|
|
20388
|
+
}
|
|
20389
|
+
}
|
|
20390
|
+
;
|
|
20391
|
+
[prev, curr] = [curr, prev];
|
|
20392
|
+
}
|
|
20393
|
+
return prev[m];
|
|
20000
20394
|
}
|
|
20001
|
-
|
|
20395
|
+
|
|
20396
|
+
// src/roundtrip/markdown-units.ts
|
|
20397
|
+
function splitMarkdownUnits(md2) {
|
|
20002
20398
|
const lines = md2.split("\n");
|
|
20003
|
-
const
|
|
20399
|
+
const units = [];
|
|
20004
20400
|
let i = 0;
|
|
20005
20401
|
while (i < lines.length) {
|
|
20006
20402
|
const line = lines[i];
|
|
@@ -20008,66 +20404,523 @@ function parseMarkdownToBlocks(md2) {
|
|
|
20008
20404
|
i++;
|
|
20009
20405
|
continue;
|
|
20010
20406
|
}
|
|
20011
|
-
|
|
20012
|
-
|
|
20013
|
-
|
|
20014
|
-
|
|
20015
|
-
|
|
20016
|
-
|
|
20017
|
-
|
|
20018
|
-
|
|
20407
|
+
if (line.trim().startsWith("<table>")) {
|
|
20408
|
+
const collected2 = [];
|
|
20409
|
+
let depth = 0;
|
|
20410
|
+
while (i < lines.length) {
|
|
20411
|
+
const l = lines[i];
|
|
20412
|
+
collected2.push(l);
|
|
20413
|
+
depth += (l.match(/<table>/g) || []).length;
|
|
20414
|
+
depth -= (l.match(/<\/table>/g) || []).length;
|
|
20019
20415
|
i++;
|
|
20416
|
+
if (depth <= 0) break;
|
|
20020
20417
|
}
|
|
20021
|
-
|
|
20022
|
-
blocks.push({ type: "code_block", text: codeLines.join("\n"), lang });
|
|
20023
|
-
continue;
|
|
20024
|
-
}
|
|
20025
|
-
if (/^(\*{3,}|-{3,}|_{3,})\s*$/.test(line.trim())) {
|
|
20026
|
-
blocks.push({ type: "hr" });
|
|
20027
|
-
i++;
|
|
20028
|
-
continue;
|
|
20029
|
-
}
|
|
20030
|
-
const headingMatch = line.match(/^(#{1,6})\s+(.+)$/);
|
|
20031
|
-
if (headingMatch) {
|
|
20032
|
-
blocks.push({ type: "heading", text: headingMatch[2].trim(), level: headingMatch[1].length });
|
|
20033
|
-
i++;
|
|
20418
|
+
units.push({ kind: "html-table", raw: collected2.join("\n"), lines: collected2 });
|
|
20034
20419
|
continue;
|
|
20035
20420
|
}
|
|
20036
20421
|
if (line.trimStart().startsWith("|")) {
|
|
20037
|
-
const
|
|
20422
|
+
const collected2 = [];
|
|
20038
20423
|
while (i < lines.length && lines[i].trimStart().startsWith("|")) {
|
|
20039
|
-
|
|
20040
|
-
if (/^[\s|:\-]+$/.test(row)) {
|
|
20041
|
-
i++;
|
|
20042
|
-
continue;
|
|
20043
|
-
}
|
|
20044
|
-
const cells = row.split("|").slice(1, -1).map((c) => c.trim());
|
|
20045
|
-
if (cells.length > 0) tableRows.push(cells);
|
|
20424
|
+
collected2.push(lines[i]);
|
|
20046
20425
|
i++;
|
|
20047
20426
|
}
|
|
20048
|
-
|
|
20427
|
+
units.push({ kind: "gfm-table", raw: collected2.join("\n"), lines: collected2 });
|
|
20049
20428
|
continue;
|
|
20050
20429
|
}
|
|
20051
|
-
if (line.
|
|
20052
|
-
|
|
20053
|
-
|
|
20054
|
-
quoteLines.push(lines[i].replace(/^>\s?/, ""));
|
|
20055
|
-
i++;
|
|
20056
|
-
}
|
|
20057
|
-
for (const ql of quoteLines) {
|
|
20058
|
-
blocks.push({ type: "blockquote", text: ql.trim() || "" });
|
|
20059
|
-
}
|
|
20430
|
+
if (/^-{3,}\s*$/.test(line.trim())) {
|
|
20431
|
+
units.push({ kind: "separator", raw: line.trim(), lines: [line.trim()] });
|
|
20432
|
+
i++;
|
|
20060
20433
|
continue;
|
|
20061
20434
|
}
|
|
20062
|
-
|
|
20063
|
-
|
|
20064
|
-
const indent = Math.floor(listMatch[1].length / 2);
|
|
20065
|
-
const ordered = /\d/.test(listMatch[2]);
|
|
20066
|
-
blocks.push({ type: "list_item", text: listMatch[3].trim(), ordered, indent });
|
|
20435
|
+
if (/^!\[image\]\([^)]*\)\s*$/.test(line.trim())) {
|
|
20436
|
+
units.push({ kind: "image", raw: line.trim(), lines: [line.trim()] });
|
|
20067
20437
|
i++;
|
|
20068
20438
|
continue;
|
|
20069
20439
|
}
|
|
20070
|
-
|
|
20440
|
+
const collected = [];
|
|
20441
|
+
while (i < lines.length && lines[i].trim() && !lines[i].trimStart().startsWith("|") && !lines[i].trim().startsWith("<table>")) {
|
|
20442
|
+
collected.push(lines[i].trim());
|
|
20443
|
+
i++;
|
|
20444
|
+
}
|
|
20445
|
+
units.push({ kind: "text", raw: collected.join("\n"), lines: collected });
|
|
20446
|
+
}
|
|
20447
|
+
return units;
|
|
20448
|
+
}
|
|
20449
|
+
function alignUnits(a, b) {
|
|
20450
|
+
const m = a.length, n = b.length;
|
|
20451
|
+
if (m * n > 4e6) {
|
|
20452
|
+
const result2 = [];
|
|
20453
|
+
let pre = 0;
|
|
20454
|
+
while (pre < m && pre < n && a[pre] === b[pre]) {
|
|
20455
|
+
result2.push([pre, pre]);
|
|
20456
|
+
pre++;
|
|
20457
|
+
}
|
|
20458
|
+
let suf = 0;
|
|
20459
|
+
while (suf < m - pre && suf < n - pre && a[m - 1 - suf] === b[n - 1 - suf]) suf++;
|
|
20460
|
+
const aMid = m - pre - suf, bMid = n - pre - suf;
|
|
20461
|
+
if (aMid === bMid) {
|
|
20462
|
+
for (let i2 = 0; i2 < aMid; i2++) result2.push([pre + i2, pre + i2]);
|
|
20463
|
+
} else {
|
|
20464
|
+
for (let i2 = 0; i2 < aMid; i2++) result2.push([pre + i2, null]);
|
|
20465
|
+
for (let j2 = 0; j2 < bMid; j2++) result2.push([null, pre + j2]);
|
|
20466
|
+
}
|
|
20467
|
+
for (let s = suf - 1; s >= 0; s--) result2.push([m - 1 - s, n - 1 - s]);
|
|
20468
|
+
return result2;
|
|
20469
|
+
}
|
|
20470
|
+
const dp = Array.from({ length: m + 1 }, () => new Int32Array(n + 1));
|
|
20471
|
+
for (let i2 = 1; i2 <= m; i2++) {
|
|
20472
|
+
for (let j2 = 1; j2 <= n; j2++) {
|
|
20473
|
+
dp[i2][j2] = a[i2 - 1] === b[j2 - 1] ? dp[i2 - 1][j2 - 1] + 1 : Math.max(dp[i2 - 1][j2], dp[i2][j2 - 1]);
|
|
20474
|
+
}
|
|
20475
|
+
}
|
|
20476
|
+
const matches = [];
|
|
20477
|
+
let i = m, j = n;
|
|
20478
|
+
while (i > 0 && j > 0) {
|
|
20479
|
+
if (a[i - 1] === b[j - 1] && dp[i][j] === dp[i - 1][j - 1] + 1) {
|
|
20480
|
+
matches.push([i - 1, j - 1]);
|
|
20481
|
+
i--;
|
|
20482
|
+
j--;
|
|
20483
|
+
} else if (dp[i - 1][j] >= dp[i][j - 1]) i--;
|
|
20484
|
+
else j--;
|
|
20485
|
+
}
|
|
20486
|
+
matches.reverse();
|
|
20487
|
+
const result = [];
|
|
20488
|
+
let ai = 0, bi = 0;
|
|
20489
|
+
const flushGap = (aEnd, bEnd) => {
|
|
20490
|
+
if (aEnd - ai === bEnd - bi) {
|
|
20491
|
+
while (ai < aEnd) result.push([ai++, bi++]);
|
|
20492
|
+
return;
|
|
20493
|
+
}
|
|
20494
|
+
while (ai < aEnd && bi < bEnd) {
|
|
20495
|
+
const sim = normalizedSimilarity(a[ai], b[bi]);
|
|
20496
|
+
if (sim >= 0.4) {
|
|
20497
|
+
if (aEnd - ai > bEnd - bi && bestSimInRange(a, ai + 1, ai + (aEnd - ai) - (bEnd - bi), b[bi]) > sim) {
|
|
20498
|
+
result.push([ai++, null]);
|
|
20499
|
+
} else if (bEnd - bi > aEnd - ai && bestSimInRange(b, bi + 1, bi + (bEnd - bi) - (aEnd - ai), a[ai]) > sim) {
|
|
20500
|
+
result.push([null, bi++]);
|
|
20501
|
+
} else {
|
|
20502
|
+
result.push([ai++, bi++]);
|
|
20503
|
+
}
|
|
20504
|
+
} else if (aEnd - ai >= bEnd - bi) result.push([ai++, null]);
|
|
20505
|
+
else result.push([null, bi++]);
|
|
20506
|
+
}
|
|
20507
|
+
while (ai < aEnd) result.push([ai++, null]);
|
|
20508
|
+
while (bi < bEnd) result.push([null, bi++]);
|
|
20509
|
+
};
|
|
20510
|
+
for (const [pi, pj] of matches) {
|
|
20511
|
+
flushGap(pi, pj);
|
|
20512
|
+
result.push([ai++, bi++]);
|
|
20513
|
+
}
|
|
20514
|
+
flushGap(m, n);
|
|
20515
|
+
return result;
|
|
20516
|
+
}
|
|
20517
|
+
function bestSimInRange(arr, from, to, target) {
|
|
20518
|
+
let best = 0;
|
|
20519
|
+
for (let k = from; k <= to && k < arr.length; k++) {
|
|
20520
|
+
const s = normalizedSimilarity(arr[k], target);
|
|
20521
|
+
if (s > best) best = s;
|
|
20522
|
+
}
|
|
20523
|
+
return best;
|
|
20524
|
+
}
|
|
20525
|
+
function escapeGfm(text) {
|
|
20526
|
+
return text.replace(/~/g, "\\~");
|
|
20527
|
+
}
|
|
20528
|
+
var HWP_SHAPE_ALT_TEXT_RE = /(?:모서리가 둥근 |둥근 )?(?:사각형|직사각형|정사각형|원|타원|삼각형|이등변 삼각형|직각 삼각형|선|직선|곡선|화살표|굵은 화살표|이중 화살표|오각형|육각형|팔각형|별|[4-8]점별|십자|십자형|구름|구름형|마름모|도넛|평행사변형|사다리꼴|부채꼴|호|반원|물결|번개|하트|빗금|블록 화살표|수식|표|그림|개체|그리기\s?개체|묶음\s?개체|글상자|수식\s?개체|OLE\s?개체)\s?입니다\.?/g;
|
|
20529
|
+
function sanitizeText(text) {
|
|
20530
|
+
let result = mapPuaText(text).replace(/[\u{F0000}-\u{FFFFD}]/gu, "").replace(HWP_SHAPE_ALT_TEXT_RE, "").replace(/ +/g, " ").trim();
|
|
20531
|
+
if (result.length <= 30 && result.includes(" ")) {
|
|
20532
|
+
const tokens = result.split(" ");
|
|
20533
|
+
const koreanSingleCharCount = tokens.filter((t) => t.length === 1 && /[가-ㄱ-ㆎ]/.test(t)).length;
|
|
20534
|
+
if (tokens.length >= 3 && koreanSingleCharCount / tokens.length >= 0.7) {
|
|
20535
|
+
result = tokens.join("");
|
|
20536
|
+
}
|
|
20537
|
+
}
|
|
20538
|
+
return result;
|
|
20539
|
+
}
|
|
20540
|
+
function normForMatch(text) {
|
|
20541
|
+
return sanitizeText(text).replace(/\s+/g, " ").trim();
|
|
20542
|
+
}
|
|
20543
|
+
function unescapeGfm(text) {
|
|
20544
|
+
return text.replace(/\\~/g, "~");
|
|
20545
|
+
}
|
|
20546
|
+
function summarize(text) {
|
|
20547
|
+
const t = text.replace(/\s+/g, " ").trim();
|
|
20548
|
+
return t.length > 80 ? t.slice(0, 77) + "..." : t;
|
|
20549
|
+
}
|
|
20550
|
+
function replicateGfmTable(table) {
|
|
20551
|
+
const { cells, rows: numRows, cols: numCols } = table;
|
|
20552
|
+
if (numRows === 0 || numCols === 0) return null;
|
|
20553
|
+
if (numRows === 1 && numCols === 1) return null;
|
|
20554
|
+
if (numCols === 1) return null;
|
|
20555
|
+
const display = Array.from({ length: numRows }, (_, r) => Array.from({ length: numCols }, (_2, c) => ({ text: "", gridR: r, gridC: c })));
|
|
20556
|
+
const skip = /* @__PURE__ */ new Set();
|
|
20557
|
+
for (let r = 0; r < numRows; r++) {
|
|
20558
|
+
for (let c = 0; c < numCols; c++) {
|
|
20559
|
+
if (skip.has(`${r},${c}`)) continue;
|
|
20560
|
+
const cell = cells[r]?.[c];
|
|
20561
|
+
if (!cell) continue;
|
|
20562
|
+
display[r][c] = {
|
|
20563
|
+
text: escapeGfm(sanitizeText(cell.text)).replace(/\|/g, "\\|").replace(/\n/g, "<br>"),
|
|
20564
|
+
gridR: r,
|
|
20565
|
+
gridC: c
|
|
20566
|
+
};
|
|
20567
|
+
for (let dr = 0; dr < cell.rowSpan; dr++) {
|
|
20568
|
+
for (let dc = 0; dc < cell.colSpan; dc++) {
|
|
20569
|
+
if (dr === 0 && dc === 0) continue;
|
|
20570
|
+
if (r + dr < numRows && c + dc < numCols) skip.add(`${r + dr},${c + dc}`);
|
|
20571
|
+
}
|
|
20572
|
+
}
|
|
20573
|
+
c += cell.colSpan - 1;
|
|
20574
|
+
}
|
|
20575
|
+
}
|
|
20576
|
+
const uniqueRows = [];
|
|
20577
|
+
let pendingLabelRow = null;
|
|
20578
|
+
for (let r = 0; r < display.length; r++) {
|
|
20579
|
+
const row = display[r];
|
|
20580
|
+
if (row.every((cell) => cell.text === "")) continue;
|
|
20581
|
+
const nonEmptyCols = row.filter((cell) => cell.text !== "");
|
|
20582
|
+
const hasSkipInRow = row.some((_, c) => skip.has(`${r},${c}`));
|
|
20583
|
+
if (!hasSkipInRow && nonEmptyCols.length === 1 && row[0].text !== "" && row.slice(1).every((c) => c.text === "")) {
|
|
20584
|
+
if (pendingLabelRow) uniqueRows.push(pendingLabelRow);
|
|
20585
|
+
pendingLabelRow = row;
|
|
20586
|
+
continue;
|
|
20587
|
+
}
|
|
20588
|
+
if (pendingLabelRow) {
|
|
20589
|
+
if (row[0].text === "") row[0] = pendingLabelRow[0];
|
|
20590
|
+
else uniqueRows.push(pendingLabelRow);
|
|
20591
|
+
pendingLabelRow = null;
|
|
20592
|
+
}
|
|
20593
|
+
uniqueRows.push(row);
|
|
20594
|
+
}
|
|
20595
|
+
if (pendingLabelRow) uniqueRows.push(pendingLabelRow);
|
|
20596
|
+
return uniqueRows.length > 0 ? uniqueRows : null;
|
|
20597
|
+
}
|
|
20598
|
+
function parseGfmTable(lines) {
|
|
20599
|
+
const rows = [];
|
|
20600
|
+
for (const line of lines) {
|
|
20601
|
+
const trimmed = line.trim();
|
|
20602
|
+
if (!trimmed.startsWith("|")) continue;
|
|
20603
|
+
const cells = trimmed.split(/(?<!\\)\|/).slice(1, -1).map((c) => c.trim());
|
|
20604
|
+
if (cells.length === 0) continue;
|
|
20605
|
+
if (cells.every((c) => /^:?-{3,}:?$/.test(c))) continue;
|
|
20606
|
+
rows.push(cells);
|
|
20607
|
+
}
|
|
20608
|
+
return rows;
|
|
20609
|
+
}
|
|
20610
|
+
function unescapeGfmCell(text) {
|
|
20611
|
+
return text.replace(/<br\s*\/?>/gi, "\n").replace(/\\\|/g, "|").replace(/\\~/g, "~");
|
|
20612
|
+
}
|
|
20613
|
+
function replicateCellInnerHtml(cell) {
|
|
20614
|
+
if (cell.blocks?.length) {
|
|
20615
|
+
return cell.blocks.map((b) => {
|
|
20616
|
+
if (b.type === "table" && b.table) {
|
|
20617
|
+
const cap = b.table.caption ? sanitizeText(b.table.caption) : "";
|
|
20618
|
+
return (cap ? cap + "<br>" : "") + replicateTableToHtml(b.table);
|
|
20619
|
+
}
|
|
20620
|
+
if (b.type === "image" && b.text) return `<img src="${b.text}" alt="image">`;
|
|
20621
|
+
const t = sanitizeText(b.text ?? "");
|
|
20622
|
+
return t ? t.replace(/\n/g, "<br>") : "";
|
|
20623
|
+
}).filter(Boolean).join("<br>");
|
|
20624
|
+
}
|
|
20625
|
+
return sanitizeText(cell.text).replace(/\n/g, "<br>");
|
|
20626
|
+
}
|
|
20627
|
+
function replicateTableToHtml(table) {
|
|
20628
|
+
const rows = replicateHtmlTable(table);
|
|
20629
|
+
const lines = ["<table>"];
|
|
20630
|
+
for (let r = 0; r < rows.length; r++) {
|
|
20631
|
+
const tag = rows[r].tag;
|
|
20632
|
+
const rowHtml = rows[r].cells.map((cell) => {
|
|
20633
|
+
const attrs = [];
|
|
20634
|
+
if (cell.colSpan > 1) attrs.push(`colspan="${cell.colSpan}"`);
|
|
20635
|
+
if (cell.rowSpan > 1) attrs.push(`rowspan="${cell.rowSpan}"`);
|
|
20636
|
+
const attrStr = attrs.length ? " " + attrs.join(" ") : "";
|
|
20637
|
+
return `<${tag}${attrStr}>${cell.inner}</${tag}>`;
|
|
20638
|
+
});
|
|
20639
|
+
if (rowHtml.length) lines.push(`<tr>${rowHtml.join("")}</tr>`);
|
|
20640
|
+
}
|
|
20641
|
+
lines.push("</table>");
|
|
20642
|
+
return lines.join("\n");
|
|
20643
|
+
}
|
|
20644
|
+
function replicateHtmlTable(table) {
|
|
20645
|
+
const { cells, rows: numRows, cols: numCols } = table;
|
|
20646
|
+
const skip = /* @__PURE__ */ new Set();
|
|
20647
|
+
const result = [];
|
|
20648
|
+
for (let r = 0; r < numRows; r++) {
|
|
20649
|
+
const tag = r === 0 ? "th" : "td";
|
|
20650
|
+
const rowCells = [];
|
|
20651
|
+
for (let c = 0; c < numCols; c++) {
|
|
20652
|
+
if (skip.has(`${r},${c}`)) continue;
|
|
20653
|
+
const cell = cells[r]?.[c];
|
|
20654
|
+
if (!cell) continue;
|
|
20655
|
+
for (let dr = 0; dr < cell.rowSpan; dr++) {
|
|
20656
|
+
for (let dc = 0; dc < cell.colSpan; dc++) {
|
|
20657
|
+
if (dr === 0 && dc === 0) continue;
|
|
20658
|
+
if (r + dr < numRows && c + dc < numCols) skip.add(`${r + dr},${c + dc}`);
|
|
20659
|
+
}
|
|
20660
|
+
}
|
|
20661
|
+
rowCells.push({
|
|
20662
|
+
inner: replicateCellInnerHtml(cell),
|
|
20663
|
+
colSpan: cell.colSpan,
|
|
20664
|
+
rowSpan: cell.rowSpan,
|
|
20665
|
+
gridR: r,
|
|
20666
|
+
gridC: c
|
|
20667
|
+
});
|
|
20668
|
+
}
|
|
20669
|
+
if (rowCells.length) result.push({ tag, cells: rowCells });
|
|
20670
|
+
}
|
|
20671
|
+
return result;
|
|
20672
|
+
}
|
|
20673
|
+
function parseHtmlTable(raw) {
|
|
20674
|
+
const re = /<(\/?)(table|tr|td|th)((?:"[^"]*"|'[^']*'|[^>"'])*?)>/gi;
|
|
20675
|
+
let depth = 0;
|
|
20676
|
+
let currentRow = null;
|
|
20677
|
+
let cellStart = -1;
|
|
20678
|
+
let cellInfo = null;
|
|
20679
|
+
const rows = [];
|
|
20680
|
+
let m;
|
|
20681
|
+
while ((m = re.exec(raw)) !== null) {
|
|
20682
|
+
const isClose = m[1] === "/";
|
|
20683
|
+
const tag = m[2].toLowerCase();
|
|
20684
|
+
const attrs = m[3] || "";
|
|
20685
|
+
if (tag === "table") {
|
|
20686
|
+
depth += isClose ? -1 : 1;
|
|
20687
|
+
if (depth < 0) return null;
|
|
20688
|
+
continue;
|
|
20689
|
+
}
|
|
20690
|
+
if (depth !== 1) continue;
|
|
20691
|
+
if (tag === "tr") {
|
|
20692
|
+
if (!isClose) currentRow = [];
|
|
20693
|
+
else if (currentRow) {
|
|
20694
|
+
rows.push({ tag: rows.length === 0 ? "th" : "td", cells: currentRow });
|
|
20695
|
+
currentRow = null;
|
|
20696
|
+
}
|
|
20697
|
+
} else {
|
|
20698
|
+
if (!isClose) {
|
|
20699
|
+
const cs = parseInt(attrs.match(/colspan\s*=\s*"(\d+)"/i)?.[1] || "1", 10);
|
|
20700
|
+
const rs = parseInt(attrs.match(/rowspan\s*=\s*"(\d+)"/i)?.[1] || "1", 10);
|
|
20701
|
+
cellStart = m.index + m[0].length;
|
|
20702
|
+
cellInfo = { colSpan: isNaN(cs) ? 1 : cs, rowSpan: isNaN(rs) ? 1 : rs };
|
|
20703
|
+
} else if (cellStart >= 0 && cellInfo && currentRow) {
|
|
20704
|
+
currentRow.push({ inner: raw.slice(cellStart, m.index), colSpan: cellInfo.colSpan, rowSpan: cellInfo.rowSpan });
|
|
20705
|
+
cellStart = -1;
|
|
20706
|
+
cellInfo = null;
|
|
20707
|
+
}
|
|
20708
|
+
}
|
|
20709
|
+
}
|
|
20710
|
+
if (depth !== 0) return null;
|
|
20711
|
+
return rows;
|
|
20712
|
+
}
|
|
20713
|
+
var AUTONUM_PREFIX_RE = /^(?:[0-90-9a-zA-Z가-힣]{1,6}[.)\]:]|[([][0-90-9a-zA-Z가-힣]{1,6}[)\]][.:]?|[ⅰ-ⅹⅠ-Ⅹ①-⑮][.)\]:]?)$/u;
|
|
20714
|
+
function htmlCellInnerToLines(inner) {
|
|
20715
|
+
let hadNonText = false;
|
|
20716
|
+
let work = inner;
|
|
20717
|
+
if (/<table[\s>]/i.test(work)) {
|
|
20718
|
+
hadNonText = true;
|
|
20719
|
+
work = removeNestedTables(work);
|
|
20720
|
+
}
|
|
20721
|
+
if (/<img\s/i.test(work)) {
|
|
20722
|
+
hadNonText = true;
|
|
20723
|
+
work = work.replace(/<img\s(?:"[^"]*"|'[^']*'|[^>"'])*?>/gi, "");
|
|
20724
|
+
}
|
|
20725
|
+
const lines = work.split(/<br\s*\/?>/gi).map((s) => s.trim()).filter((s) => s.length > 0);
|
|
20726
|
+
return { lines, hadNonText };
|
|
20727
|
+
}
|
|
20728
|
+
function extractTopLevelTables(html) {
|
|
20729
|
+
const result = [];
|
|
20730
|
+
let depth = 0;
|
|
20731
|
+
let start = -1;
|
|
20732
|
+
const re = /<(\/?)table(?:[\s>]|>)/gi;
|
|
20733
|
+
let m;
|
|
20734
|
+
while ((m = re.exec(html)) !== null) {
|
|
20735
|
+
if (m[1] !== "/") {
|
|
20736
|
+
if (depth === 0) start = m.index;
|
|
20737
|
+
depth++;
|
|
20738
|
+
} else {
|
|
20739
|
+
depth--;
|
|
20740
|
+
if (depth === 0 && start >= 0) {
|
|
20741
|
+
result.push(html.slice(start, m.index + m[0].length));
|
|
20742
|
+
start = -1;
|
|
20743
|
+
}
|
|
20744
|
+
if (depth < 0) depth = 0;
|
|
20745
|
+
}
|
|
20746
|
+
}
|
|
20747
|
+
return result;
|
|
20748
|
+
}
|
|
20749
|
+
function removeNestedTables(html) {
|
|
20750
|
+
let result = "";
|
|
20751
|
+
let depth = 0;
|
|
20752
|
+
const re = /<(\/?)table(?:[\s>]|>)/gi;
|
|
20753
|
+
let last = 0;
|
|
20754
|
+
let m;
|
|
20755
|
+
while ((m = re.exec(html)) !== null) {
|
|
20756
|
+
if (m[1] !== "/") {
|
|
20757
|
+
if (depth === 0) result += html.slice(last, m.index);
|
|
20758
|
+
depth++;
|
|
20759
|
+
} else {
|
|
20760
|
+
depth--;
|
|
20761
|
+
if (depth === 0) last = m.index + m[0].length;
|
|
20762
|
+
if (depth < 0) depth = 0;
|
|
20763
|
+
}
|
|
20764
|
+
}
|
|
20765
|
+
if (depth === 0) result += html.slice(last);
|
|
20766
|
+
return result;
|
|
20767
|
+
}
|
|
20768
|
+
|
|
20769
|
+
// src/hwpx/generator.ts
|
|
20770
|
+
var NS_SECTION = "http://www.hancom.co.kr/hwpml/2011/section";
|
|
20771
|
+
var NS_PARA = "http://www.hancom.co.kr/hwpml/2011/paragraph";
|
|
20772
|
+
var NS_HEAD = "http://www.hancom.co.kr/hwpml/2011/head";
|
|
20773
|
+
var NS_CORE = "http://www.hancom.co.kr/hwpml/2011/core";
|
|
20774
|
+
var NS_OPF = "http://www.idpf.org/2007/opf/";
|
|
20775
|
+
var NS_HPF = "http://www.hancom.co.kr/schema/2011/hpf";
|
|
20776
|
+
var NS_OCF = "urn:oasis:names:tc:opendocument:xmlns:container";
|
|
20777
|
+
var CHAR_NORMAL = 0;
|
|
20778
|
+
var CHAR_BOLD = 1;
|
|
20779
|
+
var CHAR_ITALIC = 2;
|
|
20780
|
+
var CHAR_BOLD_ITALIC = 3;
|
|
20781
|
+
var CHAR_CODE = 4;
|
|
20782
|
+
var CHAR_H1 = 5;
|
|
20783
|
+
var CHAR_H2 = 6;
|
|
20784
|
+
var CHAR_H3 = 7;
|
|
20785
|
+
var CHAR_H4 = 8;
|
|
20786
|
+
var CHAR_TABLE_HEADER = 9;
|
|
20787
|
+
var CHAR_QUOTE = 10;
|
|
20788
|
+
var PARA_NORMAL = 0;
|
|
20789
|
+
var PARA_H1 = 1;
|
|
20790
|
+
var PARA_H2 = 2;
|
|
20791
|
+
var PARA_H3 = 3;
|
|
20792
|
+
var PARA_H4 = 4;
|
|
20793
|
+
var PARA_CODE = 5;
|
|
20794
|
+
var PARA_QUOTE = 6;
|
|
20795
|
+
var PARA_LIST = 7;
|
|
20796
|
+
var DEFAULT_TEXT_COLOR = "#000000";
|
|
20797
|
+
function resolveTheme(theme) {
|
|
20798
|
+
return {
|
|
20799
|
+
h1: theme?.headingColors?.[1] ?? DEFAULT_TEXT_COLOR,
|
|
20800
|
+
h2: theme?.headingColors?.[2] ?? DEFAULT_TEXT_COLOR,
|
|
20801
|
+
h3: theme?.headingColors?.[3] ?? DEFAULT_TEXT_COLOR,
|
|
20802
|
+
h4: theme?.headingColors?.[4] ?? theme?.headingColors?.[3] ?? DEFAULT_TEXT_COLOR,
|
|
20803
|
+
body: theme?.bodyColor ?? DEFAULT_TEXT_COLOR,
|
|
20804
|
+
quote: theme?.quoteColor ?? DEFAULT_TEXT_COLOR,
|
|
20805
|
+
/** quoteColor가 명시되었는지 — blockquote charPr 분기에 사용 (baseline 호환) */
|
|
20806
|
+
hasQuoteOption: theme?.quoteColor !== void 0,
|
|
20807
|
+
tableHeader: theme?.tableHeaderColor ?? theme?.bodyColor ?? DEFAULT_TEXT_COLOR,
|
|
20808
|
+
tableHeaderBold: !!theme?.tableHeaderBold
|
|
20809
|
+
};
|
|
20810
|
+
}
|
|
20811
|
+
async function markdownToHwpx(markdown, options) {
|
|
20812
|
+
const theme = resolveTheme(options?.theme);
|
|
20813
|
+
const gongmun = options?.gongmun ? resolveGongmun(options.gongmun) : null;
|
|
20814
|
+
const blocks = parseMarkdownToBlocks(markdown);
|
|
20815
|
+
const gongmunList = gongmun ? precomputeGongmunList(blocks, gongmun) : null;
|
|
20816
|
+
const fit = gongmun && gongmunList ? computeGongmunFitPlan(blocks, gongmun, gongmunList) : null;
|
|
20817
|
+
const sectionXml = blocksToSectionXml(blocks, theme, gongmun, gongmunList, fit);
|
|
20818
|
+
const zip = new JSZip5();
|
|
20819
|
+
zip.file("mimetype", "application/hwp+zip", { compression: "STORE" });
|
|
20820
|
+
zip.file("META-INF/container.xml", generateContainerXml());
|
|
20821
|
+
zip.file("Contents/content.hpf", generateManifest());
|
|
20822
|
+
zip.file("Contents/header.xml", generateHeaderXml(theme, gongmun, fit?.variants ?? []));
|
|
20823
|
+
zip.file("Contents/section0.xml", sectionXml);
|
|
20824
|
+
zip.file("Preview/PrvText.txt", buildPrvText(blocks));
|
|
20825
|
+
return await zip.generateAsync({ type: "arraybuffer" });
|
|
20826
|
+
}
|
|
20827
|
+
function buildPrvText(blocks) {
|
|
20828
|
+
const lines = [];
|
|
20829
|
+
let bytes = 0;
|
|
20830
|
+
for (const b of blocks) {
|
|
20831
|
+
let text = b.text || (b.rows ? b.rows.map((r) => r.join(" ")).join("\n") : "");
|
|
20832
|
+
if (b.type === "html_table") text = text.replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
|
|
20833
|
+
if (!text) continue;
|
|
20834
|
+
lines.push(text);
|
|
20835
|
+
bytes += text.length * 3;
|
|
20836
|
+
if (bytes > 1024) break;
|
|
20837
|
+
}
|
|
20838
|
+
return lines.join("\n").slice(0, 1024);
|
|
20839
|
+
}
|
|
20840
|
+
function parseMarkdownToBlocks(md2) {
|
|
20841
|
+
const lines = md2.split("\n");
|
|
20842
|
+
const blocks = [];
|
|
20843
|
+
let i = 0;
|
|
20844
|
+
while (i < lines.length) {
|
|
20845
|
+
const line = lines[i];
|
|
20846
|
+
if (!line.trim()) {
|
|
20847
|
+
i++;
|
|
20848
|
+
continue;
|
|
20849
|
+
}
|
|
20850
|
+
const fenceMatch = line.match(/^(`{3,}|~{3,})(.*)$/);
|
|
20851
|
+
if (fenceMatch) {
|
|
20852
|
+
const fence = fenceMatch[1];
|
|
20853
|
+
const lang = fenceMatch[2].trim();
|
|
20854
|
+
const codeLines = [];
|
|
20855
|
+
i++;
|
|
20856
|
+
while (i < lines.length && !lines[i].startsWith(fence)) {
|
|
20857
|
+
codeLines.push(lines[i]);
|
|
20858
|
+
i++;
|
|
20859
|
+
}
|
|
20860
|
+
if (i < lines.length) i++;
|
|
20861
|
+
blocks.push({ type: "code_block", text: codeLines.join("\n"), lang });
|
|
20862
|
+
continue;
|
|
20863
|
+
}
|
|
20864
|
+
if (/^(\*{3,}|-{3,}|_{3,})\s*$/.test(line.trim())) {
|
|
20865
|
+
blocks.push({ type: "hr" });
|
|
20866
|
+
i++;
|
|
20867
|
+
continue;
|
|
20868
|
+
}
|
|
20869
|
+
const headingMatch = line.match(/^(#{1,6})\s+(.+)$/);
|
|
20870
|
+
if (headingMatch) {
|
|
20871
|
+
blocks.push({ type: "heading", text: headingMatch[2].trim(), level: headingMatch[1].length });
|
|
20872
|
+
i++;
|
|
20873
|
+
continue;
|
|
20874
|
+
}
|
|
20875
|
+
if (/^<table[\s>]/i.test(line.trimStart())) {
|
|
20876
|
+
const htmlLines = [];
|
|
20877
|
+
let depth = 0;
|
|
20878
|
+
while (i < lines.length) {
|
|
20879
|
+
const l = lines[i];
|
|
20880
|
+
htmlLines.push(l);
|
|
20881
|
+
depth += (l.match(/<table[\s>]/gi) ?? []).length;
|
|
20882
|
+
depth -= (l.match(/<\/table>/gi) ?? []).length;
|
|
20883
|
+
i++;
|
|
20884
|
+
if (depth <= 0) break;
|
|
20885
|
+
}
|
|
20886
|
+
blocks.push({ type: "html_table", text: htmlLines.join("\n") });
|
|
20887
|
+
continue;
|
|
20888
|
+
}
|
|
20889
|
+
if (line.trimStart().startsWith("|")) {
|
|
20890
|
+
const tableRows = [];
|
|
20891
|
+
while (i < lines.length && lines[i].trimStart().startsWith("|")) {
|
|
20892
|
+
const row = lines[i];
|
|
20893
|
+
if (/^[\s|:\-]+$/.test(row)) {
|
|
20894
|
+
i++;
|
|
20895
|
+
continue;
|
|
20896
|
+
}
|
|
20897
|
+
const cells = row.split("|").slice(1, -1).map((c) => c.trim());
|
|
20898
|
+
if (cells.length > 0) tableRows.push(cells);
|
|
20899
|
+
i++;
|
|
20900
|
+
}
|
|
20901
|
+
if (tableRows.length > 0) blocks.push({ type: "table", rows: tableRows });
|
|
20902
|
+
continue;
|
|
20903
|
+
}
|
|
20904
|
+
if (line.trimStart().startsWith("> ")) {
|
|
20905
|
+
const quoteLines = [];
|
|
20906
|
+
while (i < lines.length && (lines[i].trimStart().startsWith("> ") || lines[i].trimStart().startsWith(">"))) {
|
|
20907
|
+
quoteLines.push(lines[i].replace(/^>\s?/, ""));
|
|
20908
|
+
i++;
|
|
20909
|
+
}
|
|
20910
|
+
for (const ql of quoteLines) {
|
|
20911
|
+
blocks.push({ type: "blockquote", text: ql.trim() || "" });
|
|
20912
|
+
}
|
|
20913
|
+
continue;
|
|
20914
|
+
}
|
|
20915
|
+
const listMatch = line.match(/^(\s*)([-*+]|\d+[.)]) (.+)$/);
|
|
20916
|
+
if (listMatch) {
|
|
20917
|
+
const indent = Math.floor(listMatch[1].length / 2);
|
|
20918
|
+
const ordered = /\d/.test(listMatch[2]);
|
|
20919
|
+
blocks.push({ type: "list_item", text: listMatch[3].trim(), ordered, indent });
|
|
20920
|
+
i++;
|
|
20921
|
+
continue;
|
|
20922
|
+
}
|
|
20923
|
+
blocks.push({ type: "paragraph", text: line.trim() });
|
|
20071
20924
|
i++;
|
|
20072
20925
|
}
|
|
20073
20926
|
return blocks;
|
|
@@ -20114,18 +20967,19 @@ function spanToCharPrId(span) {
|
|
|
20114
20967
|
function escapeXml(text) {
|
|
20115
20968
|
return text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
20116
20969
|
}
|
|
20117
|
-
function generateRuns(text, defaultCharPr = CHAR_NORMAL) {
|
|
20970
|
+
function generateRuns(text, defaultCharPr = CHAR_NORMAL, mapCharId) {
|
|
20118
20971
|
const spans = parseInlineMarkdown(text);
|
|
20119
20972
|
return spans.map((span) => {
|
|
20120
|
-
|
|
20973
|
+
let charId = span.code || span.bold || span.italic ? spanToCharPrId(span) : defaultCharPr;
|
|
20974
|
+
if (mapCharId) charId = mapCharId(charId);
|
|
20121
20975
|
return `<hp:run charPrIDRef="${charId}"><hp:t>${escapeXml(span.text)}</hp:t></hp:run>`;
|
|
20122
20976
|
}).join("");
|
|
20123
20977
|
}
|
|
20124
|
-
function generateParagraph(text, paraPrId = PARA_NORMAL, charPrId = CHAR_NORMAL) {
|
|
20978
|
+
function generateParagraph(text, paraPrId = PARA_NORMAL, charPrId = CHAR_NORMAL, mapCharId) {
|
|
20125
20979
|
if (paraPrId === PARA_CODE) {
|
|
20126
20980
|
return `<hp:p paraPrIDRef="${paraPrId}" styleIDRef="0"><hp:run charPrIDRef="${CHAR_CODE}"><hp:t>${escapeXml(text)}</hp:t></hp:run></hp:p>`;
|
|
20127
20981
|
}
|
|
20128
|
-
const runs = generateRuns(text, charPrId);
|
|
20982
|
+
const runs = generateRuns(text, charPrId, mapCharId);
|
|
20129
20983
|
return `<hp:p paraPrIDRef="${paraPrId}" styleIDRef="0">${runs}</hp:p>`;
|
|
20130
20984
|
}
|
|
20131
20985
|
function headingParaPrId(level) {
|
|
@@ -20165,7 +21019,7 @@ function charPr(id, height, bold, italic, fontId = 0, textColor = DEFAULT_TEXT_C
|
|
|
20165
21019
|
const boldAttr = bold ? ` bold="1"` : "";
|
|
20166
21020
|
const italicAttr = italic ? ` italic="1"` : "";
|
|
20167
21021
|
const effFont = bold ? 2 : fontId;
|
|
20168
|
-
return ` <hh:charPr id="${id}" height="${height}" textColor="${textColor}" shadeColor="none" useFontSpace="0" useKerning="0" symMark="NONE" borderFillIDRef="
|
|
21022
|
+
return ` <hh:charPr id="${id}" height="${height}" textColor="${textColor}" shadeColor="none" useFontSpace="0" useKerning="0" symMark="NONE" borderFillIDRef="1"${boldAttr}${italicAttr}>
|
|
20169
21023
|
<hh:fontRef hangul="${effFont}" latin="${effFont}" hanja="${effFont}" japanese="${effFont}" other="${effFont}" symbol="${effFont}" user="${effFont}"/>
|
|
20170
21024
|
<hh:ratio hangul="${ratioPct}" latin="${ratioPct}" hanja="${ratioPct}" japanese="100" other="100" symbol="100" user="100"/>
|
|
20171
21025
|
<hh:spacing hangul="0" latin="0" hanja="0" japanese="0" other="0" symbol="0" user="0"/>
|
|
@@ -20184,13 +21038,58 @@ function paraPr(id, opts = {}) {
|
|
|
20184
21038
|
<hh:autoSpacing eAsianEng="0" eAsianNum="0"/>
|
|
20185
21039
|
<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>
|
|
20186
21040
|
<hh:lineSpacing type="PERCENT" value="${lineSpacing}"/>
|
|
20187
|
-
<hh:border borderFillIDRef="
|
|
21041
|
+
<hh:border borderFillIDRef="1" offsetLeft="0" offsetRight="0" offsetTop="0" offsetBottom="0" connect="0" ignoreMargin="0"/>
|
|
20188
21042
|
</hh:paraPr>`;
|
|
20189
21043
|
}
|
|
20190
21044
|
var GONGMUN_LIST_BASE = 8;
|
|
20191
21045
|
var GONGMUN_LIST_LEVELS = 8;
|
|
20192
21046
|
var GONGMUN_CENTER = GONGMUN_LIST_BASE + GONGMUN_LIST_LEVELS;
|
|
20193
|
-
|
|
21047
|
+
var CHAR_VARIANT_BASE = 11;
|
|
21048
|
+
var GONGMUN_BODY_RATIO = 95;
|
|
21049
|
+
function plainRenderText(text) {
|
|
21050
|
+
return parseInlineMarkdown(text).map((s) => s.text).join("");
|
|
21051
|
+
}
|
|
21052
|
+
function computeGongmunFitPlan(blocks, gongmun, gongmunList) {
|
|
21053
|
+
const minRatio = gongmun.autoFitMinRatio;
|
|
21054
|
+
if (minRatio === null || minRatio >= GONGMUN_BODY_RATIO) return null;
|
|
21055
|
+
const pageW = 59528 - mmToHwpunit(gongmun.margins.left) - mmToHwpunit(gongmun.margins.right);
|
|
21056
|
+
const ratioByBlock = /* @__PURE__ */ new Map();
|
|
21057
|
+
const variants = [];
|
|
21058
|
+
for (let i = 0; i < blocks.length; i++) {
|
|
21059
|
+
const block = blocks[i];
|
|
21060
|
+
let text;
|
|
21061
|
+
let firstW;
|
|
21062
|
+
let contW;
|
|
21063
|
+
if (block.type === "list_item" && gongmunList.has(i)) {
|
|
21064
|
+
const { marker, depth } = gongmunList.get(i);
|
|
21065
|
+
const content = plainRenderText(block.text || "");
|
|
21066
|
+
text = marker ? `${marker} ${content}` : content;
|
|
21067
|
+
const { left, indent } = levelIndent(depth, gongmun.bodyHeight, gongmun.numbering);
|
|
21068
|
+
firstW = pageW - left - Math.max(indent, 0);
|
|
21069
|
+
contW = pageW - left - Math.max(-indent, 0);
|
|
21070
|
+
} else if (block.type === "paragraph") {
|
|
21071
|
+
const raw = (block.text || "").trim();
|
|
21072
|
+
if (/^<center>[\s\S]*<\/center>$/i.test(raw)) continue;
|
|
21073
|
+
text = plainRenderText(raw);
|
|
21074
|
+
firstW = contW = pageW;
|
|
21075
|
+
} else {
|
|
21076
|
+
continue;
|
|
21077
|
+
}
|
|
21078
|
+
if (!text) continue;
|
|
21079
|
+
const r = fitRatioForFewerLines(text, firstW, contW, gongmun.bodyHeight, GONGMUN_BODY_RATIO, minRatio);
|
|
21080
|
+
if (r === null) continue;
|
|
21081
|
+
ratioByBlock.set(i, r);
|
|
21082
|
+
if (!variants.includes(r)) variants.push(r);
|
|
21083
|
+
}
|
|
21084
|
+
return ratioByBlock.size > 0 ? { ratioByBlock, variants } : null;
|
|
21085
|
+
}
|
|
21086
|
+
function variantMapper(fit, blockIdx) {
|
|
21087
|
+
const r = fit.ratioByBlock.get(blockIdx);
|
|
21088
|
+
if (r === void 0) return void 0;
|
|
21089
|
+
const vi = fit.variants.indexOf(r);
|
|
21090
|
+
return (id) => id >= 0 && id <= 3 ? CHAR_VARIANT_BASE + vi * 4 + id : id;
|
|
21091
|
+
}
|
|
21092
|
+
function buildCharProperties(theme, gongmun, ratioVariants = []) {
|
|
20194
21093
|
let body = 1e3, code = 900, h1 = 1800, h2 = 1400, h3 = 1200, h4 = 1100;
|
|
20195
21094
|
if (gongmun) {
|
|
20196
21095
|
body = gongmun.bodyHeight;
|
|
@@ -20200,7 +21099,7 @@ function buildCharProperties(theme, gongmun) {
|
|
|
20200
21099
|
h3 = body;
|
|
20201
21100
|
h4 = Math.max(body - 100, 1300);
|
|
20202
21101
|
}
|
|
20203
|
-
const bodyRatio = gongmun ?
|
|
21102
|
+
const bodyRatio = gongmun ? GONGMUN_BODY_RATIO : 100;
|
|
20204
21103
|
const rows = [
|
|
20205
21104
|
charPr(0, body, false, false, 0, theme.body, bodyRatio),
|
|
20206
21105
|
charPr(1, body, true, false, 0, theme.body, bodyRatio),
|
|
@@ -20214,6 +21113,14 @@ function buildCharProperties(theme, gongmun) {
|
|
|
20214
21113
|
charPr(CHAR_TABLE_HEADER, body, theme.tableHeaderBold, false, 0, theme.tableHeader),
|
|
20215
21114
|
charPr(CHAR_QUOTE, body, false, true, 0, theme.quote)
|
|
20216
21115
|
];
|
|
21116
|
+
for (const r of ratioVariants) {
|
|
21117
|
+
rows.push(
|
|
21118
|
+
charPr(rows.length, body, false, false, 0, theme.body, r),
|
|
21119
|
+
charPr(rows.length + 1, body, true, false, 0, theme.body, r),
|
|
21120
|
+
charPr(rows.length + 2, body, false, true, 0, theme.body, r),
|
|
21121
|
+
charPr(rows.length + 3, body, true, true, 0, theme.body, r)
|
|
21122
|
+
);
|
|
21123
|
+
}
|
|
20217
21124
|
return `<hh:charProperties itemCnt="${rows.length}">
|
|
20218
21125
|
${rows.join("\n")}
|
|
20219
21126
|
</hh:charProperties>`;
|
|
@@ -20256,9 +21163,9 @@ ${base2.join("\n")}
|
|
|
20256
21163
|
${base.join("\n")}
|
|
20257
21164
|
</hh:paraProperties>`;
|
|
20258
21165
|
}
|
|
20259
|
-
function generateHeaderXml(theme, gongmun) {
|
|
21166
|
+
function generateHeaderXml(theme, gongmun, ratioVariants = []) {
|
|
20260
21167
|
const bodyFace = gongmun?.bodyFont === "gothic" ? "\uB9D1\uC740 \uACE0\uB515" : "\uD568\uCD08\uB86C\uBC14\uD0D5";
|
|
20261
|
-
const charPropsXml = buildCharProperties(theme, gongmun);
|
|
21168
|
+
const charPropsXml = buildCharProperties(theme, gongmun, ratioVariants);
|
|
20262
21169
|
const paraPropsXml = buildParaProperties(gongmun);
|
|
20263
21170
|
return `<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
|
|
20264
21171
|
<hh:head xmlns:hh="${NS_HEAD}" xmlns:hp="${NS_PARA}" xmlns:hc="${NS_CORE}" version="1.4" secCnt="1">
|
|
@@ -20314,25 +21221,21 @@ function generateHeaderXml(theme, gongmun) {
|
|
|
20314
21221
|
</hh:fontface>
|
|
20315
21222
|
</hh:fontfaces>
|
|
20316
21223
|
<hh:borderFills itemCnt="2">
|
|
20317
|
-
<hh:borderFill id="
|
|
21224
|
+
<hh:borderFill id="1" threeD="0" shadow="0" centerLine="NONE" breakCellSeparateLine="0">
|
|
20318
21225
|
<hh:slash type="NONE" Crooked="0" isCounter="0"/>
|
|
20319
21226
|
<hh:backSlash type="NONE" Crooked="0" isCounter="0"/>
|
|
20320
21227
|
<hh:leftBorder type="NONE" width="0.1 mm" color="#000000"/>
|
|
20321
21228
|
<hh:rightBorder type="NONE" width="0.1 mm" color="#000000"/>
|
|
20322
21229
|
<hh:topBorder type="NONE" width="0.1 mm" color="#000000"/>
|
|
20323
21230
|
<hh:bottomBorder type="NONE" width="0.1 mm" color="#000000"/>
|
|
20324
|
-
<hh:diagonal type="NONE" width="0.1 mm" color="#000000"/>
|
|
20325
|
-
<hh:fillInfo/>
|
|
20326
21231
|
</hh:borderFill>
|
|
20327
|
-
<hh:borderFill id="
|
|
21232
|
+
<hh:borderFill id="2" threeD="0" shadow="0" centerLine="NONE" breakCellSeparateLine="0">
|
|
20328
21233
|
<hh:slash type="NONE" Crooked="0" isCounter="0"/>
|
|
20329
21234
|
<hh:backSlash type="NONE" Crooked="0" isCounter="0"/>
|
|
20330
21235
|
<hh:leftBorder type="SOLID" width="0.12 mm" color="#000000"/>
|
|
20331
21236
|
<hh:rightBorder type="SOLID" width="0.12 mm" color="#000000"/>
|
|
20332
21237
|
<hh:topBorder type="SOLID" width="0.12 mm" color="#000000"/>
|
|
20333
21238
|
<hh:bottomBorder type="SOLID" width="0.12 mm" color="#000000"/>
|
|
20334
|
-
<hh:diagonal type="NONE" width="0.1 mm" color="#000000"/>
|
|
20335
|
-
<hh:fillInfo/>
|
|
20336
21239
|
</hh:borderFill>
|
|
20337
21240
|
</hh:borderFills>
|
|
20338
21241
|
${charPropsXml}
|
|
@@ -20379,14 +21282,74 @@ function generateTable(rows, theme) {
|
|
|
20379
21282
|
const tdElements = cells.map((cell, colIdx) => {
|
|
20380
21283
|
const runs = generateRuns(cell, headerCharPr);
|
|
20381
21284
|
const p = `<hp:p paraPrIDRef="0" styleIDRef="0">${runs}</hp:p>`;
|
|
20382
|
-
return `<hp:tc name="" header="${isHeaderRow ? 1 : 0}" hasMargin="0" protect="0" editable="1" dirty="0" borderFillIDRef="
|
|
21285
|
+
return `<hp:tc name="" header="${isHeaderRow ? 1 : 0}" hasMargin="0" protect="0" editable="1" dirty="0" borderFillIDRef="2"><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>`;
|
|
20383
21286
|
}).join("");
|
|
20384
21287
|
return `<hp:tr>${tdElements}</hp:tr>`;
|
|
20385
21288
|
}).join("");
|
|
20386
21289
|
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;
|
|
20387
|
-
const tbl = `<hp:tbl id="${tblId}" zOrder="0" numberingType="TABLE" pageBreak="CELL" repeatHeader="0" rowCnt="${rowCnt}" colCnt="${colCnt}" cellSpacing="0" borderFillIDRef="
|
|
21290
|
+
const tbl = `<hp:tbl id="${tblId}" zOrder="0" numberingType="TABLE" pageBreak="CELL" repeatHeader="0" rowCnt="${rowCnt}" colCnt="${colCnt}" cellSpacing="0" borderFillIDRef="2" noShading="0">${tblInner}</hp:tbl>`;
|
|
20388
21291
|
return `<hp:p paraPrIDRef="0" styleIDRef="0"><hp:run charPrIDRef="0">${tbl}</hp:run></hp:p>`;
|
|
20389
21292
|
}
|
|
21293
|
+
function layoutHtmlRows(rows) {
|
|
21294
|
+
const occupied = /* @__PURE__ */ new Set();
|
|
21295
|
+
const placed = [];
|
|
21296
|
+
let colCnt = 0;
|
|
21297
|
+
for (let r = 0; r < rows.length; r++) {
|
|
21298
|
+
let c = 0;
|
|
21299
|
+
for (const cell of rows[r].cells) {
|
|
21300
|
+
while (occupied.has(`${r},${c}`)) c++;
|
|
21301
|
+
const colSpan = Math.max(1, cell.colSpan);
|
|
21302
|
+
const rowSpan = Math.max(1, cell.rowSpan);
|
|
21303
|
+
placed.push({ r, c, colSpan, rowSpan, inner: cell.inner, isHeader: rows[r].tag === "th" });
|
|
21304
|
+
for (let dr = 0; dr < rowSpan; dr++) {
|
|
21305
|
+
for (let dc = 0; dc < colSpan; dc++) occupied.add(`${r + dr},${c + dc}`);
|
|
21306
|
+
}
|
|
21307
|
+
c += colSpan;
|
|
21308
|
+
colCnt = Math.max(colCnt, c);
|
|
21309
|
+
}
|
|
21310
|
+
}
|
|
21311
|
+
return { placed, rowCnt: rows.length, colCnt };
|
|
21312
|
+
}
|
|
21313
|
+
function unescapeHtml(s) {
|
|
21314
|
+
return s.replace(/</g, "<").replace(/>/g, ">").replace(/"/g, '"').replace(/'/g, "'").replace(/&/g, "&");
|
|
21315
|
+
}
|
|
21316
|
+
function generateHtmlTableXml(rawHtml, theme, totalWidth = 44e3) {
|
|
21317
|
+
const rows = parseHtmlTable(rawHtml);
|
|
21318
|
+
if (!rows || rows.length === 0) return null;
|
|
21319
|
+
const { placed, rowCnt, colCnt } = layoutHtmlRows(rows);
|
|
21320
|
+
if (rowCnt === 0 || colCnt === 0) return null;
|
|
21321
|
+
const colW = Math.floor(totalWidth / colCnt);
|
|
21322
|
+
const cellH = 1500;
|
|
21323
|
+
const tblW = colW * colCnt;
|
|
21324
|
+
const tblId = nextTableId();
|
|
21325
|
+
const useHeaderStyle = theme.tableHeader !== theme.body || theme.tableHeaderBold;
|
|
21326
|
+
const tcXmls = placed.map((cell) => {
|
|
21327
|
+
const headerCharPr = cell.isHeader && useHeaderStyle ? CHAR_TABLE_HEADER : CHAR_NORMAL;
|
|
21328
|
+
const { lines } = htmlCellInnerToLines(cell.inner);
|
|
21329
|
+
const paras = lines.map(
|
|
21330
|
+
(line) => `<hp:p paraPrIDRef="0" styleIDRef="0"><hp:run charPrIDRef="${headerCharPr}"><hp:t>${escapeXml(unescapeHtml(line))}</hp:t></hp:run></hp:p>`
|
|
21331
|
+
);
|
|
21332
|
+
let nestedH = 0;
|
|
21333
|
+
for (const nested of extractTopLevelTables(cell.inner)) {
|
|
21334
|
+
const nestedXml = generateHtmlTableXml(nested, theme, Math.max(colW * cell.colSpan - 1020, 4e3));
|
|
21335
|
+
if (nestedXml) {
|
|
21336
|
+
paras.push(`<hp:p paraPrIDRef="0" styleIDRef="0"><hp:run charPrIDRef="0">${nestedXml}</hp:run></hp:p>`);
|
|
21337
|
+
nestedH += (nested.match(/<tr[\s>]/gi) ?? []).length * cellH + 300;
|
|
21338
|
+
}
|
|
21339
|
+
}
|
|
21340
|
+
if (paras.length === 0) {
|
|
21341
|
+
paras.push(`<hp:p paraPrIDRef="0" styleIDRef="0"><hp:run charPrIDRef="${headerCharPr}"><hp:t></hp:t></hp:run></hp:p>`);
|
|
21342
|
+
}
|
|
21343
|
+
const cellHeight = Math.max(cellH * cell.rowSpan, Math.max(lines.length, 1) * 800 + nestedH);
|
|
21344
|
+
return `<hp:tc name="" header="${cell.isHeader ? 1 : 0}" hasMargin="0" protect="0" editable="1" dirty="0" borderFillIDRef="2"><hp:subList id="" textDirection="HORIZONTAL" lineWrap="BREAK" vertAlign="TOP" linkListIDRef="0" linkListNextIDRef="0" textWidth="0" textHeight="0" hasTextRef="0" hasNumRef="0">${paras.join("")}</hp:subList><hp:cellAddr colAddr="${cell.c}" rowAddr="${cell.r}"/><hp:cellSpan colSpan="${cell.colSpan}" rowSpan="${cell.rowSpan}"/><hp:cellSz width="${colW * cell.colSpan}" height="${cellHeight}"/><hp:cellMargin left="141" right="141" top="141" bottom="141"/></hp:tc>`;
|
|
21345
|
+
});
|
|
21346
|
+
const trXmls = [];
|
|
21347
|
+
for (let r = 0; r < rowCnt; r++) {
|
|
21348
|
+
const rowTcs = tcXmls.filter((_, i) => placed[i].r === r);
|
|
21349
|
+
trXmls.push(`<hp:tr>${rowTcs.join("")}</hp:tr>`);
|
|
21350
|
+
}
|
|
21351
|
+
return `<hp:tbl id="${tblId}" zOrder="0" numberingType="TABLE" pageBreak="CELL" repeatHeader="0" rowCnt="${rowCnt}" colCnt="${colCnt}" cellSpacing="0" borderFillIDRef="2" noShading="0"><hp:sz width="${tblW}" widthRelTo="ABSOLUTE" height="${cellH * rowCnt}" 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"/>` + trXmls.join("") + `</hp:tbl>`;
|
|
21352
|
+
}
|
|
20390
21353
|
function precomputeGongmunList(blocks, gongmun) {
|
|
20391
21354
|
const result = /* @__PURE__ */ new Map();
|
|
20392
21355
|
let i = 0;
|
|
@@ -20410,12 +21373,11 @@ function precomputeGongmunList(blocks, gongmun) {
|
|
|
20410
21373
|
}
|
|
20411
21374
|
return result;
|
|
20412
21375
|
}
|
|
20413
|
-
function blocksToSectionXml(blocks, theme, gongmun) {
|
|
21376
|
+
function blocksToSectionXml(blocks, theme, gongmun, gongmunList = gongmun ? precomputeGongmunList(blocks, gongmun) : null, fit = null) {
|
|
20414
21377
|
const paraXmls = [];
|
|
20415
21378
|
let isFirst = true;
|
|
20416
21379
|
const orderedCounters = {};
|
|
20417
21380
|
let prevWasOrdered = false;
|
|
20418
|
-
const gongmunList = gongmun ? precomputeGongmunList(blocks, gongmun) : null;
|
|
20419
21381
|
for (let blockIdx = 0; blockIdx < blocks.length; blockIdx++) {
|
|
20420
21382
|
const block = blocks[blockIdx];
|
|
20421
21383
|
let xml = "";
|
|
@@ -20437,7 +21399,7 @@ function blocksToSectionXml(blocks, theme, gongmun) {
|
|
|
20437
21399
|
if (ctr) {
|
|
20438
21400
|
xml = generateParagraph(ctr[1].trim(), GONGMUN_CENTER);
|
|
20439
21401
|
} else {
|
|
20440
|
-
xml = generateParagraph(block.text || "");
|
|
21402
|
+
xml = generateParagraph(block.text || "", PARA_NORMAL, CHAR_NORMAL, fit ? variantMapper(fit, blockIdx) : void 0);
|
|
20441
21403
|
}
|
|
20442
21404
|
break;
|
|
20443
21405
|
}
|
|
@@ -20461,7 +21423,7 @@ function blocksToSectionXml(blocks, theme, gongmun) {
|
|
|
20461
21423
|
const content = block.text || "";
|
|
20462
21424
|
const text = marker2 ? `${marker2} ${content}` : content;
|
|
20463
21425
|
const listCharPr = gongmun.numbering === "report" && depth === 0 ? CHAR_BOLD : CHAR_NORMAL;
|
|
20464
|
-
xml = generateParagraph(text, GONGMUN_LIST_BASE + depth, listCharPr);
|
|
21426
|
+
xml = generateParagraph(text, GONGMUN_LIST_BASE + depth, listCharPr, fit ? variantMapper(fit, blockIdx) : void 0);
|
|
20465
21427
|
break;
|
|
20466
21428
|
}
|
|
20467
21429
|
const indent = block.indent || 0;
|
|
@@ -20486,78 +21448,50 @@ function blocksToSectionXml(blocks, theme, gongmun) {
|
|
|
20486
21448
|
}
|
|
20487
21449
|
case "hr":
|
|
20488
21450
|
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>`;
|
|
20489
|
-
break;
|
|
20490
|
-
case "table":
|
|
20491
|
-
if (block.rows) {
|
|
20492
|
-
if (isFirst) {
|
|
20493
|
-
const secRun = `<hp:run charPrIDRef="0">${generateSecPr(gongmun)}<hp:t></hp:t></hp:run>`;
|
|
20494
|
-
paraXmls.push(`<hp:p paraPrIDRef="0" styleIDRef="0">${secRun}</hp:p>`);
|
|
20495
|
-
isFirst = false;
|
|
20496
|
-
}
|
|
20497
|
-
xml = generateTable(block.rows, theme);
|
|
20498
|
-
}
|
|
20499
|
-
break;
|
|
20500
|
-
|
|
20501
|
-
|
|
20502
|
-
|
|
20503
|
-
|
|
20504
|
-
|
|
20505
|
-
|
|
20506
|
-
|
|
20507
|
-
|
|
20508
|
-
|
|
20509
|
-
|
|
20510
|
-
|
|
20511
|
-
|
|
20512
|
-
|
|
20513
|
-
|
|
20514
|
-
return `<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
|
|
20515
|
-
<hs:sec xmlns:hs="${NS_SECTION}" xmlns:hp="${NS_PARA}">
|
|
20516
|
-
${paraXmls.join("\n ")}
|
|
20517
|
-
</hs:sec>`;
|
|
20518
|
-
}
|
|
20519
|
-
|
|
20520
|
-
// src/diff/text-diff.ts
|
|
20521
|
-
function similarity(a, b) {
|
|
20522
|
-
if (a === b) return 1;
|
|
20523
|
-
if (!a || !b) return 0;
|
|
20524
|
-
const maxLen = Math.max(a.length, b.length);
|
|
20525
|
-
if (maxLen === 0) return 1;
|
|
20526
|
-
return 1 - levenshtein(a, b) / maxLen;
|
|
20527
|
-
}
|
|
20528
|
-
function normalizedSimilarity(a, b) {
|
|
20529
|
-
return similarity(normalize(a), normalize(b));
|
|
20530
|
-
}
|
|
20531
|
-
function normalize(s) {
|
|
20532
|
-
return s.replace(/\s+/g, " ").trim();
|
|
20533
|
-
}
|
|
20534
|
-
var MAX_LEVENSHTEIN_LEN = 1e4;
|
|
20535
|
-
function levenshtein(a, b) {
|
|
20536
|
-
if (a.length + b.length > MAX_LEVENSHTEIN_LEN) {
|
|
20537
|
-
const sampleLen = Math.min(500, a.length, b.length);
|
|
20538
|
-
let diffs = 0;
|
|
20539
|
-
for (let i = 0; i < sampleLen; i++) if (a[i] !== b[i]) diffs++;
|
|
20540
|
-
const sampleRate = sampleLen > 0 ? diffs / sampleLen : 1;
|
|
20541
|
-
return Math.abs(a.length - b.length) + Math.round(Math.min(a.length, b.length) * sampleRate);
|
|
20542
|
-
}
|
|
20543
|
-
if (a.length > b.length) [a, b] = [b, a];
|
|
20544
|
-
const m = a.length;
|
|
20545
|
-
const n = b.length;
|
|
20546
|
-
let prev = Array.from({ length: m + 1 }, (_, i) => i);
|
|
20547
|
-
let curr = new Array(m + 1);
|
|
20548
|
-
for (let j = 1; j <= n; j++) {
|
|
20549
|
-
curr[0] = j;
|
|
20550
|
-
for (let i = 1; i <= m; i++) {
|
|
20551
|
-
if (a[i - 1] === b[j - 1]) {
|
|
20552
|
-
curr[i] = prev[i - 1];
|
|
20553
|
-
} else {
|
|
20554
|
-
curr[i] = 1 + Math.min(prev[i - 1], prev[i], curr[i - 1]);
|
|
21451
|
+
break;
|
|
21452
|
+
case "table":
|
|
21453
|
+
if (block.rows) {
|
|
21454
|
+
if (isFirst) {
|
|
21455
|
+
const secRun = `<hp:run charPrIDRef="0">${generateSecPr(gongmun)}<hp:t></hp:t></hp:run>`;
|
|
21456
|
+
paraXmls.push(`<hp:p paraPrIDRef="0" styleIDRef="0">${secRun}</hp:p>`);
|
|
21457
|
+
isFirst = false;
|
|
21458
|
+
}
|
|
21459
|
+
xml = generateTable(block.rows, theme);
|
|
21460
|
+
}
|
|
21461
|
+
break;
|
|
21462
|
+
case "html_table": {
|
|
21463
|
+
const tbl = generateHtmlTableXml(block.text || "", theme);
|
|
21464
|
+
if (tbl) {
|
|
21465
|
+
if (isFirst) {
|
|
21466
|
+
const secRun = `<hp:run charPrIDRef="0">${generateSecPr(gongmun)}<hp:t></hp:t></hp:run>`;
|
|
21467
|
+
paraXmls.push(`<hp:p paraPrIDRef="0" styleIDRef="0">${secRun}</hp:p>`);
|
|
21468
|
+
isFirst = false;
|
|
21469
|
+
}
|
|
21470
|
+
xml = `<hp:p paraPrIDRef="0" styleIDRef="0"><hp:run charPrIDRef="0">${tbl}</hp:run></hp:p>`;
|
|
21471
|
+
} else {
|
|
21472
|
+
const plain = (block.text || "").replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
|
|
21473
|
+
xml = plain ? generateParagraph(plain) : "";
|
|
21474
|
+
}
|
|
21475
|
+
break;
|
|
20555
21476
|
}
|
|
20556
21477
|
}
|
|
20557
|
-
;
|
|
20558
|
-
|
|
21478
|
+
if (!xml) continue;
|
|
21479
|
+
if (isFirst && block.type !== "table") {
|
|
21480
|
+
xml = xml.replace(
|
|
21481
|
+
/<hp:run charPrIDRef="(\d+)">/,
|
|
21482
|
+
`<hp:run charPrIDRef="$1">${generateSecPr(gongmun)}`
|
|
21483
|
+
);
|
|
21484
|
+
isFirst = false;
|
|
21485
|
+
}
|
|
21486
|
+
paraXmls.push(xml);
|
|
20559
21487
|
}
|
|
20560
|
-
|
|
21488
|
+
if (paraXmls.length === 0) {
|
|
21489
|
+
paraXmls.push(`<hp:p paraPrIDRef="0" styleIDRef="0"><hp:run charPrIDRef="0">${generateSecPr(gongmun)}<hp:t></hp:t></hp:run></hp:p>`);
|
|
21490
|
+
}
|
|
21491
|
+
return `<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
|
|
21492
|
+
<hs:sec xmlns:hs="${NS_SECTION}" xmlns:hp="${NS_PARA}">
|
|
21493
|
+
${paraXmls.join("\n ")}
|
|
21494
|
+
</hs:sec>`;
|
|
20561
21495
|
}
|
|
20562
21496
|
|
|
20563
21497
|
// src/diff/compare.ts
|
|
@@ -20675,322 +21609,253 @@ function tableSimilarity(a, b) {
|
|
|
20675
21609
|
}
|
|
20676
21610
|
function diffTableCells(a, b) {
|
|
20677
21611
|
const maxRows = Math.max(a.rows, b.rows);
|
|
20678
|
-
const maxCols = Math.max(a.cols, b.cols);
|
|
20679
|
-
const result = [];
|
|
20680
|
-
for (let r = 0; r < maxRows; r++) {
|
|
20681
|
-
const row = [];
|
|
20682
|
-
for (let c = 0; c < maxCols; c++) {
|
|
20683
|
-
const cellA = r < a.rows && c < a.cols ? a.cells[r][c].text : void 0;
|
|
20684
|
-
const cellB = r < b.rows && c < b.cols ? b.cells[r][c].text : void 0;
|
|
20685
|
-
let type;
|
|
20686
|
-
if (cellA === void 0) type = "added";
|
|
20687
|
-
else if (cellB === void 0) type = "removed";
|
|
20688
|
-
else if (cellA === cellB) type = "unchanged";
|
|
20689
|
-
else type = "modified";
|
|
20690
|
-
row.push({ type, before: cellA, after: cellB });
|
|
20691
|
-
}
|
|
20692
|
-
result.push(row);
|
|
20693
|
-
}
|
|
20694
|
-
return result;
|
|
20695
|
-
}
|
|
20696
|
-
|
|
20697
|
-
// src/roundtrip/patcher.ts
|
|
20698
|
-
import JSZip6 from "jszip";
|
|
20699
|
-
|
|
20700
|
-
// src/roundtrip/markdown-units.ts
|
|
20701
|
-
function splitMarkdownUnits(md2) {
|
|
20702
|
-
const lines = md2.split("\n");
|
|
20703
|
-
const units = [];
|
|
20704
|
-
let i = 0;
|
|
20705
|
-
while (i < lines.length) {
|
|
20706
|
-
const line = lines[i];
|
|
20707
|
-
if (!line.trim()) {
|
|
20708
|
-
i++;
|
|
20709
|
-
continue;
|
|
20710
|
-
}
|
|
20711
|
-
if (line.trim().startsWith("<table>")) {
|
|
20712
|
-
const collected2 = [];
|
|
20713
|
-
let depth = 0;
|
|
20714
|
-
while (i < lines.length) {
|
|
20715
|
-
const l = lines[i];
|
|
20716
|
-
collected2.push(l);
|
|
20717
|
-
depth += (l.match(/<table>/g) || []).length;
|
|
20718
|
-
depth -= (l.match(/<\/table>/g) || []).length;
|
|
20719
|
-
i++;
|
|
20720
|
-
if (depth <= 0) break;
|
|
20721
|
-
}
|
|
20722
|
-
units.push({ kind: "html-table", raw: collected2.join("\n"), lines: collected2 });
|
|
20723
|
-
continue;
|
|
20724
|
-
}
|
|
20725
|
-
if (line.trimStart().startsWith("|")) {
|
|
20726
|
-
const collected2 = [];
|
|
20727
|
-
while (i < lines.length && lines[i].trimStart().startsWith("|")) {
|
|
20728
|
-
collected2.push(lines[i]);
|
|
20729
|
-
i++;
|
|
20730
|
-
}
|
|
20731
|
-
units.push({ kind: "gfm-table", raw: collected2.join("\n"), lines: collected2 });
|
|
20732
|
-
continue;
|
|
20733
|
-
}
|
|
20734
|
-
if (/^-{3,}\s*$/.test(line.trim())) {
|
|
20735
|
-
units.push({ kind: "separator", raw: line.trim(), lines: [line.trim()] });
|
|
20736
|
-
i++;
|
|
20737
|
-
continue;
|
|
20738
|
-
}
|
|
20739
|
-
if (/^!\[image\]\([^)]*\)\s*$/.test(line.trim())) {
|
|
20740
|
-
units.push({ kind: "image", raw: line.trim(), lines: [line.trim()] });
|
|
20741
|
-
i++;
|
|
20742
|
-
continue;
|
|
20743
|
-
}
|
|
20744
|
-
const collected = [];
|
|
20745
|
-
while (i < lines.length && lines[i].trim() && !lines[i].trimStart().startsWith("|") && !lines[i].trim().startsWith("<table>")) {
|
|
20746
|
-
collected.push(lines[i].trim());
|
|
20747
|
-
i++;
|
|
20748
|
-
}
|
|
20749
|
-
units.push({ kind: "text", raw: collected.join("\n"), lines: collected });
|
|
20750
|
-
}
|
|
20751
|
-
return units;
|
|
20752
|
-
}
|
|
20753
|
-
function escapeGfm(text) {
|
|
20754
|
-
return text.replace(/~/g, "\\~");
|
|
20755
|
-
}
|
|
20756
|
-
var HWP_SHAPE_ALT_TEXT_RE = /(?:모서리가 둥근 |둥근 )?(?:사각형|직사각형|정사각형|원|타원|삼각형|이등변 삼각형|직각 삼각형|선|직선|곡선|화살표|굵은 화살표|이중 화살표|오각형|육각형|팔각형|별|[4-8]점별|십자|십자형|구름|구름형|마름모|도넛|평행사변형|사다리꼴|부채꼴|호|반원|물결|번개|하트|빗금|블록 화살표|수식|표|그림|개체|그리기\s?개체|묶음\s?개체|글상자|수식\s?개체|OLE\s?개체)\s?입니다\.?/g;
|
|
20757
|
-
function sanitizeText(text) {
|
|
20758
|
-
let result = mapPuaText(text).replace(/[\u{F0000}-\u{FFFFD}]/gu, "").replace(HWP_SHAPE_ALT_TEXT_RE, "").replace(/ +/g, " ").trim();
|
|
20759
|
-
if (result.length <= 30 && result.includes(" ")) {
|
|
20760
|
-
const tokens = result.split(" ");
|
|
20761
|
-
const koreanSingleCharCount = tokens.filter((t) => t.length === 1 && /[가-ㄱ-ㆎ]/.test(t)).length;
|
|
20762
|
-
if (tokens.length >= 3 && koreanSingleCharCount / tokens.length >= 0.7) {
|
|
20763
|
-
result = tokens.join("");
|
|
20764
|
-
}
|
|
20765
|
-
}
|
|
20766
|
-
return result;
|
|
20767
|
-
}
|
|
20768
|
-
function normForMatch(text) {
|
|
20769
|
-
return sanitizeText(text).replace(/\s+/g, " ").trim();
|
|
20770
|
-
}
|
|
20771
|
-
function unescapeGfm(text) {
|
|
20772
|
-
return text.replace(/\\~/g, "~");
|
|
20773
|
-
}
|
|
20774
|
-
function summarize(text) {
|
|
20775
|
-
const t = text.replace(/\s+/g, " ").trim();
|
|
20776
|
-
return t.length > 80 ? t.slice(0, 77) + "..." : t;
|
|
20777
|
-
}
|
|
20778
|
-
function replicateGfmTable(table) {
|
|
20779
|
-
const { cells, rows: numRows, cols: numCols } = table;
|
|
20780
|
-
if (numRows === 0 || numCols === 0) return null;
|
|
20781
|
-
if (numRows === 1 && numCols === 1) return null;
|
|
20782
|
-
if (numCols === 1) return null;
|
|
20783
|
-
const display = Array.from({ length: numRows }, (_, r) => Array.from({ length: numCols }, (_2, c) => ({ text: "", gridR: r, gridC: c })));
|
|
20784
|
-
const skip = /* @__PURE__ */ new Set();
|
|
20785
|
-
for (let r = 0; r < numRows; r++) {
|
|
20786
|
-
for (let c = 0; c < numCols; c++) {
|
|
20787
|
-
if (skip.has(`${r},${c}`)) continue;
|
|
20788
|
-
const cell = cells[r]?.[c];
|
|
20789
|
-
if (!cell) continue;
|
|
20790
|
-
display[r][c] = {
|
|
20791
|
-
text: escapeGfm(sanitizeText(cell.text)).replace(/\|/g, "\\|").replace(/\n/g, "<br>"),
|
|
20792
|
-
gridR: r,
|
|
20793
|
-
gridC: c
|
|
20794
|
-
};
|
|
20795
|
-
for (let dr = 0; dr < cell.rowSpan; dr++) {
|
|
20796
|
-
for (let dc = 0; dc < cell.colSpan; dc++) {
|
|
20797
|
-
if (dr === 0 && dc === 0) continue;
|
|
20798
|
-
if (r + dr < numRows && c + dc < numCols) skip.add(`${r + dr},${c + dc}`);
|
|
20799
|
-
}
|
|
20800
|
-
}
|
|
20801
|
-
c += cell.colSpan - 1;
|
|
20802
|
-
}
|
|
20803
|
-
}
|
|
20804
|
-
const uniqueRows = [];
|
|
20805
|
-
let pendingLabelRow = null;
|
|
20806
|
-
for (let r = 0; r < display.length; r++) {
|
|
20807
|
-
const row = display[r];
|
|
20808
|
-
if (row.every((cell) => cell.text === "")) continue;
|
|
20809
|
-
const nonEmptyCols = row.filter((cell) => cell.text !== "");
|
|
20810
|
-
const hasSkipInRow = row.some((_, c) => skip.has(`${r},${c}`));
|
|
20811
|
-
if (!hasSkipInRow && nonEmptyCols.length === 1 && row[0].text !== "" && row.slice(1).every((c) => c.text === "")) {
|
|
20812
|
-
if (pendingLabelRow) uniqueRows.push(pendingLabelRow);
|
|
20813
|
-
pendingLabelRow = row;
|
|
20814
|
-
continue;
|
|
20815
|
-
}
|
|
20816
|
-
if (pendingLabelRow) {
|
|
20817
|
-
if (row[0].text === "") row[0] = pendingLabelRow[0];
|
|
20818
|
-
else uniqueRows.push(pendingLabelRow);
|
|
20819
|
-
pendingLabelRow = null;
|
|
20820
|
-
}
|
|
20821
|
-
uniqueRows.push(row);
|
|
20822
|
-
}
|
|
20823
|
-
if (pendingLabelRow) uniqueRows.push(pendingLabelRow);
|
|
20824
|
-
return uniqueRows.length > 0 ? uniqueRows : null;
|
|
20825
|
-
}
|
|
20826
|
-
function parseGfmTable(lines) {
|
|
20827
|
-
const rows = [];
|
|
20828
|
-
for (const line of lines) {
|
|
20829
|
-
const trimmed = line.trim();
|
|
20830
|
-
if (!trimmed.startsWith("|")) continue;
|
|
20831
|
-
const cells = trimmed.split(/(?<!\\)\|/).slice(1, -1).map((c) => c.trim());
|
|
20832
|
-
if (cells.length === 0) continue;
|
|
20833
|
-
if (cells.every((c) => /^:?-{3,}:?$/.test(c))) continue;
|
|
20834
|
-
rows.push(cells);
|
|
20835
|
-
}
|
|
20836
|
-
return rows;
|
|
20837
|
-
}
|
|
20838
|
-
function unescapeGfmCell(text) {
|
|
20839
|
-
return text.replace(/<br\s*\/?>/gi, "\n").replace(/\\\|/g, "|").replace(/\\~/g, "~");
|
|
20840
|
-
}
|
|
20841
|
-
function replicateCellInnerHtml(cell) {
|
|
20842
|
-
if (cell.blocks?.length) {
|
|
20843
|
-
return cell.blocks.map((b) => {
|
|
20844
|
-
if (b.type === "table" && b.table) {
|
|
20845
|
-
const cap = b.table.caption ? sanitizeText(b.table.caption) : "";
|
|
20846
|
-
return (cap ? cap + "<br>" : "") + replicateTableToHtml(b.table);
|
|
20847
|
-
}
|
|
20848
|
-
if (b.type === "image" && b.text) return `<img src="${b.text}" alt="image">`;
|
|
20849
|
-
const t = sanitizeText(b.text ?? "");
|
|
20850
|
-
return t ? t.replace(/\n/g, "<br>") : "";
|
|
20851
|
-
}).filter(Boolean).join("<br>");
|
|
20852
|
-
}
|
|
20853
|
-
return sanitizeText(cell.text).replace(/\n/g, "<br>");
|
|
20854
|
-
}
|
|
20855
|
-
function replicateTableToHtml(table) {
|
|
20856
|
-
const rows = replicateHtmlTable(table);
|
|
20857
|
-
const lines = ["<table>"];
|
|
20858
|
-
for (let r = 0; r < rows.length; r++) {
|
|
20859
|
-
const tag = rows[r].tag;
|
|
20860
|
-
const rowHtml = rows[r].cells.map((cell) => {
|
|
20861
|
-
const attrs = [];
|
|
20862
|
-
if (cell.colSpan > 1) attrs.push(`colspan="${cell.colSpan}"`);
|
|
20863
|
-
if (cell.rowSpan > 1) attrs.push(`rowspan="${cell.rowSpan}"`);
|
|
20864
|
-
const attrStr = attrs.length ? " " + attrs.join(" ") : "";
|
|
20865
|
-
return `<${tag}${attrStr}>${cell.inner}</${tag}>`;
|
|
20866
|
-
});
|
|
20867
|
-
if (rowHtml.length) lines.push(`<tr>${rowHtml.join("")}</tr>`);
|
|
20868
|
-
}
|
|
20869
|
-
lines.push("</table>");
|
|
20870
|
-
return lines.join("\n");
|
|
20871
|
-
}
|
|
20872
|
-
function replicateHtmlTable(table) {
|
|
20873
|
-
const { cells, rows: numRows, cols: numCols } = table;
|
|
20874
|
-
const skip = /* @__PURE__ */ new Set();
|
|
20875
|
-
const result = [];
|
|
20876
|
-
for (let r = 0; r < numRows; r++) {
|
|
20877
|
-
const tag = r === 0 ? "th" : "td";
|
|
20878
|
-
const rowCells = [];
|
|
20879
|
-
for (let c = 0; c < numCols; c++) {
|
|
20880
|
-
if (skip.has(`${r},${c}`)) continue;
|
|
20881
|
-
const cell = cells[r]?.[c];
|
|
20882
|
-
if (!cell) continue;
|
|
20883
|
-
for (let dr = 0; dr < cell.rowSpan; dr++) {
|
|
20884
|
-
for (let dc = 0; dc < cell.colSpan; dc++) {
|
|
20885
|
-
if (dr === 0 && dc === 0) continue;
|
|
20886
|
-
if (r + dr < numRows && c + dc < numCols) skip.add(`${r + dr},${c + dc}`);
|
|
20887
|
-
}
|
|
20888
|
-
}
|
|
20889
|
-
rowCells.push({
|
|
20890
|
-
inner: replicateCellInnerHtml(cell),
|
|
20891
|
-
colSpan: cell.colSpan,
|
|
20892
|
-
rowSpan: cell.rowSpan,
|
|
20893
|
-
gridR: r,
|
|
20894
|
-
gridC: c
|
|
20895
|
-
});
|
|
21612
|
+
const maxCols = Math.max(a.cols, b.cols);
|
|
21613
|
+
const result = [];
|
|
21614
|
+
for (let r = 0; r < maxRows; r++) {
|
|
21615
|
+
const row = [];
|
|
21616
|
+
for (let c = 0; c < maxCols; c++) {
|
|
21617
|
+
const cellA = r < a.rows && c < a.cols ? a.cells[r][c].text : void 0;
|
|
21618
|
+
const cellB = r < b.rows && c < b.cols ? b.cells[r][c].text : void 0;
|
|
21619
|
+
let type;
|
|
21620
|
+
if (cellA === void 0) type = "added";
|
|
21621
|
+
else if (cellB === void 0) type = "removed";
|
|
21622
|
+
else if (cellA === cellB) type = "unchanged";
|
|
21623
|
+
else type = "modified";
|
|
21624
|
+
row.push({ type, before: cellA, after: cellB });
|
|
20896
21625
|
}
|
|
20897
|
-
|
|
21626
|
+
result.push(row);
|
|
20898
21627
|
}
|
|
20899
21628
|
return result;
|
|
20900
21629
|
}
|
|
20901
|
-
|
|
20902
|
-
|
|
20903
|
-
|
|
20904
|
-
|
|
20905
|
-
|
|
20906
|
-
|
|
20907
|
-
|
|
20908
|
-
|
|
20909
|
-
|
|
20910
|
-
|
|
20911
|
-
|
|
20912
|
-
|
|
20913
|
-
|
|
20914
|
-
|
|
20915
|
-
|
|
20916
|
-
|
|
21630
|
+
|
|
21631
|
+
// src/roundtrip/patcher.ts
|
|
21632
|
+
import JSZip6 from "jszip";
|
|
21633
|
+
|
|
21634
|
+
// src/roundtrip/table-rows.ts
|
|
21635
|
+
var ROW_OBJECT_RE = /<(?:[A-Za-z0-9_]+:)?(?:tbl|pic|equation|ole|container|shape|drawingObject|drawText|video|chart|fieldBegin|fieldEnd|ctrl)\b/;
|
|
21636
|
+
var TAG_AT_RE = /<[A-Za-z0-9_:]+(?:"[^"]*"|'[^']*'|[^>"'])*>/y;
|
|
21637
|
+
function patchTableRows(input) {
|
|
21638
|
+
const { table, scanTable, ctx, skip, origKeys, editedKeys } = input;
|
|
21639
|
+
const xml = ctx.scans[scanTable.sectionIndex]?.xml;
|
|
21640
|
+
if (!xml) return skip("\uC139\uC158 XML \uB9E4\uD551 \uC2E4\uD328");
|
|
21641
|
+
const numRows = table.rows;
|
|
21642
|
+
if (origKeys.length !== numRows) return skip("\uD45C \uD589 \uC88C\uD45C \uBD88\uC77C\uCE58 \u2014 \uD589 \uCD94\uAC00/\uC0AD\uC81C \uBBF8\uC9C0\uC6D0");
|
|
21643
|
+
if (scanTable.rows.length !== numRows || scanTable.rowRanges.length !== numRows) {
|
|
21644
|
+
return skip("\uD45C \uD589 \uAD6C\uC870 \uC88C\uD45C \uBD88\uC77C\uCE58 (\uBE48 \uD589/\uBCD1\uD569 \uC18C\uC2E4) \u2014 \uD589 \uCD94\uAC00/\uC0AD\uC81C \uBBF8\uC9C0\uC6D0");
|
|
21645
|
+
}
|
|
21646
|
+
for (let r = 0; r < numRows; r++) {
|
|
21647
|
+
if (scanTable.rows[r].some((c) => c.rowAddr !== r)) {
|
|
21648
|
+
return skip("\uD589 \uC8FC\uC18C \uBE44\uC5F0\uC18D \u2014 \uD589 \uCD94\uAC00/\uC0AD\uC81C \uBBF8\uC9C0\uC6D0");
|
|
20917
21649
|
}
|
|
20918
|
-
|
|
20919
|
-
|
|
20920
|
-
|
|
20921
|
-
|
|
20922
|
-
|
|
20923
|
-
|
|
21650
|
+
}
|
|
21651
|
+
const allCells = scanTable.rows.flat();
|
|
21652
|
+
const explicitAddr = (c) => !!c.addrTagRange && /\browAddr\s*=\s*"/.test(xml.slice(c.addrTagRange.start, c.addrTagRange.end));
|
|
21653
|
+
const explicitCount = allCells.filter(explicitAddr).length;
|
|
21654
|
+
if (explicitCount !== 0 && explicitCount !== allCells.length) {
|
|
21655
|
+
return skip("\uC140 \uC8FC\uC18C(cellAddr) \uD45C\uAE30 \uD63C\uC7AC \u2014 \uD589 \uCD94\uAC00/\uC0AD\uC81C \uBBF8\uC9C0\uC6D0");
|
|
21656
|
+
}
|
|
21657
|
+
const hasExplicitAddr = explicitCount > 0;
|
|
21658
|
+
const pairs = alignUnits(origKeys, editedKeys);
|
|
21659
|
+
const seq = [];
|
|
21660
|
+
let lastOrig = -1;
|
|
21661
|
+
for (const [oi, ei] of pairs) {
|
|
21662
|
+
if (oi !== null && ei !== null) {
|
|
21663
|
+
seq.push({ kind: "keep", oi, ei });
|
|
21664
|
+
lastOrig = oi;
|
|
21665
|
+
} else if (oi !== null) {
|
|
21666
|
+
seq.push({ kind: "del", oi });
|
|
21667
|
+
lastOrig = oi;
|
|
21668
|
+
} else if (ei !== null) seq.push({ kind: "ins", ei, insertAt: lastOrig + 1 });
|
|
21669
|
+
}
|
|
21670
|
+
const dels = seq.filter((e) => e.kind === "del");
|
|
21671
|
+
const inss = seq.filter((e) => e.kind === "ins");
|
|
21672
|
+
const keeps = seq.filter((e) => e.kind === "keep");
|
|
21673
|
+
if (dels.length === 0 && inss.length === 0) {
|
|
21674
|
+
return skip("\uD45C \uD589 \uC815\uB82C \uC2E4\uD328 \u2014 \uD589 \uCD94\uAC00/\uC0AD\uC81C \uBBF8\uC9C0\uC6D0");
|
|
21675
|
+
}
|
|
21676
|
+
if (keeps.length + inss.length === 0) return skip("\uBAA8\uB4E0 \uD589 \uC0AD\uC81C\uB294 \uBBF8\uC9C0\uC6D0");
|
|
21677
|
+
if (keeps.length === 0) return skip("\uD589 \uC804\uBA74 \uAD50\uCCB4 \u2014 \uC11C\uC2DD \uAE30\uC900 \uD589\uC774 \uC5C6\uC5B4 \uBBF8\uC9C0\uC6D0");
|
|
21678
|
+
const spans = allCells.filter((c) => (c.rowSpan ?? 1) > 1);
|
|
21679
|
+
for (const d of dels) {
|
|
21680
|
+
if (spans.some((s) => s.rowAddr <= d.oi && s.rowAddr + s.rowSpan > d.oi)) {
|
|
21681
|
+
return skip(`\uD45C ${d.oi + 1}\uD589 \uC0AD\uC81C\uAC00 \uC138\uB85C \uBCD1\uD569\uACFC \uACB9\uCE68 \u2014 \uBBF8\uC9C0\uC6D0`);
|
|
21682
|
+
}
|
|
21683
|
+
}
|
|
21684
|
+
for (const ins of inss) {
|
|
21685
|
+
const p = ins.insertAt;
|
|
21686
|
+
if (spans.some((s) => s.rowAddr < p && s.rowAddr + s.rowSpan > p)) {
|
|
21687
|
+
return skip("\uD589 \uC0BD\uC785 \uC704\uCE58\uAC00 \uC138\uB85C \uBCD1\uD569 \uB0B4\uBD80 \u2014 \uBBF8\uC9C0\uC6D0");
|
|
21688
|
+
}
|
|
21689
|
+
}
|
|
21690
|
+
for (const d of dels) {
|
|
21691
|
+
const rr = scanTable.rowRanges[d.oi];
|
|
21692
|
+
if (ROW_OBJECT_RE.test(xml.slice(rr.start, rr.end))) {
|
|
21693
|
+
return skip(`\uD45C ${d.oi + 1}\uD589\uC5D0 \uAC1C\uCCB4(\uC911\uCCA9\uD45C/\uC774\uBBF8\uC9C0/\uD544\uB4DC) \uD3EC\uD568 \u2014 \uD589 \uC0AD\uC81C \uBBF8\uC9C0\uC6D0`);
|
|
21694
|
+
}
|
|
21695
|
+
}
|
|
21696
|
+
const keptSet = new Set(keeps.map((k) => k.oi));
|
|
21697
|
+
const insertPlans = [];
|
|
21698
|
+
for (const ins of inss) {
|
|
21699
|
+
const p = ins.insertAt;
|
|
21700
|
+
const kept = [...keptSet].sort((a, b) => a - b);
|
|
21701
|
+
let template = -1;
|
|
21702
|
+
for (const k of kept) {
|
|
21703
|
+
if (k < p) template = k;
|
|
21704
|
+
}
|
|
21705
|
+
if (template <= 0 && p >= 1) {
|
|
21706
|
+
const following = kept.find((k) => k >= 1);
|
|
21707
|
+
if (following !== void 0) template = following;
|
|
21708
|
+
}
|
|
21709
|
+
if (template < 0) template = kept[0];
|
|
21710
|
+
const cells = input.editedCells(ins.ei);
|
|
21711
|
+
if (!cells) return skip("\uC0BD\uC785 \uD589\uC5D0 \uC774\uBBF8\uC9C0/\uC911\uCCA9\uD45C \uD3EC\uD568 \u2014 \uD589 \uCD94\uAC00 \uBBF8\uC9C0\uC6D0");
|
|
21712
|
+
for (const cell of cells) {
|
|
21713
|
+
const unstable = cell.lines.find((l) => sanitizeText(l) !== l);
|
|
21714
|
+
if (unstable !== void 0) return skip("\uC0BD\uC785 \uD589\uC5D0 \uACF5\uBC31 \uC815\uADDC\uD654 \uBD88\uC548\uC815 \uD14D\uC2A4\uD2B8 \u2014 \uBBF8\uC9C0\uC6D0");
|
|
21715
|
+
}
|
|
21716
|
+
const tmplCells = scanTable.rows[template];
|
|
21717
|
+
const rr = scanTable.rowRanges[template];
|
|
21718
|
+
if (ROW_OBJECT_RE.test(xml.slice(rr.start, rr.end))) {
|
|
21719
|
+
return skip("\uC11C\uC2DD \uAE30\uC900 \uD589\uC5D0 \uAC1C\uCCB4(\uC911\uCCA9\uD45C/\uC774\uBBF8\uC9C0/\uD544\uB4DC) \uD3EC\uD568 \u2014 \uD589 \uCD94\uAC00 \uBBF8\uC9C0\uC6D0");
|
|
21720
|
+
}
|
|
21721
|
+
if (tmplCells.some((c) => c.rowSpan > 1)) return skip("\uC11C\uC2DD \uAE30\uC900 \uD589\uC5D0 \uC138\uB85C \uBCD1\uD569 \u2014 \uD589 \uCD94\uAC00 \uBBF8\uC9C0\uC6D0");
|
|
21722
|
+
const tmplWidth = tmplCells.reduce((s, c) => s + c.colSpan, 0);
|
|
21723
|
+
if (tmplWidth !== table.cols) return skip("\uC11C\uC2DD \uAE30\uC900 \uD589\uC774 \uACA9\uC790 \uC804\uCCB4\uB97C \uB36E\uC9C0 \uC54A\uC74C \u2014 \uD589 \uCD94\uAC00 \uBBF8\uC9C0\uC6D0");
|
|
21724
|
+
if (cells.length !== tmplCells.length) {
|
|
21725
|
+
return skip(`\uC0BD\uC785 \uD589 \uC140 \uC218(${cells.length}) \u2260 \uAE30\uC900 \uD589 \uC140 \uC218(${tmplCells.length}) \u2014 \uBBF8\uC9C0\uC6D0`);
|
|
21726
|
+
}
|
|
21727
|
+
for (let i = 0; i < cells.length; i++) {
|
|
21728
|
+
if (cells[i].rowSpan !== 1 || cells[i].colSpan !== tmplCells[i].colSpan) {
|
|
21729
|
+
return skip("\uC0BD\uC785 \uD589 \uBCD1\uD569 \uAD6C\uC870\uAC00 \uAE30\uC900 \uD589\uACFC \uB2E4\uB984 \u2014 \uBBF8\uC9C0\uC6D0");
|
|
20924
21730
|
}
|
|
20925
|
-
}
|
|
20926
|
-
|
|
20927
|
-
|
|
20928
|
-
|
|
20929
|
-
|
|
20930
|
-
|
|
20931
|
-
|
|
20932
|
-
|
|
20933
|
-
|
|
20934
|
-
|
|
21731
|
+
}
|
|
21732
|
+
insertPlans.push({ entry: ins, template, cells });
|
|
21733
|
+
}
|
|
21734
|
+
const net = inss.length - dels.length;
|
|
21735
|
+
TAG_AT_RE.lastIndex = scanTable.start;
|
|
21736
|
+
const tblOpen = TAG_AT_RE.exec(xml);
|
|
21737
|
+
if (!tblOpen || tblOpen.index !== scanTable.start) return skip("\uD45C \uC5EC\uB294 \uD0DC\uADF8 \uD574\uC11D \uC2E4\uD328");
|
|
21738
|
+
const rowCntM = tblOpen[0].match(/\browCnt\s*=\s*"(\d+)"/);
|
|
21739
|
+
if (!rowCntM || rowCntM.index === void 0) return skip("\uD45C rowCnt \uC18D\uC131 \uC5C6\uC74C \u2014 \uD589 \uCD94\uAC00/\uC0AD\uC81C \uBBF8\uC9C0\uC6D0");
|
|
21740
|
+
if (parseInt(rowCntM[1], 10) !== numRows) return skip("rowCnt\uC640 \uC2E4\uC81C \uD589 \uC218 \uBD88\uC77C\uCE58 \u2014 \uD589 \uCD94\uAC00/\uC0AD\uC81C \uBBF8\uC9C0\uC6D0");
|
|
21741
|
+
const finalIndex = /* @__PURE__ */ new Map();
|
|
21742
|
+
{
|
|
21743
|
+
let fi = 0;
|
|
21744
|
+
for (const e of seq) {
|
|
21745
|
+
if (e.kind !== "del") finalIndex.set(e, fi++);
|
|
21746
|
+
}
|
|
21747
|
+
}
|
|
21748
|
+
const splices = [];
|
|
21749
|
+
let applied = 0;
|
|
21750
|
+
for (const d of dels) {
|
|
21751
|
+
const rr = scanTable.rowRanges[d.oi];
|
|
21752
|
+
splices.push({ start: rr.start, end: rr.end, replacement: "" });
|
|
21753
|
+
applied++;
|
|
21754
|
+
}
|
|
21755
|
+
const fragmentsByAnchor = /* @__PURE__ */ new Map();
|
|
21756
|
+
let heightDelta = 0;
|
|
21757
|
+
for (const plan of insertPlans) {
|
|
21758
|
+
const finalRow = finalIndex.get(plan.entry);
|
|
21759
|
+
const fragment = buildRowFragment(xml, scanTable, plan.template, plan.cells, finalRow, hasExplicitAddr, ctx);
|
|
21760
|
+
if (fragment === null) return skip("\uD589 \uBCF5\uC81C \uC2E4\uD328 (\uC140 \uBB38\uB2E8 \uAD6C\uC870 \uBBF8\uC9C0\uC6D0)");
|
|
21761
|
+
const p = plan.entry.insertAt;
|
|
21762
|
+
const anchor = p === 0 ? scanTable.rowRanges[0].start : scanTable.rowRanges[p - 1].end;
|
|
21763
|
+
let list = fragmentsByAnchor.get(anchor);
|
|
21764
|
+
if (!list) fragmentsByAnchor.set(anchor, list = []);
|
|
21765
|
+
list.push(fragment);
|
|
21766
|
+
heightDelta += rowHeightOf(fragment);
|
|
21767
|
+
applied++;
|
|
21768
|
+
}
|
|
21769
|
+
for (const [anchor, fragments] of fragmentsByAnchor) {
|
|
21770
|
+
splices.push({ start: anchor, end: anchor, replacement: fragments.join("") });
|
|
21771
|
+
}
|
|
21772
|
+
for (const d of dels) {
|
|
21773
|
+
const rr = scanTable.rowRanges[d.oi];
|
|
21774
|
+
heightDelta -= rowHeightOf(xml.slice(rr.start, rr.end));
|
|
21775
|
+
}
|
|
21776
|
+
if (hasExplicitAddr) {
|
|
21777
|
+
for (const k of keeps) {
|
|
21778
|
+
const fi = finalIndex.get(k);
|
|
21779
|
+
if (fi === k.oi) continue;
|
|
21780
|
+
for (const cell of scanTable.rows[k.oi]) {
|
|
21781
|
+
const sp = rowAddrRewrite(xml, cell, fi);
|
|
21782
|
+
if (sp) splices.push(sp);
|
|
20935
21783
|
}
|
|
20936
21784
|
}
|
|
20937
21785
|
}
|
|
20938
|
-
|
|
20939
|
-
|
|
20940
|
-
}
|
|
20941
|
-
function htmlCellInnerToLines(inner) {
|
|
20942
|
-
let hadNonText = false;
|
|
20943
|
-
let work = inner;
|
|
20944
|
-
if (/<table[\s>]/i.test(work)) {
|
|
20945
|
-
hadNonText = true;
|
|
20946
|
-
work = removeNestedTables(work);
|
|
21786
|
+
{
|
|
21787
|
+
const valStart = scanTable.start + rowCntM.index + rowCntM[0].indexOf('"') + 1;
|
|
21788
|
+
splices.push({ start: valStart, end: valStart + rowCntM[1].length, replacement: String(numRows + net) });
|
|
20947
21789
|
}
|
|
20948
|
-
if (
|
|
20949
|
-
|
|
20950
|
-
|
|
21790
|
+
if (heightDelta !== 0) {
|
|
21791
|
+
const sp = tableSzHeightSplice(xml, scanTable, tblOpen[0].length, heightDelta);
|
|
21792
|
+
if (sp) splices.push(sp);
|
|
20951
21793
|
}
|
|
20952
|
-
|
|
20953
|
-
|
|
21794
|
+
ctx.sectionSplices[scanTable.sectionIndex].push(...splices);
|
|
21795
|
+
for (const k of keeps) {
|
|
21796
|
+
if (origKeys[k.oi] !== editedKeys[k.ei]) applied += input.patchMatched(k.oi, k.ei);
|
|
21797
|
+
}
|
|
21798
|
+
return applied;
|
|
20954
21799
|
}
|
|
20955
|
-
|
|
20956
|
-
|
|
20957
|
-
|
|
20958
|
-
|
|
20959
|
-
const
|
|
20960
|
-
|
|
20961
|
-
|
|
20962
|
-
|
|
20963
|
-
|
|
20964
|
-
|
|
20965
|
-
|
|
20966
|
-
|
|
20967
|
-
|
|
20968
|
-
|
|
20969
|
-
|
|
20970
|
-
}
|
|
20971
|
-
|
|
21800
|
+
var FRAG_OPEN = "<hp:tbl>";
|
|
21801
|
+
var FRAG_CLOSE = "</hp:tbl>";
|
|
21802
|
+
function buildRowFragment(xml, scanTable, template, cells, finalRow, hasExplicitAddr, ctx) {
|
|
21803
|
+
const rr = scanTable.rowRanges[template];
|
|
21804
|
+
const wrapped = FRAG_OPEN + xml.slice(rr.start, rr.end) + FRAG_CLOSE;
|
|
21805
|
+
const scan = scanSectionXml(wrapped, 0);
|
|
21806
|
+
const row = scan.tables[0]?.rows[0];
|
|
21807
|
+
if (!row || row.length !== cells.length) return null;
|
|
21808
|
+
const splices = allLinesegRemovalSplices(wrapped);
|
|
21809
|
+
for (let i = 0; i < cells.length; i++) {
|
|
21810
|
+
const paras = row[i].paragraphs;
|
|
21811
|
+
let lines = cells[i].lines;
|
|
21812
|
+
if (lines.length > 0 && paras.length === 0) return null;
|
|
21813
|
+
if (lines.length > paras.length) {
|
|
21814
|
+
lines = [...lines.slice(0, paras.length - 1), lines.slice(paras.length - 1).join(" ")];
|
|
21815
|
+
ctx.skipped.push({ reason: "\uC0BD\uC785 \uD589 \uC140\uC758 \uC904 \uC218\uAC00 \uBB38\uB2E8 \uC218 \uCD08\uACFC \u2014 \uB9C8\uC9C0\uB9C9 \uBB38\uB2E8\uC5D0 \uBCD1\uD569 \uC801\uC6A9", after: summarize(cells[i].lines.join(" ")), partial: true });
|
|
21816
|
+
}
|
|
21817
|
+
for (let p = 0; p < paras.length; p++) {
|
|
21818
|
+
const sp = buildParagraphSplices(paras[p], lines[p] ?? "", wrapped);
|
|
21819
|
+
if (sp === null) return null;
|
|
21820
|
+
splices.push(...sp);
|
|
21821
|
+
}
|
|
21822
|
+
if (hasExplicitAddr) {
|
|
21823
|
+
const sp = rowAddrRewrite(wrapped, row[i], finalRow);
|
|
21824
|
+
if (sp) splices.push(sp);
|
|
20972
21825
|
}
|
|
20973
21826
|
}
|
|
20974
|
-
|
|
21827
|
+
const patched = applySplices(wrapped, splices);
|
|
21828
|
+
return patched.slice(FRAG_OPEN.length, patched.length - FRAG_CLOSE.length);
|
|
20975
21829
|
}
|
|
20976
|
-
function
|
|
20977
|
-
|
|
20978
|
-
|
|
20979
|
-
const
|
|
20980
|
-
|
|
20981
|
-
|
|
20982
|
-
|
|
20983
|
-
|
|
20984
|
-
|
|
20985
|
-
|
|
20986
|
-
|
|
20987
|
-
|
|
20988
|
-
|
|
20989
|
-
|
|
20990
|
-
}
|
|
21830
|
+
function rowAddrRewrite(xml, cell, newRow) {
|
|
21831
|
+
if (!cell.addrTagRange) return null;
|
|
21832
|
+
const tag = xml.slice(cell.addrTagRange.start, cell.addrTagRange.end);
|
|
21833
|
+
const m = tag.match(/\browAddr\s*=\s*"(\d+)"/);
|
|
21834
|
+
if (!m || m.index === void 0) return null;
|
|
21835
|
+
if (parseInt(m[1], 10) === newRow) return null;
|
|
21836
|
+
const valStart = cell.addrTagRange.start + m.index + m[0].indexOf('"') + 1;
|
|
21837
|
+
return { start: valStart, end: valStart + m[1].length, replacement: String(newRow) };
|
|
21838
|
+
}
|
|
21839
|
+
function rowHeightOf(fragment) {
|
|
21840
|
+
let max = 0;
|
|
21841
|
+
for (const m of fragment.matchAll(/<(?:[A-Za-z0-9_]+:)?cellSz\b(?:"[^"]*"|'[^']*'|[^>"'])*>/g)) {
|
|
21842
|
+
const h = m[0].match(/\bheight\s*=\s*"(\d+)"/);
|
|
21843
|
+
if (h) max = Math.max(max, parseInt(h[1], 10));
|
|
20991
21844
|
}
|
|
20992
|
-
|
|
20993
|
-
|
|
21845
|
+
return max;
|
|
21846
|
+
}
|
|
21847
|
+
function tableSzHeightSplice(xml, scanTable, tblOpenLen, delta) {
|
|
21848
|
+
const from = scanTable.start + tblOpenLen;
|
|
21849
|
+
const to = scanTable.rowRanges[0]?.start ?? from;
|
|
21850
|
+
const slice = xml.slice(from, to);
|
|
21851
|
+
const szM = slice.match(/<(?:[A-Za-z0-9_]+:)?sz\b(?:"[^"]*"|'[^']*'|[^>"'])*>/);
|
|
21852
|
+
if (!szM || szM.index === void 0) return null;
|
|
21853
|
+
const hM = szM[0].match(/\bheight\s*=\s*"(\d+)"/);
|
|
21854
|
+
if (!hM || hM.index === void 0) return null;
|
|
21855
|
+
const oldH = parseInt(hM[1], 10);
|
|
21856
|
+
const newH = Math.max(0, oldH + delta);
|
|
21857
|
+
const valStart = from + szM.index + hM.index + hM[0].indexOf('"') + 1;
|
|
21858
|
+
return { start: valStart, end: valStart + hM[1].length, replacement: String(newH) };
|
|
20994
21859
|
}
|
|
20995
21860
|
|
|
20996
21861
|
// src/roundtrip/table-patch.ts
|
|
@@ -21002,37 +21867,89 @@ function patchGfmTable(table, scanTable, orig, edited, ctx, skip) {
|
|
|
21002
21867
|
if (replica.length !== origRows.length || replica.some((row, r) => row.length !== origRows[r].length || row.some((c, j) => c.text !== origRows[r][j]))) {
|
|
21003
21868
|
return skip("\uD45C \uC88C\uD45C \uC7AC\uD604 \uBD88\uC77C\uCE58 \u2014 \uB9E4\uD551 \uC2E0\uB8B0 \uBD88\uAC00");
|
|
21004
21869
|
}
|
|
21005
|
-
if (editedRows.length !== origRows.length)
|
|
21870
|
+
if (editedRows.length !== origRows.length) {
|
|
21871
|
+
return patchGfmTableRows(table, scanTable, origRows, editedRows, replica, ctx, skip);
|
|
21872
|
+
}
|
|
21006
21873
|
let applied = 0;
|
|
21007
21874
|
for (let r = 0; r < origRows.length; r++) {
|
|
21008
|
-
|
|
21009
|
-
|
|
21875
|
+
applied += patchGfmRowPair(table, scanTable, origRows, editedRows, replica, r, r, ctx, skip);
|
|
21876
|
+
}
|
|
21877
|
+
return applied;
|
|
21878
|
+
}
|
|
21879
|
+
function patchGfmRowPair(table, scanTable, origRows, editedRows, replica, r, er, ctx, skip) {
|
|
21880
|
+
if (editedRows[er].length !== origRows[r].length) {
|
|
21881
|
+
skip(`\uD45C ${r + 1}\uD589 \uC5F4 \uC218 \uBCC0\uACBD\uC740 \uBBF8\uC9C0\uC6D0`);
|
|
21882
|
+
return 0;
|
|
21883
|
+
}
|
|
21884
|
+
let applied = 0;
|
|
21885
|
+
for (let c = 0; c < origRows[r].length; c++) {
|
|
21886
|
+
if (origRows[r][c] === editedRows[er][c]) continue;
|
|
21887
|
+
const { gridR, gridC } = replica[r][c];
|
|
21888
|
+
const origTokens = extractCellTokens(origRows[r][c]);
|
|
21889
|
+
const editedTokens = extractCellTokens(editedRows[er][c]);
|
|
21890
|
+
if (origTokens !== editedTokens) {
|
|
21891
|
+
skip("\uC140 \uB0B4 \uC774\uBBF8\uC9C0 \uBCC0\uACBD\uC740 \uBBF8\uC9C0\uC6D0");
|
|
21010
21892
|
continue;
|
|
21011
21893
|
}
|
|
21012
|
-
|
|
21013
|
-
|
|
21014
|
-
|
|
21015
|
-
|
|
21016
|
-
|
|
21017
|
-
|
|
21018
|
-
|
|
21019
|
-
|
|
21020
|
-
|
|
21021
|
-
|
|
21022
|
-
const origLines = unescapeGfmCell(stripCellTokens(origRows[r][c])).split("\n").map((s) => s.trim()).filter(Boolean);
|
|
21023
|
-
const n = applyCellEdit(table, scanTable, gridR, gridC, newLines, ctx, origRows[r][c], editedRows[r][c], origLines.length);
|
|
21024
|
-
if (n > 0 && origTokens) {
|
|
21025
|
-
ctx.skipped.push({
|
|
21026
|
-
reason: "\uC140 \uB0B4 \uC774\uBBF8\uC9C0\xB7\uD14D\uC2A4\uD2B8 \uD63C\uC7AC \u2014 \uD14D\uC2A4\uD2B8\uB9CC \uC801\uC6A9 (\uC774\uBBF8\uC9C0 \uC778\uC811 \uBC30\uCE58\uB294 <br> \uBD84\uB9AC\uB85C \uC7AC\uD604\uB428)",
|
|
21027
|
-
before: summarize(origRows[r][c]),
|
|
21028
|
-
after: summarize(editedRows[r][c])
|
|
21029
|
-
});
|
|
21030
|
-
}
|
|
21031
|
-
applied += n;
|
|
21894
|
+
const newLines = unescapeGfmCell(stripCellTokens(editedRows[er][c])).split("\n").map((s) => s.trim()).filter(Boolean);
|
|
21895
|
+
const origLines = unescapeGfmCell(stripCellTokens(origRows[r][c])).split("\n").map((s) => s.trim()).filter(Boolean);
|
|
21896
|
+
const n = applyCellEdit(table, scanTable, gridR, gridC, newLines, ctx, origRows[r][c], editedRows[er][c], origLines.length);
|
|
21897
|
+
if (n > 0 && origTokens) {
|
|
21898
|
+
ctx.skipped.push({
|
|
21899
|
+
reason: "\uC140 \uB0B4 \uC774\uBBF8\uC9C0\xB7\uD14D\uC2A4\uD2B8 \uD63C\uC7AC \u2014 \uD14D\uC2A4\uD2B8\uB9CC \uC801\uC6A9 (\uC774\uBBF8\uC9C0 \uC778\uC811 \uBC30\uCE58\uB294 <br> \uBD84\uB9AC\uB85C \uC7AC\uD604\uB428)",
|
|
21900
|
+
before: summarize(origRows[r][c]),
|
|
21901
|
+
after: summarize(editedRows[er][c]),
|
|
21902
|
+
partial: true
|
|
21903
|
+
});
|
|
21032
21904
|
}
|
|
21905
|
+
applied += n;
|
|
21033
21906
|
}
|
|
21034
21907
|
return applied;
|
|
21035
21908
|
}
|
|
21909
|
+
function patchGfmTableRows(table, scanTable, origRows, editedRows, replica, ctx, skip) {
|
|
21910
|
+
if (replica.length !== table.rows || replica.some((row, r) => row.some((c) => c.gridR !== r))) {
|
|
21911
|
+
return skip("\uD45C \uB80C\uB354 \uD589\uACFC \uACA9\uC790 \uD589 \uBD88\uC77C\uCE58 (\uBE48 \uD589/\uBCD1\uD569) \u2014 \uD589 \uCD94\uAC00/\uC0AD\uC81C \uBBF8\uC9C0\uC6D0");
|
|
21912
|
+
}
|
|
21913
|
+
if (table.cells.some((row) => row.some((c) => c && (c.colSpan > 1 || c.rowSpan > 1)))) {
|
|
21914
|
+
return skip("\uBCD1\uD569 \uC140 \uD45C \u2014 GFM \uD589 \uCD94\uAC00/\uC0AD\uC81C \uBBF8\uC9C0\uC6D0");
|
|
21915
|
+
}
|
|
21916
|
+
if (!gfmRenderStable(editedRows, table.cols)) {
|
|
21917
|
+
return skip("\uD589 \uBCC0\uACBD \uACB0\uACFC\uAC00 \uD45C \uB80C\uB354\uC5D0\uC11C \uBCC0\uD615\uB428 (\uBE48 \uD589/\uCCAB \uC5F4 \uC804\uD30C/\uC5F4 \uC218 \uBD88\uC77C\uCE58) \u2014 \uBBF8\uC9C0\uC6D0");
|
|
21918
|
+
}
|
|
21919
|
+
const key = (row) => row.join("\0");
|
|
21920
|
+
return patchTableRows({
|
|
21921
|
+
table,
|
|
21922
|
+
scanTable,
|
|
21923
|
+
ctx,
|
|
21924
|
+
skip,
|
|
21925
|
+
origKeys: origRows.map(key),
|
|
21926
|
+
editedKeys: editedRows.map(key),
|
|
21927
|
+
editedCells: (ei) => {
|
|
21928
|
+
const cells = [];
|
|
21929
|
+
for (const cellMd of editedRows[ei]) {
|
|
21930
|
+
if (extractCellTokens(cellMd)) return null;
|
|
21931
|
+
const lines = unescapeGfmCell(cellMd).split("\n").map((s) => s.trim()).filter(Boolean);
|
|
21932
|
+
cells.push({ lines, colSpan: 1, rowSpan: 1 });
|
|
21933
|
+
}
|
|
21934
|
+
return cells;
|
|
21935
|
+
},
|
|
21936
|
+
patchMatched: (oi, ei) => patchGfmRowPair(table, scanTable, origRows, editedRows, replica, oi, ei, ctx, skip)
|
|
21937
|
+
});
|
|
21938
|
+
}
|
|
21939
|
+
function gfmRenderStable(editedRows, cols) {
|
|
21940
|
+
const sim = {
|
|
21941
|
+
rows: editedRows.length,
|
|
21942
|
+
cols,
|
|
21943
|
+
hasHeader: editedRows.length > 1,
|
|
21944
|
+
cells: editedRows.map((row) => {
|
|
21945
|
+
const padded = row.length < cols ? [...row, ...Array(cols - row.length).fill("")] : row;
|
|
21946
|
+
return padded.map((md2) => ({ text: unescapeGfmCell(md2), colSpan: 1, rowSpan: 1 }));
|
|
21947
|
+
})
|
|
21948
|
+
};
|
|
21949
|
+
const replica = replicateGfmTable(sim);
|
|
21950
|
+
if (!replica || replica.length !== editedRows.length) return false;
|
|
21951
|
+
return replica.every((row, r) => row.length === editedRows[r].length && row.every((c, j) => c.text === editedRows[r][j]));
|
|
21952
|
+
}
|
|
21036
21953
|
function patchHtmlTable(table, scanTable, orig, edited, ctx, skip) {
|
|
21037
21954
|
return patchHtmlTableRaw(table, scanTable, orig.raw, edited.raw, ctx, skip, 0);
|
|
21038
21955
|
}
|
|
@@ -21046,46 +21963,77 @@ function patchHtmlTableRaw(table, scanTable, origRaw, editedRaw, ctx, skip, dept
|
|
|
21046
21963
|
}
|
|
21047
21964
|
const editedRows = parseHtmlTable(editedRaw);
|
|
21048
21965
|
if (!editedRows) return skip("\uD3B8\uC9D1\uB41C HTML \uD45C \uD30C\uC2F1 \uC2E4\uD328");
|
|
21049
|
-
if (editedRows.length !== replica.length)
|
|
21966
|
+
if (editedRows.length !== replica.length) {
|
|
21967
|
+
return patchHtmlTableRows(table, scanTable, replica, editedRows, ctx, skip, depth);
|
|
21968
|
+
}
|
|
21050
21969
|
let applied = 0;
|
|
21051
21970
|
for (let r = 0; r < replica.length; r++) {
|
|
21052
|
-
|
|
21053
|
-
|
|
21971
|
+
applied += patchHtmlRowPair(table, scanTable, replica, editedRows, r, r, ctx, skip, depth);
|
|
21972
|
+
}
|
|
21973
|
+
return applied;
|
|
21974
|
+
}
|
|
21975
|
+
function patchHtmlRowPair(table, scanTable, replica, editedRows, r, er, ctx, skip, depth) {
|
|
21976
|
+
if (editedRows[er].cells.length !== replica[r].cells.length) {
|
|
21977
|
+
skip(`\uD45C ${r + 1}\uD589 \uC140 \uC218 \uBCC0\uACBD\uC740 \uBBF8\uC9C0\uC6D0`);
|
|
21978
|
+
return 0;
|
|
21979
|
+
}
|
|
21980
|
+
let applied = 0;
|
|
21981
|
+
for (let c = 0; c < replica[r].cells.length; c++) {
|
|
21982
|
+
const oc = replica[r].cells[c];
|
|
21983
|
+
const ec = editedRows[er].cells[c];
|
|
21984
|
+
if (oc.colSpan !== ec.colSpan || oc.rowSpan !== ec.rowSpan) {
|
|
21985
|
+
skip(`\uC140 \uBCD1\uD569(colspan/rowspan) \uBCC0\uACBD\uC740 \uBBF8\uC9C0\uC6D0`);
|
|
21054
21986
|
continue;
|
|
21055
21987
|
}
|
|
21056
|
-
|
|
21057
|
-
|
|
21058
|
-
|
|
21059
|
-
|
|
21060
|
-
|
|
21988
|
+
if (oc.inner === ec.inner) continue;
|
|
21989
|
+
const origContent = htmlCellInnerToLines(oc.inner);
|
|
21990
|
+
const editedContent = htmlCellInnerToLines(ec.inner);
|
|
21991
|
+
if (origContent.hadNonText || editedContent.hadNonText) {
|
|
21992
|
+
if (extractImgTags(oc.inner) !== extractImgTags(ec.inner)) {
|
|
21993
|
+
skip("\uC140 \uB0B4 \uC774\uBBF8\uC9C0 \uBCC0\uACBD\uC740 \uBBF8\uC9C0\uC6D0");
|
|
21061
21994
|
continue;
|
|
21062
21995
|
}
|
|
21063
|
-
|
|
21064
|
-
const
|
|
21065
|
-
|
|
21066
|
-
|
|
21067
|
-
|
|
21068
|
-
skip("\uC140 \uB0B4 \uC774\uBBF8\uC9C0 \uBCC0\uACBD\uC740 \uBBF8\uC9C0\uC6D0");
|
|
21069
|
-
continue;
|
|
21070
|
-
}
|
|
21071
|
-
const origTables = extractTopLevelTables(oc.inner);
|
|
21072
|
-
const editedTables = extractTopLevelTables(ec.inner);
|
|
21073
|
-
if (origTables.length !== editedTables.length) {
|
|
21074
|
-
skip("\uC140 \uB0B4 \uC911\uCCA9\uD45C \uCD94\uAC00/\uC0AD\uC81C\uB294 \uBBF8\uC9C0\uC6D0");
|
|
21075
|
-
continue;
|
|
21076
|
-
}
|
|
21077
|
-
if (origTables.join("\n") !== editedTables.join("\n")) {
|
|
21078
|
-
applied += patchNestedTables(table, scanTable, oc, origTables, editedTables, ctx, skip, depth);
|
|
21079
|
-
}
|
|
21996
|
+
const origTables = extractTopLevelTables(oc.inner);
|
|
21997
|
+
const editedTables = extractTopLevelTables(ec.inner);
|
|
21998
|
+
if (origTables.length !== editedTables.length) {
|
|
21999
|
+
skip("\uC140 \uB0B4 \uC911\uCCA9\uD45C \uCD94\uAC00/\uC0AD\uC81C\uB294 \uBBF8\uC9C0\uC6D0");
|
|
22000
|
+
continue;
|
|
21080
22001
|
}
|
|
21081
|
-
if (
|
|
21082
|
-
|
|
21083
|
-
applied += applyCellEdit(table, scanTable, oc.gridR, oc.gridC, newLines, ctx, oc.inner, ec.inner, origContent.lines.length);
|
|
22002
|
+
if (origTables.join("\n") !== editedTables.join("\n")) {
|
|
22003
|
+
applied += patchNestedTables(table, scanTable, oc, origTables, editedTables, ctx, skip, depth);
|
|
21084
22004
|
}
|
|
21085
22005
|
}
|
|
22006
|
+
if (origContent.lines.join("\n") !== editedContent.lines.join("\n")) {
|
|
22007
|
+
const newLines = editedContent.lines.map((l) => unescapeGfm(l));
|
|
22008
|
+
applied += applyCellEdit(table, scanTable, oc.gridR, oc.gridC, newLines, ctx, oc.inner, ec.inner, origContent.lines.length);
|
|
22009
|
+
}
|
|
21086
22010
|
}
|
|
21087
22011
|
return applied;
|
|
21088
22012
|
}
|
|
22013
|
+
function patchHtmlTableRows(table, scanTable, replica, editedRows, ctx, skip, depth) {
|
|
22014
|
+
if (replica.length !== table.rows || replica.some((row, r) => row.cells.some((c) => c.gridR !== r))) {
|
|
22015
|
+
return skip("\uD45C \uB80C\uB354 \uD589\uACFC \uACA9\uC790 \uD589 \uBD88\uC77C\uCE58 (\uBCD1\uD569 \uC18C\uC2E4 \uD589) \u2014 \uD589 \uCD94\uAC00/\uC0AD\uC81C \uBBF8\uC9C0\uC6D0");
|
|
22016
|
+
}
|
|
22017
|
+
const key = (row) => row.cells.map((c) => `${c.colSpan}x${c.rowSpan}:${c.inner}`).join("\0");
|
|
22018
|
+
return patchTableRows({
|
|
22019
|
+
table,
|
|
22020
|
+
scanTable,
|
|
22021
|
+
ctx,
|
|
22022
|
+
skip,
|
|
22023
|
+
origKeys: replica.map(key),
|
|
22024
|
+
editedKeys: editedRows.map(key),
|
|
22025
|
+
editedCells: (ei) => {
|
|
22026
|
+
const cells = [];
|
|
22027
|
+
for (const cell of editedRows[ei].cells) {
|
|
22028
|
+
const content = htmlCellInnerToLines(cell.inner);
|
|
22029
|
+
if (content.hadNonText) return null;
|
|
22030
|
+
cells.push({ lines: content.lines.map((l) => unescapeGfm(l)), colSpan: cell.colSpan, rowSpan: cell.rowSpan });
|
|
22031
|
+
}
|
|
22032
|
+
return cells;
|
|
22033
|
+
},
|
|
22034
|
+
patchMatched: (oi, ei) => patchHtmlRowPair(table, scanTable, replica, editedRows, oi, ei, ctx, skip, depth)
|
|
22035
|
+
});
|
|
22036
|
+
}
|
|
21089
22037
|
function patchNestedTables(table, scanTable, oc, origTables, editedTables, ctx, skip, depth) {
|
|
21090
22038
|
const irCell = table.cells[oc.gridR]?.[oc.gridC];
|
|
21091
22039
|
const scanCell = scanTable.cellByAnchor.get(`${oc.gridR},${oc.gridC}`);
|
|
@@ -21185,7 +22133,7 @@ function applyCellEdit(table, scanTable, gridR, gridC, newLines, ctx, before, af
|
|
|
21185
22133
|
splices.push(...sp);
|
|
21186
22134
|
sectionIndex = target.sectionIndex;
|
|
21187
22135
|
if (newLines.length > 1) {
|
|
21188
|
-
ctx.skipped.push({ reason: "\uC140 \uB0B4 \uC904 \uCD94\uAC00\uB294 \uBB38\uB2E8 \uC0DD\uC131 \uBBF8\uC9C0\uC6D0 \u2014 \uD55C \uBB38\uB2E8\uC73C\uB85C \uBCD1\uD569 \uC801\uC6A9", after: summarize(after) });
|
|
22136
|
+
ctx.skipped.push({ reason: "\uC140 \uB0B4 \uC904 \uCD94\uAC00\uB294 \uBB38\uB2E8 \uC0DD\uC131 \uBBF8\uC9C0\uC6D0 \u2014 \uD55C \uBB38\uB2E8\uC73C\uB85C \uBCD1\uD569 \uC801\uC6A9", after: summarize(after), partial: true });
|
|
21189
22137
|
}
|
|
21190
22138
|
} else {
|
|
21191
22139
|
const assigned = [];
|
|
@@ -21197,7 +22145,9 @@ function applyCellEdit(table, scanTable, gridR, gridC, newLines, ctx, before, af
|
|
|
21197
22145
|
}
|
|
21198
22146
|
}
|
|
21199
22147
|
if (newLines.length > nonEmpty.length) {
|
|
21200
|
-
ctx.skipped.push({ reason: "\uC140 \uB0B4 \uC904 \uCD94\uAC00\uB294 \uBB38\uB2E8 \uC0DD\uC131 \uBBF8\uC9C0\uC6D0 \u2014 \uB9C8\uC9C0\uB9C9 \uBB38\uB2E8\uC5D0 \uBCD1\uD569 \uC801\uC6A9", after: summarize(after) });
|
|
22148
|
+
ctx.skipped.push({ reason: "\uC140 \uB0B4 \uC904 \uCD94\uAC00\uB294 \uBB38\uB2E8 \uC0DD\uC131 \uBBF8\uC9C0\uC6D0 \u2014 \uB9C8\uC9C0\uB9C9 \uBB38\uB2E8\uC5D0 \uBCD1\uD569 \uC801\uC6A9", after: summarize(after), partial: true });
|
|
22149
|
+
} else if (newLines.length < nonEmpty.length && nonEmpty.length > 1) {
|
|
22150
|
+
ctx.skipped.push({ reason: "\uC140 \uB0B4 \uC904 \uC0AD\uC81C\uB294 \uBB38\uB2E8 \uC81C\uAC70 \uBBF8\uC9C0\uC6D0 \u2014 \uBE48 \uBB38\uB2E8 \uC794\uC874(\uBDF0\uC5B4\uC5D0 \uBE48 \uC904 \uD45C\uC2DC \uAC00\uB2A5)", before: summarize(before), after: summarize(after), partial: true });
|
|
21201
22151
|
}
|
|
21202
22152
|
for (let i = 0; i < nonEmpty.length; i++) {
|
|
21203
22153
|
if (assigned[i] === nonEmpty[i].text || normForMatch(assigned[i]) === normForMatch(nonEmpty[i].text)) continue;
|
|
@@ -21370,7 +22320,8 @@ async function patchHwpx(original, editedMarkdown, options) {
|
|
|
21370
22320
|
try {
|
|
21371
22321
|
for (let i = 0; i < scans.length; i++) {
|
|
21372
22322
|
if (sectionSplices[i].length === 0) continue;
|
|
21373
|
-
sectionSplices[i].
|
|
22323
|
+
const claimed = sectionSplices[i].filter((s) => s.end > s.start);
|
|
22324
|
+
sectionSplices[i].push(...allLinesegRemovalSplices(scans[i].xml).filter((ls) => !claimed.some((c) => ls.start >= c.start && ls.end <= c.end)));
|
|
21374
22325
|
const newXml = applySplices(scans[i].xml, sectionSplices[i]);
|
|
21375
22326
|
replacements.set(sectionPaths[i], encoder.encode(newXml));
|
|
21376
22327
|
}
|
|
@@ -21438,82 +22389,6 @@ function buildTableOrdinals(blocks) {
|
|
|
21438
22389
|
}
|
|
21439
22390
|
return map;
|
|
21440
22391
|
}
|
|
21441
|
-
function alignUnits(a, b) {
|
|
21442
|
-
const m = a.length, n = b.length;
|
|
21443
|
-
if (m * n > 4e6) {
|
|
21444
|
-
const result2 = [];
|
|
21445
|
-
let pre = 0;
|
|
21446
|
-
while (pre < m && pre < n && a[pre] === b[pre]) {
|
|
21447
|
-
result2.push([pre, pre]);
|
|
21448
|
-
pre++;
|
|
21449
|
-
}
|
|
21450
|
-
let suf = 0;
|
|
21451
|
-
while (suf < m - pre && suf < n - pre && a[m - 1 - suf] === b[n - 1 - suf]) suf++;
|
|
21452
|
-
const aMid = m - pre - suf, bMid = n - pre - suf;
|
|
21453
|
-
if (aMid === bMid) {
|
|
21454
|
-
for (let i2 = 0; i2 < aMid; i2++) result2.push([pre + i2, pre + i2]);
|
|
21455
|
-
} else {
|
|
21456
|
-
for (let i2 = 0; i2 < aMid; i2++) result2.push([pre + i2, null]);
|
|
21457
|
-
for (let j2 = 0; j2 < bMid; j2++) result2.push([null, pre + j2]);
|
|
21458
|
-
}
|
|
21459
|
-
for (let s = suf - 1; s >= 0; s--) result2.push([m - 1 - s, n - 1 - s]);
|
|
21460
|
-
return result2;
|
|
21461
|
-
}
|
|
21462
|
-
const dp = Array.from({ length: m + 1 }, () => new Int32Array(n + 1));
|
|
21463
|
-
for (let i2 = 1; i2 <= m; i2++) {
|
|
21464
|
-
for (let j2 = 1; j2 <= n; j2++) {
|
|
21465
|
-
dp[i2][j2] = a[i2 - 1] === b[j2 - 1] ? dp[i2 - 1][j2 - 1] + 1 : Math.max(dp[i2 - 1][j2], dp[i2][j2 - 1]);
|
|
21466
|
-
}
|
|
21467
|
-
}
|
|
21468
|
-
const matches = [];
|
|
21469
|
-
let i = m, j = n;
|
|
21470
|
-
while (i > 0 && j > 0) {
|
|
21471
|
-
if (a[i - 1] === b[j - 1] && dp[i][j] === dp[i - 1][j - 1] + 1) {
|
|
21472
|
-
matches.push([i - 1, j - 1]);
|
|
21473
|
-
i--;
|
|
21474
|
-
j--;
|
|
21475
|
-
} else if (dp[i - 1][j] >= dp[i][j - 1]) i--;
|
|
21476
|
-
else j--;
|
|
21477
|
-
}
|
|
21478
|
-
matches.reverse();
|
|
21479
|
-
const result = [];
|
|
21480
|
-
let ai = 0, bi = 0;
|
|
21481
|
-
const flushGap = (aEnd, bEnd) => {
|
|
21482
|
-
if (aEnd - ai === bEnd - bi) {
|
|
21483
|
-
while (ai < aEnd) result.push([ai++, bi++]);
|
|
21484
|
-
return;
|
|
21485
|
-
}
|
|
21486
|
-
while (ai < aEnd && bi < bEnd) {
|
|
21487
|
-
const sim = normalizedSimilarity(a[ai], b[bi]);
|
|
21488
|
-
if (sim >= 0.4) {
|
|
21489
|
-
if (aEnd - ai > bEnd - bi && bestSimInRange(a, ai + 1, ai + (aEnd - ai) - (bEnd - bi), b[bi]) > sim) {
|
|
21490
|
-
result.push([ai++, null]);
|
|
21491
|
-
} else if (bEnd - bi > aEnd - ai && bestSimInRange(b, bi + 1, bi + (bEnd - bi) - (aEnd - ai), a[ai]) > sim) {
|
|
21492
|
-
result.push([null, bi++]);
|
|
21493
|
-
} else {
|
|
21494
|
-
result.push([ai++, bi++]);
|
|
21495
|
-
}
|
|
21496
|
-
} else if (aEnd - ai >= bEnd - bi) result.push([ai++, null]);
|
|
21497
|
-
else result.push([null, bi++]);
|
|
21498
|
-
}
|
|
21499
|
-
while (ai < aEnd) result.push([ai++, null]);
|
|
21500
|
-
while (bi < bEnd) result.push([null, bi++]);
|
|
21501
|
-
};
|
|
21502
|
-
for (const [pi, pj] of matches) {
|
|
21503
|
-
flushGap(pi, pj);
|
|
21504
|
-
result.push([ai++, bi++]);
|
|
21505
|
-
}
|
|
21506
|
-
flushGap(m, n);
|
|
21507
|
-
return result;
|
|
21508
|
-
}
|
|
21509
|
-
function bestSimInRange(arr, from, to, target) {
|
|
21510
|
-
let best = 0;
|
|
21511
|
-
for (let k = from; k <= to && k < arr.length; k++) {
|
|
21512
|
-
const s = normalizedSimilarity(arr[k], target);
|
|
21513
|
-
if (s > best) best = s;
|
|
21514
|
-
}
|
|
21515
|
-
return best;
|
|
21516
|
-
}
|
|
21517
22392
|
function resolveParagraphMappings(blocks, scans) {
|
|
21518
22393
|
const buckets = /* @__PURE__ */ new Map();
|
|
21519
22394
|
for (const scan of scans) {
|
|
@@ -21656,7 +22531,7 @@ function patchParagraphUnit(block, orig, edited, ctx, skip) {
|
|
|
21656
22531
|
const origPrefix = block.text.split(" ", 1)[0];
|
|
21657
22532
|
const sp = newPlain.indexOf(" ");
|
|
21658
22533
|
const newFirst = sp > 0 ? newPlain.slice(0, sp) : newPlain;
|
|
21659
|
-
if (newFirst === origPrefix ||
|
|
22534
|
+
if (newFirst === origPrefix || AUTONUM_PREFIX_RE.test(newFirst)) {
|
|
21660
22535
|
newPlain = sp > 0 ? newPlain.slice(sp + 1) : "";
|
|
21661
22536
|
} else {
|
|
21662
22537
|
ctx.skipped.push({ reason: "\uC790\uB3D9\uBC88\uD638 \uC811\uB450 \uC2DD\uBCC4 \uC2E4\uD328 \u2014 \uBC88\uD638 \uD3EC\uD568 \uD14D\uC2A4\uD2B8\uB85C \uC801\uC6A9 (\uBDF0\uC5B4\uC5D0\uC11C \uC911\uBCF5 \uD45C\uC2DC \uAC00\uB2A5)", after: summarize(newPlain) });
|
|
@@ -22399,7 +23274,7 @@ function patchParagraph(block, orig, edited, ctx, skip) {
|
|
|
22399
23274
|
const origPrefix = block.text.split(" ", 1)[0];
|
|
22400
23275
|
const sp = newPlain.indexOf(" ");
|
|
22401
23276
|
const newFirst = sp > 0 ? newPlain.slice(0, sp) : newPlain;
|
|
22402
|
-
if (newFirst === origPrefix ||
|
|
23277
|
+
if (newFirst === origPrefix || AUTONUM_PREFIX_RE.test(newFirst)) {
|
|
22403
23278
|
newPlain = sp > 0 ? newPlain.slice(sp + 1) : "";
|
|
22404
23279
|
} else {
|
|
22405
23280
|
ctx.skipped.push({ reason: "\uC790\uB3D9\uBC88\uD638 \uC811\uB450 \uC2DD\uBCC4 \uC2E4\uD328 \u2014 \uBC88\uD638 \uD3EC\uD568 \uD14D\uC2A4\uD2B8\uB85C \uC801\uC6A9 (\uBDF0\uC5B4\uC5D0\uC11C \uC911\uBCF5 \uD45C\uC2DC \uAC00\uB2A5)", after: summarize(newPlain) });
|
|
@@ -22464,9 +23339,6 @@ function patchGfmCells(scanTable, orig, edited, ctx, skip) {
|
|
|
22464
23339
|
}
|
|
22465
23340
|
return applied;
|
|
22466
23341
|
}
|
|
22467
|
-
function extractImgTags5(inner) {
|
|
22468
|
-
return (inner.match(/<img\s(?:"[^"]*"|'[^']*'|[^>"'])*?>/gi) || []).join(" ");
|
|
22469
|
-
}
|
|
22470
23342
|
function patchHtmlCells5(table, scanTable, orig, edited, ctx, skip) {
|
|
22471
23343
|
if (replicateTableToHtml(table) !== orig.raw) return skip("\uD45C \uC88C\uD45C \uC7AC\uD604 \uBD88\uC77C\uCE58 \u2014 \uB9E4\uD551 \uC2E0\uB8B0 \uBD88\uAC00");
|
|
22472
23344
|
const replica = replicateHtmlTable(table);
|
|
@@ -22494,7 +23366,7 @@ function patchHtmlCells5(table, scanTable, orig, edited, ctx, skip) {
|
|
|
22494
23366
|
const origContent = htmlCellInnerToLines(oc.inner);
|
|
22495
23367
|
const editedContent = htmlCellInnerToLines(ec.inner);
|
|
22496
23368
|
if (origContent.hadNonText || editedContent.hadNonText) {
|
|
22497
|
-
if (
|
|
23369
|
+
if (extractImgTags(oc.inner) !== extractImgTags(ec.inner)) {
|
|
22498
23370
|
skip("\uC140 \uB0B4 \uC774\uBBF8\uC9C0 \uBCC0\uACBD\uC740 \uBBF8\uC9C0\uC6D0");
|
|
22499
23371
|
continue;
|
|
22500
23372
|
}
|
|
@@ -22585,7 +23457,9 @@ function applyCellEdit5(table, scanTable, gridR, gridC, newLines, ctx, before, a
|
|
|
22585
23457
|
}
|
|
22586
23458
|
}
|
|
22587
23459
|
if (newLines.length > nonEmpty.length) {
|
|
22588
|
-
ctx.skipped.push({ reason: "\uC140 \uB0B4 \uC904 \uCD94\uAC00\uB294 \uBB38\uB2E8 \uC0DD\uC131 \uBBF8\uC9C0\uC6D0 \u2014 \uB9C8\uC9C0\uB9C9 \uBB38\uB2E8\uC5D0 \uBCD1\uD569 \uC801\uC6A9", after: summarize(after) });
|
|
23460
|
+
ctx.skipped.push({ reason: "\uC140 \uB0B4 \uC904 \uCD94\uAC00\uB294 \uBB38\uB2E8 \uC0DD\uC131 \uBBF8\uC9C0\uC6D0 \u2014 \uB9C8\uC9C0\uB9C9 \uBB38\uB2E8\uC5D0 \uBCD1\uD569 \uC801\uC6A9", after: summarize(after), partial: true });
|
|
23461
|
+
} else if (newLines.length < nonEmpty.length && nonEmpty.length > 1) {
|
|
23462
|
+
ctx.skipped.push({ reason: "\uC140 \uB0B4 \uC904 \uC0AD\uC81C\uB294 \uBB38\uB2E8 \uC81C\uAC70 \uBBF8\uC9C0\uC6D0 \u2014 \uBE48 \uBB38\uB2E8 \uC794\uC874(\uBDF0\uC5B4\uC5D0 \uBE48 \uC904 \uD45C\uC2DC \uAC00\uB2A5)", before: summarize(before), after: summarize(after), partial: true });
|
|
22589
23463
|
}
|
|
22590
23464
|
let staged = 0;
|
|
22591
23465
|
for (let i = 0; i < nonEmpty.length; i++) {
|
|
@@ -22960,6 +23834,7 @@ var HwpxSession = class _HwpxSession {
|
|
|
22960
23834
|
try {
|
|
22961
23835
|
for (let s = 0; s < st.scans.length; s++) {
|
|
22962
23836
|
if (sectionSplices[s].length === 0) continue;
|
|
23837
|
+
sectionSplices[s].push(...allLinesegRemovalSplices(st.scans[s].xml));
|
|
22963
23838
|
replacements.set(st.sectionPaths[s], encoder.encode(applySplices(st.scans[s].xml, sectionSplices[s])));
|
|
22964
23839
|
}
|
|
22965
23840
|
} catch (err) {
|
|
@@ -23016,7 +23891,7 @@ var HwpxSession = class _HwpxSession {
|
|
|
23016
23891
|
const origPrefix = block.text.split(" ", 1)[0];
|
|
23017
23892
|
const sp = newPlain.indexOf(" ");
|
|
23018
23893
|
const newFirst = sp > 0 ? newPlain.slice(0, sp) : newPlain;
|
|
23019
|
-
if (newFirst === origPrefix ||
|
|
23894
|
+
if (newFirst === origPrefix || AUTONUM_PREFIX_RE.test(newFirst)) {
|
|
23020
23895
|
newPlain = sp > 0 ? newPlain.slice(sp + 1) : "";
|
|
23021
23896
|
} else {
|
|
23022
23897
|
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) });
|
|
@@ -23310,7 +24185,7 @@ async function parseHwp(buffer, options) {
|
|
|
23310
24185
|
async function parsePdf(buffer, options) {
|
|
23311
24186
|
let parsePdfDocument;
|
|
23312
24187
|
try {
|
|
23313
|
-
const mod = await import("./parser-
|
|
24188
|
+
const mod = await import("./parser-DR5CTZ74.js");
|
|
23314
24189
|
parsePdfDocument = mod.parsePdfDocument;
|
|
23315
24190
|
} catch {
|
|
23316
24191
|
return {
|
|
@@ -23403,6 +24278,7 @@ async function fillForm(input, values, outputFormat = "markdown") {
|
|
|
23403
24278
|
export {
|
|
23404
24279
|
extractHwpxMetadataOnly,
|
|
23405
24280
|
extractHwp5MetadataOnly,
|
|
24281
|
+
ValueCursor,
|
|
23406
24282
|
isLabelCell,
|
|
23407
24283
|
extractFormFields,
|
|
23408
24284
|
inferFieldType,
|
|
@@ -23413,6 +24289,13 @@ export {
|
|
|
23413
24289
|
buildRangeSplices,
|
|
23414
24290
|
applySplices,
|
|
23415
24291
|
fillHwpx,
|
|
24292
|
+
charWidthEm1000,
|
|
24293
|
+
SPACE_EM_FIXED,
|
|
24294
|
+
SPACE_EM_FONT,
|
|
24295
|
+
measureTextWidth,
|
|
24296
|
+
simulateWrap,
|
|
24297
|
+
simulateWrapKeepWord,
|
|
24298
|
+
fitRatioForFewerLines,
|
|
23416
24299
|
PRESET_ALIAS,
|
|
23417
24300
|
normalizeGongmunPreset,
|
|
23418
24301
|
markdownToHwpx,
|
|
@@ -23437,4 +24320,4 @@ export {
|
|
|
23437
24320
|
parseHwpml,
|
|
23438
24321
|
fillForm
|
|
23439
24322
|
};
|
|
23440
|
-
//# sourceMappingURL=chunk-
|
|
24323
|
+
//# sourceMappingURL=chunk-NXRABCWW.js.map
|