kordoc 3.5.4 → 3.8.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 +23 -1
- package/dist/{-KX7VLNW5.js → -ATVQYFSW.js} +21 -5
- package/dist/{chunk-WEAP42T3.js → chunk-3R3YK7EM.js} +2 -2
- package/dist/chunk-3R3YK7EM.js.map +1 -0
- package/dist/{chunk-XZTDYOZV.cjs → chunk-ITJIALN5.cjs} +2 -2
- package/dist/{chunk-XZTDYOZV.cjs.map → chunk-ITJIALN5.cjs.map} +1 -1
- package/dist/{chunk-VEB43O2L.js → chunk-QV25HMU7.js} +2 -2
- package/dist/chunk-QV25HMU7.js.map +1 -0
- package/dist/{chunk-DK2IE43H.js → chunk-SLKF72QF.js} +1682 -718
- package/dist/chunk-SLKF72QF.js.map +1 -0
- package/dist/cli.js +4 -4
- package/dist/index.cjs +1908 -944
- 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 +1681 -717
- package/dist/index.js.map +1 -1
- package/dist/mcp.js +3 -3
- package/dist/{parser-W5TBEAVX.js → parser-7G5F7PT2.js} +2 -2
- package/dist/{parser-XT65AEDJ.js → parser-DCK42RMA.js} +2 -2
- package/dist/{parser-CPTBEGQP.cjs → parser-GUSJH44K.cjs} +14 -14
- package/dist/{parser-CPTBEGQP.cjs.map → parser-GUSJH44K.cjs.map} +1 -1
- package/dist/{watch-VOSNRZAY.js → watch-GVZESOCE.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 → -ATVQYFSW.js.map} +0 -0
- /package/dist/{parser-W5TBEAVX.js.map → parser-7G5F7PT2.js.map} +0 -0
- /package/dist/{parser-XT65AEDJ.js.map → parser-DCK42RMA.js.map} +0 -0
- /package/dist/{watch-VOSNRZAY.js.map → watch-GVZESOCE.js.map} +0 -0
|
@@ -25,7 +25,7 @@ import {
|
|
|
25
25
|
sanitizeHref,
|
|
26
26
|
stripDtd,
|
|
27
27
|
toArrayBuffer
|
|
28
|
-
} from "./chunk-
|
|
28
|
+
} from "./chunk-3R3YK7EM.js";
|
|
29
29
|
import {
|
|
30
30
|
parsePageRange
|
|
31
31
|
} from "./chunk-MOL7MDBG.js";
|
|
@@ -967,57 +967,62 @@ async function extractImagesFromZip(zip, blocks, decompressed, warnings) {
|
|
|
967
967
|
let imageIndex = 0;
|
|
968
968
|
const imageBlocks = [];
|
|
969
969
|
collectImageBlocks(blocks, imageBlocks);
|
|
970
|
+
const resolved = /* @__PURE__ */ new Map();
|
|
970
971
|
for (const { block, ownerCell } of imageBlocks) {
|
|
971
972
|
if (block.type !== "image" || !block.text) continue;
|
|
972
973
|
const ref = block.text;
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
974
|
+
let img = resolved.get(ref);
|
|
975
|
+
if (img === void 0) {
|
|
976
|
+
img = null;
|
|
977
|
+
const candidates = [
|
|
978
|
+
`BinData/${ref}`,
|
|
979
|
+
`Contents/BinData/${ref}`,
|
|
980
|
+
ref
|
|
981
|
+
// 절대 경로일 수도 있음
|
|
982
|
+
];
|
|
983
|
+
let resolvedPath = null;
|
|
984
|
+
if (!ref.includes(".")) {
|
|
985
|
+
const prefixes = [`BinData/${ref}`, `Contents/BinData/${ref}`];
|
|
986
|
+
for (const prefix of prefixes) {
|
|
987
|
+
const match = zip.file(new RegExp(`^${prefix.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\.[a-zA-Z0-9]+$`));
|
|
988
|
+
if (match.length > 0) {
|
|
989
|
+
resolvedPath = match[0].name;
|
|
990
|
+
break;
|
|
991
|
+
}
|
|
987
992
|
}
|
|
988
993
|
}
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
if (ownerCell) ownerCell.text = ownerCell.text.replace(``, ``);
|
|
1009
|
-
found = true;
|
|
1010
|
-
break;
|
|
1011
|
-
} catch (err) {
|
|
1012
|
-
if (err instanceof KordocError) throw err;
|
|
994
|
+
const allCandidates = resolvedPath ? [resolvedPath, ...candidates] : candidates;
|
|
995
|
+
for (const path of allCandidates) {
|
|
996
|
+
if (isPathTraversal(path)) continue;
|
|
997
|
+
const file = zip.file(path);
|
|
998
|
+
if (!file) continue;
|
|
999
|
+
try {
|
|
1000
|
+
const data = await file.async("uint8array");
|
|
1001
|
+
decompressed.total += data.length;
|
|
1002
|
+
if (decompressed.total > MAX_DECOMPRESS_SIZE) throw new KordocError("ZIP \uC555\uCD95 \uD574\uC81C \uD06C\uAE30 \uCD08\uACFC (ZIP bomb \uC758\uC2EC)");
|
|
1003
|
+
const ext = path.includes(".") ? path.split(".").pop() || "png" : "png";
|
|
1004
|
+
const mimeType = imageExtToMime(ext);
|
|
1005
|
+
imageIndex++;
|
|
1006
|
+
const filename = `image_${String(imageIndex).padStart(3, "0")}.${mimeToExt(mimeType)}`;
|
|
1007
|
+
img = { filename, data, mimeType };
|
|
1008
|
+
images.push(img);
|
|
1009
|
+
break;
|
|
1010
|
+
} catch (err) {
|
|
1011
|
+
if (err instanceof KordocError) throw err;
|
|
1012
|
+
}
|
|
1013
1013
|
}
|
|
1014
|
+
if (!img) warnings?.push({ page: block.pageNumber, message: `\uC774\uBBF8\uC9C0 \uD30C\uC77C \uC5C6\uC74C: ${ref}`, code: "SKIPPED_IMAGE" });
|
|
1015
|
+
resolved.set(ref, img);
|
|
1014
1016
|
}
|
|
1015
|
-
if (!
|
|
1016
|
-
warnings?.push({ page: block.pageNumber, message: `\uC774\uBBF8\uC9C0 \uD30C\uC77C \uC5C6\uC74C: ${ref}`, code: "SKIPPED_IMAGE" });
|
|
1017
|
+
if (!img) {
|
|
1017
1018
|
block.type = "paragraph";
|
|
1018
1019
|
block.text = `[\uC774\uBBF8\uC9C0: ${ref}]`;
|
|
1019
1020
|
if (ownerCell) ownerCell.text = ownerCell.text.replace(``, `[\uC774\uBBF8\uC9C0: ${ref}]`);
|
|
1021
|
+
continue;
|
|
1020
1022
|
}
|
|
1023
|
+
block.text = img.filename;
|
|
1024
|
+
block.imageData = { data: img.data, mimeType: img.mimeType, filename: ref };
|
|
1025
|
+
if (ownerCell) ownerCell.text = ownerCell.text.replace(``, ``);
|
|
1021
1026
|
}
|
|
1022
1027
|
return images;
|
|
1023
1028
|
}
|
|
@@ -1218,9 +1223,32 @@ function detectHwpxHeadings(blocks, styleMap) {
|
|
|
1218
1223
|
function buildTableWithCellMeta(state) {
|
|
1219
1224
|
const table = buildTable(state.rows);
|
|
1220
1225
|
if (state.caption) table.caption = state.caption;
|
|
1226
|
+
const anchors = [];
|
|
1227
|
+
{
|
|
1228
|
+
const covered = /* @__PURE__ */ new Set();
|
|
1229
|
+
for (let r = 0; r < table.rows; r++) {
|
|
1230
|
+
for (let c = 0; c < table.cols; c++) {
|
|
1231
|
+
if (covered.has(`${r},${c}`)) continue;
|
|
1232
|
+
const cell = table.cells[r]?.[c];
|
|
1233
|
+
if (!cell) continue;
|
|
1234
|
+
for (let dr = 0; dr < cell.rowSpan; dr++) {
|
|
1235
|
+
for (let dc = 0; dc < cell.colSpan; dc++) {
|
|
1236
|
+
if (dr === 0 && dc === 0) continue;
|
|
1237
|
+
if (r + dr < table.rows && c + dc < table.cols) covered.add(`${r + dr},${c + dc}`);
|
|
1238
|
+
}
|
|
1239
|
+
}
|
|
1240
|
+
anchors.push(cell);
|
|
1241
|
+
c += cell.colSpan - 1;
|
|
1242
|
+
}
|
|
1243
|
+
}
|
|
1244
|
+
}
|
|
1245
|
+
const srcCount = state.rows.reduce((s, r) => s + r.length, 0);
|
|
1246
|
+
const ordinalReliable = anchors.length === srcCount;
|
|
1221
1247
|
const claimed = /* @__PURE__ */ new Set();
|
|
1248
|
+
let flatIdx = -1;
|
|
1222
1249
|
for (const row of state.rows) {
|
|
1223
1250
|
for (const src of row) {
|
|
1251
|
+
flatIdx++;
|
|
1224
1252
|
const needsBlocks = src.hasStructure && src.blocks && src.blocks.length > 0;
|
|
1225
1253
|
if (!needsBlocks && !src.isHeader) continue;
|
|
1226
1254
|
let target;
|
|
@@ -1239,6 +1267,10 @@ function buildTableWithCellMeta(state) {
|
|
|
1239
1267
|
}
|
|
1240
1268
|
}
|
|
1241
1269
|
}
|
|
1270
|
+
if (!target && ordinalReliable) {
|
|
1271
|
+
const cand = anchors[flatIdx];
|
|
1272
|
+
if (cand && !claimed.has(cand)) target = cand;
|
|
1273
|
+
}
|
|
1242
1274
|
if (!target) continue;
|
|
1243
1275
|
claimed.add(target);
|
|
1244
1276
|
if (needsBlocks) target.blocks = src.blocks;
|
|
@@ -2369,32 +2401,42 @@ function resolveImageBlocks(binDataMap, blocks, warnings) {
|
|
|
2369
2401
|
if (imageBlocks.length === 0) return [];
|
|
2370
2402
|
const images = [];
|
|
2371
2403
|
const renamed = /* @__PURE__ */ new Map();
|
|
2404
|
+
const resolved = /* @__PURE__ */ new Map();
|
|
2372
2405
|
let imageIndex = 0;
|
|
2373
2406
|
for (const block of imageBlocks) {
|
|
2374
2407
|
if (!block.text) continue;
|
|
2375
2408
|
const storageId = parseInt(block.text, 10);
|
|
2376
2409
|
if (isNaN(storageId)) continue;
|
|
2377
|
-
|
|
2378
|
-
if (
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
|
|
2410
|
+
let img = resolved.get(storageId);
|
|
2411
|
+
if (img === void 0) {
|
|
2412
|
+
const bin = binDataMap.get(storageId);
|
|
2413
|
+
if (!bin) {
|
|
2414
|
+
warnings.push({ page: block.pageNumber, message: `BinData ${storageId} \uC5C6\uC74C`, code: "SKIPPED_IMAGE" });
|
|
2415
|
+
resolved.set(storageId, null);
|
|
2416
|
+
} else {
|
|
2417
|
+
const mime = detectImageMime(bin.data);
|
|
2418
|
+
if (!mime) {
|
|
2419
|
+
warnings.push({ page: block.pageNumber, message: `BinData ${storageId}: \uC54C \uC218 \uC5C6\uB294 \uC774\uBBF8\uC9C0 \uD615\uC2DD`, code: "SKIPPED_IMAGE" });
|
|
2420
|
+
resolved.set(storageId, null);
|
|
2421
|
+
} else {
|
|
2422
|
+
imageIndex++;
|
|
2423
|
+
const ext = mime.includes("jpeg") ? "jpg" : mime.includes("png") ? "png" : mime.includes("gif") ? "gif" : mime.includes("bmp") ? "bmp" : "bin";
|
|
2424
|
+
img = { filename: `image_${String(imageIndex).padStart(3, "0")}.${ext}`, data: new Uint8Array(bin.data), mime };
|
|
2425
|
+
resolved.set(storageId, img);
|
|
2426
|
+
images.push({ filename: img.filename, data: img.data, mimeType: img.mime });
|
|
2427
|
+
renamed.set(storageId, img.filename);
|
|
2428
|
+
}
|
|
2429
|
+
}
|
|
2430
|
+
img = resolved.get(storageId);
|
|
2383
2431
|
}
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
warnings.push({ page: block.pageNumber, message: `BinData ${storageId}: \uC54C \uC218 \uC5C6\uB294 \uC774\uBBF8\uC9C0 \uD615\uC2DD`, code: "SKIPPED_IMAGE" });
|
|
2432
|
+
if (!img) {
|
|
2433
|
+
const bin = binDataMap.get(storageId);
|
|
2387
2434
|
block.type = "paragraph";
|
|
2388
|
-
block.text = `[\uC774\uBBF8\uC9C0: ${bin.name}]`;
|
|
2435
|
+
block.text = bin ? `[\uC774\uBBF8\uC9C0: ${bin.name}]` : `[\uC774\uBBF8\uC9C0: BinData ${storageId}]`;
|
|
2389
2436
|
continue;
|
|
2390
2437
|
}
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
const filename = `image_${String(imageIndex).padStart(3, "0")}.${ext}`;
|
|
2394
|
-
images.push({ filename, data: new Uint8Array(bin.data), mimeType: mime });
|
|
2395
|
-
renamed.set(storageId, filename);
|
|
2396
|
-
block.text = filename;
|
|
2397
|
-
block.imageData = { data: new Uint8Array(bin.data), mimeType: mime, filename: bin.name };
|
|
2438
|
+
block.text = img.filename;
|
|
2439
|
+
block.imageData = { data: img.data, mimeType: img.mime, filename: binDataMap.get(storageId).name };
|
|
2398
2440
|
}
|
|
2399
2441
|
resolveCellImageSentinels(blocks, renamed);
|
|
2400
2442
|
return images;
|
|
@@ -18096,7 +18138,7 @@ function parseTable(tbl, styles, numbering, footnotes, rels) {
|
|
|
18096
18138
|
};
|
|
18097
18139
|
return { type: "table", table };
|
|
18098
18140
|
}
|
|
18099
|
-
async function extractImages(zip, rels, doc) {
|
|
18141
|
+
async function extractImages(zip, rels, doc, warnings) {
|
|
18100
18142
|
const blocks = [];
|
|
18101
18143
|
const images = [];
|
|
18102
18144
|
const drawingElements = findElements(doc.documentElement, "drawing");
|
|
@@ -18127,7 +18169,11 @@ async function extractImages(zip, rels, doc) {
|
|
|
18127
18169
|
const filename = `image_${String(imgIdx).padStart(3, "0")}.${ext}`;
|
|
18128
18170
|
images.push({ filename, data, mimeType: mimeMap[ext] ?? "image/png" });
|
|
18129
18171
|
blocks.push({ type: "image", text: filename });
|
|
18130
|
-
} catch {
|
|
18172
|
+
} catch (err) {
|
|
18173
|
+
warnings.push({
|
|
18174
|
+
code: "SKIPPED_IMAGE",
|
|
18175
|
+
message: `DOCX \uC774\uBBF8\uC9C0 \uCD94\uCD9C \uC2E4\uD328 (${imgPath}): ${err instanceof Error ? err.message : String(err)}`
|
|
18176
|
+
});
|
|
18131
18177
|
}
|
|
18132
18178
|
}
|
|
18133
18179
|
}
|
|
@@ -18151,7 +18197,11 @@ async function parseDocxDocument(buffer, options) {
|
|
|
18151
18197
|
if (stylesFile) {
|
|
18152
18198
|
try {
|
|
18153
18199
|
styles = parseStyles(await stylesFile.async("text"));
|
|
18154
|
-
} catch {
|
|
18200
|
+
} catch (err) {
|
|
18201
|
+
warnings.push({
|
|
18202
|
+
code: "PARTIAL_PARSE",
|
|
18203
|
+
message: `DOCX \uC2A4\uD0C0\uC77C(styles.xml) \uD30C\uC2F1 \uC2E4\uD328 \u2014 \uAE30\uBCF8 \uC2A4\uD0C0\uC77C\uB85C \uACC4\uC18D: ${err instanceof Error ? err.message : String(err)}`
|
|
18204
|
+
});
|
|
18155
18205
|
}
|
|
18156
18206
|
}
|
|
18157
18207
|
let numbering = /* @__PURE__ */ new Map();
|
|
@@ -18159,7 +18209,11 @@ async function parseDocxDocument(buffer, options) {
|
|
|
18159
18209
|
if (numFile) {
|
|
18160
18210
|
try {
|
|
18161
18211
|
numbering = parseNumbering(await numFile.async("text"));
|
|
18162
|
-
} catch {
|
|
18212
|
+
} catch (err) {
|
|
18213
|
+
warnings.push({
|
|
18214
|
+
code: "PARTIAL_PARSE",
|
|
18215
|
+
message: `DOCX \uBC88\uD638\uB9E4\uAE30\uAE30(numbering.xml) \uD30C\uC2F1 \uC2E4\uD328 \u2014 \uBAA9\uB85D \uBC88\uD638 \uC0DD\uB7B5: ${err instanceof Error ? err.message : String(err)}`
|
|
18216
|
+
});
|
|
18163
18217
|
}
|
|
18164
18218
|
}
|
|
18165
18219
|
let footnotes = /* @__PURE__ */ new Map();
|
|
@@ -18167,7 +18221,11 @@ async function parseDocxDocument(buffer, options) {
|
|
|
18167
18221
|
if (fnFile) {
|
|
18168
18222
|
try {
|
|
18169
18223
|
footnotes = parseFootnotes(await fnFile.async("text"));
|
|
18170
|
-
} catch {
|
|
18224
|
+
} catch (err) {
|
|
18225
|
+
warnings.push({
|
|
18226
|
+
code: "PARTIAL_PARSE",
|
|
18227
|
+
message: `DOCX \uAC01\uC8FC(footnotes.xml) \uD30C\uC2F1 \uC2E4\uD328 \u2014 \uAC01\uC8FC \uC0DD\uB7B5: ${err instanceof Error ? err.message : String(err)}`
|
|
18228
|
+
});
|
|
18171
18229
|
}
|
|
18172
18230
|
}
|
|
18173
18231
|
const docXml = await docFile.async("text");
|
|
@@ -18189,7 +18247,7 @@ async function parseDocxDocument(buffer, options) {
|
|
|
18189
18247
|
if (block) blocks.push(block);
|
|
18190
18248
|
}
|
|
18191
18249
|
}
|
|
18192
|
-
const { blocks: imgBlocks, images } = await extractImages(zip, rels, doc);
|
|
18250
|
+
const { blocks: imgBlocks, images } = await extractImages(zip, rels, doc, warnings);
|
|
18193
18251
|
const metadata = {};
|
|
18194
18252
|
const coreFile = zip.file("docProps/core.xml");
|
|
18195
18253
|
if (coreFile) {
|
|
@@ -18207,7 +18265,11 @@ async function parseDocxDocument(buffer, options) {
|
|
|
18207
18265
|
if (created) metadata.createdAt = created;
|
|
18208
18266
|
const modified = getFirst("dcterms:modified");
|
|
18209
18267
|
if (modified) metadata.modifiedAt = modified;
|
|
18210
|
-
} catch {
|
|
18268
|
+
} catch (err) {
|
|
18269
|
+
warnings.push({
|
|
18270
|
+
code: "PARTIAL_PARSE",
|
|
18271
|
+
message: `DOCX \uBA54\uD0C0\uB370\uC774\uD130(core.xml) \uD30C\uC2F1 \uC2E4\uD328: ${err instanceof Error ? err.message : String(err)}`
|
|
18272
|
+
});
|
|
18211
18273
|
}
|
|
18212
18274
|
}
|
|
18213
18275
|
const outline = blocks.filter((b) => b.type === "heading").map((b) => ({ level: b.level ?? 2, text: b.text ?? "" }));
|
|
@@ -18476,6 +18538,45 @@ function countSections(body) {
|
|
|
18476
18538
|
}
|
|
18477
18539
|
|
|
18478
18540
|
// src/form/match.ts
|
|
18541
|
+
var ValueCursor = class {
|
|
18542
|
+
constructor(values) {
|
|
18543
|
+
this.values = values;
|
|
18544
|
+
}
|
|
18545
|
+
nextIdx = /* @__PURE__ */ new Map();
|
|
18546
|
+
keys() {
|
|
18547
|
+
return this.values.keys();
|
|
18548
|
+
}
|
|
18549
|
+
has(key) {
|
|
18550
|
+
return this.values.has(key);
|
|
18551
|
+
}
|
|
18552
|
+
isArray(key) {
|
|
18553
|
+
return Array.isArray(this.values.get(key));
|
|
18554
|
+
}
|
|
18555
|
+
/** 남은 값이 있으면 true (스칼라는 항상 true) */
|
|
18556
|
+
available(key) {
|
|
18557
|
+
const v = this.values.get(key);
|
|
18558
|
+
if (v === void 0) return false;
|
|
18559
|
+
return typeof v === "string" || (this.nextIdx.get(key) ?? 0) < v.length;
|
|
18560
|
+
}
|
|
18561
|
+
/** 현재 값 미리보기 (소진 없음) */
|
|
18562
|
+
peek(key) {
|
|
18563
|
+
const v = this.values.get(key);
|
|
18564
|
+
if (v === void 0) return void 0;
|
|
18565
|
+
if (typeof v === "string") return v;
|
|
18566
|
+
const i = this.nextIdx.get(key) ?? 0;
|
|
18567
|
+
return i < v.length ? v[i] : void 0;
|
|
18568
|
+
}
|
|
18569
|
+
/** 값 소비 — 배열이면 커서 전진, 소진 시 undefined */
|
|
18570
|
+
consume(key) {
|
|
18571
|
+
const v = this.values.get(key);
|
|
18572
|
+
if (v === void 0) return void 0;
|
|
18573
|
+
if (typeof v === "string") return v;
|
|
18574
|
+
const i = this.nextIdx.get(key) ?? 0;
|
|
18575
|
+
if (i >= v.length) return void 0;
|
|
18576
|
+
this.nextIdx.set(key, i + 1);
|
|
18577
|
+
return v[i];
|
|
18578
|
+
}
|
|
18579
|
+
};
|
|
18479
18580
|
function normalizeLabel(label) {
|
|
18480
18581
|
return label.trim().replace(/[::\s()()·]/g, "");
|
|
18481
18582
|
}
|
|
@@ -18514,9 +18615,9 @@ function fillInCellPatterns(cellText, values, matchedLabels) {
|
|
|
18514
18615
|
(match, prefix, suffix) => {
|
|
18515
18616
|
const label = prefix + suffix;
|
|
18516
18617
|
const normalizedLabel = normalizeLabel(label);
|
|
18517
|
-
const matchKey = values.
|
|
18618
|
+
const matchKey = values.available(normalizedLabel) ? normalizedLabel : values.available(normalizeLabel(prefix)) ? normalizeLabel(prefix) : void 0;
|
|
18518
18619
|
if (matchKey === void 0) return match;
|
|
18519
|
-
const newValue = values.
|
|
18620
|
+
const newValue = values.consume(matchKey);
|
|
18520
18621
|
matchedLabels.add(matchKey);
|
|
18521
18622
|
matches.push({ key: matchKey, label, value: newValue });
|
|
18522
18623
|
return `${prefix}(${newValue})${suffix}`;
|
|
@@ -18526,11 +18627,12 @@ function fillInCellPatterns(cellText, values, matchedLabels) {
|
|
|
18526
18627
|
/□([가-힣A-Za-z]+)/g,
|
|
18527
18628
|
(match, keyword) => {
|
|
18528
18629
|
const normalizedKw = normalizeLabel(keyword);
|
|
18529
|
-
const matchKey = values.
|
|
18630
|
+
const matchKey = values.available(normalizedKw) ? normalizedKw : void 0;
|
|
18530
18631
|
if (matchKey === void 0) return match;
|
|
18531
|
-
const val = values.
|
|
18632
|
+
const val = values.peek(matchKey);
|
|
18532
18633
|
const isTruthy = ["\u2611", "\u2713", "\u2714", "v", "V", "true", "1", "yes", "o", "O"].includes(val.trim()) || val.trim() === "";
|
|
18533
18634
|
if (!isTruthy) return match;
|
|
18635
|
+
values.consume(matchKey);
|
|
18534
18636
|
matchedLabels.add(matchKey);
|
|
18535
18637
|
matches.push({ key: matchKey, label: `\u25A1${keyword}`, value: "\u2611" });
|
|
18536
18638
|
return `\u2611${keyword}`;
|
|
@@ -18540,9 +18642,9 @@ function fillInCellPatterns(cellText, values, matchedLabels) {
|
|
|
18540
18642
|
/\(([가-힣A-Za-z]+)[::]\s{1,}\)/g,
|
|
18541
18643
|
(match, keyword) => {
|
|
18542
18644
|
const normalizedKw = normalizeLabel(keyword);
|
|
18543
|
-
const matchKey = values.
|
|
18645
|
+
const matchKey = values.available(normalizedKw) ? normalizedKw : void 0;
|
|
18544
18646
|
if (matchKey === void 0) return match;
|
|
18545
|
-
const newValue = values.
|
|
18647
|
+
const newValue = values.consume(matchKey);
|
|
18546
18648
|
matchedLabels.add(matchKey);
|
|
18547
18649
|
matches.push({ key: matchKey, label: keyword, value: newValue });
|
|
18548
18650
|
return `(${keyword}\uFF1A${newValue})`;
|
|
@@ -18648,14 +18750,66 @@ var LABEL_KEYWORDS = /* @__PURE__ */ new Set([
|
|
|
18648
18750
|
"\uCCAD\uAD6C\uC0AC\uC720",
|
|
18649
18751
|
"\uC18C\uBA85\uC790\uB8CC"
|
|
18650
18752
|
]);
|
|
18753
|
+
var ENGLISH_LABEL_WORDS = /* @__PURE__ */ new Set([
|
|
18754
|
+
"name",
|
|
18755
|
+
"date",
|
|
18756
|
+
"address",
|
|
18757
|
+
"tel",
|
|
18758
|
+
"phone",
|
|
18759
|
+
"mobile",
|
|
18760
|
+
"fax",
|
|
18761
|
+
"email",
|
|
18762
|
+
"e-mail",
|
|
18763
|
+
"dept",
|
|
18764
|
+
"department",
|
|
18765
|
+
"division",
|
|
18766
|
+
"title",
|
|
18767
|
+
"position",
|
|
18768
|
+
"grade",
|
|
18769
|
+
"rank",
|
|
18770
|
+
"birth",
|
|
18771
|
+
"nationality",
|
|
18772
|
+
"sex",
|
|
18773
|
+
"gender",
|
|
18774
|
+
"signature",
|
|
18775
|
+
"sign",
|
|
18776
|
+
"seal",
|
|
18777
|
+
"remarks",
|
|
18778
|
+
"note",
|
|
18779
|
+
"period",
|
|
18780
|
+
"place",
|
|
18781
|
+
"purpose",
|
|
18782
|
+
"reason",
|
|
18783
|
+
"amount",
|
|
18784
|
+
"total",
|
|
18785
|
+
"sum",
|
|
18786
|
+
"qty",
|
|
18787
|
+
"quantity",
|
|
18788
|
+
"unit",
|
|
18789
|
+
"no",
|
|
18790
|
+
"id",
|
|
18791
|
+
"passport"
|
|
18792
|
+
]);
|
|
18793
|
+
var ENGLISH_STOPWORDS = /* @__PURE__ */ new Set(["of", "the", "and", "or", "in"]);
|
|
18794
|
+
var NUMERIC_VALUE_RE = /^제?\d+(?:[.,]\d+)*[십백천만억조]*(?:원|명|건|개|회|부|매|장|점|호|번|년|월|일|시|분|초|개월|주년|차례|퍼센트)?$/;
|
|
18795
|
+
var SENTENCE_ENDING_RE = /(?:입니다|합니다|습니다|하세요|십시오|시오|바랍니다|바람|할 것|할것|하며|하고|한다|된다|됨|음|임)$/;
|
|
18651
18796
|
function isLabelCell(text) {
|
|
18652
18797
|
const trimmed = text.trim().replace(/[¹²³⁴⁵⁶⁷⁸⁹⁰*※]+$/g, "").trim();
|
|
18653
18798
|
if (!trimmed || trimmed.length > 30) return false;
|
|
18654
18799
|
for (const kw of LABEL_KEYWORDS) {
|
|
18655
18800
|
if (trimmed.includes(kw)) return true;
|
|
18656
18801
|
}
|
|
18657
|
-
|
|
18802
|
+
const compact = trimmed.replace(/\s/g, "");
|
|
18803
|
+
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)) {
|
|
18804
|
+
return true;
|
|
18805
|
+
}
|
|
18658
18806
|
if (/^[가-힣A-Za-z\s]+[::]$/.test(trimmed)) return true;
|
|
18807
|
+
if (/^[A-Za-z][A-Za-z\s./&-]*$/.test(trimmed) && trimmed.length <= 20) {
|
|
18808
|
+
const words = trimmed.toLowerCase().split(/[\s/&]+/).filter((w) => w && !ENGLISH_STOPWORDS.has(w));
|
|
18809
|
+
if (words.length >= 1 && words.length <= 3 && words.every((w) => ENGLISH_LABEL_WORDS.has(w.replace(/\.$/, "")))) {
|
|
18810
|
+
return true;
|
|
18811
|
+
}
|
|
18812
|
+
}
|
|
18659
18813
|
return false;
|
|
18660
18814
|
}
|
|
18661
18815
|
function extractFormFields(blocks) {
|
|
@@ -18796,17 +18950,18 @@ function fillFormFields(blocks, values) {
|
|
|
18796
18950
|
const filled = [];
|
|
18797
18951
|
const matchedLabels = /* @__PURE__ */ new Set();
|
|
18798
18952
|
const normalizedValues = normalizeValues(values);
|
|
18953
|
+
const cursor = new ValueCursor(normalizedValues);
|
|
18954
|
+
const allTables = collectIRTables(cloned, 0);
|
|
18799
18955
|
const patternFilledCells = /* @__PURE__ */ new Set();
|
|
18800
|
-
for (const
|
|
18801
|
-
|
|
18802
|
-
|
|
18803
|
-
|
|
18804
|
-
const cell = block.table.cells[r]?.[c];
|
|
18956
|
+
for (const table of allTables) {
|
|
18957
|
+
for (let r = 0; r < table.rows; r++) {
|
|
18958
|
+
for (let c = 0; c < table.cols; c++) {
|
|
18959
|
+
const cell = table.cells[r]?.[c];
|
|
18805
18960
|
if (!cell) continue;
|
|
18806
|
-
const result = fillInCellPatterns(cell.text,
|
|
18961
|
+
const result = fillInCellPatterns(cell.text, cursor, matchedLabels);
|
|
18807
18962
|
if (result) {
|
|
18808
18963
|
cell.text = result.text;
|
|
18809
|
-
patternFilledCells.add(
|
|
18964
|
+
patternFilledCells.add(cell);
|
|
18810
18965
|
for (const m of result.matches) {
|
|
18811
18966
|
filled.push({ label: m.label, value: m.value, row: r, col: c });
|
|
18812
18967
|
}
|
|
@@ -18814,33 +18969,71 @@ function fillFormFields(blocks, values) {
|
|
|
18814
18969
|
}
|
|
18815
18970
|
}
|
|
18816
18971
|
}
|
|
18817
|
-
for (const
|
|
18818
|
-
|
|
18819
|
-
fillTable(block.table, normalizedValues, filled, matchedLabels, patternFilledCells);
|
|
18972
|
+
for (const table of allTables) {
|
|
18973
|
+
fillTable(table, cursor, filled, matchedLabels, patternFilledCells);
|
|
18820
18974
|
}
|
|
18821
18975
|
for (const block of cloned) {
|
|
18822
18976
|
if (block.type !== "paragraph" || !block.text) continue;
|
|
18823
|
-
const newText = fillInlineFields(block.text,
|
|
18977
|
+
const newText = fillInlineFields(block.text, cursor, filled, matchedLabels);
|
|
18824
18978
|
if (newText !== block.text) block.text = newText;
|
|
18825
18979
|
}
|
|
18826
18980
|
const unmatched = resolveUnmatched(normalizedValues, matchedLabels, values);
|
|
18827
18981
|
return { blocks: cloned, filled, unmatched };
|
|
18828
18982
|
}
|
|
18983
|
+
function collectIRTables(blocks, depth) {
|
|
18984
|
+
if (depth > 16) return [];
|
|
18985
|
+
const out = [];
|
|
18986
|
+
for (const block of blocks) {
|
|
18987
|
+
if (block.type !== "table" || !block.table) continue;
|
|
18988
|
+
out.push(block.table);
|
|
18989
|
+
for (const row of block.table.cells) {
|
|
18990
|
+
for (const cell of row) {
|
|
18991
|
+
if (cell?.blocks?.length) out.push(...collectIRTables(cell.blocks, depth + 1));
|
|
18992
|
+
}
|
|
18993
|
+
}
|
|
18994
|
+
}
|
|
18995
|
+
return out;
|
|
18996
|
+
}
|
|
18997
|
+
function coveredPositions(table) {
|
|
18998
|
+
const covered = /* @__PURE__ */ new Set();
|
|
18999
|
+
for (let r = 0; r < table.rows; r++) {
|
|
19000
|
+
for (let c = 0; c < table.cols; c++) {
|
|
19001
|
+
if (covered.has(`${r},${c}`)) continue;
|
|
19002
|
+
const cell = table.cells[r]?.[c];
|
|
19003
|
+
if (!cell) continue;
|
|
19004
|
+
for (let dr = 0; dr < cell.rowSpan; dr++) {
|
|
19005
|
+
for (let dc = 0; dc < cell.colSpan; dc++) {
|
|
19006
|
+
if (dr === 0 && dc === 0) continue;
|
|
19007
|
+
if (r + dr < table.rows && c + dc < table.cols) covered.add(`${r + dr},${c + dc}`);
|
|
19008
|
+
}
|
|
19009
|
+
}
|
|
19010
|
+
c += cell.colSpan - 1;
|
|
19011
|
+
}
|
|
19012
|
+
}
|
|
19013
|
+
return covered;
|
|
19014
|
+
}
|
|
18829
19015
|
function fillTable(table, values, filled, matchedLabels, patternFilledCells) {
|
|
18830
19016
|
if (table.cols < 2) return;
|
|
19017
|
+
const covered = coveredPositions(table);
|
|
18831
19018
|
for (let r = 0; r < table.rows; r++) {
|
|
18832
|
-
for (let c = 0; c < table.cols
|
|
19019
|
+
for (let c = 0; c < table.cols; c++) {
|
|
19020
|
+
if (covered.has(`${r},${c}`)) continue;
|
|
18833
19021
|
const labelCell = table.cells[r][c];
|
|
18834
|
-
|
|
18835
|
-
if (!labelCell || !valueCell) continue;
|
|
19022
|
+
if (!labelCell) continue;
|
|
18836
19023
|
if (!isLabelCell(labelCell.text)) continue;
|
|
19024
|
+
let vc = c + labelCell.colSpan;
|
|
19025
|
+
while (vc < table.cols && covered.has(`${r},${vc}`)) vc++;
|
|
19026
|
+
if (vc >= table.cols) continue;
|
|
19027
|
+
const valueCell = table.cells[r][vc];
|
|
19028
|
+
if (!valueCell) continue;
|
|
18837
19029
|
if (isKeywordLabel(valueCell.text)) continue;
|
|
18838
19030
|
const normalizedCellLabel = normalizeLabel(labelCell.text);
|
|
18839
19031
|
if (!normalizedCellLabel) continue;
|
|
18840
19032
|
const matchKey = findMatchingKey(normalizedCellLabel, values);
|
|
18841
19033
|
if (matchKey === void 0) continue;
|
|
18842
|
-
const newValue = values.
|
|
18843
|
-
if (
|
|
19034
|
+
const newValue = values.consume(matchKey);
|
|
19035
|
+
if (newValue === void 0) continue;
|
|
19036
|
+
if (patternFilledCells?.has(valueCell)) {
|
|
18844
19037
|
valueCell.text = newValue + " " + valueCell.text;
|
|
18845
19038
|
} else {
|
|
18846
19039
|
valueCell.text = newValue;
|
|
@@ -18863,14 +19056,16 @@ function fillTable(table, values, filled, matchedLabels, patternFilledCells) {
|
|
|
18863
19056
|
if (!allLabels) return;
|
|
18864
19057
|
for (let r = 1; r < table.rows; r++) {
|
|
18865
19058
|
for (let c = 0; c < table.cols; c++) {
|
|
19059
|
+
if (covered.has(`${r},${c}`)) continue;
|
|
18866
19060
|
const headerCell = headerRow[c];
|
|
18867
19061
|
const valueCell = table.cells[r]?.[c];
|
|
18868
19062
|
if (!headerCell || !valueCell) continue;
|
|
18869
19063
|
const headerLabel = normalizeLabel(headerCell.text);
|
|
18870
19064
|
const matchKey = findMatchingKey(headerLabel, values);
|
|
18871
19065
|
if (matchKey === void 0) continue;
|
|
18872
|
-
if (matchedLabels.has(matchKey)) continue;
|
|
18873
|
-
const newValue = values.
|
|
19066
|
+
if (!values.isArray(matchKey) && matchedLabels.has(matchKey)) continue;
|
|
19067
|
+
const newValue = values.consume(matchKey);
|
|
19068
|
+
if (newValue === void 0) continue;
|
|
18874
19069
|
valueCell.text = newValue;
|
|
18875
19070
|
matchedLabels.add(matchKey);
|
|
18876
19071
|
filled.push({
|
|
@@ -18891,7 +19086,8 @@ function fillInlineFields(text, values, filled, matchedLabels) {
|
|
|
18891
19086
|
for (const seg of segments) {
|
|
18892
19087
|
const matchKey = findMatchingKey(normalizeLabel(seg.label), values);
|
|
18893
19088
|
if (matchKey === void 0) continue;
|
|
18894
|
-
const newValue = values.
|
|
19089
|
+
const newValue = values.consume(matchKey);
|
|
19090
|
+
if (newValue === void 0) continue;
|
|
18895
19091
|
matchedLabels.add(matchKey);
|
|
18896
19092
|
filled.push({ label: seg.label.trim(), value: newValue, row: -1, col: -1 });
|
|
18897
19093
|
out += text.slice(pos, seg.valueStart);
|
|
@@ -18998,6 +19194,7 @@ function scanSectionXml(xml, sectionIndex) {
|
|
|
18998
19194
|
const paraStack = [];
|
|
18999
19195
|
const tableStack = [];
|
|
19000
19196
|
const rowStack = [];
|
|
19197
|
+
const trStartStack = [];
|
|
19001
19198
|
const cellStack = [];
|
|
19002
19199
|
let pendingT = null;
|
|
19003
19200
|
const ctrlSubStack = [];
|
|
@@ -19062,11 +19259,17 @@ function scanSectionXml(xml, sectionIndex) {
|
|
|
19062
19259
|
} else if (local2 === "tr") {
|
|
19063
19260
|
const row = rowStack[rowStack.length - 1];
|
|
19064
19261
|
const table = tableStack[tableStack.length - 1];
|
|
19065
|
-
if (row && table && row.length > 0)
|
|
19262
|
+
if (row && table && row.length > 0) {
|
|
19263
|
+
table.rows.push(row);
|
|
19264
|
+
const trStart = trStartStack[trStartStack.length - 1];
|
|
19265
|
+
if (trStart >= 0) table.rowRanges.push({ start: trStart, end: m.index + full.length });
|
|
19266
|
+
}
|
|
19066
19267
|
if (rowStack.length > 0) rowStack[rowStack.length - 1] = [];
|
|
19268
|
+
if (trStartStack.length > 0) trStartStack[trStartStack.length - 1] = -1;
|
|
19067
19269
|
} else if (local2 === "tbl") {
|
|
19068
19270
|
const table = tableStack.pop();
|
|
19069
19271
|
rowStack.pop();
|
|
19272
|
+
trStartStack.pop();
|
|
19070
19273
|
if (table) {
|
|
19071
19274
|
finalizeTable(table);
|
|
19072
19275
|
if (!table.topLevel) {
|
|
@@ -19109,6 +19312,7 @@ function scanSectionXml(xml, sectionIndex) {
|
|
|
19109
19312
|
const ra = parseInt(getAttr2(attrsRaw, "rowAddr") || "", 10);
|
|
19110
19313
|
if (!isNaN(ca)) cell.colAddr = ca;
|
|
19111
19314
|
if (!isNaN(ra)) cell.rowAddr = ra;
|
|
19315
|
+
cell.addrTagRange = { start: m.index, end: m.index + full.length };
|
|
19112
19316
|
}
|
|
19113
19317
|
} else if (local === "cellSpan") {
|
|
19114
19318
|
const cell = cellStack[cellStack.length - 1];
|
|
@@ -19159,6 +19363,7 @@ function scanSectionXml(xml, sectionIndex) {
|
|
|
19159
19363
|
start: m.index,
|
|
19160
19364
|
topLevel: false,
|
|
19161
19365
|
rows: [],
|
|
19366
|
+
rowRanges: [],
|
|
19162
19367
|
cellByAnchor: /* @__PURE__ */ new Map()
|
|
19163
19368
|
};
|
|
19164
19369
|
stack.pop();
|
|
@@ -19166,9 +19371,11 @@ function scanSectionXml(xml, sectionIndex) {
|
|
|
19166
19371
|
stack.push({ local, qname, contentStart });
|
|
19167
19372
|
tableStack.push(table);
|
|
19168
19373
|
rowStack.push([]);
|
|
19374
|
+
trStartStack.push(-1);
|
|
19169
19375
|
if (table.topLevel) tables.push(table);
|
|
19170
19376
|
} else if (local === "tr") {
|
|
19171
19377
|
if (rowStack.length > 0) rowStack[rowStack.length - 1] = [];
|
|
19378
|
+
if (trStartStack.length > 0) trStartStack[trStartStack.length - 1] = m.index;
|
|
19172
19379
|
} else if (local === "tc") {
|
|
19173
19380
|
cellStack.push({ colSpan: 1, rowSpan: 1, paragraphs: [], tables: [] });
|
|
19174
19381
|
} else if (local === "cellAddr" || local === "cellSpan") {
|
|
@@ -19179,6 +19386,7 @@ function scanSectionXml(xml, sectionIndex) {
|
|
|
19179
19386
|
const ra = parseInt(getAttr2(attrsRaw, "rowAddr") || "", 10);
|
|
19180
19387
|
if (!isNaN(ca)) cell.colAddr = ca;
|
|
19181
19388
|
if (!isNaN(ra)) cell.rowAddr = ra;
|
|
19389
|
+
cell.addrTagRange = { start: m.index, end: contentStart };
|
|
19182
19390
|
} else {
|
|
19183
19391
|
const cs = parseInt(getAttr2(attrsRaw, "colSpan") || "1", 10);
|
|
19184
19392
|
const rs = parseInt(getAttr2(attrsRaw, "rowSpan") || "1", 10);
|
|
@@ -19571,6 +19779,7 @@ async function fillHwpx(hwpxBuffer, values) {
|
|
|
19571
19779
|
throw new KordocError("HWPX\uC5D0\uC11C \uC139\uC158 \uD30C\uC77C\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4");
|
|
19572
19780
|
}
|
|
19573
19781
|
const normalizedValues = normalizeValues(values);
|
|
19782
|
+
const cursor = new ValueCursor(normalizedValues);
|
|
19574
19783
|
const matchedLabels = /* @__PURE__ */ new Set();
|
|
19575
19784
|
const filled = [];
|
|
19576
19785
|
const failedKeys = /* @__PURE__ */ new Set();
|
|
@@ -19606,7 +19815,7 @@ async function fillHwpx(hwpxBuffer, values) {
|
|
|
19606
19815
|
for (const cell of row) {
|
|
19607
19816
|
for (const para of cell.paragraphs) {
|
|
19608
19817
|
const text = matchText(para);
|
|
19609
|
-
const result = fillInCellPatterns(text,
|
|
19818
|
+
const result = fillInCellPatterns(text, cursor, matchedLabels);
|
|
19610
19819
|
if (!result) continue;
|
|
19611
19820
|
const l = led(para);
|
|
19612
19821
|
if (l.fullText !== void 0) continue;
|
|
@@ -19640,21 +19849,30 @@ async function fillHwpx(hwpxBuffer, values) {
|
|
|
19640
19849
|
if (isKeywordLabel(cellLabelText(valueCell))) continue;
|
|
19641
19850
|
const normalizedCellLabel = normalizeLabel(labelText);
|
|
19642
19851
|
if (!normalizedCellLabel) continue;
|
|
19643
|
-
const matchKey = findMatchingKey(normalizedCellLabel,
|
|
19852
|
+
const matchKey = findMatchingKey(normalizedCellLabel, cursor);
|
|
19644
19853
|
if (matchKey === void 0) continue;
|
|
19645
|
-
const newValue = normalizedValues.get(matchKey);
|
|
19646
19854
|
if (patternApplied.has(valueCell)) {
|
|
19647
19855
|
const target = valueCell.paragraphs.find((p) => p.tRanges.length > 0) ?? valueCell.paragraphs[0];
|
|
19648
19856
|
if (!target) continue;
|
|
19649
19857
|
const l = led(target);
|
|
19650
|
-
if (l.fullText
|
|
19651
|
-
|
|
19652
|
-
|
|
19653
|
-
|
|
19654
|
-
|
|
19858
|
+
if (l.fullText !== void 0) continue;
|
|
19859
|
+
const newValue = cursor.consume(matchKey);
|
|
19860
|
+
if (newValue === void 0) continue;
|
|
19861
|
+
l.ranges.push({ start: 0, end: 0, replacement: newValue + " " });
|
|
19862
|
+
l.filledIdx.push(filled.length);
|
|
19863
|
+
l.matchKeys.push(matchKey);
|
|
19864
|
+
matchedLabels.add(matchKey);
|
|
19865
|
+
filled.push({
|
|
19866
|
+
label: labelText.trim().replace(/[::]\s*$/, ""),
|
|
19867
|
+
value: newValue,
|
|
19868
|
+
row: rowIdx,
|
|
19869
|
+
col: colIdx
|
|
19870
|
+
});
|
|
19655
19871
|
} else {
|
|
19656
19872
|
const paras = valueCell.paragraphs;
|
|
19657
19873
|
if (paras.length === 0) continue;
|
|
19874
|
+
const newValue = cursor.consume(matchKey);
|
|
19875
|
+
if (newValue === void 0) continue;
|
|
19658
19876
|
const l0 = led(paras[0]);
|
|
19659
19877
|
l0.fullText = newValue;
|
|
19660
19878
|
l0.ranges = [];
|
|
@@ -19665,14 +19883,14 @@ async function fillHwpx(hwpxBuffer, values) {
|
|
|
19665
19883
|
lk.fullText = "";
|
|
19666
19884
|
lk.ranges = [];
|
|
19667
19885
|
}
|
|
19886
|
+
matchedLabels.add(matchKey);
|
|
19887
|
+
filled.push({
|
|
19888
|
+
label: labelText.trim().replace(/[::]\s*$/, ""),
|
|
19889
|
+
value: newValue,
|
|
19890
|
+
row: rowIdx,
|
|
19891
|
+
col: colIdx
|
|
19892
|
+
});
|
|
19668
19893
|
}
|
|
19669
|
-
matchedLabels.add(matchKey);
|
|
19670
|
-
filled.push({
|
|
19671
|
-
label: labelText.trim().replace(/[::]\s*$/, ""),
|
|
19672
|
-
value: newValue,
|
|
19673
|
-
row: rowIdx,
|
|
19674
|
-
col: colIdx
|
|
19675
|
-
});
|
|
19676
19894
|
}
|
|
19677
19895
|
}
|
|
19678
19896
|
if (table.rows.length >= 2) {
|
|
@@ -19686,10 +19904,11 @@ async function fillHwpx(hwpxBuffer, values) {
|
|
|
19686
19904
|
const dataCells = table.rows[rowIdx];
|
|
19687
19905
|
for (let colIdx = 0; colIdx < Math.min(headerCells.length, dataCells.length); colIdx++) {
|
|
19688
19906
|
const headerLabel = normalizeLabel(cellLabelText(headerCells[colIdx]));
|
|
19689
|
-
const matchKey = findMatchingKey(headerLabel,
|
|
19907
|
+
const matchKey = findMatchingKey(headerLabel, cursor);
|
|
19690
19908
|
if (matchKey === void 0) continue;
|
|
19691
|
-
if (matchedLabels.has(matchKey)) continue;
|
|
19692
|
-
const newValue =
|
|
19909
|
+
if (!cursor.isArray(matchKey) && matchedLabels.has(matchKey)) continue;
|
|
19910
|
+
const newValue = cursor.consume(matchKey);
|
|
19911
|
+
if (newValue === void 0) continue;
|
|
19693
19912
|
const paras = dataCells[colIdx].paragraphs;
|
|
19694
19913
|
if (paras.length === 0) continue;
|
|
19695
19914
|
const l0 = led(paras[0]);
|
|
@@ -19719,9 +19938,10 @@ async function fillHwpx(hwpxBuffer, values) {
|
|
|
19719
19938
|
if (existing?.fullText !== void 0) continue;
|
|
19720
19939
|
const text = matchText(para);
|
|
19721
19940
|
for (const seg of scanInlineSegments(text)) {
|
|
19722
|
-
const matchKey = findMatchingKey(normalizeLabel(seg.label),
|
|
19941
|
+
const matchKey = findMatchingKey(normalizeLabel(seg.label), cursor);
|
|
19723
19942
|
if (matchKey === void 0) continue;
|
|
19724
|
-
const newValue =
|
|
19943
|
+
const newValue = cursor.consume(matchKey);
|
|
19944
|
+
if (newValue === void 0) continue;
|
|
19725
19945
|
const replacement = seg.valueStart === seg.valueEnd ? padInsertion(text, seg.valueStart, newValue) : newValue;
|
|
19726
19946
|
const l = led(para);
|
|
19727
19947
|
l.ranges.push({ start: seg.valueStart, end: seg.valueEnd, replacement });
|
|
@@ -19776,6 +19996,7 @@ async function fillHwpx(hwpxBuffer, values) {
|
|
|
19776
19996
|
splices.push(...paraSplices);
|
|
19777
19997
|
}
|
|
19778
19998
|
if (splices.length > 0) {
|
|
19999
|
+
splices.push(...allLinesegRemovalSplices(xml));
|
|
19779
20000
|
replacements.set(sectionPaths[si], encoder.encode(applySplices(xml, splices)));
|
|
19780
20001
|
}
|
|
19781
20002
|
}
|
|
@@ -19795,6 +20016,245 @@ async function fillHwpx(hwpxBuffer, values) {
|
|
|
19795
20016
|
// src/hwpx/generator.ts
|
|
19796
20017
|
import JSZip5 from "jszip";
|
|
19797
20018
|
|
|
20019
|
+
// src/hwpx/text-metrics.ts
|
|
20020
|
+
var ASCII_W = [
|
|
20021
|
+
300,
|
|
20022
|
+
320,
|
|
20023
|
+
320,
|
|
20024
|
+
610,
|
|
20025
|
+
610,
|
|
20026
|
+
830,
|
|
20027
|
+
724,
|
|
20028
|
+
320,
|
|
20029
|
+
320,
|
|
20030
|
+
320,
|
|
20031
|
+
550,
|
|
20032
|
+
550,
|
|
20033
|
+
320,
|
|
20034
|
+
550,
|
|
20035
|
+
320,
|
|
20036
|
+
550,
|
|
20037
|
+
// 0x20-0x2F
|
|
20038
|
+
550,
|
|
20039
|
+
550,
|
|
20040
|
+
550,
|
|
20041
|
+
550,
|
|
20042
|
+
550,
|
|
20043
|
+
550,
|
|
20044
|
+
550,
|
|
20045
|
+
550,
|
|
20046
|
+
550,
|
|
20047
|
+
550,
|
|
20048
|
+
320,
|
|
20049
|
+
320,
|
|
20050
|
+
550,
|
|
20051
|
+
550,
|
|
20052
|
+
550,
|
|
20053
|
+
550,
|
|
20054
|
+
// 0x30-0x3F
|
|
20055
|
+
830,
|
|
20056
|
+
706,
|
|
20057
|
+
605,
|
|
20058
|
+
685,
|
|
20059
|
+
719,
|
|
20060
|
+
627,
|
|
20061
|
+
617,
|
|
20062
|
+
683,
|
|
20063
|
+
734,
|
|
20064
|
+
305,
|
|
20065
|
+
315,
|
|
20066
|
+
660,
|
|
20067
|
+
605,
|
|
20068
|
+
839,
|
|
20069
|
+
734,
|
|
20070
|
+
732,
|
|
20071
|
+
// 0x40-0x4F
|
|
20072
|
+
603,
|
|
20073
|
+
705,
|
|
20074
|
+
660,
|
|
20075
|
+
627,
|
|
20076
|
+
664,
|
|
20077
|
+
731,
|
|
20078
|
+
706,
|
|
20079
|
+
910,
|
|
20080
|
+
705,
|
|
20081
|
+
705,
|
|
20082
|
+
626,
|
|
20083
|
+
320,
|
|
20084
|
+
550,
|
|
20085
|
+
320,
|
|
20086
|
+
550,
|
|
20087
|
+
550,
|
|
20088
|
+
// 0x50-0x5F
|
|
20089
|
+
320,
|
|
20090
|
+
569,
|
|
20091
|
+
597,
|
|
20092
|
+
552,
|
|
20093
|
+
597,
|
|
20094
|
+
536,
|
|
20095
|
+
356,
|
|
20096
|
+
562,
|
|
20097
|
+
635,
|
|
20098
|
+
287,
|
|
20099
|
+
288,
|
|
20100
|
+
582,
|
|
20101
|
+
287,
|
|
20102
|
+
907,
|
|
20103
|
+
635,
|
|
20104
|
+
588,
|
|
20105
|
+
// 0x60-0x6F
|
|
20106
|
+
597,
|
|
20107
|
+
579,
|
|
20108
|
+
478,
|
|
20109
|
+
496,
|
|
20110
|
+
356,
|
|
20111
|
+
635,
|
|
20112
|
+
563,
|
|
20113
|
+
720,
|
|
20114
|
+
542,
|
|
20115
|
+
543,
|
|
20116
|
+
486,
|
|
20117
|
+
320,
|
|
20118
|
+
320,
|
|
20119
|
+
320,
|
|
20120
|
+
550,
|
|
20121
|
+
0
|
|
20122
|
+
// 0x70-0x7E(+DEL)
|
|
20123
|
+
];
|
|
20124
|
+
var SYM_W = {
|
|
20125
|
+
160: 300,
|
|
20126
|
+
163: 568,
|
|
20127
|
+
165: 707,
|
|
20128
|
+
167: 498,
|
|
20129
|
+
171: 440,
|
|
20130
|
+
172: 564,
|
|
20131
|
+
176: 291,
|
|
20132
|
+
177: 798,
|
|
20133
|
+
182: 606,
|
|
20134
|
+
183: 320,
|
|
20135
|
+
187: 440,
|
|
20136
|
+
215: 617,
|
|
20137
|
+
247: 678,
|
|
20138
|
+
8211: 625,
|
|
20139
|
+
8212: 875,
|
|
20140
|
+
8213: 875,
|
|
20141
|
+
8216: 320,
|
|
20142
|
+
8217: 320,
|
|
20143
|
+
8220: 480,
|
|
20144
|
+
8221: 480,
|
|
20145
|
+
8224: 558,
|
|
20146
|
+
8225: 438,
|
|
20147
|
+
8229: 640,
|
|
20148
|
+
8230: 960,
|
|
20149
|
+
8240: 988,
|
|
20150
|
+
8242: 335,
|
|
20151
|
+
8243: 474,
|
|
20152
|
+
8251: 770,
|
|
20153
|
+
8364: 656,
|
|
20154
|
+
9756: 1012,
|
|
20155
|
+
9758: 1012
|
|
20156
|
+
};
|
|
20157
|
+
function charWidthEm1000(cp) {
|
|
20158
|
+
if (cp >= 32 && cp <= 126) return ASCII_W[cp - 32];
|
|
20159
|
+
const sym = SYM_W[cp];
|
|
20160
|
+
if (sym !== void 0) return sym;
|
|
20161
|
+
if (cp >= 44032 && cp <= 55203) return 970;
|
|
20162
|
+
if (cp >= 4352 && cp <= 4607) return 970;
|
|
20163
|
+
if (cp >= 12593 && cp <= 12686) return 970;
|
|
20164
|
+
if (cp >= 19968 && cp <= 40959 || cp >= 63744 && cp <= 64255) return 1e3;
|
|
20165
|
+
if (cp >= 12296 && cp <= 12305 || cp >= 12308 && cp <= 12315) return 500;
|
|
20166
|
+
if (cp === 12288) return 970;
|
|
20167
|
+
if (cp >= 8592 && cp <= 8959) return 970;
|
|
20168
|
+
if (cp >= 9312 && cp <= 9471) return 970;
|
|
20169
|
+
if (cp >= 9632 && cp <= 9983) return 970;
|
|
20170
|
+
if (cp >= 12800 && cp <= 13311) return 970;
|
|
20171
|
+
if (cp >= 65281 && cp <= 65376) return 970;
|
|
20172
|
+
return cp >= 11904 ? 970 : 550;
|
|
20173
|
+
}
|
|
20174
|
+
var SPACE_EM_FIXED = 500;
|
|
20175
|
+
var SPACE_EM_FONT = 300;
|
|
20176
|
+
function measureTextWidth(text, height, ratioPct, opts) {
|
|
20177
|
+
const spaceEm = opts?.spaceEm ?? SPACE_EM_FIXED;
|
|
20178
|
+
const spacing = opts?.spacingPct ?? 0;
|
|
20179
|
+
let em = 0;
|
|
20180
|
+
for (const ch of text) {
|
|
20181
|
+
const cp = ch.codePointAt(0);
|
|
20182
|
+
const w = cp === 32 ? spaceEm : charWidthEm1000(cp);
|
|
20183
|
+
em += w * (1 + spacing / 100);
|
|
20184
|
+
}
|
|
20185
|
+
return em / 1e3 * height * (ratioPct / 100);
|
|
20186
|
+
}
|
|
20187
|
+
var FORBID_START = /* @__PURE__ */ new Set([..."!%),.:;?]}\xA2\xB0\u2032\u2033\u2103\u3009\u300B\u300D\u300F\u3011\u3015!%),.:;?]}\u20A9~\u2026\xB7\u3001\u3002\u3003"]);
|
|
20188
|
+
var FORBID_END = /* @__PURE__ */ new Set([..."$([{\xA3\xA5\u3008\u300A\u300C\u300E\u3010\u3014$([{\u20A9"]);
|
|
20189
|
+
function simulateWrap(text, firstWidth, contWidth, height, ratioPct, mode = "keep", opts) {
|
|
20190
|
+
const EPS = 0.5;
|
|
20191
|
+
const spaceEm = opts?.spaceEm ?? SPACE_EM_FIXED;
|
|
20192
|
+
const spacing = opts?.spacingPct ?? 0;
|
|
20193
|
+
const k = height * ratioPct / 100 / 1e3;
|
|
20194
|
+
const cwCp = (cp) => (cp === 32 ? spaceEm : charWidthEm1000(cp)) * (1 + spacing / 100) * k;
|
|
20195
|
+
const charW = (ch) => cwCp(ch.codePointAt(0));
|
|
20196
|
+
const rangeW = (from, to) => {
|
|
20197
|
+
let w = 0;
|
|
20198
|
+
for (const ch of text.slice(from, to)) w += charW(ch);
|
|
20199
|
+
return w;
|
|
20200
|
+
};
|
|
20201
|
+
const units = text.match(mode === "keep" ? / +|[^ ]+/g : / +|[^ ]/g) ?? [];
|
|
20202
|
+
const starts = [0];
|
|
20203
|
+
let lineW = 0;
|
|
20204
|
+
let avail = firstWidth;
|
|
20205
|
+
let pos = 0;
|
|
20206
|
+
const lineStart = () => starts[starts.length - 1];
|
|
20207
|
+
const breakBefore = (unitPos, w) => {
|
|
20208
|
+
let bp = unitPos;
|
|
20209
|
+
const u = text[unitPos];
|
|
20210
|
+
if (u !== void 0 && FORBID_START.has(u) && bp - 1 > lineStart() && text[bp - 1] !== " ") bp--;
|
|
20211
|
+
while (bp - 1 > lineStart() && FORBID_END.has(text[bp - 1])) bp--;
|
|
20212
|
+
if (bp <= lineStart()) bp = unitPos;
|
|
20213
|
+
starts.push(bp);
|
|
20214
|
+
avail = contWidth;
|
|
20215
|
+
lineW = rangeW(bp, unitPos) + w;
|
|
20216
|
+
};
|
|
20217
|
+
for (const u of units) {
|
|
20218
|
+
if (u[0] === " ") {
|
|
20219
|
+
lineW += charW(" ") * u.length;
|
|
20220
|
+
pos += u.length;
|
|
20221
|
+
continue;
|
|
20222
|
+
}
|
|
20223
|
+
const w = rangeW(pos, pos + u.length);
|
|
20224
|
+
if (lineW + w <= avail + EPS) {
|
|
20225
|
+
lineW += w;
|
|
20226
|
+
pos += u.length;
|
|
20227
|
+
continue;
|
|
20228
|
+
}
|
|
20229
|
+
if (lineW === 0 || w > contWidth + EPS) {
|
|
20230
|
+
let sub = 0;
|
|
20231
|
+
for (const ch of u) {
|
|
20232
|
+
const c = charW(ch);
|
|
20233
|
+
if (lineW + c > avail + EPS && lineW > 0) breakBefore(pos + sub, 0);
|
|
20234
|
+
lineW += c;
|
|
20235
|
+
sub += ch.length;
|
|
20236
|
+
}
|
|
20237
|
+
pos += u.length;
|
|
20238
|
+
continue;
|
|
20239
|
+
}
|
|
20240
|
+
breakBefore(pos, w);
|
|
20241
|
+
pos += u.length;
|
|
20242
|
+
}
|
|
20243
|
+
return { lines: starts.length, starts, lastLineWidth: lineW };
|
|
20244
|
+
}
|
|
20245
|
+
function simulateWrapKeepWord(text, firstWidth, contWidth, height, ratioPct, opts) {
|
|
20246
|
+
return simulateWrap(text, firstWidth, contWidth, height, ratioPct, "keep", opts);
|
|
20247
|
+
}
|
|
20248
|
+
function fitRatioForFewerLines(text, firstWidth, contWidth, height, baseRatio, minRatio, opts) {
|
|
20249
|
+
const base = simulateWrap(text, firstWidth, contWidth, height, baseRatio, "keep", opts);
|
|
20250
|
+
if (base.lines < 2) return null;
|
|
20251
|
+
for (let r = baseRatio - 1; r >= minRatio; r--) {
|
|
20252
|
+
const sim = simulateWrap(text, firstWidth, contWidth, height, r, "keep", opts);
|
|
20253
|
+
if (sim.lines < base.lines) return r;
|
|
20254
|
+
}
|
|
20255
|
+
return null;
|
|
20256
|
+
}
|
|
20257
|
+
|
|
19798
20258
|
// src/hwpx/gongmun.ts
|
|
19799
20259
|
var OFFICIAL_MARGINS = { top: 20, bottom: 10, left: 20, right: 20 };
|
|
19800
20260
|
var PRESET_DEFAULTS = {
|
|
@@ -19830,6 +20290,7 @@ function resolveGongmun(opts) {
|
|
|
19830
20290
|
const preset = normalizeGongmunPreset(opts.preset);
|
|
19831
20291
|
const d = PRESET_DEFAULTS[preset];
|
|
19832
20292
|
const bodyPt = opts.bodyPt ?? d.bodyPt;
|
|
20293
|
+
const autoFitMinRatio = opts.autoFit === false ? null : typeof opts.autoFit === "object" ? Math.min(Math.max(opts.autoFit.minRatio ?? 90, 50), 99) : 90;
|
|
19833
20294
|
return {
|
|
19834
20295
|
preset,
|
|
19835
20296
|
bodyFont: opts.bodyFont ?? "myeongjo",
|
|
@@ -19837,7 +20298,8 @@ function resolveGongmun(opts) {
|
|
|
19837
20298
|
lineSpacing: opts.lineSpacing ?? d.lineSpacing,
|
|
19838
20299
|
numbering: opts.numbering ?? d.numbering,
|
|
19839
20300
|
margins: opts.margins ?? OFFICIAL_MARGINS,
|
|
19840
|
-
centerTitle: opts.centerTitle ?? true
|
|
20301
|
+
centerTitle: opts.centerTitle ?? true,
|
|
20302
|
+
autoFitMinRatio
|
|
19841
20303
|
};
|
|
19842
20304
|
}
|
|
19843
20305
|
var HANGUL_INITIALS = [0, 2, 3, 5, 6, 7, 9, 11, 12, 14, 15, 16, 17, 18];
|
|
@@ -19880,17 +20342,10 @@ function standardMarker(depth, n) {
|
|
|
19880
20342
|
function reportMarker(depth) {
|
|
19881
20343
|
return REPORT_BULLETS[Math.min(depth, REPORT_BULLETS.length - 1)];
|
|
19882
20344
|
}
|
|
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
20345
|
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);
|
|
20346
|
+
let em = SPACE_EM_FIXED;
|
|
20347
|
+
for (const c of marker) em += charWidthEm1000(c.codePointAt(0));
|
|
20348
|
+
return Math.round(em / 1e3 * bodyHeight);
|
|
19894
20349
|
}
|
|
19895
20350
|
function levelIndent(depth, bodyHeight, numbering) {
|
|
19896
20351
|
const marker = numbering === "report" ? reportMarker(depth) : standardMarker(depth, 0);
|
|
@@ -19930,77 +20385,53 @@ function mmToHwpunit(mm) {
|
|
|
19930
20385
|
return Math.round(mm * 7200 / 25.4);
|
|
19931
20386
|
}
|
|
19932
20387
|
|
|
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
|
-
};
|
|
20388
|
+
// src/diff/text-diff.ts
|
|
20389
|
+
function similarity(a, b) {
|
|
20390
|
+
if (a === b) return 1;
|
|
20391
|
+
if (!a || !b) return 0;
|
|
20392
|
+
const maxLen = Math.max(a.length, b.length);
|
|
20393
|
+
if (maxLen === 0) return 1;
|
|
20394
|
+
return 1 - levenshtein(a, b) / maxLen;
|
|
19974
20395
|
}
|
|
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" });
|
|
20396
|
+
function normalizedSimilarity(a, b) {
|
|
20397
|
+
return similarity(normalize(a), normalize(b));
|
|
19988
20398
|
}
|
|
19989
|
-
function
|
|
19990
|
-
|
|
19991
|
-
|
|
19992
|
-
|
|
19993
|
-
|
|
19994
|
-
|
|
19995
|
-
|
|
19996
|
-
|
|
19997
|
-
if (
|
|
20399
|
+
function normalize(s) {
|
|
20400
|
+
return s.replace(/\s+/g, " ").trim();
|
|
20401
|
+
}
|
|
20402
|
+
var MAX_LEVENSHTEIN_LEN = 1e4;
|
|
20403
|
+
function levenshtein(a, b) {
|
|
20404
|
+
if (a.length + b.length > MAX_LEVENSHTEIN_LEN) {
|
|
20405
|
+
const sampleLen = Math.min(500, a.length, b.length);
|
|
20406
|
+
let diffs = 0;
|
|
20407
|
+
for (let i = 0; i < sampleLen; i++) if (a[i] !== b[i]) diffs++;
|
|
20408
|
+
const sampleRate = sampleLen > 0 ? diffs / sampleLen : 1;
|
|
20409
|
+
return Math.abs(a.length - b.length) + Math.round(Math.min(a.length, b.length) * sampleRate);
|
|
19998
20410
|
}
|
|
19999
|
-
|
|
20411
|
+
if (a.length > b.length) [a, b] = [b, a];
|
|
20412
|
+
const m = a.length;
|
|
20413
|
+
const n = b.length;
|
|
20414
|
+
let prev = Array.from({ length: m + 1 }, (_, i) => i);
|
|
20415
|
+
let curr = new Array(m + 1);
|
|
20416
|
+
for (let j = 1; j <= n; j++) {
|
|
20417
|
+
curr[0] = j;
|
|
20418
|
+
for (let i = 1; i <= m; i++) {
|
|
20419
|
+
if (a[i - 1] === b[j - 1]) {
|
|
20420
|
+
curr[i] = prev[i - 1];
|
|
20421
|
+
} else {
|
|
20422
|
+
curr[i] = 1 + Math.min(prev[i - 1], prev[i], curr[i - 1]);
|
|
20423
|
+
}
|
|
20424
|
+
}
|
|
20425
|
+
;
|
|
20426
|
+
[prev, curr] = [curr, prev];
|
|
20427
|
+
}
|
|
20428
|
+
return prev[m];
|
|
20000
20429
|
}
|
|
20001
|
-
|
|
20430
|
+
|
|
20431
|
+
// src/roundtrip/markdown-units.ts
|
|
20432
|
+
function splitMarkdownUnits(md2) {
|
|
20002
20433
|
const lines = md2.split("\n");
|
|
20003
|
-
const
|
|
20434
|
+
const units = [];
|
|
20004
20435
|
let i = 0;
|
|
20005
20436
|
while (i < lines.length) {
|
|
20006
20437
|
const line = lines[i];
|
|
@@ -20008,21 +20439,464 @@ function parseMarkdownToBlocks(md2) {
|
|
|
20008
20439
|
i++;
|
|
20009
20440
|
continue;
|
|
20010
20441
|
}
|
|
20011
|
-
|
|
20012
|
-
|
|
20013
|
-
|
|
20014
|
-
|
|
20015
|
-
|
|
20016
|
-
|
|
20017
|
-
|
|
20018
|
-
|
|
20442
|
+
if (line.trim().startsWith("<table>")) {
|
|
20443
|
+
const collected2 = [];
|
|
20444
|
+
let depth = 0;
|
|
20445
|
+
while (i < lines.length) {
|
|
20446
|
+
const l = lines[i];
|
|
20447
|
+
collected2.push(l);
|
|
20448
|
+
depth += (l.match(/<table>/g) || []).length;
|
|
20449
|
+
depth -= (l.match(/<\/table>/g) || []).length;
|
|
20019
20450
|
i++;
|
|
20451
|
+
if (depth <= 0) break;
|
|
20020
20452
|
}
|
|
20021
|
-
|
|
20022
|
-
blocks.push({ type: "code_block", text: codeLines.join("\n"), lang });
|
|
20453
|
+
units.push({ kind: "html-table", raw: collected2.join("\n"), lines: collected2 });
|
|
20023
20454
|
continue;
|
|
20024
20455
|
}
|
|
20025
|
-
if (
|
|
20456
|
+
if (line.trimStart().startsWith("|")) {
|
|
20457
|
+
const collected2 = [];
|
|
20458
|
+
while (i < lines.length && lines[i].trimStart().startsWith("|")) {
|
|
20459
|
+
collected2.push(lines[i]);
|
|
20460
|
+
i++;
|
|
20461
|
+
}
|
|
20462
|
+
units.push({ kind: "gfm-table", raw: collected2.join("\n"), lines: collected2 });
|
|
20463
|
+
continue;
|
|
20464
|
+
}
|
|
20465
|
+
if (/^-{3,}\s*$/.test(line.trim())) {
|
|
20466
|
+
units.push({ kind: "separator", raw: line.trim(), lines: [line.trim()] });
|
|
20467
|
+
i++;
|
|
20468
|
+
continue;
|
|
20469
|
+
}
|
|
20470
|
+
if (/^!\[image\]\([^)]*\)\s*$/.test(line.trim())) {
|
|
20471
|
+
units.push({ kind: "image", raw: line.trim(), lines: [line.trim()] });
|
|
20472
|
+
i++;
|
|
20473
|
+
continue;
|
|
20474
|
+
}
|
|
20475
|
+
const collected = [];
|
|
20476
|
+
while (i < lines.length && lines[i].trim() && !lines[i].trimStart().startsWith("|") && !lines[i].trim().startsWith("<table>")) {
|
|
20477
|
+
collected.push(lines[i].trim());
|
|
20478
|
+
i++;
|
|
20479
|
+
}
|
|
20480
|
+
units.push({ kind: "text", raw: collected.join("\n"), lines: collected });
|
|
20481
|
+
}
|
|
20482
|
+
return units;
|
|
20483
|
+
}
|
|
20484
|
+
function alignUnits(a, b) {
|
|
20485
|
+
const m = a.length, n = b.length;
|
|
20486
|
+
if (m * n > 4e6) {
|
|
20487
|
+
const result2 = [];
|
|
20488
|
+
let pre = 0;
|
|
20489
|
+
while (pre < m && pre < n && a[pre] === b[pre]) {
|
|
20490
|
+
result2.push([pre, pre]);
|
|
20491
|
+
pre++;
|
|
20492
|
+
}
|
|
20493
|
+
let suf = 0;
|
|
20494
|
+
while (suf < m - pre && suf < n - pre && a[m - 1 - suf] === b[n - 1 - suf]) suf++;
|
|
20495
|
+
const aMid = m - pre - suf, bMid = n - pre - suf;
|
|
20496
|
+
if (aMid === bMid) {
|
|
20497
|
+
for (let i2 = 0; i2 < aMid; i2++) result2.push([pre + i2, pre + i2]);
|
|
20498
|
+
} else {
|
|
20499
|
+
for (let i2 = 0; i2 < aMid; i2++) result2.push([pre + i2, null]);
|
|
20500
|
+
for (let j2 = 0; j2 < bMid; j2++) result2.push([null, pre + j2]);
|
|
20501
|
+
}
|
|
20502
|
+
for (let s = suf - 1; s >= 0; s--) result2.push([m - 1 - s, n - 1 - s]);
|
|
20503
|
+
return result2;
|
|
20504
|
+
}
|
|
20505
|
+
const dp = Array.from({ length: m + 1 }, () => new Int32Array(n + 1));
|
|
20506
|
+
for (let i2 = 1; i2 <= m; i2++) {
|
|
20507
|
+
for (let j2 = 1; j2 <= n; j2++) {
|
|
20508
|
+
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]);
|
|
20509
|
+
}
|
|
20510
|
+
}
|
|
20511
|
+
const matches = [];
|
|
20512
|
+
let i = m, j = n;
|
|
20513
|
+
while (i > 0 && j > 0) {
|
|
20514
|
+
if (a[i - 1] === b[j - 1] && dp[i][j] === dp[i - 1][j - 1] + 1) {
|
|
20515
|
+
matches.push([i - 1, j - 1]);
|
|
20516
|
+
i--;
|
|
20517
|
+
j--;
|
|
20518
|
+
} else if (dp[i - 1][j] >= dp[i][j - 1]) i--;
|
|
20519
|
+
else j--;
|
|
20520
|
+
}
|
|
20521
|
+
matches.reverse();
|
|
20522
|
+
const result = [];
|
|
20523
|
+
let ai = 0, bi = 0;
|
|
20524
|
+
const flushGap = (aEnd, bEnd) => {
|
|
20525
|
+
if (aEnd - ai === bEnd - bi) {
|
|
20526
|
+
while (ai < aEnd) result.push([ai++, bi++]);
|
|
20527
|
+
return;
|
|
20528
|
+
}
|
|
20529
|
+
while (ai < aEnd && bi < bEnd) {
|
|
20530
|
+
const sim = normalizedSimilarity(a[ai], b[bi]);
|
|
20531
|
+
if (sim >= 0.4) {
|
|
20532
|
+
if (aEnd - ai > bEnd - bi && bestSimInRange(a, ai + 1, ai + (aEnd - ai) - (bEnd - bi), b[bi]) > sim) {
|
|
20533
|
+
result.push([ai++, null]);
|
|
20534
|
+
} else if (bEnd - bi > aEnd - ai && bestSimInRange(b, bi + 1, bi + (bEnd - bi) - (aEnd - ai), a[ai]) > sim) {
|
|
20535
|
+
result.push([null, bi++]);
|
|
20536
|
+
} else {
|
|
20537
|
+
result.push([ai++, bi++]);
|
|
20538
|
+
}
|
|
20539
|
+
} else if (aEnd - ai >= bEnd - bi) result.push([ai++, null]);
|
|
20540
|
+
else result.push([null, bi++]);
|
|
20541
|
+
}
|
|
20542
|
+
while (ai < aEnd) result.push([ai++, null]);
|
|
20543
|
+
while (bi < bEnd) result.push([null, bi++]);
|
|
20544
|
+
};
|
|
20545
|
+
for (const [pi, pj] of matches) {
|
|
20546
|
+
flushGap(pi, pj);
|
|
20547
|
+
result.push([ai++, bi++]);
|
|
20548
|
+
}
|
|
20549
|
+
flushGap(m, n);
|
|
20550
|
+
return result;
|
|
20551
|
+
}
|
|
20552
|
+
function bestSimInRange(arr, from, to, target) {
|
|
20553
|
+
let best = 0;
|
|
20554
|
+
for (let k = from; k <= to && k < arr.length; k++) {
|
|
20555
|
+
const s = normalizedSimilarity(arr[k], target);
|
|
20556
|
+
if (s > best) best = s;
|
|
20557
|
+
}
|
|
20558
|
+
return best;
|
|
20559
|
+
}
|
|
20560
|
+
function escapeGfm(text) {
|
|
20561
|
+
return text.replace(/~/g, "\\~");
|
|
20562
|
+
}
|
|
20563
|
+
var HWP_SHAPE_ALT_TEXT_RE = /(?:모서리가 둥근 |둥근 )?(?:사각형|직사각형|정사각형|원|타원|삼각형|이등변 삼각형|직각 삼각형|선|직선|곡선|화살표|굵은 화살표|이중 화살표|오각형|육각형|팔각형|별|[4-8]점별|십자|십자형|구름|구름형|마름모|도넛|평행사변형|사다리꼴|부채꼴|호|반원|물결|번개|하트|빗금|블록 화살표|수식|표|그림|개체|그리기\s?개체|묶음\s?개체|글상자|수식\s?개체|OLE\s?개체)\s?입니다\.?/g;
|
|
20564
|
+
function sanitizeText(text) {
|
|
20565
|
+
let result = mapPuaText(text).replace(/[\u{F0000}-\u{FFFFD}]/gu, "").replace(HWP_SHAPE_ALT_TEXT_RE, "").replace(/ +/g, " ").trim();
|
|
20566
|
+
if (result.length <= 30 && result.includes(" ")) {
|
|
20567
|
+
const tokens = result.split(" ");
|
|
20568
|
+
const koreanSingleCharCount = tokens.filter((t) => t.length === 1 && /[가-ㄱ-ㆎ]/.test(t)).length;
|
|
20569
|
+
if (tokens.length >= 3 && koreanSingleCharCount / tokens.length >= 0.7) {
|
|
20570
|
+
result = tokens.join("");
|
|
20571
|
+
}
|
|
20572
|
+
}
|
|
20573
|
+
return result;
|
|
20574
|
+
}
|
|
20575
|
+
function normForMatch(text) {
|
|
20576
|
+
return sanitizeText(text).replace(/\s+/g, " ").trim();
|
|
20577
|
+
}
|
|
20578
|
+
function unescapeGfm(text) {
|
|
20579
|
+
return text.replace(/\\~/g, "~");
|
|
20580
|
+
}
|
|
20581
|
+
function summarize(text) {
|
|
20582
|
+
const t = text.replace(/\s+/g, " ").trim();
|
|
20583
|
+
return t.length > 80 ? t.slice(0, 77) + "..." : t;
|
|
20584
|
+
}
|
|
20585
|
+
function replicateGfmTable(table) {
|
|
20586
|
+
const { cells, rows: numRows, cols: numCols } = table;
|
|
20587
|
+
if (numRows === 0 || numCols === 0) return null;
|
|
20588
|
+
if (numRows === 1 && numCols === 1) return null;
|
|
20589
|
+
if (numCols === 1) return null;
|
|
20590
|
+
const display = Array.from({ length: numRows }, (_, r) => Array.from({ length: numCols }, (_2, c) => ({ text: "", gridR: r, gridC: c })));
|
|
20591
|
+
const skip = /* @__PURE__ */ new Set();
|
|
20592
|
+
for (let r = 0; r < numRows; r++) {
|
|
20593
|
+
for (let c = 0; c < numCols; c++) {
|
|
20594
|
+
if (skip.has(`${r},${c}`)) continue;
|
|
20595
|
+
const cell = cells[r]?.[c];
|
|
20596
|
+
if (!cell) continue;
|
|
20597
|
+
display[r][c] = {
|
|
20598
|
+
text: escapeGfm(sanitizeText(cell.text)).replace(/\|/g, "\\|").replace(/\n/g, "<br>"),
|
|
20599
|
+
gridR: r,
|
|
20600
|
+
gridC: c
|
|
20601
|
+
};
|
|
20602
|
+
for (let dr = 0; dr < cell.rowSpan; dr++) {
|
|
20603
|
+
for (let dc = 0; dc < cell.colSpan; dc++) {
|
|
20604
|
+
if (dr === 0 && dc === 0) continue;
|
|
20605
|
+
if (r + dr < numRows && c + dc < numCols) skip.add(`${r + dr},${c + dc}`);
|
|
20606
|
+
}
|
|
20607
|
+
}
|
|
20608
|
+
c += cell.colSpan - 1;
|
|
20609
|
+
}
|
|
20610
|
+
}
|
|
20611
|
+
const uniqueRows = [];
|
|
20612
|
+
let pendingLabelRow = null;
|
|
20613
|
+
for (let r = 0; r < display.length; r++) {
|
|
20614
|
+
const row = display[r];
|
|
20615
|
+
if (row.every((cell) => cell.text === "")) continue;
|
|
20616
|
+
const nonEmptyCols = row.filter((cell) => cell.text !== "");
|
|
20617
|
+
const hasSkipInRow = row.some((_, c) => skip.has(`${r},${c}`));
|
|
20618
|
+
if (!hasSkipInRow && nonEmptyCols.length === 1 && row[0].text !== "" && row.slice(1).every((c) => c.text === "")) {
|
|
20619
|
+
if (pendingLabelRow) uniqueRows.push(pendingLabelRow);
|
|
20620
|
+
pendingLabelRow = row;
|
|
20621
|
+
continue;
|
|
20622
|
+
}
|
|
20623
|
+
if (pendingLabelRow) {
|
|
20624
|
+
if (row[0].text === "") row[0] = pendingLabelRow[0];
|
|
20625
|
+
else uniqueRows.push(pendingLabelRow);
|
|
20626
|
+
pendingLabelRow = null;
|
|
20627
|
+
}
|
|
20628
|
+
uniqueRows.push(row);
|
|
20629
|
+
}
|
|
20630
|
+
if (pendingLabelRow) uniqueRows.push(pendingLabelRow);
|
|
20631
|
+
return uniqueRows.length > 0 ? uniqueRows : null;
|
|
20632
|
+
}
|
|
20633
|
+
function parseGfmTable(lines) {
|
|
20634
|
+
const rows = [];
|
|
20635
|
+
for (const line of lines) {
|
|
20636
|
+
const trimmed = line.trim();
|
|
20637
|
+
if (!trimmed.startsWith("|")) continue;
|
|
20638
|
+
const cells = trimmed.split(/(?<!\\)\|/).slice(1, -1).map((c) => c.trim());
|
|
20639
|
+
if (cells.length === 0) continue;
|
|
20640
|
+
if (cells.every((c) => /^:?-{3,}:?$/.test(c))) continue;
|
|
20641
|
+
rows.push(cells);
|
|
20642
|
+
}
|
|
20643
|
+
return rows;
|
|
20644
|
+
}
|
|
20645
|
+
function unescapeGfmCell(text) {
|
|
20646
|
+
return text.replace(/<br\s*\/?>/gi, "\n").replace(/\\\|/g, "|").replace(/\\~/g, "~");
|
|
20647
|
+
}
|
|
20648
|
+
function replicateCellInnerHtml(cell) {
|
|
20649
|
+
if (cell.blocks?.length) {
|
|
20650
|
+
return cell.blocks.map((b) => {
|
|
20651
|
+
if (b.type === "table" && b.table) {
|
|
20652
|
+
const cap = b.table.caption ? sanitizeText(b.table.caption) : "";
|
|
20653
|
+
return (cap ? cap + "<br>" : "") + replicateTableToHtml(b.table);
|
|
20654
|
+
}
|
|
20655
|
+
if (b.type === "image" && b.text) return `<img src="${b.text}" alt="image">`;
|
|
20656
|
+
const t = sanitizeText(b.text ?? "");
|
|
20657
|
+
return t ? t.replace(/\n/g, "<br>") : "";
|
|
20658
|
+
}).filter(Boolean).join("<br>");
|
|
20659
|
+
}
|
|
20660
|
+
return sanitizeText(cell.text).replace(/\n/g, "<br>");
|
|
20661
|
+
}
|
|
20662
|
+
function replicateTableToHtml(table) {
|
|
20663
|
+
const rows = replicateHtmlTable(table);
|
|
20664
|
+
const lines = ["<table>"];
|
|
20665
|
+
for (let r = 0; r < rows.length; r++) {
|
|
20666
|
+
const tag = rows[r].tag;
|
|
20667
|
+
const rowHtml = rows[r].cells.map((cell) => {
|
|
20668
|
+
const attrs = [];
|
|
20669
|
+
if (cell.colSpan > 1) attrs.push(`colspan="${cell.colSpan}"`);
|
|
20670
|
+
if (cell.rowSpan > 1) attrs.push(`rowspan="${cell.rowSpan}"`);
|
|
20671
|
+
const attrStr = attrs.length ? " " + attrs.join(" ") : "";
|
|
20672
|
+
return `<${tag}${attrStr}>${cell.inner}</${tag}>`;
|
|
20673
|
+
});
|
|
20674
|
+
if (rowHtml.length) lines.push(`<tr>${rowHtml.join("")}</tr>`);
|
|
20675
|
+
}
|
|
20676
|
+
lines.push("</table>");
|
|
20677
|
+
return lines.join("\n");
|
|
20678
|
+
}
|
|
20679
|
+
function replicateHtmlTable(table) {
|
|
20680
|
+
const { cells, rows: numRows, cols: numCols } = table;
|
|
20681
|
+
const skip = /* @__PURE__ */ new Set();
|
|
20682
|
+
const result = [];
|
|
20683
|
+
for (let r = 0; r < numRows; r++) {
|
|
20684
|
+
const tag = r === 0 ? "th" : "td";
|
|
20685
|
+
const rowCells = [];
|
|
20686
|
+
for (let c = 0; c < numCols; c++) {
|
|
20687
|
+
if (skip.has(`${r},${c}`)) continue;
|
|
20688
|
+
const cell = cells[r]?.[c];
|
|
20689
|
+
if (!cell) continue;
|
|
20690
|
+
for (let dr = 0; dr < cell.rowSpan; dr++) {
|
|
20691
|
+
for (let dc = 0; dc < cell.colSpan; dc++) {
|
|
20692
|
+
if (dr === 0 && dc === 0) continue;
|
|
20693
|
+
if (r + dr < numRows && c + dc < numCols) skip.add(`${r + dr},${c + dc}`);
|
|
20694
|
+
}
|
|
20695
|
+
}
|
|
20696
|
+
rowCells.push({
|
|
20697
|
+
inner: replicateCellInnerHtml(cell),
|
|
20698
|
+
colSpan: cell.colSpan,
|
|
20699
|
+
rowSpan: cell.rowSpan,
|
|
20700
|
+
gridR: r,
|
|
20701
|
+
gridC: c
|
|
20702
|
+
});
|
|
20703
|
+
}
|
|
20704
|
+
if (rowCells.length) result.push({ tag, cells: rowCells });
|
|
20705
|
+
}
|
|
20706
|
+
return result;
|
|
20707
|
+
}
|
|
20708
|
+
function parseHtmlTable(raw) {
|
|
20709
|
+
const re = /<(\/?)(table|tr|td|th)((?:"[^"]*"|'[^']*'|[^>"'])*?)>/gi;
|
|
20710
|
+
let depth = 0;
|
|
20711
|
+
let currentRow = null;
|
|
20712
|
+
let cellStart = -1;
|
|
20713
|
+
let cellInfo = null;
|
|
20714
|
+
const rows = [];
|
|
20715
|
+
let m;
|
|
20716
|
+
while ((m = re.exec(raw)) !== null) {
|
|
20717
|
+
const isClose = m[1] === "/";
|
|
20718
|
+
const tag = m[2].toLowerCase();
|
|
20719
|
+
const attrs = m[3] || "";
|
|
20720
|
+
if (tag === "table") {
|
|
20721
|
+
depth += isClose ? -1 : 1;
|
|
20722
|
+
if (depth < 0) return null;
|
|
20723
|
+
continue;
|
|
20724
|
+
}
|
|
20725
|
+
if (depth !== 1) continue;
|
|
20726
|
+
if (tag === "tr") {
|
|
20727
|
+
if (!isClose) currentRow = [];
|
|
20728
|
+
else if (currentRow) {
|
|
20729
|
+
rows.push({ tag: rows.length === 0 ? "th" : "td", cells: currentRow });
|
|
20730
|
+
currentRow = null;
|
|
20731
|
+
}
|
|
20732
|
+
} else {
|
|
20733
|
+
if (!isClose) {
|
|
20734
|
+
const cs = parseInt(attrs.match(/colspan\s*=\s*"(\d+)"/i)?.[1] || "1", 10);
|
|
20735
|
+
const rs = parseInt(attrs.match(/rowspan\s*=\s*"(\d+)"/i)?.[1] || "1", 10);
|
|
20736
|
+
cellStart = m.index + m[0].length;
|
|
20737
|
+
cellInfo = { colSpan: isNaN(cs) ? 1 : cs, rowSpan: isNaN(rs) ? 1 : rs };
|
|
20738
|
+
} else if (cellStart >= 0 && cellInfo && currentRow) {
|
|
20739
|
+
currentRow.push({ inner: raw.slice(cellStart, m.index), colSpan: cellInfo.colSpan, rowSpan: cellInfo.rowSpan });
|
|
20740
|
+
cellStart = -1;
|
|
20741
|
+
cellInfo = null;
|
|
20742
|
+
}
|
|
20743
|
+
}
|
|
20744
|
+
}
|
|
20745
|
+
if (depth !== 0) return null;
|
|
20746
|
+
return rows;
|
|
20747
|
+
}
|
|
20748
|
+
var AUTONUM_PREFIX_RE = /^(?:[0-90-9a-zA-Z가-힣]{1,6}[.)\]:]|[([][0-90-9a-zA-Z가-힣]{1,6}[)\]][.:]?|[ⅰ-ⅹⅠ-Ⅹ①-⑮][.)\]:]?)$/u;
|
|
20749
|
+
function htmlCellInnerToLines(inner) {
|
|
20750
|
+
let hadNonText = false;
|
|
20751
|
+
let work = inner;
|
|
20752
|
+
if (/<table[\s>]/i.test(work)) {
|
|
20753
|
+
hadNonText = true;
|
|
20754
|
+
work = removeNestedTables(work);
|
|
20755
|
+
}
|
|
20756
|
+
if (/<img\s/i.test(work)) {
|
|
20757
|
+
hadNonText = true;
|
|
20758
|
+
work = work.replace(/<img\s(?:"[^"]*"|'[^']*'|[^>"'])*?>/gi, "");
|
|
20759
|
+
}
|
|
20760
|
+
const lines = work.split(/<br\s*\/?>/gi).map((s) => s.trim()).filter((s) => s.length > 0);
|
|
20761
|
+
return { lines, hadNonText };
|
|
20762
|
+
}
|
|
20763
|
+
function extractTopLevelTables(html) {
|
|
20764
|
+
const result = [];
|
|
20765
|
+
let depth = 0;
|
|
20766
|
+
let start = -1;
|
|
20767
|
+
const re = /<(\/?)table(?:[\s>]|>)/gi;
|
|
20768
|
+
let m;
|
|
20769
|
+
while ((m = re.exec(html)) !== null) {
|
|
20770
|
+
if (m[1] !== "/") {
|
|
20771
|
+
if (depth === 0) start = m.index;
|
|
20772
|
+
depth++;
|
|
20773
|
+
} else {
|
|
20774
|
+
depth--;
|
|
20775
|
+
if (depth === 0 && start >= 0) {
|
|
20776
|
+
result.push(html.slice(start, m.index + m[0].length));
|
|
20777
|
+
start = -1;
|
|
20778
|
+
}
|
|
20779
|
+
if (depth < 0) depth = 0;
|
|
20780
|
+
}
|
|
20781
|
+
}
|
|
20782
|
+
return result;
|
|
20783
|
+
}
|
|
20784
|
+
function removeNestedTables(html) {
|
|
20785
|
+
let result = "";
|
|
20786
|
+
let depth = 0;
|
|
20787
|
+
const re = /<(\/?)table(?:[\s>]|>)/gi;
|
|
20788
|
+
let last = 0;
|
|
20789
|
+
let m;
|
|
20790
|
+
while ((m = re.exec(html)) !== null) {
|
|
20791
|
+
if (m[1] !== "/") {
|
|
20792
|
+
if (depth === 0) result += html.slice(last, m.index);
|
|
20793
|
+
depth++;
|
|
20794
|
+
} else {
|
|
20795
|
+
depth--;
|
|
20796
|
+
if (depth === 0) last = m.index + m[0].length;
|
|
20797
|
+
if (depth < 0) depth = 0;
|
|
20798
|
+
}
|
|
20799
|
+
}
|
|
20800
|
+
if (depth === 0) result += html.slice(last);
|
|
20801
|
+
return result;
|
|
20802
|
+
}
|
|
20803
|
+
|
|
20804
|
+
// src/hwpx/generator.ts
|
|
20805
|
+
var NS_SECTION = "http://www.hancom.co.kr/hwpml/2011/section";
|
|
20806
|
+
var NS_PARA = "http://www.hancom.co.kr/hwpml/2011/paragraph";
|
|
20807
|
+
var NS_HEAD = "http://www.hancom.co.kr/hwpml/2011/head";
|
|
20808
|
+
var NS_CORE = "http://www.hancom.co.kr/hwpml/2011/core";
|
|
20809
|
+
var NS_OPF = "http://www.idpf.org/2007/opf/";
|
|
20810
|
+
var NS_HPF = "http://www.hancom.co.kr/schema/2011/hpf";
|
|
20811
|
+
var NS_OCF = "urn:oasis:names:tc:opendocument:xmlns:container";
|
|
20812
|
+
var CHAR_NORMAL = 0;
|
|
20813
|
+
var CHAR_BOLD = 1;
|
|
20814
|
+
var CHAR_ITALIC = 2;
|
|
20815
|
+
var CHAR_BOLD_ITALIC = 3;
|
|
20816
|
+
var CHAR_CODE = 4;
|
|
20817
|
+
var CHAR_H1 = 5;
|
|
20818
|
+
var CHAR_H2 = 6;
|
|
20819
|
+
var CHAR_H3 = 7;
|
|
20820
|
+
var CHAR_H4 = 8;
|
|
20821
|
+
var CHAR_TABLE_HEADER = 9;
|
|
20822
|
+
var CHAR_QUOTE = 10;
|
|
20823
|
+
var PARA_NORMAL = 0;
|
|
20824
|
+
var PARA_H1 = 1;
|
|
20825
|
+
var PARA_H2 = 2;
|
|
20826
|
+
var PARA_H3 = 3;
|
|
20827
|
+
var PARA_H4 = 4;
|
|
20828
|
+
var PARA_CODE = 5;
|
|
20829
|
+
var PARA_QUOTE = 6;
|
|
20830
|
+
var PARA_LIST = 7;
|
|
20831
|
+
var DEFAULT_TEXT_COLOR = "#000000";
|
|
20832
|
+
function resolveTheme(theme) {
|
|
20833
|
+
return {
|
|
20834
|
+
h1: theme?.headingColors?.[1] ?? DEFAULT_TEXT_COLOR,
|
|
20835
|
+
h2: theme?.headingColors?.[2] ?? DEFAULT_TEXT_COLOR,
|
|
20836
|
+
h3: theme?.headingColors?.[3] ?? DEFAULT_TEXT_COLOR,
|
|
20837
|
+
h4: theme?.headingColors?.[4] ?? theme?.headingColors?.[3] ?? DEFAULT_TEXT_COLOR,
|
|
20838
|
+
body: theme?.bodyColor ?? DEFAULT_TEXT_COLOR,
|
|
20839
|
+
quote: theme?.quoteColor ?? DEFAULT_TEXT_COLOR,
|
|
20840
|
+
/** quoteColor가 명시되었는지 — blockquote charPr 분기에 사용 (baseline 호환) */
|
|
20841
|
+
hasQuoteOption: theme?.quoteColor !== void 0,
|
|
20842
|
+
tableHeader: theme?.tableHeaderColor ?? theme?.bodyColor ?? DEFAULT_TEXT_COLOR,
|
|
20843
|
+
tableHeaderBold: !!theme?.tableHeaderBold
|
|
20844
|
+
};
|
|
20845
|
+
}
|
|
20846
|
+
async function markdownToHwpx(markdown, options) {
|
|
20847
|
+
const theme = resolveTheme(options?.theme);
|
|
20848
|
+
const gongmun = options?.gongmun ? resolveGongmun(options.gongmun) : null;
|
|
20849
|
+
const blocks = parseMarkdownToBlocks(markdown);
|
|
20850
|
+
const gongmunList = gongmun ? precomputeGongmunList(blocks, gongmun) : null;
|
|
20851
|
+
const fit = gongmun && gongmunList ? computeGongmunFitPlan(blocks, gongmun, gongmunList) : null;
|
|
20852
|
+
const sectionXml = blocksToSectionXml(blocks, theme, gongmun, gongmunList, fit);
|
|
20853
|
+
const zip = new JSZip5();
|
|
20854
|
+
zip.file("mimetype", "application/hwp+zip", { compression: "STORE" });
|
|
20855
|
+
zip.file("META-INF/container.xml", generateContainerXml());
|
|
20856
|
+
zip.file("Contents/content.hpf", generateManifest());
|
|
20857
|
+
zip.file("Contents/header.xml", generateHeaderXml(theme, gongmun, fit?.variants ?? []));
|
|
20858
|
+
zip.file("Contents/section0.xml", sectionXml);
|
|
20859
|
+
zip.file("Preview/PrvText.txt", buildPrvText(blocks));
|
|
20860
|
+
return await zip.generateAsync({ type: "arraybuffer" });
|
|
20861
|
+
}
|
|
20862
|
+
function buildPrvText(blocks) {
|
|
20863
|
+
const lines = [];
|
|
20864
|
+
let bytes = 0;
|
|
20865
|
+
for (const b of blocks) {
|
|
20866
|
+
let text = b.text || (b.rows ? b.rows.map((r) => r.join(" ")).join("\n") : "");
|
|
20867
|
+
if (b.type === "html_table") text = text.replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
|
|
20868
|
+
if (!text) continue;
|
|
20869
|
+
lines.push(text);
|
|
20870
|
+
bytes += text.length * 3;
|
|
20871
|
+
if (bytes > 1024) break;
|
|
20872
|
+
}
|
|
20873
|
+
return lines.join("\n").slice(0, 1024);
|
|
20874
|
+
}
|
|
20875
|
+
function parseMarkdownToBlocks(md2) {
|
|
20876
|
+
const lines = md2.split("\n");
|
|
20877
|
+
const blocks = [];
|
|
20878
|
+
let i = 0;
|
|
20879
|
+
while (i < lines.length) {
|
|
20880
|
+
const line = lines[i];
|
|
20881
|
+
if (!line.trim()) {
|
|
20882
|
+
i++;
|
|
20883
|
+
continue;
|
|
20884
|
+
}
|
|
20885
|
+
const fenceMatch = line.match(/^(`{3,}|~{3,})(.*)$/);
|
|
20886
|
+
if (fenceMatch) {
|
|
20887
|
+
const fence = fenceMatch[1];
|
|
20888
|
+
const lang = fenceMatch[2].trim();
|
|
20889
|
+
const codeLines = [];
|
|
20890
|
+
i++;
|
|
20891
|
+
while (i < lines.length && !lines[i].startsWith(fence)) {
|
|
20892
|
+
codeLines.push(lines[i]);
|
|
20893
|
+
i++;
|
|
20894
|
+
}
|
|
20895
|
+
if (i < lines.length) i++;
|
|
20896
|
+
blocks.push({ type: "code_block", text: codeLines.join("\n"), lang });
|
|
20897
|
+
continue;
|
|
20898
|
+
}
|
|
20899
|
+
if (/^(\*{3,}|-{3,}|_{3,})\s*$/.test(line.trim())) {
|
|
20026
20900
|
blocks.push({ type: "hr" });
|
|
20027
20901
|
i++;
|
|
20028
20902
|
continue;
|
|
@@ -20033,6 +20907,20 @@ function parseMarkdownToBlocks(md2) {
|
|
|
20033
20907
|
i++;
|
|
20034
20908
|
continue;
|
|
20035
20909
|
}
|
|
20910
|
+
if (/^<table[\s>]/i.test(line.trimStart())) {
|
|
20911
|
+
const htmlLines = [];
|
|
20912
|
+
let depth = 0;
|
|
20913
|
+
while (i < lines.length) {
|
|
20914
|
+
const l = lines[i];
|
|
20915
|
+
htmlLines.push(l);
|
|
20916
|
+
depth += (l.match(/<table[\s>]/gi) ?? []).length;
|
|
20917
|
+
depth -= (l.match(/<\/table>/gi) ?? []).length;
|
|
20918
|
+
i++;
|
|
20919
|
+
if (depth <= 0) break;
|
|
20920
|
+
}
|
|
20921
|
+
blocks.push({ type: "html_table", text: htmlLines.join("\n") });
|
|
20922
|
+
continue;
|
|
20923
|
+
}
|
|
20036
20924
|
if (line.trimStart().startsWith("|")) {
|
|
20037
20925
|
const tableRows = [];
|
|
20038
20926
|
while (i < lines.length && lines[i].trimStart().startsWith("|")) {
|
|
@@ -20114,18 +21002,19 @@ function spanToCharPrId(span) {
|
|
|
20114
21002
|
function escapeXml(text) {
|
|
20115
21003
|
return text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
20116
21004
|
}
|
|
20117
|
-
function generateRuns(text, defaultCharPr = CHAR_NORMAL) {
|
|
21005
|
+
function generateRuns(text, defaultCharPr = CHAR_NORMAL, mapCharId) {
|
|
20118
21006
|
const spans = parseInlineMarkdown(text);
|
|
20119
21007
|
return spans.map((span) => {
|
|
20120
|
-
|
|
21008
|
+
let charId = span.code || span.bold || span.italic ? spanToCharPrId(span) : defaultCharPr;
|
|
21009
|
+
if (mapCharId) charId = mapCharId(charId);
|
|
20121
21010
|
return `<hp:run charPrIDRef="${charId}"><hp:t>${escapeXml(span.text)}</hp:t></hp:run>`;
|
|
20122
21011
|
}).join("");
|
|
20123
21012
|
}
|
|
20124
|
-
function generateParagraph(text, paraPrId = PARA_NORMAL, charPrId = CHAR_NORMAL) {
|
|
21013
|
+
function generateParagraph(text, paraPrId = PARA_NORMAL, charPrId = CHAR_NORMAL, mapCharId) {
|
|
20125
21014
|
if (paraPrId === PARA_CODE) {
|
|
20126
21015
|
return `<hp:p paraPrIDRef="${paraPrId}" styleIDRef="0"><hp:run charPrIDRef="${CHAR_CODE}"><hp:t>${escapeXml(text)}</hp:t></hp:run></hp:p>`;
|
|
20127
21016
|
}
|
|
20128
|
-
const runs = generateRuns(text, charPrId);
|
|
21017
|
+
const runs = generateRuns(text, charPrId, mapCharId);
|
|
20129
21018
|
return `<hp:p paraPrIDRef="${paraPrId}" styleIDRef="0">${runs}</hp:p>`;
|
|
20130
21019
|
}
|
|
20131
21020
|
function headingParaPrId(level) {
|
|
@@ -20165,7 +21054,7 @@ function charPr(id, height, bold, italic, fontId = 0, textColor = DEFAULT_TEXT_C
|
|
|
20165
21054
|
const boldAttr = bold ? ` bold="1"` : "";
|
|
20166
21055
|
const italicAttr = italic ? ` italic="1"` : "";
|
|
20167
21056
|
const effFont = bold ? 2 : fontId;
|
|
20168
|
-
return ` <hh:charPr id="${id}" height="${height}" textColor="${textColor}" shadeColor="none" useFontSpace="0" useKerning="0" symMark="NONE" borderFillIDRef="
|
|
21057
|
+
return ` <hh:charPr id="${id}" height="${height}" textColor="${textColor}" shadeColor="none" useFontSpace="0" useKerning="0" symMark="NONE" borderFillIDRef="1"${boldAttr}${italicAttr}>
|
|
20169
21058
|
<hh:fontRef hangul="${effFont}" latin="${effFont}" hanja="${effFont}" japanese="${effFont}" other="${effFont}" symbol="${effFont}" user="${effFont}"/>
|
|
20170
21059
|
<hh:ratio hangul="${ratioPct}" latin="${ratioPct}" hanja="${ratioPct}" japanese="100" other="100" symbol="100" user="100"/>
|
|
20171
21060
|
<hh:spacing hangul="0" latin="0" hanja="0" japanese="0" other="0" symbol="0" user="0"/>
|
|
@@ -20184,13 +21073,58 @@ function paraPr(id, opts = {}) {
|
|
|
20184
21073
|
<hh:autoSpacing eAsianEng="0" eAsianNum="0"/>
|
|
20185
21074
|
<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
21075
|
<hh:lineSpacing type="PERCENT" value="${lineSpacing}"/>
|
|
20187
|
-
<hh:border borderFillIDRef="
|
|
21076
|
+
<hh:border borderFillIDRef="1" offsetLeft="0" offsetRight="0" offsetTop="0" offsetBottom="0" connect="0" ignoreMargin="0"/>
|
|
20188
21077
|
</hh:paraPr>`;
|
|
20189
21078
|
}
|
|
20190
21079
|
var GONGMUN_LIST_BASE = 8;
|
|
20191
21080
|
var GONGMUN_LIST_LEVELS = 8;
|
|
20192
21081
|
var GONGMUN_CENTER = GONGMUN_LIST_BASE + GONGMUN_LIST_LEVELS;
|
|
20193
|
-
|
|
21082
|
+
var CHAR_VARIANT_BASE = 11;
|
|
21083
|
+
var GONGMUN_BODY_RATIO = 95;
|
|
21084
|
+
function plainRenderText(text) {
|
|
21085
|
+
return parseInlineMarkdown(text).map((s) => s.text).join("");
|
|
21086
|
+
}
|
|
21087
|
+
function computeGongmunFitPlan(blocks, gongmun, gongmunList) {
|
|
21088
|
+
const minRatio = gongmun.autoFitMinRatio;
|
|
21089
|
+
if (minRatio === null || minRatio >= GONGMUN_BODY_RATIO) return null;
|
|
21090
|
+
const pageW = 59528 - mmToHwpunit(gongmun.margins.left) - mmToHwpunit(gongmun.margins.right);
|
|
21091
|
+
const ratioByBlock = /* @__PURE__ */ new Map();
|
|
21092
|
+
const variants = [];
|
|
21093
|
+
for (let i = 0; i < blocks.length; i++) {
|
|
21094
|
+
const block = blocks[i];
|
|
21095
|
+
let text;
|
|
21096
|
+
let firstW;
|
|
21097
|
+
let contW;
|
|
21098
|
+
if (block.type === "list_item" && gongmunList.has(i)) {
|
|
21099
|
+
const { marker, depth } = gongmunList.get(i);
|
|
21100
|
+
const content = plainRenderText(block.text || "");
|
|
21101
|
+
text = marker ? `${marker} ${content}` : content;
|
|
21102
|
+
const { left, indent } = levelIndent(depth, gongmun.bodyHeight, gongmun.numbering);
|
|
21103
|
+
firstW = pageW - left - Math.max(indent, 0);
|
|
21104
|
+
contW = pageW - left - Math.max(-indent, 0);
|
|
21105
|
+
} else if (block.type === "paragraph") {
|
|
21106
|
+
const raw = (block.text || "").trim();
|
|
21107
|
+
if (/^<center>[\s\S]*<\/center>$/i.test(raw)) continue;
|
|
21108
|
+
text = plainRenderText(raw);
|
|
21109
|
+
firstW = contW = pageW;
|
|
21110
|
+
} else {
|
|
21111
|
+
continue;
|
|
21112
|
+
}
|
|
21113
|
+
if (!text) continue;
|
|
21114
|
+
const r = fitRatioForFewerLines(text, firstW, contW, gongmun.bodyHeight, GONGMUN_BODY_RATIO, minRatio);
|
|
21115
|
+
if (r === null) continue;
|
|
21116
|
+
ratioByBlock.set(i, r);
|
|
21117
|
+
if (!variants.includes(r)) variants.push(r);
|
|
21118
|
+
}
|
|
21119
|
+
return ratioByBlock.size > 0 ? { ratioByBlock, variants } : null;
|
|
21120
|
+
}
|
|
21121
|
+
function variantMapper(fit, blockIdx) {
|
|
21122
|
+
const r = fit.ratioByBlock.get(blockIdx);
|
|
21123
|
+
if (r === void 0) return void 0;
|
|
21124
|
+
const vi = fit.variants.indexOf(r);
|
|
21125
|
+
return (id) => id >= 0 && id <= 3 ? CHAR_VARIANT_BASE + vi * 4 + id : id;
|
|
21126
|
+
}
|
|
21127
|
+
function buildCharProperties(theme, gongmun, ratioVariants = []) {
|
|
20194
21128
|
let body = 1e3, code = 900, h1 = 1800, h2 = 1400, h3 = 1200, h4 = 1100;
|
|
20195
21129
|
if (gongmun) {
|
|
20196
21130
|
body = gongmun.bodyHeight;
|
|
@@ -20200,7 +21134,7 @@ function buildCharProperties(theme, gongmun) {
|
|
|
20200
21134
|
h3 = body;
|
|
20201
21135
|
h4 = Math.max(body - 100, 1300);
|
|
20202
21136
|
}
|
|
20203
|
-
const bodyRatio = gongmun ?
|
|
21137
|
+
const bodyRatio = gongmun ? GONGMUN_BODY_RATIO : 100;
|
|
20204
21138
|
const rows = [
|
|
20205
21139
|
charPr(0, body, false, false, 0, theme.body, bodyRatio),
|
|
20206
21140
|
charPr(1, body, true, false, 0, theme.body, bodyRatio),
|
|
@@ -20214,6 +21148,14 @@ function buildCharProperties(theme, gongmun) {
|
|
|
20214
21148
|
charPr(CHAR_TABLE_HEADER, body, theme.tableHeaderBold, false, 0, theme.tableHeader),
|
|
20215
21149
|
charPr(CHAR_QUOTE, body, false, true, 0, theme.quote)
|
|
20216
21150
|
];
|
|
21151
|
+
for (const r of ratioVariants) {
|
|
21152
|
+
rows.push(
|
|
21153
|
+
charPr(rows.length, body, false, false, 0, theme.body, r),
|
|
21154
|
+
charPr(rows.length + 1, body, true, false, 0, theme.body, r),
|
|
21155
|
+
charPr(rows.length + 2, body, false, true, 0, theme.body, r),
|
|
21156
|
+
charPr(rows.length + 3, body, true, true, 0, theme.body, r)
|
|
21157
|
+
);
|
|
21158
|
+
}
|
|
20217
21159
|
return `<hh:charProperties itemCnt="${rows.length}">
|
|
20218
21160
|
${rows.join("\n")}
|
|
20219
21161
|
</hh:charProperties>`;
|
|
@@ -20256,9 +21198,9 @@ ${base2.join("\n")}
|
|
|
20256
21198
|
${base.join("\n")}
|
|
20257
21199
|
</hh:paraProperties>`;
|
|
20258
21200
|
}
|
|
20259
|
-
function generateHeaderXml(theme, gongmun) {
|
|
21201
|
+
function generateHeaderXml(theme, gongmun, ratioVariants = []) {
|
|
20260
21202
|
const bodyFace = gongmun?.bodyFont === "gothic" ? "\uB9D1\uC740 \uACE0\uB515" : "\uD568\uCD08\uB86C\uBC14\uD0D5";
|
|
20261
|
-
const charPropsXml = buildCharProperties(theme, gongmun);
|
|
21203
|
+
const charPropsXml = buildCharProperties(theme, gongmun, ratioVariants);
|
|
20262
21204
|
const paraPropsXml = buildParaProperties(gongmun);
|
|
20263
21205
|
return `<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
|
|
20264
21206
|
<hh:head xmlns:hh="${NS_HEAD}" xmlns:hp="${NS_PARA}" xmlns:hc="${NS_CORE}" version="1.4" secCnt="1">
|
|
@@ -20314,25 +21256,21 @@ function generateHeaderXml(theme, gongmun) {
|
|
|
20314
21256
|
</hh:fontface>
|
|
20315
21257
|
</hh:fontfaces>
|
|
20316
21258
|
<hh:borderFills itemCnt="2">
|
|
20317
|
-
<hh:borderFill id="
|
|
21259
|
+
<hh:borderFill id="1" threeD="0" shadow="0" centerLine="NONE" breakCellSeparateLine="0">
|
|
20318
21260
|
<hh:slash type="NONE" Crooked="0" isCounter="0"/>
|
|
20319
21261
|
<hh:backSlash type="NONE" Crooked="0" isCounter="0"/>
|
|
20320
21262
|
<hh:leftBorder type="NONE" width="0.1 mm" color="#000000"/>
|
|
20321
21263
|
<hh:rightBorder type="NONE" width="0.1 mm" color="#000000"/>
|
|
20322
21264
|
<hh:topBorder type="NONE" width="0.1 mm" color="#000000"/>
|
|
20323
21265
|
<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
21266
|
</hh:borderFill>
|
|
20327
|
-
<hh:borderFill id="
|
|
21267
|
+
<hh:borderFill id="2" threeD="0" shadow="0" centerLine="NONE" breakCellSeparateLine="0">
|
|
20328
21268
|
<hh:slash type="NONE" Crooked="0" isCounter="0"/>
|
|
20329
21269
|
<hh:backSlash type="NONE" Crooked="0" isCounter="0"/>
|
|
20330
21270
|
<hh:leftBorder type="SOLID" width="0.12 mm" color="#000000"/>
|
|
20331
21271
|
<hh:rightBorder type="SOLID" width="0.12 mm" color="#000000"/>
|
|
20332
21272
|
<hh:topBorder type="SOLID" width="0.12 mm" color="#000000"/>
|
|
20333
21273
|
<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
21274
|
</hh:borderFill>
|
|
20337
21275
|
</hh:borderFills>
|
|
20338
21276
|
${charPropsXml}
|
|
@@ -20379,14 +21317,74 @@ function generateTable(rows, theme) {
|
|
|
20379
21317
|
const tdElements = cells.map((cell, colIdx) => {
|
|
20380
21318
|
const runs = generateRuns(cell, headerCharPr);
|
|
20381
21319
|
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="
|
|
21320
|
+
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
21321
|
}).join("");
|
|
20384
21322
|
return `<hp:tr>${tdElements}</hp:tr>`;
|
|
20385
21323
|
}).join("");
|
|
20386
21324
|
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="
|
|
21325
|
+
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
21326
|
return `<hp:p paraPrIDRef="0" styleIDRef="0"><hp:run charPrIDRef="0">${tbl}</hp:run></hp:p>`;
|
|
20389
21327
|
}
|
|
21328
|
+
function layoutHtmlRows(rows) {
|
|
21329
|
+
const occupied = /* @__PURE__ */ new Set();
|
|
21330
|
+
const placed = [];
|
|
21331
|
+
let colCnt = 0;
|
|
21332
|
+
for (let r = 0; r < rows.length; r++) {
|
|
21333
|
+
let c = 0;
|
|
21334
|
+
for (const cell of rows[r].cells) {
|
|
21335
|
+
while (occupied.has(`${r},${c}`)) c++;
|
|
21336
|
+
const colSpan = Math.max(1, cell.colSpan);
|
|
21337
|
+
const rowSpan = Math.max(1, cell.rowSpan);
|
|
21338
|
+
placed.push({ r, c, colSpan, rowSpan, inner: cell.inner, isHeader: rows[r].tag === "th" });
|
|
21339
|
+
for (let dr = 0; dr < rowSpan; dr++) {
|
|
21340
|
+
for (let dc = 0; dc < colSpan; dc++) occupied.add(`${r + dr},${c + dc}`);
|
|
21341
|
+
}
|
|
21342
|
+
c += colSpan;
|
|
21343
|
+
colCnt = Math.max(colCnt, c);
|
|
21344
|
+
}
|
|
21345
|
+
}
|
|
21346
|
+
return { placed, rowCnt: rows.length, colCnt };
|
|
21347
|
+
}
|
|
21348
|
+
function unescapeHtml(s) {
|
|
21349
|
+
return s.replace(/</g, "<").replace(/>/g, ">").replace(/"/g, '"').replace(/'/g, "'").replace(/&/g, "&");
|
|
21350
|
+
}
|
|
21351
|
+
function generateHtmlTableXml(rawHtml, theme, totalWidth = 44e3) {
|
|
21352
|
+
const rows = parseHtmlTable(rawHtml);
|
|
21353
|
+
if (!rows || rows.length === 0) return null;
|
|
21354
|
+
const { placed, rowCnt, colCnt } = layoutHtmlRows(rows);
|
|
21355
|
+
if (rowCnt === 0 || colCnt === 0) return null;
|
|
21356
|
+
const colW = Math.floor(totalWidth / colCnt);
|
|
21357
|
+
const cellH = 1500;
|
|
21358
|
+
const tblW = colW * colCnt;
|
|
21359
|
+
const tblId = nextTableId();
|
|
21360
|
+
const useHeaderStyle = theme.tableHeader !== theme.body || theme.tableHeaderBold;
|
|
21361
|
+
const tcXmls = placed.map((cell) => {
|
|
21362
|
+
const headerCharPr = cell.isHeader && useHeaderStyle ? CHAR_TABLE_HEADER : CHAR_NORMAL;
|
|
21363
|
+
const { lines } = htmlCellInnerToLines(cell.inner);
|
|
21364
|
+
const paras = lines.map(
|
|
21365
|
+
(line) => `<hp:p paraPrIDRef="0" styleIDRef="0"><hp:run charPrIDRef="${headerCharPr}"><hp:t>${escapeXml(unescapeHtml(line))}</hp:t></hp:run></hp:p>`
|
|
21366
|
+
);
|
|
21367
|
+
let nestedH = 0;
|
|
21368
|
+
for (const nested of extractTopLevelTables(cell.inner)) {
|
|
21369
|
+
const nestedXml = generateHtmlTableXml(nested, theme, Math.max(colW * cell.colSpan - 1020, 4e3));
|
|
21370
|
+
if (nestedXml) {
|
|
21371
|
+
paras.push(`<hp:p paraPrIDRef="0" styleIDRef="0"><hp:run charPrIDRef="0">${nestedXml}</hp:run></hp:p>`);
|
|
21372
|
+
nestedH += (nested.match(/<tr[\s>]/gi) ?? []).length * cellH + 300;
|
|
21373
|
+
}
|
|
21374
|
+
}
|
|
21375
|
+
if (paras.length === 0) {
|
|
21376
|
+
paras.push(`<hp:p paraPrIDRef="0" styleIDRef="0"><hp:run charPrIDRef="${headerCharPr}"><hp:t></hp:t></hp:run></hp:p>`);
|
|
21377
|
+
}
|
|
21378
|
+
const cellHeight = Math.max(cellH * cell.rowSpan, Math.max(lines.length, 1) * 800 + nestedH);
|
|
21379
|
+
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>`;
|
|
21380
|
+
});
|
|
21381
|
+
const trXmls = [];
|
|
21382
|
+
for (let r = 0; r < rowCnt; r++) {
|
|
21383
|
+
const rowTcs = tcXmls.filter((_, i) => placed[i].r === r);
|
|
21384
|
+
trXmls.push(`<hp:tr>${rowTcs.join("")}</hp:tr>`);
|
|
21385
|
+
}
|
|
21386
|
+
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>`;
|
|
21387
|
+
}
|
|
20390
21388
|
function precomputeGongmunList(blocks, gongmun) {
|
|
20391
21389
|
const result = /* @__PURE__ */ new Map();
|
|
20392
21390
|
let i = 0;
|
|
@@ -20396,9 +21394,22 @@ function precomputeGongmunList(blocks, gongmun) {
|
|
|
20396
21394
|
continue;
|
|
20397
21395
|
}
|
|
20398
21396
|
const run = [];
|
|
20399
|
-
while (i < blocks.length
|
|
20400
|
-
|
|
20401
|
-
|
|
21397
|
+
while (i < blocks.length) {
|
|
21398
|
+
const t = blocks[i].type;
|
|
21399
|
+
if (t === "list_item") {
|
|
21400
|
+
run.push(i);
|
|
21401
|
+
i++;
|
|
21402
|
+
continue;
|
|
21403
|
+
}
|
|
21404
|
+
if (t === "table" || t === "html_table") {
|
|
21405
|
+
let j = i + 1;
|
|
21406
|
+
while (j < blocks.length && (blocks[j].type === "table" || blocks[j].type === "html_table")) j++;
|
|
21407
|
+
if (j < blocks.length && blocks[j].type === "list_item") {
|
|
21408
|
+
i = j;
|
|
21409
|
+
continue;
|
|
21410
|
+
}
|
|
21411
|
+
}
|
|
21412
|
+
break;
|
|
20402
21413
|
}
|
|
20403
21414
|
const depths = run.map((bi) => Math.min(Math.max(blocks[bi].indent || 0, 0), GONGMUN_LIST_LEVELS - 1));
|
|
20404
21415
|
const suppress = gongmun.numbering === "standard" ? computeSuppression(depths) : depths.map(() => false);
|
|
@@ -20410,12 +21421,11 @@ function precomputeGongmunList(blocks, gongmun) {
|
|
|
20410
21421
|
}
|
|
20411
21422
|
return result;
|
|
20412
21423
|
}
|
|
20413
|
-
function blocksToSectionXml(blocks, theme, gongmun) {
|
|
21424
|
+
function blocksToSectionXml(blocks, theme, gongmun, gongmunList = gongmun ? precomputeGongmunList(blocks, gongmun) : null, fit = null) {
|
|
20414
21425
|
const paraXmls = [];
|
|
20415
21426
|
let isFirst = true;
|
|
20416
21427
|
const orderedCounters = {};
|
|
20417
21428
|
let prevWasOrdered = false;
|
|
20418
|
-
const gongmunList = gongmun ? precomputeGongmunList(blocks, gongmun) : null;
|
|
20419
21429
|
for (let blockIdx = 0; blockIdx < blocks.length; blockIdx++) {
|
|
20420
21430
|
const block = blocks[blockIdx];
|
|
20421
21431
|
let xml = "";
|
|
@@ -20437,7 +21447,7 @@ function blocksToSectionXml(blocks, theme, gongmun) {
|
|
|
20437
21447
|
if (ctr) {
|
|
20438
21448
|
xml = generateParagraph(ctr[1].trim(), GONGMUN_CENTER);
|
|
20439
21449
|
} else {
|
|
20440
|
-
xml = generateParagraph(block.text || "");
|
|
21450
|
+
xml = generateParagraph(block.text || "", PARA_NORMAL, CHAR_NORMAL, fit ? variantMapper(fit, blockIdx) : void 0);
|
|
20441
21451
|
}
|
|
20442
21452
|
break;
|
|
20443
21453
|
}
|
|
@@ -20461,7 +21471,7 @@ function blocksToSectionXml(blocks, theme, gongmun) {
|
|
|
20461
21471
|
const content = block.text || "";
|
|
20462
21472
|
const text = marker2 ? `${marker2} ${content}` : content;
|
|
20463
21473
|
const listCharPr = gongmun.numbering === "report" && depth === 0 ? CHAR_BOLD : CHAR_NORMAL;
|
|
20464
|
-
xml = generateParagraph(text, GONGMUN_LIST_BASE + depth, listCharPr);
|
|
21474
|
+
xml = generateParagraph(text, GONGMUN_LIST_BASE + depth, listCharPr, fit ? variantMapper(fit, blockIdx) : void 0);
|
|
20465
21475
|
break;
|
|
20466
21476
|
}
|
|
20467
21477
|
const indent = block.indent || 0;
|
|
@@ -20487,16 +21497,31 @@ function blocksToSectionXml(blocks, theme, gongmun) {
|
|
|
20487
21497
|
case "hr":
|
|
20488
21498
|
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
21499
|
break;
|
|
20490
|
-
case "table":
|
|
20491
|
-
if (block.rows) {
|
|
21500
|
+
case "table":
|
|
21501
|
+
if (block.rows) {
|
|
21502
|
+
if (isFirst) {
|
|
21503
|
+
const secRun = `<hp:run charPrIDRef="0">${generateSecPr(gongmun)}<hp:t></hp:t></hp:run>`;
|
|
21504
|
+
paraXmls.push(`<hp:p paraPrIDRef="0" styleIDRef="0">${secRun}</hp:p>`);
|
|
21505
|
+
isFirst = false;
|
|
21506
|
+
}
|
|
21507
|
+
xml = generateTable(block.rows, theme);
|
|
21508
|
+
}
|
|
21509
|
+
break;
|
|
21510
|
+
case "html_table": {
|
|
21511
|
+
const tbl = generateHtmlTableXml(block.text || "", theme);
|
|
21512
|
+
if (tbl) {
|
|
20492
21513
|
if (isFirst) {
|
|
20493
21514
|
const secRun = `<hp:run charPrIDRef="0">${generateSecPr(gongmun)}<hp:t></hp:t></hp:run>`;
|
|
20494
21515
|
paraXmls.push(`<hp:p paraPrIDRef="0" styleIDRef="0">${secRun}</hp:p>`);
|
|
20495
21516
|
isFirst = false;
|
|
20496
21517
|
}
|
|
20497
|
-
xml =
|
|
21518
|
+
xml = `<hp:p paraPrIDRef="0" styleIDRef="0"><hp:run charPrIDRef="0">${tbl}</hp:run></hp:p>`;
|
|
21519
|
+
} else {
|
|
21520
|
+
const plain = (block.text || "").replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
|
|
21521
|
+
xml = plain ? generateParagraph(plain) : "";
|
|
20498
21522
|
}
|
|
20499
21523
|
break;
|
|
21524
|
+
}
|
|
20500
21525
|
}
|
|
20501
21526
|
if (!xml) continue;
|
|
20502
21527
|
if (isFirst && block.type !== "table") {
|
|
@@ -20517,49 +21542,6 @@ function blocksToSectionXml(blocks, theme, gongmun) {
|
|
|
20517
21542
|
</hs:sec>`;
|
|
20518
21543
|
}
|
|
20519
21544
|
|
|
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]);
|
|
20555
|
-
}
|
|
20556
|
-
}
|
|
20557
|
-
;
|
|
20558
|
-
[prev, curr] = [curr, prev];
|
|
20559
|
-
}
|
|
20560
|
-
return prev[m];
|
|
20561
|
-
}
|
|
20562
|
-
|
|
20563
21545
|
// src/diff/compare.ts
|
|
20564
21546
|
var SIMILARITY_THRESHOLD = 0.4;
|
|
20565
21547
|
async function compare(bufferA, bufferB, options) {
|
|
@@ -20687,310 +21669,241 @@ function diffTableCells(a, b) {
|
|
|
20687
21669
|
else if (cellB === void 0) type = "removed";
|
|
20688
21670
|
else if (cellA === cellB) type = "unchanged";
|
|
20689
21671
|
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
|
-
});
|
|
21672
|
+
row.push({ type, before: cellA, after: cellB });
|
|
20896
21673
|
}
|
|
20897
|
-
|
|
21674
|
+
result.push(row);
|
|
20898
21675
|
}
|
|
20899
21676
|
return result;
|
|
20900
21677
|
}
|
|
20901
|
-
|
|
20902
|
-
|
|
20903
|
-
|
|
20904
|
-
|
|
20905
|
-
|
|
20906
|
-
|
|
20907
|
-
|
|
20908
|
-
|
|
20909
|
-
|
|
20910
|
-
|
|
20911
|
-
|
|
20912
|
-
|
|
20913
|
-
|
|
20914
|
-
|
|
20915
|
-
|
|
20916
|
-
|
|
21678
|
+
|
|
21679
|
+
// src/roundtrip/patcher.ts
|
|
21680
|
+
import JSZip6 from "jszip";
|
|
21681
|
+
|
|
21682
|
+
// src/roundtrip/table-rows.ts
|
|
21683
|
+
var ROW_OBJECT_RE = /<(?:[A-Za-z0-9_]+:)?(?:tbl|pic|equation|ole|container|shape|drawingObject|drawText|video|chart|fieldBegin|fieldEnd|ctrl)\b/;
|
|
21684
|
+
var TAG_AT_RE = /<[A-Za-z0-9_:]+(?:"[^"]*"|'[^']*'|[^>"'])*>/y;
|
|
21685
|
+
function patchTableRows(input) {
|
|
21686
|
+
const { table, scanTable, ctx, skip, origKeys, editedKeys } = input;
|
|
21687
|
+
const xml = ctx.scans[scanTable.sectionIndex]?.xml;
|
|
21688
|
+
if (!xml) return skip("\uC139\uC158 XML \uB9E4\uD551 \uC2E4\uD328");
|
|
21689
|
+
const numRows = table.rows;
|
|
21690
|
+
if (origKeys.length !== numRows) return skip("\uD45C \uD589 \uC88C\uD45C \uBD88\uC77C\uCE58 \u2014 \uD589 \uCD94\uAC00/\uC0AD\uC81C \uBBF8\uC9C0\uC6D0");
|
|
21691
|
+
if (scanTable.rows.length !== numRows || scanTable.rowRanges.length !== numRows) {
|
|
21692
|
+
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");
|
|
21693
|
+
}
|
|
21694
|
+
for (let r = 0; r < numRows; r++) {
|
|
21695
|
+
if (scanTable.rows[r].some((c) => c.rowAddr !== r)) {
|
|
21696
|
+
return skip("\uD589 \uC8FC\uC18C \uBE44\uC5F0\uC18D \u2014 \uD589 \uCD94\uAC00/\uC0AD\uC81C \uBBF8\uC9C0\uC6D0");
|
|
20917
21697
|
}
|
|
20918
|
-
|
|
20919
|
-
|
|
20920
|
-
|
|
20921
|
-
|
|
20922
|
-
|
|
20923
|
-
|
|
21698
|
+
}
|
|
21699
|
+
const allCells = scanTable.rows.flat();
|
|
21700
|
+
const explicitAddr = (c) => !!c.addrTagRange && /\browAddr\s*=\s*"/.test(xml.slice(c.addrTagRange.start, c.addrTagRange.end));
|
|
21701
|
+
const explicitCount = allCells.filter(explicitAddr).length;
|
|
21702
|
+
if (explicitCount !== 0 && explicitCount !== allCells.length) {
|
|
21703
|
+
return skip("\uC140 \uC8FC\uC18C(cellAddr) \uD45C\uAE30 \uD63C\uC7AC \u2014 \uD589 \uCD94\uAC00/\uC0AD\uC81C \uBBF8\uC9C0\uC6D0");
|
|
21704
|
+
}
|
|
21705
|
+
const hasExplicitAddr = explicitCount > 0;
|
|
21706
|
+
const pairs = alignUnits(origKeys, editedKeys);
|
|
21707
|
+
const seq = [];
|
|
21708
|
+
let lastOrig = -1;
|
|
21709
|
+
for (const [oi, ei] of pairs) {
|
|
21710
|
+
if (oi !== null && ei !== null) {
|
|
21711
|
+
seq.push({ kind: "keep", oi, ei });
|
|
21712
|
+
lastOrig = oi;
|
|
21713
|
+
} else if (oi !== null) {
|
|
21714
|
+
seq.push({ kind: "del", oi });
|
|
21715
|
+
lastOrig = oi;
|
|
21716
|
+
} else if (ei !== null) seq.push({ kind: "ins", ei, insertAt: lastOrig + 1 });
|
|
21717
|
+
}
|
|
21718
|
+
const dels = seq.filter((e) => e.kind === "del");
|
|
21719
|
+
const inss = seq.filter((e) => e.kind === "ins");
|
|
21720
|
+
const keeps = seq.filter((e) => e.kind === "keep");
|
|
21721
|
+
if (dels.length === 0 && inss.length === 0) {
|
|
21722
|
+
return skip("\uD45C \uD589 \uC815\uB82C \uC2E4\uD328 \u2014 \uD589 \uCD94\uAC00/\uC0AD\uC81C \uBBF8\uC9C0\uC6D0");
|
|
21723
|
+
}
|
|
21724
|
+
if (keeps.length + inss.length === 0) return skip("\uBAA8\uB4E0 \uD589 \uC0AD\uC81C\uB294 \uBBF8\uC9C0\uC6D0");
|
|
21725
|
+
if (keeps.length === 0) return skip("\uD589 \uC804\uBA74 \uAD50\uCCB4 \u2014 \uC11C\uC2DD \uAE30\uC900 \uD589\uC774 \uC5C6\uC5B4 \uBBF8\uC9C0\uC6D0");
|
|
21726
|
+
const spans = allCells.filter((c) => (c.rowSpan ?? 1) > 1);
|
|
21727
|
+
for (const d of dels) {
|
|
21728
|
+
if (spans.some((s) => s.rowAddr <= d.oi && s.rowAddr + s.rowSpan > d.oi)) {
|
|
21729
|
+
return skip(`\uD45C ${d.oi + 1}\uD589 \uC0AD\uC81C\uAC00 \uC138\uB85C \uBCD1\uD569\uACFC \uACB9\uCE68 \u2014 \uBBF8\uC9C0\uC6D0`);
|
|
21730
|
+
}
|
|
21731
|
+
}
|
|
21732
|
+
for (const ins of inss) {
|
|
21733
|
+
const p = ins.insertAt;
|
|
21734
|
+
if (spans.some((s) => s.rowAddr < p && s.rowAddr + s.rowSpan > p)) {
|
|
21735
|
+
return skip("\uD589 \uC0BD\uC785 \uC704\uCE58\uAC00 \uC138\uB85C \uBCD1\uD569 \uB0B4\uBD80 \u2014 \uBBF8\uC9C0\uC6D0");
|
|
21736
|
+
}
|
|
21737
|
+
}
|
|
21738
|
+
for (const d of dels) {
|
|
21739
|
+
const rr = scanTable.rowRanges[d.oi];
|
|
21740
|
+
if (ROW_OBJECT_RE.test(xml.slice(rr.start, rr.end))) {
|
|
21741
|
+
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`);
|
|
21742
|
+
}
|
|
21743
|
+
}
|
|
21744
|
+
const keptSet = new Set(keeps.map((k) => k.oi));
|
|
21745
|
+
const insertPlans = [];
|
|
21746
|
+
for (const ins of inss) {
|
|
21747
|
+
const p = ins.insertAt;
|
|
21748
|
+
const kept = [...keptSet].sort((a, b) => a - b);
|
|
21749
|
+
let template = -1;
|
|
21750
|
+
for (const k of kept) {
|
|
21751
|
+
if (k < p) template = k;
|
|
21752
|
+
}
|
|
21753
|
+
if (template <= 0 && p >= 1) {
|
|
21754
|
+
const following = kept.find((k) => k >= 1);
|
|
21755
|
+
if (following !== void 0) template = following;
|
|
21756
|
+
}
|
|
21757
|
+
if (template < 0) template = kept[0];
|
|
21758
|
+
const cells = input.editedCells(ins.ei);
|
|
21759
|
+
if (!cells) return skip("\uC0BD\uC785 \uD589\uC5D0 \uC774\uBBF8\uC9C0/\uC911\uCCA9\uD45C \uD3EC\uD568 \u2014 \uD589 \uCD94\uAC00 \uBBF8\uC9C0\uC6D0");
|
|
21760
|
+
for (const cell of cells) {
|
|
21761
|
+
const unstable = cell.lines.find((l) => sanitizeText(l) !== l);
|
|
21762
|
+
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");
|
|
21763
|
+
}
|
|
21764
|
+
const tmplCells = scanTable.rows[template];
|
|
21765
|
+
const rr = scanTable.rowRanges[template];
|
|
21766
|
+
if (ROW_OBJECT_RE.test(xml.slice(rr.start, rr.end))) {
|
|
21767
|
+
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");
|
|
21768
|
+
}
|
|
21769
|
+
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");
|
|
21770
|
+
const tmplWidth = tmplCells.reduce((s, c) => s + c.colSpan, 0);
|
|
21771
|
+
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");
|
|
21772
|
+
if (cells.length !== tmplCells.length) {
|
|
21773
|
+
return skip(`\uC0BD\uC785 \uD589 \uC140 \uC218(${cells.length}) \u2260 \uAE30\uC900 \uD589 \uC140 \uC218(${tmplCells.length}) \u2014 \uBBF8\uC9C0\uC6D0`);
|
|
21774
|
+
}
|
|
21775
|
+
for (let i = 0; i < cells.length; i++) {
|
|
21776
|
+
if (cells[i].rowSpan !== 1 || cells[i].colSpan !== tmplCells[i].colSpan) {
|
|
21777
|
+
return skip("\uC0BD\uC785 \uD589 \uBCD1\uD569 \uAD6C\uC870\uAC00 \uAE30\uC900 \uD589\uACFC \uB2E4\uB984 \u2014 \uBBF8\uC9C0\uC6D0");
|
|
20924
21778
|
}
|
|
20925
|
-
}
|
|
20926
|
-
|
|
20927
|
-
|
|
20928
|
-
|
|
20929
|
-
|
|
20930
|
-
|
|
20931
|
-
|
|
20932
|
-
|
|
20933
|
-
|
|
20934
|
-
|
|
21779
|
+
}
|
|
21780
|
+
insertPlans.push({ entry: ins, template, cells });
|
|
21781
|
+
}
|
|
21782
|
+
const net = inss.length - dels.length;
|
|
21783
|
+
TAG_AT_RE.lastIndex = scanTable.start;
|
|
21784
|
+
const tblOpen = TAG_AT_RE.exec(xml);
|
|
21785
|
+
if (!tblOpen || tblOpen.index !== scanTable.start) return skip("\uD45C \uC5EC\uB294 \uD0DC\uADF8 \uD574\uC11D \uC2E4\uD328");
|
|
21786
|
+
const rowCntM = tblOpen[0].match(/\browCnt\s*=\s*"(\d+)"/);
|
|
21787
|
+
if (!rowCntM || rowCntM.index === void 0) return skip("\uD45C rowCnt \uC18D\uC131 \uC5C6\uC74C \u2014 \uD589 \uCD94\uAC00/\uC0AD\uC81C \uBBF8\uC9C0\uC6D0");
|
|
21788
|
+
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");
|
|
21789
|
+
const finalIndex = /* @__PURE__ */ new Map();
|
|
21790
|
+
{
|
|
21791
|
+
let fi = 0;
|
|
21792
|
+
for (const e of seq) {
|
|
21793
|
+
if (e.kind !== "del") finalIndex.set(e, fi++);
|
|
21794
|
+
}
|
|
21795
|
+
}
|
|
21796
|
+
const splices = [];
|
|
21797
|
+
let applied = 0;
|
|
21798
|
+
for (const d of dels) {
|
|
21799
|
+
const rr = scanTable.rowRanges[d.oi];
|
|
21800
|
+
splices.push({ start: rr.start, end: rr.end, replacement: "" });
|
|
21801
|
+
applied++;
|
|
21802
|
+
}
|
|
21803
|
+
const fragmentsByAnchor = /* @__PURE__ */ new Map();
|
|
21804
|
+
let heightDelta = 0;
|
|
21805
|
+
for (const plan of insertPlans) {
|
|
21806
|
+
const finalRow = finalIndex.get(plan.entry);
|
|
21807
|
+
const fragment = buildRowFragment(xml, scanTable, plan.template, plan.cells, finalRow, hasExplicitAddr, ctx);
|
|
21808
|
+
if (fragment === null) return skip("\uD589 \uBCF5\uC81C \uC2E4\uD328 (\uC140 \uBB38\uB2E8 \uAD6C\uC870 \uBBF8\uC9C0\uC6D0)");
|
|
21809
|
+
const p = plan.entry.insertAt;
|
|
21810
|
+
const anchor = p === 0 ? scanTable.rowRanges[0].start : scanTable.rowRanges[p - 1].end;
|
|
21811
|
+
let list = fragmentsByAnchor.get(anchor);
|
|
21812
|
+
if (!list) fragmentsByAnchor.set(anchor, list = []);
|
|
21813
|
+
list.push(fragment);
|
|
21814
|
+
heightDelta += rowHeightOf(fragment);
|
|
21815
|
+
applied++;
|
|
21816
|
+
}
|
|
21817
|
+
for (const [anchor, fragments] of fragmentsByAnchor) {
|
|
21818
|
+
splices.push({ start: anchor, end: anchor, replacement: fragments.join("") });
|
|
21819
|
+
}
|
|
21820
|
+
for (const d of dels) {
|
|
21821
|
+
const rr = scanTable.rowRanges[d.oi];
|
|
21822
|
+
heightDelta -= rowHeightOf(xml.slice(rr.start, rr.end));
|
|
21823
|
+
}
|
|
21824
|
+
if (hasExplicitAddr) {
|
|
21825
|
+
for (const k of keeps) {
|
|
21826
|
+
const fi = finalIndex.get(k);
|
|
21827
|
+
if (fi === k.oi) continue;
|
|
21828
|
+
for (const cell of scanTable.rows[k.oi]) {
|
|
21829
|
+
const sp = rowAddrRewrite(xml, cell, fi);
|
|
21830
|
+
if (sp) splices.push(sp);
|
|
20935
21831
|
}
|
|
20936
21832
|
}
|
|
20937
21833
|
}
|
|
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);
|
|
21834
|
+
{
|
|
21835
|
+
const valStart = scanTable.start + rowCntM.index + rowCntM[0].indexOf('"') + 1;
|
|
21836
|
+
splices.push({ start: valStart, end: valStart + rowCntM[1].length, replacement: String(numRows + net) });
|
|
20947
21837
|
}
|
|
20948
|
-
if (
|
|
20949
|
-
|
|
20950
|
-
|
|
21838
|
+
if (heightDelta !== 0) {
|
|
21839
|
+
const sp = tableSzHeightSplice(xml, scanTable, tblOpen[0].length, heightDelta);
|
|
21840
|
+
if (sp) splices.push(sp);
|
|
20951
21841
|
}
|
|
20952
|
-
|
|
20953
|
-
|
|
21842
|
+
ctx.sectionSplices[scanTable.sectionIndex].push(...splices);
|
|
21843
|
+
for (const k of keeps) {
|
|
21844
|
+
if (origKeys[k.oi] !== editedKeys[k.ei]) applied += input.patchMatched(k.oi, k.ei);
|
|
21845
|
+
}
|
|
21846
|
+
return applied;
|
|
20954
21847
|
}
|
|
20955
|
-
|
|
20956
|
-
|
|
20957
|
-
|
|
20958
|
-
|
|
20959
|
-
const
|
|
20960
|
-
|
|
20961
|
-
|
|
20962
|
-
|
|
20963
|
-
|
|
20964
|
-
|
|
20965
|
-
|
|
20966
|
-
|
|
20967
|
-
|
|
20968
|
-
|
|
20969
|
-
|
|
20970
|
-
}
|
|
20971
|
-
|
|
21848
|
+
var FRAG_OPEN = "<hp:tbl>";
|
|
21849
|
+
var FRAG_CLOSE = "</hp:tbl>";
|
|
21850
|
+
function buildRowFragment(xml, scanTable, template, cells, finalRow, hasExplicitAddr, ctx) {
|
|
21851
|
+
const rr = scanTable.rowRanges[template];
|
|
21852
|
+
const wrapped = FRAG_OPEN + xml.slice(rr.start, rr.end) + FRAG_CLOSE;
|
|
21853
|
+
const scan = scanSectionXml(wrapped, 0);
|
|
21854
|
+
const row = scan.tables[0]?.rows[0];
|
|
21855
|
+
if (!row || row.length !== cells.length) return null;
|
|
21856
|
+
const splices = allLinesegRemovalSplices(wrapped);
|
|
21857
|
+
for (let i = 0; i < cells.length; i++) {
|
|
21858
|
+
const paras = row[i].paragraphs;
|
|
21859
|
+
let lines = cells[i].lines;
|
|
21860
|
+
if (lines.length > 0 && paras.length === 0) return null;
|
|
21861
|
+
if (lines.length > paras.length) {
|
|
21862
|
+
lines = [...lines.slice(0, paras.length - 1), lines.slice(paras.length - 1).join(" ")];
|
|
21863
|
+
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 });
|
|
21864
|
+
}
|
|
21865
|
+
for (let p = 0; p < paras.length; p++) {
|
|
21866
|
+
const sp = buildParagraphSplices(paras[p], lines[p] ?? "", wrapped);
|
|
21867
|
+
if (sp === null) return null;
|
|
21868
|
+
splices.push(...sp);
|
|
21869
|
+
}
|
|
21870
|
+
if (hasExplicitAddr) {
|
|
21871
|
+
const sp = rowAddrRewrite(wrapped, row[i], finalRow);
|
|
21872
|
+
if (sp) splices.push(sp);
|
|
20972
21873
|
}
|
|
20973
21874
|
}
|
|
20974
|
-
|
|
21875
|
+
const patched = applySplices(wrapped, splices);
|
|
21876
|
+
return patched.slice(FRAG_OPEN.length, patched.length - FRAG_CLOSE.length);
|
|
20975
21877
|
}
|
|
20976
|
-
function
|
|
20977
|
-
|
|
20978
|
-
|
|
20979
|
-
const
|
|
20980
|
-
|
|
20981
|
-
|
|
20982
|
-
|
|
20983
|
-
|
|
20984
|
-
|
|
20985
|
-
|
|
20986
|
-
|
|
20987
|
-
|
|
20988
|
-
|
|
20989
|
-
|
|
20990
|
-
}
|
|
21878
|
+
function rowAddrRewrite(xml, cell, newRow) {
|
|
21879
|
+
if (!cell.addrTagRange) return null;
|
|
21880
|
+
const tag = xml.slice(cell.addrTagRange.start, cell.addrTagRange.end);
|
|
21881
|
+
const m = tag.match(/\browAddr\s*=\s*"(\d+)"/);
|
|
21882
|
+
if (!m || m.index === void 0) return null;
|
|
21883
|
+
if (parseInt(m[1], 10) === newRow) return null;
|
|
21884
|
+
const valStart = cell.addrTagRange.start + m.index + m[0].indexOf('"') + 1;
|
|
21885
|
+
return { start: valStart, end: valStart + m[1].length, replacement: String(newRow) };
|
|
21886
|
+
}
|
|
21887
|
+
function rowHeightOf(fragment) {
|
|
21888
|
+
let max = 0;
|
|
21889
|
+
for (const m of fragment.matchAll(/<(?:[A-Za-z0-9_]+:)?cellSz\b(?:"[^"]*"|'[^']*'|[^>"'])*>/g)) {
|
|
21890
|
+
const h = m[0].match(/\bheight\s*=\s*"(\d+)"/);
|
|
21891
|
+
if (h) max = Math.max(max, parseInt(h[1], 10));
|
|
20991
21892
|
}
|
|
20992
|
-
|
|
20993
|
-
|
|
21893
|
+
return max;
|
|
21894
|
+
}
|
|
21895
|
+
function tableSzHeightSplice(xml, scanTable, tblOpenLen, delta) {
|
|
21896
|
+
const from = scanTable.start + tblOpenLen;
|
|
21897
|
+
const to = scanTable.rowRanges[0]?.start ?? from;
|
|
21898
|
+
const slice = xml.slice(from, to);
|
|
21899
|
+
const szM = slice.match(/<(?:[A-Za-z0-9_]+:)?sz\b(?:"[^"]*"|'[^']*'|[^>"'])*>/);
|
|
21900
|
+
if (!szM || szM.index === void 0) return null;
|
|
21901
|
+
const hM = szM[0].match(/\bheight\s*=\s*"(\d+)"/);
|
|
21902
|
+
if (!hM || hM.index === void 0) return null;
|
|
21903
|
+
const oldH = parseInt(hM[1], 10);
|
|
21904
|
+
const newH = Math.max(0, oldH + delta);
|
|
21905
|
+
const valStart = from + szM.index + hM.index + hM[0].indexOf('"') + 1;
|
|
21906
|
+
return { start: valStart, end: valStart + hM[1].length, replacement: String(newH) };
|
|
20994
21907
|
}
|
|
20995
21908
|
|
|
20996
21909
|
// src/roundtrip/table-patch.ts
|
|
@@ -21002,37 +21915,89 @@ function patchGfmTable(table, scanTable, orig, edited, ctx, skip) {
|
|
|
21002
21915
|
if (replica.length !== origRows.length || replica.some((row, r) => row.length !== origRows[r].length || row.some((c, j) => c.text !== origRows[r][j]))) {
|
|
21003
21916
|
return skip("\uD45C \uC88C\uD45C \uC7AC\uD604 \uBD88\uC77C\uCE58 \u2014 \uB9E4\uD551 \uC2E0\uB8B0 \uBD88\uAC00");
|
|
21004
21917
|
}
|
|
21005
|
-
if (editedRows.length !== origRows.length)
|
|
21918
|
+
if (editedRows.length !== origRows.length) {
|
|
21919
|
+
return patchGfmTableRows(table, scanTable, origRows, editedRows, replica, ctx, skip);
|
|
21920
|
+
}
|
|
21006
21921
|
let applied = 0;
|
|
21007
21922
|
for (let r = 0; r < origRows.length; r++) {
|
|
21008
|
-
|
|
21009
|
-
|
|
21923
|
+
applied += patchGfmRowPair(table, scanTable, origRows, editedRows, replica, r, r, ctx, skip);
|
|
21924
|
+
}
|
|
21925
|
+
return applied;
|
|
21926
|
+
}
|
|
21927
|
+
function patchGfmRowPair(table, scanTable, origRows, editedRows, replica, r, er, ctx, skip) {
|
|
21928
|
+
if (editedRows[er].length !== origRows[r].length) {
|
|
21929
|
+
skip(`\uD45C ${r + 1}\uD589 \uC5F4 \uC218 \uBCC0\uACBD\uC740 \uBBF8\uC9C0\uC6D0`);
|
|
21930
|
+
return 0;
|
|
21931
|
+
}
|
|
21932
|
+
let applied = 0;
|
|
21933
|
+
for (let c = 0; c < origRows[r].length; c++) {
|
|
21934
|
+
if (origRows[r][c] === editedRows[er][c]) continue;
|
|
21935
|
+
const { gridR, gridC } = replica[r][c];
|
|
21936
|
+
const origTokens = extractCellTokens(origRows[r][c]);
|
|
21937
|
+
const editedTokens = extractCellTokens(editedRows[er][c]);
|
|
21938
|
+
if (origTokens !== editedTokens) {
|
|
21939
|
+
skip("\uC140 \uB0B4 \uC774\uBBF8\uC9C0 \uBCC0\uACBD\uC740 \uBBF8\uC9C0\uC6D0");
|
|
21010
21940
|
continue;
|
|
21011
21941
|
}
|
|
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;
|
|
21942
|
+
const newLines = unescapeGfmCell(stripCellTokens(editedRows[er][c])).split("\n").map((s) => s.trim()).filter(Boolean);
|
|
21943
|
+
const origLines = unescapeGfmCell(stripCellTokens(origRows[r][c])).split("\n").map((s) => s.trim()).filter(Boolean);
|
|
21944
|
+
const n = applyCellEdit(table, scanTable, gridR, gridC, newLines, ctx, origRows[r][c], editedRows[er][c], origLines.length);
|
|
21945
|
+
if (n > 0 && origTokens) {
|
|
21946
|
+
ctx.skipped.push({
|
|
21947
|
+
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)",
|
|
21948
|
+
before: summarize(origRows[r][c]),
|
|
21949
|
+
after: summarize(editedRows[er][c]),
|
|
21950
|
+
partial: true
|
|
21951
|
+
});
|
|
21032
21952
|
}
|
|
21953
|
+
applied += n;
|
|
21033
21954
|
}
|
|
21034
21955
|
return applied;
|
|
21035
21956
|
}
|
|
21957
|
+
function patchGfmTableRows(table, scanTable, origRows, editedRows, replica, ctx, skip) {
|
|
21958
|
+
if (replica.length !== table.rows || replica.some((row, r) => row.some((c) => c.gridR !== r))) {
|
|
21959
|
+
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");
|
|
21960
|
+
}
|
|
21961
|
+
if (table.cells.some((row) => row.some((c) => c && (c.colSpan > 1 || c.rowSpan > 1)))) {
|
|
21962
|
+
return skip("\uBCD1\uD569 \uC140 \uD45C \u2014 GFM \uD589 \uCD94\uAC00/\uC0AD\uC81C \uBBF8\uC9C0\uC6D0");
|
|
21963
|
+
}
|
|
21964
|
+
if (!gfmRenderStable(editedRows, table.cols)) {
|
|
21965
|
+
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");
|
|
21966
|
+
}
|
|
21967
|
+
const key = (row) => row.join("\0");
|
|
21968
|
+
return patchTableRows({
|
|
21969
|
+
table,
|
|
21970
|
+
scanTable,
|
|
21971
|
+
ctx,
|
|
21972
|
+
skip,
|
|
21973
|
+
origKeys: origRows.map(key),
|
|
21974
|
+
editedKeys: editedRows.map(key),
|
|
21975
|
+
editedCells: (ei) => {
|
|
21976
|
+
const cells = [];
|
|
21977
|
+
for (const cellMd of editedRows[ei]) {
|
|
21978
|
+
if (extractCellTokens(cellMd)) return null;
|
|
21979
|
+
const lines = unescapeGfmCell(cellMd).split("\n").map((s) => s.trim()).filter(Boolean);
|
|
21980
|
+
cells.push({ lines, colSpan: 1, rowSpan: 1 });
|
|
21981
|
+
}
|
|
21982
|
+
return cells;
|
|
21983
|
+
},
|
|
21984
|
+
patchMatched: (oi, ei) => patchGfmRowPair(table, scanTable, origRows, editedRows, replica, oi, ei, ctx, skip)
|
|
21985
|
+
});
|
|
21986
|
+
}
|
|
21987
|
+
function gfmRenderStable(editedRows, cols) {
|
|
21988
|
+
const sim = {
|
|
21989
|
+
rows: editedRows.length,
|
|
21990
|
+
cols,
|
|
21991
|
+
hasHeader: editedRows.length > 1,
|
|
21992
|
+
cells: editedRows.map((row) => {
|
|
21993
|
+
const padded = row.length < cols ? [...row, ...Array(cols - row.length).fill("")] : row;
|
|
21994
|
+
return padded.map((md2) => ({ text: unescapeGfmCell(md2), colSpan: 1, rowSpan: 1 }));
|
|
21995
|
+
})
|
|
21996
|
+
};
|
|
21997
|
+
const replica = replicateGfmTable(sim);
|
|
21998
|
+
if (!replica || replica.length !== editedRows.length) return false;
|
|
21999
|
+
return replica.every((row, r) => row.length === editedRows[r].length && row.every((c, j) => c.text === editedRows[r][j]));
|
|
22000
|
+
}
|
|
21036
22001
|
function patchHtmlTable(table, scanTable, orig, edited, ctx, skip) {
|
|
21037
22002
|
return patchHtmlTableRaw(table, scanTable, orig.raw, edited.raw, ctx, skip, 0);
|
|
21038
22003
|
}
|
|
@@ -21046,46 +22011,77 @@ function patchHtmlTableRaw(table, scanTable, origRaw, editedRaw, ctx, skip, dept
|
|
|
21046
22011
|
}
|
|
21047
22012
|
const editedRows = parseHtmlTable(editedRaw);
|
|
21048
22013
|
if (!editedRows) return skip("\uD3B8\uC9D1\uB41C HTML \uD45C \uD30C\uC2F1 \uC2E4\uD328");
|
|
21049
|
-
if (editedRows.length !== replica.length)
|
|
22014
|
+
if (editedRows.length !== replica.length) {
|
|
22015
|
+
return patchHtmlTableRows(table, scanTable, replica, editedRows, ctx, skip, depth);
|
|
22016
|
+
}
|
|
21050
22017
|
let applied = 0;
|
|
21051
22018
|
for (let r = 0; r < replica.length; r++) {
|
|
21052
|
-
|
|
21053
|
-
|
|
22019
|
+
applied += patchHtmlRowPair(table, scanTable, replica, editedRows, r, r, ctx, skip, depth);
|
|
22020
|
+
}
|
|
22021
|
+
return applied;
|
|
22022
|
+
}
|
|
22023
|
+
function patchHtmlRowPair(table, scanTable, replica, editedRows, r, er, ctx, skip, depth) {
|
|
22024
|
+
if (editedRows[er].cells.length !== replica[r].cells.length) {
|
|
22025
|
+
skip(`\uD45C ${r + 1}\uD589 \uC140 \uC218 \uBCC0\uACBD\uC740 \uBBF8\uC9C0\uC6D0`);
|
|
22026
|
+
return 0;
|
|
22027
|
+
}
|
|
22028
|
+
let applied = 0;
|
|
22029
|
+
for (let c = 0; c < replica[r].cells.length; c++) {
|
|
22030
|
+
const oc = replica[r].cells[c];
|
|
22031
|
+
const ec = editedRows[er].cells[c];
|
|
22032
|
+
if (oc.colSpan !== ec.colSpan || oc.rowSpan !== ec.rowSpan) {
|
|
22033
|
+
skip(`\uC140 \uBCD1\uD569(colspan/rowspan) \uBCC0\uACBD\uC740 \uBBF8\uC9C0\uC6D0`);
|
|
21054
22034
|
continue;
|
|
21055
22035
|
}
|
|
21056
|
-
|
|
21057
|
-
|
|
21058
|
-
|
|
21059
|
-
|
|
21060
|
-
|
|
22036
|
+
if (oc.inner === ec.inner) continue;
|
|
22037
|
+
const origContent = htmlCellInnerToLines(oc.inner);
|
|
22038
|
+
const editedContent = htmlCellInnerToLines(ec.inner);
|
|
22039
|
+
if (origContent.hadNonText || editedContent.hadNonText) {
|
|
22040
|
+
if (extractImgTags(oc.inner) !== extractImgTags(ec.inner)) {
|
|
22041
|
+
skip("\uC140 \uB0B4 \uC774\uBBF8\uC9C0 \uBCC0\uACBD\uC740 \uBBF8\uC9C0\uC6D0");
|
|
21061
22042
|
continue;
|
|
21062
22043
|
}
|
|
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
|
-
}
|
|
22044
|
+
const origTables = extractTopLevelTables(oc.inner);
|
|
22045
|
+
const editedTables = extractTopLevelTables(ec.inner);
|
|
22046
|
+
if (origTables.length !== editedTables.length) {
|
|
22047
|
+
skip("\uC140 \uB0B4 \uC911\uCCA9\uD45C \uCD94\uAC00/\uC0AD\uC81C\uB294 \uBBF8\uC9C0\uC6D0");
|
|
22048
|
+
continue;
|
|
21080
22049
|
}
|
|
21081
|
-
if (
|
|
21082
|
-
|
|
21083
|
-
applied += applyCellEdit(table, scanTable, oc.gridR, oc.gridC, newLines, ctx, oc.inner, ec.inner, origContent.lines.length);
|
|
22050
|
+
if (origTables.join("\n") !== editedTables.join("\n")) {
|
|
22051
|
+
applied += patchNestedTables(table, scanTable, oc, origTables, editedTables, ctx, skip, depth);
|
|
21084
22052
|
}
|
|
21085
22053
|
}
|
|
22054
|
+
if (origContent.lines.join("\n") !== editedContent.lines.join("\n")) {
|
|
22055
|
+
const newLines = editedContent.lines.map((l) => unescapeGfm(l));
|
|
22056
|
+
applied += applyCellEdit(table, scanTable, oc.gridR, oc.gridC, newLines, ctx, oc.inner, ec.inner, origContent.lines.length);
|
|
22057
|
+
}
|
|
21086
22058
|
}
|
|
21087
22059
|
return applied;
|
|
21088
22060
|
}
|
|
22061
|
+
function patchHtmlTableRows(table, scanTable, replica, editedRows, ctx, skip, depth) {
|
|
22062
|
+
if (replica.length !== table.rows || replica.some((row, r) => row.cells.some((c) => c.gridR !== r))) {
|
|
22063
|
+
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");
|
|
22064
|
+
}
|
|
22065
|
+
const key = (row) => row.cells.map((c) => `${c.colSpan}x${c.rowSpan}:${c.inner}`).join("\0");
|
|
22066
|
+
return patchTableRows({
|
|
22067
|
+
table,
|
|
22068
|
+
scanTable,
|
|
22069
|
+
ctx,
|
|
22070
|
+
skip,
|
|
22071
|
+
origKeys: replica.map(key),
|
|
22072
|
+
editedKeys: editedRows.map(key),
|
|
22073
|
+
editedCells: (ei) => {
|
|
22074
|
+
const cells = [];
|
|
22075
|
+
for (const cell of editedRows[ei].cells) {
|
|
22076
|
+
const content = htmlCellInnerToLines(cell.inner);
|
|
22077
|
+
if (content.hadNonText) return null;
|
|
22078
|
+
cells.push({ lines: content.lines.map((l) => unescapeGfm(l)), colSpan: cell.colSpan, rowSpan: cell.rowSpan });
|
|
22079
|
+
}
|
|
22080
|
+
return cells;
|
|
22081
|
+
},
|
|
22082
|
+
patchMatched: (oi, ei) => patchHtmlRowPair(table, scanTable, replica, editedRows, oi, ei, ctx, skip, depth)
|
|
22083
|
+
});
|
|
22084
|
+
}
|
|
21089
22085
|
function patchNestedTables(table, scanTable, oc, origTables, editedTables, ctx, skip, depth) {
|
|
21090
22086
|
const irCell = table.cells[oc.gridR]?.[oc.gridC];
|
|
21091
22087
|
const scanCell = scanTable.cellByAnchor.get(`${oc.gridR},${oc.gridC}`);
|
|
@@ -21185,7 +22181,7 @@ function applyCellEdit(table, scanTable, gridR, gridC, newLines, ctx, before, af
|
|
|
21185
22181
|
splices.push(...sp);
|
|
21186
22182
|
sectionIndex = target.sectionIndex;
|
|
21187
22183
|
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) });
|
|
22184
|
+
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
22185
|
}
|
|
21190
22186
|
} else {
|
|
21191
22187
|
const assigned = [];
|
|
@@ -21197,7 +22193,9 @@ function applyCellEdit(table, scanTable, gridR, gridC, newLines, ctx, before, af
|
|
|
21197
22193
|
}
|
|
21198
22194
|
}
|
|
21199
22195
|
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) });
|
|
22196
|
+
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 });
|
|
22197
|
+
} else if (newLines.length < nonEmpty.length && nonEmpty.length > 1) {
|
|
22198
|
+
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
22199
|
}
|
|
21202
22200
|
for (let i = 0; i < nonEmpty.length; i++) {
|
|
21203
22201
|
if (assigned[i] === nonEmpty[i].text || normForMatch(assigned[i]) === normForMatch(nonEmpty[i].text)) continue;
|
|
@@ -21370,7 +22368,8 @@ async function patchHwpx(original, editedMarkdown, options) {
|
|
|
21370
22368
|
try {
|
|
21371
22369
|
for (let i = 0; i < scans.length; i++) {
|
|
21372
22370
|
if (sectionSplices[i].length === 0) continue;
|
|
21373
|
-
sectionSplices[i].
|
|
22371
|
+
const claimed = sectionSplices[i].filter((s) => s.end > s.start);
|
|
22372
|
+
sectionSplices[i].push(...allLinesegRemovalSplices(scans[i].xml).filter((ls) => !claimed.some((c) => ls.start >= c.start && ls.end <= c.end)));
|
|
21374
22373
|
const newXml = applySplices(scans[i].xml, sectionSplices[i]);
|
|
21375
22374
|
replacements.set(sectionPaths[i], encoder.encode(newXml));
|
|
21376
22375
|
}
|
|
@@ -21438,82 +22437,6 @@ function buildTableOrdinals(blocks) {
|
|
|
21438
22437
|
}
|
|
21439
22438
|
return map;
|
|
21440
22439
|
}
|
|
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
22440
|
function resolveParagraphMappings(blocks, scans) {
|
|
21518
22441
|
const buckets = /* @__PURE__ */ new Map();
|
|
21519
22442
|
for (const scan of scans) {
|
|
@@ -21656,7 +22579,7 @@ function patchParagraphUnit(block, orig, edited, ctx, skip) {
|
|
|
21656
22579
|
const origPrefix = block.text.split(" ", 1)[0];
|
|
21657
22580
|
const sp = newPlain.indexOf(" ");
|
|
21658
22581
|
const newFirst = sp > 0 ? newPlain.slice(0, sp) : newPlain;
|
|
21659
|
-
if (newFirst === origPrefix ||
|
|
22582
|
+
if (newFirst === origPrefix || AUTONUM_PREFIX_RE.test(newFirst)) {
|
|
21660
22583
|
newPlain = sp > 0 ? newPlain.slice(sp + 1) : "";
|
|
21661
22584
|
} else {
|
|
21662
22585
|
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) });
|
|
@@ -22055,21 +22978,24 @@ function readRecordsStrict(stream) {
|
|
|
22055
22978
|
}
|
|
22056
22979
|
return recs;
|
|
22057
22980
|
}
|
|
22058
|
-
function serializeRecords(recs, repl) {
|
|
22981
|
+
function serializeRecords(recs, repl, inserts) {
|
|
22059
22982
|
const parts = [];
|
|
22060
|
-
|
|
22061
|
-
const data = repl?.get(i) ?? recs[i].data;
|
|
22983
|
+
const push = (tagId, level, data) => {
|
|
22062
22984
|
const ext = data.length >= 4095;
|
|
22063
22985
|
const header = Buffer.alloc(ext ? 8 : 4);
|
|
22064
|
-
header.writeUInt32LE((
|
|
22986
|
+
header.writeUInt32LE((tagId & 1023 | (level & 1023) << 10 | (ext ? 4095 : data.length) << 20) >>> 0, 0);
|
|
22065
22987
|
if (ext) header.writeUInt32LE(data.length, 4);
|
|
22066
22988
|
parts.push(header, data);
|
|
22989
|
+
};
|
|
22990
|
+
for (let i = 0; i < recs.length; i++) {
|
|
22991
|
+
for (const ins of inserts?.get(i) ?? []) push(ins.tagId, ins.level, ins.data);
|
|
22992
|
+
push(recs[i].tagId, recs[i].level, repl?.get(i) ?? recs[i].data);
|
|
22067
22993
|
}
|
|
22068
22994
|
return Buffer.concat(parts);
|
|
22069
22995
|
}
|
|
22070
22996
|
function scanSection(stream, sectionIndex, compressed) {
|
|
22071
22997
|
const records = readRecordsStrict(stream);
|
|
22072
|
-
if (!records) return { records: [], safe: false, paras: [], tables: [], compressed, repl: /* @__PURE__ */ new Map() };
|
|
22998
|
+
if (!records) return { records: [], safe: false, paras: [], tables: [], compressed, repl: /* @__PURE__ */ new Map(), inserts: /* @__PURE__ */ new Map() };
|
|
22073
22999
|
const safe = serializeRecords(records).equals(stream);
|
|
22074
23000
|
const parent = new Int32Array(records.length).fill(-1);
|
|
22075
23001
|
const stack = [];
|
|
@@ -22168,7 +23094,7 @@ function scanSection(stream, sectionIndex, compressed) {
|
|
|
22168
23094
|
}
|
|
22169
23095
|
tables.push({ sectionIndex, rows, cols, cells });
|
|
22170
23096
|
}
|
|
22171
|
-
return { records, safe, paras, tables, compressed, repl: /* @__PURE__ */ new Map() };
|
|
23097
|
+
return { records, safe, paras, tables, compressed, repl: /* @__PURE__ */ new Map(), inserts: /* @__PURE__ */ new Map() };
|
|
22172
23098
|
}
|
|
22173
23099
|
async function patchHwp(original, editedMarkdown, options) {
|
|
22174
23100
|
const skipped = [];
|
|
@@ -22236,15 +23162,15 @@ async function patchHwp(original, editedMarkdown, options) {
|
|
|
22236
23162
|
}
|
|
22237
23163
|
}
|
|
22238
23164
|
let data;
|
|
22239
|
-
const dirty = scans.some((s) => s.repl.size > 0);
|
|
23165
|
+
const dirty = scans.some((s) => s.repl.size > 0 || s.inserts.size > 0);
|
|
22240
23166
|
if (!dirty) {
|
|
22241
23167
|
data = new Uint8Array(original);
|
|
22242
23168
|
} else {
|
|
22243
23169
|
try {
|
|
22244
23170
|
let out = originalBuf;
|
|
22245
23171
|
for (let i = 0; i < scans.length; i++) {
|
|
22246
|
-
if (scans[i].repl.size === 0) continue;
|
|
22247
|
-
const newStream = serializeRecords(scans[i].records, scans[i].repl);
|
|
23172
|
+
if (scans[i].repl.size === 0 && scans[i].inserts.size === 0) continue;
|
|
23173
|
+
const newStream = serializeRecords(scans[i].records, scans[i].repl, scans[i].inserts);
|
|
22248
23174
|
const content = compressed ? deflateRawSync2(newStream) : newStream;
|
|
22249
23175
|
out = replaceOleStream(out, sectionPaths[i], content);
|
|
22250
23176
|
}
|
|
@@ -22399,7 +23325,7 @@ function patchParagraph(block, orig, edited, ctx, skip) {
|
|
|
22399
23325
|
const origPrefix = block.text.split(" ", 1)[0];
|
|
22400
23326
|
const sp = newPlain.indexOf(" ");
|
|
22401
23327
|
const newFirst = sp > 0 ? newPlain.slice(0, sp) : newPlain;
|
|
22402
|
-
if (newFirst === origPrefix ||
|
|
23328
|
+
if (newFirst === origPrefix || AUTONUM_PREFIX_RE.test(newFirst)) {
|
|
22403
23329
|
newPlain = sp > 0 ? newPlain.slice(sp + 1) : "";
|
|
22404
23330
|
} else {
|
|
22405
23331
|
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 +23390,6 @@ function patchGfmCells(scanTable, orig, edited, ctx, skip) {
|
|
|
22464
23390
|
}
|
|
22465
23391
|
return applied;
|
|
22466
23392
|
}
|
|
22467
|
-
function extractImgTags5(inner) {
|
|
22468
|
-
return (inner.match(/<img\s(?:"[^"]*"|'[^']*'|[^>"'])*?>/gi) || []).join(" ");
|
|
22469
|
-
}
|
|
22470
23393
|
function patchHtmlCells5(table, scanTable, orig, edited, ctx, skip) {
|
|
22471
23394
|
if (replicateTableToHtml(table) !== orig.raw) return skip("\uD45C \uC88C\uD45C \uC7AC\uD604 \uBD88\uC77C\uCE58 \u2014 \uB9E4\uD551 \uC2E0\uB8B0 \uBD88\uAC00");
|
|
22472
23395
|
const replica = replicateHtmlTable(table);
|
|
@@ -22494,7 +23417,7 @@ function patchHtmlCells5(table, scanTable, orig, edited, ctx, skip) {
|
|
|
22494
23417
|
const origContent = htmlCellInnerToLines(oc.inner);
|
|
22495
23418
|
const editedContent = htmlCellInnerToLines(ec.inner);
|
|
22496
23419
|
if (origContent.hadNonText || editedContent.hadNonText) {
|
|
22497
|
-
if (
|
|
23420
|
+
if (extractImgTags(oc.inner) !== extractImgTags(ec.inner)) {
|
|
22498
23421
|
skip("\uC140 \uB0B4 \uC774\uBBF8\uC9C0 \uBCC0\uACBD\uC740 \uBBF8\uC9C0\uC6D0");
|
|
22499
23422
|
continue;
|
|
22500
23423
|
}
|
|
@@ -22575,22 +23498,25 @@ function applyCellEdit5(table, scanTable, gridR, gridC, newLines, ctx, before, a
|
|
|
22575
23498
|
}
|
|
22576
23499
|
const unstable = newLines.find((l) => sanitizeText(l) !== l);
|
|
22577
23500
|
if (unstable !== void 0) return skip("\uACF5\uBC31 \uC815\uADDC\uD654 \uBD88\uC548\uC815 \uD14D\uC2A4\uD2B8 \u2014 \uD328\uCE58 \uC2DC \uC6D0\uBB38 \uBCF4\uC874 \uBD88\uAC00\uB85C \uBBF8\uC9C0\uC6D0");
|
|
22578
|
-
|
|
23501
|
+
const targets = nonEmpty.length > 0 ? nonEmpty : cell.paras;
|
|
23502
|
+
if (targets.length === 0) return skip("\uC140\uC5D0 \uBB38\uB2E8\uC774 \uC5C6\uC74C \u2014 \uBBF8\uC9C0\uC6D0");
|
|
22579
23503
|
const assigned = [];
|
|
22580
|
-
for (let i = 0; i <
|
|
23504
|
+
for (let i = 0; i < targets.length; i++) {
|
|
22581
23505
|
if (i < newLines.length) {
|
|
22582
|
-
assigned.push(i ===
|
|
23506
|
+
assigned.push(i === targets.length - 1 && newLines.length > targets.length ? newLines.slice(i).join(" ") : newLines[i]);
|
|
22583
23507
|
} else {
|
|
22584
23508
|
assigned.push("");
|
|
22585
23509
|
}
|
|
22586
23510
|
}
|
|
22587
|
-
if (newLines.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) });
|
|
23511
|
+
if (newLines.length > targets.length) {
|
|
23512
|
+
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 });
|
|
23513
|
+
} else if (newLines.length < nonEmpty.length && nonEmpty.length > 1) {
|
|
23514
|
+
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
23515
|
}
|
|
22590
23516
|
let staged = 0;
|
|
22591
|
-
for (let i = 0; i <
|
|
22592
|
-
if (assigned[i] ===
|
|
22593
|
-
staged += stageParaPatch(ctx.scans[
|
|
23517
|
+
for (let i = 0; i < targets.length; i++) {
|
|
23518
|
+
if (assigned[i] === targets[i].rawText || normForMatch(assigned[i]) === normForMatch(targets[i].rawText)) continue;
|
|
23519
|
+
staged += stageParaPatch(ctx.scans[targets[i].sectionIndex], targets[i], assigned[i], skip);
|
|
22594
23520
|
}
|
|
22595
23521
|
return staged > 0 ? 1 : 0;
|
|
22596
23522
|
}
|
|
@@ -22660,7 +23586,21 @@ function splitParaText(data) {
|
|
|
22660
23586
|
if (firstP < 0) firstP = k;
|
|
22661
23587
|
lastP = k;
|
|
22662
23588
|
}
|
|
22663
|
-
if (firstP < 0)
|
|
23589
|
+
if (firstP < 0) {
|
|
23590
|
+
if (toks.some((t) => t.visible)) return null;
|
|
23591
|
+
let cut = data.length;
|
|
23592
|
+
for (const t of toks) if (data.readUInt16LE(t.start) === 13) {
|
|
23593
|
+
cut = t.start;
|
|
23594
|
+
break;
|
|
23595
|
+
}
|
|
23596
|
+
return {
|
|
23597
|
+
prefix: data.subarray(0, cut),
|
|
23598
|
+
prefixUnits: cut / 2,
|
|
23599
|
+
core: "",
|
|
23600
|
+
suffix: data.subarray(cut),
|
|
23601
|
+
suffixUnits: (data.length - cut) / 2
|
|
23602
|
+
};
|
|
23603
|
+
}
|
|
22664
23604
|
for (let k = firstP; k <= lastP; k++) if (!toks[k].plain) return null;
|
|
22665
23605
|
for (let k = 0; k < firstP; k++) if (toks[k].visible) return null;
|
|
22666
23606
|
for (let k = lastP + 1; k < toks.length; k++) if (toks[k].visible) return null;
|
|
@@ -22693,7 +23633,6 @@ function rebuildCharShape(csData, coreStartUnit) {
|
|
|
22693
23633
|
}
|
|
22694
23634
|
function stageParaPatch(scan, para, newPlain, skip) {
|
|
22695
23635
|
if (!scan.safe) return skip("\uC139\uC158 \uB808\uCF54\uB4DC \uC7AC\uC9C1\uB82C\uD654 \uBD88\uC77C\uCE58 \u2014 \uC548\uC804\uC744 \uC704\uD574 \uC774 \uC139\uC158\uC740 \uBBF8\uC9C0\uC6D0");
|
|
22696
|
-
if (para.textIdx === -1) return skip("\uBE48 \uBB38\uB2E8 \uD14D\uC2A4\uD2B8 \uCD94\uAC00\uB294 \uBBF8\uC9C0\uC6D0 (v1)");
|
|
22697
23636
|
if (para.textIdx === -2) return skip("\uBCF5\uC218 PARA_TEXT \uB808\uCF54\uB4DC \uBB38\uB2E8 \u2014 \uBBF8\uC9C0\uC6D0 (v1)");
|
|
22698
23637
|
if (para.rangeTagCount > 0) return skip("\uBC94\uC704 \uD0DC\uADF8(\uD615\uAD11\uD39C/\uAD50\uC815\uBD80\uD638) \uBB38\uB2E8 \u2014 \uBBF8\uC9C0\uC6D0 (v1)");
|
|
22699
23638
|
if (para.charShapeIdx < 0 || para.lineSegIdx < 0) return skip("\uBB38\uB2E8 \uB808\uCF54\uB4DC \uAD6C\uC131 \uBE44\uC815\uD615 \u2014 \uBBF8\uC9C0\uC6D0");
|
|
@@ -22701,11 +23640,27 @@ function stageParaPatch(scan, para, newPlain, skip) {
|
|
|
22701
23640
|
if (/[\u0000-\u001f]/.test(newPlain)) return skip("\uC0C8 \uD14D\uC2A4\uD2B8\uC5D0 \uC81C\uC5B4\uBB38\uC790 \uD3EC\uD568 \u2014 \uBBF8\uC9C0\uC6D0");
|
|
22702
23641
|
const records = scan.records;
|
|
22703
23642
|
const headerRec = records[para.headerIdx];
|
|
22704
|
-
const textRec = records[para.textIdx];
|
|
22705
23643
|
const charShapeRec = records[para.charShapeIdx];
|
|
22706
23644
|
if (charShapeRec.data.length < 8) {
|
|
22707
23645
|
return skip("CHAR_SHAPE \uB808\uCF54\uB4DC \uBE44\uC815\uD615 \u2014 \uBBF8\uC9C0\uC6D0");
|
|
22708
23646
|
}
|
|
23647
|
+
if (para.textIdx === -1) {
|
|
23648
|
+
const nCharsLow = para.nCharsRaw & 2147483647;
|
|
23649
|
+
if (nCharsLow > 1) return skip("PARA_TEXT \uC5C6\uB294 \uBB38\uB2E8\uC758 nChars \uBE44\uC815\uD615 \u2014 \uBBF8\uC9C0\uC6D0");
|
|
23650
|
+
const paraEnd = nCharsLow === 1 ? Buffer.from([13, 0]) : Buffer.alloc(0);
|
|
23651
|
+
const at = para.headerIdx + 1;
|
|
23652
|
+
const list = scan.inserts.get(at) ?? [];
|
|
23653
|
+
list.push({ tagId: TAG_PARA_TEXT, level: headerRec.level + 1, data: Buffer.concat([Buffer.from(newPlain, "utf16le"), paraEnd]) });
|
|
23654
|
+
scan.inserts.set(at, list);
|
|
23655
|
+
const newHeader2 = Buffer.from(headerRec.data);
|
|
23656
|
+
newHeader2.writeUInt32LE((para.nCharsRaw & 2147483648 | newPlain.length + nCharsLow) >>> 0, 0);
|
|
23657
|
+
const cs2 = rebuildCharShape(charShapeRec.data, 0);
|
|
23658
|
+
scan.repl.set(para.charShapeIdx, cs2.buf);
|
|
23659
|
+
newHeader2.writeUInt16LE(cs2.count, 12);
|
|
23660
|
+
scan.repl.set(para.headerIdx, newHeader2);
|
|
23661
|
+
return 1;
|
|
23662
|
+
}
|
|
23663
|
+
const textRec = records[para.textIdx];
|
|
22709
23664
|
const seg = splitParaText(textRec.data);
|
|
22710
23665
|
if (!seg) {
|
|
22711
23666
|
return skip(para.ctrlMask !== 0 ? "\uCEE8\uD2B8\uB864 \uBB38\uC790(\uD0ED/\uD544\uB4DC/\uD2B9\uC218\uACF5\uBC31 \uB4F1 \uD14D\uC2A4\uD2B8 \uC911\uAC04) \uD3EC\uD568 \uBB38\uB2E8 \u2014 \uBBF8\uC9C0\uC6D0 (v1)" : "PARA_TEXT \uC7AC\uAD6C\uC131 \uBD88\uC77C\uCE58 \u2014 \uC6D0\uBB38 \uBCF4\uC874 \uBD88\uAC00\uB85C \uBBF8\uC9C0\uC6D0");
|
|
@@ -22960,6 +23915,7 @@ var HwpxSession = class _HwpxSession {
|
|
|
22960
23915
|
try {
|
|
22961
23916
|
for (let s = 0; s < st.scans.length; s++) {
|
|
22962
23917
|
if (sectionSplices[s].length === 0) continue;
|
|
23918
|
+
sectionSplices[s].push(...allLinesegRemovalSplices(st.scans[s].xml));
|
|
22963
23919
|
replacements.set(st.sectionPaths[s], encoder.encode(applySplices(st.scans[s].xml, sectionSplices[s])));
|
|
22964
23920
|
}
|
|
22965
23921
|
} catch (err) {
|
|
@@ -23016,7 +23972,7 @@ var HwpxSession = class _HwpxSession {
|
|
|
23016
23972
|
const origPrefix = block.text.split(" ", 1)[0];
|
|
23017
23973
|
const sp = newPlain.indexOf(" ");
|
|
23018
23974
|
const newFirst = sp > 0 ? newPlain.slice(0, sp) : newPlain;
|
|
23019
|
-
if (newFirst === origPrefix ||
|
|
23975
|
+
if (newFirst === origPrefix || AUTONUM_PREFIX_RE.test(newFirst)) {
|
|
23020
23976
|
newPlain = sp > 0 ? newPlain.slice(sp + 1) : "";
|
|
23021
23977
|
} else {
|
|
23022
23978
|
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 +24266,7 @@ async function parseHwp(buffer, options) {
|
|
|
23310
24266
|
async function parsePdf(buffer, options) {
|
|
23311
24267
|
let parsePdfDocument;
|
|
23312
24268
|
try {
|
|
23313
|
-
const mod = await import("./parser-
|
|
24269
|
+
const mod = await import("./parser-7G5F7PT2.js");
|
|
23314
24270
|
parsePdfDocument = mod.parsePdfDocument;
|
|
23315
24271
|
} catch {
|
|
23316
24272
|
return {
|
|
@@ -23403,6 +24359,7 @@ async function fillForm(input, values, outputFormat = "markdown") {
|
|
|
23403
24359
|
export {
|
|
23404
24360
|
extractHwpxMetadataOnly,
|
|
23405
24361
|
extractHwp5MetadataOnly,
|
|
24362
|
+
ValueCursor,
|
|
23406
24363
|
isLabelCell,
|
|
23407
24364
|
extractFormFields,
|
|
23408
24365
|
inferFieldType,
|
|
@@ -23413,6 +24370,13 @@ export {
|
|
|
23413
24370
|
buildRangeSplices,
|
|
23414
24371
|
applySplices,
|
|
23415
24372
|
fillHwpx,
|
|
24373
|
+
charWidthEm1000,
|
|
24374
|
+
SPACE_EM_FIXED,
|
|
24375
|
+
SPACE_EM_FONT,
|
|
24376
|
+
measureTextWidth,
|
|
24377
|
+
simulateWrap,
|
|
24378
|
+
simulateWrapKeepWord,
|
|
24379
|
+
fitRatioForFewerLines,
|
|
23416
24380
|
PRESET_ALIAS,
|
|
23417
24381
|
normalizeGongmunPreset,
|
|
23418
24382
|
markdownToHwpx,
|
|
@@ -23437,4 +24401,4 @@ export {
|
|
|
23437
24401
|
parseHwpml,
|
|
23438
24402
|
fillForm
|
|
23439
24403
|
};
|
|
23440
|
-
//# sourceMappingURL=chunk-
|
|
24404
|
+
//# sourceMappingURL=chunk-SLKF72QF.js.map
|