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
package/dist/index.js
CHANGED
|
@@ -19,7 +19,7 @@ import {
|
|
|
19
19
|
sanitizeHref,
|
|
20
20
|
stripDtd,
|
|
21
21
|
toArrayBuffer
|
|
22
|
-
} from "./chunk-
|
|
22
|
+
} from "./chunk-QV25HMU7.js";
|
|
23
23
|
import {
|
|
24
24
|
parsePageRange
|
|
25
25
|
} from "./chunk-GE43BE46.js";
|
|
@@ -1212,57 +1212,62 @@ async function extractImagesFromZip(zip, blocks, decompressed, warnings) {
|
|
|
1212
1212
|
let imageIndex = 0;
|
|
1213
1213
|
const imageBlocks = [];
|
|
1214
1214
|
collectImageBlocks(blocks, imageBlocks);
|
|
1215
|
+
const resolved = /* @__PURE__ */ new Map();
|
|
1215
1216
|
for (const { block, ownerCell } of imageBlocks) {
|
|
1216
1217
|
if (block.type !== "image" || !block.text) continue;
|
|
1217
1218
|
const ref = block.text;
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1219
|
+
let img = resolved.get(ref);
|
|
1220
|
+
if (img === void 0) {
|
|
1221
|
+
img = null;
|
|
1222
|
+
const candidates = [
|
|
1223
|
+
`BinData/${ref}`,
|
|
1224
|
+
`Contents/BinData/${ref}`,
|
|
1225
|
+
ref
|
|
1226
|
+
// 절대 경로일 수도 있음
|
|
1227
|
+
];
|
|
1228
|
+
let resolvedPath = null;
|
|
1229
|
+
if (!ref.includes(".")) {
|
|
1230
|
+
const prefixes = [`BinData/${ref}`, `Contents/BinData/${ref}`];
|
|
1231
|
+
for (const prefix of prefixes) {
|
|
1232
|
+
const match = zip.file(new RegExp(`^${prefix.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\.[a-zA-Z0-9]+$`));
|
|
1233
|
+
if (match.length > 0) {
|
|
1234
|
+
resolvedPath = match[0].name;
|
|
1235
|
+
break;
|
|
1236
|
+
}
|
|
1232
1237
|
}
|
|
1233
1238
|
}
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
if (ownerCell) ownerCell.text = ownerCell.text.replace(``, ``);
|
|
1254
|
-
found = true;
|
|
1255
|
-
break;
|
|
1256
|
-
} catch (err) {
|
|
1257
|
-
if (err instanceof KordocError) throw err;
|
|
1239
|
+
const allCandidates = resolvedPath ? [resolvedPath, ...candidates] : candidates;
|
|
1240
|
+
for (const path of allCandidates) {
|
|
1241
|
+
if (isPathTraversal(path)) continue;
|
|
1242
|
+
const file = zip.file(path);
|
|
1243
|
+
if (!file) continue;
|
|
1244
|
+
try {
|
|
1245
|
+
const data = await file.async("uint8array");
|
|
1246
|
+
decompressed.total += data.length;
|
|
1247
|
+
if (decompressed.total > MAX_DECOMPRESS_SIZE) throw new KordocError("ZIP \uC555\uCD95 \uD574\uC81C \uD06C\uAE30 \uCD08\uACFC (ZIP bomb \uC758\uC2EC)");
|
|
1248
|
+
const ext = path.includes(".") ? path.split(".").pop() || "png" : "png";
|
|
1249
|
+
const mimeType = imageExtToMime(ext);
|
|
1250
|
+
imageIndex++;
|
|
1251
|
+
const filename = `image_${String(imageIndex).padStart(3, "0")}.${mimeToExt(mimeType)}`;
|
|
1252
|
+
img = { filename, data, mimeType };
|
|
1253
|
+
images.push(img);
|
|
1254
|
+
break;
|
|
1255
|
+
} catch (err) {
|
|
1256
|
+
if (err instanceof KordocError) throw err;
|
|
1257
|
+
}
|
|
1258
1258
|
}
|
|
1259
|
+
if (!img) warnings?.push({ page: block.pageNumber, message: `\uC774\uBBF8\uC9C0 \uD30C\uC77C \uC5C6\uC74C: ${ref}`, code: "SKIPPED_IMAGE" });
|
|
1260
|
+
resolved.set(ref, img);
|
|
1259
1261
|
}
|
|
1260
|
-
if (!
|
|
1261
|
-
warnings?.push({ page: block.pageNumber, message: `\uC774\uBBF8\uC9C0 \uD30C\uC77C \uC5C6\uC74C: ${ref}`, code: "SKIPPED_IMAGE" });
|
|
1262
|
+
if (!img) {
|
|
1262
1263
|
block.type = "paragraph";
|
|
1263
1264
|
block.text = `[\uC774\uBBF8\uC9C0: ${ref}]`;
|
|
1264
1265
|
if (ownerCell) ownerCell.text = ownerCell.text.replace(``, `[\uC774\uBBF8\uC9C0: ${ref}]`);
|
|
1266
|
+
continue;
|
|
1265
1267
|
}
|
|
1268
|
+
block.text = img.filename;
|
|
1269
|
+
block.imageData = { data: img.data, mimeType: img.mimeType, filename: ref };
|
|
1270
|
+
if (ownerCell) ownerCell.text = ownerCell.text.replace(``, ``);
|
|
1266
1271
|
}
|
|
1267
1272
|
return images;
|
|
1268
1273
|
}
|
|
@@ -1450,9 +1455,32 @@ function detectHwpxHeadings(blocks, styleMap) {
|
|
|
1450
1455
|
function buildTableWithCellMeta(state) {
|
|
1451
1456
|
const table = buildTable(state.rows);
|
|
1452
1457
|
if (state.caption) table.caption = state.caption;
|
|
1458
|
+
const anchors = [];
|
|
1459
|
+
{
|
|
1460
|
+
const covered = /* @__PURE__ */ new Set();
|
|
1461
|
+
for (let r = 0; r < table.rows; r++) {
|
|
1462
|
+
for (let c = 0; c < table.cols; c++) {
|
|
1463
|
+
if (covered.has(`${r},${c}`)) continue;
|
|
1464
|
+
const cell = table.cells[r]?.[c];
|
|
1465
|
+
if (!cell) continue;
|
|
1466
|
+
for (let dr = 0; dr < cell.rowSpan; dr++) {
|
|
1467
|
+
for (let dc = 0; dc < cell.colSpan; dc++) {
|
|
1468
|
+
if (dr === 0 && dc === 0) continue;
|
|
1469
|
+
if (r + dr < table.rows && c + dc < table.cols) covered.add(`${r + dr},${c + dc}`);
|
|
1470
|
+
}
|
|
1471
|
+
}
|
|
1472
|
+
anchors.push(cell);
|
|
1473
|
+
c += cell.colSpan - 1;
|
|
1474
|
+
}
|
|
1475
|
+
}
|
|
1476
|
+
}
|
|
1477
|
+
const srcCount = state.rows.reduce((s, r) => s + r.length, 0);
|
|
1478
|
+
const ordinalReliable = anchors.length === srcCount;
|
|
1453
1479
|
const claimed = /* @__PURE__ */ new Set();
|
|
1480
|
+
let flatIdx = -1;
|
|
1454
1481
|
for (const row of state.rows) {
|
|
1455
1482
|
for (const src of row) {
|
|
1483
|
+
flatIdx++;
|
|
1456
1484
|
const needsBlocks = src.hasStructure && src.blocks && src.blocks.length > 0;
|
|
1457
1485
|
if (!needsBlocks && !src.isHeader) continue;
|
|
1458
1486
|
let target;
|
|
@@ -1471,6 +1499,10 @@ function buildTableWithCellMeta(state) {
|
|
|
1471
1499
|
}
|
|
1472
1500
|
}
|
|
1473
1501
|
}
|
|
1502
|
+
if (!target && ordinalReliable) {
|
|
1503
|
+
const cand = anchors[flatIdx];
|
|
1504
|
+
if (cand && !claimed.has(cand)) target = cand;
|
|
1505
|
+
}
|
|
1474
1506
|
if (!target) continue;
|
|
1475
1507
|
claimed.add(target);
|
|
1476
1508
|
if (needsBlocks) target.blocks = src.blocks;
|
|
@@ -2601,32 +2633,42 @@ function resolveImageBlocks(binDataMap, blocks, warnings) {
|
|
|
2601
2633
|
if (imageBlocks.length === 0) return [];
|
|
2602
2634
|
const images = [];
|
|
2603
2635
|
const renamed = /* @__PURE__ */ new Map();
|
|
2636
|
+
const resolved = /* @__PURE__ */ new Map();
|
|
2604
2637
|
let imageIndex = 0;
|
|
2605
2638
|
for (const block of imageBlocks) {
|
|
2606
2639
|
if (!block.text) continue;
|
|
2607
2640
|
const storageId = parseInt(block.text, 10);
|
|
2608
2641
|
if (isNaN(storageId)) continue;
|
|
2609
|
-
|
|
2610
|
-
if (
|
|
2611
|
-
|
|
2612
|
-
|
|
2613
|
-
|
|
2614
|
-
|
|
2642
|
+
let img = resolved.get(storageId);
|
|
2643
|
+
if (img === void 0) {
|
|
2644
|
+
const bin = binDataMap.get(storageId);
|
|
2645
|
+
if (!bin) {
|
|
2646
|
+
warnings.push({ page: block.pageNumber, message: `BinData ${storageId} \uC5C6\uC74C`, code: "SKIPPED_IMAGE" });
|
|
2647
|
+
resolved.set(storageId, null);
|
|
2648
|
+
} else {
|
|
2649
|
+
const mime = detectImageMime(bin.data);
|
|
2650
|
+
if (!mime) {
|
|
2651
|
+
warnings.push({ page: block.pageNumber, message: `BinData ${storageId}: \uC54C \uC218 \uC5C6\uB294 \uC774\uBBF8\uC9C0 \uD615\uC2DD`, code: "SKIPPED_IMAGE" });
|
|
2652
|
+
resolved.set(storageId, null);
|
|
2653
|
+
} else {
|
|
2654
|
+
imageIndex++;
|
|
2655
|
+
const ext = mime.includes("jpeg") ? "jpg" : mime.includes("png") ? "png" : mime.includes("gif") ? "gif" : mime.includes("bmp") ? "bmp" : "bin";
|
|
2656
|
+
img = { filename: `image_${String(imageIndex).padStart(3, "0")}.${ext}`, data: new Uint8Array(bin.data), mime };
|
|
2657
|
+
resolved.set(storageId, img);
|
|
2658
|
+
images.push({ filename: img.filename, data: img.data, mimeType: img.mime });
|
|
2659
|
+
renamed.set(storageId, img.filename);
|
|
2660
|
+
}
|
|
2661
|
+
}
|
|
2662
|
+
img = resolved.get(storageId);
|
|
2615
2663
|
}
|
|
2616
|
-
|
|
2617
|
-
|
|
2618
|
-
warnings.push({ page: block.pageNumber, message: `BinData ${storageId}: \uC54C \uC218 \uC5C6\uB294 \uC774\uBBF8\uC9C0 \uD615\uC2DD`, code: "SKIPPED_IMAGE" });
|
|
2664
|
+
if (!img) {
|
|
2665
|
+
const bin = binDataMap.get(storageId);
|
|
2619
2666
|
block.type = "paragraph";
|
|
2620
|
-
block.text = `[\uC774\uBBF8\uC9C0: ${bin.name}]`;
|
|
2667
|
+
block.text = bin ? `[\uC774\uBBF8\uC9C0: ${bin.name}]` : `[\uC774\uBBF8\uC9C0: BinData ${storageId}]`;
|
|
2621
2668
|
continue;
|
|
2622
2669
|
}
|
|
2623
|
-
|
|
2624
|
-
|
|
2625
|
-
const filename = `image_${String(imageIndex).padStart(3, "0")}.${ext}`;
|
|
2626
|
-
images.push({ filename, data: new Uint8Array(bin.data), mimeType: mime });
|
|
2627
|
-
renamed.set(storageId, filename);
|
|
2628
|
-
block.text = filename;
|
|
2629
|
-
block.imageData = { data: new Uint8Array(bin.data), mimeType: mime, filename: bin.name };
|
|
2670
|
+
block.text = img.filename;
|
|
2671
|
+
block.imageData = { data: img.data, mimeType: img.mime, filename: binDataMap.get(storageId).name };
|
|
2630
2672
|
}
|
|
2631
2673
|
resolveCellImageSentinels(blocks, renamed);
|
|
2632
2674
|
return images;
|
|
@@ -18314,7 +18356,7 @@ function parseTable(tbl, styles, numbering, footnotes, rels) {
|
|
|
18314
18356
|
};
|
|
18315
18357
|
return { type: "table", table };
|
|
18316
18358
|
}
|
|
18317
|
-
async function extractImages(zip, rels, doc) {
|
|
18359
|
+
async function extractImages(zip, rels, doc, warnings) {
|
|
18318
18360
|
const blocks = [];
|
|
18319
18361
|
const images = [];
|
|
18320
18362
|
const drawingElements = findElements(doc.documentElement, "drawing");
|
|
@@ -18345,7 +18387,11 @@ async function extractImages(zip, rels, doc) {
|
|
|
18345
18387
|
const filename = `image_${String(imgIdx).padStart(3, "0")}.${ext}`;
|
|
18346
18388
|
images.push({ filename, data, mimeType: mimeMap[ext] ?? "image/png" });
|
|
18347
18389
|
blocks.push({ type: "image", text: filename });
|
|
18348
|
-
} catch {
|
|
18390
|
+
} catch (err) {
|
|
18391
|
+
warnings.push({
|
|
18392
|
+
code: "SKIPPED_IMAGE",
|
|
18393
|
+
message: `DOCX \uC774\uBBF8\uC9C0 \uCD94\uCD9C \uC2E4\uD328 (${imgPath}): ${err instanceof Error ? err.message : String(err)}`
|
|
18394
|
+
});
|
|
18349
18395
|
}
|
|
18350
18396
|
}
|
|
18351
18397
|
}
|
|
@@ -18369,7 +18415,11 @@ async function parseDocxDocument(buffer, options) {
|
|
|
18369
18415
|
if (stylesFile) {
|
|
18370
18416
|
try {
|
|
18371
18417
|
styles = parseStyles(await stylesFile.async("text"));
|
|
18372
|
-
} catch {
|
|
18418
|
+
} catch (err) {
|
|
18419
|
+
warnings.push({
|
|
18420
|
+
code: "PARTIAL_PARSE",
|
|
18421
|
+
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)}`
|
|
18422
|
+
});
|
|
18373
18423
|
}
|
|
18374
18424
|
}
|
|
18375
18425
|
let numbering = /* @__PURE__ */ new Map();
|
|
@@ -18377,7 +18427,11 @@ async function parseDocxDocument(buffer, options) {
|
|
|
18377
18427
|
if (numFile) {
|
|
18378
18428
|
try {
|
|
18379
18429
|
numbering = parseNumbering(await numFile.async("text"));
|
|
18380
|
-
} catch {
|
|
18430
|
+
} catch (err) {
|
|
18431
|
+
warnings.push({
|
|
18432
|
+
code: "PARTIAL_PARSE",
|
|
18433
|
+
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)}`
|
|
18434
|
+
});
|
|
18381
18435
|
}
|
|
18382
18436
|
}
|
|
18383
18437
|
let footnotes = /* @__PURE__ */ new Map();
|
|
@@ -18385,7 +18439,11 @@ async function parseDocxDocument(buffer, options) {
|
|
|
18385
18439
|
if (fnFile) {
|
|
18386
18440
|
try {
|
|
18387
18441
|
footnotes = parseFootnotes(await fnFile.async("text"));
|
|
18388
|
-
} catch {
|
|
18442
|
+
} catch (err) {
|
|
18443
|
+
warnings.push({
|
|
18444
|
+
code: "PARTIAL_PARSE",
|
|
18445
|
+
message: `DOCX \uAC01\uC8FC(footnotes.xml) \uD30C\uC2F1 \uC2E4\uD328 \u2014 \uAC01\uC8FC \uC0DD\uB7B5: ${err instanceof Error ? err.message : String(err)}`
|
|
18446
|
+
});
|
|
18389
18447
|
}
|
|
18390
18448
|
}
|
|
18391
18449
|
const docXml = await docFile.async("text");
|
|
@@ -18407,7 +18465,7 @@ async function parseDocxDocument(buffer, options) {
|
|
|
18407
18465
|
if (block) blocks.push(block);
|
|
18408
18466
|
}
|
|
18409
18467
|
}
|
|
18410
|
-
const { blocks: imgBlocks, images } = await extractImages(zip, rels, doc);
|
|
18468
|
+
const { blocks: imgBlocks, images } = await extractImages(zip, rels, doc, warnings);
|
|
18411
18469
|
const metadata = {};
|
|
18412
18470
|
const coreFile = zip.file("docProps/core.xml");
|
|
18413
18471
|
if (coreFile) {
|
|
@@ -18425,7 +18483,11 @@ async function parseDocxDocument(buffer, options) {
|
|
|
18425
18483
|
if (created) metadata.createdAt = created;
|
|
18426
18484
|
const modified = getFirst("dcterms:modified");
|
|
18427
18485
|
if (modified) metadata.modifiedAt = modified;
|
|
18428
|
-
} catch {
|
|
18486
|
+
} catch (err) {
|
|
18487
|
+
warnings.push({
|
|
18488
|
+
code: "PARTIAL_PARSE",
|
|
18489
|
+
message: `DOCX \uBA54\uD0C0\uB370\uC774\uD130(core.xml) \uD30C\uC2F1 \uC2E4\uD328: ${err instanceof Error ? err.message : String(err)}`
|
|
18490
|
+
});
|
|
18429
18491
|
}
|
|
18430
18492
|
}
|
|
18431
18493
|
const outline = blocks.filter((b) => b.type === "heading").map((b) => ({ level: b.level ?? 2, text: b.text ?? "" }));
|
|
@@ -18694,6 +18756,45 @@ function countSections(body) {
|
|
|
18694
18756
|
}
|
|
18695
18757
|
|
|
18696
18758
|
// src/form/match.ts
|
|
18759
|
+
var ValueCursor = class {
|
|
18760
|
+
constructor(values) {
|
|
18761
|
+
this.values = values;
|
|
18762
|
+
}
|
|
18763
|
+
nextIdx = /* @__PURE__ */ new Map();
|
|
18764
|
+
keys() {
|
|
18765
|
+
return this.values.keys();
|
|
18766
|
+
}
|
|
18767
|
+
has(key) {
|
|
18768
|
+
return this.values.has(key);
|
|
18769
|
+
}
|
|
18770
|
+
isArray(key) {
|
|
18771
|
+
return Array.isArray(this.values.get(key));
|
|
18772
|
+
}
|
|
18773
|
+
/** 남은 값이 있으면 true (스칼라는 항상 true) */
|
|
18774
|
+
available(key) {
|
|
18775
|
+
const v = this.values.get(key);
|
|
18776
|
+
if (v === void 0) return false;
|
|
18777
|
+
return typeof v === "string" || (this.nextIdx.get(key) ?? 0) < v.length;
|
|
18778
|
+
}
|
|
18779
|
+
/** 현재 값 미리보기 (소진 없음) */
|
|
18780
|
+
peek(key) {
|
|
18781
|
+
const v = this.values.get(key);
|
|
18782
|
+
if (v === void 0) return void 0;
|
|
18783
|
+
if (typeof v === "string") return v;
|
|
18784
|
+
const i = this.nextIdx.get(key) ?? 0;
|
|
18785
|
+
return i < v.length ? v[i] : void 0;
|
|
18786
|
+
}
|
|
18787
|
+
/** 값 소비 — 배열이면 커서 전진, 소진 시 undefined */
|
|
18788
|
+
consume(key) {
|
|
18789
|
+
const v = this.values.get(key);
|
|
18790
|
+
if (v === void 0) return void 0;
|
|
18791
|
+
if (typeof v === "string") return v;
|
|
18792
|
+
const i = this.nextIdx.get(key) ?? 0;
|
|
18793
|
+
if (i >= v.length) return void 0;
|
|
18794
|
+
this.nextIdx.set(key, i + 1);
|
|
18795
|
+
return v[i];
|
|
18796
|
+
}
|
|
18797
|
+
};
|
|
18697
18798
|
function normalizeLabel(label) {
|
|
18698
18799
|
return label.trim().replace(/[::\s()()·]/g, "");
|
|
18699
18800
|
}
|
|
@@ -18732,9 +18833,9 @@ function fillInCellPatterns(cellText, values, matchedLabels) {
|
|
|
18732
18833
|
(match, prefix, suffix) => {
|
|
18733
18834
|
const label = prefix + suffix;
|
|
18734
18835
|
const normalizedLabel = normalizeLabel(label);
|
|
18735
|
-
const matchKey = values.
|
|
18836
|
+
const matchKey = values.available(normalizedLabel) ? normalizedLabel : values.available(normalizeLabel(prefix)) ? normalizeLabel(prefix) : void 0;
|
|
18736
18837
|
if (matchKey === void 0) return match;
|
|
18737
|
-
const newValue = values.
|
|
18838
|
+
const newValue = values.consume(matchKey);
|
|
18738
18839
|
matchedLabels.add(matchKey);
|
|
18739
18840
|
matches.push({ key: matchKey, label, value: newValue });
|
|
18740
18841
|
return `${prefix}(${newValue})${suffix}`;
|
|
@@ -18744,11 +18845,12 @@ function fillInCellPatterns(cellText, values, matchedLabels) {
|
|
|
18744
18845
|
/□([가-힣A-Za-z]+)/g,
|
|
18745
18846
|
(match, keyword) => {
|
|
18746
18847
|
const normalizedKw = normalizeLabel(keyword);
|
|
18747
|
-
const matchKey = values.
|
|
18848
|
+
const matchKey = values.available(normalizedKw) ? normalizedKw : void 0;
|
|
18748
18849
|
if (matchKey === void 0) return match;
|
|
18749
|
-
const val = values.
|
|
18850
|
+
const val = values.peek(matchKey);
|
|
18750
18851
|
const isTruthy = ["\u2611", "\u2713", "\u2714", "v", "V", "true", "1", "yes", "o", "O"].includes(val.trim()) || val.trim() === "";
|
|
18751
18852
|
if (!isTruthy) return match;
|
|
18853
|
+
values.consume(matchKey);
|
|
18752
18854
|
matchedLabels.add(matchKey);
|
|
18753
18855
|
matches.push({ key: matchKey, label: `\u25A1${keyword}`, value: "\u2611" });
|
|
18754
18856
|
return `\u2611${keyword}`;
|
|
@@ -18758,9 +18860,9 @@ function fillInCellPatterns(cellText, values, matchedLabels) {
|
|
|
18758
18860
|
/\(([가-힣A-Za-z]+)[::]\s{1,}\)/g,
|
|
18759
18861
|
(match, keyword) => {
|
|
18760
18862
|
const normalizedKw = normalizeLabel(keyword);
|
|
18761
|
-
const matchKey = values.
|
|
18863
|
+
const matchKey = values.available(normalizedKw) ? normalizedKw : void 0;
|
|
18762
18864
|
if (matchKey === void 0) return match;
|
|
18763
|
-
const newValue = values.
|
|
18865
|
+
const newValue = values.consume(matchKey);
|
|
18764
18866
|
matchedLabels.add(matchKey);
|
|
18765
18867
|
matches.push({ key: matchKey, label: keyword, value: newValue });
|
|
18766
18868
|
return `(${keyword}\uFF1A${newValue})`;
|
|
@@ -18866,14 +18968,66 @@ var LABEL_KEYWORDS = /* @__PURE__ */ new Set([
|
|
|
18866
18968
|
"\uCCAD\uAD6C\uC0AC\uC720",
|
|
18867
18969
|
"\uC18C\uBA85\uC790\uB8CC"
|
|
18868
18970
|
]);
|
|
18971
|
+
var ENGLISH_LABEL_WORDS = /* @__PURE__ */ new Set([
|
|
18972
|
+
"name",
|
|
18973
|
+
"date",
|
|
18974
|
+
"address",
|
|
18975
|
+
"tel",
|
|
18976
|
+
"phone",
|
|
18977
|
+
"mobile",
|
|
18978
|
+
"fax",
|
|
18979
|
+
"email",
|
|
18980
|
+
"e-mail",
|
|
18981
|
+
"dept",
|
|
18982
|
+
"department",
|
|
18983
|
+
"division",
|
|
18984
|
+
"title",
|
|
18985
|
+
"position",
|
|
18986
|
+
"grade",
|
|
18987
|
+
"rank",
|
|
18988
|
+
"birth",
|
|
18989
|
+
"nationality",
|
|
18990
|
+
"sex",
|
|
18991
|
+
"gender",
|
|
18992
|
+
"signature",
|
|
18993
|
+
"sign",
|
|
18994
|
+
"seal",
|
|
18995
|
+
"remarks",
|
|
18996
|
+
"note",
|
|
18997
|
+
"period",
|
|
18998
|
+
"place",
|
|
18999
|
+
"purpose",
|
|
19000
|
+
"reason",
|
|
19001
|
+
"amount",
|
|
19002
|
+
"total",
|
|
19003
|
+
"sum",
|
|
19004
|
+
"qty",
|
|
19005
|
+
"quantity",
|
|
19006
|
+
"unit",
|
|
19007
|
+
"no",
|
|
19008
|
+
"id",
|
|
19009
|
+
"passport"
|
|
19010
|
+
]);
|
|
19011
|
+
var ENGLISH_STOPWORDS = /* @__PURE__ */ new Set(["of", "the", "and", "or", "in"]);
|
|
19012
|
+
var NUMERIC_VALUE_RE = /^제?\d+(?:[.,]\d+)*[십백천만억조]*(?:원|명|건|개|회|부|매|장|점|호|번|년|월|일|시|분|초|개월|주년|차례|퍼센트)?$/;
|
|
19013
|
+
var SENTENCE_ENDING_RE = /(?:입니다|합니다|습니다|하세요|십시오|시오|바랍니다|바람|할 것|할것|하며|하고|한다|된다|됨|음|임)$/;
|
|
18869
19014
|
function isLabelCell(text) {
|
|
18870
19015
|
const trimmed = text.trim().replace(/[¹²³⁴⁵⁶⁷⁸⁹⁰*※]+$/g, "").trim();
|
|
18871
19016
|
if (!trimmed || trimmed.length > 30) return false;
|
|
18872
19017
|
for (const kw of LABEL_KEYWORDS) {
|
|
18873
19018
|
if (trimmed.includes(kw)) return true;
|
|
18874
19019
|
}
|
|
18875
|
-
|
|
19020
|
+
const compact = trimmed.replace(/\s/g, "");
|
|
19021
|
+
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)) {
|
|
19022
|
+
return true;
|
|
19023
|
+
}
|
|
18876
19024
|
if (/^[가-힣A-Za-z\s]+[::]$/.test(trimmed)) return true;
|
|
19025
|
+
if (/^[A-Za-z][A-Za-z\s./&-]*$/.test(trimmed) && trimmed.length <= 20) {
|
|
19026
|
+
const words = trimmed.toLowerCase().split(/[\s/&]+/).filter((w) => w && !ENGLISH_STOPWORDS.has(w));
|
|
19027
|
+
if (words.length >= 1 && words.length <= 3 && words.every((w) => ENGLISH_LABEL_WORDS.has(w.replace(/\.$/, "")))) {
|
|
19028
|
+
return true;
|
|
19029
|
+
}
|
|
19030
|
+
}
|
|
18877
19031
|
return false;
|
|
18878
19032
|
}
|
|
18879
19033
|
function extractFormFields(blocks) {
|
|
@@ -19014,17 +19168,18 @@ function fillFormFields(blocks, values) {
|
|
|
19014
19168
|
const filled = [];
|
|
19015
19169
|
const matchedLabels = /* @__PURE__ */ new Set();
|
|
19016
19170
|
const normalizedValues = normalizeValues(values);
|
|
19171
|
+
const cursor = new ValueCursor(normalizedValues);
|
|
19172
|
+
const allTables = collectIRTables(cloned, 0);
|
|
19017
19173
|
const patternFilledCells = /* @__PURE__ */ new Set();
|
|
19018
|
-
for (const
|
|
19019
|
-
|
|
19020
|
-
|
|
19021
|
-
|
|
19022
|
-
const cell = block.table.cells[r]?.[c];
|
|
19174
|
+
for (const table of allTables) {
|
|
19175
|
+
for (let r = 0; r < table.rows; r++) {
|
|
19176
|
+
for (let c = 0; c < table.cols; c++) {
|
|
19177
|
+
const cell = table.cells[r]?.[c];
|
|
19023
19178
|
if (!cell) continue;
|
|
19024
|
-
const result = fillInCellPatterns(cell.text,
|
|
19179
|
+
const result = fillInCellPatterns(cell.text, cursor, matchedLabels);
|
|
19025
19180
|
if (result) {
|
|
19026
19181
|
cell.text = result.text;
|
|
19027
|
-
patternFilledCells.add(
|
|
19182
|
+
patternFilledCells.add(cell);
|
|
19028
19183
|
for (const m of result.matches) {
|
|
19029
19184
|
filled.push({ label: m.label, value: m.value, row: r, col: c });
|
|
19030
19185
|
}
|
|
@@ -19032,33 +19187,71 @@ function fillFormFields(blocks, values) {
|
|
|
19032
19187
|
}
|
|
19033
19188
|
}
|
|
19034
19189
|
}
|
|
19035
|
-
for (const
|
|
19036
|
-
|
|
19037
|
-
fillTable(block.table, normalizedValues, filled, matchedLabels, patternFilledCells);
|
|
19190
|
+
for (const table of allTables) {
|
|
19191
|
+
fillTable(table, cursor, filled, matchedLabels, patternFilledCells);
|
|
19038
19192
|
}
|
|
19039
19193
|
for (const block of cloned) {
|
|
19040
19194
|
if (block.type !== "paragraph" || !block.text) continue;
|
|
19041
|
-
const newText = fillInlineFields(block.text,
|
|
19195
|
+
const newText = fillInlineFields(block.text, cursor, filled, matchedLabels);
|
|
19042
19196
|
if (newText !== block.text) block.text = newText;
|
|
19043
19197
|
}
|
|
19044
19198
|
const unmatched = resolveUnmatched(normalizedValues, matchedLabels, values);
|
|
19045
19199
|
return { blocks: cloned, filled, unmatched };
|
|
19046
19200
|
}
|
|
19201
|
+
function collectIRTables(blocks, depth) {
|
|
19202
|
+
if (depth > 16) return [];
|
|
19203
|
+
const out = [];
|
|
19204
|
+
for (const block of blocks) {
|
|
19205
|
+
if (block.type !== "table" || !block.table) continue;
|
|
19206
|
+
out.push(block.table);
|
|
19207
|
+
for (const row of block.table.cells) {
|
|
19208
|
+
for (const cell of row) {
|
|
19209
|
+
if (cell?.blocks?.length) out.push(...collectIRTables(cell.blocks, depth + 1));
|
|
19210
|
+
}
|
|
19211
|
+
}
|
|
19212
|
+
}
|
|
19213
|
+
return out;
|
|
19214
|
+
}
|
|
19215
|
+
function coveredPositions(table) {
|
|
19216
|
+
const covered = /* @__PURE__ */ new Set();
|
|
19217
|
+
for (let r = 0; r < table.rows; r++) {
|
|
19218
|
+
for (let c = 0; c < table.cols; c++) {
|
|
19219
|
+
if (covered.has(`${r},${c}`)) continue;
|
|
19220
|
+
const cell = table.cells[r]?.[c];
|
|
19221
|
+
if (!cell) continue;
|
|
19222
|
+
for (let dr = 0; dr < cell.rowSpan; dr++) {
|
|
19223
|
+
for (let dc = 0; dc < cell.colSpan; dc++) {
|
|
19224
|
+
if (dr === 0 && dc === 0) continue;
|
|
19225
|
+
if (r + dr < table.rows && c + dc < table.cols) covered.add(`${r + dr},${c + dc}`);
|
|
19226
|
+
}
|
|
19227
|
+
}
|
|
19228
|
+
c += cell.colSpan - 1;
|
|
19229
|
+
}
|
|
19230
|
+
}
|
|
19231
|
+
return covered;
|
|
19232
|
+
}
|
|
19047
19233
|
function fillTable(table, values, filled, matchedLabels, patternFilledCells) {
|
|
19048
19234
|
if (table.cols < 2) return;
|
|
19235
|
+
const covered = coveredPositions(table);
|
|
19049
19236
|
for (let r = 0; r < table.rows; r++) {
|
|
19050
|
-
for (let c = 0; c < table.cols
|
|
19237
|
+
for (let c = 0; c < table.cols; c++) {
|
|
19238
|
+
if (covered.has(`${r},${c}`)) continue;
|
|
19051
19239
|
const labelCell = table.cells[r][c];
|
|
19052
|
-
|
|
19053
|
-
if (!labelCell || !valueCell) continue;
|
|
19240
|
+
if (!labelCell) continue;
|
|
19054
19241
|
if (!isLabelCell(labelCell.text)) continue;
|
|
19242
|
+
let vc = c + labelCell.colSpan;
|
|
19243
|
+
while (vc < table.cols && covered.has(`${r},${vc}`)) vc++;
|
|
19244
|
+
if (vc >= table.cols) continue;
|
|
19245
|
+
const valueCell = table.cells[r][vc];
|
|
19246
|
+
if (!valueCell) continue;
|
|
19055
19247
|
if (isKeywordLabel(valueCell.text)) continue;
|
|
19056
19248
|
const normalizedCellLabel = normalizeLabel(labelCell.text);
|
|
19057
19249
|
if (!normalizedCellLabel) continue;
|
|
19058
19250
|
const matchKey = findMatchingKey(normalizedCellLabel, values);
|
|
19059
19251
|
if (matchKey === void 0) continue;
|
|
19060
|
-
const newValue = values.
|
|
19061
|
-
if (
|
|
19252
|
+
const newValue = values.consume(matchKey);
|
|
19253
|
+
if (newValue === void 0) continue;
|
|
19254
|
+
if (patternFilledCells?.has(valueCell)) {
|
|
19062
19255
|
valueCell.text = newValue + " " + valueCell.text;
|
|
19063
19256
|
} else {
|
|
19064
19257
|
valueCell.text = newValue;
|
|
@@ -19081,14 +19274,16 @@ function fillTable(table, values, filled, matchedLabels, patternFilledCells) {
|
|
|
19081
19274
|
if (!allLabels) return;
|
|
19082
19275
|
for (let r = 1; r < table.rows; r++) {
|
|
19083
19276
|
for (let c = 0; c < table.cols; c++) {
|
|
19277
|
+
if (covered.has(`${r},${c}`)) continue;
|
|
19084
19278
|
const headerCell = headerRow[c];
|
|
19085
19279
|
const valueCell = table.cells[r]?.[c];
|
|
19086
19280
|
if (!headerCell || !valueCell) continue;
|
|
19087
19281
|
const headerLabel = normalizeLabel(headerCell.text);
|
|
19088
19282
|
const matchKey = findMatchingKey(headerLabel, values);
|
|
19089
19283
|
if (matchKey === void 0) continue;
|
|
19090
|
-
if (matchedLabels.has(matchKey)) continue;
|
|
19091
|
-
const newValue = values.
|
|
19284
|
+
if (!values.isArray(matchKey) && matchedLabels.has(matchKey)) continue;
|
|
19285
|
+
const newValue = values.consume(matchKey);
|
|
19286
|
+
if (newValue === void 0) continue;
|
|
19092
19287
|
valueCell.text = newValue;
|
|
19093
19288
|
matchedLabels.add(matchKey);
|
|
19094
19289
|
filled.push({
|
|
@@ -19109,7 +19304,8 @@ function fillInlineFields(text, values, filled, matchedLabels) {
|
|
|
19109
19304
|
for (const seg of segments) {
|
|
19110
19305
|
const matchKey = findMatchingKey(normalizeLabel(seg.label), values);
|
|
19111
19306
|
if (matchKey === void 0) continue;
|
|
19112
|
-
const newValue = values.
|
|
19307
|
+
const newValue = values.consume(matchKey);
|
|
19308
|
+
if (newValue === void 0) continue;
|
|
19113
19309
|
matchedLabels.add(matchKey);
|
|
19114
19310
|
filled.push({ label: seg.label.trim(), value: newValue, row: -1, col: -1 });
|
|
19115
19311
|
out += text.slice(pos, seg.valueStart);
|
|
@@ -19216,6 +19412,7 @@ function scanSectionXml(xml, sectionIndex) {
|
|
|
19216
19412
|
const paraStack = [];
|
|
19217
19413
|
const tableStack = [];
|
|
19218
19414
|
const rowStack = [];
|
|
19415
|
+
const trStartStack = [];
|
|
19219
19416
|
const cellStack = [];
|
|
19220
19417
|
let pendingT = null;
|
|
19221
19418
|
const ctrlSubStack = [];
|
|
@@ -19280,11 +19477,17 @@ function scanSectionXml(xml, sectionIndex) {
|
|
|
19280
19477
|
} else if (local2 === "tr") {
|
|
19281
19478
|
const row = rowStack[rowStack.length - 1];
|
|
19282
19479
|
const table = tableStack[tableStack.length - 1];
|
|
19283
|
-
if (row && table && row.length > 0)
|
|
19480
|
+
if (row && table && row.length > 0) {
|
|
19481
|
+
table.rows.push(row);
|
|
19482
|
+
const trStart = trStartStack[trStartStack.length - 1];
|
|
19483
|
+
if (trStart >= 0) table.rowRanges.push({ start: trStart, end: m.index + full.length });
|
|
19484
|
+
}
|
|
19284
19485
|
if (rowStack.length > 0) rowStack[rowStack.length - 1] = [];
|
|
19486
|
+
if (trStartStack.length > 0) trStartStack[trStartStack.length - 1] = -1;
|
|
19285
19487
|
} else if (local2 === "tbl") {
|
|
19286
19488
|
const table = tableStack.pop();
|
|
19287
19489
|
rowStack.pop();
|
|
19490
|
+
trStartStack.pop();
|
|
19288
19491
|
if (table) {
|
|
19289
19492
|
finalizeTable(table);
|
|
19290
19493
|
if (!table.topLevel) {
|
|
@@ -19327,6 +19530,7 @@ function scanSectionXml(xml, sectionIndex) {
|
|
|
19327
19530
|
const ra = parseInt(getAttr2(attrsRaw, "rowAddr") || "", 10);
|
|
19328
19531
|
if (!isNaN(ca)) cell.colAddr = ca;
|
|
19329
19532
|
if (!isNaN(ra)) cell.rowAddr = ra;
|
|
19533
|
+
cell.addrTagRange = { start: m.index, end: m.index + full.length };
|
|
19330
19534
|
}
|
|
19331
19535
|
} else if (local === "cellSpan") {
|
|
19332
19536
|
const cell = cellStack[cellStack.length - 1];
|
|
@@ -19377,6 +19581,7 @@ function scanSectionXml(xml, sectionIndex) {
|
|
|
19377
19581
|
start: m.index,
|
|
19378
19582
|
topLevel: false,
|
|
19379
19583
|
rows: [],
|
|
19584
|
+
rowRanges: [],
|
|
19380
19585
|
cellByAnchor: /* @__PURE__ */ new Map()
|
|
19381
19586
|
};
|
|
19382
19587
|
stack.pop();
|
|
@@ -19384,9 +19589,11 @@ function scanSectionXml(xml, sectionIndex) {
|
|
|
19384
19589
|
stack.push({ local, qname, contentStart });
|
|
19385
19590
|
tableStack.push(table);
|
|
19386
19591
|
rowStack.push([]);
|
|
19592
|
+
trStartStack.push(-1);
|
|
19387
19593
|
if (table.topLevel) tables.push(table);
|
|
19388
19594
|
} else if (local === "tr") {
|
|
19389
19595
|
if (rowStack.length > 0) rowStack[rowStack.length - 1] = [];
|
|
19596
|
+
if (trStartStack.length > 0) trStartStack[trStartStack.length - 1] = m.index;
|
|
19390
19597
|
} else if (local === "tc") {
|
|
19391
19598
|
cellStack.push({ colSpan: 1, rowSpan: 1, paragraphs: [], tables: [] });
|
|
19392
19599
|
} else if (local === "cellAddr" || local === "cellSpan") {
|
|
@@ -19397,6 +19604,7 @@ function scanSectionXml(xml, sectionIndex) {
|
|
|
19397
19604
|
const ra = parseInt(getAttr2(attrsRaw, "rowAddr") || "", 10);
|
|
19398
19605
|
if (!isNaN(ca)) cell.colAddr = ca;
|
|
19399
19606
|
if (!isNaN(ra)) cell.rowAddr = ra;
|
|
19607
|
+
cell.addrTagRange = { start: m.index, end: contentStart };
|
|
19400
19608
|
} else {
|
|
19401
19609
|
const cs = parseInt(getAttr2(attrsRaw, "colSpan") || "1", 10);
|
|
19402
19610
|
const rs = parseInt(getAttr2(attrsRaw, "rowSpan") || "1", 10);
|
|
@@ -19789,6 +19997,7 @@ async function fillHwpx(hwpxBuffer, values) {
|
|
|
19789
19997
|
throw new KordocError("HWPX\uC5D0\uC11C \uC139\uC158 \uD30C\uC77C\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4");
|
|
19790
19998
|
}
|
|
19791
19999
|
const normalizedValues = normalizeValues(values);
|
|
20000
|
+
const cursor = new ValueCursor(normalizedValues);
|
|
19792
20001
|
const matchedLabels = /* @__PURE__ */ new Set();
|
|
19793
20002
|
const filled = [];
|
|
19794
20003
|
const failedKeys = /* @__PURE__ */ new Set();
|
|
@@ -19824,7 +20033,7 @@ async function fillHwpx(hwpxBuffer, values) {
|
|
|
19824
20033
|
for (const cell of row) {
|
|
19825
20034
|
for (const para of cell.paragraphs) {
|
|
19826
20035
|
const text = matchText(para);
|
|
19827
|
-
const result = fillInCellPatterns(text,
|
|
20036
|
+
const result = fillInCellPatterns(text, cursor, matchedLabels);
|
|
19828
20037
|
if (!result) continue;
|
|
19829
20038
|
const l = led(para);
|
|
19830
20039
|
if (l.fullText !== void 0) continue;
|
|
@@ -19858,21 +20067,30 @@ async function fillHwpx(hwpxBuffer, values) {
|
|
|
19858
20067
|
if (isKeywordLabel(cellLabelText(valueCell))) continue;
|
|
19859
20068
|
const normalizedCellLabel = normalizeLabel(labelText);
|
|
19860
20069
|
if (!normalizedCellLabel) continue;
|
|
19861
|
-
const matchKey = findMatchingKey(normalizedCellLabel,
|
|
20070
|
+
const matchKey = findMatchingKey(normalizedCellLabel, cursor);
|
|
19862
20071
|
if (matchKey === void 0) continue;
|
|
19863
|
-
const newValue = normalizedValues.get(matchKey);
|
|
19864
20072
|
if (patternApplied.has(valueCell)) {
|
|
19865
20073
|
const target = valueCell.paragraphs.find((p) => p.tRanges.length > 0) ?? valueCell.paragraphs[0];
|
|
19866
20074
|
if (!target) continue;
|
|
19867
20075
|
const l = led(target);
|
|
19868
|
-
if (l.fullText
|
|
19869
|
-
|
|
19870
|
-
|
|
19871
|
-
|
|
19872
|
-
|
|
20076
|
+
if (l.fullText !== void 0) continue;
|
|
20077
|
+
const newValue = cursor.consume(matchKey);
|
|
20078
|
+
if (newValue === void 0) continue;
|
|
20079
|
+
l.ranges.push({ start: 0, end: 0, replacement: newValue + " " });
|
|
20080
|
+
l.filledIdx.push(filled.length);
|
|
20081
|
+
l.matchKeys.push(matchKey);
|
|
20082
|
+
matchedLabels.add(matchKey);
|
|
20083
|
+
filled.push({
|
|
20084
|
+
label: labelText.trim().replace(/[::]\s*$/, ""),
|
|
20085
|
+
value: newValue,
|
|
20086
|
+
row: rowIdx,
|
|
20087
|
+
col: colIdx
|
|
20088
|
+
});
|
|
19873
20089
|
} else {
|
|
19874
20090
|
const paras = valueCell.paragraphs;
|
|
19875
20091
|
if (paras.length === 0) continue;
|
|
20092
|
+
const newValue = cursor.consume(matchKey);
|
|
20093
|
+
if (newValue === void 0) continue;
|
|
19876
20094
|
const l0 = led(paras[0]);
|
|
19877
20095
|
l0.fullText = newValue;
|
|
19878
20096
|
l0.ranges = [];
|
|
@@ -19883,14 +20101,14 @@ async function fillHwpx(hwpxBuffer, values) {
|
|
|
19883
20101
|
lk.fullText = "";
|
|
19884
20102
|
lk.ranges = [];
|
|
19885
20103
|
}
|
|
20104
|
+
matchedLabels.add(matchKey);
|
|
20105
|
+
filled.push({
|
|
20106
|
+
label: labelText.trim().replace(/[::]\s*$/, ""),
|
|
20107
|
+
value: newValue,
|
|
20108
|
+
row: rowIdx,
|
|
20109
|
+
col: colIdx
|
|
20110
|
+
});
|
|
19886
20111
|
}
|
|
19887
|
-
matchedLabels.add(matchKey);
|
|
19888
|
-
filled.push({
|
|
19889
|
-
label: labelText.trim().replace(/[::]\s*$/, ""),
|
|
19890
|
-
value: newValue,
|
|
19891
|
-
row: rowIdx,
|
|
19892
|
-
col: colIdx
|
|
19893
|
-
});
|
|
19894
20112
|
}
|
|
19895
20113
|
}
|
|
19896
20114
|
if (table.rows.length >= 2) {
|
|
@@ -19904,10 +20122,11 @@ async function fillHwpx(hwpxBuffer, values) {
|
|
|
19904
20122
|
const dataCells = table.rows[rowIdx];
|
|
19905
20123
|
for (let colIdx = 0; colIdx < Math.min(headerCells.length, dataCells.length); colIdx++) {
|
|
19906
20124
|
const headerLabel = normalizeLabel(cellLabelText(headerCells[colIdx]));
|
|
19907
|
-
const matchKey = findMatchingKey(headerLabel,
|
|
20125
|
+
const matchKey = findMatchingKey(headerLabel, cursor);
|
|
19908
20126
|
if (matchKey === void 0) continue;
|
|
19909
|
-
if (matchedLabels.has(matchKey)) continue;
|
|
19910
|
-
const newValue =
|
|
20127
|
+
if (!cursor.isArray(matchKey) && matchedLabels.has(matchKey)) continue;
|
|
20128
|
+
const newValue = cursor.consume(matchKey);
|
|
20129
|
+
if (newValue === void 0) continue;
|
|
19911
20130
|
const paras = dataCells[colIdx].paragraphs;
|
|
19912
20131
|
if (paras.length === 0) continue;
|
|
19913
20132
|
const l0 = led(paras[0]);
|
|
@@ -19937,9 +20156,10 @@ async function fillHwpx(hwpxBuffer, values) {
|
|
|
19937
20156
|
if (existing?.fullText !== void 0) continue;
|
|
19938
20157
|
const text = matchText(para);
|
|
19939
20158
|
for (const seg of scanInlineSegments(text)) {
|
|
19940
|
-
const matchKey = findMatchingKey(normalizeLabel(seg.label),
|
|
20159
|
+
const matchKey = findMatchingKey(normalizeLabel(seg.label), cursor);
|
|
19941
20160
|
if (matchKey === void 0) continue;
|
|
19942
|
-
const newValue =
|
|
20161
|
+
const newValue = cursor.consume(matchKey);
|
|
20162
|
+
if (newValue === void 0) continue;
|
|
19943
20163
|
const replacement = seg.valueStart === seg.valueEnd ? padInsertion(text, seg.valueStart, newValue) : newValue;
|
|
19944
20164
|
const l = led(para);
|
|
19945
20165
|
l.ranges.push({ start: seg.valueStart, end: seg.valueEnd, replacement });
|
|
@@ -19994,6 +20214,7 @@ async function fillHwpx(hwpxBuffer, values) {
|
|
|
19994
20214
|
splices.push(...paraSplices);
|
|
19995
20215
|
}
|
|
19996
20216
|
if (splices.length > 0) {
|
|
20217
|
+
splices.push(...allLinesegRemovalSplices(xml));
|
|
19997
20218
|
replacements.set(sectionPaths[si], encoder.encode(applySplices(xml, splices)));
|
|
19998
20219
|
}
|
|
19999
20220
|
}
|
|
@@ -20013,6 +20234,245 @@ async function fillHwpx(hwpxBuffer, values) {
|
|
|
20013
20234
|
// src/hwpx/generator.ts
|
|
20014
20235
|
import JSZip6 from "jszip";
|
|
20015
20236
|
|
|
20237
|
+
// src/hwpx/text-metrics.ts
|
|
20238
|
+
var ASCII_W = [
|
|
20239
|
+
300,
|
|
20240
|
+
320,
|
|
20241
|
+
320,
|
|
20242
|
+
610,
|
|
20243
|
+
610,
|
|
20244
|
+
830,
|
|
20245
|
+
724,
|
|
20246
|
+
320,
|
|
20247
|
+
320,
|
|
20248
|
+
320,
|
|
20249
|
+
550,
|
|
20250
|
+
550,
|
|
20251
|
+
320,
|
|
20252
|
+
550,
|
|
20253
|
+
320,
|
|
20254
|
+
550,
|
|
20255
|
+
// 0x20-0x2F
|
|
20256
|
+
550,
|
|
20257
|
+
550,
|
|
20258
|
+
550,
|
|
20259
|
+
550,
|
|
20260
|
+
550,
|
|
20261
|
+
550,
|
|
20262
|
+
550,
|
|
20263
|
+
550,
|
|
20264
|
+
550,
|
|
20265
|
+
550,
|
|
20266
|
+
320,
|
|
20267
|
+
320,
|
|
20268
|
+
550,
|
|
20269
|
+
550,
|
|
20270
|
+
550,
|
|
20271
|
+
550,
|
|
20272
|
+
// 0x30-0x3F
|
|
20273
|
+
830,
|
|
20274
|
+
706,
|
|
20275
|
+
605,
|
|
20276
|
+
685,
|
|
20277
|
+
719,
|
|
20278
|
+
627,
|
|
20279
|
+
617,
|
|
20280
|
+
683,
|
|
20281
|
+
734,
|
|
20282
|
+
305,
|
|
20283
|
+
315,
|
|
20284
|
+
660,
|
|
20285
|
+
605,
|
|
20286
|
+
839,
|
|
20287
|
+
734,
|
|
20288
|
+
732,
|
|
20289
|
+
// 0x40-0x4F
|
|
20290
|
+
603,
|
|
20291
|
+
705,
|
|
20292
|
+
660,
|
|
20293
|
+
627,
|
|
20294
|
+
664,
|
|
20295
|
+
731,
|
|
20296
|
+
706,
|
|
20297
|
+
910,
|
|
20298
|
+
705,
|
|
20299
|
+
705,
|
|
20300
|
+
626,
|
|
20301
|
+
320,
|
|
20302
|
+
550,
|
|
20303
|
+
320,
|
|
20304
|
+
550,
|
|
20305
|
+
550,
|
|
20306
|
+
// 0x50-0x5F
|
|
20307
|
+
320,
|
|
20308
|
+
569,
|
|
20309
|
+
597,
|
|
20310
|
+
552,
|
|
20311
|
+
597,
|
|
20312
|
+
536,
|
|
20313
|
+
356,
|
|
20314
|
+
562,
|
|
20315
|
+
635,
|
|
20316
|
+
287,
|
|
20317
|
+
288,
|
|
20318
|
+
582,
|
|
20319
|
+
287,
|
|
20320
|
+
907,
|
|
20321
|
+
635,
|
|
20322
|
+
588,
|
|
20323
|
+
// 0x60-0x6F
|
|
20324
|
+
597,
|
|
20325
|
+
579,
|
|
20326
|
+
478,
|
|
20327
|
+
496,
|
|
20328
|
+
356,
|
|
20329
|
+
635,
|
|
20330
|
+
563,
|
|
20331
|
+
720,
|
|
20332
|
+
542,
|
|
20333
|
+
543,
|
|
20334
|
+
486,
|
|
20335
|
+
320,
|
|
20336
|
+
320,
|
|
20337
|
+
320,
|
|
20338
|
+
550,
|
|
20339
|
+
0
|
|
20340
|
+
// 0x70-0x7E(+DEL)
|
|
20341
|
+
];
|
|
20342
|
+
var SYM_W = {
|
|
20343
|
+
160: 300,
|
|
20344
|
+
163: 568,
|
|
20345
|
+
165: 707,
|
|
20346
|
+
167: 498,
|
|
20347
|
+
171: 440,
|
|
20348
|
+
172: 564,
|
|
20349
|
+
176: 291,
|
|
20350
|
+
177: 798,
|
|
20351
|
+
182: 606,
|
|
20352
|
+
183: 320,
|
|
20353
|
+
187: 440,
|
|
20354
|
+
215: 617,
|
|
20355
|
+
247: 678,
|
|
20356
|
+
8211: 625,
|
|
20357
|
+
8212: 875,
|
|
20358
|
+
8213: 875,
|
|
20359
|
+
8216: 320,
|
|
20360
|
+
8217: 320,
|
|
20361
|
+
8220: 480,
|
|
20362
|
+
8221: 480,
|
|
20363
|
+
8224: 558,
|
|
20364
|
+
8225: 438,
|
|
20365
|
+
8229: 640,
|
|
20366
|
+
8230: 960,
|
|
20367
|
+
8240: 988,
|
|
20368
|
+
8242: 335,
|
|
20369
|
+
8243: 474,
|
|
20370
|
+
8251: 770,
|
|
20371
|
+
8364: 656,
|
|
20372
|
+
9756: 1012,
|
|
20373
|
+
9758: 1012
|
|
20374
|
+
};
|
|
20375
|
+
function charWidthEm1000(cp) {
|
|
20376
|
+
if (cp >= 32 && cp <= 126) return ASCII_W[cp - 32];
|
|
20377
|
+
const sym = SYM_W[cp];
|
|
20378
|
+
if (sym !== void 0) return sym;
|
|
20379
|
+
if (cp >= 44032 && cp <= 55203) return 970;
|
|
20380
|
+
if (cp >= 4352 && cp <= 4607) return 970;
|
|
20381
|
+
if (cp >= 12593 && cp <= 12686) return 970;
|
|
20382
|
+
if (cp >= 19968 && cp <= 40959 || cp >= 63744 && cp <= 64255) return 1e3;
|
|
20383
|
+
if (cp >= 12296 && cp <= 12305 || cp >= 12308 && cp <= 12315) return 500;
|
|
20384
|
+
if (cp === 12288) return 970;
|
|
20385
|
+
if (cp >= 8592 && cp <= 8959) return 970;
|
|
20386
|
+
if (cp >= 9312 && cp <= 9471) return 970;
|
|
20387
|
+
if (cp >= 9632 && cp <= 9983) return 970;
|
|
20388
|
+
if (cp >= 12800 && cp <= 13311) return 970;
|
|
20389
|
+
if (cp >= 65281 && cp <= 65376) return 970;
|
|
20390
|
+
return cp >= 11904 ? 970 : 550;
|
|
20391
|
+
}
|
|
20392
|
+
var SPACE_EM_FIXED = 500;
|
|
20393
|
+
var SPACE_EM_FONT = 300;
|
|
20394
|
+
function measureTextWidth(text, height, ratioPct, opts) {
|
|
20395
|
+
const spaceEm = opts?.spaceEm ?? SPACE_EM_FIXED;
|
|
20396
|
+
const spacing = opts?.spacingPct ?? 0;
|
|
20397
|
+
let em = 0;
|
|
20398
|
+
for (const ch of text) {
|
|
20399
|
+
const cp = ch.codePointAt(0);
|
|
20400
|
+
const w = cp === 32 ? spaceEm : charWidthEm1000(cp);
|
|
20401
|
+
em += w * (1 + spacing / 100);
|
|
20402
|
+
}
|
|
20403
|
+
return em / 1e3 * height * (ratioPct / 100);
|
|
20404
|
+
}
|
|
20405
|
+
var FORBID_START = /* @__PURE__ */ new Set([..."!%),.:;?]}\xA2\xB0\u2032\u2033\u2103\u3009\u300B\u300D\u300F\u3011\u3015!%),.:;?]}\u20A9~\u2026\xB7\u3001\u3002\u3003"]);
|
|
20406
|
+
var FORBID_END = /* @__PURE__ */ new Set([..."$([{\xA3\xA5\u3008\u300A\u300C\u300E\u3010\u3014$([{\u20A9"]);
|
|
20407
|
+
function simulateWrap(text, firstWidth, contWidth, height, ratioPct, mode = "keep", opts) {
|
|
20408
|
+
const EPS = 0.5;
|
|
20409
|
+
const spaceEm = opts?.spaceEm ?? SPACE_EM_FIXED;
|
|
20410
|
+
const spacing = opts?.spacingPct ?? 0;
|
|
20411
|
+
const k = height * ratioPct / 100 / 1e3;
|
|
20412
|
+
const cwCp = (cp) => (cp === 32 ? spaceEm : charWidthEm1000(cp)) * (1 + spacing / 100) * k;
|
|
20413
|
+
const charW = (ch) => cwCp(ch.codePointAt(0));
|
|
20414
|
+
const rangeW = (from, to) => {
|
|
20415
|
+
let w = 0;
|
|
20416
|
+
for (const ch of text.slice(from, to)) w += charW(ch);
|
|
20417
|
+
return w;
|
|
20418
|
+
};
|
|
20419
|
+
const units = text.match(mode === "keep" ? / +|[^ ]+/g : / +|[^ ]/g) ?? [];
|
|
20420
|
+
const starts = [0];
|
|
20421
|
+
let lineW = 0;
|
|
20422
|
+
let avail = firstWidth;
|
|
20423
|
+
let pos = 0;
|
|
20424
|
+
const lineStart = () => starts[starts.length - 1];
|
|
20425
|
+
const breakBefore = (unitPos, w) => {
|
|
20426
|
+
let bp = unitPos;
|
|
20427
|
+
const u = text[unitPos];
|
|
20428
|
+
if (u !== void 0 && FORBID_START.has(u) && bp - 1 > lineStart() && text[bp - 1] !== " ") bp--;
|
|
20429
|
+
while (bp - 1 > lineStart() && FORBID_END.has(text[bp - 1])) bp--;
|
|
20430
|
+
if (bp <= lineStart()) bp = unitPos;
|
|
20431
|
+
starts.push(bp);
|
|
20432
|
+
avail = contWidth;
|
|
20433
|
+
lineW = rangeW(bp, unitPos) + w;
|
|
20434
|
+
};
|
|
20435
|
+
for (const u of units) {
|
|
20436
|
+
if (u[0] === " ") {
|
|
20437
|
+
lineW += charW(" ") * u.length;
|
|
20438
|
+
pos += u.length;
|
|
20439
|
+
continue;
|
|
20440
|
+
}
|
|
20441
|
+
const w = rangeW(pos, pos + u.length);
|
|
20442
|
+
if (lineW + w <= avail + EPS) {
|
|
20443
|
+
lineW += w;
|
|
20444
|
+
pos += u.length;
|
|
20445
|
+
continue;
|
|
20446
|
+
}
|
|
20447
|
+
if (lineW === 0 || w > contWidth + EPS) {
|
|
20448
|
+
let sub = 0;
|
|
20449
|
+
for (const ch of u) {
|
|
20450
|
+
const c = charW(ch);
|
|
20451
|
+
if (lineW + c > avail + EPS && lineW > 0) breakBefore(pos + sub, 0);
|
|
20452
|
+
lineW += c;
|
|
20453
|
+
sub += ch.length;
|
|
20454
|
+
}
|
|
20455
|
+
pos += u.length;
|
|
20456
|
+
continue;
|
|
20457
|
+
}
|
|
20458
|
+
breakBefore(pos, w);
|
|
20459
|
+
pos += u.length;
|
|
20460
|
+
}
|
|
20461
|
+
return { lines: starts.length, starts, lastLineWidth: lineW };
|
|
20462
|
+
}
|
|
20463
|
+
function simulateWrapKeepWord(text, firstWidth, contWidth, height, ratioPct, opts) {
|
|
20464
|
+
return simulateWrap(text, firstWidth, contWidth, height, ratioPct, "keep", opts);
|
|
20465
|
+
}
|
|
20466
|
+
function fitRatioForFewerLines(text, firstWidth, contWidth, height, baseRatio, minRatio, opts) {
|
|
20467
|
+
const base = simulateWrap(text, firstWidth, contWidth, height, baseRatio, "keep", opts);
|
|
20468
|
+
if (base.lines < 2) return null;
|
|
20469
|
+
for (let r = baseRatio - 1; r >= minRatio; r--) {
|
|
20470
|
+
const sim = simulateWrap(text, firstWidth, contWidth, height, r, "keep", opts);
|
|
20471
|
+
if (sim.lines < base.lines) return r;
|
|
20472
|
+
}
|
|
20473
|
+
return null;
|
|
20474
|
+
}
|
|
20475
|
+
|
|
20016
20476
|
// src/hwpx/gongmun.ts
|
|
20017
20477
|
var OFFICIAL_MARGINS = { top: 20, bottom: 10, left: 20, right: 20 };
|
|
20018
20478
|
var PRESET_DEFAULTS = {
|
|
@@ -20048,6 +20508,7 @@ function resolveGongmun(opts) {
|
|
|
20048
20508
|
const preset = normalizeGongmunPreset(opts.preset);
|
|
20049
20509
|
const d = PRESET_DEFAULTS[preset];
|
|
20050
20510
|
const bodyPt = opts.bodyPt ?? d.bodyPt;
|
|
20511
|
+
const autoFitMinRatio = opts.autoFit === false ? null : typeof opts.autoFit === "object" ? Math.min(Math.max(opts.autoFit.minRatio ?? 90, 50), 99) : 90;
|
|
20051
20512
|
return {
|
|
20052
20513
|
preset,
|
|
20053
20514
|
bodyFont: opts.bodyFont ?? "myeongjo",
|
|
@@ -20055,7 +20516,8 @@ function resolveGongmun(opts) {
|
|
|
20055
20516
|
lineSpacing: opts.lineSpacing ?? d.lineSpacing,
|
|
20056
20517
|
numbering: opts.numbering ?? d.numbering,
|
|
20057
20518
|
margins: opts.margins ?? OFFICIAL_MARGINS,
|
|
20058
|
-
centerTitle: opts.centerTitle ?? true
|
|
20519
|
+
centerTitle: opts.centerTitle ?? true,
|
|
20520
|
+
autoFitMinRatio
|
|
20059
20521
|
};
|
|
20060
20522
|
}
|
|
20061
20523
|
var HANGUL_INITIALS = [0, 2, 3, 5, 6, 7, 9, 11, 12, 14, 15, 16, 17, 18];
|
|
@@ -20098,17 +20560,10 @@ function standardMarker(depth, n) {
|
|
|
20098
20560
|
function reportMarker(depth) {
|
|
20099
20561
|
return REPORT_BULLETS[Math.min(depth, REPORT_BULLETS.length - 1)];
|
|
20100
20562
|
}
|
|
20101
|
-
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";
|
|
20102
20563
|
function markerWidth(marker, bodyHeight) {
|
|
20103
|
-
|
|
20104
|
-
|
|
20105
|
-
|
|
20106
|
-
if (/[가-힣]/.test(c) || MARKER_FULLWIDTH.includes(c)) w += bodyHeight;
|
|
20107
|
-
else if (c === "." || c === ",") w += Math.round(bodyHeight * 0.25);
|
|
20108
|
-
else if (c === "(" || c === ")") w += Math.round(bodyHeight * 0.45);
|
|
20109
|
-
else w += Math.round(ta);
|
|
20110
|
-
}
|
|
20111
|
-
return Math.round(w + ta);
|
|
20564
|
+
let em = SPACE_EM_FIXED;
|
|
20565
|
+
for (const c of marker) em += charWidthEm1000(c.codePointAt(0));
|
|
20566
|
+
return Math.round(em / 1e3 * bodyHeight);
|
|
20112
20567
|
}
|
|
20113
20568
|
function levelIndent(depth, bodyHeight, numbering) {
|
|
20114
20569
|
const marker = numbering === "report" ? reportMarker(depth) : standardMarker(depth, 0);
|
|
@@ -20148,77 +20603,53 @@ function mmToHwpunit(mm) {
|
|
|
20148
20603
|
return Math.round(mm * 7200 / 25.4);
|
|
20149
20604
|
}
|
|
20150
20605
|
|
|
20151
|
-
// src/
|
|
20152
|
-
|
|
20153
|
-
|
|
20154
|
-
|
|
20155
|
-
|
|
20156
|
-
|
|
20157
|
-
|
|
20158
|
-
var NS_OCF = "urn:oasis:names:tc:opendocument:xmlns:container";
|
|
20159
|
-
var CHAR_NORMAL = 0;
|
|
20160
|
-
var CHAR_BOLD = 1;
|
|
20161
|
-
var CHAR_ITALIC = 2;
|
|
20162
|
-
var CHAR_BOLD_ITALIC = 3;
|
|
20163
|
-
var CHAR_CODE = 4;
|
|
20164
|
-
var CHAR_H1 = 5;
|
|
20165
|
-
var CHAR_H2 = 6;
|
|
20166
|
-
var CHAR_H3 = 7;
|
|
20167
|
-
var CHAR_H4 = 8;
|
|
20168
|
-
var CHAR_TABLE_HEADER = 9;
|
|
20169
|
-
var CHAR_QUOTE = 10;
|
|
20170
|
-
var PARA_NORMAL = 0;
|
|
20171
|
-
var PARA_H1 = 1;
|
|
20172
|
-
var PARA_H2 = 2;
|
|
20173
|
-
var PARA_H3 = 3;
|
|
20174
|
-
var PARA_H4 = 4;
|
|
20175
|
-
var PARA_CODE = 5;
|
|
20176
|
-
var PARA_QUOTE = 6;
|
|
20177
|
-
var PARA_LIST = 7;
|
|
20178
|
-
var DEFAULT_TEXT_COLOR = "#000000";
|
|
20179
|
-
function resolveTheme(theme) {
|
|
20180
|
-
return {
|
|
20181
|
-
h1: theme?.headingColors?.[1] ?? DEFAULT_TEXT_COLOR,
|
|
20182
|
-
h2: theme?.headingColors?.[2] ?? DEFAULT_TEXT_COLOR,
|
|
20183
|
-
h3: theme?.headingColors?.[3] ?? DEFAULT_TEXT_COLOR,
|
|
20184
|
-
h4: theme?.headingColors?.[4] ?? theme?.headingColors?.[3] ?? DEFAULT_TEXT_COLOR,
|
|
20185
|
-
body: theme?.bodyColor ?? DEFAULT_TEXT_COLOR,
|
|
20186
|
-
quote: theme?.quoteColor ?? DEFAULT_TEXT_COLOR,
|
|
20187
|
-
/** quoteColor가 명시되었는지 — blockquote charPr 분기에 사용 (baseline 호환) */
|
|
20188
|
-
hasQuoteOption: theme?.quoteColor !== void 0,
|
|
20189
|
-
tableHeader: theme?.tableHeaderColor ?? theme?.bodyColor ?? DEFAULT_TEXT_COLOR,
|
|
20190
|
-
tableHeaderBold: !!theme?.tableHeaderBold
|
|
20191
|
-
};
|
|
20606
|
+
// src/diff/text-diff.ts
|
|
20607
|
+
function similarity(a, b) {
|
|
20608
|
+
if (a === b) return 1;
|
|
20609
|
+
if (!a || !b) return 0;
|
|
20610
|
+
const maxLen = Math.max(a.length, b.length);
|
|
20611
|
+
if (maxLen === 0) return 1;
|
|
20612
|
+
return 1 - levenshtein(a, b) / maxLen;
|
|
20192
20613
|
}
|
|
20193
|
-
|
|
20194
|
-
|
|
20195
|
-
const gongmun = options?.gongmun ? resolveGongmun(options.gongmun) : null;
|
|
20196
|
-
const blocks = parseMarkdownToBlocks(markdown);
|
|
20197
|
-
const sectionXml = blocksToSectionXml(blocks, theme, gongmun);
|
|
20198
|
-
const zip = new JSZip6();
|
|
20199
|
-
zip.file("mimetype", "application/hwp+zip", { compression: "STORE" });
|
|
20200
|
-
zip.file("META-INF/container.xml", generateContainerXml());
|
|
20201
|
-
zip.file("Contents/content.hpf", generateManifest());
|
|
20202
|
-
zip.file("Contents/header.xml", generateHeaderXml(theme, gongmun));
|
|
20203
|
-
zip.file("Contents/section0.xml", sectionXml);
|
|
20204
|
-
zip.file("Preview/PrvText.txt", buildPrvText(blocks));
|
|
20205
|
-
return await zip.generateAsync({ type: "arraybuffer" });
|
|
20614
|
+
function normalizedSimilarity(a, b) {
|
|
20615
|
+
return similarity(normalize(a), normalize(b));
|
|
20206
20616
|
}
|
|
20207
|
-
function
|
|
20208
|
-
|
|
20209
|
-
|
|
20210
|
-
|
|
20211
|
-
|
|
20212
|
-
|
|
20213
|
-
|
|
20214
|
-
|
|
20215
|
-
if (
|
|
20617
|
+
function normalize(s) {
|
|
20618
|
+
return s.replace(/\s+/g, " ").trim();
|
|
20619
|
+
}
|
|
20620
|
+
var MAX_LEVENSHTEIN_LEN = 1e4;
|
|
20621
|
+
function levenshtein(a, b) {
|
|
20622
|
+
if (a.length + b.length > MAX_LEVENSHTEIN_LEN) {
|
|
20623
|
+
const sampleLen = Math.min(500, a.length, b.length);
|
|
20624
|
+
let diffs = 0;
|
|
20625
|
+
for (let i = 0; i < sampleLen; i++) if (a[i] !== b[i]) diffs++;
|
|
20626
|
+
const sampleRate = sampleLen > 0 ? diffs / sampleLen : 1;
|
|
20627
|
+
return Math.abs(a.length - b.length) + Math.round(Math.min(a.length, b.length) * sampleRate);
|
|
20216
20628
|
}
|
|
20217
|
-
|
|
20629
|
+
if (a.length > b.length) [a, b] = [b, a];
|
|
20630
|
+
const m = a.length;
|
|
20631
|
+
const n = b.length;
|
|
20632
|
+
let prev = Array.from({ length: m + 1 }, (_, i) => i);
|
|
20633
|
+
let curr = new Array(m + 1);
|
|
20634
|
+
for (let j = 1; j <= n; j++) {
|
|
20635
|
+
curr[0] = j;
|
|
20636
|
+
for (let i = 1; i <= m; i++) {
|
|
20637
|
+
if (a[i - 1] === b[j - 1]) {
|
|
20638
|
+
curr[i] = prev[i - 1];
|
|
20639
|
+
} else {
|
|
20640
|
+
curr[i] = 1 + Math.min(prev[i - 1], prev[i], curr[i - 1]);
|
|
20641
|
+
}
|
|
20642
|
+
}
|
|
20643
|
+
;
|
|
20644
|
+
[prev, curr] = [curr, prev];
|
|
20645
|
+
}
|
|
20646
|
+
return prev[m];
|
|
20218
20647
|
}
|
|
20219
|
-
|
|
20648
|
+
|
|
20649
|
+
// src/roundtrip/markdown-units.ts
|
|
20650
|
+
function splitMarkdownUnits(md2) {
|
|
20220
20651
|
const lines = md2.split("\n");
|
|
20221
|
-
const
|
|
20652
|
+
const units = [];
|
|
20222
20653
|
let i = 0;
|
|
20223
20654
|
while (i < lines.length) {
|
|
20224
20655
|
const line = lines[i];
|
|
@@ -20226,21 +20657,464 @@ function parseMarkdownToBlocks(md2) {
|
|
|
20226
20657
|
i++;
|
|
20227
20658
|
continue;
|
|
20228
20659
|
}
|
|
20229
|
-
|
|
20230
|
-
|
|
20231
|
-
|
|
20232
|
-
|
|
20233
|
-
|
|
20234
|
-
|
|
20235
|
-
|
|
20236
|
-
|
|
20660
|
+
if (line.trim().startsWith("<table>")) {
|
|
20661
|
+
const collected2 = [];
|
|
20662
|
+
let depth = 0;
|
|
20663
|
+
while (i < lines.length) {
|
|
20664
|
+
const l = lines[i];
|
|
20665
|
+
collected2.push(l);
|
|
20666
|
+
depth += (l.match(/<table>/g) || []).length;
|
|
20667
|
+
depth -= (l.match(/<\/table>/g) || []).length;
|
|
20237
20668
|
i++;
|
|
20669
|
+
if (depth <= 0) break;
|
|
20238
20670
|
}
|
|
20239
|
-
|
|
20240
|
-
blocks.push({ type: "code_block", text: codeLines.join("\n"), lang });
|
|
20671
|
+
units.push({ kind: "html-table", raw: collected2.join("\n"), lines: collected2 });
|
|
20241
20672
|
continue;
|
|
20242
20673
|
}
|
|
20243
|
-
if (
|
|
20674
|
+
if (line.trimStart().startsWith("|")) {
|
|
20675
|
+
const collected2 = [];
|
|
20676
|
+
while (i < lines.length && lines[i].trimStart().startsWith("|")) {
|
|
20677
|
+
collected2.push(lines[i]);
|
|
20678
|
+
i++;
|
|
20679
|
+
}
|
|
20680
|
+
units.push({ kind: "gfm-table", raw: collected2.join("\n"), lines: collected2 });
|
|
20681
|
+
continue;
|
|
20682
|
+
}
|
|
20683
|
+
if (/^-{3,}\s*$/.test(line.trim())) {
|
|
20684
|
+
units.push({ kind: "separator", raw: line.trim(), lines: [line.trim()] });
|
|
20685
|
+
i++;
|
|
20686
|
+
continue;
|
|
20687
|
+
}
|
|
20688
|
+
if (/^!\[image\]\([^)]*\)\s*$/.test(line.trim())) {
|
|
20689
|
+
units.push({ kind: "image", raw: line.trim(), lines: [line.trim()] });
|
|
20690
|
+
i++;
|
|
20691
|
+
continue;
|
|
20692
|
+
}
|
|
20693
|
+
const collected = [];
|
|
20694
|
+
while (i < lines.length && lines[i].trim() && !lines[i].trimStart().startsWith("|") && !lines[i].trim().startsWith("<table>")) {
|
|
20695
|
+
collected.push(lines[i].trim());
|
|
20696
|
+
i++;
|
|
20697
|
+
}
|
|
20698
|
+
units.push({ kind: "text", raw: collected.join("\n"), lines: collected });
|
|
20699
|
+
}
|
|
20700
|
+
return units;
|
|
20701
|
+
}
|
|
20702
|
+
function alignUnits(a, b) {
|
|
20703
|
+
const m = a.length, n = b.length;
|
|
20704
|
+
if (m * n > 4e6) {
|
|
20705
|
+
const result2 = [];
|
|
20706
|
+
let pre = 0;
|
|
20707
|
+
while (pre < m && pre < n && a[pre] === b[pre]) {
|
|
20708
|
+
result2.push([pre, pre]);
|
|
20709
|
+
pre++;
|
|
20710
|
+
}
|
|
20711
|
+
let suf = 0;
|
|
20712
|
+
while (suf < m - pre && suf < n - pre && a[m - 1 - suf] === b[n - 1 - suf]) suf++;
|
|
20713
|
+
const aMid = m - pre - suf, bMid = n - pre - suf;
|
|
20714
|
+
if (aMid === bMid) {
|
|
20715
|
+
for (let i2 = 0; i2 < aMid; i2++) result2.push([pre + i2, pre + i2]);
|
|
20716
|
+
} else {
|
|
20717
|
+
for (let i2 = 0; i2 < aMid; i2++) result2.push([pre + i2, null]);
|
|
20718
|
+
for (let j2 = 0; j2 < bMid; j2++) result2.push([null, pre + j2]);
|
|
20719
|
+
}
|
|
20720
|
+
for (let s = suf - 1; s >= 0; s--) result2.push([m - 1 - s, n - 1 - s]);
|
|
20721
|
+
return result2;
|
|
20722
|
+
}
|
|
20723
|
+
const dp = Array.from({ length: m + 1 }, () => new Int32Array(n + 1));
|
|
20724
|
+
for (let i2 = 1; i2 <= m; i2++) {
|
|
20725
|
+
for (let j2 = 1; j2 <= n; j2++) {
|
|
20726
|
+
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]);
|
|
20727
|
+
}
|
|
20728
|
+
}
|
|
20729
|
+
const matches = [];
|
|
20730
|
+
let i = m, j = n;
|
|
20731
|
+
while (i > 0 && j > 0) {
|
|
20732
|
+
if (a[i - 1] === b[j - 1] && dp[i][j] === dp[i - 1][j - 1] + 1) {
|
|
20733
|
+
matches.push([i - 1, j - 1]);
|
|
20734
|
+
i--;
|
|
20735
|
+
j--;
|
|
20736
|
+
} else if (dp[i - 1][j] >= dp[i][j - 1]) i--;
|
|
20737
|
+
else j--;
|
|
20738
|
+
}
|
|
20739
|
+
matches.reverse();
|
|
20740
|
+
const result = [];
|
|
20741
|
+
let ai = 0, bi = 0;
|
|
20742
|
+
const flushGap = (aEnd, bEnd) => {
|
|
20743
|
+
if (aEnd - ai === bEnd - bi) {
|
|
20744
|
+
while (ai < aEnd) result.push([ai++, bi++]);
|
|
20745
|
+
return;
|
|
20746
|
+
}
|
|
20747
|
+
while (ai < aEnd && bi < bEnd) {
|
|
20748
|
+
const sim = normalizedSimilarity(a[ai], b[bi]);
|
|
20749
|
+
if (sim >= 0.4) {
|
|
20750
|
+
if (aEnd - ai > bEnd - bi && bestSimInRange(a, ai + 1, ai + (aEnd - ai) - (bEnd - bi), b[bi]) > sim) {
|
|
20751
|
+
result.push([ai++, null]);
|
|
20752
|
+
} else if (bEnd - bi > aEnd - ai && bestSimInRange(b, bi + 1, bi + (bEnd - bi) - (aEnd - ai), a[ai]) > sim) {
|
|
20753
|
+
result.push([null, bi++]);
|
|
20754
|
+
} else {
|
|
20755
|
+
result.push([ai++, bi++]);
|
|
20756
|
+
}
|
|
20757
|
+
} else if (aEnd - ai >= bEnd - bi) result.push([ai++, null]);
|
|
20758
|
+
else result.push([null, bi++]);
|
|
20759
|
+
}
|
|
20760
|
+
while (ai < aEnd) result.push([ai++, null]);
|
|
20761
|
+
while (bi < bEnd) result.push([null, bi++]);
|
|
20762
|
+
};
|
|
20763
|
+
for (const [pi, pj] of matches) {
|
|
20764
|
+
flushGap(pi, pj);
|
|
20765
|
+
result.push([ai++, bi++]);
|
|
20766
|
+
}
|
|
20767
|
+
flushGap(m, n);
|
|
20768
|
+
return result;
|
|
20769
|
+
}
|
|
20770
|
+
function bestSimInRange(arr, from, to, target) {
|
|
20771
|
+
let best = 0;
|
|
20772
|
+
for (let k = from; k <= to && k < arr.length; k++) {
|
|
20773
|
+
const s = normalizedSimilarity(arr[k], target);
|
|
20774
|
+
if (s > best) best = s;
|
|
20775
|
+
}
|
|
20776
|
+
return best;
|
|
20777
|
+
}
|
|
20778
|
+
function escapeGfm(text) {
|
|
20779
|
+
return text.replace(/~/g, "\\~");
|
|
20780
|
+
}
|
|
20781
|
+
var HWP_SHAPE_ALT_TEXT_RE = /(?:모서리가 둥근 |둥근 )?(?:사각형|직사각형|정사각형|원|타원|삼각형|이등변 삼각형|직각 삼각형|선|직선|곡선|화살표|굵은 화살표|이중 화살표|오각형|육각형|팔각형|별|[4-8]점별|십자|십자형|구름|구름형|마름모|도넛|평행사변형|사다리꼴|부채꼴|호|반원|물결|번개|하트|빗금|블록 화살표|수식|표|그림|개체|그리기\s?개체|묶음\s?개체|글상자|수식\s?개체|OLE\s?개체)\s?입니다\.?/g;
|
|
20782
|
+
function sanitizeText(text) {
|
|
20783
|
+
let result = mapPuaText(text).replace(/[\u{F0000}-\u{FFFFD}]/gu, "").replace(HWP_SHAPE_ALT_TEXT_RE, "").replace(/ +/g, " ").trim();
|
|
20784
|
+
if (result.length <= 30 && result.includes(" ")) {
|
|
20785
|
+
const tokens = result.split(" ");
|
|
20786
|
+
const koreanSingleCharCount = tokens.filter((t) => t.length === 1 && /[가-ㄱ-ㆎ]/.test(t)).length;
|
|
20787
|
+
if (tokens.length >= 3 && koreanSingleCharCount / tokens.length >= 0.7) {
|
|
20788
|
+
result = tokens.join("");
|
|
20789
|
+
}
|
|
20790
|
+
}
|
|
20791
|
+
return result;
|
|
20792
|
+
}
|
|
20793
|
+
function normForMatch(text) {
|
|
20794
|
+
return sanitizeText(text).replace(/\s+/g, " ").trim();
|
|
20795
|
+
}
|
|
20796
|
+
function unescapeGfm(text) {
|
|
20797
|
+
return text.replace(/\\~/g, "~");
|
|
20798
|
+
}
|
|
20799
|
+
function summarize(text) {
|
|
20800
|
+
const t = text.replace(/\s+/g, " ").trim();
|
|
20801
|
+
return t.length > 80 ? t.slice(0, 77) + "..." : t;
|
|
20802
|
+
}
|
|
20803
|
+
function replicateGfmTable(table) {
|
|
20804
|
+
const { cells, rows: numRows, cols: numCols } = table;
|
|
20805
|
+
if (numRows === 0 || numCols === 0) return null;
|
|
20806
|
+
if (numRows === 1 && numCols === 1) return null;
|
|
20807
|
+
if (numCols === 1) return null;
|
|
20808
|
+
const display = Array.from({ length: numRows }, (_, r) => Array.from({ length: numCols }, (_2, c) => ({ text: "", gridR: r, gridC: c })));
|
|
20809
|
+
const skip = /* @__PURE__ */ new Set();
|
|
20810
|
+
for (let r = 0; r < numRows; r++) {
|
|
20811
|
+
for (let c = 0; c < numCols; c++) {
|
|
20812
|
+
if (skip.has(`${r},${c}`)) continue;
|
|
20813
|
+
const cell = cells[r]?.[c];
|
|
20814
|
+
if (!cell) continue;
|
|
20815
|
+
display[r][c] = {
|
|
20816
|
+
text: escapeGfm(sanitizeText(cell.text)).replace(/\|/g, "\\|").replace(/\n/g, "<br>"),
|
|
20817
|
+
gridR: r,
|
|
20818
|
+
gridC: c
|
|
20819
|
+
};
|
|
20820
|
+
for (let dr = 0; dr < cell.rowSpan; dr++) {
|
|
20821
|
+
for (let dc = 0; dc < cell.colSpan; dc++) {
|
|
20822
|
+
if (dr === 0 && dc === 0) continue;
|
|
20823
|
+
if (r + dr < numRows && c + dc < numCols) skip.add(`${r + dr},${c + dc}`);
|
|
20824
|
+
}
|
|
20825
|
+
}
|
|
20826
|
+
c += cell.colSpan - 1;
|
|
20827
|
+
}
|
|
20828
|
+
}
|
|
20829
|
+
const uniqueRows = [];
|
|
20830
|
+
let pendingLabelRow = null;
|
|
20831
|
+
for (let r = 0; r < display.length; r++) {
|
|
20832
|
+
const row = display[r];
|
|
20833
|
+
if (row.every((cell) => cell.text === "")) continue;
|
|
20834
|
+
const nonEmptyCols = row.filter((cell) => cell.text !== "");
|
|
20835
|
+
const hasSkipInRow = row.some((_, c) => skip.has(`${r},${c}`));
|
|
20836
|
+
if (!hasSkipInRow && nonEmptyCols.length === 1 && row[0].text !== "" && row.slice(1).every((c) => c.text === "")) {
|
|
20837
|
+
if (pendingLabelRow) uniqueRows.push(pendingLabelRow);
|
|
20838
|
+
pendingLabelRow = row;
|
|
20839
|
+
continue;
|
|
20840
|
+
}
|
|
20841
|
+
if (pendingLabelRow) {
|
|
20842
|
+
if (row[0].text === "") row[0] = pendingLabelRow[0];
|
|
20843
|
+
else uniqueRows.push(pendingLabelRow);
|
|
20844
|
+
pendingLabelRow = null;
|
|
20845
|
+
}
|
|
20846
|
+
uniqueRows.push(row);
|
|
20847
|
+
}
|
|
20848
|
+
if (pendingLabelRow) uniqueRows.push(pendingLabelRow);
|
|
20849
|
+
return uniqueRows.length > 0 ? uniqueRows : null;
|
|
20850
|
+
}
|
|
20851
|
+
function parseGfmTable(lines) {
|
|
20852
|
+
const rows = [];
|
|
20853
|
+
for (const line of lines) {
|
|
20854
|
+
const trimmed = line.trim();
|
|
20855
|
+
if (!trimmed.startsWith("|")) continue;
|
|
20856
|
+
const cells = trimmed.split(/(?<!\\)\|/).slice(1, -1).map((c) => c.trim());
|
|
20857
|
+
if (cells.length === 0) continue;
|
|
20858
|
+
if (cells.every((c) => /^:?-{3,}:?$/.test(c))) continue;
|
|
20859
|
+
rows.push(cells);
|
|
20860
|
+
}
|
|
20861
|
+
return rows;
|
|
20862
|
+
}
|
|
20863
|
+
function unescapeGfmCell(text) {
|
|
20864
|
+
return text.replace(/<br\s*\/?>/gi, "\n").replace(/\\\|/g, "|").replace(/\\~/g, "~");
|
|
20865
|
+
}
|
|
20866
|
+
function replicateCellInnerHtml(cell) {
|
|
20867
|
+
if (cell.blocks?.length) {
|
|
20868
|
+
return cell.blocks.map((b) => {
|
|
20869
|
+
if (b.type === "table" && b.table) {
|
|
20870
|
+
const cap = b.table.caption ? sanitizeText(b.table.caption) : "";
|
|
20871
|
+
return (cap ? cap + "<br>" : "") + replicateTableToHtml(b.table);
|
|
20872
|
+
}
|
|
20873
|
+
if (b.type === "image" && b.text) return `<img src="${b.text}" alt="image">`;
|
|
20874
|
+
const t = sanitizeText(b.text ?? "");
|
|
20875
|
+
return t ? t.replace(/\n/g, "<br>") : "";
|
|
20876
|
+
}).filter(Boolean).join("<br>");
|
|
20877
|
+
}
|
|
20878
|
+
return sanitizeText(cell.text).replace(/\n/g, "<br>");
|
|
20879
|
+
}
|
|
20880
|
+
function replicateTableToHtml(table) {
|
|
20881
|
+
const rows = replicateHtmlTable(table);
|
|
20882
|
+
const lines = ["<table>"];
|
|
20883
|
+
for (let r = 0; r < rows.length; r++) {
|
|
20884
|
+
const tag = rows[r].tag;
|
|
20885
|
+
const rowHtml = rows[r].cells.map((cell) => {
|
|
20886
|
+
const attrs = [];
|
|
20887
|
+
if (cell.colSpan > 1) attrs.push(`colspan="${cell.colSpan}"`);
|
|
20888
|
+
if (cell.rowSpan > 1) attrs.push(`rowspan="${cell.rowSpan}"`);
|
|
20889
|
+
const attrStr = attrs.length ? " " + attrs.join(" ") : "";
|
|
20890
|
+
return `<${tag}${attrStr}>${cell.inner}</${tag}>`;
|
|
20891
|
+
});
|
|
20892
|
+
if (rowHtml.length) lines.push(`<tr>${rowHtml.join("")}</tr>`);
|
|
20893
|
+
}
|
|
20894
|
+
lines.push("</table>");
|
|
20895
|
+
return lines.join("\n");
|
|
20896
|
+
}
|
|
20897
|
+
function replicateHtmlTable(table) {
|
|
20898
|
+
const { cells, rows: numRows, cols: numCols } = table;
|
|
20899
|
+
const skip = /* @__PURE__ */ new Set();
|
|
20900
|
+
const result = [];
|
|
20901
|
+
for (let r = 0; r < numRows; r++) {
|
|
20902
|
+
const tag = r === 0 ? "th" : "td";
|
|
20903
|
+
const rowCells = [];
|
|
20904
|
+
for (let c = 0; c < numCols; c++) {
|
|
20905
|
+
if (skip.has(`${r},${c}`)) continue;
|
|
20906
|
+
const cell = cells[r]?.[c];
|
|
20907
|
+
if (!cell) continue;
|
|
20908
|
+
for (let dr = 0; dr < cell.rowSpan; dr++) {
|
|
20909
|
+
for (let dc = 0; dc < cell.colSpan; dc++) {
|
|
20910
|
+
if (dr === 0 && dc === 0) continue;
|
|
20911
|
+
if (r + dr < numRows && c + dc < numCols) skip.add(`${r + dr},${c + dc}`);
|
|
20912
|
+
}
|
|
20913
|
+
}
|
|
20914
|
+
rowCells.push({
|
|
20915
|
+
inner: replicateCellInnerHtml(cell),
|
|
20916
|
+
colSpan: cell.colSpan,
|
|
20917
|
+
rowSpan: cell.rowSpan,
|
|
20918
|
+
gridR: r,
|
|
20919
|
+
gridC: c
|
|
20920
|
+
});
|
|
20921
|
+
}
|
|
20922
|
+
if (rowCells.length) result.push({ tag, cells: rowCells });
|
|
20923
|
+
}
|
|
20924
|
+
return result;
|
|
20925
|
+
}
|
|
20926
|
+
function parseHtmlTable(raw) {
|
|
20927
|
+
const re = /<(\/?)(table|tr|td|th)((?:"[^"]*"|'[^']*'|[^>"'])*?)>/gi;
|
|
20928
|
+
let depth = 0;
|
|
20929
|
+
let currentRow = null;
|
|
20930
|
+
let cellStart = -1;
|
|
20931
|
+
let cellInfo = null;
|
|
20932
|
+
const rows = [];
|
|
20933
|
+
let m;
|
|
20934
|
+
while ((m = re.exec(raw)) !== null) {
|
|
20935
|
+
const isClose = m[1] === "/";
|
|
20936
|
+
const tag = m[2].toLowerCase();
|
|
20937
|
+
const attrs = m[3] || "";
|
|
20938
|
+
if (tag === "table") {
|
|
20939
|
+
depth += isClose ? -1 : 1;
|
|
20940
|
+
if (depth < 0) return null;
|
|
20941
|
+
continue;
|
|
20942
|
+
}
|
|
20943
|
+
if (depth !== 1) continue;
|
|
20944
|
+
if (tag === "tr") {
|
|
20945
|
+
if (!isClose) currentRow = [];
|
|
20946
|
+
else if (currentRow) {
|
|
20947
|
+
rows.push({ tag: rows.length === 0 ? "th" : "td", cells: currentRow });
|
|
20948
|
+
currentRow = null;
|
|
20949
|
+
}
|
|
20950
|
+
} else {
|
|
20951
|
+
if (!isClose) {
|
|
20952
|
+
const cs = parseInt(attrs.match(/colspan\s*=\s*"(\d+)"/i)?.[1] || "1", 10);
|
|
20953
|
+
const rs = parseInt(attrs.match(/rowspan\s*=\s*"(\d+)"/i)?.[1] || "1", 10);
|
|
20954
|
+
cellStart = m.index + m[0].length;
|
|
20955
|
+
cellInfo = { colSpan: isNaN(cs) ? 1 : cs, rowSpan: isNaN(rs) ? 1 : rs };
|
|
20956
|
+
} else if (cellStart >= 0 && cellInfo && currentRow) {
|
|
20957
|
+
currentRow.push({ inner: raw.slice(cellStart, m.index), colSpan: cellInfo.colSpan, rowSpan: cellInfo.rowSpan });
|
|
20958
|
+
cellStart = -1;
|
|
20959
|
+
cellInfo = null;
|
|
20960
|
+
}
|
|
20961
|
+
}
|
|
20962
|
+
}
|
|
20963
|
+
if (depth !== 0) return null;
|
|
20964
|
+
return rows;
|
|
20965
|
+
}
|
|
20966
|
+
var AUTONUM_PREFIX_RE = /^(?:[0-90-9a-zA-Z가-힣]{1,6}[.)\]:]|[([][0-90-9a-zA-Z가-힣]{1,6}[)\]][.:]?|[ⅰ-ⅹⅠ-Ⅹ①-⑮][.)\]:]?)$/u;
|
|
20967
|
+
function htmlCellInnerToLines(inner) {
|
|
20968
|
+
let hadNonText = false;
|
|
20969
|
+
let work = inner;
|
|
20970
|
+
if (/<table[\s>]/i.test(work)) {
|
|
20971
|
+
hadNonText = true;
|
|
20972
|
+
work = removeNestedTables(work);
|
|
20973
|
+
}
|
|
20974
|
+
if (/<img\s/i.test(work)) {
|
|
20975
|
+
hadNonText = true;
|
|
20976
|
+
work = work.replace(/<img\s(?:"[^"]*"|'[^']*'|[^>"'])*?>/gi, "");
|
|
20977
|
+
}
|
|
20978
|
+
const lines = work.split(/<br\s*\/?>/gi).map((s) => s.trim()).filter((s) => s.length > 0);
|
|
20979
|
+
return { lines, hadNonText };
|
|
20980
|
+
}
|
|
20981
|
+
function extractTopLevelTables(html) {
|
|
20982
|
+
const result = [];
|
|
20983
|
+
let depth = 0;
|
|
20984
|
+
let start = -1;
|
|
20985
|
+
const re = /<(\/?)table(?:[\s>]|>)/gi;
|
|
20986
|
+
let m;
|
|
20987
|
+
while ((m = re.exec(html)) !== null) {
|
|
20988
|
+
if (m[1] !== "/") {
|
|
20989
|
+
if (depth === 0) start = m.index;
|
|
20990
|
+
depth++;
|
|
20991
|
+
} else {
|
|
20992
|
+
depth--;
|
|
20993
|
+
if (depth === 0 && start >= 0) {
|
|
20994
|
+
result.push(html.slice(start, m.index + m[0].length));
|
|
20995
|
+
start = -1;
|
|
20996
|
+
}
|
|
20997
|
+
if (depth < 0) depth = 0;
|
|
20998
|
+
}
|
|
20999
|
+
}
|
|
21000
|
+
return result;
|
|
21001
|
+
}
|
|
21002
|
+
function removeNestedTables(html) {
|
|
21003
|
+
let result = "";
|
|
21004
|
+
let depth = 0;
|
|
21005
|
+
const re = /<(\/?)table(?:[\s>]|>)/gi;
|
|
21006
|
+
let last = 0;
|
|
21007
|
+
let m;
|
|
21008
|
+
while ((m = re.exec(html)) !== null) {
|
|
21009
|
+
if (m[1] !== "/") {
|
|
21010
|
+
if (depth === 0) result += html.slice(last, m.index);
|
|
21011
|
+
depth++;
|
|
21012
|
+
} else {
|
|
21013
|
+
depth--;
|
|
21014
|
+
if (depth === 0) last = m.index + m[0].length;
|
|
21015
|
+
if (depth < 0) depth = 0;
|
|
21016
|
+
}
|
|
21017
|
+
}
|
|
21018
|
+
if (depth === 0) result += html.slice(last);
|
|
21019
|
+
return result;
|
|
21020
|
+
}
|
|
21021
|
+
|
|
21022
|
+
// src/hwpx/generator.ts
|
|
21023
|
+
var NS_SECTION = "http://www.hancom.co.kr/hwpml/2011/section";
|
|
21024
|
+
var NS_PARA = "http://www.hancom.co.kr/hwpml/2011/paragraph";
|
|
21025
|
+
var NS_HEAD = "http://www.hancom.co.kr/hwpml/2011/head";
|
|
21026
|
+
var NS_CORE = "http://www.hancom.co.kr/hwpml/2011/core";
|
|
21027
|
+
var NS_OPF = "http://www.idpf.org/2007/opf/";
|
|
21028
|
+
var NS_HPF = "http://www.hancom.co.kr/schema/2011/hpf";
|
|
21029
|
+
var NS_OCF = "urn:oasis:names:tc:opendocument:xmlns:container";
|
|
21030
|
+
var CHAR_NORMAL = 0;
|
|
21031
|
+
var CHAR_BOLD = 1;
|
|
21032
|
+
var CHAR_ITALIC = 2;
|
|
21033
|
+
var CHAR_BOLD_ITALIC = 3;
|
|
21034
|
+
var CHAR_CODE = 4;
|
|
21035
|
+
var CHAR_H1 = 5;
|
|
21036
|
+
var CHAR_H2 = 6;
|
|
21037
|
+
var CHAR_H3 = 7;
|
|
21038
|
+
var CHAR_H4 = 8;
|
|
21039
|
+
var CHAR_TABLE_HEADER = 9;
|
|
21040
|
+
var CHAR_QUOTE = 10;
|
|
21041
|
+
var PARA_NORMAL = 0;
|
|
21042
|
+
var PARA_H1 = 1;
|
|
21043
|
+
var PARA_H2 = 2;
|
|
21044
|
+
var PARA_H3 = 3;
|
|
21045
|
+
var PARA_H4 = 4;
|
|
21046
|
+
var PARA_CODE = 5;
|
|
21047
|
+
var PARA_QUOTE = 6;
|
|
21048
|
+
var PARA_LIST = 7;
|
|
21049
|
+
var DEFAULT_TEXT_COLOR = "#000000";
|
|
21050
|
+
function resolveTheme(theme) {
|
|
21051
|
+
return {
|
|
21052
|
+
h1: theme?.headingColors?.[1] ?? DEFAULT_TEXT_COLOR,
|
|
21053
|
+
h2: theme?.headingColors?.[2] ?? DEFAULT_TEXT_COLOR,
|
|
21054
|
+
h3: theme?.headingColors?.[3] ?? DEFAULT_TEXT_COLOR,
|
|
21055
|
+
h4: theme?.headingColors?.[4] ?? theme?.headingColors?.[3] ?? DEFAULT_TEXT_COLOR,
|
|
21056
|
+
body: theme?.bodyColor ?? DEFAULT_TEXT_COLOR,
|
|
21057
|
+
quote: theme?.quoteColor ?? DEFAULT_TEXT_COLOR,
|
|
21058
|
+
/** quoteColor가 명시되었는지 — blockquote charPr 분기에 사용 (baseline 호환) */
|
|
21059
|
+
hasQuoteOption: theme?.quoteColor !== void 0,
|
|
21060
|
+
tableHeader: theme?.tableHeaderColor ?? theme?.bodyColor ?? DEFAULT_TEXT_COLOR,
|
|
21061
|
+
tableHeaderBold: !!theme?.tableHeaderBold
|
|
21062
|
+
};
|
|
21063
|
+
}
|
|
21064
|
+
async function markdownToHwpx(markdown, options) {
|
|
21065
|
+
const theme = resolveTheme(options?.theme);
|
|
21066
|
+
const gongmun = options?.gongmun ? resolveGongmun(options.gongmun) : null;
|
|
21067
|
+
const blocks = parseMarkdownToBlocks(markdown);
|
|
21068
|
+
const gongmunList = gongmun ? precomputeGongmunList(blocks, gongmun) : null;
|
|
21069
|
+
const fit = gongmun && gongmunList ? computeGongmunFitPlan(blocks, gongmun, gongmunList) : null;
|
|
21070
|
+
const sectionXml = blocksToSectionXml(blocks, theme, gongmun, gongmunList, fit);
|
|
21071
|
+
const zip = new JSZip6();
|
|
21072
|
+
zip.file("mimetype", "application/hwp+zip", { compression: "STORE" });
|
|
21073
|
+
zip.file("META-INF/container.xml", generateContainerXml());
|
|
21074
|
+
zip.file("Contents/content.hpf", generateManifest());
|
|
21075
|
+
zip.file("Contents/header.xml", generateHeaderXml(theme, gongmun, fit?.variants ?? []));
|
|
21076
|
+
zip.file("Contents/section0.xml", sectionXml);
|
|
21077
|
+
zip.file("Preview/PrvText.txt", buildPrvText(blocks));
|
|
21078
|
+
return await zip.generateAsync({ type: "arraybuffer" });
|
|
21079
|
+
}
|
|
21080
|
+
function buildPrvText(blocks) {
|
|
21081
|
+
const lines = [];
|
|
21082
|
+
let bytes = 0;
|
|
21083
|
+
for (const b of blocks) {
|
|
21084
|
+
let text = b.text || (b.rows ? b.rows.map((r) => r.join(" ")).join("\n") : "");
|
|
21085
|
+
if (b.type === "html_table") text = text.replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
|
|
21086
|
+
if (!text) continue;
|
|
21087
|
+
lines.push(text);
|
|
21088
|
+
bytes += text.length * 3;
|
|
21089
|
+
if (bytes > 1024) break;
|
|
21090
|
+
}
|
|
21091
|
+
return lines.join("\n").slice(0, 1024);
|
|
21092
|
+
}
|
|
21093
|
+
function parseMarkdownToBlocks(md2) {
|
|
21094
|
+
const lines = md2.split("\n");
|
|
21095
|
+
const blocks = [];
|
|
21096
|
+
let i = 0;
|
|
21097
|
+
while (i < lines.length) {
|
|
21098
|
+
const line = lines[i];
|
|
21099
|
+
if (!line.trim()) {
|
|
21100
|
+
i++;
|
|
21101
|
+
continue;
|
|
21102
|
+
}
|
|
21103
|
+
const fenceMatch = line.match(/^(`{3,}|~{3,})(.*)$/);
|
|
21104
|
+
if (fenceMatch) {
|
|
21105
|
+
const fence = fenceMatch[1];
|
|
21106
|
+
const lang = fenceMatch[2].trim();
|
|
21107
|
+
const codeLines = [];
|
|
21108
|
+
i++;
|
|
21109
|
+
while (i < lines.length && !lines[i].startsWith(fence)) {
|
|
21110
|
+
codeLines.push(lines[i]);
|
|
21111
|
+
i++;
|
|
21112
|
+
}
|
|
21113
|
+
if (i < lines.length) i++;
|
|
21114
|
+
blocks.push({ type: "code_block", text: codeLines.join("\n"), lang });
|
|
21115
|
+
continue;
|
|
21116
|
+
}
|
|
21117
|
+
if (/^(\*{3,}|-{3,}|_{3,})\s*$/.test(line.trim())) {
|
|
20244
21118
|
blocks.push({ type: "hr" });
|
|
20245
21119
|
i++;
|
|
20246
21120
|
continue;
|
|
@@ -20251,6 +21125,20 @@ function parseMarkdownToBlocks(md2) {
|
|
|
20251
21125
|
i++;
|
|
20252
21126
|
continue;
|
|
20253
21127
|
}
|
|
21128
|
+
if (/^<table[\s>]/i.test(line.trimStart())) {
|
|
21129
|
+
const htmlLines = [];
|
|
21130
|
+
let depth = 0;
|
|
21131
|
+
while (i < lines.length) {
|
|
21132
|
+
const l = lines[i];
|
|
21133
|
+
htmlLines.push(l);
|
|
21134
|
+
depth += (l.match(/<table[\s>]/gi) ?? []).length;
|
|
21135
|
+
depth -= (l.match(/<\/table>/gi) ?? []).length;
|
|
21136
|
+
i++;
|
|
21137
|
+
if (depth <= 0) break;
|
|
21138
|
+
}
|
|
21139
|
+
blocks.push({ type: "html_table", text: htmlLines.join("\n") });
|
|
21140
|
+
continue;
|
|
21141
|
+
}
|
|
20254
21142
|
if (line.trimStart().startsWith("|")) {
|
|
20255
21143
|
const tableRows = [];
|
|
20256
21144
|
while (i < lines.length && lines[i].trimStart().startsWith("|")) {
|
|
@@ -20332,18 +21220,19 @@ function spanToCharPrId(span) {
|
|
|
20332
21220
|
function escapeXml(text) {
|
|
20333
21221
|
return text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
20334
21222
|
}
|
|
20335
|
-
function generateRuns(text, defaultCharPr = CHAR_NORMAL) {
|
|
21223
|
+
function generateRuns(text, defaultCharPr = CHAR_NORMAL, mapCharId) {
|
|
20336
21224
|
const spans = parseInlineMarkdown(text);
|
|
20337
21225
|
return spans.map((span) => {
|
|
20338
|
-
|
|
21226
|
+
let charId = span.code || span.bold || span.italic ? spanToCharPrId(span) : defaultCharPr;
|
|
21227
|
+
if (mapCharId) charId = mapCharId(charId);
|
|
20339
21228
|
return `<hp:run charPrIDRef="${charId}"><hp:t>${escapeXml(span.text)}</hp:t></hp:run>`;
|
|
20340
21229
|
}).join("");
|
|
20341
21230
|
}
|
|
20342
|
-
function generateParagraph(text, paraPrId = PARA_NORMAL, charPrId = CHAR_NORMAL) {
|
|
21231
|
+
function generateParagraph(text, paraPrId = PARA_NORMAL, charPrId = CHAR_NORMAL, mapCharId) {
|
|
20343
21232
|
if (paraPrId === PARA_CODE) {
|
|
20344
21233
|
return `<hp:p paraPrIDRef="${paraPrId}" styleIDRef="0"><hp:run charPrIDRef="${CHAR_CODE}"><hp:t>${escapeXml(text)}</hp:t></hp:run></hp:p>`;
|
|
20345
21234
|
}
|
|
20346
|
-
const runs = generateRuns(text, charPrId);
|
|
21235
|
+
const runs = generateRuns(text, charPrId, mapCharId);
|
|
20347
21236
|
return `<hp:p paraPrIDRef="${paraPrId}" styleIDRef="0">${runs}</hp:p>`;
|
|
20348
21237
|
}
|
|
20349
21238
|
function headingParaPrId(level) {
|
|
@@ -20383,7 +21272,7 @@ function charPr(id, height, bold, italic, fontId = 0, textColor = DEFAULT_TEXT_C
|
|
|
20383
21272
|
const boldAttr = bold ? ` bold="1"` : "";
|
|
20384
21273
|
const italicAttr = italic ? ` italic="1"` : "";
|
|
20385
21274
|
const effFont = bold ? 2 : fontId;
|
|
20386
|
-
return ` <hh:charPr id="${id}" height="${height}" textColor="${textColor}" shadeColor="none" useFontSpace="0" useKerning="0" symMark="NONE" borderFillIDRef="
|
|
21275
|
+
return ` <hh:charPr id="${id}" height="${height}" textColor="${textColor}" shadeColor="none" useFontSpace="0" useKerning="0" symMark="NONE" borderFillIDRef="1"${boldAttr}${italicAttr}>
|
|
20387
21276
|
<hh:fontRef hangul="${effFont}" latin="${effFont}" hanja="${effFont}" japanese="${effFont}" other="${effFont}" symbol="${effFont}" user="${effFont}"/>
|
|
20388
21277
|
<hh:ratio hangul="${ratioPct}" latin="${ratioPct}" hanja="${ratioPct}" japanese="100" other="100" symbol="100" user="100"/>
|
|
20389
21278
|
<hh:spacing hangul="0" latin="0" hanja="0" japanese="0" other="0" symbol="0" user="0"/>
|
|
@@ -20402,13 +21291,58 @@ function paraPr(id, opts = {}) {
|
|
|
20402
21291
|
<hh:autoSpacing eAsianEng="0" eAsianNum="0"/>
|
|
20403
21292
|
<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>
|
|
20404
21293
|
<hh:lineSpacing type="PERCENT" value="${lineSpacing}"/>
|
|
20405
|
-
<hh:border borderFillIDRef="
|
|
21294
|
+
<hh:border borderFillIDRef="1" offsetLeft="0" offsetRight="0" offsetTop="0" offsetBottom="0" connect="0" ignoreMargin="0"/>
|
|
20406
21295
|
</hh:paraPr>`;
|
|
20407
21296
|
}
|
|
20408
21297
|
var GONGMUN_LIST_BASE = 8;
|
|
20409
21298
|
var GONGMUN_LIST_LEVELS = 8;
|
|
20410
21299
|
var GONGMUN_CENTER = GONGMUN_LIST_BASE + GONGMUN_LIST_LEVELS;
|
|
20411
|
-
|
|
21300
|
+
var CHAR_VARIANT_BASE = 11;
|
|
21301
|
+
var GONGMUN_BODY_RATIO = 95;
|
|
21302
|
+
function plainRenderText(text) {
|
|
21303
|
+
return parseInlineMarkdown(text).map((s) => s.text).join("");
|
|
21304
|
+
}
|
|
21305
|
+
function computeGongmunFitPlan(blocks, gongmun, gongmunList) {
|
|
21306
|
+
const minRatio = gongmun.autoFitMinRatio;
|
|
21307
|
+
if (minRatio === null || minRatio >= GONGMUN_BODY_RATIO) return null;
|
|
21308
|
+
const pageW = 59528 - mmToHwpunit(gongmun.margins.left) - mmToHwpunit(gongmun.margins.right);
|
|
21309
|
+
const ratioByBlock = /* @__PURE__ */ new Map();
|
|
21310
|
+
const variants = [];
|
|
21311
|
+
for (let i = 0; i < blocks.length; i++) {
|
|
21312
|
+
const block = blocks[i];
|
|
21313
|
+
let text;
|
|
21314
|
+
let firstW;
|
|
21315
|
+
let contW;
|
|
21316
|
+
if (block.type === "list_item" && gongmunList.has(i)) {
|
|
21317
|
+
const { marker, depth } = gongmunList.get(i);
|
|
21318
|
+
const content = plainRenderText(block.text || "");
|
|
21319
|
+
text = marker ? `${marker} ${content}` : content;
|
|
21320
|
+
const { left, indent } = levelIndent(depth, gongmun.bodyHeight, gongmun.numbering);
|
|
21321
|
+
firstW = pageW - left - Math.max(indent, 0);
|
|
21322
|
+
contW = pageW - left - Math.max(-indent, 0);
|
|
21323
|
+
} else if (block.type === "paragraph") {
|
|
21324
|
+
const raw = (block.text || "").trim();
|
|
21325
|
+
if (/^<center>[\s\S]*<\/center>$/i.test(raw)) continue;
|
|
21326
|
+
text = plainRenderText(raw);
|
|
21327
|
+
firstW = contW = pageW;
|
|
21328
|
+
} else {
|
|
21329
|
+
continue;
|
|
21330
|
+
}
|
|
21331
|
+
if (!text) continue;
|
|
21332
|
+
const r = fitRatioForFewerLines(text, firstW, contW, gongmun.bodyHeight, GONGMUN_BODY_RATIO, minRatio);
|
|
21333
|
+
if (r === null) continue;
|
|
21334
|
+
ratioByBlock.set(i, r);
|
|
21335
|
+
if (!variants.includes(r)) variants.push(r);
|
|
21336
|
+
}
|
|
21337
|
+
return ratioByBlock.size > 0 ? { ratioByBlock, variants } : null;
|
|
21338
|
+
}
|
|
21339
|
+
function variantMapper(fit, blockIdx) {
|
|
21340
|
+
const r = fit.ratioByBlock.get(blockIdx);
|
|
21341
|
+
if (r === void 0) return void 0;
|
|
21342
|
+
const vi = fit.variants.indexOf(r);
|
|
21343
|
+
return (id) => id >= 0 && id <= 3 ? CHAR_VARIANT_BASE + vi * 4 + id : id;
|
|
21344
|
+
}
|
|
21345
|
+
function buildCharProperties(theme, gongmun, ratioVariants = []) {
|
|
20412
21346
|
let body = 1e3, code = 900, h1 = 1800, h2 = 1400, h3 = 1200, h4 = 1100;
|
|
20413
21347
|
if (gongmun) {
|
|
20414
21348
|
body = gongmun.bodyHeight;
|
|
@@ -20418,7 +21352,7 @@ function buildCharProperties(theme, gongmun) {
|
|
|
20418
21352
|
h3 = body;
|
|
20419
21353
|
h4 = Math.max(body - 100, 1300);
|
|
20420
21354
|
}
|
|
20421
|
-
const bodyRatio = gongmun ?
|
|
21355
|
+
const bodyRatio = gongmun ? GONGMUN_BODY_RATIO : 100;
|
|
20422
21356
|
const rows = [
|
|
20423
21357
|
charPr(0, body, false, false, 0, theme.body, bodyRatio),
|
|
20424
21358
|
charPr(1, body, true, false, 0, theme.body, bodyRatio),
|
|
@@ -20432,6 +21366,14 @@ function buildCharProperties(theme, gongmun) {
|
|
|
20432
21366
|
charPr(CHAR_TABLE_HEADER, body, theme.tableHeaderBold, false, 0, theme.tableHeader),
|
|
20433
21367
|
charPr(CHAR_QUOTE, body, false, true, 0, theme.quote)
|
|
20434
21368
|
];
|
|
21369
|
+
for (const r of ratioVariants) {
|
|
21370
|
+
rows.push(
|
|
21371
|
+
charPr(rows.length, body, false, false, 0, theme.body, r),
|
|
21372
|
+
charPr(rows.length + 1, body, true, false, 0, theme.body, r),
|
|
21373
|
+
charPr(rows.length + 2, body, false, true, 0, theme.body, r),
|
|
21374
|
+
charPr(rows.length + 3, body, true, true, 0, theme.body, r)
|
|
21375
|
+
);
|
|
21376
|
+
}
|
|
20435
21377
|
return `<hh:charProperties itemCnt="${rows.length}">
|
|
20436
21378
|
${rows.join("\n")}
|
|
20437
21379
|
</hh:charProperties>`;
|
|
@@ -20474,9 +21416,9 @@ ${base2.join("\n")}
|
|
|
20474
21416
|
${base.join("\n")}
|
|
20475
21417
|
</hh:paraProperties>`;
|
|
20476
21418
|
}
|
|
20477
|
-
function generateHeaderXml(theme, gongmun) {
|
|
21419
|
+
function generateHeaderXml(theme, gongmun, ratioVariants = []) {
|
|
20478
21420
|
const bodyFace = gongmun?.bodyFont === "gothic" ? "\uB9D1\uC740 \uACE0\uB515" : "\uD568\uCD08\uB86C\uBC14\uD0D5";
|
|
20479
|
-
const charPropsXml = buildCharProperties(theme, gongmun);
|
|
21421
|
+
const charPropsXml = buildCharProperties(theme, gongmun, ratioVariants);
|
|
20480
21422
|
const paraPropsXml = buildParaProperties(gongmun);
|
|
20481
21423
|
return `<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
|
|
20482
21424
|
<hh:head xmlns:hh="${NS_HEAD}" xmlns:hp="${NS_PARA}" xmlns:hc="${NS_CORE}" version="1.4" secCnt="1">
|
|
@@ -20532,25 +21474,21 @@ function generateHeaderXml(theme, gongmun) {
|
|
|
20532
21474
|
</hh:fontface>
|
|
20533
21475
|
</hh:fontfaces>
|
|
20534
21476
|
<hh:borderFills itemCnt="2">
|
|
20535
|
-
<hh:borderFill id="
|
|
21477
|
+
<hh:borderFill id="1" threeD="0" shadow="0" centerLine="NONE" breakCellSeparateLine="0">
|
|
20536
21478
|
<hh:slash type="NONE" Crooked="0" isCounter="0"/>
|
|
20537
21479
|
<hh:backSlash type="NONE" Crooked="0" isCounter="0"/>
|
|
20538
21480
|
<hh:leftBorder type="NONE" width="0.1 mm" color="#000000"/>
|
|
20539
21481
|
<hh:rightBorder type="NONE" width="0.1 mm" color="#000000"/>
|
|
20540
21482
|
<hh:topBorder type="NONE" width="0.1 mm" color="#000000"/>
|
|
20541
21483
|
<hh:bottomBorder type="NONE" width="0.1 mm" color="#000000"/>
|
|
20542
|
-
<hh:diagonal type="NONE" width="0.1 mm" color="#000000"/>
|
|
20543
|
-
<hh:fillInfo/>
|
|
20544
21484
|
</hh:borderFill>
|
|
20545
|
-
<hh:borderFill id="
|
|
21485
|
+
<hh:borderFill id="2" threeD="0" shadow="0" centerLine="NONE" breakCellSeparateLine="0">
|
|
20546
21486
|
<hh:slash type="NONE" Crooked="0" isCounter="0"/>
|
|
20547
21487
|
<hh:backSlash type="NONE" Crooked="0" isCounter="0"/>
|
|
20548
21488
|
<hh:leftBorder type="SOLID" width="0.12 mm" color="#000000"/>
|
|
20549
21489
|
<hh:rightBorder type="SOLID" width="0.12 mm" color="#000000"/>
|
|
20550
21490
|
<hh:topBorder type="SOLID" width="0.12 mm" color="#000000"/>
|
|
20551
21491
|
<hh:bottomBorder type="SOLID" width="0.12 mm" color="#000000"/>
|
|
20552
|
-
<hh:diagonal type="NONE" width="0.1 mm" color="#000000"/>
|
|
20553
|
-
<hh:fillInfo/>
|
|
20554
21492
|
</hh:borderFill>
|
|
20555
21493
|
</hh:borderFills>
|
|
20556
21494
|
${charPropsXml}
|
|
@@ -20597,14 +21535,74 @@ function generateTable(rows, theme) {
|
|
|
20597
21535
|
const tdElements = cells.map((cell, colIdx) => {
|
|
20598
21536
|
const runs = generateRuns(cell, headerCharPr);
|
|
20599
21537
|
const p = `<hp:p paraPrIDRef="0" styleIDRef="0">${runs}</hp:p>`;
|
|
20600
|
-
return `<hp:tc name="" header="${isHeaderRow ? 1 : 0}" hasMargin="0" protect="0" editable="1" dirty="0" borderFillIDRef="
|
|
21538
|
+
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>`;
|
|
20601
21539
|
}).join("");
|
|
20602
21540
|
return `<hp:tr>${tdElements}</hp:tr>`;
|
|
20603
21541
|
}).join("");
|
|
20604
21542
|
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;
|
|
20605
|
-
const tbl = `<hp:tbl id="${tblId}" zOrder="0" numberingType="TABLE" pageBreak="CELL" repeatHeader="0" rowCnt="${rowCnt}" colCnt="${colCnt}" cellSpacing="0" borderFillIDRef="
|
|
21543
|
+
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>`;
|
|
20606
21544
|
return `<hp:p paraPrIDRef="0" styleIDRef="0"><hp:run charPrIDRef="0">${tbl}</hp:run></hp:p>`;
|
|
20607
21545
|
}
|
|
21546
|
+
function layoutHtmlRows(rows) {
|
|
21547
|
+
const occupied = /* @__PURE__ */ new Set();
|
|
21548
|
+
const placed = [];
|
|
21549
|
+
let colCnt = 0;
|
|
21550
|
+
for (let r = 0; r < rows.length; r++) {
|
|
21551
|
+
let c = 0;
|
|
21552
|
+
for (const cell of rows[r].cells) {
|
|
21553
|
+
while (occupied.has(`${r},${c}`)) c++;
|
|
21554
|
+
const colSpan = Math.max(1, cell.colSpan);
|
|
21555
|
+
const rowSpan = Math.max(1, cell.rowSpan);
|
|
21556
|
+
placed.push({ r, c, colSpan, rowSpan, inner: cell.inner, isHeader: rows[r].tag === "th" });
|
|
21557
|
+
for (let dr = 0; dr < rowSpan; dr++) {
|
|
21558
|
+
for (let dc = 0; dc < colSpan; dc++) occupied.add(`${r + dr},${c + dc}`);
|
|
21559
|
+
}
|
|
21560
|
+
c += colSpan;
|
|
21561
|
+
colCnt = Math.max(colCnt, c);
|
|
21562
|
+
}
|
|
21563
|
+
}
|
|
21564
|
+
return { placed, rowCnt: rows.length, colCnt };
|
|
21565
|
+
}
|
|
21566
|
+
function unescapeHtml(s) {
|
|
21567
|
+
return s.replace(/</g, "<").replace(/>/g, ">").replace(/"/g, '"').replace(/'/g, "'").replace(/&/g, "&");
|
|
21568
|
+
}
|
|
21569
|
+
function generateHtmlTableXml(rawHtml, theme, totalWidth = 44e3) {
|
|
21570
|
+
const rows = parseHtmlTable(rawHtml);
|
|
21571
|
+
if (!rows || rows.length === 0) return null;
|
|
21572
|
+
const { placed, rowCnt, colCnt } = layoutHtmlRows(rows);
|
|
21573
|
+
if (rowCnt === 0 || colCnt === 0) return null;
|
|
21574
|
+
const colW = Math.floor(totalWidth / colCnt);
|
|
21575
|
+
const cellH = 1500;
|
|
21576
|
+
const tblW = colW * colCnt;
|
|
21577
|
+
const tblId = nextTableId();
|
|
21578
|
+
const useHeaderStyle = theme.tableHeader !== theme.body || theme.tableHeaderBold;
|
|
21579
|
+
const tcXmls = placed.map((cell) => {
|
|
21580
|
+
const headerCharPr = cell.isHeader && useHeaderStyle ? CHAR_TABLE_HEADER : CHAR_NORMAL;
|
|
21581
|
+
const { lines } = htmlCellInnerToLines(cell.inner);
|
|
21582
|
+
const paras = lines.map(
|
|
21583
|
+
(line) => `<hp:p paraPrIDRef="0" styleIDRef="0"><hp:run charPrIDRef="${headerCharPr}"><hp:t>${escapeXml(unescapeHtml(line))}</hp:t></hp:run></hp:p>`
|
|
21584
|
+
);
|
|
21585
|
+
let nestedH = 0;
|
|
21586
|
+
for (const nested of extractTopLevelTables(cell.inner)) {
|
|
21587
|
+
const nestedXml = generateHtmlTableXml(nested, theme, Math.max(colW * cell.colSpan - 1020, 4e3));
|
|
21588
|
+
if (nestedXml) {
|
|
21589
|
+
paras.push(`<hp:p paraPrIDRef="0" styleIDRef="0"><hp:run charPrIDRef="0">${nestedXml}</hp:run></hp:p>`);
|
|
21590
|
+
nestedH += (nested.match(/<tr[\s>]/gi) ?? []).length * cellH + 300;
|
|
21591
|
+
}
|
|
21592
|
+
}
|
|
21593
|
+
if (paras.length === 0) {
|
|
21594
|
+
paras.push(`<hp:p paraPrIDRef="0" styleIDRef="0"><hp:run charPrIDRef="${headerCharPr}"><hp:t></hp:t></hp:run></hp:p>`);
|
|
21595
|
+
}
|
|
21596
|
+
const cellHeight = Math.max(cellH * cell.rowSpan, Math.max(lines.length, 1) * 800 + nestedH);
|
|
21597
|
+
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>`;
|
|
21598
|
+
});
|
|
21599
|
+
const trXmls = [];
|
|
21600
|
+
for (let r = 0; r < rowCnt; r++) {
|
|
21601
|
+
const rowTcs = tcXmls.filter((_, i) => placed[i].r === r);
|
|
21602
|
+
trXmls.push(`<hp:tr>${rowTcs.join("")}</hp:tr>`);
|
|
21603
|
+
}
|
|
21604
|
+
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>`;
|
|
21605
|
+
}
|
|
20608
21606
|
function precomputeGongmunList(blocks, gongmun) {
|
|
20609
21607
|
const result = /* @__PURE__ */ new Map();
|
|
20610
21608
|
let i = 0;
|
|
@@ -20614,9 +21612,22 @@ function precomputeGongmunList(blocks, gongmun) {
|
|
|
20614
21612
|
continue;
|
|
20615
21613
|
}
|
|
20616
21614
|
const run = [];
|
|
20617
|
-
while (i < blocks.length
|
|
20618
|
-
|
|
20619
|
-
|
|
21615
|
+
while (i < blocks.length) {
|
|
21616
|
+
const t = blocks[i].type;
|
|
21617
|
+
if (t === "list_item") {
|
|
21618
|
+
run.push(i);
|
|
21619
|
+
i++;
|
|
21620
|
+
continue;
|
|
21621
|
+
}
|
|
21622
|
+
if (t === "table" || t === "html_table") {
|
|
21623
|
+
let j = i + 1;
|
|
21624
|
+
while (j < blocks.length && (blocks[j].type === "table" || blocks[j].type === "html_table")) j++;
|
|
21625
|
+
if (j < blocks.length && blocks[j].type === "list_item") {
|
|
21626
|
+
i = j;
|
|
21627
|
+
continue;
|
|
21628
|
+
}
|
|
21629
|
+
}
|
|
21630
|
+
break;
|
|
20620
21631
|
}
|
|
20621
21632
|
const depths = run.map((bi) => Math.min(Math.max(blocks[bi].indent || 0, 0), GONGMUN_LIST_LEVELS - 1));
|
|
20622
21633
|
const suppress = gongmun.numbering === "standard" ? computeSuppression(depths) : depths.map(() => false);
|
|
@@ -20628,12 +21639,11 @@ function precomputeGongmunList(blocks, gongmun) {
|
|
|
20628
21639
|
}
|
|
20629
21640
|
return result;
|
|
20630
21641
|
}
|
|
20631
|
-
function blocksToSectionXml(blocks, theme, gongmun) {
|
|
21642
|
+
function blocksToSectionXml(blocks, theme, gongmun, gongmunList = gongmun ? precomputeGongmunList(blocks, gongmun) : null, fit = null) {
|
|
20632
21643
|
const paraXmls = [];
|
|
20633
21644
|
let isFirst = true;
|
|
20634
21645
|
const orderedCounters = {};
|
|
20635
21646
|
let prevWasOrdered = false;
|
|
20636
|
-
const gongmunList = gongmun ? precomputeGongmunList(blocks, gongmun) : null;
|
|
20637
21647
|
for (let blockIdx = 0; blockIdx < blocks.length; blockIdx++) {
|
|
20638
21648
|
const block = blocks[blockIdx];
|
|
20639
21649
|
let xml = "";
|
|
@@ -20655,7 +21665,7 @@ function blocksToSectionXml(blocks, theme, gongmun) {
|
|
|
20655
21665
|
if (ctr) {
|
|
20656
21666
|
xml = generateParagraph(ctr[1].trim(), GONGMUN_CENTER);
|
|
20657
21667
|
} else {
|
|
20658
|
-
xml = generateParagraph(block.text || "");
|
|
21668
|
+
xml = generateParagraph(block.text || "", PARA_NORMAL, CHAR_NORMAL, fit ? variantMapper(fit, blockIdx) : void 0);
|
|
20659
21669
|
}
|
|
20660
21670
|
break;
|
|
20661
21671
|
}
|
|
@@ -20679,7 +21689,7 @@ function blocksToSectionXml(blocks, theme, gongmun) {
|
|
|
20679
21689
|
const content = block.text || "";
|
|
20680
21690
|
const text = marker2 ? `${marker2} ${content}` : content;
|
|
20681
21691
|
const listCharPr = gongmun.numbering === "report" && depth === 0 ? CHAR_BOLD : CHAR_NORMAL;
|
|
20682
|
-
xml = generateParagraph(text, GONGMUN_LIST_BASE + depth, listCharPr);
|
|
21692
|
+
xml = generateParagraph(text, GONGMUN_LIST_BASE + depth, listCharPr, fit ? variantMapper(fit, blockIdx) : void 0);
|
|
20683
21693
|
break;
|
|
20684
21694
|
}
|
|
20685
21695
|
const indent = block.indent || 0;
|
|
@@ -20705,16 +21715,31 @@ function blocksToSectionXml(blocks, theme, gongmun) {
|
|
|
20705
21715
|
case "hr":
|
|
20706
21716
|
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>`;
|
|
20707
21717
|
break;
|
|
20708
|
-
case "table":
|
|
20709
|
-
if (block.rows) {
|
|
21718
|
+
case "table":
|
|
21719
|
+
if (block.rows) {
|
|
21720
|
+
if (isFirst) {
|
|
21721
|
+
const secRun = `<hp:run charPrIDRef="0">${generateSecPr(gongmun)}<hp:t></hp:t></hp:run>`;
|
|
21722
|
+
paraXmls.push(`<hp:p paraPrIDRef="0" styleIDRef="0">${secRun}</hp:p>`);
|
|
21723
|
+
isFirst = false;
|
|
21724
|
+
}
|
|
21725
|
+
xml = generateTable(block.rows, theme);
|
|
21726
|
+
}
|
|
21727
|
+
break;
|
|
21728
|
+
case "html_table": {
|
|
21729
|
+
const tbl = generateHtmlTableXml(block.text || "", theme);
|
|
21730
|
+
if (tbl) {
|
|
20710
21731
|
if (isFirst) {
|
|
20711
21732
|
const secRun = `<hp:run charPrIDRef="0">${generateSecPr(gongmun)}<hp:t></hp:t></hp:run>`;
|
|
20712
21733
|
paraXmls.push(`<hp:p paraPrIDRef="0" styleIDRef="0">${secRun}</hp:p>`);
|
|
20713
21734
|
isFirst = false;
|
|
20714
21735
|
}
|
|
20715
|
-
xml =
|
|
21736
|
+
xml = `<hp:p paraPrIDRef="0" styleIDRef="0"><hp:run charPrIDRef="0">${tbl}</hp:run></hp:p>`;
|
|
21737
|
+
} else {
|
|
21738
|
+
const plain = (block.text || "").replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
|
|
21739
|
+
xml = plain ? generateParagraph(plain) : "";
|
|
20716
21740
|
}
|
|
20717
21741
|
break;
|
|
21742
|
+
}
|
|
20718
21743
|
}
|
|
20719
21744
|
if (!xml) continue;
|
|
20720
21745
|
if (isFirst && block.type !== "table") {
|
|
@@ -20735,49 +21760,6 @@ function blocksToSectionXml(blocks, theme, gongmun) {
|
|
|
20735
21760
|
</hs:sec>`;
|
|
20736
21761
|
}
|
|
20737
21762
|
|
|
20738
|
-
// src/diff/text-diff.ts
|
|
20739
|
-
function similarity(a, b) {
|
|
20740
|
-
if (a === b) return 1;
|
|
20741
|
-
if (!a || !b) return 0;
|
|
20742
|
-
const maxLen = Math.max(a.length, b.length);
|
|
20743
|
-
if (maxLen === 0) return 1;
|
|
20744
|
-
return 1 - levenshtein(a, b) / maxLen;
|
|
20745
|
-
}
|
|
20746
|
-
function normalizedSimilarity(a, b) {
|
|
20747
|
-
return similarity(normalize(a), normalize(b));
|
|
20748
|
-
}
|
|
20749
|
-
function normalize(s) {
|
|
20750
|
-
return s.replace(/\s+/g, " ").trim();
|
|
20751
|
-
}
|
|
20752
|
-
var MAX_LEVENSHTEIN_LEN = 1e4;
|
|
20753
|
-
function levenshtein(a, b) {
|
|
20754
|
-
if (a.length + b.length > MAX_LEVENSHTEIN_LEN) {
|
|
20755
|
-
const sampleLen = Math.min(500, a.length, b.length);
|
|
20756
|
-
let diffs = 0;
|
|
20757
|
-
for (let i = 0; i < sampleLen; i++) if (a[i] !== b[i]) diffs++;
|
|
20758
|
-
const sampleRate = sampleLen > 0 ? diffs / sampleLen : 1;
|
|
20759
|
-
return Math.abs(a.length - b.length) + Math.round(Math.min(a.length, b.length) * sampleRate);
|
|
20760
|
-
}
|
|
20761
|
-
if (a.length > b.length) [a, b] = [b, a];
|
|
20762
|
-
const m = a.length;
|
|
20763
|
-
const n = b.length;
|
|
20764
|
-
let prev = Array.from({ length: m + 1 }, (_, i) => i);
|
|
20765
|
-
let curr = new Array(m + 1);
|
|
20766
|
-
for (let j = 1; j <= n; j++) {
|
|
20767
|
-
curr[0] = j;
|
|
20768
|
-
for (let i = 1; i <= m; i++) {
|
|
20769
|
-
if (a[i - 1] === b[j - 1]) {
|
|
20770
|
-
curr[i] = prev[i - 1];
|
|
20771
|
-
} else {
|
|
20772
|
-
curr[i] = 1 + Math.min(prev[i - 1], prev[i], curr[i - 1]);
|
|
20773
|
-
}
|
|
20774
|
-
}
|
|
20775
|
-
;
|
|
20776
|
-
[prev, curr] = [curr, prev];
|
|
20777
|
-
}
|
|
20778
|
-
return prev[m];
|
|
20779
|
-
}
|
|
20780
|
-
|
|
20781
21763
|
// src/diff/compare.ts
|
|
20782
21764
|
var SIMILARITY_THRESHOLD = 0.4;
|
|
20783
21765
|
async function compare(bufferA, bufferB, options) {
|
|
@@ -20907,308 +21889,239 @@ function diffTableCells(a, b) {
|
|
|
20907
21889
|
else type = "modified";
|
|
20908
21890
|
row.push({ type, before: cellA, after: cellB });
|
|
20909
21891
|
}
|
|
20910
|
-
result.push(row);
|
|
20911
|
-
}
|
|
20912
|
-
return result;
|
|
20913
|
-
}
|
|
20914
|
-
|
|
20915
|
-
// src/roundtrip/patcher.ts
|
|
20916
|
-
import JSZip7 from "jszip";
|
|
20917
|
-
|
|
20918
|
-
// src/roundtrip/markdown-units.ts
|
|
20919
|
-
function splitMarkdownUnits(md2) {
|
|
20920
|
-
const lines = md2.split("\n");
|
|
20921
|
-
const units = [];
|
|
20922
|
-
let i = 0;
|
|
20923
|
-
while (i < lines.length) {
|
|
20924
|
-
const line = lines[i];
|
|
20925
|
-
if (!line.trim()) {
|
|
20926
|
-
i++;
|
|
20927
|
-
continue;
|
|
20928
|
-
}
|
|
20929
|
-
if (line.trim().startsWith("<table>")) {
|
|
20930
|
-
const collected2 = [];
|
|
20931
|
-
let depth = 0;
|
|
20932
|
-
while (i < lines.length) {
|
|
20933
|
-
const l = lines[i];
|
|
20934
|
-
collected2.push(l);
|
|
20935
|
-
depth += (l.match(/<table>/g) || []).length;
|
|
20936
|
-
depth -= (l.match(/<\/table>/g) || []).length;
|
|
20937
|
-
i++;
|
|
20938
|
-
if (depth <= 0) break;
|
|
20939
|
-
}
|
|
20940
|
-
units.push({ kind: "html-table", raw: collected2.join("\n"), lines: collected2 });
|
|
20941
|
-
continue;
|
|
20942
|
-
}
|
|
20943
|
-
if (line.trimStart().startsWith("|")) {
|
|
20944
|
-
const collected2 = [];
|
|
20945
|
-
while (i < lines.length && lines[i].trimStart().startsWith("|")) {
|
|
20946
|
-
collected2.push(lines[i]);
|
|
20947
|
-
i++;
|
|
20948
|
-
}
|
|
20949
|
-
units.push({ kind: "gfm-table", raw: collected2.join("\n"), lines: collected2 });
|
|
20950
|
-
continue;
|
|
20951
|
-
}
|
|
20952
|
-
if (/^-{3,}\s*$/.test(line.trim())) {
|
|
20953
|
-
units.push({ kind: "separator", raw: line.trim(), lines: [line.trim()] });
|
|
20954
|
-
i++;
|
|
20955
|
-
continue;
|
|
20956
|
-
}
|
|
20957
|
-
if (/^!\[image\]\([^)]*\)\s*$/.test(line.trim())) {
|
|
20958
|
-
units.push({ kind: "image", raw: line.trim(), lines: [line.trim()] });
|
|
20959
|
-
i++;
|
|
20960
|
-
continue;
|
|
20961
|
-
}
|
|
20962
|
-
const collected = [];
|
|
20963
|
-
while (i < lines.length && lines[i].trim() && !lines[i].trimStart().startsWith("|") && !lines[i].trim().startsWith("<table>")) {
|
|
20964
|
-
collected.push(lines[i].trim());
|
|
20965
|
-
i++;
|
|
20966
|
-
}
|
|
20967
|
-
units.push({ kind: "text", raw: collected.join("\n"), lines: collected });
|
|
20968
|
-
}
|
|
20969
|
-
return units;
|
|
20970
|
-
}
|
|
20971
|
-
function escapeGfm(text) {
|
|
20972
|
-
return text.replace(/~/g, "\\~");
|
|
20973
|
-
}
|
|
20974
|
-
var HWP_SHAPE_ALT_TEXT_RE = /(?:모서리가 둥근 |둥근 )?(?:사각형|직사각형|정사각형|원|타원|삼각형|이등변 삼각형|직각 삼각형|선|직선|곡선|화살표|굵은 화살표|이중 화살표|오각형|육각형|팔각형|별|[4-8]점별|십자|십자형|구름|구름형|마름모|도넛|평행사변형|사다리꼴|부채꼴|호|반원|물결|번개|하트|빗금|블록 화살표|수식|표|그림|개체|그리기\s?개체|묶음\s?개체|글상자|수식\s?개체|OLE\s?개체)\s?입니다\.?/g;
|
|
20975
|
-
function sanitizeText(text) {
|
|
20976
|
-
let result = mapPuaText(text).replace(/[\u{F0000}-\u{FFFFD}]/gu, "").replace(HWP_SHAPE_ALT_TEXT_RE, "").replace(/ +/g, " ").trim();
|
|
20977
|
-
if (result.length <= 30 && result.includes(" ")) {
|
|
20978
|
-
const tokens = result.split(" ");
|
|
20979
|
-
const koreanSingleCharCount = tokens.filter((t) => t.length === 1 && /[가-ㄱ-ㆎ]/.test(t)).length;
|
|
20980
|
-
if (tokens.length >= 3 && koreanSingleCharCount / tokens.length >= 0.7) {
|
|
20981
|
-
result = tokens.join("");
|
|
20982
|
-
}
|
|
20983
|
-
}
|
|
20984
|
-
return result;
|
|
20985
|
-
}
|
|
20986
|
-
function normForMatch(text) {
|
|
20987
|
-
return sanitizeText(text).replace(/\s+/g, " ").trim();
|
|
20988
|
-
}
|
|
20989
|
-
function unescapeGfm(text) {
|
|
20990
|
-
return text.replace(/\\~/g, "~");
|
|
20991
|
-
}
|
|
20992
|
-
function summarize(text) {
|
|
20993
|
-
const t = text.replace(/\s+/g, " ").trim();
|
|
20994
|
-
return t.length > 80 ? t.slice(0, 77) + "..." : t;
|
|
20995
|
-
}
|
|
20996
|
-
function replicateGfmTable(table) {
|
|
20997
|
-
const { cells, rows: numRows, cols: numCols } = table;
|
|
20998
|
-
if (numRows === 0 || numCols === 0) return null;
|
|
20999
|
-
if (numRows === 1 && numCols === 1) return null;
|
|
21000
|
-
if (numCols === 1) return null;
|
|
21001
|
-
const display = Array.from({ length: numRows }, (_, r) => Array.from({ length: numCols }, (_2, c) => ({ text: "", gridR: r, gridC: c })));
|
|
21002
|
-
const skip = /* @__PURE__ */ new Set();
|
|
21003
|
-
for (let r = 0; r < numRows; r++) {
|
|
21004
|
-
for (let c = 0; c < numCols; c++) {
|
|
21005
|
-
if (skip.has(`${r},${c}`)) continue;
|
|
21006
|
-
const cell = cells[r]?.[c];
|
|
21007
|
-
if (!cell) continue;
|
|
21008
|
-
display[r][c] = {
|
|
21009
|
-
text: escapeGfm(sanitizeText(cell.text)).replace(/\|/g, "\\|").replace(/\n/g, "<br>"),
|
|
21010
|
-
gridR: r,
|
|
21011
|
-
gridC: c
|
|
21012
|
-
};
|
|
21013
|
-
for (let dr = 0; dr < cell.rowSpan; dr++) {
|
|
21014
|
-
for (let dc = 0; dc < cell.colSpan; dc++) {
|
|
21015
|
-
if (dr === 0 && dc === 0) continue;
|
|
21016
|
-
if (r + dr < numRows && c + dc < numCols) skip.add(`${r + dr},${c + dc}`);
|
|
21017
|
-
}
|
|
21018
|
-
}
|
|
21019
|
-
c += cell.colSpan - 1;
|
|
21020
|
-
}
|
|
21021
|
-
}
|
|
21022
|
-
const uniqueRows = [];
|
|
21023
|
-
let pendingLabelRow = null;
|
|
21024
|
-
for (let r = 0; r < display.length; r++) {
|
|
21025
|
-
const row = display[r];
|
|
21026
|
-
if (row.every((cell) => cell.text === "")) continue;
|
|
21027
|
-
const nonEmptyCols = row.filter((cell) => cell.text !== "");
|
|
21028
|
-
const hasSkipInRow = row.some((_, c) => skip.has(`${r},${c}`));
|
|
21029
|
-
if (!hasSkipInRow && nonEmptyCols.length === 1 && row[0].text !== "" && row.slice(1).every((c) => c.text === "")) {
|
|
21030
|
-
if (pendingLabelRow) uniqueRows.push(pendingLabelRow);
|
|
21031
|
-
pendingLabelRow = row;
|
|
21032
|
-
continue;
|
|
21033
|
-
}
|
|
21034
|
-
if (pendingLabelRow) {
|
|
21035
|
-
if (row[0].text === "") row[0] = pendingLabelRow[0];
|
|
21036
|
-
else uniqueRows.push(pendingLabelRow);
|
|
21037
|
-
pendingLabelRow = null;
|
|
21038
|
-
}
|
|
21039
|
-
uniqueRows.push(row);
|
|
21040
|
-
}
|
|
21041
|
-
if (pendingLabelRow) uniqueRows.push(pendingLabelRow);
|
|
21042
|
-
return uniqueRows.length > 0 ? uniqueRows : null;
|
|
21043
|
-
}
|
|
21044
|
-
function parseGfmTable(lines) {
|
|
21045
|
-
const rows = [];
|
|
21046
|
-
for (const line of lines) {
|
|
21047
|
-
const trimmed = line.trim();
|
|
21048
|
-
if (!trimmed.startsWith("|")) continue;
|
|
21049
|
-
const cells = trimmed.split(/(?<!\\)\|/).slice(1, -1).map((c) => c.trim());
|
|
21050
|
-
if (cells.length === 0) continue;
|
|
21051
|
-
if (cells.every((c) => /^:?-{3,}:?$/.test(c))) continue;
|
|
21052
|
-
rows.push(cells);
|
|
21053
|
-
}
|
|
21054
|
-
return rows;
|
|
21055
|
-
}
|
|
21056
|
-
function unescapeGfmCell(text) {
|
|
21057
|
-
return text.replace(/<br\s*\/?>/gi, "\n").replace(/\\\|/g, "|").replace(/\\~/g, "~");
|
|
21058
|
-
}
|
|
21059
|
-
function replicateCellInnerHtml(cell) {
|
|
21060
|
-
if (cell.blocks?.length) {
|
|
21061
|
-
return cell.blocks.map((b) => {
|
|
21062
|
-
if (b.type === "table" && b.table) {
|
|
21063
|
-
const cap = b.table.caption ? sanitizeText(b.table.caption) : "";
|
|
21064
|
-
return (cap ? cap + "<br>" : "") + replicateTableToHtml(b.table);
|
|
21065
|
-
}
|
|
21066
|
-
if (b.type === "image" && b.text) return `<img src="${b.text}" alt="image">`;
|
|
21067
|
-
const t = sanitizeText(b.text ?? "");
|
|
21068
|
-
return t ? t.replace(/\n/g, "<br>") : "";
|
|
21069
|
-
}).filter(Boolean).join("<br>");
|
|
21070
|
-
}
|
|
21071
|
-
return sanitizeText(cell.text).replace(/\n/g, "<br>");
|
|
21072
|
-
}
|
|
21073
|
-
function replicateTableToHtml(table) {
|
|
21074
|
-
const rows = replicateHtmlTable(table);
|
|
21075
|
-
const lines = ["<table>"];
|
|
21076
|
-
for (let r = 0; r < rows.length; r++) {
|
|
21077
|
-
const tag = rows[r].tag;
|
|
21078
|
-
const rowHtml = rows[r].cells.map((cell) => {
|
|
21079
|
-
const attrs = [];
|
|
21080
|
-
if (cell.colSpan > 1) attrs.push(`colspan="${cell.colSpan}"`);
|
|
21081
|
-
if (cell.rowSpan > 1) attrs.push(`rowspan="${cell.rowSpan}"`);
|
|
21082
|
-
const attrStr = attrs.length ? " " + attrs.join(" ") : "";
|
|
21083
|
-
return `<${tag}${attrStr}>${cell.inner}</${tag}>`;
|
|
21084
|
-
});
|
|
21085
|
-
if (rowHtml.length) lines.push(`<tr>${rowHtml.join("")}</tr>`);
|
|
21086
|
-
}
|
|
21087
|
-
lines.push("</table>");
|
|
21088
|
-
return lines.join("\n");
|
|
21089
|
-
}
|
|
21090
|
-
function replicateHtmlTable(table) {
|
|
21091
|
-
const { cells, rows: numRows, cols: numCols } = table;
|
|
21092
|
-
const skip = /* @__PURE__ */ new Set();
|
|
21093
|
-
const result = [];
|
|
21094
|
-
for (let r = 0; r < numRows; r++) {
|
|
21095
|
-
const tag = r === 0 ? "th" : "td";
|
|
21096
|
-
const rowCells = [];
|
|
21097
|
-
for (let c = 0; c < numCols; c++) {
|
|
21098
|
-
if (skip.has(`${r},${c}`)) continue;
|
|
21099
|
-
const cell = cells[r]?.[c];
|
|
21100
|
-
if (!cell) continue;
|
|
21101
|
-
for (let dr = 0; dr < cell.rowSpan; dr++) {
|
|
21102
|
-
for (let dc = 0; dc < cell.colSpan; dc++) {
|
|
21103
|
-
if (dr === 0 && dc === 0) continue;
|
|
21104
|
-
if (r + dr < numRows && c + dc < numCols) skip.add(`${r + dr},${c + dc}`);
|
|
21105
|
-
}
|
|
21106
|
-
}
|
|
21107
|
-
rowCells.push({
|
|
21108
|
-
inner: replicateCellInnerHtml(cell),
|
|
21109
|
-
colSpan: cell.colSpan,
|
|
21110
|
-
rowSpan: cell.rowSpan,
|
|
21111
|
-
gridR: r,
|
|
21112
|
-
gridC: c
|
|
21113
|
-
});
|
|
21114
|
-
}
|
|
21115
|
-
if (rowCells.length) result.push({ tag, cells: rowCells });
|
|
21892
|
+
result.push(row);
|
|
21116
21893
|
}
|
|
21117
21894
|
return result;
|
|
21118
21895
|
}
|
|
21119
|
-
|
|
21120
|
-
|
|
21121
|
-
|
|
21122
|
-
|
|
21123
|
-
|
|
21124
|
-
|
|
21125
|
-
|
|
21126
|
-
|
|
21127
|
-
|
|
21128
|
-
|
|
21129
|
-
|
|
21130
|
-
|
|
21131
|
-
|
|
21132
|
-
|
|
21133
|
-
|
|
21134
|
-
|
|
21896
|
+
|
|
21897
|
+
// src/roundtrip/patcher.ts
|
|
21898
|
+
import JSZip7 from "jszip";
|
|
21899
|
+
|
|
21900
|
+
// src/roundtrip/table-rows.ts
|
|
21901
|
+
var ROW_OBJECT_RE = /<(?:[A-Za-z0-9_]+:)?(?:tbl|pic|equation|ole|container|shape|drawingObject|drawText|video|chart|fieldBegin|fieldEnd|ctrl)\b/;
|
|
21902
|
+
var TAG_AT_RE = /<[A-Za-z0-9_:]+(?:"[^"]*"|'[^']*'|[^>"'])*>/y;
|
|
21903
|
+
function patchTableRows(input) {
|
|
21904
|
+
const { table, scanTable, ctx, skip, origKeys, editedKeys } = input;
|
|
21905
|
+
const xml = ctx.scans[scanTable.sectionIndex]?.xml;
|
|
21906
|
+
if (!xml) return skip("\uC139\uC158 XML \uB9E4\uD551 \uC2E4\uD328");
|
|
21907
|
+
const numRows = table.rows;
|
|
21908
|
+
if (origKeys.length !== numRows) return skip("\uD45C \uD589 \uC88C\uD45C \uBD88\uC77C\uCE58 \u2014 \uD589 \uCD94\uAC00/\uC0AD\uC81C \uBBF8\uC9C0\uC6D0");
|
|
21909
|
+
if (scanTable.rows.length !== numRows || scanTable.rowRanges.length !== numRows) {
|
|
21910
|
+
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");
|
|
21911
|
+
}
|
|
21912
|
+
for (let r = 0; r < numRows; r++) {
|
|
21913
|
+
if (scanTable.rows[r].some((c) => c.rowAddr !== r)) {
|
|
21914
|
+
return skip("\uD589 \uC8FC\uC18C \uBE44\uC5F0\uC18D \u2014 \uD589 \uCD94\uAC00/\uC0AD\uC81C \uBBF8\uC9C0\uC6D0");
|
|
21135
21915
|
}
|
|
21136
|
-
|
|
21137
|
-
|
|
21138
|
-
|
|
21139
|
-
|
|
21140
|
-
|
|
21141
|
-
|
|
21916
|
+
}
|
|
21917
|
+
const allCells = scanTable.rows.flat();
|
|
21918
|
+
const explicitAddr = (c) => !!c.addrTagRange && /\browAddr\s*=\s*"/.test(xml.slice(c.addrTagRange.start, c.addrTagRange.end));
|
|
21919
|
+
const explicitCount = allCells.filter(explicitAddr).length;
|
|
21920
|
+
if (explicitCount !== 0 && explicitCount !== allCells.length) {
|
|
21921
|
+
return skip("\uC140 \uC8FC\uC18C(cellAddr) \uD45C\uAE30 \uD63C\uC7AC \u2014 \uD589 \uCD94\uAC00/\uC0AD\uC81C \uBBF8\uC9C0\uC6D0");
|
|
21922
|
+
}
|
|
21923
|
+
const hasExplicitAddr = explicitCount > 0;
|
|
21924
|
+
const pairs = alignUnits(origKeys, editedKeys);
|
|
21925
|
+
const seq = [];
|
|
21926
|
+
let lastOrig = -1;
|
|
21927
|
+
for (const [oi, ei] of pairs) {
|
|
21928
|
+
if (oi !== null && ei !== null) {
|
|
21929
|
+
seq.push({ kind: "keep", oi, ei });
|
|
21930
|
+
lastOrig = oi;
|
|
21931
|
+
} else if (oi !== null) {
|
|
21932
|
+
seq.push({ kind: "del", oi });
|
|
21933
|
+
lastOrig = oi;
|
|
21934
|
+
} else if (ei !== null) seq.push({ kind: "ins", ei, insertAt: lastOrig + 1 });
|
|
21935
|
+
}
|
|
21936
|
+
const dels = seq.filter((e) => e.kind === "del");
|
|
21937
|
+
const inss = seq.filter((e) => e.kind === "ins");
|
|
21938
|
+
const keeps = seq.filter((e) => e.kind === "keep");
|
|
21939
|
+
if (dels.length === 0 && inss.length === 0) {
|
|
21940
|
+
return skip("\uD45C \uD589 \uC815\uB82C \uC2E4\uD328 \u2014 \uD589 \uCD94\uAC00/\uC0AD\uC81C \uBBF8\uC9C0\uC6D0");
|
|
21941
|
+
}
|
|
21942
|
+
if (keeps.length + inss.length === 0) return skip("\uBAA8\uB4E0 \uD589 \uC0AD\uC81C\uB294 \uBBF8\uC9C0\uC6D0");
|
|
21943
|
+
if (keeps.length === 0) return skip("\uD589 \uC804\uBA74 \uAD50\uCCB4 \u2014 \uC11C\uC2DD \uAE30\uC900 \uD589\uC774 \uC5C6\uC5B4 \uBBF8\uC9C0\uC6D0");
|
|
21944
|
+
const spans = allCells.filter((c) => (c.rowSpan ?? 1) > 1);
|
|
21945
|
+
for (const d of dels) {
|
|
21946
|
+
if (spans.some((s) => s.rowAddr <= d.oi && s.rowAddr + s.rowSpan > d.oi)) {
|
|
21947
|
+
return skip(`\uD45C ${d.oi + 1}\uD589 \uC0AD\uC81C\uAC00 \uC138\uB85C \uBCD1\uD569\uACFC \uACB9\uCE68 \u2014 \uBBF8\uC9C0\uC6D0`);
|
|
21948
|
+
}
|
|
21949
|
+
}
|
|
21950
|
+
for (const ins of inss) {
|
|
21951
|
+
const p = ins.insertAt;
|
|
21952
|
+
if (spans.some((s) => s.rowAddr < p && s.rowAddr + s.rowSpan > p)) {
|
|
21953
|
+
return skip("\uD589 \uC0BD\uC785 \uC704\uCE58\uAC00 \uC138\uB85C \uBCD1\uD569 \uB0B4\uBD80 \u2014 \uBBF8\uC9C0\uC6D0");
|
|
21954
|
+
}
|
|
21955
|
+
}
|
|
21956
|
+
for (const d of dels) {
|
|
21957
|
+
const rr = scanTable.rowRanges[d.oi];
|
|
21958
|
+
if (ROW_OBJECT_RE.test(xml.slice(rr.start, rr.end))) {
|
|
21959
|
+
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`);
|
|
21960
|
+
}
|
|
21961
|
+
}
|
|
21962
|
+
const keptSet = new Set(keeps.map((k) => k.oi));
|
|
21963
|
+
const insertPlans = [];
|
|
21964
|
+
for (const ins of inss) {
|
|
21965
|
+
const p = ins.insertAt;
|
|
21966
|
+
const kept = [...keptSet].sort((a, b) => a - b);
|
|
21967
|
+
let template = -1;
|
|
21968
|
+
for (const k of kept) {
|
|
21969
|
+
if (k < p) template = k;
|
|
21970
|
+
}
|
|
21971
|
+
if (template <= 0 && p >= 1) {
|
|
21972
|
+
const following = kept.find((k) => k >= 1);
|
|
21973
|
+
if (following !== void 0) template = following;
|
|
21974
|
+
}
|
|
21975
|
+
if (template < 0) template = kept[0];
|
|
21976
|
+
const cells = input.editedCells(ins.ei);
|
|
21977
|
+
if (!cells) return skip("\uC0BD\uC785 \uD589\uC5D0 \uC774\uBBF8\uC9C0/\uC911\uCCA9\uD45C \uD3EC\uD568 \u2014 \uD589 \uCD94\uAC00 \uBBF8\uC9C0\uC6D0");
|
|
21978
|
+
for (const cell of cells) {
|
|
21979
|
+
const unstable = cell.lines.find((l) => sanitizeText(l) !== l);
|
|
21980
|
+
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");
|
|
21981
|
+
}
|
|
21982
|
+
const tmplCells = scanTable.rows[template];
|
|
21983
|
+
const rr = scanTable.rowRanges[template];
|
|
21984
|
+
if (ROW_OBJECT_RE.test(xml.slice(rr.start, rr.end))) {
|
|
21985
|
+
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");
|
|
21986
|
+
}
|
|
21987
|
+
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");
|
|
21988
|
+
const tmplWidth = tmplCells.reduce((s, c) => s + c.colSpan, 0);
|
|
21989
|
+
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");
|
|
21990
|
+
if (cells.length !== tmplCells.length) {
|
|
21991
|
+
return skip(`\uC0BD\uC785 \uD589 \uC140 \uC218(${cells.length}) \u2260 \uAE30\uC900 \uD589 \uC140 \uC218(${tmplCells.length}) \u2014 \uBBF8\uC9C0\uC6D0`);
|
|
21992
|
+
}
|
|
21993
|
+
for (let i = 0; i < cells.length; i++) {
|
|
21994
|
+
if (cells[i].rowSpan !== 1 || cells[i].colSpan !== tmplCells[i].colSpan) {
|
|
21995
|
+
return skip("\uC0BD\uC785 \uD589 \uBCD1\uD569 \uAD6C\uC870\uAC00 \uAE30\uC900 \uD589\uACFC \uB2E4\uB984 \u2014 \uBBF8\uC9C0\uC6D0");
|
|
21142
21996
|
}
|
|
21143
|
-
}
|
|
21144
|
-
|
|
21145
|
-
|
|
21146
|
-
|
|
21147
|
-
|
|
21148
|
-
|
|
21149
|
-
|
|
21150
|
-
|
|
21151
|
-
|
|
21152
|
-
|
|
21997
|
+
}
|
|
21998
|
+
insertPlans.push({ entry: ins, template, cells });
|
|
21999
|
+
}
|
|
22000
|
+
const net = inss.length - dels.length;
|
|
22001
|
+
TAG_AT_RE.lastIndex = scanTable.start;
|
|
22002
|
+
const tblOpen = TAG_AT_RE.exec(xml);
|
|
22003
|
+
if (!tblOpen || tblOpen.index !== scanTable.start) return skip("\uD45C \uC5EC\uB294 \uD0DC\uADF8 \uD574\uC11D \uC2E4\uD328");
|
|
22004
|
+
const rowCntM = tblOpen[0].match(/\browCnt\s*=\s*"(\d+)"/);
|
|
22005
|
+
if (!rowCntM || rowCntM.index === void 0) return skip("\uD45C rowCnt \uC18D\uC131 \uC5C6\uC74C \u2014 \uD589 \uCD94\uAC00/\uC0AD\uC81C \uBBF8\uC9C0\uC6D0");
|
|
22006
|
+
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");
|
|
22007
|
+
const finalIndex = /* @__PURE__ */ new Map();
|
|
22008
|
+
{
|
|
22009
|
+
let fi = 0;
|
|
22010
|
+
for (const e of seq) {
|
|
22011
|
+
if (e.kind !== "del") finalIndex.set(e, fi++);
|
|
22012
|
+
}
|
|
22013
|
+
}
|
|
22014
|
+
const splices = [];
|
|
22015
|
+
let applied = 0;
|
|
22016
|
+
for (const d of dels) {
|
|
22017
|
+
const rr = scanTable.rowRanges[d.oi];
|
|
22018
|
+
splices.push({ start: rr.start, end: rr.end, replacement: "" });
|
|
22019
|
+
applied++;
|
|
22020
|
+
}
|
|
22021
|
+
const fragmentsByAnchor = /* @__PURE__ */ new Map();
|
|
22022
|
+
let heightDelta = 0;
|
|
22023
|
+
for (const plan of insertPlans) {
|
|
22024
|
+
const finalRow = finalIndex.get(plan.entry);
|
|
22025
|
+
const fragment = buildRowFragment(xml, scanTable, plan.template, plan.cells, finalRow, hasExplicitAddr, ctx);
|
|
22026
|
+
if (fragment === null) return skip("\uD589 \uBCF5\uC81C \uC2E4\uD328 (\uC140 \uBB38\uB2E8 \uAD6C\uC870 \uBBF8\uC9C0\uC6D0)");
|
|
22027
|
+
const p = plan.entry.insertAt;
|
|
22028
|
+
const anchor = p === 0 ? scanTable.rowRanges[0].start : scanTable.rowRanges[p - 1].end;
|
|
22029
|
+
let list = fragmentsByAnchor.get(anchor);
|
|
22030
|
+
if (!list) fragmentsByAnchor.set(anchor, list = []);
|
|
22031
|
+
list.push(fragment);
|
|
22032
|
+
heightDelta += rowHeightOf(fragment);
|
|
22033
|
+
applied++;
|
|
22034
|
+
}
|
|
22035
|
+
for (const [anchor, fragments] of fragmentsByAnchor) {
|
|
22036
|
+
splices.push({ start: anchor, end: anchor, replacement: fragments.join("") });
|
|
22037
|
+
}
|
|
22038
|
+
for (const d of dels) {
|
|
22039
|
+
const rr = scanTable.rowRanges[d.oi];
|
|
22040
|
+
heightDelta -= rowHeightOf(xml.slice(rr.start, rr.end));
|
|
22041
|
+
}
|
|
22042
|
+
if (hasExplicitAddr) {
|
|
22043
|
+
for (const k of keeps) {
|
|
22044
|
+
const fi = finalIndex.get(k);
|
|
22045
|
+
if (fi === k.oi) continue;
|
|
22046
|
+
for (const cell of scanTable.rows[k.oi]) {
|
|
22047
|
+
const sp = rowAddrRewrite(xml, cell, fi);
|
|
22048
|
+
if (sp) splices.push(sp);
|
|
21153
22049
|
}
|
|
21154
22050
|
}
|
|
21155
22051
|
}
|
|
21156
|
-
|
|
21157
|
-
|
|
21158
|
-
}
|
|
21159
|
-
function htmlCellInnerToLines(inner) {
|
|
21160
|
-
let hadNonText = false;
|
|
21161
|
-
let work = inner;
|
|
21162
|
-
if (/<table[\s>]/i.test(work)) {
|
|
21163
|
-
hadNonText = true;
|
|
21164
|
-
work = removeNestedTables(work);
|
|
22052
|
+
{
|
|
22053
|
+
const valStart = scanTable.start + rowCntM.index + rowCntM[0].indexOf('"') + 1;
|
|
22054
|
+
splices.push({ start: valStart, end: valStart + rowCntM[1].length, replacement: String(numRows + net) });
|
|
21165
22055
|
}
|
|
21166
|
-
if (
|
|
21167
|
-
|
|
21168
|
-
|
|
22056
|
+
if (heightDelta !== 0) {
|
|
22057
|
+
const sp = tableSzHeightSplice(xml, scanTable, tblOpen[0].length, heightDelta);
|
|
22058
|
+
if (sp) splices.push(sp);
|
|
21169
22059
|
}
|
|
21170
|
-
|
|
21171
|
-
|
|
22060
|
+
ctx.sectionSplices[scanTable.sectionIndex].push(...splices);
|
|
22061
|
+
for (const k of keeps) {
|
|
22062
|
+
if (origKeys[k.oi] !== editedKeys[k.ei]) applied += input.patchMatched(k.oi, k.ei);
|
|
22063
|
+
}
|
|
22064
|
+
return applied;
|
|
21172
22065
|
}
|
|
21173
|
-
|
|
21174
|
-
|
|
21175
|
-
|
|
21176
|
-
|
|
21177
|
-
const
|
|
21178
|
-
|
|
21179
|
-
|
|
21180
|
-
|
|
21181
|
-
|
|
21182
|
-
|
|
21183
|
-
|
|
21184
|
-
|
|
21185
|
-
|
|
21186
|
-
|
|
21187
|
-
|
|
21188
|
-
}
|
|
21189
|
-
|
|
22066
|
+
var FRAG_OPEN = "<hp:tbl>";
|
|
22067
|
+
var FRAG_CLOSE = "</hp:tbl>";
|
|
22068
|
+
function buildRowFragment(xml, scanTable, template, cells, finalRow, hasExplicitAddr, ctx) {
|
|
22069
|
+
const rr = scanTable.rowRanges[template];
|
|
22070
|
+
const wrapped = FRAG_OPEN + xml.slice(rr.start, rr.end) + FRAG_CLOSE;
|
|
22071
|
+
const scan = scanSectionXml(wrapped, 0);
|
|
22072
|
+
const row = scan.tables[0]?.rows[0];
|
|
22073
|
+
if (!row || row.length !== cells.length) return null;
|
|
22074
|
+
const splices = allLinesegRemovalSplices(wrapped);
|
|
22075
|
+
for (let i = 0; i < cells.length; i++) {
|
|
22076
|
+
const paras = row[i].paragraphs;
|
|
22077
|
+
let lines = cells[i].lines;
|
|
22078
|
+
if (lines.length > 0 && paras.length === 0) return null;
|
|
22079
|
+
if (lines.length > paras.length) {
|
|
22080
|
+
lines = [...lines.slice(0, paras.length - 1), lines.slice(paras.length - 1).join(" ")];
|
|
22081
|
+
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 });
|
|
22082
|
+
}
|
|
22083
|
+
for (let p = 0; p < paras.length; p++) {
|
|
22084
|
+
const sp = buildParagraphSplices(paras[p], lines[p] ?? "", wrapped);
|
|
22085
|
+
if (sp === null) return null;
|
|
22086
|
+
splices.push(...sp);
|
|
22087
|
+
}
|
|
22088
|
+
if (hasExplicitAddr) {
|
|
22089
|
+
const sp = rowAddrRewrite(wrapped, row[i], finalRow);
|
|
22090
|
+
if (sp) splices.push(sp);
|
|
21190
22091
|
}
|
|
21191
22092
|
}
|
|
21192
|
-
|
|
22093
|
+
const patched = applySplices(wrapped, splices);
|
|
22094
|
+
return patched.slice(FRAG_OPEN.length, patched.length - FRAG_CLOSE.length);
|
|
21193
22095
|
}
|
|
21194
|
-
function
|
|
21195
|
-
|
|
21196
|
-
|
|
21197
|
-
const
|
|
21198
|
-
|
|
21199
|
-
|
|
21200
|
-
|
|
21201
|
-
|
|
21202
|
-
|
|
21203
|
-
|
|
21204
|
-
|
|
21205
|
-
|
|
21206
|
-
|
|
21207
|
-
|
|
21208
|
-
}
|
|
22096
|
+
function rowAddrRewrite(xml, cell, newRow) {
|
|
22097
|
+
if (!cell.addrTagRange) return null;
|
|
22098
|
+
const tag = xml.slice(cell.addrTagRange.start, cell.addrTagRange.end);
|
|
22099
|
+
const m = tag.match(/\browAddr\s*=\s*"(\d+)"/);
|
|
22100
|
+
if (!m || m.index === void 0) return null;
|
|
22101
|
+
if (parseInt(m[1], 10) === newRow) return null;
|
|
22102
|
+
const valStart = cell.addrTagRange.start + m.index + m[0].indexOf('"') + 1;
|
|
22103
|
+
return { start: valStart, end: valStart + m[1].length, replacement: String(newRow) };
|
|
22104
|
+
}
|
|
22105
|
+
function rowHeightOf(fragment) {
|
|
22106
|
+
let max = 0;
|
|
22107
|
+
for (const m of fragment.matchAll(/<(?:[A-Za-z0-9_]+:)?cellSz\b(?:"[^"]*"|'[^']*'|[^>"'])*>/g)) {
|
|
22108
|
+
const h = m[0].match(/\bheight\s*=\s*"(\d+)"/);
|
|
22109
|
+
if (h) max = Math.max(max, parseInt(h[1], 10));
|
|
21209
22110
|
}
|
|
21210
|
-
|
|
21211
|
-
|
|
22111
|
+
return max;
|
|
22112
|
+
}
|
|
22113
|
+
function tableSzHeightSplice(xml, scanTable, tblOpenLen, delta) {
|
|
22114
|
+
const from = scanTable.start + tblOpenLen;
|
|
22115
|
+
const to = scanTable.rowRanges[0]?.start ?? from;
|
|
22116
|
+
const slice = xml.slice(from, to);
|
|
22117
|
+
const szM = slice.match(/<(?:[A-Za-z0-9_]+:)?sz\b(?:"[^"]*"|'[^']*'|[^>"'])*>/);
|
|
22118
|
+
if (!szM || szM.index === void 0) return null;
|
|
22119
|
+
const hM = szM[0].match(/\bheight\s*=\s*"(\d+)"/);
|
|
22120
|
+
if (!hM || hM.index === void 0) return null;
|
|
22121
|
+
const oldH = parseInt(hM[1], 10);
|
|
22122
|
+
const newH = Math.max(0, oldH + delta);
|
|
22123
|
+
const valStart = from + szM.index + hM.index + hM[0].indexOf('"') + 1;
|
|
22124
|
+
return { start: valStart, end: valStart + hM[1].length, replacement: String(newH) };
|
|
21212
22125
|
}
|
|
21213
22126
|
|
|
21214
22127
|
// src/roundtrip/table-patch.ts
|
|
@@ -21220,37 +22133,89 @@ function patchGfmTable(table, scanTable, orig, edited, ctx, skip) {
|
|
|
21220
22133
|
if (replica.length !== origRows.length || replica.some((row, r) => row.length !== origRows[r].length || row.some((c, j) => c.text !== origRows[r][j]))) {
|
|
21221
22134
|
return skip("\uD45C \uC88C\uD45C \uC7AC\uD604 \uBD88\uC77C\uCE58 \u2014 \uB9E4\uD551 \uC2E0\uB8B0 \uBD88\uAC00");
|
|
21222
22135
|
}
|
|
21223
|
-
if (editedRows.length !== origRows.length)
|
|
22136
|
+
if (editedRows.length !== origRows.length) {
|
|
22137
|
+
return patchGfmTableRows(table, scanTable, origRows, editedRows, replica, ctx, skip);
|
|
22138
|
+
}
|
|
21224
22139
|
let applied = 0;
|
|
21225
22140
|
for (let r = 0; r < origRows.length; r++) {
|
|
21226
|
-
|
|
21227
|
-
|
|
22141
|
+
applied += patchGfmRowPair(table, scanTable, origRows, editedRows, replica, r, r, ctx, skip);
|
|
22142
|
+
}
|
|
22143
|
+
return applied;
|
|
22144
|
+
}
|
|
22145
|
+
function patchGfmRowPair(table, scanTable, origRows, editedRows, replica, r, er, ctx, skip) {
|
|
22146
|
+
if (editedRows[er].length !== origRows[r].length) {
|
|
22147
|
+
skip(`\uD45C ${r + 1}\uD589 \uC5F4 \uC218 \uBCC0\uACBD\uC740 \uBBF8\uC9C0\uC6D0`);
|
|
22148
|
+
return 0;
|
|
22149
|
+
}
|
|
22150
|
+
let applied = 0;
|
|
22151
|
+
for (let c = 0; c < origRows[r].length; c++) {
|
|
22152
|
+
if (origRows[r][c] === editedRows[er][c]) continue;
|
|
22153
|
+
const { gridR, gridC } = replica[r][c];
|
|
22154
|
+
const origTokens = extractCellTokens(origRows[r][c]);
|
|
22155
|
+
const editedTokens = extractCellTokens(editedRows[er][c]);
|
|
22156
|
+
if (origTokens !== editedTokens) {
|
|
22157
|
+
skip("\uC140 \uB0B4 \uC774\uBBF8\uC9C0 \uBCC0\uACBD\uC740 \uBBF8\uC9C0\uC6D0");
|
|
21228
22158
|
continue;
|
|
21229
22159
|
}
|
|
21230
|
-
|
|
21231
|
-
|
|
21232
|
-
|
|
21233
|
-
|
|
21234
|
-
|
|
21235
|
-
|
|
21236
|
-
|
|
21237
|
-
|
|
21238
|
-
|
|
21239
|
-
|
|
21240
|
-
const origLines = unescapeGfmCell(stripCellTokens(origRows[r][c])).split("\n").map((s) => s.trim()).filter(Boolean);
|
|
21241
|
-
const n = applyCellEdit(table, scanTable, gridR, gridC, newLines, ctx, origRows[r][c], editedRows[r][c], origLines.length);
|
|
21242
|
-
if (n > 0 && origTokens) {
|
|
21243
|
-
ctx.skipped.push({
|
|
21244
|
-
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)",
|
|
21245
|
-
before: summarize(origRows[r][c]),
|
|
21246
|
-
after: summarize(editedRows[r][c])
|
|
21247
|
-
});
|
|
21248
|
-
}
|
|
21249
|
-
applied += n;
|
|
22160
|
+
const newLines = unescapeGfmCell(stripCellTokens(editedRows[er][c])).split("\n").map((s) => s.trim()).filter(Boolean);
|
|
22161
|
+
const origLines = unescapeGfmCell(stripCellTokens(origRows[r][c])).split("\n").map((s) => s.trim()).filter(Boolean);
|
|
22162
|
+
const n = applyCellEdit(table, scanTable, gridR, gridC, newLines, ctx, origRows[r][c], editedRows[er][c], origLines.length);
|
|
22163
|
+
if (n > 0 && origTokens) {
|
|
22164
|
+
ctx.skipped.push({
|
|
22165
|
+
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)",
|
|
22166
|
+
before: summarize(origRows[r][c]),
|
|
22167
|
+
after: summarize(editedRows[er][c]),
|
|
22168
|
+
partial: true
|
|
22169
|
+
});
|
|
21250
22170
|
}
|
|
22171
|
+
applied += n;
|
|
21251
22172
|
}
|
|
21252
22173
|
return applied;
|
|
21253
22174
|
}
|
|
22175
|
+
function patchGfmTableRows(table, scanTable, origRows, editedRows, replica, ctx, skip) {
|
|
22176
|
+
if (replica.length !== table.rows || replica.some((row, r) => row.some((c) => c.gridR !== r))) {
|
|
22177
|
+
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");
|
|
22178
|
+
}
|
|
22179
|
+
if (table.cells.some((row) => row.some((c) => c && (c.colSpan > 1 || c.rowSpan > 1)))) {
|
|
22180
|
+
return skip("\uBCD1\uD569 \uC140 \uD45C \u2014 GFM \uD589 \uCD94\uAC00/\uC0AD\uC81C \uBBF8\uC9C0\uC6D0");
|
|
22181
|
+
}
|
|
22182
|
+
if (!gfmRenderStable(editedRows, table.cols)) {
|
|
22183
|
+
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");
|
|
22184
|
+
}
|
|
22185
|
+
const key = (row) => row.join("\0");
|
|
22186
|
+
return patchTableRows({
|
|
22187
|
+
table,
|
|
22188
|
+
scanTable,
|
|
22189
|
+
ctx,
|
|
22190
|
+
skip,
|
|
22191
|
+
origKeys: origRows.map(key),
|
|
22192
|
+
editedKeys: editedRows.map(key),
|
|
22193
|
+
editedCells: (ei) => {
|
|
22194
|
+
const cells = [];
|
|
22195
|
+
for (const cellMd of editedRows[ei]) {
|
|
22196
|
+
if (extractCellTokens(cellMd)) return null;
|
|
22197
|
+
const lines = unescapeGfmCell(cellMd).split("\n").map((s) => s.trim()).filter(Boolean);
|
|
22198
|
+
cells.push({ lines, colSpan: 1, rowSpan: 1 });
|
|
22199
|
+
}
|
|
22200
|
+
return cells;
|
|
22201
|
+
},
|
|
22202
|
+
patchMatched: (oi, ei) => patchGfmRowPair(table, scanTable, origRows, editedRows, replica, oi, ei, ctx, skip)
|
|
22203
|
+
});
|
|
22204
|
+
}
|
|
22205
|
+
function gfmRenderStable(editedRows, cols) {
|
|
22206
|
+
const sim = {
|
|
22207
|
+
rows: editedRows.length,
|
|
22208
|
+
cols,
|
|
22209
|
+
hasHeader: editedRows.length > 1,
|
|
22210
|
+
cells: editedRows.map((row) => {
|
|
22211
|
+
const padded = row.length < cols ? [...row, ...Array(cols - row.length).fill("")] : row;
|
|
22212
|
+
return padded.map((md2) => ({ text: unescapeGfmCell(md2), colSpan: 1, rowSpan: 1 }));
|
|
22213
|
+
})
|
|
22214
|
+
};
|
|
22215
|
+
const replica = replicateGfmTable(sim);
|
|
22216
|
+
if (!replica || replica.length !== editedRows.length) return false;
|
|
22217
|
+
return replica.every((row, r) => row.length === editedRows[r].length && row.every((c, j) => c.text === editedRows[r][j]));
|
|
22218
|
+
}
|
|
21254
22219
|
function patchHtmlTable(table, scanTable, orig, edited, ctx, skip) {
|
|
21255
22220
|
return patchHtmlTableRaw(table, scanTable, orig.raw, edited.raw, ctx, skip, 0);
|
|
21256
22221
|
}
|
|
@@ -21264,46 +22229,77 @@ function patchHtmlTableRaw(table, scanTable, origRaw, editedRaw, ctx, skip, dept
|
|
|
21264
22229
|
}
|
|
21265
22230
|
const editedRows = parseHtmlTable(editedRaw);
|
|
21266
22231
|
if (!editedRows) return skip("\uD3B8\uC9D1\uB41C HTML \uD45C \uD30C\uC2F1 \uC2E4\uD328");
|
|
21267
|
-
if (editedRows.length !== replica.length)
|
|
22232
|
+
if (editedRows.length !== replica.length) {
|
|
22233
|
+
return patchHtmlTableRows(table, scanTable, replica, editedRows, ctx, skip, depth);
|
|
22234
|
+
}
|
|
21268
22235
|
let applied = 0;
|
|
21269
22236
|
for (let r = 0; r < replica.length; r++) {
|
|
21270
|
-
|
|
21271
|
-
|
|
22237
|
+
applied += patchHtmlRowPair(table, scanTable, replica, editedRows, r, r, ctx, skip, depth);
|
|
22238
|
+
}
|
|
22239
|
+
return applied;
|
|
22240
|
+
}
|
|
22241
|
+
function patchHtmlRowPair(table, scanTable, replica, editedRows, r, er, ctx, skip, depth) {
|
|
22242
|
+
if (editedRows[er].cells.length !== replica[r].cells.length) {
|
|
22243
|
+
skip(`\uD45C ${r + 1}\uD589 \uC140 \uC218 \uBCC0\uACBD\uC740 \uBBF8\uC9C0\uC6D0`);
|
|
22244
|
+
return 0;
|
|
22245
|
+
}
|
|
22246
|
+
let applied = 0;
|
|
22247
|
+
for (let c = 0; c < replica[r].cells.length; c++) {
|
|
22248
|
+
const oc = replica[r].cells[c];
|
|
22249
|
+
const ec = editedRows[er].cells[c];
|
|
22250
|
+
if (oc.colSpan !== ec.colSpan || oc.rowSpan !== ec.rowSpan) {
|
|
22251
|
+
skip(`\uC140 \uBCD1\uD569(colspan/rowspan) \uBCC0\uACBD\uC740 \uBBF8\uC9C0\uC6D0`);
|
|
21272
22252
|
continue;
|
|
21273
22253
|
}
|
|
21274
|
-
|
|
21275
|
-
|
|
21276
|
-
|
|
21277
|
-
|
|
21278
|
-
|
|
22254
|
+
if (oc.inner === ec.inner) continue;
|
|
22255
|
+
const origContent = htmlCellInnerToLines(oc.inner);
|
|
22256
|
+
const editedContent = htmlCellInnerToLines(ec.inner);
|
|
22257
|
+
if (origContent.hadNonText || editedContent.hadNonText) {
|
|
22258
|
+
if (extractImgTags(oc.inner) !== extractImgTags(ec.inner)) {
|
|
22259
|
+
skip("\uC140 \uB0B4 \uC774\uBBF8\uC9C0 \uBCC0\uACBD\uC740 \uBBF8\uC9C0\uC6D0");
|
|
21279
22260
|
continue;
|
|
21280
22261
|
}
|
|
21281
|
-
|
|
21282
|
-
const
|
|
21283
|
-
|
|
21284
|
-
|
|
21285
|
-
|
|
21286
|
-
skip("\uC140 \uB0B4 \uC774\uBBF8\uC9C0 \uBCC0\uACBD\uC740 \uBBF8\uC9C0\uC6D0");
|
|
21287
|
-
continue;
|
|
21288
|
-
}
|
|
21289
|
-
const origTables = extractTopLevelTables(oc.inner);
|
|
21290
|
-
const editedTables = extractTopLevelTables(ec.inner);
|
|
21291
|
-
if (origTables.length !== editedTables.length) {
|
|
21292
|
-
skip("\uC140 \uB0B4 \uC911\uCCA9\uD45C \uCD94\uAC00/\uC0AD\uC81C\uB294 \uBBF8\uC9C0\uC6D0");
|
|
21293
|
-
continue;
|
|
21294
|
-
}
|
|
21295
|
-
if (origTables.join("\n") !== editedTables.join("\n")) {
|
|
21296
|
-
applied += patchNestedTables(table, scanTable, oc, origTables, editedTables, ctx, skip, depth);
|
|
21297
|
-
}
|
|
22262
|
+
const origTables = extractTopLevelTables(oc.inner);
|
|
22263
|
+
const editedTables = extractTopLevelTables(ec.inner);
|
|
22264
|
+
if (origTables.length !== editedTables.length) {
|
|
22265
|
+
skip("\uC140 \uB0B4 \uC911\uCCA9\uD45C \uCD94\uAC00/\uC0AD\uC81C\uB294 \uBBF8\uC9C0\uC6D0");
|
|
22266
|
+
continue;
|
|
21298
22267
|
}
|
|
21299
|
-
if (
|
|
21300
|
-
|
|
21301
|
-
applied += applyCellEdit(table, scanTable, oc.gridR, oc.gridC, newLines, ctx, oc.inner, ec.inner, origContent.lines.length);
|
|
22268
|
+
if (origTables.join("\n") !== editedTables.join("\n")) {
|
|
22269
|
+
applied += patchNestedTables(table, scanTable, oc, origTables, editedTables, ctx, skip, depth);
|
|
21302
22270
|
}
|
|
21303
22271
|
}
|
|
22272
|
+
if (origContent.lines.join("\n") !== editedContent.lines.join("\n")) {
|
|
22273
|
+
const newLines = editedContent.lines.map((l) => unescapeGfm(l));
|
|
22274
|
+
applied += applyCellEdit(table, scanTable, oc.gridR, oc.gridC, newLines, ctx, oc.inner, ec.inner, origContent.lines.length);
|
|
22275
|
+
}
|
|
21304
22276
|
}
|
|
21305
22277
|
return applied;
|
|
21306
22278
|
}
|
|
22279
|
+
function patchHtmlTableRows(table, scanTable, replica, editedRows, ctx, skip, depth) {
|
|
22280
|
+
if (replica.length !== table.rows || replica.some((row, r) => row.cells.some((c) => c.gridR !== r))) {
|
|
22281
|
+
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");
|
|
22282
|
+
}
|
|
22283
|
+
const key = (row) => row.cells.map((c) => `${c.colSpan}x${c.rowSpan}:${c.inner}`).join("\0");
|
|
22284
|
+
return patchTableRows({
|
|
22285
|
+
table,
|
|
22286
|
+
scanTable,
|
|
22287
|
+
ctx,
|
|
22288
|
+
skip,
|
|
22289
|
+
origKeys: replica.map(key),
|
|
22290
|
+
editedKeys: editedRows.map(key),
|
|
22291
|
+
editedCells: (ei) => {
|
|
22292
|
+
const cells = [];
|
|
22293
|
+
for (const cell of editedRows[ei].cells) {
|
|
22294
|
+
const content = htmlCellInnerToLines(cell.inner);
|
|
22295
|
+
if (content.hadNonText) return null;
|
|
22296
|
+
cells.push({ lines: content.lines.map((l) => unescapeGfm(l)), colSpan: cell.colSpan, rowSpan: cell.rowSpan });
|
|
22297
|
+
}
|
|
22298
|
+
return cells;
|
|
22299
|
+
},
|
|
22300
|
+
patchMatched: (oi, ei) => patchHtmlRowPair(table, scanTable, replica, editedRows, oi, ei, ctx, skip, depth)
|
|
22301
|
+
});
|
|
22302
|
+
}
|
|
21307
22303
|
function patchNestedTables(table, scanTable, oc, origTables, editedTables, ctx, skip, depth) {
|
|
21308
22304
|
const irCell = table.cells[oc.gridR]?.[oc.gridC];
|
|
21309
22305
|
const scanCell = scanTable.cellByAnchor.get(`${oc.gridR},${oc.gridC}`);
|
|
@@ -21403,7 +22399,7 @@ function applyCellEdit(table, scanTable, gridR, gridC, newLines, ctx, before, af
|
|
|
21403
22399
|
splices.push(...sp);
|
|
21404
22400
|
sectionIndex = target.sectionIndex;
|
|
21405
22401
|
if (newLines.length > 1) {
|
|
21406
|
-
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) });
|
|
22402
|
+
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 });
|
|
21407
22403
|
}
|
|
21408
22404
|
} else {
|
|
21409
22405
|
const assigned = [];
|
|
@@ -21415,7 +22411,9 @@ function applyCellEdit(table, scanTable, gridR, gridC, newLines, ctx, before, af
|
|
|
21415
22411
|
}
|
|
21416
22412
|
}
|
|
21417
22413
|
if (newLines.length > nonEmpty.length) {
|
|
21418
|
-
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) });
|
|
22414
|
+
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 });
|
|
22415
|
+
} else if (newLines.length < nonEmpty.length && nonEmpty.length > 1) {
|
|
22416
|
+
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 });
|
|
21419
22417
|
}
|
|
21420
22418
|
for (let i = 0; i < nonEmpty.length; i++) {
|
|
21421
22419
|
if (assigned[i] === nonEmpty[i].text || normForMatch(assigned[i]) === normForMatch(nonEmpty[i].text)) continue;
|
|
@@ -21588,7 +22586,8 @@ async function patchHwpx(original, editedMarkdown, options) {
|
|
|
21588
22586
|
try {
|
|
21589
22587
|
for (let i = 0; i < scans.length; i++) {
|
|
21590
22588
|
if (sectionSplices[i].length === 0) continue;
|
|
21591
|
-
sectionSplices[i].
|
|
22589
|
+
const claimed = sectionSplices[i].filter((s) => s.end > s.start);
|
|
22590
|
+
sectionSplices[i].push(...allLinesegRemovalSplices(scans[i].xml).filter((ls) => !claimed.some((c) => ls.start >= c.start && ls.end <= c.end)));
|
|
21592
22591
|
const newXml = applySplices(scans[i].xml, sectionSplices[i]);
|
|
21593
22592
|
replacements.set(sectionPaths[i], encoder.encode(newXml));
|
|
21594
22593
|
}
|
|
@@ -21656,82 +22655,6 @@ function buildTableOrdinals(blocks) {
|
|
|
21656
22655
|
}
|
|
21657
22656
|
return map;
|
|
21658
22657
|
}
|
|
21659
|
-
function alignUnits(a, b) {
|
|
21660
|
-
const m = a.length, n = b.length;
|
|
21661
|
-
if (m * n > 4e6) {
|
|
21662
|
-
const result2 = [];
|
|
21663
|
-
let pre = 0;
|
|
21664
|
-
while (pre < m && pre < n && a[pre] === b[pre]) {
|
|
21665
|
-
result2.push([pre, pre]);
|
|
21666
|
-
pre++;
|
|
21667
|
-
}
|
|
21668
|
-
let suf = 0;
|
|
21669
|
-
while (suf < m - pre && suf < n - pre && a[m - 1 - suf] === b[n - 1 - suf]) suf++;
|
|
21670
|
-
const aMid = m - pre - suf, bMid = n - pre - suf;
|
|
21671
|
-
if (aMid === bMid) {
|
|
21672
|
-
for (let i2 = 0; i2 < aMid; i2++) result2.push([pre + i2, pre + i2]);
|
|
21673
|
-
} else {
|
|
21674
|
-
for (let i2 = 0; i2 < aMid; i2++) result2.push([pre + i2, null]);
|
|
21675
|
-
for (let j2 = 0; j2 < bMid; j2++) result2.push([null, pre + j2]);
|
|
21676
|
-
}
|
|
21677
|
-
for (let s = suf - 1; s >= 0; s--) result2.push([m - 1 - s, n - 1 - s]);
|
|
21678
|
-
return result2;
|
|
21679
|
-
}
|
|
21680
|
-
const dp = Array.from({ length: m + 1 }, () => new Int32Array(n + 1));
|
|
21681
|
-
for (let i2 = 1; i2 <= m; i2++) {
|
|
21682
|
-
for (let j2 = 1; j2 <= n; j2++) {
|
|
21683
|
-
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]);
|
|
21684
|
-
}
|
|
21685
|
-
}
|
|
21686
|
-
const matches = [];
|
|
21687
|
-
let i = m, j = n;
|
|
21688
|
-
while (i > 0 && j > 0) {
|
|
21689
|
-
if (a[i - 1] === b[j - 1] && dp[i][j] === dp[i - 1][j - 1] + 1) {
|
|
21690
|
-
matches.push([i - 1, j - 1]);
|
|
21691
|
-
i--;
|
|
21692
|
-
j--;
|
|
21693
|
-
} else if (dp[i - 1][j] >= dp[i][j - 1]) i--;
|
|
21694
|
-
else j--;
|
|
21695
|
-
}
|
|
21696
|
-
matches.reverse();
|
|
21697
|
-
const result = [];
|
|
21698
|
-
let ai = 0, bi = 0;
|
|
21699
|
-
const flushGap = (aEnd, bEnd) => {
|
|
21700
|
-
if (aEnd - ai === bEnd - bi) {
|
|
21701
|
-
while (ai < aEnd) result.push([ai++, bi++]);
|
|
21702
|
-
return;
|
|
21703
|
-
}
|
|
21704
|
-
while (ai < aEnd && bi < bEnd) {
|
|
21705
|
-
const sim = normalizedSimilarity(a[ai], b[bi]);
|
|
21706
|
-
if (sim >= 0.4) {
|
|
21707
|
-
if (aEnd - ai > bEnd - bi && bestSimInRange(a, ai + 1, ai + (aEnd - ai) - (bEnd - bi), b[bi]) > sim) {
|
|
21708
|
-
result.push([ai++, null]);
|
|
21709
|
-
} else if (bEnd - bi > aEnd - ai && bestSimInRange(b, bi + 1, bi + (bEnd - bi) - (aEnd - ai), a[ai]) > sim) {
|
|
21710
|
-
result.push([null, bi++]);
|
|
21711
|
-
} else {
|
|
21712
|
-
result.push([ai++, bi++]);
|
|
21713
|
-
}
|
|
21714
|
-
} else if (aEnd - ai >= bEnd - bi) result.push([ai++, null]);
|
|
21715
|
-
else result.push([null, bi++]);
|
|
21716
|
-
}
|
|
21717
|
-
while (ai < aEnd) result.push([ai++, null]);
|
|
21718
|
-
while (bi < bEnd) result.push([null, bi++]);
|
|
21719
|
-
};
|
|
21720
|
-
for (const [pi, pj] of matches) {
|
|
21721
|
-
flushGap(pi, pj);
|
|
21722
|
-
result.push([ai++, bi++]);
|
|
21723
|
-
}
|
|
21724
|
-
flushGap(m, n);
|
|
21725
|
-
return result;
|
|
21726
|
-
}
|
|
21727
|
-
function bestSimInRange(arr, from, to, target) {
|
|
21728
|
-
let best = 0;
|
|
21729
|
-
for (let k = from; k <= to && k < arr.length; k++) {
|
|
21730
|
-
const s = normalizedSimilarity(arr[k], target);
|
|
21731
|
-
if (s > best) best = s;
|
|
21732
|
-
}
|
|
21733
|
-
return best;
|
|
21734
|
-
}
|
|
21735
22658
|
function resolveParagraphMappings(blocks, scans) {
|
|
21736
22659
|
const buckets = /* @__PURE__ */ new Map();
|
|
21737
22660
|
for (const scan of scans) {
|
|
@@ -21874,7 +22797,7 @@ function patchParagraphUnit(block, orig, edited, ctx, skip) {
|
|
|
21874
22797
|
const origPrefix = block.text.split(" ", 1)[0];
|
|
21875
22798
|
const sp = newPlain.indexOf(" ");
|
|
21876
22799
|
const newFirst = sp > 0 ? newPlain.slice(0, sp) : newPlain;
|
|
21877
|
-
if (newFirst === origPrefix ||
|
|
22800
|
+
if (newFirst === origPrefix || AUTONUM_PREFIX_RE.test(newFirst)) {
|
|
21878
22801
|
newPlain = sp > 0 ? newPlain.slice(sp + 1) : "";
|
|
21879
22802
|
} else {
|
|
21880
22803
|
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) });
|
|
@@ -22273,21 +23196,24 @@ function readRecordsStrict(stream) {
|
|
|
22273
23196
|
}
|
|
22274
23197
|
return recs;
|
|
22275
23198
|
}
|
|
22276
|
-
function serializeRecords(recs, repl) {
|
|
23199
|
+
function serializeRecords(recs, repl, inserts) {
|
|
22277
23200
|
const parts = [];
|
|
22278
|
-
|
|
22279
|
-
const data = repl?.get(i) ?? recs[i].data;
|
|
23201
|
+
const push = (tagId, level, data) => {
|
|
22280
23202
|
const ext = data.length >= 4095;
|
|
22281
23203
|
const header = Buffer.alloc(ext ? 8 : 4);
|
|
22282
|
-
header.writeUInt32LE((
|
|
23204
|
+
header.writeUInt32LE((tagId & 1023 | (level & 1023) << 10 | (ext ? 4095 : data.length) << 20) >>> 0, 0);
|
|
22283
23205
|
if (ext) header.writeUInt32LE(data.length, 4);
|
|
22284
23206
|
parts.push(header, data);
|
|
23207
|
+
};
|
|
23208
|
+
for (let i = 0; i < recs.length; i++) {
|
|
23209
|
+
for (const ins of inserts?.get(i) ?? []) push(ins.tagId, ins.level, ins.data);
|
|
23210
|
+
push(recs[i].tagId, recs[i].level, repl?.get(i) ?? recs[i].data);
|
|
22285
23211
|
}
|
|
22286
23212
|
return Buffer.concat(parts);
|
|
22287
23213
|
}
|
|
22288
23214
|
function scanSection(stream, sectionIndex, compressed) {
|
|
22289
23215
|
const records = readRecordsStrict(stream);
|
|
22290
|
-
if (!records) return { records: [], safe: false, paras: [], tables: [], compressed, repl: /* @__PURE__ */ new Map() };
|
|
23216
|
+
if (!records) return { records: [], safe: false, paras: [], tables: [], compressed, repl: /* @__PURE__ */ new Map(), inserts: /* @__PURE__ */ new Map() };
|
|
22291
23217
|
const safe = serializeRecords(records).equals(stream);
|
|
22292
23218
|
const parent = new Int32Array(records.length).fill(-1);
|
|
22293
23219
|
const stack = [];
|
|
@@ -22386,7 +23312,7 @@ function scanSection(stream, sectionIndex, compressed) {
|
|
|
22386
23312
|
}
|
|
22387
23313
|
tables.push({ sectionIndex, rows, cols, cells });
|
|
22388
23314
|
}
|
|
22389
|
-
return { records, safe, paras, tables, compressed, repl: /* @__PURE__ */ new Map() };
|
|
23315
|
+
return { records, safe, paras, tables, compressed, repl: /* @__PURE__ */ new Map(), inserts: /* @__PURE__ */ new Map() };
|
|
22390
23316
|
}
|
|
22391
23317
|
async function patchHwp(original, editedMarkdown, options) {
|
|
22392
23318
|
const skipped = [];
|
|
@@ -22454,15 +23380,15 @@ async function patchHwp(original, editedMarkdown, options) {
|
|
|
22454
23380
|
}
|
|
22455
23381
|
}
|
|
22456
23382
|
let data;
|
|
22457
|
-
const dirty = scans.some((s) => s.repl.size > 0);
|
|
23383
|
+
const dirty = scans.some((s) => s.repl.size > 0 || s.inserts.size > 0);
|
|
22458
23384
|
if (!dirty) {
|
|
22459
23385
|
data = new Uint8Array(original);
|
|
22460
23386
|
} else {
|
|
22461
23387
|
try {
|
|
22462
23388
|
let out = originalBuf;
|
|
22463
23389
|
for (let i = 0; i < scans.length; i++) {
|
|
22464
|
-
if (scans[i].repl.size === 0) continue;
|
|
22465
|
-
const newStream = serializeRecords(scans[i].records, scans[i].repl);
|
|
23390
|
+
if (scans[i].repl.size === 0 && scans[i].inserts.size === 0) continue;
|
|
23391
|
+
const newStream = serializeRecords(scans[i].records, scans[i].repl, scans[i].inserts);
|
|
22466
23392
|
const content = compressed ? deflateRawSync2(newStream) : newStream;
|
|
22467
23393
|
out = replaceOleStream(out, sectionPaths[i], content);
|
|
22468
23394
|
}
|
|
@@ -22617,7 +23543,7 @@ function patchParagraph(block, orig, edited, ctx, skip) {
|
|
|
22617
23543
|
const origPrefix = block.text.split(" ", 1)[0];
|
|
22618
23544
|
const sp = newPlain.indexOf(" ");
|
|
22619
23545
|
const newFirst = sp > 0 ? newPlain.slice(0, sp) : newPlain;
|
|
22620
|
-
if (newFirst === origPrefix ||
|
|
23546
|
+
if (newFirst === origPrefix || AUTONUM_PREFIX_RE.test(newFirst)) {
|
|
22621
23547
|
newPlain = sp > 0 ? newPlain.slice(sp + 1) : "";
|
|
22622
23548
|
} else {
|
|
22623
23549
|
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) });
|
|
@@ -22682,9 +23608,6 @@ function patchGfmCells(scanTable, orig, edited, ctx, skip) {
|
|
|
22682
23608
|
}
|
|
22683
23609
|
return applied;
|
|
22684
23610
|
}
|
|
22685
|
-
function extractImgTags5(inner) {
|
|
22686
|
-
return (inner.match(/<img\s(?:"[^"]*"|'[^']*'|[^>"'])*?>/gi) || []).join(" ");
|
|
22687
|
-
}
|
|
22688
23611
|
function patchHtmlCells5(table, scanTable, orig, edited, ctx, skip) {
|
|
22689
23612
|
if (replicateTableToHtml(table) !== orig.raw) return skip("\uD45C \uC88C\uD45C \uC7AC\uD604 \uBD88\uC77C\uCE58 \u2014 \uB9E4\uD551 \uC2E0\uB8B0 \uBD88\uAC00");
|
|
22690
23613
|
const replica = replicateHtmlTable(table);
|
|
@@ -22712,7 +23635,7 @@ function patchHtmlCells5(table, scanTable, orig, edited, ctx, skip) {
|
|
|
22712
23635
|
const origContent = htmlCellInnerToLines(oc.inner);
|
|
22713
23636
|
const editedContent = htmlCellInnerToLines(ec.inner);
|
|
22714
23637
|
if (origContent.hadNonText || editedContent.hadNonText) {
|
|
22715
|
-
if (
|
|
23638
|
+
if (extractImgTags(oc.inner) !== extractImgTags(ec.inner)) {
|
|
22716
23639
|
skip("\uC140 \uB0B4 \uC774\uBBF8\uC9C0 \uBCC0\uACBD\uC740 \uBBF8\uC9C0\uC6D0");
|
|
22717
23640
|
continue;
|
|
22718
23641
|
}
|
|
@@ -22793,22 +23716,25 @@ function applyCellEdit5(table, scanTable, gridR, gridC, newLines, ctx, before, a
|
|
|
22793
23716
|
}
|
|
22794
23717
|
const unstable = newLines.find((l) => sanitizeText(l) !== l);
|
|
22795
23718
|
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");
|
|
22796
|
-
|
|
23719
|
+
const targets = nonEmpty.length > 0 ? nonEmpty : cell.paras;
|
|
23720
|
+
if (targets.length === 0) return skip("\uC140\uC5D0 \uBB38\uB2E8\uC774 \uC5C6\uC74C \u2014 \uBBF8\uC9C0\uC6D0");
|
|
22797
23721
|
const assigned = [];
|
|
22798
|
-
for (let i = 0; i <
|
|
23722
|
+
for (let i = 0; i < targets.length; i++) {
|
|
22799
23723
|
if (i < newLines.length) {
|
|
22800
|
-
assigned.push(i ===
|
|
23724
|
+
assigned.push(i === targets.length - 1 && newLines.length > targets.length ? newLines.slice(i).join(" ") : newLines[i]);
|
|
22801
23725
|
} else {
|
|
22802
23726
|
assigned.push("");
|
|
22803
23727
|
}
|
|
22804
23728
|
}
|
|
22805
|
-
if (newLines.length >
|
|
22806
|
-
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) });
|
|
23729
|
+
if (newLines.length > targets.length) {
|
|
23730
|
+
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 });
|
|
23731
|
+
} else if (newLines.length < nonEmpty.length && nonEmpty.length > 1) {
|
|
23732
|
+
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 });
|
|
22807
23733
|
}
|
|
22808
23734
|
let staged = 0;
|
|
22809
|
-
for (let i = 0; i <
|
|
22810
|
-
if (assigned[i] ===
|
|
22811
|
-
staged += stageParaPatch(ctx.scans[
|
|
23735
|
+
for (let i = 0; i < targets.length; i++) {
|
|
23736
|
+
if (assigned[i] === targets[i].rawText || normForMatch(assigned[i]) === normForMatch(targets[i].rawText)) continue;
|
|
23737
|
+
staged += stageParaPatch(ctx.scans[targets[i].sectionIndex], targets[i], assigned[i], skip);
|
|
22812
23738
|
}
|
|
22813
23739
|
return staged > 0 ? 1 : 0;
|
|
22814
23740
|
}
|
|
@@ -22878,7 +23804,21 @@ function splitParaText(data) {
|
|
|
22878
23804
|
if (firstP < 0) firstP = k;
|
|
22879
23805
|
lastP = k;
|
|
22880
23806
|
}
|
|
22881
|
-
if (firstP < 0)
|
|
23807
|
+
if (firstP < 0) {
|
|
23808
|
+
if (toks.some((t) => t.visible)) return null;
|
|
23809
|
+
let cut = data.length;
|
|
23810
|
+
for (const t of toks) if (data.readUInt16LE(t.start) === 13) {
|
|
23811
|
+
cut = t.start;
|
|
23812
|
+
break;
|
|
23813
|
+
}
|
|
23814
|
+
return {
|
|
23815
|
+
prefix: data.subarray(0, cut),
|
|
23816
|
+
prefixUnits: cut / 2,
|
|
23817
|
+
core: "",
|
|
23818
|
+
suffix: data.subarray(cut),
|
|
23819
|
+
suffixUnits: (data.length - cut) / 2
|
|
23820
|
+
};
|
|
23821
|
+
}
|
|
22882
23822
|
for (let k = firstP; k <= lastP; k++) if (!toks[k].plain) return null;
|
|
22883
23823
|
for (let k = 0; k < firstP; k++) if (toks[k].visible) return null;
|
|
22884
23824
|
for (let k = lastP + 1; k < toks.length; k++) if (toks[k].visible) return null;
|
|
@@ -22911,7 +23851,6 @@ function rebuildCharShape(csData, coreStartUnit) {
|
|
|
22911
23851
|
}
|
|
22912
23852
|
function stageParaPatch(scan, para, newPlain, skip) {
|
|
22913
23853
|
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");
|
|
22914
|
-
if (para.textIdx === -1) return skip("\uBE48 \uBB38\uB2E8 \uD14D\uC2A4\uD2B8 \uCD94\uAC00\uB294 \uBBF8\uC9C0\uC6D0 (v1)");
|
|
22915
23854
|
if (para.textIdx === -2) return skip("\uBCF5\uC218 PARA_TEXT \uB808\uCF54\uB4DC \uBB38\uB2E8 \u2014 \uBBF8\uC9C0\uC6D0 (v1)");
|
|
22916
23855
|
if (para.rangeTagCount > 0) return skip("\uBC94\uC704 \uD0DC\uADF8(\uD615\uAD11\uD39C/\uAD50\uC815\uBD80\uD638) \uBB38\uB2E8 \u2014 \uBBF8\uC9C0\uC6D0 (v1)");
|
|
22917
23856
|
if (para.charShapeIdx < 0 || para.lineSegIdx < 0) return skip("\uBB38\uB2E8 \uB808\uCF54\uB4DC \uAD6C\uC131 \uBE44\uC815\uD615 \u2014 \uBBF8\uC9C0\uC6D0");
|
|
@@ -22919,11 +23858,27 @@ function stageParaPatch(scan, para, newPlain, skip) {
|
|
|
22919
23858
|
if (/[\u0000-\u001f]/.test(newPlain)) return skip("\uC0C8 \uD14D\uC2A4\uD2B8\uC5D0 \uC81C\uC5B4\uBB38\uC790 \uD3EC\uD568 \u2014 \uBBF8\uC9C0\uC6D0");
|
|
22920
23859
|
const records = scan.records;
|
|
22921
23860
|
const headerRec = records[para.headerIdx];
|
|
22922
|
-
const textRec = records[para.textIdx];
|
|
22923
23861
|
const charShapeRec = records[para.charShapeIdx];
|
|
22924
23862
|
if (charShapeRec.data.length < 8) {
|
|
22925
23863
|
return skip("CHAR_SHAPE \uB808\uCF54\uB4DC \uBE44\uC815\uD615 \u2014 \uBBF8\uC9C0\uC6D0");
|
|
22926
23864
|
}
|
|
23865
|
+
if (para.textIdx === -1) {
|
|
23866
|
+
const nCharsLow = para.nCharsRaw & 2147483647;
|
|
23867
|
+
if (nCharsLow > 1) return skip("PARA_TEXT \uC5C6\uB294 \uBB38\uB2E8\uC758 nChars \uBE44\uC815\uD615 \u2014 \uBBF8\uC9C0\uC6D0");
|
|
23868
|
+
const paraEnd = nCharsLow === 1 ? Buffer.from([13, 0]) : Buffer.alloc(0);
|
|
23869
|
+
const at = para.headerIdx + 1;
|
|
23870
|
+
const list = scan.inserts.get(at) ?? [];
|
|
23871
|
+
list.push({ tagId: TAG_PARA_TEXT, level: headerRec.level + 1, data: Buffer.concat([Buffer.from(newPlain, "utf16le"), paraEnd]) });
|
|
23872
|
+
scan.inserts.set(at, list);
|
|
23873
|
+
const newHeader2 = Buffer.from(headerRec.data);
|
|
23874
|
+
newHeader2.writeUInt32LE((para.nCharsRaw & 2147483648 | newPlain.length + nCharsLow) >>> 0, 0);
|
|
23875
|
+
const cs2 = rebuildCharShape(charShapeRec.data, 0);
|
|
23876
|
+
scan.repl.set(para.charShapeIdx, cs2.buf);
|
|
23877
|
+
newHeader2.writeUInt16LE(cs2.count, 12);
|
|
23878
|
+
scan.repl.set(para.headerIdx, newHeader2);
|
|
23879
|
+
return 1;
|
|
23880
|
+
}
|
|
23881
|
+
const textRec = records[para.textIdx];
|
|
22927
23882
|
const seg = splitParaText(textRec.data);
|
|
22928
23883
|
if (!seg) {
|
|
22929
23884
|
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");
|
|
@@ -23178,6 +24133,7 @@ var HwpxSession = class _HwpxSession {
|
|
|
23178
24133
|
try {
|
|
23179
24134
|
for (let s = 0; s < st.scans.length; s++) {
|
|
23180
24135
|
if (sectionSplices[s].length === 0) continue;
|
|
24136
|
+
sectionSplices[s].push(...allLinesegRemovalSplices(st.scans[s].xml));
|
|
23181
24137
|
replacements.set(st.sectionPaths[s], encoder.encode(applySplices(st.scans[s].xml, sectionSplices[s])));
|
|
23182
24138
|
}
|
|
23183
24139
|
} catch (err) {
|
|
@@ -23234,7 +24190,7 @@ var HwpxSession = class _HwpxSession {
|
|
|
23234
24190
|
const origPrefix = block.text.split(" ", 1)[0];
|
|
23235
24191
|
const sp = newPlain.indexOf(" ");
|
|
23236
24192
|
const newFirst = sp > 0 ? newPlain.slice(0, sp) : newPlain;
|
|
23237
|
-
if (newFirst === origPrefix ||
|
|
24193
|
+
if (newFirst === origPrefix || AUTONUM_PREFIX_RE.test(newFirst)) {
|
|
23238
24194
|
newPlain = sp > 0 ? newPlain.slice(sp + 1) : "";
|
|
23239
24195
|
} else {
|
|
23240
24196
|
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) });
|
|
@@ -23528,7 +24484,7 @@ async function parseHwp(buffer, options) {
|
|
|
23528
24484
|
async function parsePdf(buffer, options) {
|
|
23529
24485
|
let parsePdfDocument;
|
|
23530
24486
|
try {
|
|
23531
|
-
const mod = await import("./parser-
|
|
24487
|
+
const mod = await import("./parser-DCK42RMA.js");
|
|
23532
24488
|
parsePdfDocument = mod.parsePdfDocument;
|
|
23533
24489
|
} catch {
|
|
23534
24490
|
return {
|
|
@@ -23620,12 +24576,16 @@ async function fillForm(input, values, outputFormat = "markdown") {
|
|
|
23620
24576
|
export {
|
|
23621
24577
|
HwpxSession,
|
|
23622
24578
|
PRESET_ALIAS,
|
|
24579
|
+
SPACE_EM_FIXED,
|
|
24580
|
+
SPACE_EM_FONT,
|
|
23623
24581
|
VERSION,
|
|
24582
|
+
ValueCursor,
|
|
23624
24583
|
applySplices,
|
|
23625
24584
|
blocksToMarkdown,
|
|
23626
24585
|
blocksToPdf,
|
|
23627
24586
|
buildParagraphSplices,
|
|
23628
24587
|
buildRangeSplices,
|
|
24588
|
+
charWidthEm1000,
|
|
23629
24589
|
compare,
|
|
23630
24590
|
detectFormat,
|
|
23631
24591
|
detectOle2Format,
|
|
@@ -23636,6 +24596,7 @@ export {
|
|
|
23636
24596
|
fillForm,
|
|
23637
24597
|
fillFormFields,
|
|
23638
24598
|
fillHwpx,
|
|
24599
|
+
fitRatioForFewerLines,
|
|
23639
24600
|
inferFieldType,
|
|
23640
24601
|
isHwpxFile,
|
|
23641
24602
|
isLabelCell,
|
|
@@ -23644,6 +24605,7 @@ export {
|
|
|
23644
24605
|
isZipFile,
|
|
23645
24606
|
markdownToHwpx,
|
|
23646
24607
|
markdownToPdf,
|
|
24608
|
+
measureTextWidth,
|
|
23647
24609
|
normalizeGongmunPreset,
|
|
23648
24610
|
openHwpxDocument,
|
|
23649
24611
|
parse,
|
|
@@ -23659,6 +24621,8 @@ export {
|
|
|
23659
24621
|
patchHwpx,
|
|
23660
24622
|
patchHwpxBlocks,
|
|
23661
24623
|
renderHtml,
|
|
23662
|
-
scanSectionXml
|
|
24624
|
+
scanSectionXml,
|
|
24625
|
+
simulateWrap,
|
|
24626
|
+
simulateWrapKeepWord
|
|
23663
24627
|
};
|
|
23664
24628
|
//# sourceMappingURL=index.js.map
|