kordoc 4.12.2 → 4.13.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 +32 -1
- package/dist/{-EZH6NKTE.js → -UZDRUWTX.js} +43 -22
- package/dist/{chunk-FBLMVHDK.js → chunk-27QN27I3.js} +2 -2
- package/dist/{chunk-MUYU4JI5.js → chunk-2B45S7FM.js} +2 -2
- package/dist/{chunk-LD2K4K5V.js → chunk-2RREQZ44.js} +1662 -652
- package/dist/chunk-2RREQZ44.js.map +1 -0
- package/dist/{chunk-OEYECZGV.js → chunk-3ABHPKYA.js} +10 -6
- package/dist/chunk-3ABHPKYA.js.map +1 -0
- package/dist/{chunk-QM2RSJ2S.js → chunk-3MCFJIN2.js} +5 -5
- package/dist/{chunk-JCMBQC5N.js → chunk-5VHLKNCE.js} +2 -2
- package/dist/{chunk-FVBR7SC3.js → chunk-6JXMIX3G.js} +2 -2
- package/dist/chunk-6JXMIX3G.js.map +1 -0
- package/dist/{chunk-5RPYBC2Q.js → chunk-6NLXGGNH.js} +37 -6
- package/dist/chunk-6NLXGGNH.js.map +1 -0
- package/dist/{chunk-2B7OIEJJ.js → chunk-AGBFCFQY.js} +10 -6
- package/dist/chunk-AGBFCFQY.js.map +1 -0
- package/dist/{rasterize-NRWP2C6L.js → chunk-BQLZOQES.js} +22 -6
- package/dist/chunk-BQLZOQES.js.map +1 -0
- package/dist/{chunk-ONNAHSPV.cjs → chunk-CKMK42FV.cjs} +18 -14
- package/dist/chunk-CKMK42FV.cjs.map +1 -0
- package/dist/{chunk-T7GRHAIW.cjs → chunk-COUGOFT3.cjs} +1 -1
- package/dist/chunk-COUGOFT3.cjs.map +1 -0
- package/dist/{chunk-2LO525TL.js → chunk-F3FEIYI7.js} +2 -2
- package/dist/{chunk-Q6ETEJFK.js → chunk-GWFX5ZD7.js} +11 -7
- package/dist/chunk-GWFX5ZD7.js.map +1 -0
- package/dist/{chunk-2HBYWA5X.js → chunk-H5JWPWQP.js} +2 -2
- package/dist/{chunk-N3DLQ2YH.js → chunk-HG5LXXCC.js} +1 -1
- package/dist/chunk-HG5LXXCC.js.map +1 -0
- package/dist/{chunk-KX7IYCCZ.js → chunk-IEJR23OA.js} +3 -3
- package/dist/{chunk-7XUXSDH5.js → chunk-ILDTN72S.js} +10 -6
- package/dist/chunk-ILDTN72S.js.map +1 -0
- package/dist/{chunk-QBCMFNHS.js → chunk-L3CEPNQS.js} +2 -2
- package/dist/{chunk-XXDZ2KTW.js → chunk-LP5RTYFM.js} +2 -2
- package/dist/{chunk-2LKYJ4G3.cjs → chunk-MCD45IHO.cjs} +6 -6
- package/dist/{chunk-2LKYJ4G3.cjs.map → chunk-MCD45IHO.cjs.map} +1 -1
- package/dist/{chunk-V5BUPZ6I.cjs → chunk-OJV7HNGJ.cjs} +13 -9
- package/dist/chunk-OJV7HNGJ.cjs.map +1 -0
- package/dist/{chunk-DBGEUDBY.js → chunk-OSLJ3A6D.js} +2 -2
- package/dist/{chunk-2LPBIW2B.cjs → chunk-S2VKZKUX.cjs} +3 -3
- package/dist/{chunk-2LPBIW2B.cjs.map → chunk-S2VKZKUX.cjs.map} +1 -1
- package/dist/{chunk-IOJX4Q63.js → chunk-T7AFLXGA.js} +786 -19
- package/dist/chunk-T7AFLXGA.js.map +1 -0
- package/dist/{chunk-UL2MDA5C.js → chunk-TA2LEFOF.js} +3 -3
- package/dist/{chunk-62IU4NV3.cjs → chunk-TAYCQE6K.cjs} +2 -2
- package/dist/{chunk-62IU4NV3.cjs.map → chunk-TAYCQE6K.cjs.map} +1 -1
- package/dist/{chunk-ONQHGZTS.js → chunk-XVIJLHCW.js} +1 -1
- package/dist/chunk-XVIJLHCW.js.map +1 -0
- package/dist/chunks-G3GK536Y.js +10 -0
- package/dist/cli.js +187 -38
- package/dist/cli.js.map +1 -1
- package/dist/{formula-6RQX4NMJ.js → formula-3T7HQA4O.js} +4 -4
- package/dist/{formula-NW33S4TA.js → formula-FWVAOSIK.js} +3 -3
- package/dist/{formula-NBEO6CFC.cjs → formula-YALEMZMF.cjs} +9 -9
- package/dist/{formula-NBEO6CFC.cjs.map → formula-YALEMZMF.cjs.map} +1 -1
- package/dist/{image-ocr-SO56VETS.js → image-ocr-2M7K3CE5.js} +8 -8
- package/dist/{image-ocr-V4KY373B.cjs → image-ocr-4EUZIF6M.cjs} +12 -12
- package/dist/{image-ocr-V4KY373B.cjs.map → image-ocr-4EUZIF6M.cjs.map} +1 -1
- package/dist/{image-ocr-CAVU2TNZ.js → image-ocr-X3I2HUK2.js} +6 -6
- package/dist/index.cjs +2349 -576
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +305 -12
- package/dist/index.d.ts +305 -12
- package/dist/index.js +1980 -207
- package/dist/index.js.map +1 -1
- package/dist/mcp.js +109 -35
- package/dist/mcp.js.map +1 -1
- package/dist/{model-bundle-EIJF34DR.js → model-bundle-XXYZ6ELQ.js} +5 -5
- package/dist/{models-6V4CPQXC.js → models-APDSXJW7.js} +5 -5
- package/dist/{parser-OMT25WH2.cjs → parser-4KE2FR5F.cjs} +60 -39
- package/dist/parser-4KE2FR5F.cjs.map +1 -0
- package/dist/{parser-OBHNXEQM.js → parser-T5R4YVZS.js} +32 -11
- package/dist/parser-T5R4YVZS.js.map +1 -0
- package/dist/{parser-GIREMEPD.js → parser-XKSOS6GY.js} +32 -11
- package/dist/parser-XKSOS6GY.js.map +1 -0
- package/dist/pdf-ocr-JZKCEKWX.cjs +14 -0
- package/dist/{pdf-ocr-TFYVK4FI.cjs.map → pdf-ocr-JZKCEKWX.cjs.map} +1 -1
- package/dist/pdf-ocr-X62B7YDV.js +13 -0
- package/dist/pdf-ocr-YDJQEGDS.js +16 -0
- package/dist/{profile-io-PZVMM66V.js → profile-io-Y24GOHWM.js} +3 -3
- package/dist/rasterize-QCOBWHAG.js +13 -0
- package/dist/render-DHFQQCSV.js +38 -0
- package/dist/seal-UTHFQPLO.js +10 -0
- package/dist/seal-UTHFQPLO.js.map +1 -0
- package/dist/{setup-V55AJP6H.js → setup-UWX3KH3G.js} +3 -3
- package/dist/visual-DF2KSPEC.js +19 -0
- package/dist/visual-DF2KSPEC.js.map +1 -0
- package/dist/{watch-7CQSMDDP.js → watch-4ZUTMC3Q.js} +15 -13
- package/dist/{watch-7CQSMDDP.js.map → watch-4ZUTMC3Q.js.map} +1 -1
- package/package.json +1 -1
- package/dist/chunk-2B7OIEJJ.js.map +0 -1
- package/dist/chunk-5RPYBC2Q.js.map +0 -1
- package/dist/chunk-7XUXSDH5.js.map +0 -1
- package/dist/chunk-FVBR7SC3.js.map +0 -1
- package/dist/chunk-IOJX4Q63.js.map +0 -1
- package/dist/chunk-LD2K4K5V.js.map +0 -1
- package/dist/chunk-N3DLQ2YH.js.map +0 -1
- package/dist/chunk-OEYECZGV.js.map +0 -1
- package/dist/chunk-ONNAHSPV.cjs.map +0 -1
- package/dist/chunk-ONQHGZTS.js.map +0 -1
- package/dist/chunk-Q6ETEJFK.js.map +0 -1
- package/dist/chunk-T7GRHAIW.cjs.map +0 -1
- package/dist/chunk-V5BUPZ6I.cjs.map +0 -1
- package/dist/chunks-TT7ZE5QQ.js +0 -10
- package/dist/parser-GIREMEPD.js.map +0 -1
- package/dist/parser-OBHNXEQM.js.map +0 -1
- package/dist/parser-OMT25WH2.cjs.map +0 -1
- package/dist/pdf-ocr-IRXQ5GBM.js +0 -13
- package/dist/pdf-ocr-SL4GCOGS.js +0 -16
- package/dist/pdf-ocr-TFYVK4FI.cjs +0 -14
- package/dist/rasterize-NRWP2C6L.js.map +0 -1
- package/dist/render-V5ZPTZQ7.js +0 -10
- package/dist/seal-LVG2DGAB.js +0 -10
- /package/dist/{-EZH6NKTE.js.map → -UZDRUWTX.js.map} +0 -0
- /package/dist/{chunk-FBLMVHDK.js.map → chunk-27QN27I3.js.map} +0 -0
- /package/dist/{chunk-MUYU4JI5.js.map → chunk-2B45S7FM.js.map} +0 -0
- /package/dist/{chunk-QM2RSJ2S.js.map → chunk-3MCFJIN2.js.map} +0 -0
- /package/dist/{chunk-JCMBQC5N.js.map → chunk-5VHLKNCE.js.map} +0 -0
- /package/dist/{chunk-2LO525TL.js.map → chunk-F3FEIYI7.js.map} +0 -0
- /package/dist/{chunk-2HBYWA5X.js.map → chunk-H5JWPWQP.js.map} +0 -0
- /package/dist/{chunk-KX7IYCCZ.js.map → chunk-IEJR23OA.js.map} +0 -0
- /package/dist/{chunk-QBCMFNHS.js.map → chunk-L3CEPNQS.js.map} +0 -0
- /package/dist/{chunk-XXDZ2KTW.js.map → chunk-LP5RTYFM.js.map} +0 -0
- /package/dist/{chunk-DBGEUDBY.js.map → chunk-OSLJ3A6D.js.map} +0 -0
- /package/dist/{chunk-UL2MDA5C.js.map → chunk-TA2LEFOF.js.map} +0 -0
- /package/dist/{chunks-TT7ZE5QQ.js.map → chunks-G3GK536Y.js.map} +0 -0
- /package/dist/{formula-6RQX4NMJ.js.map → formula-3T7HQA4O.js.map} +0 -0
- /package/dist/{formula-NW33S4TA.js.map → formula-FWVAOSIK.js.map} +0 -0
- /package/dist/{image-ocr-SO56VETS.js.map → image-ocr-2M7K3CE5.js.map} +0 -0
- /package/dist/{image-ocr-CAVU2TNZ.js.map → image-ocr-X3I2HUK2.js.map} +0 -0
- /package/dist/{model-bundle-EIJF34DR.js.map → model-bundle-XXYZ6ELQ.js.map} +0 -0
- /package/dist/{models-6V4CPQXC.js.map → models-APDSXJW7.js.map} +0 -0
- /package/dist/{pdf-ocr-IRXQ5GBM.js.map → pdf-ocr-X62B7YDV.js.map} +0 -0
- /package/dist/{pdf-ocr-SL4GCOGS.js.map → pdf-ocr-YDJQEGDS.js.map} +0 -0
- /package/dist/{profile-io-PZVMM66V.js.map → profile-io-Y24GOHWM.js.map} +0 -0
- /package/dist/{render-V5ZPTZQ7.js.map → rasterize-QCOBWHAG.js.map} +0 -0
- /package/dist/{seal-LVG2DGAB.js.map → render-DHFQQCSV.js.map} +0 -0
- /package/dist/{setup-V55AJP6H.js.map → setup-UWX3KH3G.js.map} +0 -0
package/dist/index.js
CHANGED
|
@@ -6,9 +6,10 @@ import {
|
|
|
6
6
|
convertTableToText,
|
|
7
7
|
dedupeRunningHeaders,
|
|
8
8
|
flattenLayoutTables,
|
|
9
|
+
hasStructuredCellContent,
|
|
9
10
|
inlineImagesIntoMarkdown,
|
|
10
11
|
mapPuaText
|
|
11
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-AGBFCFQY.js";
|
|
12
13
|
import {
|
|
13
14
|
parsePageRange
|
|
14
15
|
} from "./chunk-GE43BE46.js";
|
|
@@ -16,7 +17,7 @@ import {
|
|
|
16
17
|
HEADING_RATIO_H1,
|
|
17
18
|
HEADING_RATIO_H2,
|
|
18
19
|
HEADING_RATIO_H3
|
|
19
|
-
} from "./chunk-
|
|
20
|
+
} from "./chunk-XVIJLHCW.js";
|
|
20
21
|
import {
|
|
21
22
|
KordocError,
|
|
22
23
|
VERSION,
|
|
@@ -29,10 +30,10 @@ import {
|
|
|
29
30
|
sanitizeHref,
|
|
30
31
|
stripDtd,
|
|
31
32
|
toArrayBuffer
|
|
32
|
-
} from "./chunk-
|
|
33
|
+
} from "./chunk-2B45S7FM.js";
|
|
33
34
|
|
|
34
35
|
// src/index.ts
|
|
35
|
-
import { readFile } from "fs/promises";
|
|
36
|
+
import { readFile as readFile3 } from "fs/promises";
|
|
36
37
|
|
|
37
38
|
// src/detect.ts
|
|
38
39
|
import JSZip from "jszip";
|
|
@@ -570,9 +571,9 @@ function decryptEntry(cipher, entry, startKey) {
|
|
|
570
571
|
const decipher = createDecipheriv("aes-256-cbc", key, entry.iv);
|
|
571
572
|
decipher.setAutoPadding(false);
|
|
572
573
|
const raw = Buffer.concat([decipher.update(cipher), decipher.final()]);
|
|
573
|
-
const
|
|
574
|
-
const sum = createHash("sha256").update(
|
|
575
|
-
if (sum.length === entry.checksum.length && timingSafeEqual(sum, entry.checksum)) return
|
|
574
|
+
const plain3 = inflateRawSync(raw, { maxOutputLength: MAX_DECOMPRESS_SIZE });
|
|
575
|
+
const sum = createHash("sha256").update(plain3.subarray(0, Math.min(CHECKSUM_PREFIX_BYTES, plain3.length))).digest();
|
|
576
|
+
if (sum.length === entry.checksum.length && timingSafeEqual(sum, entry.checksum)) return plain3;
|
|
576
577
|
} catch {
|
|
577
578
|
}
|
|
578
579
|
}
|
|
@@ -588,13 +589,13 @@ async function decryptHwpxInPlace(zip, manifestXml, password) {
|
|
|
588
589
|
if (!file) {
|
|
589
590
|
throw new KordocError(`\uC554\uD638 \uBAA9\uB85D\uC5D0 \uC788\uB294 \uD30C\uC77C\uC774 \uBB38\uC11C\uC5D0 \uC5C6\uC2B5\uB2C8\uB2E4: ${entry.path}`);
|
|
590
591
|
}
|
|
591
|
-
const
|
|
592
|
-
if (!
|
|
592
|
+
const plain3 = decryptEntry(await file.async("nodebuffer"), entry, startKey);
|
|
593
|
+
if (!plain3) {
|
|
593
594
|
throw new KordocError("\uBE44\uBC00\uBC88\uD638\uAC00 \uC77C\uCE58\uD558\uC9C0 \uC54A\uAC70\uB098 \uC554\uD638\uD654 \uB370\uC774\uD130\uAC00 \uC190\uC0C1\uB418\uC5C8\uC2B5\uB2C8\uB2E4.");
|
|
594
595
|
}
|
|
595
|
-
decrypted.push({ path: entry.path, plain:
|
|
596
|
+
decrypted.push({ path: entry.path, plain: plain3 });
|
|
596
597
|
}
|
|
597
|
-
for (const { path, plain:
|
|
598
|
+
for (const { path, plain: plain3 } of decrypted) zip.file(path, plain3);
|
|
598
599
|
zip.file("META-INF/manifest.xml", stripEncryptionData(manifestXml));
|
|
599
600
|
return decrypted.length;
|
|
600
601
|
}
|
|
@@ -1331,6 +1332,26 @@ function circledNumber(n) {
|
|
|
1331
1332
|
function circledHangul(n) {
|
|
1332
1333
|
return n < 14 ? String.fromCodePoint(12910 + n) : hangulOrdinal(n);
|
|
1333
1334
|
}
|
|
1335
|
+
var AMOUNT_DIGITS = "\uC601\uC77C\uC774\uC0BC\uC0AC\uC624\uC721\uCE60\uD314\uAD6C";
|
|
1336
|
+
var AMOUNT_SMALL = ["", "\uC2ED", "\uBC31", "\uCC9C"];
|
|
1337
|
+
var AMOUNT_BIG = ["", "\uB9CC", "\uC5B5", "\uC870", "\uACBD", "\uD574"];
|
|
1338
|
+
function hangulAmount(n) {
|
|
1339
|
+
const digits = String(n).replace(/\D/g, "").replace(/^0+(?=\d)/, "");
|
|
1340
|
+
if (!digits || digits === "0") return "\uC601";
|
|
1341
|
+
const groups = [];
|
|
1342
|
+
for (let end = digits.length; end > 0; end -= 4) groups.unshift(digits.slice(Math.max(0, end - 4), end));
|
|
1343
|
+
let out = "";
|
|
1344
|
+
groups.forEach((g, i) => {
|
|
1345
|
+
let part = "";
|
|
1346
|
+
const padded = g.padStart(4, "0");
|
|
1347
|
+
for (let k = 0; k < 4; k++) {
|
|
1348
|
+
const d = Number(padded[k]);
|
|
1349
|
+
if (d) part += AMOUNT_DIGITS[d] + AMOUNT_SMALL[3 - k];
|
|
1350
|
+
}
|
|
1351
|
+
if (part) out += part + (AMOUNT_BIG[groups.length - 1 - i] ?? "");
|
|
1352
|
+
});
|
|
1353
|
+
return out;
|
|
1354
|
+
}
|
|
1334
1355
|
function romanNumeral(n, upper) {
|
|
1335
1356
|
if (n <= 0 || n > 3999) return String(n);
|
|
1336
1357
|
const values = [1e3, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1];
|
|
@@ -1427,8 +1448,9 @@ function resolveParaHeading(paraEl, ctx) {
|
|
|
1427
1448
|
function buildTableWithCellMeta(state, keepAnchoredEmptyCols, keepEmptyParagraphs) {
|
|
1428
1449
|
const table2 = buildTable(state.rows, { keepAnchoredEmptyCols, keepEmptyParagraphs });
|
|
1429
1450
|
if (state.caption) table2.caption = state.caption;
|
|
1451
|
+
if (state.sourceId) table2.sourceId = state.sourceId;
|
|
1430
1452
|
if (state.captionBlocks?.length) table2.captionBlocks = state.captionBlocks;
|
|
1431
|
-
const
|
|
1453
|
+
const anchors2 = [];
|
|
1432
1454
|
{
|
|
1433
1455
|
const covered = /* @__PURE__ */ new Set();
|
|
1434
1456
|
for (let r = 0; r < table2.rows; r++) {
|
|
@@ -1442,13 +1464,13 @@ function buildTableWithCellMeta(state, keepAnchoredEmptyCols, keepEmptyParagraph
|
|
|
1442
1464
|
if (r + dr < table2.rows && c + dc < table2.cols) covered.add(`${r + dr},${c + dc}`);
|
|
1443
1465
|
}
|
|
1444
1466
|
}
|
|
1445
|
-
|
|
1467
|
+
anchors2.push(cell2);
|
|
1446
1468
|
c += cell2.colSpan - 1;
|
|
1447
1469
|
}
|
|
1448
1470
|
}
|
|
1449
1471
|
}
|
|
1450
1472
|
const srcCount = state.rows.reduce((s, r) => s + r.length, 0);
|
|
1451
|
-
const ordinalReliable =
|
|
1473
|
+
const ordinalReliable = anchors2.length === srcCount;
|
|
1452
1474
|
const claimed = /* @__PURE__ */ new Set();
|
|
1453
1475
|
let flatIdx = -1;
|
|
1454
1476
|
for (const row of state.rows) {
|
|
@@ -1473,7 +1495,7 @@ function buildTableWithCellMeta(state, keepAnchoredEmptyCols, keepEmptyParagraph
|
|
|
1473
1495
|
}
|
|
1474
1496
|
}
|
|
1475
1497
|
if (!target && ordinalReliable) {
|
|
1476
|
-
const cand =
|
|
1498
|
+
const cand = anchors2[flatIdx];
|
|
1477
1499
|
if (cand && !claimed.has(cand)) target = cand;
|
|
1478
1500
|
}
|
|
1479
1501
|
if (!target) continue;
|
|
@@ -1702,7 +1724,7 @@ function walkSection(node, blocks, tableCtx, tableStack, ctx, depth = 0) {
|
|
|
1702
1724
|
}
|
|
1703
1725
|
}
|
|
1704
1726
|
if (tableCtx) tableStack.push(tableCtx);
|
|
1705
|
-
const newTable = { rows: [], currentRow: [], cell: null };
|
|
1727
|
+
const newTable = { rows: [], currentRow: [], cell: null, sourceId: el.getAttribute("id") ?? void 0 };
|
|
1706
1728
|
walkSection(el, blocks, newTable, tableStack, ctx, depth + 1);
|
|
1707
1729
|
tableCtx = completeTable(newTable, tableStack, blocks, ctx);
|
|
1708
1730
|
break;
|
|
@@ -2025,7 +2047,7 @@ function collectSubListContent(el, ctx, depth = 0) {
|
|
|
2025
2047
|
}
|
|
2026
2048
|
function buildSubListTable(el, ctx, depth) {
|
|
2027
2049
|
const sink = [];
|
|
2028
|
-
const st = { rows: [], currentRow: [], cell: null };
|
|
2050
|
+
const st = { rows: [], currentRow: [], cell: null, sourceId: el.getAttribute("id") ?? void 0 };
|
|
2029
2051
|
walkSection(el, sink, st, [], ctx, depth + 1);
|
|
2030
2052
|
let flat = convertTableToText(st.rows);
|
|
2031
2053
|
if (st.caption) flat = st.caption + (flat ? "\n" + flat : "");
|
|
@@ -2073,7 +2095,7 @@ function walkParagraphChildren(node, blocks, tableCtx, tableStack, ctx, depth =
|
|
|
2073
2095
|
}
|
|
2074
2096
|
}
|
|
2075
2097
|
if (tableCtx) tableStack.push(tableCtx);
|
|
2076
|
-
const newTable = { rows: [], currentRow: [], cell: null, inline: isInlineTbl(el) };
|
|
2098
|
+
const newTable = { rows: [], currentRow: [], cell: null, inline: isInlineTbl(el), sourceId: el.getAttribute("id") ?? void 0 };
|
|
2077
2099
|
walkSection(el, blocks, newTable, tableStack, ctx, d + 1);
|
|
2078
2100
|
tableCtx = completeTable(newTable, tableStack, blocks, ctx);
|
|
2079
2101
|
} else if (localTag === "caption" && !inShape) {
|
|
@@ -2785,8 +2807,9 @@ function appendParaText(state, data, resolveControl) {
|
|
|
2785
2807
|
case CHAR_PARA:
|
|
2786
2808
|
break;
|
|
2787
2809
|
// 문단 끝
|
|
2810
|
+
// 하이픈(24)은 한컴이 그리지 않는다 — 한컴 PDF·rhwp export-hwpx 모두 "60g/㎡"(licbyl2 17855137,
|
|
2811
|
+
// 전 코퍼스 유일 출현). 실렌더가 진실이므로 방출하지 않는다 (v4.12.3, 종전 "-")
|
|
2788
2812
|
case CHAR_HYPHEN:
|
|
2789
|
-
result += "-";
|
|
2790
2813
|
break;
|
|
2791
2814
|
case CHAR_NBSP:
|
|
2792
2815
|
result += "\xA0";
|
|
@@ -22556,6 +22579,166 @@ async function fillHwpx(hwpxBuffer, values, blockedLabels) {
|
|
|
22556
22579
|
};
|
|
22557
22580
|
}
|
|
22558
22581
|
|
|
22582
|
+
// src/table/classifier.ts
|
|
22583
|
+
var DIAGRAM_KEYWORDS = /조직도|비상\s*연락망|연락망|체계도|업무\s*체계도|기구표|배치도|흐름도|추진\s*체계/u;
|
|
22584
|
+
var KEYWORD_GATE = 0.3;
|
|
22585
|
+
function cellType(cell2) {
|
|
22586
|
+
const t = cell2.text.trim();
|
|
22587
|
+
if (!t && !cell2.blocks?.length) return "empty";
|
|
22588
|
+
if (!t) return "short";
|
|
22589
|
+
if (/^[-+]?[\d,]+(\.\d+)?\s*(%|원|명|건|개|천원|백만원|㎡|㎢|kg|km|시간|일)?$/u.test(t)) return "number";
|
|
22590
|
+
if (/^\d{2,4}[.\-/년]\s*\d{1,2}[.\-/월]?(\s*\d{1,2}[.일]?)?\.?$/u.test(t)) return "date";
|
|
22591
|
+
return [...t].length <= 12 ? "short" : "long";
|
|
22592
|
+
}
|
|
22593
|
+
function anchors(table2) {
|
|
22594
|
+
const out = [];
|
|
22595
|
+
const covered = /* @__PURE__ */ new Set();
|
|
22596
|
+
for (let r = 0; r < table2.rows; r++) {
|
|
22597
|
+
for (let c = 0; c < table2.cols; c++) {
|
|
22598
|
+
if (covered.has(`${r},${c}`)) continue;
|
|
22599
|
+
const cell2 = table2.cells[r]?.[c];
|
|
22600
|
+
if (!cell2) continue;
|
|
22601
|
+
for (let dr = 0; dr < cell2.rowSpan; dr++) for (let dc = 0; dc < cell2.colSpan; dc++) if (dr || dc) covered.add(`${r + dr},${c + dc}`);
|
|
22602
|
+
out.push({ r, c, cell: cell2, type: cellType(cell2) });
|
|
22603
|
+
}
|
|
22604
|
+
}
|
|
22605
|
+
return out;
|
|
22606
|
+
}
|
|
22607
|
+
function isWrapper(table2) {
|
|
22608
|
+
if (table2.rows !== 1 || table2.cols !== 1) return false;
|
|
22609
|
+
const cell2 = table2.cells[0]?.[0];
|
|
22610
|
+
return !!cell2?.blocks?.some((b) => b.type === "table" && b.table);
|
|
22611
|
+
}
|
|
22612
|
+
function round2(v) {
|
|
22613
|
+
return Math.round(v * 100) / 100;
|
|
22614
|
+
}
|
|
22615
|
+
function classifyTable(table2, ctx = {}) {
|
|
22616
|
+
const reasons = [];
|
|
22617
|
+
const done = (kind, semantic2, nonTabular2) => ({ kind, confidence: round2(Math.min(1, Math.abs(semantic2 - nonTabular2))), semanticScore: round2(semantic2), nonTabularScore: round2(nonTabular2), reasons });
|
|
22618
|
+
if (isWrapper(table2)) {
|
|
22619
|
+
reasons.push("nested-structure-wrapper");
|
|
22620
|
+
return done("non-tabular-layout", 0.05, 0.85);
|
|
22621
|
+
}
|
|
22622
|
+
const slots = table2.rows * table2.cols;
|
|
22623
|
+
const A = anchors(table2);
|
|
22624
|
+
if (slots <= 4 || A.length <= 2) {
|
|
22625
|
+
reasons.push("low-evidence");
|
|
22626
|
+
return done("uncertain", 0.2, 0.2);
|
|
22627
|
+
}
|
|
22628
|
+
const activeSlots = A.filter((a) => a.type !== "empty").reduce((s, a) => s + a.cell.colSpan * a.cell.rowSpan, 0);
|
|
22629
|
+
const activeRatio = activeSlots / slots;
|
|
22630
|
+
const rowHasContent = Array.from({ length: table2.rows }, () => false), colHasContent = Array.from({ length: table2.cols }, () => false);
|
|
22631
|
+
for (const a of A) if (a.type !== "empty") {
|
|
22632
|
+
for (let dr = 0; dr < a.cell.rowSpan; dr++) rowHasContent[a.r + dr] = true;
|
|
22633
|
+
for (let dc = 0; dc < a.cell.colSpan; dc++) colHasContent[a.c + dc] = true;
|
|
22634
|
+
}
|
|
22635
|
+
const spacerRows = rowHasContent.filter((x) => !x).length, spacerCols = colHasContent.filter((x) => !x).length;
|
|
22636
|
+
const spacerBands = (spacerRows + spacerCols) / (table2.rows + table2.cols);
|
|
22637
|
+
const body = A.filter((a) => a.r > 0);
|
|
22638
|
+
const bodyMerged = body.filter((a) => a.cell.colSpan > 1 || a.cell.rowSpan > 1).length;
|
|
22639
|
+
const anchorsPerRow = /* @__PURE__ */ new Map();
|
|
22640
|
+
for (const a of A) anchorsPerRow.set(a.r, (anchorsPerRow.get(a.r) ?? 0) + 1);
|
|
22641
|
+
const counts = [...anchorsPerRow.values()];
|
|
22642
|
+
const meanCnt = counts.reduce((s, v) => s + v, 0) / Math.max(1, counts.length);
|
|
22643
|
+
const rowVariance = counts.length > 1 ? counts.reduce((s, v) => s + Math.abs(v - meanCnt), 0) / counts.length / Math.max(1, meanCnt) : 0;
|
|
22644
|
+
const spanIrregularity = Math.min(1, (body.length ? bodyMerged / body.length : 0) * 0.7 + Math.min(1, rowVariance) * 0.6);
|
|
22645
|
+
const gridRegularity = A.length ? A.filter((a) => a.cell.colSpan === 1 && a.cell.rowSpan === 1 || a.r === 0 && a.cell.rowSpan === 1).length / A.length : 0;
|
|
22646
|
+
let repeatedRowSchema = 0;
|
|
22647
|
+
if (table2.rows >= 3) {
|
|
22648
|
+
const sigs = /* @__PURE__ */ new Map();
|
|
22649
|
+
let n = 0;
|
|
22650
|
+
for (let r = 1; r < table2.rows; r++) {
|
|
22651
|
+
const rowA = A.filter((a) => a.r === r && a.type !== "empty");
|
|
22652
|
+
if (!rowA.length) continue;
|
|
22653
|
+
const sig = rowA.map((a) => `${a.c}:${a.type === "long" ? "short" : a.type}`).join("|");
|
|
22654
|
+
sigs.set(sig, (sigs.get(sig) ?? 0) + 1);
|
|
22655
|
+
n++;
|
|
22656
|
+
}
|
|
22657
|
+
repeatedRowSchema = n ? Math.max(...sigs.values()) / n : 0;
|
|
22658
|
+
if (n < 2) repeatedRowSchema *= 0.5;
|
|
22659
|
+
}
|
|
22660
|
+
let columnTypeConsistency = 0;
|
|
22661
|
+
{
|
|
22662
|
+
const cols = [];
|
|
22663
|
+
for (let c = 0; c < table2.cols; c++) {
|
|
22664
|
+
const types = A.filter((a) => a.r > 0 && a.c === c && a.cell.colSpan === 1 && a.type !== "empty").map((a) => a.type);
|
|
22665
|
+
if (types.length < 2) continue;
|
|
22666
|
+
const freq = /* @__PURE__ */ new Map();
|
|
22667
|
+
for (const t of types) freq.set(t, (freq.get(t) ?? 0) + 1);
|
|
22668
|
+
cols.push(Math.max(...freq.values()) / types.length);
|
|
22669
|
+
}
|
|
22670
|
+
columnTypeConsistency = cols.length ? cols.reduce((s, v) => s + v, 0) / cols.length : 0;
|
|
22671
|
+
}
|
|
22672
|
+
const sparsity = Math.max(0, (0.6 - activeRatio) / 0.6);
|
|
22673
|
+
const emptyShare = 1 - activeRatio;
|
|
22674
|
+
let semantic = (0.35 * repeatedRowSchema + 0.25 * gridRegularity + 0.2 * Math.min(1, activeRatio / 0.7) + 0.2 * columnTypeConsistency) * (1 - 0.6 * Math.max(0, emptyShare - 0.2) / 0.8) * (1 - spacerBands) * (1 - 0.5 * spanIrregularity);
|
|
22675
|
+
let nonTabular = 0.6 * spanIrregularity + 0.4 * spacerBands + 0.5 * sparsity;
|
|
22676
|
+
const nestedCells = A.filter((a) => a.cell.blocks?.some((b) => b.type === "table" && b.table)).length;
|
|
22677
|
+
if (nestedCells && nestedCells >= A.length * 0.5) {
|
|
22678
|
+
nonTabular += 0.25;
|
|
22679
|
+
reasons.push("nested-structure-wrapper");
|
|
22680
|
+
}
|
|
22681
|
+
if (repeatedRowSchema >= 0.6) reasons.push("repeated-row-schema");
|
|
22682
|
+
if (gridRegularity >= 0.9) reasons.push("grid-regularity");
|
|
22683
|
+
if (activeRatio >= 0.7) reasons.push("high-active-density");
|
|
22684
|
+
if (columnTypeConsistency >= 0.75) reasons.push("column-type-consistency");
|
|
22685
|
+
if (spanIrregularity >= 0.35) reasons.push("span-irregularity");
|
|
22686
|
+
if (spacerBands >= 0.25) reasons.push("spacer-bands");
|
|
22687
|
+
if (sparsity >= 0.5) reasons.push("extreme-sparsity");
|
|
22688
|
+
const text = [table2.caption ?? "", ...ctx.nearbyText ?? []].join("\n");
|
|
22689
|
+
if (DIAGRAM_KEYWORDS.test(text) && nonTabular >= KEYWORD_GATE) {
|
|
22690
|
+
nonTabular += 0.15;
|
|
22691
|
+
reasons.push("diagram-context-keyword");
|
|
22692
|
+
}
|
|
22693
|
+
semantic = Math.min(1, semantic);
|
|
22694
|
+
nonTabular = Math.min(1, nonTabular);
|
|
22695
|
+
if (semantic >= 0.55 && semantic - nonTabular >= 0.2) return done("semantic-table", semantic, nonTabular);
|
|
22696
|
+
if (nonTabular >= 0.45 && nonTabular - semantic >= 0.15) return done("non-tabular-layout", semantic, nonTabular);
|
|
22697
|
+
reasons.push(Math.max(semantic, nonTabular) < 0.35 ? "low-evidence" : "ambiguous-scores");
|
|
22698
|
+
return done("uncertain", semantic, nonTabular);
|
|
22699
|
+
}
|
|
22700
|
+
function collectTableBlocks(blocks, out = []) {
|
|
22701
|
+
for (const b of blocks) {
|
|
22702
|
+
if (b.type === "table" && b.table) {
|
|
22703
|
+
out.push(b);
|
|
22704
|
+
for (const row of b.table.cells) for (const cell2 of row) if (cell2.blocks) collectTableBlocks(cell2.blocks, out);
|
|
22705
|
+
if (b.table.captionBlocks) collectTableBlocks(b.table.captionBlocks, out);
|
|
22706
|
+
}
|
|
22707
|
+
if (b.children) collectTableBlocks(b.children, out);
|
|
22708
|
+
}
|
|
22709
|
+
return out;
|
|
22710
|
+
}
|
|
22711
|
+
|
|
22712
|
+
// src/table/analyze.ts
|
|
22713
|
+
function hasMerged(table2) {
|
|
22714
|
+
return table2.cells.some((row) => row.some((c) => c.colSpan > 1 || c.rowSpan > 1));
|
|
22715
|
+
}
|
|
22716
|
+
function classifyInList(blocks) {
|
|
22717
|
+
for (let i = 0; i < blocks.length; i++) {
|
|
22718
|
+
const b = blocks[i];
|
|
22719
|
+
if (b.type === "table" && b.table) {
|
|
22720
|
+
const near = [];
|
|
22721
|
+
for (const j of [i - 2, i - 1, i + 1]) {
|
|
22722
|
+
const n = blocks[j];
|
|
22723
|
+
if (n && n.type !== "table" && n.text) near.push(n.text);
|
|
22724
|
+
}
|
|
22725
|
+
b.table.classification = classifyTable(b.table, { nearbyText: near });
|
|
22726
|
+
for (const row of b.table.cells) for (const cell2 of row) if (cell2.blocks) classifyInList(cell2.blocks);
|
|
22727
|
+
if (b.table.captionBlocks) classifyInList(b.table.captionBlocks);
|
|
22728
|
+
}
|
|
22729
|
+
if (b.children) classifyInList(b.children);
|
|
22730
|
+
}
|
|
22731
|
+
}
|
|
22732
|
+
function classifyTableTree(blocks) {
|
|
22733
|
+
classifyInList(blocks);
|
|
22734
|
+
return blocks;
|
|
22735
|
+
}
|
|
22736
|
+
function chooseTableRepresentation(table2, opts = {}) {
|
|
22737
|
+
const structured = hasMerged(table2) || hasStructuredCellContent(table2);
|
|
22738
|
+
if (opts.smartVisual && table2.classification?.kind === "non-tabular-layout") return "visual";
|
|
22739
|
+
return structured ? "html" : "gfm";
|
|
22740
|
+
}
|
|
22741
|
+
|
|
22559
22742
|
// src/page-markdown.ts
|
|
22560
22743
|
function blocksToPages(blocks) {
|
|
22561
22744
|
const firstNumbered = blocks.find((b) => typeof b.pageNumber === "number")?.pageNumber;
|
|
@@ -22743,13 +22926,29 @@ var SPACE_EM_FONT = 300;
|
|
|
22743
22926
|
function faceClassOf(face) {
|
|
22744
22927
|
return face && /^(굴림체|돋움체|바탕체|궁서체)$/.test(face.trim()) ? "fixedPitch" : "hcr";
|
|
22745
22928
|
}
|
|
22929
|
+
function faceClassForGen(face) {
|
|
22930
|
+
if (!face) return "hcr";
|
|
22931
|
+
const f = face.trim();
|
|
22932
|
+
if (/^(굴림체|돋움체|바탕체|궁서체)$/.test(f)) return "fixedPitch";
|
|
22933
|
+
if (/^(한컴돋움|맑은 고딕|HY견고딕|HY헤드라인M|HY중고딕|한양중고딕|나눔고딕|나눔스퀘어|돋움|굴림)$/.test(f)) return "gothic";
|
|
22934
|
+
return "hcr";
|
|
22935
|
+
}
|
|
22936
|
+
function gothicWidthEm1000(cp) {
|
|
22937
|
+
if (cp >= 128) return 1e3;
|
|
22938
|
+
if (cp >= 65 && cp <= 90) return 720;
|
|
22939
|
+
if (cp >= 97 && cp <= 122) return 560;
|
|
22940
|
+
if (cp >= 48 && cp <= 57) return 580;
|
|
22941
|
+
if (cp === 40 || cp === 41 || cp === 91 || cp === 93) return 350;
|
|
22942
|
+
if (cp === 46 || cp === 44 || cp === 58 || cp === 59 || cp === 39) return 280;
|
|
22943
|
+
return 500;
|
|
22944
|
+
}
|
|
22746
22945
|
function fixedPitchWidthEm1000(cp) {
|
|
22747
22946
|
return cp < 128 ? 500 : 1e3;
|
|
22748
22947
|
}
|
|
22749
22948
|
function measureTextWidth(text, height, ratioPct, opts) {
|
|
22750
22949
|
const spaceEm = opts?.spaceEm ?? SPACE_EM_FIXED;
|
|
22751
22950
|
const spacing = opts?.spacingPct ?? 0;
|
|
22752
|
-
const widthEm = opts?.faceClass === "fixedPitch" ? fixedPitchWidthEm1000 : charWidthEm1000;
|
|
22951
|
+
const widthEm = opts?.faceClass === "fixedPitch" ? fixedPitchWidthEm1000 : opts?.faceClass === "gothic" ? gothicWidthEm1000 : charWidthEm1000;
|
|
22753
22952
|
let em = 0;
|
|
22754
22953
|
for (const ch of text) {
|
|
22755
22954
|
const cp = ch.codePointAt(0);
|
|
@@ -22764,7 +22963,7 @@ function simulateWrap(text, firstWidth, contWidth, height, ratioPct, mode = "kee
|
|
|
22764
22963
|
const EPS = 0.5;
|
|
22765
22964
|
const spaceEm = opts?.spaceEm ?? SPACE_EM_FIXED;
|
|
22766
22965
|
const spacing = opts?.spacingPct ?? 0;
|
|
22767
|
-
const widthEm = opts?.faceClass === "fixedPitch" ? fixedPitchWidthEm1000 : charWidthEm1000;
|
|
22966
|
+
const widthEm = opts?.faceClass === "fixedPitch" ? fixedPitchWidthEm1000 : opts?.faceClass === "gothic" ? gothicWidthEm1000 : charWidthEm1000;
|
|
22768
22967
|
const k = height * ratioPct / 100 / 1e3;
|
|
22769
22968
|
const cwCp = (cp) => (cp === 32 ? spaceEm : widthEm(cp)) * (1 + spacing / 100) * k;
|
|
22770
22969
|
const charW2 = (ch) => cwCp(ch.codePointAt(0));
|
|
@@ -22973,6 +23172,8 @@ function bodyTitleGeom(bodyHeight, ov = {}) {
|
|
|
22973
23172
|
// src/hwpx/gongmun.ts
|
|
22974
23173
|
var OFFICIAL_MARGINS = { top: 20, bottom: 15, left: 20, right: 15 };
|
|
22975
23174
|
var GAEJOSIK_MARGINS = { top: 15, bottom: 15, left: 20, right: 20 };
|
|
23175
|
+
var SEOUL_REPORT_MARGINS = { top: 13, bottom: 13, left: 18, right: 18 };
|
|
23176
|
+
var SEOUL_REPORT_HEADER_FOOTER = 3600;
|
|
22976
23177
|
var GAEJOSIK_HEADER_FOOTER = 4251;
|
|
22977
23178
|
var PRESET_DEFAULTS = {
|
|
22978
23179
|
// 서울시 실결재 104건 중 12pt 64건(13pt 30, 15pt 5) — 지배값을 기본으로 사용.
|
|
@@ -23055,6 +23256,36 @@ function validateGongmunOptions(opts) {
|
|
|
23055
23256
|
if (opts.approval && opts.approval.length > 6) {
|
|
23056
23257
|
throw new KordocError("approval must contain at most 6 labels");
|
|
23057
23258
|
}
|
|
23259
|
+
if (opts.levels) {
|
|
23260
|
+
for (const [key, st] of Object.entries(opts.levels)) {
|
|
23261
|
+
const depth = Number(key);
|
|
23262
|
+
if (!Number.isInteger(depth) || depth < 0 || depth >= GONGMUN_LEVEL_COUNT) {
|
|
23263
|
+
throw new KordocError(`levels: depth must be an integer between 0 and ${GONGMUN_LEVEL_COUNT - 1} (got "${key}")`);
|
|
23264
|
+
}
|
|
23265
|
+
if (!st || typeof st !== "object") throw new KordocError(`levels.${key} must be an object {font, pt, bold}`);
|
|
23266
|
+
if (st.pt !== void 0) assertFiniteRange(`levels.${key}.pt`, st.pt, 6, 60);
|
|
23267
|
+
if (st.font !== void 0 && (typeof st.font !== "string" || !st.font.trim())) {
|
|
23268
|
+
throw new KordocError(`levels.${key}.font must be a non-empty string`);
|
|
23269
|
+
}
|
|
23270
|
+
}
|
|
23271
|
+
}
|
|
23272
|
+
}
|
|
23273
|
+
var GONGMUN_LEVEL_COUNT = 8;
|
|
23274
|
+
function resolveLevels(levels, bodyHeight) {
|
|
23275
|
+
if (!levels) return null;
|
|
23276
|
+
const out = {};
|
|
23277
|
+
for (const [key, st] of Object.entries(levels)) {
|
|
23278
|
+
if (st.font === void 0 && st.pt === void 0 && st.bold === void 0) continue;
|
|
23279
|
+
out[Number(key)] = {
|
|
23280
|
+
font: st.font?.trim() || null,
|
|
23281
|
+
height: st.pt !== void 0 ? Math.round(st.pt * 100) : bodyHeight,
|
|
23282
|
+
bold: !!st.bold
|
|
23283
|
+
};
|
|
23284
|
+
}
|
|
23285
|
+
return Object.keys(out).length > 0 ? out : null;
|
|
23286
|
+
}
|
|
23287
|
+
function levelMarkerHeight(g, depth) {
|
|
23288
|
+
return g.levels?.[depth]?.height ?? g.bodyHeight;
|
|
23058
23289
|
}
|
|
23059
23290
|
function incompatibleGongmunWarnings(opts) {
|
|
23060
23291
|
const warns = [];
|
|
@@ -23090,29 +23321,37 @@ function resolveGongmun(opts) {
|
|
|
23090
23321
|
bodyHeight: Math.round(bodyPt * 100),
|
|
23091
23322
|
lineSpacing: opts.lineSpacing ?? d.lineSpacing,
|
|
23092
23323
|
numbering: opts.numbering ?? d.numbering,
|
|
23093
|
-
margins: opts.margins ?? (reportFamily ? GAEJOSIK_MARGINS : OFFICIAL_MARGINS),
|
|
23324
|
+
margins: opts.margins ?? (preset === "report" || preset === "plan" ? SEOUL_REPORT_MARGINS : reportFamily ? GAEJOSIK_MARGINS : OFFICIAL_MARGINS),
|
|
23094
23325
|
centerTitle: opts.centerTitle ?? true,
|
|
23095
23326
|
autoFitMinRatio,
|
|
23096
23327
|
// 보도자료는 머리박스가 1페이지 최상단을 차지하는 서식이라 표지·목차와 양립 불가 —
|
|
23097
23328
|
// 켜면 머리박스가 표지에 얹히고 25pt 제목·부제가 유실된다 (docHead 프리셋 게이팅과 동일 관례)
|
|
23098
|
-
cover: coverOn && preset !== "press" ? { date: coverOpts.date ?? null, org: coverOpts.org ?? "" } : null,
|
|
23329
|
+
cover: coverOn && preset !== "press" ? { date: coverOpts.date ?? null, org: coverOpts.org ?? "", ...coverOpts.dept ? { dept: coverOpts.dept } : {} } : null,
|
|
23330
|
+
bodyPtExplicit: opts.bodyPt !== void 0,
|
|
23331
|
+
lineSpacingExplicit: opts.lineSpacing !== void 0,
|
|
23332
|
+
bodyFontExplicit: opts.bodyFont !== void 0,
|
|
23333
|
+
summary: opts.summary?.trim() || null,
|
|
23334
|
+
docInfo: opts.docInfo ?? null,
|
|
23099
23335
|
toc: preset !== "press" && (opts.toc ?? gaejosik),
|
|
23100
23336
|
fonts: opts.fonts ?? {},
|
|
23101
23337
|
sizes: opts.sizes ?? {},
|
|
23338
|
+
levels: resolveLevels(opts.levels, Math.round(bodyPt * 100)),
|
|
23102
23339
|
// 쪽번호 — 보고서 계열 관행(실측: 2_보고서 양식·추진계획·공고문 전부 하단 중앙)
|
|
23103
23340
|
pageNumbers: opts.pageNumbers ?? (gaejosik || preset === "report" || preset === "plan"),
|
|
23104
23341
|
// 머리말·꼬리말 — 실측: 보고서 계열 15mm(GT3·t2·춘천·브라더), 공고·보도 10mm,
|
|
23105
23342
|
// 기안문 0(실결재 41/60건 h0/f0)
|
|
23106
|
-
headerFooter: usesReportFonts(preset) ? GAEJOSIK_HEADER_FOOTER : preset === "notice" || preset === "press" ? 2835 : 0,
|
|
23343
|
+
headerFooter: preset === "report" || preset === "plan" ? SEOUL_REPORT_HEADER_FOOTER : usesReportFonts(preset) ? GAEJOSIK_HEADER_FOOTER : preset === "notice" || preset === "press" ? 2835 : 0,
|
|
23107
23344
|
// "끝." — 기안문 규정(본문 끝 2타+"끝."). 그 외는 opt-in
|
|
23108
23345
|
endMark: opts.endMark ?? preset === "official",
|
|
23109
23346
|
approval: opts.approval && opts.approval.length > 0 ? opts.approval : null,
|
|
23110
23347
|
// 본문 제목박스 — 실측(GT3·GT12): 목차 뒤 본문 시작에 제목 반복. 표지 켜진 개조식 기본
|
|
23111
23348
|
bodyTitleBox: opts.bodyTitleBox ?? (gaejosik && coverOn),
|
|
23112
23349
|
// h2 말머리 — 실측: 보고서 양식 □ 대항목(QA-2), 공고문 아라비아("1. 사업개요", 바이오헬스 실측)
|
|
23113
|
-
|
|
23350
|
+
// v5 라운드 3: 보고서·계획서 기본 band(띠 표) — 서울 plan 7/19·교육청 7/18 실측, 실무자 요청
|
|
23351
|
+
h2Marker: opts.h2Marker ?? (preset === "report" || preset === "plan" ? "band" : preset === "notice" ? "number" : "none"),
|
|
23114
23352
|
// 2단계 부호 — 실결재 기안문·공고문 ㅇ 지배(60건 중 ㅇ134:○5), 보고서 양식 계열 ○
|
|
23115
|
-
|
|
23353
|
+
// v5: 서울 실결재 ㅇ(이응) 지배 — 보고서·계획서·통지·보도자료 ㅇ, 중앙부처 개조식 양식만 ○
|
|
23354
|
+
bullet2: opts.bullet2 ?? (preset === "gaejosik" ? "\u25CB" : "\u3147"),
|
|
23116
23355
|
// 단일 형제 부호 생략 — 규정이지만 부호 없는 계단이 실무 눈에 어색 (실무자 QA)
|
|
23117
23356
|
suppressSingle: opts.suppressSingle ?? false,
|
|
23118
23357
|
docHead: preset === "official" && opts.docHead ? opts.docHead : null,
|
|
@@ -23157,10 +23396,10 @@ function markerWidth(marker, bodyHeight) {
|
|
|
23157
23396
|
for (const c of marker) em += charWidthEm1000(c.codePointAt(0));
|
|
23158
23397
|
return Math.round(em / 1e3 * bodyHeight);
|
|
23159
23398
|
}
|
|
23160
|
-
function levelIndent(depth, bodyHeight, numbering, sizes = {}, bullet2 = "\u25CB", asteriskThird = false) {
|
|
23399
|
+
function levelIndent(depth, bodyHeight, numbering, sizes = {}, bullet2 = "\u25CB", asteriskThird = false, markerHeight = bodyHeight) {
|
|
23161
23400
|
if (numbering === "gaejosik") return gaejosikLevelIndent(depth, bodyHeight, sizes, bullet2);
|
|
23162
23401
|
const marker = numbering === "report" ? reportMarker(depth, bullet2, asteriskThird) : standardMarker(depth, 0);
|
|
23163
|
-
return { left: Math.round(depth * bodyHeight), indent: -markerWidth(marker,
|
|
23402
|
+
return { left: Math.round(depth * bodyHeight), indent: -markerWidth(marker, markerHeight) };
|
|
23164
23403
|
}
|
|
23165
23404
|
function computeSuppression(depths) {
|
|
23166
23405
|
const counts = /* @__PURE__ */ new Map();
|
|
@@ -23253,21 +23492,21 @@ function headingCharPrId(level) {
|
|
|
23253
23492
|
if (level === 3) return CHAR_H3;
|
|
23254
23493
|
return CHAR_H4;
|
|
23255
23494
|
}
|
|
23256
|
-
function charPr(id, height, bold, italic, fontId = 0, textColor = DEFAULT_TEXT_COLOR, ratioPct = 100) {
|
|
23495
|
+
function charPr(id, height, bold, italic, fontId = 0, textColor = DEFAULT_TEXT_COLOR, ratioPct = 100, restFont = fontId, spacingPct = 0) {
|
|
23257
23496
|
const boldAttr = bold ? ` bold="1"` : "";
|
|
23258
23497
|
const italicAttr = italic ? ` italic="1"` : "";
|
|
23259
23498
|
const effFont = fontId;
|
|
23260
23499
|
const boldEl = bold ? `<hh:bold/>` : "";
|
|
23261
23500
|
return ` <hh:charPr id="${id}" height="${height}" textColor="${escapeXml(textColor)}" shadeColor="none" useFontSpace="0" useKerning="0" symMark="NONE" borderFillIDRef="1"${boldAttr}${italicAttr}>
|
|
23262
|
-
<hh:fontRef hangul="${effFont}" latin="${effFont}" hanja="${
|
|
23501
|
+
<hh:fontRef hangul="${effFont}" latin="${effFont}" hanja="${restFont}" japanese="${restFont}" other="${restFont}" symbol="${restFont}" user="${restFont}"/>
|
|
23263
23502
|
<hh:ratio hangul="${ratioPct}" latin="${ratioPct}" hanja="${ratioPct}" japanese="100" other="100" symbol="100" user="100"/>
|
|
23264
|
-
<hh:spacing hangul="
|
|
23503
|
+
<hh:spacing hangul="${spacingPct}" latin="${spacingPct}" hanja="${spacingPct}" japanese="0" other="0" symbol="0" user="0"/>
|
|
23265
23504
|
<hh:relSz hangul="100" latin="100" hanja="100" japanese="100" other="100" symbol="100" user="100"/>
|
|
23266
23505
|
<hh:offset hangul="0" latin="0" hanja="0" japanese="0" other="0" symbol="0" user="0"/>${boldEl}
|
|
23267
23506
|
</hh:charPr>`;
|
|
23268
23507
|
}
|
|
23269
23508
|
function paraPr(id, opts = {}) {
|
|
23270
|
-
const { align = "JUSTIFY", spaceBefore = 0, spaceAfter = 0, lineSpacing = 160, indent = 0, left = 0, keepWord = false, keepWithNext = false, outlineLevel } = opts;
|
|
23509
|
+
const { align = "JUSTIFY", spaceBefore = 0, spaceAfter = 0, lineSpacing = 160, indent = 0, left = 0, right = 0, keepWord = false, keepWithNext = false, outlineLevel } = opts;
|
|
23271
23510
|
const breakNonLatin = keepWord ? "BREAK_WORD" : "KEEP_WORD";
|
|
23272
23511
|
const snapGrid = keepWord ? "0" : "1";
|
|
23273
23512
|
const heading = outlineLevel !== void 0 ? `<hh:heading type="OUTLINE" idRef="0" level="${outlineLevel}"/>` : `<hh:heading type="NONE" idRef="0" level="0"/>`;
|
|
@@ -23276,7 +23515,7 @@ function paraPr(id, opts = {}) {
|
|
|
23276
23515
|
${heading}
|
|
23277
23516
|
<hh:breakSetting breakLatinWord="KEEP_WORD" breakNonLatinWord="${breakNonLatin}" widowOrphan="0" keepWithNext="${keepWithNext ? 1 : 0}" keepLines="0" pageBreakBefore="0" lineWrap="BREAK"/>
|
|
23278
23517
|
<hh:autoSpacing eAsianEng="0" eAsianNum="0"/>
|
|
23279
|
-
<hh:margin><hc:intent value="${indent}" unit="HWPUNIT"/><hc:left value="${left}" unit="HWPUNIT"/><hc:right value="
|
|
23518
|
+
<hh:margin><hc:intent value="${indent}" unit="HWPUNIT"/><hc:left value="${left}" unit="HWPUNIT"/><hc:right value="${right}" unit="HWPUNIT"/><hc:prev value="${spaceBefore}" unit="HWPUNIT"/><hc:next value="${spaceAfter}" unit="HWPUNIT"/></hh:margin>
|
|
23280
23519
|
<hh:lineSpacing type="PERCENT" value="${lineSpacing}"/>
|
|
23281
23520
|
<hh:border borderFillIDRef="1" offsetLeft="0" offsetRight="0" offsetTop="0" offsetBottom="0" connect="0" ignoreMargin="0"/>
|
|
23282
23521
|
</hh:paraPr>`;
|
|
@@ -23835,9 +24074,9 @@ function assertSequentialIds(rows, label, startId) {
|
|
|
23835
24074
|
}
|
|
23836
24075
|
}
|
|
23837
24076
|
}
|
|
23838
|
-
function buildCharProperties(theme, gongmun, ratioVariants = [], extraCharPrXmls = []) {
|
|
23839
|
-
const measured = !!gongmun && usesReportFonts(gongmun.preset);
|
|
23840
|
-
const richAssets = !!gongmun && needsGaejosikAssets(gongmun);
|
|
24077
|
+
function buildCharProperties(theme, gongmun, ratioVariants = [], extraCharPrXmls = [], v5 = false) {
|
|
24078
|
+
const measured = !v5 && !!gongmun && usesReportFonts(gongmun.preset);
|
|
24079
|
+
const richAssets = !v5 && !!gongmun && needsGaejosikAssets(gongmun);
|
|
23841
24080
|
let body = 1e3, code = 900, h1 = 1800, h2 = 1400, h3 = 1200, h4 = 1100;
|
|
23842
24081
|
if (gongmun) {
|
|
23843
24082
|
body = gongmun.bodyHeight;
|
|
@@ -23975,7 +24214,7 @@ ${base2.join("\n")}
|
|
|
23975
24214
|
];
|
|
23976
24215
|
const listAlign = gongmun.numbering === "gaejosik" || gongmun.numbering === "report" ? "LEFT" : "JUSTIFY";
|
|
23977
24216
|
for (let d = 0; d < GONGMUN_LIST_LEVELS; d++) {
|
|
23978
|
-
const { left, indent } = levelIndent(d, gongmun.bodyHeight, gongmun.numbering, gongmun.sizes, gongmun.bullet2, usesAsteriskThird(gongmun.preset));
|
|
24217
|
+
const { left, indent } = levelIndent(d, gongmun.bodyHeight, gongmun.numbering, gongmun.sizes, gongmun.bullet2, usesAsteriskThird(gongmun.preset), levelMarkerHeight(gongmun, d));
|
|
23979
24218
|
const sectionGap = gongmun.numbering === "gaejosik" || gongmun.numbering === "report" ? gaejosikSpaceBefore(d, gongmun.bodyHeight) : 0;
|
|
23980
24219
|
const keepNext = (gongmun.numbering === "gaejosik" || gongmun.numbering === "report") && d === 0;
|
|
23981
24220
|
base.push(paraPr(GONGMUN_LIST_BASE + d, { align: listAlign, lineSpacing: ls, left, indent, spaceBefore: sectionGap, keepWord: true, keepWithNext: keepNext }));
|
|
@@ -24016,6 +24255,24 @@ ${base2.join("\n")}
|
|
|
24016
24255
|
${base.join("\n")}
|
|
24017
24256
|
</hh:paraProperties>`;
|
|
24018
24257
|
}
|
|
24258
|
+
function buildParaPropertiesV5(gongmun, extra) {
|
|
24259
|
+
const ls = gongmun.lineSpacing;
|
|
24260
|
+
const base = [
|
|
24261
|
+
paraPr(0, { lineSpacing: ls, keepWord: true }),
|
|
24262
|
+
paraPr(1, { align: gongmun.centerTitle ? "CENTER" : "LEFT", spaceBefore: 400, spaceAfter: 400, lineSpacing: ls, keepWord: true }),
|
|
24263
|
+
paraPr(2, { align: "LEFT", spaceBefore: 600, spaceAfter: 150, lineSpacing: ls, keepWord: true }),
|
|
24264
|
+
paraPr(3, { align: "LEFT", spaceBefore: 400, spaceAfter: 100, lineSpacing: ls, keepWord: true }),
|
|
24265
|
+
paraPr(4, { align: "LEFT", spaceBefore: 300, spaceAfter: 100, lineSpacing: ls, keepWord: true }),
|
|
24266
|
+
paraPr(5, { align: "LEFT", lineSpacing: 130, indent: 400, keepWord: true }),
|
|
24267
|
+
paraPr(6, { align: "LEFT", lineSpacing: ls, indent: 600, keepWord: true }),
|
|
24268
|
+
paraPr(7, { align: "LEFT", lineSpacing: ls, indent: 600, keepWord: true }),
|
|
24269
|
+
...extra
|
|
24270
|
+
];
|
|
24271
|
+
assertSequentialIds(base, "paraPr", 0);
|
|
24272
|
+
return `<hh:paraProperties itemCnt="${base.length}">
|
|
24273
|
+
${base.join("\n")}
|
|
24274
|
+
</hh:paraProperties>`;
|
|
24275
|
+
}
|
|
24019
24276
|
function buildNumberings() {
|
|
24020
24277
|
const heads = Array.from(
|
|
24021
24278
|
{ length: 7 },
|
|
@@ -24032,11 +24289,11 @@ function fontEntry(id, face, weight = 6) {
|
|
|
24032
24289
|
<hh:typeInfo familyType="FCAT_GOTHIC" weight="${weight}" proportion="4" contrast="0" strokeVariation="1" armStyle="1" letterform="1" midline="1" xHeight="1"/>
|
|
24033
24290
|
</hh:font>`;
|
|
24034
24291
|
}
|
|
24035
|
-
function staticFontNext(gongmun) {
|
|
24036
|
-
return gongmun && needsGaejosikAssets(gongmun) ? 8 : 3;
|
|
24292
|
+
function staticFontNext(gongmun, v5 = false) {
|
|
24293
|
+
return !v5 && gongmun && needsGaejosikAssets(gongmun) ? 8 : 3;
|
|
24037
24294
|
}
|
|
24038
|
-
function buildFontFaces(gongmun, bodyFace, extraFonts = []) {
|
|
24039
|
-
if (gongmun && needsGaejosikAssets(gongmun)) {
|
|
24295
|
+
function buildFontFaces(gongmun, bodyFace, extraFonts = [], v5 = false) {
|
|
24296
|
+
if (!v5 && gongmun && needsGaejosikAssets(gongmun)) {
|
|
24040
24297
|
const ov = gongmun.fonts;
|
|
24041
24298
|
const bodyDefault = usesReportFonts(gongmun.preset) ? gongmun.preset !== "gaejosik" && gongmun.bodyFont === "gothic" ? "\uB9D1\uC740 \uACE0\uB515" : "\uD734\uBA3C\uBA85\uC870" : bodyFace;
|
|
24042
24299
|
const faces = [
|
|
@@ -24052,58 +24309,29 @@ function buildFontFaces(gongmun, bodyFace, extraFonts = []) {
|
|
|
24052
24309
|
if (faces.length !== staticFontNext(gongmun)) {
|
|
24053
24310
|
throw new Error(`fontface \uC815\uC801 \uAC1C\uC218 \uB4DC\uB9AC\uD504\uD2B8: ${faces.length} \u2260 staticFontNext ${staticFontNext(gongmun)}`);
|
|
24054
24311
|
}
|
|
24055
|
-
const
|
|
24056
|
-
const
|
|
24057
|
-
return `<hh:fontfaces itemCnt="${
|
|
24058
|
-
` +
|
|
24059
|
-
${
|
|
24312
|
+
const list2 = faces.map((f, i) => fontEntry(i, f, i === 2 || i === 3 ? 9 : 6)).concat(extraFonts.map((f, i) => fontEntry(faces.length + i, f))).join("\n");
|
|
24313
|
+
const langs2 = ["HANGUL", "LATIN", "HANJA", "JAPANESE", "OTHER", "SYMBOL", "USER"];
|
|
24314
|
+
return `<hh:fontfaces itemCnt="${langs2.length}">
|
|
24315
|
+
` + langs2.map((l) => ` <hh:fontface lang="${l}" fontCnt="${faces.length + extraFonts.length}">
|
|
24316
|
+
${list2}
|
|
24060
24317
|
</hh:fontface>`).join("\n") + `
|
|
24061
24318
|
</hh:fontfaces>`;
|
|
24062
24319
|
}
|
|
24063
|
-
const
|
|
24064
|
-
const
|
|
24065
|
-
|
|
24066
|
-
|
|
24067
|
-
|
|
24068
|
-
|
|
24069
|
-
${fontEntry(1, "\uD568\uCD08\uB86C\uB3CB\uC6C0")}
|
|
24070
|
-
${fontEntry(2, "HY\uACAC\uACE0\uB515", 9)}${extraBlock}
|
|
24071
|
-
</hh:fontface>
|
|
24072
|
-
<hh:fontface lang="LATIN" fontCnt="${3 + extraFonts.length}">
|
|
24073
|
-
<hh:font id="0" face="Times New Roman" type="TTF" isEmbedded="0">
|
|
24074
|
-
<hh:typeInfo familyType="FCAT_OLDSTYLE" weight="5" proportion="4" contrast="2" strokeVariation="0" armStyle="0" letterform="0" midline="0" xHeight="4"/>
|
|
24075
|
-
</hh:font>
|
|
24076
|
-
<hh:font id="1" face="Consolas" type="TTF" isEmbedded="0">
|
|
24077
|
-
<hh:typeInfo familyType="FCAT_MODERN" weight="5" proportion="0" contrast="0" strokeVariation="0" armStyle="0" letterform="0" midline="0" xHeight="0"/>
|
|
24078
|
-
</hh:font>
|
|
24079
|
-
<hh:font id="2" face="Arial Black" type="TTF" isEmbedded="0">
|
|
24080
|
-
<hh:typeInfo familyType="FCAT_GOTHIC" weight="9" proportion="0" contrast="0" strokeVariation="0" armStyle="0" letterform="0" midline="0" xHeight="0"/>
|
|
24081
|
-
</hh:font>${extraBlock}
|
|
24082
|
-
</hh:fontface>
|
|
24083
|
-
<hh:fontface lang="HANJA" fontCnt="1">
|
|
24084
|
-
${fontEntry(0, "\uD568\uCD08\uB86C\uBC14\uD0D5")}
|
|
24085
|
-
</hh:fontface>
|
|
24086
|
-
<hh:fontface lang="JAPANESE" fontCnt="1">
|
|
24087
|
-
${fontEntry(0, "\uAD74\uB9BC")}
|
|
24088
|
-
</hh:fontface>
|
|
24089
|
-
<hh:fontface lang="OTHER" fontCnt="1">
|
|
24090
|
-
${fontEntry(0, "\uAD74\uB9BC")}
|
|
24091
|
-
</hh:fontface>
|
|
24092
|
-
<hh:fontface lang="SYMBOL" fontCnt="1">
|
|
24093
|
-
${fontEntry(0, "Symbol")}
|
|
24094
|
-
</hh:fontface>
|
|
24095
|
-
<hh:fontface lang="USER" fontCnt="1">
|
|
24096
|
-
${fontEntry(0, "\uAD74\uB9BC")}
|
|
24097
|
-
</hh:fontface>
|
|
24320
|
+
const list = [fontEntry(0, bodyFace), fontEntry(1, "\uD568\uCD08\uB86C\uB3CB\uC6C0"), fontEntry(2, "HY\uACAC\uACE0\uB515", 9)].concat(extraFonts.map((f, i) => fontEntry(3 + i, f))).join("\n");
|
|
24321
|
+
const langs = ["HANGUL", "LATIN", "HANJA", "JAPANESE", "OTHER", "SYMBOL", "USER"];
|
|
24322
|
+
return `<hh:fontfaces itemCnt="${langs.length}">
|
|
24323
|
+
` + langs.map((l) => ` <hh:fontface lang="${l}" fontCnt="${3 + extraFonts.length}">
|
|
24324
|
+
${list}
|
|
24325
|
+
</hh:fontface>`).join("\n") + `
|
|
24098
24326
|
</hh:fontfaces>`;
|
|
24099
24327
|
}
|
|
24100
|
-
function staticBorderFillItems(gongmun) {
|
|
24328
|
+
function staticBorderFillItems(gongmun, v5 = false) {
|
|
24101
24329
|
const thin = ["0.12 mm", "#000000"];
|
|
24102
24330
|
const items = [
|
|
24103
24331
|
borderFillEntry(1, {}),
|
|
24104
24332
|
borderFillEntry(2, { l: thin, r: thin, t: thin, b: thin })
|
|
24105
24333
|
];
|
|
24106
|
-
if (gongmun && needsGaejosikAssets(gongmun)) {
|
|
24334
|
+
if (!v5 && gongmun && needsGaejosikAssets(gongmun)) {
|
|
24107
24335
|
const c = GAEJOSIK_COLORS;
|
|
24108
24336
|
const edge = ["0.12 mm", c.border];
|
|
24109
24337
|
const grayLine = ["0.1 mm", c.titleLine];
|
|
@@ -24130,11 +24358,11 @@ function staticBorderFillItems(gongmun) {
|
|
|
24130
24358
|
}
|
|
24131
24359
|
return items;
|
|
24132
24360
|
}
|
|
24133
|
-
function staticBorderFillNext(gongmun) {
|
|
24134
|
-
return staticBorderFillItems(gongmun).length + 1;
|
|
24361
|
+
function staticBorderFillNext(gongmun, v5 = false) {
|
|
24362
|
+
return staticBorderFillItems(gongmun, v5).length + 1;
|
|
24135
24363
|
}
|
|
24136
|
-
function buildBorderFills(gongmun, extra = []) {
|
|
24137
|
-
const items = [...staticBorderFillItems(gongmun), ...extra];
|
|
24364
|
+
function buildBorderFills(gongmun, extra = [], v5 = false) {
|
|
24365
|
+
const items = [...staticBorderFillItems(gongmun, v5), ...extra];
|
|
24138
24366
|
assertSequentialIds(items, "borderFill", 1);
|
|
24139
24367
|
return `<hh:borderFills itemCnt="${items.length}">
|
|
24140
24368
|
${items.join("\n")}
|
|
@@ -24142,27 +24370,27 @@ ${items.join("\n")}
|
|
|
24142
24370
|
}
|
|
24143
24371
|
function buildStyles(gongmun) {
|
|
24144
24372
|
const items = [
|
|
24145
|
-
`<hh:style id="0" type="PARA" name="\uBC14\uD0D5\uAE00" engName="Normal" paraPrIDRef="0" charPrIDRef="0" nextStyleIDRef="0"
|
|
24373
|
+
`<hh:style id="0" type="PARA" name="\uBC14\uD0D5\uAE00" engName="Normal" paraPrIDRef="0" charPrIDRef="0" nextStyleIDRef="0" langID="1042" lockForm="0"/>`
|
|
24146
24374
|
];
|
|
24147
24375
|
if (gongmun) {
|
|
24148
24376
|
for (let lvl = 1; lvl <= 4; lvl++) {
|
|
24149
|
-
items.push(`<hh:style id="${lvl}" type="PARA" name="\uAC1C\uC694 ${lvl}" engName="Outline ${lvl}" paraPrIDRef="${lvl}" charPrIDRef="${4 + lvl}" nextStyleIDRef="0"
|
|
24377
|
+
items.push(`<hh:style id="${lvl}" type="PARA" name="\uAC1C\uC694 ${lvl}" engName="Outline ${lvl}" paraPrIDRef="${lvl}" charPrIDRef="${4 + lvl}" nextStyleIDRef="0" langID="1042" lockForm="0"/>`);
|
|
24150
24378
|
}
|
|
24151
24379
|
}
|
|
24152
24380
|
return `<hh:styles itemCnt="${items.length}">
|
|
24153
24381
|
${items.join("\n ")}
|
|
24154
24382
|
</hh:styles>`;
|
|
24155
24383
|
}
|
|
24156
|
-
function generateHeaderXml(theme, gongmun, ratioVariants = [], extraBorderFills = [], extraCharPrXmls = [], listIndentVariants = [], extraFonts = []) {
|
|
24157
|
-
const bodyFace = gongmun?.fonts.body ?? (gongmun?.bodyFont === "gothic" ? "\uB9D1\uC740 \uACE0\uB515" : "\uD568\uCD08\uB86C\uBC14\uD0D5");
|
|
24158
|
-
const charPropsXml = buildCharProperties(theme, gongmun, ratioVariants, extraCharPrXmls);
|
|
24159
|
-
const paraPropsXml = buildParaProperties(gongmun, gongmun ? listIndentVariants : []);
|
|
24384
|
+
function generateHeaderXml(theme, gongmun, ratioVariants = [], extraBorderFills = [], extraCharPrXmls = [], listIndentVariants = [], extraFonts = [], v5 = null) {
|
|
24385
|
+
const bodyFace = v5?.bodyFace ?? gongmun?.fonts.body ?? (gongmun?.bodyFont === "gothic" ? "\uB9D1\uC740 \uACE0\uB515" : "\uD568\uCD08\uB86C\uBC14\uD0D5");
|
|
24386
|
+
const charPropsXml = buildCharProperties(theme, gongmun, ratioVariants, extraCharPrXmls, !!v5);
|
|
24387
|
+
const paraPropsXml = v5 ? buildParaPropertiesV5(gongmun, v5.paraPrXmls) : buildParaProperties(gongmun, gongmun ? listIndentVariants : []);
|
|
24160
24388
|
return `<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
|
|
24161
24389
|
<hh:head xmlns:hh="${NS_HEAD}" xmlns:hp="${NS_PARA}" xmlns:hc="${NS_CORE}" version="1.4" secCnt="1">
|
|
24162
24390
|
<hh:beginNum page="1" footnote="1" endnote="1" pic="1" tbl="1" equation="1"/>
|
|
24163
24391
|
<hh:refList>
|
|
24164
|
-
${buildFontFaces(gongmun, bodyFace, extraFonts)}
|
|
24165
|
-
${buildBorderFills(gongmun, extraBorderFills)}
|
|
24392
|
+
${buildFontFaces(gongmun, bodyFace, extraFonts, !!v5)}
|
|
24393
|
+
${buildBorderFills(gongmun, extraBorderFills, !!v5)}
|
|
24166
24394
|
${charPropsXml}
|
|
24167
24395
|
<hh:tabProperties itemCnt="0"/>
|
|
24168
24396
|
${buildNumberings()}
|
|
@@ -24404,6 +24632,7 @@ function computeGongmunFitPlan(blocks, gongmun, gongmunList) {
|
|
|
24404
24632
|
if (block.type === "list_item" && gongmunList.items.has(i)) {
|
|
24405
24633
|
const { marker, depth, indentVariant } = gongmunList.items.get(i);
|
|
24406
24634
|
if (gongmun.numbering === "gaejosik" && (depth === 0 || (block.text || "").trimStart().startsWith("\u203B"))) continue;
|
|
24635
|
+
if (gongmun.levels?.[depth]) continue;
|
|
24407
24636
|
const content = plainRenderText(block.text || "");
|
|
24408
24637
|
text = marker ? `${marker} ${content}` : content;
|
|
24409
24638
|
const li = levelIndent(depth, gongmun.bodyHeight, gongmun.numbering, gongmun.sizes, gongmun.bullet2, usesAsteriskThird(gongmun.preset));
|
|
@@ -24469,8 +24698,9 @@ function precomputeGongmunList(blocks, gongmun) {
|
|
|
24469
24698
|
const variantIdx = /* @__PURE__ */ new Map();
|
|
24470
24699
|
const variantOf = (marker, depth) => {
|
|
24471
24700
|
if (!marker || gongmun.numbering !== "standard") return void 0;
|
|
24472
|
-
const
|
|
24473
|
-
const
|
|
24701
|
+
const h = levelMarkerHeight(gongmun, depth);
|
|
24702
|
+
const w = markerWidth(marker, h);
|
|
24703
|
+
const rep = -levelIndent(depth, gongmun.bodyHeight, gongmun.numbering, gongmun.sizes, gongmun.bullet2, usesAsteriskThird(gongmun.preset), h).indent;
|
|
24474
24704
|
if (w === rep) return void 0;
|
|
24475
24705
|
const key = `${depth}:${w}`;
|
|
24476
24706
|
let vi = variantIdx.get(key);
|
|
@@ -24564,14 +24794,14 @@ function buildGaejosikCover(title, gongmun, bodyWidth = GAEJOSIK_BASE_WIDTH) {
|
|
|
24564
24794
|
const titleRow = cell({ bf: 1, col: 0, colSpan: 3, row: 1, w: g.totalW, h: g.titleH, paras: titlePara, name: "__kordoc_h1" });
|
|
24565
24795
|
const botRow = cell({ bf: GJ_BF_BAR_DARK, col: 0, row: 2, w: g.botDarkW, h: g.botBarH, paras: barEmpty }) + cell({ bf: GJ_BF_BAR_LIGHT, col: 1, colSpan: 2, row: 2, w: g.botLightW, h: g.botBarH, paras: barEmpty });
|
|
24566
24796
|
const tbl2 = table([topRow, titleRow, botRow], g.totalW, g.barH + g.titleH + g.botBarH, 3, 1, bodyWidth);
|
|
24567
|
-
const
|
|
24797
|
+
const host2 = `<hp:p paraPrIDRef="${PARA_NORMAL}" styleIDRef="0"><hp:run charPrIDRef="${CHAR_NORMAL}">${tbl2}</hp:run></hp:p>`;
|
|
24568
24798
|
const date = gongmun.cover?.date ?? formatGaejosikDate(/* @__PURE__ */ new Date());
|
|
24569
24799
|
const org = gongmun.cover?.org ?? "";
|
|
24570
24800
|
const sub = (t) => `<hp:p paraPrIDRef="${GJ_PARA_COVER}" styleIDRef="0"><hp:run charPrIDRef="${GJ_CHAR_COVER_SUB}"><hp:t>${escapeXml(t)}</hp:t></hp:run></hp:p>`;
|
|
24571
24801
|
const subEmpty = emptyPara(GJ_PARA_COVER, false, GJ_CHAR_COVER_SUB);
|
|
24572
24802
|
return [
|
|
24573
24803
|
...Array(5).fill(emptyPara()),
|
|
24574
|
-
|
|
24804
|
+
host2,
|
|
24575
24805
|
...Array(5).fill(emptyPara()),
|
|
24576
24806
|
sub(date),
|
|
24577
24807
|
...org ? [...Array(4).fill(subEmpty), sub(org)] : []
|
|
@@ -24602,8 +24832,8 @@ function buildGaejosikToc(chapters, gongmun, bodyWidth = GAEJOSIK_BASE_WIDTH) {
|
|
|
24602
24832
|
1,
|
|
24603
24833
|
bodyWidth
|
|
24604
24834
|
);
|
|
24605
|
-
const
|
|
24606
|
-
return [label, emptyPara(GJ_PARA_BAR, false, GJ_CHAR_BAR),
|
|
24835
|
+
const host2 = `<hp:p paraPrIDRef="${GJ_PARA_BAR}" styleIDRef="0"><hp:run charPrIDRef="${CHAR_NORMAL}">${box}</hp:run></hp:p>`;
|
|
24836
|
+
return [label, emptyPara(GJ_PARA_BAR, false, GJ_CHAR_BAR), host2];
|
|
24607
24837
|
}
|
|
24608
24838
|
function buildGaejosikBodyTitle(title, gongmun, bodyWidth = GAEJOSIK_BASE_WIDTH) {
|
|
24609
24839
|
const g = coverGeom(gongmun.bodyHeight, gongmun.sizes, bodyWidth);
|
|
@@ -25083,7 +25313,8 @@ function removeNestedTables(html) {
|
|
|
25083
25313
|
var THIN = ["0.12 mm", "#000000"];
|
|
25084
25314
|
var THICK = ["0.4 mm", "#000000"];
|
|
25085
25315
|
var DOUBLE = ["0.5 mm", "#000000", "DOUBLE_SLIM"];
|
|
25086
|
-
var
|
|
25316
|
+
var MID = ["0.15 mm", "#000000"];
|
|
25317
|
+
var EDGE = { thin: THIN, thick: THICK, double: DOUBLE, mid: MID };
|
|
25087
25318
|
function sideOf(kind) {
|
|
25088
25319
|
return kind === "none" ? void 0 : EDGE[kind];
|
|
25089
25320
|
}
|
|
@@ -25180,6 +25411,7 @@ function profileCharPrXml(id, def, namedFontId) {
|
|
|
25180
25411
|
let restFont = 0;
|
|
25181
25412
|
if (namedFontId != null) {
|
|
25182
25413
|
font = namedFontId;
|
|
25414
|
+
restFont = font;
|
|
25183
25415
|
} else {
|
|
25184
25416
|
const rawFont = def.fontRef_hangul != null ? parseInt(def.fontRef_hangul, 10) || 0 : 0;
|
|
25185
25417
|
font = rawFont >= 0 && rawFont <= LEGACY_PROFILE_FONT_MAX ? rawFont : 0;
|
|
@@ -25541,6 +25773,7 @@ function nextTableId() {
|
|
|
25541
25773
|
function resetTableIds() {
|
|
25542
25774
|
tableIdCounter = TABLE_ID_BASE;
|
|
25543
25775
|
}
|
|
25776
|
+
var curFace = "hcr";
|
|
25544
25777
|
var DATA_TABLE_INSET = 1800;
|
|
25545
25778
|
function anchorOfMarkdownCell(cell2) {
|
|
25546
25779
|
return normalizeAnchor(cell2.replace(/!\[[^\]]*\]\([^)]*\)/g, ""));
|
|
@@ -25560,7 +25793,7 @@ var CELL_PAD = 1200;
|
|
|
25560
25793
|
function cellContentWidth(text, charHeight) {
|
|
25561
25794
|
let max = 0;
|
|
25562
25795
|
for (const seg of text.replace(/!\[[^\]]*\]\([^)\s]+\)/g, "").replace(/\*\*|__|`/g, "").split(/<br\s*\/?>/i)) {
|
|
25563
|
-
const w = measureTextWidth(seg.trim(), charHeight, 100);
|
|
25796
|
+
const w = measureTextWidth(seg.trim(), charHeight, 100, { faceClass: curFace });
|
|
25564
25797
|
if (w > max) max = w;
|
|
25565
25798
|
}
|
|
25566
25799
|
return max;
|
|
@@ -25569,25 +25802,38 @@ function cellMinWordWidth(text, charHeight) {
|
|
|
25569
25802
|
let max = 0;
|
|
25570
25803
|
for (const seg of text.replace(/!\[[^\]]*\]\([^)\s]+\)/g, "").replace(/\*\*|__|`/g, "").split(/<br\s*\/?>/i)) {
|
|
25571
25804
|
for (const word of seg.trim().split(/\s+/)) {
|
|
25572
|
-
const w = measureTextWidth(word, charHeight, 100);
|
|
25805
|
+
const w = measureTextWidth(word, charHeight, 100, { faceClass: curFace });
|
|
25573
25806
|
if (w > max) max = w;
|
|
25574
25807
|
}
|
|
25575
25808
|
}
|
|
25576
25809
|
return max;
|
|
25577
25810
|
}
|
|
25578
|
-
|
|
25811
|
+
var REMARK_HEAD = /^(비고|비고란|비고사항|참고|참고사항|출처|(관련|법적|추진)?근거|근거법령|담당(자|부서)?|연락처)$/u;
|
|
25812
|
+
var CONTENT_HEAD = /(내용|사항|실적|계획|방안|사유|개요|설명|임무|역할|현황|성과)$/u;
|
|
25813
|
+
var REMARK_SHARE = 0.25;
|
|
25814
|
+
var CONTENT_WEIGHT = 2;
|
|
25815
|
+
function colRoles(headers) {
|
|
25816
|
+
const clean = headers.map((h) => (h ?? "").replace(/!\[[^\]]*\]\([^)\s]+\)/g, "").replace(/\*\*|__|`|\s+/g, ""));
|
|
25817
|
+
const roles = clean.map((h) => REMARK_HEAD.test(h) ? "remark" : h.length <= 12 && CONTENT_HEAD.test(h) ? "content" : null);
|
|
25818
|
+
const last = roles.length - 1;
|
|
25819
|
+
if (roles.length >= 3 && roles.includes("content") && roles[last] === null) roles[last] = "remark";
|
|
25820
|
+
return roles;
|
|
25821
|
+
}
|
|
25822
|
+
function computeColWidths(colMax, totalWidth, colMinWord = [], roles = []) {
|
|
25579
25823
|
const colCnt = colMax.length;
|
|
25580
25824
|
const minW = Math.min(Math.max(2e3, Math.round(totalWidth * 0.06)), Math.floor(totalWidth / colCnt));
|
|
25581
25825
|
const cap = Math.round(totalWidth * 0.8);
|
|
25582
|
-
const
|
|
25583
|
-
const
|
|
25826
|
+
const capOf = colMax.map((_, i) => roles[i] === "remark" ? Math.min(cap, Math.max(Math.round(totalWidth * REMARK_SHARE), (colMinWord[i] ?? 0) + CELL_PAD)) : cap);
|
|
25827
|
+
const raw = colMax.map((w, i) => Math.min(Math.max(w + CELL_PAD, minW), capOf[i]));
|
|
25828
|
+
const wgt = raw.map((r, i) => roles[i] === "content" ? r * CONTENT_WEIGHT : r);
|
|
25829
|
+
const floor = raw.map((r, i) => Math.min(Math.max(minW, (colMinWord[i] ?? 0) + CELL_PAD), r, capOf[i]));
|
|
25584
25830
|
const widths = Array(colCnt).fill(0);
|
|
25585
25831
|
const free = new Set(raw.map((_, i) => i));
|
|
25586
25832
|
let budget = totalWidth;
|
|
25587
25833
|
let sumFloorFree = floor.reduce((a, b) => a + b, 0);
|
|
25588
25834
|
for (const i of [...raw.keys()].sort((a, b) => raw[a] - raw[b])) {
|
|
25589
25835
|
if (free.size <= 1) break;
|
|
25590
|
-
const fixed = Math.round(raw[i] * 1.12);
|
|
25836
|
+
const fixed = roles[i] === "remark" ? Math.min(Math.round(raw[i] * 1.12), capOf[i]) : Math.round(raw[i] * 1.12);
|
|
25591
25837
|
if (budget - fixed < sumFloorFree - floor[i]) break;
|
|
25592
25838
|
widths[i] = fixed;
|
|
25593
25839
|
free.delete(i);
|
|
@@ -25597,29 +25843,30 @@ function computeColWidths(colMax, totalWidth, colMinWord = []) {
|
|
|
25597
25843
|
const sumFreeRaw = [...free].reduce((a, i) => a + raw[i], 0);
|
|
25598
25844
|
if (budget > sumFreeRaw * 1.6) {
|
|
25599
25845
|
const sumRaw = raw.reduce((a, b) => a + b, 0);
|
|
25600
|
-
|
|
25846
|
+
const sumWgt = wgt.reduce((a, b) => a + b, 0);
|
|
25847
|
+
for (let i = 0; i < colCnt; i++) widths[i] = Math.min(capOf[i], Math.round(raw[i]) + Math.floor(wgt[i] / sumWgt * (totalWidth - sumRaw)));
|
|
25601
25848
|
free.clear();
|
|
25602
25849
|
} else {
|
|
25603
25850
|
for (; ; ) {
|
|
25604
|
-
const sum2 = [...free].reduce((a, i) => a +
|
|
25605
|
-
const short = [...free].filter((i) =>
|
|
25851
|
+
const sum2 = [...free].reduce((a, i) => a + wgt[i], 0);
|
|
25852
|
+
const short = [...free].filter((i) => wgt[i] / sum2 * budget < floor[i]);
|
|
25606
25853
|
if (short.length === 0) break;
|
|
25607
25854
|
const shortSum = short.reduce((a, i) => a + floor[i], 0);
|
|
25608
|
-
const
|
|
25855
|
+
const scale2 = Math.min(1, (budget - (free.size - short.length) * minW) / shortSum);
|
|
25609
25856
|
for (const i of short) {
|
|
25610
|
-
widths[i] = Math.max(minW, Math.floor(floor[i] *
|
|
25857
|
+
widths[i] = Math.max(minW, Math.floor(floor[i] * scale2));
|
|
25611
25858
|
free.delete(i);
|
|
25612
25859
|
budget -= widths[i];
|
|
25613
25860
|
}
|
|
25614
25861
|
if (free.size === 0) break;
|
|
25615
25862
|
}
|
|
25616
|
-
const sum = [...free].reduce((a, i) => a +
|
|
25617
|
-
for (const i of free) widths[i] = Math.floor(
|
|
25863
|
+
const sum = [...free].reduce((a, i) => a + wgt[i], 0);
|
|
25864
|
+
for (const i of free) widths[i] = Math.min(capOf[i], Math.floor(wgt[i] / sum * budget));
|
|
25618
25865
|
}
|
|
25619
25866
|
let rem = totalWidth - widths.reduce((a, b) => a + b, 0);
|
|
25620
|
-
const order = [...raw.keys()].sort((a, b) =>
|
|
25867
|
+
const order = [...raw.keys()].sort((a, b) => wgt[b] - wgt[a]);
|
|
25621
25868
|
for (let k = 0, skipped = 0; rem > 0; k = (k + 1) % colCnt, rem--) {
|
|
25622
|
-
while (widths[order[k]] >=
|
|
25869
|
+
while (widths[order[k]] >= capOf[order[k]] && skipped < colCnt) {
|
|
25623
25870
|
skipped++;
|
|
25624
25871
|
k = (k + 1) % colCnt;
|
|
25625
25872
|
}
|
|
@@ -25643,13 +25890,26 @@ function estimateRowHeight(cells, widths, charHeight) {
|
|
|
25643
25890
|
const usable = Math.max((widths[c] ?? widths[widths.length - 1]) - CELL_PAD, 1e3);
|
|
25644
25891
|
let lines = 0;
|
|
25645
25892
|
for (const seg of cell2.replace(/\*\*|__|`/g, "").split(/<br\s*\/?>/i)) {
|
|
25646
|
-
lines += Math.max(1, Math.ceil(measureTextWidth(seg.trim(), charHeight, 100) / usable));
|
|
25893
|
+
lines += Math.max(1, Math.ceil(measureTextWidth(seg.trim(), charHeight, 100, { faceClass: curFace }) / usable));
|
|
25647
25894
|
}
|
|
25648
25895
|
if (lines > maxLines) maxLines = lines;
|
|
25649
25896
|
});
|
|
25650
25897
|
return maxLines * Math.round(charHeight * 1.6) + 282;
|
|
25651
25898
|
}
|
|
25899
|
+
function requiredTableWidth(rows, charHeight, faceClass = "hcr") {
|
|
25900
|
+
const prev = curFace;
|
|
25901
|
+
curFace = faceClass;
|
|
25902
|
+
const colCnt = Math.max(...rows.map((r) => r.length), 1);
|
|
25903
|
+
const colMax = Array(colCnt).fill(0);
|
|
25904
|
+
for (const row of rows) row.forEach((cell2, c) => {
|
|
25905
|
+
const w = cellContentWidth(cell2, charHeight);
|
|
25906
|
+
if (w > colMax[c]) colMax[c] = w;
|
|
25907
|
+
});
|
|
25908
|
+
curFace = prev;
|
|
25909
|
+
return colMax.reduce((a, w) => a + Math.round((w + CELL_PAD) * 1.12), 0);
|
|
25910
|
+
}
|
|
25652
25911
|
function generateTable(rows, theme, style = null, remap = null, seq = 0, images = null) {
|
|
25912
|
+
curFace = style?.faceClass ?? "hcr";
|
|
25653
25913
|
const rowCnt = rows.length;
|
|
25654
25914
|
const colCnt = Math.max(...rows.map((r) => r.length), 1);
|
|
25655
25915
|
const reg = style?.bfRegistry ?? null;
|
|
@@ -25667,8 +25927,9 @@ function generateTable(rows, theme, style = null, remap = null, seq = 0, images
|
|
|
25667
25927
|
const mw = cellMinWordWidth(cell2, measureH);
|
|
25668
25928
|
if (mw > colMinWord[c]) colMinWord[c] = mw;
|
|
25669
25929
|
}));
|
|
25670
|
-
const
|
|
25671
|
-
const
|
|
25930
|
+
const roles = style ? colRoles(rows[0] ?? []) : [];
|
|
25931
|
+
const colWidths = profileColWidths(prof, colCnt) ?? computeColWidths(colMax, totalW, colMinWord, roles);
|
|
25932
|
+
const colCentered = colWidths.map((w, c) => roles[c] !== "content" && colMaxBody[c] + CELL_PAD <= w);
|
|
25672
25933
|
const labelCol0 = !!reg && colCnt === 2 && colCentered[0] && rows.every((r) => (r[0] ?? "").replace(/\*\*|__|`/g, "").length <= 12);
|
|
25673
25934
|
const tblId = nextTableId();
|
|
25674
25935
|
const useHeaderStyle = !style && (theme.tableHeader !== theme.body || theme.tableHeaderBold);
|
|
@@ -25787,8 +26048,11 @@ function generateHtmlTableXml(rawHtml, theme, totalWidth = 44e3, style = null, r
|
|
|
25787
26048
|
if (dc === 0 && mw > colMinWord[c]) colMinWord[c] = mw;
|
|
25788
26049
|
}
|
|
25789
26050
|
});
|
|
25790
|
-
const
|
|
25791
|
-
const
|
|
26051
|
+
const headers = Array.from({ length: colCnt }, () => "");
|
|
26052
|
+
for (const [i, cell2] of placed.entries()) if (cell2.r === 0 && cell2.c < colCnt) headers[cell2.c] = unescapeHtml(cellLines[i].join(" "));
|
|
26053
|
+
const roles = style ? colRoles(headers) : [];
|
|
26054
|
+
const colWidths = profileColWidths(prof, colCnt) ?? computeColWidths(colMax, totalWidth, colMinWord, roles);
|
|
26055
|
+
const colCentered = colWidths.map((w, c) => roles[c] !== "content" && colMaxBody[c] + CELL_PAD <= w);
|
|
25792
26056
|
const cellH = style ? Math.round(measureH * 1.6) + 282 : 1500;
|
|
25793
26057
|
const tblW = colWidths.reduce((a, b) => a + b, 0);
|
|
25794
26058
|
const tblId = nextTableId();
|
|
@@ -25900,7 +26164,7 @@ function tbl(rows, w, h, cols) {
|
|
|
25900
26164
|
return `<hp:tbl id="${++extraTableId}" zOrder="0" numberingType="TABLE" textWrap="TOP_AND_BOTTOM" textFlow="BOTH_SIDES" lock="0" dropcapstyle="None" pageBreak="CELL" repeatHeader="0" rowCnt="${rows.length}" colCnt="${cols}" cellSpacing="0" borderFillIDRef="1" noAdjust="1"><hp:sz width="${w}" widthRelTo="ABSOLUTE" height="${h}" heightRelTo="ABSOLUTE" protect="0"/><hp:pos treatAsChar="1" affectLSpacing="0" flowWithText="1" allowOverlap="0" holdAnchorAndSO="0" vertRelTo="PARA" horzRelTo="PARA" vertAlign="TOP" horzAlign="LEFT" vertOffset="0" horzOffset="0"/><hp:outMargin left="283" right="283" top="283" bottom="283"/><hp:inMargin left="141" right="141" top="141" bottom="141"/>` + rows.map((r) => `<hp:tr>${r}</hp:tr>`).join("") + `</hp:tbl>`;
|
|
25901
26165
|
}
|
|
25902
26166
|
function tc(opts) {
|
|
25903
|
-
return `<hp:tc name="${opts.name ?? ""}" header="0" hasMargin="0" protect="0" editable="1" dirty="0" borderFillIDRef="${opts.bf}"><hp:subList id="" textDirection="HORIZONTAL" lineWrap="BREAK" vertAlign="CENTER" linkListIDRef="0" linkListNextIDRef="0" textWidth="0" textHeight="0" hasTextRef="0" hasNumRef="0">${opts.paras}</hp:subList><hp:cellAddr colAddr="${opts.col}" rowAddr="${opts.row}"/><hp:cellSpan colSpan="${opts.colSpan ?? 1}" rowSpan="1"/><hp:cellSz width="${opts.w}" height="${opts.h}"/><hp:cellMargin left="141" right="141" top="141" bottom="141"/></hp:tc>`;
|
|
26167
|
+
return `<hp:tc name="${opts.name ?? ""}" header="0" hasMargin="0" protect="0" editable="1" dirty="0" borderFillIDRef="${opts.bf}"><hp:subList id="" textDirection="HORIZONTAL" lineWrap="BREAK" vertAlign="${opts.vAlign ?? "CENTER"}" linkListIDRef="0" linkListNextIDRef="0" textWidth="0" textHeight="0" hasTextRef="0" hasNumRef="0">${opts.paras}</hp:subList><hp:cellAddr colAddr="${opts.col}" rowAddr="${opts.row}"/><hp:cellSpan colSpan="${opts.colSpan ?? 1}" rowSpan="${opts.rowSpan ?? 1}"/><hp:cellSz width="${opts.w}" height="${opts.h}"/><hp:cellMargin left="141" right="141" top="141" bottom="141"/></hp:tc>`;
|
|
25904
26168
|
}
|
|
25905
26169
|
function para(text, paraPrId, charPrId) {
|
|
25906
26170
|
return `<hp:p paraPrIDRef="${paraPrId}" styleIDRef="0"><hp:run charPrIDRef="${charPrId}"><hp:t>${escapeXml(text)}</hp:t></hp:run></hp:p>`;
|
|
@@ -25971,48 +26235,12 @@ function docframeCharPrXmls(base, richAssets) {
|
|
|
25971
26235
|
charPr(ids.pressContact, 1e3, false, false, body)
|
|
25972
26236
|
];
|
|
25973
26237
|
}
|
|
25974
|
-
function labeled(label, value, paraPrId) {
|
|
25975
|
-
return `<hp:p paraPrIDRef="${paraPrId}" styleIDRef="0"><hp:run charPrIDRef="${CHAR_BOLD}"><hp:t>${escapeXml(label)}</hp:t></hp:run><hp:run charPrIDRef="${CHAR_NORMAL}"><hp:t>${escapeXml(value)}</hp:t></hp:run></hp:p>`;
|
|
25976
|
-
}
|
|
25977
26238
|
function blank() {
|
|
25978
26239
|
return `<hp:p paraPrIDRef="${PARA_NORMAL}" styleIDRef="0"><hp:run charPrIDRef="${CHAR_NORMAL}"><hp:t></hp:t></hp:run></hp:p>`;
|
|
25979
26240
|
}
|
|
25980
|
-
function buildDocHead(g, ids) {
|
|
25981
|
-
const h = g.docHead;
|
|
25982
|
-
const out = [];
|
|
25983
|
-
if (h.org) {
|
|
25984
|
-
out.push(para(h.org, GONGMUN_CENTER, ids.org), blank());
|
|
25985
|
-
}
|
|
25986
|
-
if (h.to !== void 0) out.push(labeled("\uC218\uC2E0 ", h.to, PARA_NORMAL));
|
|
25987
|
-
out.push(para(h.via ? `(\uACBD\uC720) ${h.via}` : "(\uACBD\uC720)", PARA_NORMAL, CHAR_NORMAL));
|
|
25988
|
-
if (h.title !== void 0) out.push(labeled("\uC81C\uBAA9 ", h.title, PARA_NORMAL));
|
|
25989
|
-
out.push(blank());
|
|
25990
|
-
return out;
|
|
25991
|
-
}
|
|
25992
|
-
function footSep(g) {
|
|
25993
|
-
const bodyMm = 210 - g.margins.left - g.margins.right;
|
|
25994
|
-
return "\u2500".repeat(Math.max(10, Math.round(bodyMm * (46 / 175))));
|
|
25995
|
-
}
|
|
25996
26241
|
function isInternalApproval(to) {
|
|
25997
26242
|
return !!to && /^\s*내\s*부\s*결\s*재/.test(to);
|
|
25998
26243
|
}
|
|
25999
|
-
function buildDocFoot(g, ids) {
|
|
26000
|
-
const f = g.docFoot;
|
|
26001
|
-
const out = [blank()];
|
|
26002
|
-
if (f.sender && !isInternalApproval(g.docHead?.to)) {
|
|
26003
|
-
out.push(para(f.sender, GONGMUN_CENTER, ids.sender), blank());
|
|
26004
|
-
}
|
|
26005
|
-
const foot = (text) => para(text, GONGMUN_TBL_LEFT, ids.foot);
|
|
26006
|
-
out.push(foot(footSep(g)));
|
|
26007
|
-
if (f.drafter || f.reviewer || f.approver)
|
|
26008
|
-
out.push(foot(`\uAE30\uC548\uC790 ${f.drafter ?? ""} \uAC80\uD1A0\uC790 ${f.reviewer ?? ""} \uACB0\uC7AC\uAD8C\uC790 ${f.approver ?? ""}`));
|
|
26009
|
-
if (f.cooperator) out.push(foot(`\uD611\uC870\uC790 ${f.cooperator}`));
|
|
26010
|
-
if (f.docNum || f.receive) out.push(foot(`\uC2DC\uD589 ${f.docNum ?? ""} \uC811\uC218 ${f.receive ?? ""}`));
|
|
26011
|
-
if (f.address || f.site) out.push(foot(`${f.address ?? ""} / ${f.site ?? ""}`));
|
|
26012
|
-
if (f.phone || f.fax || f.email || f.disclosure)
|
|
26013
|
-
out.push(foot(`\uC804\uD654 ${f.phone ?? ""} \uC804\uC1A1 ${f.fax ?? ""} / ${f.email ?? ""} / ${f.disclosure ?? ""}`));
|
|
26014
|
-
return out;
|
|
26015
|
-
}
|
|
26016
26244
|
function buildReportInfo(g, ids) {
|
|
26017
26245
|
return para(g.reportInfo, GONGMUN_RIGHT, ids.small);
|
|
26018
26246
|
}
|
|
@@ -26471,9 +26699,6 @@ function buildPreamble(blocks, ctx) {
|
|
|
26471
26699
|
if (gongmun?.approval && bfReg) {
|
|
26472
26700
|
preamble.push(buildApprovalTable(gongmun.approval, bfReg, richAssets ? GJ_CHAR_APPROVAL : GONGMUN_APPROVAL_CHAR));
|
|
26473
26701
|
}
|
|
26474
|
-
if (gongmun?.docHead && dfIds) {
|
|
26475
|
-
preamble.push(...buildDocHead(gongmun, dfIds));
|
|
26476
|
-
}
|
|
26477
26702
|
if (gongmun?.noticeHead && dfIds) {
|
|
26478
26703
|
preamble.push(...buildNoticeHead(gongmun));
|
|
26479
26704
|
}
|
|
@@ -26649,6 +26874,11 @@ function renderListItem(block, blockIdx, ctx) {
|
|
|
26649
26874
|
mapId = void 0;
|
|
26650
26875
|
}
|
|
26651
26876
|
}
|
|
26877
|
+
const lv = ctx.levelIds?.[depth];
|
|
26878
|
+
if (lv) {
|
|
26879
|
+
listCharPr = lv.normal;
|
|
26880
|
+
mapId = (id) => id === CHAR_BOLD ? lv.bold : id === CHAR_NORMAL ? lv.normal : id;
|
|
26881
|
+
}
|
|
26652
26882
|
return generateParagraph(text, listParaPr, listCharPr, mapId);
|
|
26653
26883
|
}
|
|
26654
26884
|
const indent = block.indent || 0;
|
|
@@ -26689,8 +26919,8 @@ function renderHtmlTable(block, ctx) {
|
|
|
26689
26919
|
ctx.opener.emitCarrier(ctx.paraXmls);
|
|
26690
26920
|
return `<hp:p paraPrIDRef="${tableStyle && bfReg ? GONGMUN_RIGHT : 0}" styleIDRef="0"><hp:run charPrIDRef="0">${tbl2}</hp:run></hp:p>`;
|
|
26691
26921
|
}
|
|
26692
|
-
const
|
|
26693
|
-
return
|
|
26922
|
+
const plain3 = (block.text || "").replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
|
|
26923
|
+
return plain3 ? generateParagraph(plain3) : "";
|
|
26694
26924
|
}
|
|
26695
26925
|
function appendPostamble(blocks, ctx) {
|
|
26696
26926
|
const { gongmun, dfIds, bfReg, tableStyle, paraXmls } = ctx;
|
|
@@ -26702,14 +26932,11 @@ function appendPostamble(blocks, ctx) {
|
|
|
26702
26932
|
const lastText = lastBlock && (lastBlock.type === "paragraph" || lastBlock.type === "list_item") ? lastBlock.text || "" : "";
|
|
26703
26933
|
if (!hasEndMark(lastText)) paraXmls.push(buildEndMark());
|
|
26704
26934
|
}
|
|
26705
|
-
if (gongmun?.docFoot && dfIds) {
|
|
26706
|
-
paraXmls.push(...buildDocFoot(gongmun, dfIds));
|
|
26707
|
-
}
|
|
26708
26935
|
if (gongmun?.press && dfIds && bfReg && tableStyle) {
|
|
26709
26936
|
paraXmls.push(...buildPressContact(gongmun, dfIds, tableStyle.totalWidth, bfReg));
|
|
26710
26937
|
}
|
|
26711
26938
|
}
|
|
26712
|
-
function blocksToSectionXml(blocks, theme, gongmun, gongmunList = gongmun ? precomputeGongmunList(blocks, gongmun) : null, fit = null, chartParts = null, bfReg = null, remap = null, dfIds = null, images = null, page = null) {
|
|
26939
|
+
function blocksToSectionXml(blocks, theme, gongmun, gongmunList = gongmun ? precomputeGongmunList(blocks, gongmun) : null, fit = null, chartParts = null, bfReg = null, remap = null, dfIds = null, images = null, page = null, levelIds = null) {
|
|
26713
26940
|
resetTableIds();
|
|
26714
26941
|
resetGjTableIds();
|
|
26715
26942
|
resetExtraTableIds();
|
|
@@ -26725,6 +26952,7 @@ function blocksToSectionXml(blocks, theme, gongmun, gongmunList = gongmun ? prec
|
|
|
26725
26952
|
remap,
|
|
26726
26953
|
dfIds,
|
|
26727
26954
|
images,
|
|
26955
|
+
levelIds,
|
|
26728
26956
|
gaejosik: gongmun?.preset === "gaejosik",
|
|
26729
26957
|
measured,
|
|
26730
26958
|
richAssets,
|
|
@@ -26829,6 +27057,1083 @@ function blocksToSectionXml(blocks, theme, gongmun, gongmunList = gongmun ? prec
|
|
|
26829
27057
|
</hs:sec>`;
|
|
26830
27058
|
}
|
|
26831
27059
|
|
|
27060
|
+
// src/hwpx/gen-levels.ts
|
|
27061
|
+
function levelDepths(g) {
|
|
27062
|
+
return g.levels ? Object.keys(g.levels).map(Number).sort((a, b) => a - b) : [];
|
|
27063
|
+
}
|
|
27064
|
+
function levelFontFaces(g) {
|
|
27065
|
+
const out = [];
|
|
27066
|
+
for (const d of levelDepths(g)) {
|
|
27067
|
+
const f = g.levels[d].font;
|
|
27068
|
+
if (f && !out.includes(f)) out.push(f);
|
|
27069
|
+
}
|
|
27070
|
+
return out;
|
|
27071
|
+
}
|
|
27072
|
+
function levelCharIds(g, base) {
|
|
27073
|
+
const depths = levelDepths(g);
|
|
27074
|
+
if (depths.length === 0) return null;
|
|
27075
|
+
const ids = {};
|
|
27076
|
+
depths.forEach((d, i) => {
|
|
27077
|
+
ids[d] = { normal: base + i * 2, bold: base + i * 2 + 1 };
|
|
27078
|
+
});
|
|
27079
|
+
return ids;
|
|
27080
|
+
}
|
|
27081
|
+
function levelCharPrXmls(g, base, fontBase, bodyFontId) {
|
|
27082
|
+
const ids = levelCharIds(g, base);
|
|
27083
|
+
if (!ids) return [];
|
|
27084
|
+
const faces = levelFontFaces(g);
|
|
27085
|
+
const out = [];
|
|
27086
|
+
for (const d of levelDepths(g)) {
|
|
27087
|
+
const st = g.levels[d];
|
|
27088
|
+
const fontId = st.font ? fontBase + faces.indexOf(st.font) : bodyFontId;
|
|
27089
|
+
out.push(charPr(ids[d].normal, st.height, st.bold, false, fontId, void 0, GONGMUN_BODY_RATIO, fontId));
|
|
27090
|
+
out.push(charPr(ids[d].bold, st.height, true, false, fontId, void 0, GONGMUN_BODY_RATIO, fontId));
|
|
27091
|
+
}
|
|
27092
|
+
return out;
|
|
27093
|
+
}
|
|
27094
|
+
|
|
27095
|
+
// src/hwpx/style-registry.ts
|
|
27096
|
+
var StyleRegistry = class {
|
|
27097
|
+
/**
|
|
27098
|
+
* @param charBase 첫 charPr id (정적 charPr 블록 다음)
|
|
27099
|
+
* @param paraBase 첫 paraPr id
|
|
27100
|
+
* @param fontBase 첫 append 글꼴 id (정적 fontface 다음 — HANGUL·LATIN에만 append)
|
|
27101
|
+
* @param staticFonts 정적 fontface 목록 (id 순) — 같은 이름이면 정적 id 재사용
|
|
27102
|
+
*/
|
|
27103
|
+
constructor(charBase, paraBase, fontBase, staticFonts = []) {
|
|
27104
|
+
this.charBase = charBase;
|
|
27105
|
+
this.paraBase = paraBase;
|
|
27106
|
+
this.fontBase = fontBase;
|
|
27107
|
+
staticFonts.forEach((f, i) => {
|
|
27108
|
+
if (!this.fonts.has(f)) this.fonts.set(f, i);
|
|
27109
|
+
});
|
|
27110
|
+
}
|
|
27111
|
+
chars = /* @__PURE__ */ new Map();
|
|
27112
|
+
charXmls = [];
|
|
27113
|
+
paras = /* @__PURE__ */ new Map();
|
|
27114
|
+
paraXmls = [];
|
|
27115
|
+
fonts = /* @__PURE__ */ new Map();
|
|
27116
|
+
fontList = [];
|
|
27117
|
+
font(face) {
|
|
27118
|
+
const hit = this.fonts.get(face);
|
|
27119
|
+
if (hit !== void 0) return hit;
|
|
27120
|
+
const id = this.fontBase + this.fontList.length;
|
|
27121
|
+
this.fonts.set(face, id);
|
|
27122
|
+
this.fontList.push(face);
|
|
27123
|
+
return id;
|
|
27124
|
+
}
|
|
27125
|
+
char(spec) {
|
|
27126
|
+
const key = `${spec.font}|${spec.pt}|${spec.bold ? 1 : 0}|${spec.italic ? 1 : 0}|${spec.ratio ?? 100}|${spec.spacing ?? 0}|${spec.color ?? "#000000"}`;
|
|
27127
|
+
const hit = this.chars.get(key);
|
|
27128
|
+
if (hit !== void 0) return hit;
|
|
27129
|
+
const id = this.charBase + this.charXmls.length;
|
|
27130
|
+
const fontId = this.font(spec.font);
|
|
27131
|
+
this.charXmls.push(charPr(id, Math.round(spec.pt * 100), !!spec.bold, !!spec.italic, fontId, spec.color ?? "#000000", spec.ratio ?? 100, fontId, spec.spacing ?? 0));
|
|
27132
|
+
this.chars.set(key, id);
|
|
27133
|
+
return id;
|
|
27134
|
+
}
|
|
27135
|
+
para(spec) {
|
|
27136
|
+
const key = `${spec.align ?? "JUSTIFY"}|${spec.left ?? 0}|${spec.right ?? 0}|${spec.indent ?? 0}|${spec.before ?? 0}|${spec.after ?? 0}|${spec.lineSp ?? 160}|${spec.keepWithNext ? 1 : 0}|${spec.keepWord === false ? 0 : 1}`;
|
|
27137
|
+
const hit = this.paras.get(key);
|
|
27138
|
+
if (hit !== void 0) return hit;
|
|
27139
|
+
const id = this.paraBase + this.paraXmls.length;
|
|
27140
|
+
this.paraXmls.push(paraPr(id, {
|
|
27141
|
+
align: spec.align ?? "JUSTIFY",
|
|
27142
|
+
left: spec.left ?? 0,
|
|
27143
|
+
right: spec.right ?? 0,
|
|
27144
|
+
indent: spec.indent ?? 0,
|
|
27145
|
+
spaceBefore: spec.before ?? 0,
|
|
27146
|
+
spaceAfter: spec.after ?? 0,
|
|
27147
|
+
lineSpacing: spec.lineSp ?? 160,
|
|
27148
|
+
keepWithNext: !!spec.keepWithNext,
|
|
27149
|
+
keepWord: spec.keepWord !== false
|
|
27150
|
+
}));
|
|
27151
|
+
this.paras.set(key, id);
|
|
27152
|
+
return id;
|
|
27153
|
+
}
|
|
27154
|
+
get charPrXmls() {
|
|
27155
|
+
return this.charXmls;
|
|
27156
|
+
}
|
|
27157
|
+
get paraPrXmls() {
|
|
27158
|
+
return this.paraXmls;
|
|
27159
|
+
}
|
|
27160
|
+
/** append 글꼴(정적 목록 제외) — fontBase부터 순서대로 */
|
|
27161
|
+
get extraFonts() {
|
|
27162
|
+
return this.fontList;
|
|
27163
|
+
}
|
|
27164
|
+
};
|
|
27165
|
+
|
|
27166
|
+
// src/hwpx/gongmun-scheme.ts
|
|
27167
|
+
var SEOUL_FRAME = {
|
|
27168
|
+
font: "\uAD74\uB9BC\uCCB4",
|
|
27169
|
+
titleFont: "HY\uD5E4\uB4DC\uB77C\uC778M",
|
|
27170
|
+
titlePt: 25,
|
|
27171
|
+
contactFont: "\uD734\uBA3C\uBA85\uC870",
|
|
27172
|
+
contactPt: 12,
|
|
27173
|
+
summaryFont: "\uD55C\uCEF4\uB3CB\uC6C0",
|
|
27174
|
+
summaryPt: 15,
|
|
27175
|
+
summaryFill: "#DFE6F7"
|
|
27176
|
+
};
|
|
27177
|
+
var SEOUL_TABLE = { font: "\uD55C\uCEF4\uB3CB\uC6C0", pt: 12, headerFill: "#DFE6F7", labelFill: "#F2F2F2" };
|
|
27178
|
+
function gaejosikMarkerOf(bullet2) {
|
|
27179
|
+
return (depth) => depth === 0 ? "\u25A1" : depth === 1 ? bullet2 : depth === 2 ? "-" : "\u318D";
|
|
27180
|
+
}
|
|
27181
|
+
function seoulGaejosikScheme(bodyPt = 15, lineSp = 180, bullet2 = "\u3147") {
|
|
27182
|
+
const d = bodyPt - 15;
|
|
27183
|
+
const lv = (font, pt2, bold, leadTa, extra = {}) => ({ font, pt: pt2 + d, bold, leadTa, ...extra });
|
|
27184
|
+
return {
|
|
27185
|
+
kind: "gaejosik",
|
|
27186
|
+
lineSp,
|
|
27187
|
+
body: lv("\uD55C\uCEF4\uB3CB\uC6C0", 15, false, 0),
|
|
27188
|
+
levels: [
|
|
27189
|
+
lv("HY\uACAC\uACE0\uB515", 17, true, 0, { oneLine: true, keepWithNext: true, blankBefore: true }),
|
|
27190
|
+
lv("\uD55C\uCEF4\uB3CB\uC6C0", 15, true, 1),
|
|
27191
|
+
lv("\uD734\uBA3C\uBA85\uC870", 14, false, 3),
|
|
27192
|
+
lv("\uD734\uBA3C\uBA85\uC870", 14, false, 4),
|
|
27193
|
+
lv("\uD734\uBA3C\uBA85\uC870", 14, false, 5),
|
|
27194
|
+
lv("\uD734\uBA3C\uBA85\uC870", 14, false, 6),
|
|
27195
|
+
lv("\uD734\uBA3C\uBA85\uC870", 14, false, 7),
|
|
27196
|
+
lv("\uD734\uBA3C\uBA85\uC870", 14, false, 8)
|
|
27197
|
+
],
|
|
27198
|
+
// ※·출처는 본문보다 작게(실측 14~15 혼재, 실무자 요청으로 13 — 한 단계 아래로 확실히 구분)
|
|
27199
|
+
ref: lv("\uD55C\uCEF4\uB3CB\uC6C0", 13, false, 0),
|
|
27200
|
+
sub: lv("\uD55C\uCEF4\uB3CB\uC6C0", 13, false, 2),
|
|
27201
|
+
attach: lv("\uD55C\uCEF4\uB3CB\uC6C0", 15, false, 0),
|
|
27202
|
+
chapter: lv("HY\uD5E4\uB4DC\uB77C\uC778M", 20, false, 0, { oneLine: true, keepWithNext: true }),
|
|
27203
|
+
marker: gaejosikMarkerOf(bullet2),
|
|
27204
|
+
table: { ...SEOUL_TABLE, pt: SEOUL_TABLE.pt + d },
|
|
27205
|
+
frame: SEOUL_FRAME,
|
|
27206
|
+
blankBetweenTop: false
|
|
27207
|
+
};
|
|
27208
|
+
}
|
|
27209
|
+
function seoulLegalScheme(bodyFont = "\uAD74\uB9BC\uCCB4", bodyPt = 12, lineSp = 160) {
|
|
27210
|
+
const lv = (leadTa, extra = {}) => ({ font: bodyFont, pt: bodyPt, bold: false, leadTa, ...extra });
|
|
27211
|
+
return {
|
|
27212
|
+
kind: "legal",
|
|
27213
|
+
lineSp,
|
|
27214
|
+
body: lv(0),
|
|
27215
|
+
levels: [0, 2, 4, 6, 8, 10, 12, 14].map((t) => lv(t)),
|
|
27216
|
+
ref: lv(0),
|
|
27217
|
+
sub: lv(2),
|
|
27218
|
+
attach: lv(0),
|
|
27219
|
+
chapter: { font: bodyFont, pt: bodyPt + 2, bold: true, leadTa: 0, keepWithNext: true },
|
|
27220
|
+
marker: (depth, n) => standardMarker(depth, n),
|
|
27221
|
+
table: { ...SEOUL_TABLE, pt: Math.min(SEOUL_TABLE.pt, bodyPt) },
|
|
27222
|
+
frame: SEOUL_FRAME,
|
|
27223
|
+
blankBetweenTop: false
|
|
27224
|
+
};
|
|
27225
|
+
}
|
|
27226
|
+
function applySchemeOverrides(s, g) {
|
|
27227
|
+
const out = { ...s, levels: s.levels.map((l) => ({ ...l })), body: { ...s.body }, ref: { ...s.ref }, table: { ...s.table } };
|
|
27228
|
+
if (g.fonts.heading) {
|
|
27229
|
+
out.levels[0].font = g.fonts.heading;
|
|
27230
|
+
out.chapter = { ...out.chapter, font: g.fonts.heading };
|
|
27231
|
+
}
|
|
27232
|
+
if (g.fonts.body) {
|
|
27233
|
+
out.body.font = g.fonts.body;
|
|
27234
|
+
if (s.kind === "legal") for (const l of out.levels) l.font = g.fonts.body;
|
|
27235
|
+
else out.levels[1].font = g.fonts.body;
|
|
27236
|
+
}
|
|
27237
|
+
if (g.fonts.ref) out.ref.font = g.fonts.ref;
|
|
27238
|
+
if (g.fonts.table) out.table.font = g.fonts.table;
|
|
27239
|
+
if (g.levels) {
|
|
27240
|
+
for (const [k, st] of Object.entries(g.levels)) {
|
|
27241
|
+
const l = out.levels[Number(k)];
|
|
27242
|
+
if (!l) continue;
|
|
27243
|
+
if (st.font) l.font = st.font;
|
|
27244
|
+
if (st.height !== g.bodyHeight) l.pt = st.height / 100;
|
|
27245
|
+
l.bold = st.bold;
|
|
27246
|
+
}
|
|
27247
|
+
}
|
|
27248
|
+
return out;
|
|
27249
|
+
}
|
|
27250
|
+
function taHu(pt2) {
|
|
27251
|
+
return Math.round(pt2 * 50);
|
|
27252
|
+
}
|
|
27253
|
+
function levelGeometry(style, marker) {
|
|
27254
|
+
const left = style.leadTa * taHu(style.pt);
|
|
27255
|
+
const indent = marker ? -markerWidth(marker, style.pt * 100) : 0;
|
|
27256
|
+
return { left, indent };
|
|
27257
|
+
}
|
|
27258
|
+
function pickScheme(g, bodyHasBoxMarkers) {
|
|
27259
|
+
const gaejosik = g.numbering !== "standard" || bodyHasBoxMarkers;
|
|
27260
|
+
const base = gaejosik ? seoulGaejosikScheme(g.bodyPtExplicit ? g.bodyHeight / 100 : 15, g.lineSpacingExplicit ? g.lineSpacing : 180, g.bullet2) : seoulLegalScheme(g.fonts.body ?? (g.bodyFontExplicit ? g.bodyFont === "gothic" ? "\uB9D1\uC740 \uACE0\uB515" : "\uD568\uCD08\uB86C\uBC14\uD0D5" : "\uAD74\uB9BC\uCCB4"), g.bodyPtExplicit ? g.bodyHeight / 100 : 12, g.lineSpacingExplicit ? g.lineSpacing : 160);
|
|
27261
|
+
return applySchemeOverrides(base, g);
|
|
27262
|
+
}
|
|
27263
|
+
|
|
27264
|
+
// src/hwpx/outline.ts
|
|
27265
|
+
var BOX_MARKERS = {
|
|
27266
|
+
"\u25A1": 0,
|
|
27267
|
+
"\u25A0": 0,
|
|
27268
|
+
"\u2751": 0,
|
|
27269
|
+
"\u274F": 0,
|
|
27270
|
+
"\u3141": 0,
|
|
27271
|
+
"\u25CB": 1,
|
|
27272
|
+
"\u3147": 1,
|
|
27273
|
+
"\u25E6": 1,
|
|
27274
|
+
"\u25CF": 1,
|
|
27275
|
+
"\u274D": 1,
|
|
27276
|
+
"\u25CE": 1,
|
|
27277
|
+
"-": 2,
|
|
27278
|
+
"\u2013": 2,
|
|
27279
|
+
"\u2015": 2,
|
|
27280
|
+
"\u2014": 2,
|
|
27281
|
+
"\u3161": 2,
|
|
27282
|
+
"\u2023": 2,
|
|
27283
|
+
"\u25AA": 2,
|
|
27284
|
+
"\u25AB": 2,
|
|
27285
|
+
"\u318D": 3,
|
|
27286
|
+
"\xB7": 3,
|
|
27287
|
+
"\u2022": 3,
|
|
27288
|
+
"\u2219": 3
|
|
27289
|
+
};
|
|
27290
|
+
var LEGAL_RE = /^(\d{1,2}\.|[가-힣]\.|\d{1,2}\)|[가-힣]\)|\(\d{1,2}\)|\([가-힣]\)|[①-⑳]|[㉮-㉻])\s+/u;
|
|
27291
|
+
var BOX_RE = /^([□■❑❏ㅁ○ㅇ◦●❍◎\-–―—ㅡ‣▪▫ㆍ·•∙])\s+/u;
|
|
27292
|
+
var REF_RE = /^(※|*|\*(?=\s))\s*/u;
|
|
27293
|
+
var BUNIM_RE = /^붙\s*임(?:\s|:|$)/;
|
|
27294
|
+
var SOURCE_RE = /^(출처|자료|근거|참고)\s*[::]/;
|
|
27295
|
+
var LAW_CODE_RE = /((?:법률|기본법|특별법|법|령|규칙|조례|규정|고시|훈령|예규|지침)[」』]?)\s*\((\d{5,8})\)/gu;
|
|
27296
|
+
function stripLawCodes(text) {
|
|
27297
|
+
return text.replace(LAW_CODE_RE, "$1").replace(/[,,]?\s*\(?\bDT_[A-Z0-9_]+\)?/g, "").replace(/\s*\((?:법정동|행정동|시군구|지역)?\s*코드\s*[\d-]+\)/g, "").replace(/[,,]?\s*[가-힣A-Za-z·]*\s*MCP\s*(?:조회|호출|응답)?/g, "").replace(/\s+([,,.])/g, "$1").replace(/[,,]\s*$/g, "").replace(/ {2,}/g, " ").trim();
|
|
27298
|
+
}
|
|
27299
|
+
function legalMarkerDepth(marker) {
|
|
27300
|
+
if (/^\d{1,2}\.$/.test(marker)) return 0;
|
|
27301
|
+
if (/^[가-힣]\.$/.test(marker)) return 1;
|
|
27302
|
+
if (/^\d{1,2}\)$/.test(marker)) return 2;
|
|
27303
|
+
if (/^[가-힣]\)$/.test(marker)) return 3;
|
|
27304
|
+
if (/^\(\d{1,2}\)$/.test(marker)) return 4;
|
|
27305
|
+
if (/^\([가-힣]\)$/.test(marker)) return 5;
|
|
27306
|
+
if (/^[①-⑳]$/u.test(marker)) return 6;
|
|
27307
|
+
return 7;
|
|
27308
|
+
}
|
|
27309
|
+
function parseLeadingMarker(text) {
|
|
27310
|
+
const t = text.replace(/^[\s ]+/, "");
|
|
27311
|
+
const ref = REF_RE.exec(t);
|
|
27312
|
+
if (ref) return { kind: "ref", depth: 0, marker: ref[1], rest: t.slice(ref[0].length).trim() };
|
|
27313
|
+
const box = BOX_RE.exec(t);
|
|
27314
|
+
if (box) return { kind: "box", depth: BOX_MARKERS[box[1]] ?? 3, marker: box[1], rest: t.slice(box[0].length).trim() };
|
|
27315
|
+
const legal = LEGAL_RE.exec(t);
|
|
27316
|
+
if (legal) return { kind: "legal", depth: legalMarkerDepth(legal[1]), marker: legal[1], rest: t.slice(legal[0].length).trim() };
|
|
27317
|
+
return { kind: null, depth: 0, marker: "", rest: t };
|
|
27318
|
+
}
|
|
27319
|
+
function buildOutline(blocks, opts) {
|
|
27320
|
+
const nodes = [];
|
|
27321
|
+
let title = null;
|
|
27322
|
+
let chapters = 0;
|
|
27323
|
+
let headingDepth = -1;
|
|
27324
|
+
let lastItemDepth = -1;
|
|
27325
|
+
let seenBody = false;
|
|
27326
|
+
let hasBoxMarkers = false;
|
|
27327
|
+
const pushItem = (depth, text, legalMarker, fromHeading = false) => {
|
|
27328
|
+
nodes.push({ kind: "item", depth, text, ...legalMarker ? { legalMarker } : {}, ...fromHeading ? { fromHeading: true } : {} });
|
|
27329
|
+
lastItemDepth = depth;
|
|
27330
|
+
seenBody = true;
|
|
27331
|
+
};
|
|
27332
|
+
for (let i = 0; i < blocks.length; i++) {
|
|
27333
|
+
const b = blocks[i];
|
|
27334
|
+
switch (b.type) {
|
|
27335
|
+
case "heading": {
|
|
27336
|
+
const lvl = b.level ?? 1;
|
|
27337
|
+
const raw = stripLawCodes((b.text ?? "").trim());
|
|
27338
|
+
if (lvl === 1 && title === null && opts.consumeTitle) {
|
|
27339
|
+
title = stripChapterNumber(raw) || raw;
|
|
27340
|
+
break;
|
|
27341
|
+
}
|
|
27342
|
+
if (lvl <= 2) {
|
|
27343
|
+
nodes.push({ kind: "chapter", text: stripChapterNumber(raw) || raw, index: ++chapters });
|
|
27344
|
+
headingDepth = opts.gaejosik ? -1 : 0;
|
|
27345
|
+
lastItemDepth = opts.gaejosik ? -1 : 0;
|
|
27346
|
+
seenBody = true;
|
|
27347
|
+
break;
|
|
27348
|
+
}
|
|
27349
|
+
const depth = Math.min(lvl - 3, 7);
|
|
27350
|
+
const lm = parseLeadingMarker(raw);
|
|
27351
|
+
pushItem(depth, lm.kind ? lm.rest : raw, void 0, true);
|
|
27352
|
+
headingDepth = depth;
|
|
27353
|
+
break;
|
|
27354
|
+
}
|
|
27355
|
+
case "list_item": {
|
|
27356
|
+
const text = stripLawCodes((b.text ?? "").trim());
|
|
27357
|
+
const lm = parseLeadingMarker(text);
|
|
27358
|
+
if (SOURCE_RE.test(lm.kind === "box" || lm.kind === "legal" ? lm.rest : text)) {
|
|
27359
|
+
nodes.push({ kind: "ref", depth: lastItemDepth + 1, text: lm.kind ? lm.rest : text });
|
|
27360
|
+
break;
|
|
27361
|
+
}
|
|
27362
|
+
if (b.marker === "*" && !/^\*/.test(text) && opts.gaejosik) {
|
|
27363
|
+
nodes.push({ kind: "ref", depth: lastItemDepth + 1, text });
|
|
27364
|
+
break;
|
|
27365
|
+
}
|
|
27366
|
+
if (lm.kind === "ref") {
|
|
27367
|
+
nodes.push({ kind: "ref", depth: lastItemDepth + 1, text: lm.rest });
|
|
27368
|
+
break;
|
|
27369
|
+
}
|
|
27370
|
+
if (lm.kind === "box") {
|
|
27371
|
+
hasBoxMarkers = true;
|
|
27372
|
+
pushItem(lm.depth, lm.rest);
|
|
27373
|
+
break;
|
|
27374
|
+
}
|
|
27375
|
+
if (lm.kind === "legal" && opts.gaejosik) {
|
|
27376
|
+
pushItem(Math.max(lastItemDepth, 0), lm.rest, lm.marker);
|
|
27377
|
+
break;
|
|
27378
|
+
}
|
|
27379
|
+
const base = headingDepth >= 0 ? headingDepth + 1 : 0;
|
|
27380
|
+
pushItem(Math.min(base + (b.indent ?? 0), 7), lm.kind === "legal" ? lm.rest : text);
|
|
27381
|
+
break;
|
|
27382
|
+
}
|
|
27383
|
+
case "paragraph": {
|
|
27384
|
+
const text = stripLawCodes((b.text ?? "").trim());
|
|
27385
|
+
if (!text) break;
|
|
27386
|
+
if (BUNIM_RE.test(text) || nodes.length && nodes[nodes.length - 1].kind === "attach" && /^\s/.test(b.text ?? "")) {
|
|
27387
|
+
nodes.push({ kind: "attach", text: b.text ?? "" });
|
|
27388
|
+
break;
|
|
27389
|
+
}
|
|
27390
|
+
const ctr = /^<center>([\s\S]*)<\/center>$/i.exec(text);
|
|
27391
|
+
if (ctr) {
|
|
27392
|
+
nodes.push({ kind: "para", depth: 0, text: ctr[1].trim(), align: "CENTER" });
|
|
27393
|
+
break;
|
|
27394
|
+
}
|
|
27395
|
+
const rgt = /^<right>([\s\S]*)<\/right>$/i.exec(text);
|
|
27396
|
+
if (rgt) {
|
|
27397
|
+
nodes.push({ kind: "para", depth: 0, text: rgt[1].trim(), align: "RIGHT" });
|
|
27398
|
+
break;
|
|
27399
|
+
}
|
|
27400
|
+
const lm = parseLeadingMarker(text);
|
|
27401
|
+
if (lm.kind === "ref") {
|
|
27402
|
+
nodes.push({ kind: "ref", depth: lastItemDepth + 1, text: lm.rest });
|
|
27403
|
+
break;
|
|
27404
|
+
}
|
|
27405
|
+
if (SOURCE_RE.test(lm.kind ? lm.rest : text)) {
|
|
27406
|
+
nodes.push({ kind: "ref", depth: lastItemDepth + 1, text: lm.kind ? lm.rest : text });
|
|
27407
|
+
break;
|
|
27408
|
+
}
|
|
27409
|
+
if (lm.kind === "box") {
|
|
27410
|
+
hasBoxMarkers = true;
|
|
27411
|
+
pushItem(lm.depth, lm.rest);
|
|
27412
|
+
break;
|
|
27413
|
+
}
|
|
27414
|
+
if (lm.kind === "legal") {
|
|
27415
|
+
if (opts.gaejosik) pushItem(Math.max(lastItemDepth, 0), lm.rest, lm.marker);
|
|
27416
|
+
else pushItem(lm.depth, lm.rest);
|
|
27417
|
+
break;
|
|
27418
|
+
}
|
|
27419
|
+
nodes.push({ kind: "para", depth: 0, text });
|
|
27420
|
+
seenBody = true;
|
|
27421
|
+
break;
|
|
27422
|
+
}
|
|
27423
|
+
case "blockquote": {
|
|
27424
|
+
const lines = stripLawCodes(b.text ?? "").split("\n").map((l) => l.trim()).filter(Boolean);
|
|
27425
|
+
if (!lines.length) break;
|
|
27426
|
+
if (opts.summaryFromQuote && !seenBody) nodes.push({ kind: "summary", text: lines.join("\n") });
|
|
27427
|
+
else nodes.push({ kind: "ref", depth: lastItemDepth + 1, text: lines.join(" ").replace(/^※\s*/, "") });
|
|
27428
|
+
break;
|
|
27429
|
+
}
|
|
27430
|
+
default:
|
|
27431
|
+
nodes.push({ kind: "block", depth: lastItemDepth, block: b });
|
|
27432
|
+
seenBody = true;
|
|
27433
|
+
}
|
|
27434
|
+
}
|
|
27435
|
+
return { title, nodes, hasBoxMarkers, chapters };
|
|
27436
|
+
}
|
|
27437
|
+
|
|
27438
|
+
// src/hwpx/fit-line.ts
|
|
27439
|
+
var RATIOS = [100, 97, 95, 92, 90, 87, 85];
|
|
27440
|
+
var SPACINGS = [0, -3, -5];
|
|
27441
|
+
function fitOneLine(text, font, pt2, availHu, minPt = Math.max(pt2 - 3, 10), minRatio = 85) {
|
|
27442
|
+
const faceClass = faceClassForGen(font);
|
|
27443
|
+
const avail = availHu * 0.98;
|
|
27444
|
+
const fits = (p, r, s) => measureTextWidth(text, p * 100, r, { spacingPct: s, faceClass }) <= avail;
|
|
27445
|
+
if (fits(pt2, 100, 0)) return { pt: pt2, ratio: 100, spacing: 0, overflow: false };
|
|
27446
|
+
const ratios = RATIOS.filter((r) => r >= minRatio);
|
|
27447
|
+
for (let p = pt2; p >= minPt; p--) {
|
|
27448
|
+
for (const r of ratios) for (const s of SPACINGS) {
|
|
27449
|
+
if (fits(p, r, s)) return { pt: p, ratio: r, spacing: s, overflow: false };
|
|
27450
|
+
}
|
|
27451
|
+
}
|
|
27452
|
+
return { pt: minPt, ratio: ratios[ratios.length - 1], spacing: SPACINGS[SPACINGS.length - 1], overflow: true };
|
|
27453
|
+
}
|
|
27454
|
+
function fitOrphanLine(text, font, pt2, firstW, contW, orphanRatio = 0.2) {
|
|
27455
|
+
const faceClass = faceClassForGen(font);
|
|
27456
|
+
const h = pt2 * 100;
|
|
27457
|
+
const SAFE = 0.9;
|
|
27458
|
+
const width = (r, sp) => measureTextWidth(text, h, r, { faceClass, spacingPct: sp });
|
|
27459
|
+
const est = width(100, 0);
|
|
27460
|
+
if (est <= firstW * SAFE) return null;
|
|
27461
|
+
const combos = [];
|
|
27462
|
+
for (let sp = -1; sp >= -12; sp--) combos.push([100, sp]);
|
|
27463
|
+
for (const r of [97, 95, 92, 90, 88]) for (let sp = 0; sp >= -12; sp--) combos.push([r, sp]);
|
|
27464
|
+
if (est <= firstW * 1.22) {
|
|
27465
|
+
for (const [r, sp] of combos) if (width(r, sp) <= firstW * SAFE) return { ratio: r, spacing: sp };
|
|
27466
|
+
return null;
|
|
27467
|
+
}
|
|
27468
|
+
const f = firstW * 0.95, c = contW * 0.95;
|
|
27469
|
+
const base = simulateWrap(text, f, c, h, 100, "charAll", { faceClass });
|
|
27470
|
+
if (base.lines < 2 || base.lastLineWidth > c * orphanRatio) return null;
|
|
27471
|
+
for (const [r, sp] of combos) {
|
|
27472
|
+
if (simulateWrap(text, f, c, h, r, "charAll", { faceClass, spacingPct: sp }).lines < base.lines) return { ratio: r, spacing: sp };
|
|
27473
|
+
}
|
|
27474
|
+
return null;
|
|
27475
|
+
}
|
|
27476
|
+
|
|
27477
|
+
// src/hwpx/gen-frame-seoul.ts
|
|
27478
|
+
var frameTableId = EXTRA_TABLE_ID_BASE + 500;
|
|
27479
|
+
function resetFrameTableIds() {
|
|
27480
|
+
frameTableId = EXTRA_TABLE_ID_BASE + 500;
|
|
27481
|
+
}
|
|
27482
|
+
var BF_NONE = 1;
|
|
27483
|
+
function ftbl(rows, w, h, cols, opts = {}) {
|
|
27484
|
+
return `<hp:tbl id="${++frameTableId}" zOrder="0" numberingType="TABLE" textWrap="TOP_AND_BOTTOM" textFlow="BOTH_SIDES" lock="0" dropcapstyle="None" pageBreak="CELL" repeatHeader="0" rowCnt="${rows.length}" colCnt="${cols}" cellSpacing="0" borderFillIDRef="${opts.bf ?? BF_NONE}" noAdjust="1"><hp:sz width="${w}" widthRelTo="ABSOLUTE" height="${h}" heightRelTo="ABSOLUTE" protect="0"/><hp:pos treatAsChar="1" affectLSpacing="0" flowWithText="1" 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="${opts.bottomGap ?? 0}"/><hp:inMargin left="140" right="140" top="140" bottom="140"/>` + rows.map((r) => `<hp:tr>${r}</hp:tr>`).join("") + `</hp:tbl>`;
|
|
27485
|
+
}
|
|
27486
|
+
function host(tableXml, paraPrId, charPrId, extra = "") {
|
|
27487
|
+
return `<hp:p paraPrIDRef="${paraPrId}" styleIDRef="0"${extra}><hp:run charPrIDRef="${charPrId}">${tableXml}</hp:run></hp:p>`;
|
|
27488
|
+
}
|
|
27489
|
+
function scale(cols, w) {
|
|
27490
|
+
const sum = cols.reduce((a, b) => a + b, 0);
|
|
27491
|
+
const out = cols.map((c) => Math.round(c / sum * w));
|
|
27492
|
+
out[out.length - 1] += w - out.reduce((a, b) => a + b, 0);
|
|
27493
|
+
return out;
|
|
27494
|
+
}
|
|
27495
|
+
function spacedOrgName(org) {
|
|
27496
|
+
const t = org.trim();
|
|
27497
|
+
if (/\s/.test(t) || [...t].length > 6) return t;
|
|
27498
|
+
return [...t].join(" ");
|
|
27499
|
+
}
|
|
27500
|
+
function buildDocHeadTable(h, ctx) {
|
|
27501
|
+
const { reg, bf, frame } = ctx;
|
|
27502
|
+
const w = ctx.W - 366;
|
|
27503
|
+
const [cLabel, cValue, cPad] = scale([6949, 35068, 7223], w);
|
|
27504
|
+
const center = reg.para({ align: "CENTER", lineSp: 160 });
|
|
27505
|
+
const left = reg.para({ align: "LEFT", lineSp: 160 });
|
|
27506
|
+
const c10 = reg.char({ font: frame.font, pt: 10 });
|
|
27507
|
+
const c10b = reg.char({ font: frame.font, pt: 10, bold: true });
|
|
27508
|
+
const c20b = reg.char({ font: frame.font, pt: 20, bold: true });
|
|
27509
|
+
const c14 = reg.char({ font: frame.font, pt: 14 });
|
|
27510
|
+
const bottomLine = bf.get({ t: "none", b: "thin", l: "none", r: "none" });
|
|
27511
|
+
const rows = [];
|
|
27512
|
+
let r = 0;
|
|
27513
|
+
if (h.slogan) {
|
|
27514
|
+
rows.push(tc({ bf: BF_NONE, row: r++, col: 0, w, h: 2416, colSpan: 3, paras: para(h.slogan, center, c10) }));
|
|
27515
|
+
}
|
|
27516
|
+
rows.push(
|
|
27517
|
+
tc({ bf: BF_NONE, row: r, col: 0, w: cLabel, h: 5608, paras: para("", center, c10) }) + tc({ bf: BF_NONE, row: r, col: 1, w: cValue, h: 5608, paras: para(spacedOrgName(h.org ?? ""), center, c20b) }) + tc({ bf: BF_NONE, row: r, col: 2, w: cPad, h: 5608, paras: para("", center, c10) })
|
|
27518
|
+
);
|
|
27519
|
+
r++;
|
|
27520
|
+
rows.push(tc({ bf: BF_NONE, row: r++, col: 0, w, h: 1563, colSpan: 3, paras: para("", left, c10b) }));
|
|
27521
|
+
const labeled = (label, value, height2, bfId) => tc({ bf: bfId, row: r, col: 0, w: cLabel, h: height2, paras: para(label, left, c14) }) + tc({ bf: bfId, row: r, col: 1, w: cValue + cPad, h: height2, colSpan: 2, paras: para(value, left, c14) });
|
|
27522
|
+
rows.push(labeled(" \uC218\uC2E0 ", h.to ?? "", 2097, BF_NONE));
|
|
27523
|
+
r++;
|
|
27524
|
+
rows.push(labeled("(\uACBD\uC720)", h.via ?? "", 2380, BF_NONE));
|
|
27525
|
+
r++;
|
|
27526
|
+
rows.push(labeled(" \uC81C\uBAA9 ", h.title ?? "", 2248, bottomLine));
|
|
27527
|
+
r++;
|
|
27528
|
+
const height = rows.length === 6 ? 16312 : 13896;
|
|
27529
|
+
return host(ftbl(rows, w, height, 3, { bottomGap: 852 }), reg.para({ align: "LEFT", lineSp: 100 }), c10);
|
|
27530
|
+
}
|
|
27531
|
+
function splitTitleName(s) {
|
|
27532
|
+
const t = s.trim();
|
|
27533
|
+
const i = t.lastIndexOf(" ");
|
|
27534
|
+
return i > 0 ? { title: t.slice(0, i).trim(), name: t.slice(i + 1).trim() } : { title: "", name: t };
|
|
27535
|
+
}
|
|
27536
|
+
function buildDocFootTable(f, ctx, internal) {
|
|
27537
|
+
const { reg, bf, frame } = ctx;
|
|
27538
|
+
const w = ctx.W - 86;
|
|
27539
|
+
const GRID = 48;
|
|
27540
|
+
const unit = Math.floor(w / GRID);
|
|
27541
|
+
const c10 = reg.char({ font: frame.font, pt: 10 });
|
|
27542
|
+
const c11b = reg.char({ font: frame.font, pt: 11, bold: true });
|
|
27543
|
+
const c9b = reg.char({ font: frame.font, pt: 9, bold: true });
|
|
27544
|
+
const c18b = reg.char({ font: frame.font, pt: 18, bold: true });
|
|
27545
|
+
const c3 = reg.char({ font: frame.font, pt: 3 });
|
|
27546
|
+
const center = reg.para({ align: "CENTER", lineSp: 100 });
|
|
27547
|
+
const left = reg.para({ align: "LEFT", lineSp: 100 });
|
|
27548
|
+
const gray = bf.get({ t: "none", b: "none", l: "none", r: "none", fill: "#CCCCCC" });
|
|
27549
|
+
const spans = (widths) => {
|
|
27550
|
+
const sum = widths.reduce((a, b) => a + b, 0);
|
|
27551
|
+
const out = widths.map((x) => Math.max(1, Math.round(x / sum * GRID)));
|
|
27552
|
+
let diff = GRID - out.reduce((a, b) => a + b, 0);
|
|
27553
|
+
for (let i = out.length - 1; diff !== 0 && i >= 0; i = (i - 1 + out.length) % out.length) {
|
|
27554
|
+
if (diff > 0) {
|
|
27555
|
+
out[i]++;
|
|
27556
|
+
diff--;
|
|
27557
|
+
} else if (out[i] > 1) {
|
|
27558
|
+
out[i]--;
|
|
27559
|
+
diff++;
|
|
27560
|
+
}
|
|
27561
|
+
}
|
|
27562
|
+
return out;
|
|
27563
|
+
};
|
|
27564
|
+
const rows = [];
|
|
27565
|
+
let r = 0;
|
|
27566
|
+
const row = (h, specs) => {
|
|
27567
|
+
let col = 0;
|
|
27568
|
+
let xml = "";
|
|
27569
|
+
specs.forEach((sp, i) => {
|
|
27570
|
+
const last = i === specs.length - 1;
|
|
27571
|
+
const span = last ? GRID - col : sp.span;
|
|
27572
|
+
const cw = last ? w - col * unit : span * unit;
|
|
27573
|
+
xml += tc({ bf: sp.bf ?? BF_NONE, row: r, col, w: cw, h, colSpan: span, rowSpan: sp.rowSpan, vAlign: sp.vAlign, paras: para(sp.text, sp.pp ?? left, sp.cp ?? c10) });
|
|
27574
|
+
col += span;
|
|
27575
|
+
});
|
|
27576
|
+
rows.push(xml);
|
|
27577
|
+
r++;
|
|
27578
|
+
};
|
|
27579
|
+
{
|
|
27580
|
+
const [a, b, c] = spans([7981, 32099, 9440]);
|
|
27581
|
+
row(4042, [{ span: a, text: "" }, { span: b, text: internal ? "" : f.sender ?? "", cp: c18b, pp: center }, { span: c, text: "" }]);
|
|
27582
|
+
}
|
|
27583
|
+
if (f.recipients) {
|
|
27584
|
+
const [a] = spans([4843, 44677]);
|
|
27585
|
+
row(2880, [{ span: a, text: "\uC218\uC2E0\uC790", vAlign: "TOP" }, { span: GRID - a, text: f.recipients, vAlign: "TOP" }]);
|
|
27586
|
+
} else {
|
|
27587
|
+
row(1280, [{ span: GRID, text: "" }]);
|
|
27588
|
+
}
|
|
27589
|
+
row(1146, [{ span: GRID, text: "", cp: c3, pp: center, bf: gray }]);
|
|
27590
|
+
{
|
|
27591
|
+
const n = Math.max(f.approvers.length, 1);
|
|
27592
|
+
const unitW = [6909, 4625, 983];
|
|
27593
|
+
const titleW = (t) => Math.max(unitW[0], Math.round(measureTextWidth(t, 1e3, 100, { faceClass: "fixedPitch" })) + 280 + unit + 300);
|
|
27594
|
+
const widths = [];
|
|
27595
|
+
for (let i = 0; i < n; i++) {
|
|
27596
|
+
widths.push(titleW(f.approvers[i]?.title ?? ""), unitW[1]);
|
|
27597
|
+
if (i < n - 1) widths.push(unitW[2]);
|
|
27598
|
+
}
|
|
27599
|
+
const used = widths.reduce((a, b) => a + b, 0);
|
|
27600
|
+
if (used < w) widths.push(w - used);
|
|
27601
|
+
const sp = spans(widths);
|
|
27602
|
+
const top = [], bottom = [];
|
|
27603
|
+
let k = 0;
|
|
27604
|
+
for (let i = 0; i < n; i++) {
|
|
27605
|
+
const ap = f.approvers[i] ?? { title: "", name: "" };
|
|
27606
|
+
top.push({ span: sp[k++], text: ap.title, pp: center, rowSpan: 2 });
|
|
27607
|
+
const signSpan = sp[k++];
|
|
27608
|
+
top.push({ span: signSpan, text: ap.date ?? "", cp: c9b, pp: center });
|
|
27609
|
+
bottom.push({ span: signSpan, text: ap.name, cp: c11b, pp: center });
|
|
27610
|
+
if (i < n - 1) top.push({ span: sp[k++], text: "", rowSpan: 2 });
|
|
27611
|
+
}
|
|
27612
|
+
if (k < sp.length) top.push({ span: sp[k], text: "", rowSpan: 2 });
|
|
27613
|
+
let col = 0, xmlTop = "", xmlBottom = "";
|
|
27614
|
+
let bi = 0;
|
|
27615
|
+
top.forEach((s, i) => {
|
|
27616
|
+
const last = i === top.length - 1;
|
|
27617
|
+
const span = last ? GRID - col : s.span;
|
|
27618
|
+
const cw = last ? w - col * unit : span * unit;
|
|
27619
|
+
xmlTop += tc({ bf: BF_NONE, row: r, col, w: cw, h: s.rowSpan ? 2198 : 1100, colSpan: span, rowSpan: s.rowSpan, paras: para(s.text, s.pp ?? left, s.cp ?? c10) });
|
|
27620
|
+
if (!s.rowSpan) {
|
|
27621
|
+
const b = bottom[bi++];
|
|
27622
|
+
xmlBottom += tc({ bf: BF_NONE, row: r + 1, col, w: cw, h: 1098, colSpan: span, paras: para(b.text, b.pp ?? center, b.cp ?? c11b) });
|
|
27623
|
+
}
|
|
27624
|
+
col += span;
|
|
27625
|
+
});
|
|
27626
|
+
rows.push(xmlTop, xmlBottom);
|
|
27627
|
+
r += 2;
|
|
27628
|
+
}
|
|
27629
|
+
{
|
|
27630
|
+
const [a] = spans([4455, 45065]);
|
|
27631
|
+
const coop = f.cooperators.map((c) => `${c.title} ${c.name}`.trim()).join(" ");
|
|
27632
|
+
row(2700, [{ span: a, text: "\uD611\uC870\uC790" }, { span: GRID - a, text: coop }]);
|
|
27633
|
+
}
|
|
27634
|
+
row(1400, [{ span: GRID, text: "" }]);
|
|
27635
|
+
{
|
|
27636
|
+
const [a, b, c] = spans([3340, 21532, 3301, 21347]);
|
|
27637
|
+
row(2280, [{ span: a, text: "\uC2DC\uD589" }, { span: b, text: f.docNum ?? "" }, { span: c, text: "\uC811\uC218" }, { span: 0, text: f.receive ?? "" }]);
|
|
27638
|
+
}
|
|
27639
|
+
{
|
|
27640
|
+
const [a, b, c, d] = spans([1597, 3902, 21934, 761, 21326]);
|
|
27641
|
+
row(1280, [{ span: a, text: "\uC6B0" }, { span: b, text: f.zip ?? "" }, { span: c, text: f.address ?? "" }, { span: d, text: "/" }, { span: 0, text: f.site ?? "" }]);
|
|
27642
|
+
}
|
|
27643
|
+
{
|
|
27644
|
+
const sp = spans([2750, 8006, 3196, 8311, 899, 14327, 792, 11239]);
|
|
27645
|
+
const vals = ["\uC804\uD654", f.phone ?? "", "/\uC804\uC1A1", f.fax ?? "", "/", f.email ?? "", "/", f.disclosure ?? ""];
|
|
27646
|
+
row(2e3, vals.map((v, i) => ({ span: sp[i], text: v })));
|
|
27647
|
+
}
|
|
27648
|
+
row(1e3, [{ span: GRID, text: "" }]);
|
|
27649
|
+
const height = rows.length * 1500;
|
|
27650
|
+
return host(ftbl(rows, w, height, GRID), reg.para({ align: "LEFT", lineSp: 100 }), c10);
|
|
27651
|
+
}
|
|
27652
|
+
function buildReportTitleTable(title, contact, ctx) {
|
|
27653
|
+
const { reg, bf, frame } = ctx;
|
|
27654
|
+
const w = ctx.W - 566;
|
|
27655
|
+
const fit = fitOneLine(title, frame.titleFont, frame.titlePt, w - 2 * 140 - 400, 20);
|
|
27656
|
+
const cTitle = reg.char({ font: frame.titleFont, pt: fit.pt, bold: true, ratio: fit.ratio, spacing: fit.spacing });
|
|
27657
|
+
const center = reg.para({ align: "CENTER", lineSp: 130 });
|
|
27658
|
+
const rows = [];
|
|
27659
|
+
const titleBf = bf.get({ t: "thick", b: contact ? "mid" : "thick", l: "none", r: "none" });
|
|
27660
|
+
rows.push(tc({ bf: titleBf, row: 0, col: 0, w, h: 4084, paras: para(title, center, cTitle), name: "__kordoc_h1" }));
|
|
27661
|
+
let h = 4084;
|
|
27662
|
+
if (contact) {
|
|
27663
|
+
const cContact = reg.char({ font: frame.contactFont, pt: frame.contactPt });
|
|
27664
|
+
rows.push(tc({ bf: bf.get({ t: "mid", b: "thick", l: "none", r: "none" }), row: 1, col: 0, w, h: 1943, paras: para(contact, center, cContact) }));
|
|
27665
|
+
h += 1943;
|
|
27666
|
+
}
|
|
27667
|
+
return { xml: host(ftbl(rows, w, h, 1, { bottomGap: 600 }), reg.para({ align: "CENTER", lineSp: 100 }), cTitle), overflow: fit.overflow };
|
|
27668
|
+
}
|
|
27669
|
+
var CHAPTER_BAND_DEFAULT = { color: "#003366", textColor: "#FFFFFF", numFont: "HY\uD5E4\uB4DC\uB77C\uC778M", numPt: 17 };
|
|
27670
|
+
function buildChapterBand(roman, title, ctx, spec, before) {
|
|
27671
|
+
const { reg, bf } = ctx;
|
|
27672
|
+
const w = ctx.W;
|
|
27673
|
+
const numW = 3100, gap = 400, h = 2600;
|
|
27674
|
+
const titleW = w - numW - gap;
|
|
27675
|
+
const fit = fitOneLine(title, spec.titleFont, spec.titlePt, titleW - 700, spec.titlePt - 3);
|
|
27676
|
+
const numBf = bf.get({ t: "none", b: "none", l: "none", r: "none", fill: spec.color });
|
|
27677
|
+
const gapBf = bf.get({ t: "none", b: "none", l: "none", r: "none" });
|
|
27678
|
+
const titleBf = bf.get({ t: "none", b: "thick", l: "none", r: "none" });
|
|
27679
|
+
const cNum = reg.char({ font: spec.numFont, pt: spec.numPt, bold: true, color: spec.textColor });
|
|
27680
|
+
const cTitle = reg.char({ font: spec.titleFont, pt: fit.pt, bold: true, ratio: fit.ratio, spacing: fit.spacing });
|
|
27681
|
+
const center = reg.para({ align: "CENTER", lineSp: 130 });
|
|
27682
|
+
const left = reg.para({ align: "LEFT", lineSp: 130, left: 300 });
|
|
27683
|
+
const row = tc({ bf: numBf, row: 0, col: 0, w: numW, h, paras: para(roman, center, cNum), vAlign: "CENTER" }) + tc({ bf: gapBf, row: 0, col: 1, w: gap, h, paras: para("", center, cNum), vAlign: "CENTER" }) + tc({ bf: titleBf, row: 0, col: 2, w: titleW, h, paras: para(title, left, cTitle), name: "__kordoc_h2", vAlign: "CENTER" });
|
|
27684
|
+
return { xml: host(ftbl([row], w, h, 3, { bottomGap: 1e3 }), reg.para({ align: "LEFT", lineSp: 100, before, keepWithNext: true }), cTitle), overflow: fit.overflow };
|
|
27685
|
+
}
|
|
27686
|
+
var SUMMARY_PAD = 1e3;
|
|
27687
|
+
function buildSummaryBox(text, ctx) {
|
|
27688
|
+
const { reg, bf, frame } = ctx;
|
|
27689
|
+
const w = ctx.W - 566;
|
|
27690
|
+
const avail = w - 280 - SUMMARY_PAD * 2;
|
|
27691
|
+
const p = reg.para({ align: "JUSTIFY", lineSp: 160, left: SUMMARY_PAD, right: SUMMARY_PAD, keepWord: false });
|
|
27692
|
+
const bodies = text.split("\n").map((l) => l.trim().replace(/^[□■○ㅇ◦●\-–ㆍ·•]\s*/u, "")).filter(Boolean);
|
|
27693
|
+
let lines = 0;
|
|
27694
|
+
const paras = bodies.map((b) => {
|
|
27695
|
+
const f = fitOrphanLine(b, frame.summaryFont, frame.summaryPt, avail, avail);
|
|
27696
|
+
const c = reg.char({ font: frame.summaryFont, pt: frame.summaryPt, bold: true, ratio: f?.ratio ?? 100, spacing: f?.spacing ?? 0 });
|
|
27697
|
+
lines += simulateWrap(b, avail * 0.95, avail * 0.95, frame.summaryPt * 100, f?.ratio ?? 100, "charAll", { faceClass: "gothic", spacingPct: f?.spacing ?? 0 }).lines;
|
|
27698
|
+
return para(b, p, c);
|
|
27699
|
+
});
|
|
27700
|
+
const h = lines * Math.round(frame.summaryPt * 100 * 1.6) + 280 + 600;
|
|
27701
|
+
const box = bf.get({ t: "thick", b: "thick", l: "thick", r: "thick", fill: frame.summaryFill });
|
|
27702
|
+
const row = tc({ bf: box, row: 0, col: 0, w, h, paras: paras.join(""), name: "__kordoc_summary" });
|
|
27703
|
+
return { xml: host(ftbl([row], w, h, 1, { bottomGap: 600 }), reg.para({ align: "CENTER", lineSp: 100 }), reg.char({ font: frame.summaryFont, pt: frame.summaryPt, bold: true })), lines };
|
|
27704
|
+
}
|
|
27705
|
+
function buildApprovalSeoul(labels, names, ctx) {
|
|
27706
|
+
const { reg, bf } = ctx;
|
|
27707
|
+
const n = labels.length;
|
|
27708
|
+
const colW = Math.min(8600, Math.floor(ctx.W * 0.55 / n));
|
|
27709
|
+
const w = colW * n;
|
|
27710
|
+
const c12 = reg.char({ font: "\uD55C\uCEF4\uB3CB\uC6C0", pt: 12 });
|
|
27711
|
+
const c12b = reg.char({ font: "\uD55C\uCEF4\uB3CB\uC6C0", pt: 12, bold: true });
|
|
27712
|
+
const center = reg.para({ align: "CENTER", lineSp: 100 });
|
|
27713
|
+
const edge = (row, col) => bf.get({
|
|
27714
|
+
t: row === 0 ? "thick" : "thin",
|
|
27715
|
+
b: row === 1 ? "thick" : "thin",
|
|
27716
|
+
l: col === 0 ? "thick" : "thin",
|
|
27717
|
+
r: col === n - 1 ? "thick" : "thin"
|
|
27718
|
+
});
|
|
27719
|
+
const top = labels.map((l, c) => tc({ bf: edge(0, c), row: 0, col: c, w: colW, h: 1765, paras: para(l, center, c12) })).join("");
|
|
27720
|
+
const sign = labels.map((_, c) => tc({ bf: edge(1, c), row: 1, col: c, w: colW, h: 4200, paras: para(names?.[c] ?? "", center, c12b) })).join("");
|
|
27721
|
+
return host(ftbl([top, sign], w, 5965, n, { bottomGap: 600 }), reg.para({ align: "RIGHT", lineSp: 100 }), c12);
|
|
27722
|
+
}
|
|
27723
|
+
function buildReportCover(inp, ctx) {
|
|
27724
|
+
const { reg, bf, frame } = ctx;
|
|
27725
|
+
const out = [];
|
|
27726
|
+
const c12 = reg.char({ font: "\uD55C\uCEF4\uB3CB\uC6C0", pt: 12 });
|
|
27727
|
+
const c12b = reg.char({ font: "\uD55C\uCEF4\uB3CB\uC6C0", pt: 12, bold: true });
|
|
27728
|
+
const center = reg.para({ align: "CENTER", lineSp: 100 });
|
|
27729
|
+
const blank2 = (pt2 = 15) => para("", reg.para({ align: "LEFT", lineSp: 160 }), reg.char({ font: "\uD55C\uCEF4\uB3CB\uC6C0", pt: pt2 }));
|
|
27730
|
+
const infoRows = [["\uBB38\uC11C\uBC88\uD638", inp.docInfo?.docNum ?? ""], ["\uACB0\uC7AC\uC77C\uC790", inp.docInfo?.date ?? ""], ["\uACF5\uAC1C\uC5EC\uBD80", inp.docInfo?.disclosure ?? ""], ["\uBC29\uCE68\uBC88\uD638", inp.docInfo?.policyNo ?? ""]];
|
|
27731
|
+
const infoW = Math.round(ctx.W * 0.36);
|
|
27732
|
+
const [lw, vw] = scale([4200, 12800], infoW);
|
|
27733
|
+
const infoXml = ftbl(infoRows.map(
|
|
27734
|
+
([l, v], r) => tc({ bf: bf.get({ t: r === 0 ? "thick" : "thin", b: r === 3 ? "thick" : "thin", l: "thick", r: "thin" }), row: r, col: 0, w: lw, h: 1850, paras: para(l, center, c12b) }) + tc({ bf: bf.get({ t: r === 0 ? "thick" : "thin", b: r === 3 ? "thick" : "thin", l: "thin", r: "thick" }), row: r, col: 1, w: vw, h: 1850, paras: para(v, center, c12) })
|
|
27735
|
+
), infoW, 1850 * 4, 2);
|
|
27736
|
+
let line = infoXml;
|
|
27737
|
+
if (inp.approval && inp.approval.length) {
|
|
27738
|
+
const n = inp.approval.length;
|
|
27739
|
+
const colW = Math.min(7600, Math.floor(ctx.W * 0.5 / n));
|
|
27740
|
+
const edge = (row, col) => bf.get({ t: row === 0 ? "thick" : "thin", b: row === 1 ? "thick" : "thin", l: col === 0 ? "thick" : "thin", r: col === n - 1 ? "thick" : "thin" });
|
|
27741
|
+
const top = inp.approval.map((l, c) => tc({ bf: edge(0, c), row: 0, col: c, w: colW, h: 1765, paras: para(l, center, c12) })).join("");
|
|
27742
|
+
const sign = inp.approval.map((_, c) => tc({ bf: edge(1, c), row: 1, col: c, w: colW, h: 3600, paras: para("", center, c12b) })).join("");
|
|
27743
|
+
line += `</hp:run><hp:run charPrIDRef="${c12}"><hp:t> </hp:t></hp:run><hp:run charPrIDRef="${c12}">` + ftbl([top, sign], colW * n, 5365, n);
|
|
27744
|
+
}
|
|
27745
|
+
out.push(`<hp:p paraPrIDRef="${reg.para({ align: "DISTRIBUTE", lineSp: 100 })}" styleIDRef="0"><hp:run charPrIDRef="${c12}">${line}</hp:run></hp:p>`);
|
|
27746
|
+
for (let i = 0; i < 4; i++) out.push(blank2(20));
|
|
27747
|
+
const w = ctx.W - 1200;
|
|
27748
|
+
const bar = bf.get({ t: "none", b: "none", l: "none", r: "none", fill: "#1F2FD6" });
|
|
27749
|
+
const fit = fitOneLine(inp.title, frame.titleFont, 27, w - 800, 22);
|
|
27750
|
+
const cT = reg.char({ font: frame.titleFont, pt: fit.pt, bold: true, ratio: fit.ratio, spacing: fit.spacing });
|
|
27751
|
+
const c1 = reg.char({ font: "\uD55C\uCEF4\uB3CB\uC6C0", pt: 1 });
|
|
27752
|
+
const barP = reg.para({ align: "CENTER", lineSp: 70 });
|
|
27753
|
+
const rows = [
|
|
27754
|
+
tc({ bf: bar, row: 0, col: 0, w, h: 200, paras: para("", barP, c1) }),
|
|
27755
|
+
tc({ bf: BF_NONE, row: 1, col: 0, w, h: 6e3, paras: para(inp.title, center, cT), name: "__kordoc_skip" }),
|
|
27756
|
+
tc({ bf: bar, row: 2, col: 0, w, h: 200, paras: para("", barP, c1) })
|
|
27757
|
+
];
|
|
27758
|
+
out.push(host(ftbl(rows, w, 6400, 1), reg.para({ align: "CENTER", lineSp: 100 }), cT));
|
|
27759
|
+
for (let i = 0; i < 2; i++) out.push(blank2(20));
|
|
27760
|
+
out.push(para(inp.date, center, reg.char({ font: frame.titleFont, pt: 22, bold: true })));
|
|
27761
|
+
for (let i = 0; i < 8; i++) out.push(blank2(20));
|
|
27762
|
+
if (inp.org) out.push(para(spacedOrgName(inp.org), center, reg.char({ font: frame.titleFont, pt: 24, bold: true })));
|
|
27763
|
+
if (inp.dept) out.push(para(`(${inp.dept.replace(/^\(|\)$/g, "")})`, center, reg.char({ font: "\uD55C\uCEF4\uB3CB\uC6C0", pt: 22, bold: true })));
|
|
27764
|
+
return out;
|
|
27765
|
+
}
|
|
27766
|
+
|
|
27767
|
+
// src/hwpx/gen-gongmun.ts
|
|
27768
|
+
var V5_PRESETS = /* @__PURE__ */ new Set(["official", "report", "plan", "notice", "minutes"]);
|
|
27769
|
+
function usesV5Engine(preset) {
|
|
27770
|
+
return V5_PRESETS.has(preset);
|
|
27771
|
+
}
|
|
27772
|
+
var ROMAN = ["\u2160", "\u2161", "\u2162", "\u2163", "\u2164", "\u2165", "\u2166", "\u2167", "\u2168", "\u2169", "\u216A", "\u216B"];
|
|
27773
|
+
function chapterLabel(index, style) {
|
|
27774
|
+
return style === "roman" ? `${ROMAN[(index - 1) % 12]}.` : `${index}.`;
|
|
27775
|
+
}
|
|
27776
|
+
function inlineMapper(reg, base) {
|
|
27777
|
+
const norm2 = reg.char({ font: base.font, pt: base.pt, bold: base.bold, ratio: base.ratio, spacing: base.spacing });
|
|
27778
|
+
return (id) => {
|
|
27779
|
+
if (id === CHAR_BOLD) return reg.char({ font: base.font, pt: base.pt, bold: true, ratio: base.ratio, spacing: base.spacing });
|
|
27780
|
+
if (id === CHAR_ITALIC) return reg.char({ font: base.font, pt: base.pt, bold: base.bold, italic: true, ratio: base.ratio, spacing: base.spacing });
|
|
27781
|
+
if (id === CHAR_BOLD_ITALIC) return reg.char({ font: base.font, pt: base.pt, bold: true, italic: true, ratio: base.ratio, spacing: base.spacing });
|
|
27782
|
+
if (id === CHAR_NORMAL) return norm2;
|
|
27783
|
+
return id;
|
|
27784
|
+
};
|
|
27785
|
+
}
|
|
27786
|
+
function plain(text) {
|
|
27787
|
+
return text.replace(/\*\*([^*]+)\*\*/g, "$1").replace(/\*([^*]+)\*/g, "$1").replace(/`([^`]+)`/g, "$1").replace(/!\[[^\]]*\]\([^)]*\)/g, "");
|
|
27788
|
+
}
|
|
27789
|
+
function buildGongmunSectionV5(blocks, gongmun, deps, theme) {
|
|
27790
|
+
resetTableIds();
|
|
27791
|
+
resetFrameTableIds();
|
|
27792
|
+
const { reg, bfReg, remap, images, page } = deps;
|
|
27793
|
+
const warnings = [];
|
|
27794
|
+
const g = gongmun;
|
|
27795
|
+
const preset = g.preset;
|
|
27796
|
+
const isReport = preset === "report" || preset === "plan";
|
|
27797
|
+
const W = mmToHwpunit(210 - g.margins.left - g.margins.right);
|
|
27798
|
+
const pre = buildOutline(blocks, { gaejosik: true, consumeTitle: true, summaryFromQuote: isReport });
|
|
27799
|
+
const scheme = pickScheme(g, pre.hasBoxMarkers);
|
|
27800
|
+
const gaejosik = scheme.kind === "gaejosik";
|
|
27801
|
+
const outline = gaejosik ? pre : buildOutline(blocks, { gaejosik: false, consumeTitle: true, summaryFromQuote: false });
|
|
27802
|
+
const frame = { reg, bf: bfReg, frame: scheme.frame, W };
|
|
27803
|
+
const lineHu = (st) => Math.round(st.pt * 100 * ((st.lineSp ?? scheme.lineSp) / 100));
|
|
27804
|
+
const paras = [];
|
|
27805
|
+
let pendingPageBreak = false;
|
|
27806
|
+
const tblCharPr = reg.char({ font: scheme.table.font, pt: scheme.table.pt });
|
|
27807
|
+
const tableStyle = {
|
|
27808
|
+
totalWidth: W,
|
|
27809
|
+
charPr: tblCharPr,
|
|
27810
|
+
boldCharPr: reg.char({ font: scheme.table.font, pt: scheme.table.pt, bold: true }),
|
|
27811
|
+
charHeight: scheme.table.pt * 100,
|
|
27812
|
+
headerBf: bfReg.get({ t: "thin", b: "thin", l: "thin", r: "thin", fill: scheme.table.headerFill }),
|
|
27813
|
+
centerParaPr: reg.para({ align: "CENTER", lineSp: scheme.lineSp }),
|
|
27814
|
+
tblCenterParaPr: reg.para({ align: "CENTER", lineSp: 130 }),
|
|
27815
|
+
tblLeftParaPr: reg.para({ align: "LEFT", lineSp: 130 }),
|
|
27816
|
+
bfRegistry: bfReg,
|
|
27817
|
+
rightParaPr: reg.para({ align: "RIGHT", lineSp: scheme.lineSp }),
|
|
27818
|
+
headerFill: scheme.table.headerFill,
|
|
27819
|
+
labelFill: scheme.table.labelFill,
|
|
27820
|
+
faceClass: faceClassForGen(scheme.table.font)
|
|
27821
|
+
};
|
|
27822
|
+
let frontKind = "start";
|
|
27823
|
+
const pushSummary = (t) => {
|
|
27824
|
+
frontKind = "summary";
|
|
27825
|
+
const box = buildSummaryBox(t, frame);
|
|
27826
|
+
if (box.lines > 3) warnings.push(`\uC694\uC57D\uBC15\uC2A4\uAC00 ${box.lines}\uC904\uC785\uB2C8\uB2E4 \u2014 \uBCF4\uACE0 \uBAA9\uC801\uC744 \uD55C \uBB38\uC7A5(\uC27C\uD45C \uD5C8\uC6A9) 3\uC904 \uC774\uB0B4 "\u2026\uD558\uACE0\uC790 \uD568"\uC73C\uB85C \uC904\uC774\uC138\uC694`);
|
|
27827
|
+
paras.push(box.xml);
|
|
27828
|
+
};
|
|
27829
|
+
const docTitle = g.docHead?.title ?? outline.title ?? "";
|
|
27830
|
+
if (preset === "official") {
|
|
27831
|
+
if (g.docHead) paras.push(buildDocHeadTable({ ...g.docHead, title: docTitle }, frame));
|
|
27832
|
+
else if (outline.title) paras.push(generateParagraph(outline.title, reg.para({ align: "CENTER", lineSp: scheme.lineSp, after: lineHu(scheme.body) }), reg.char({ font: scheme.body.font, pt: scheme.body.pt + 2, bold: true }), void 0, 1));
|
|
27833
|
+
if (g.reportInfo) paras.push(generateParagraph(g.reportInfo, reg.para({ align: "RIGHT", lineSp: scheme.lineSp }), reg.char({ font: scheme.body.font, pt: 12 })));
|
|
27834
|
+
if (g.approval) paras.push(buildApprovalSeoul(g.approval, null, frame));
|
|
27835
|
+
} else if (isReport) {
|
|
27836
|
+
if (g.cover) {
|
|
27837
|
+
paras.push(...buildReportCover({
|
|
27838
|
+
title: docTitle,
|
|
27839
|
+
date: g.cover.date ?? formatGaejosikDate(/* @__PURE__ */ new Date()).replace(/\s\d+\.$/, "."),
|
|
27840
|
+
org: g.cover.org || void 0,
|
|
27841
|
+
dept: g.cover.dept,
|
|
27842
|
+
docInfo: g.docInfo ?? void 0,
|
|
27843
|
+
approval: g.approval ?? void 0
|
|
27844
|
+
}, frame));
|
|
27845
|
+
pendingPageBreak = true;
|
|
27846
|
+
} else if (g.approval) {
|
|
27847
|
+
paras.push(buildApprovalSeoul(g.approval, null, frame));
|
|
27848
|
+
}
|
|
27849
|
+
if (docTitle) {
|
|
27850
|
+
const t = buildReportTitleTable(docTitle, g.reportInfo, frame);
|
|
27851
|
+
if (t.overflow) warnings.push(`\uC81C\uBAA9\uC774 \uAE38\uC5B4 \uD55C \uC904\uC5D0 \uB2F4\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4(20pt\xB7\uC7A5\uD3C9 85%\uAE4C\uC9C0 \uCD95\uC18C) \u2014 \uC81C\uBAA9\uC744 \uC904\uC774\uC138\uC694: "${docTitle.slice(0, 30)}\u2026"`);
|
|
27852
|
+
if (pendingPageBreak) {
|
|
27853
|
+
paras.push(t.xml.replace(/^<hp:p /, `<hp:p pageBreak="1" `).replace(/<hp:run charPrIDRef="(\d+)">/, `<hp:run charPrIDRef="$1">${newPageNumCtrl(1)}`));
|
|
27854
|
+
pendingPageBreak = false;
|
|
27855
|
+
} else paras.push(t.xml);
|
|
27856
|
+
frontKind = "title";
|
|
27857
|
+
}
|
|
27858
|
+
if (!docTitle && g.reportInfo) paras.push(generateParagraph(g.reportInfo, reg.para({ align: "RIGHT", lineSp: scheme.lineSp }), reg.char({ font: scheme.body.font, pt: 12 })));
|
|
27859
|
+
if (g.summary) pushSummary(g.summary);
|
|
27860
|
+
else if (!outline.nodes.some((n) => n.kind === "summary")) warnings.push('\uBCF4\uACE0\uC11C \uC694\uC57D\uBC15\uC2A4\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4 \u2014 \uC81C\uBAA9 \uC9C1\uD6C4 \uC778\uC6A9\uBB38(>)\uC5D0 \uBCF4\uACE0 \uBAA9\uC801\uC744 \uD55C \uBB38\uC7A5(\uC27C\uD45C \uD5C8\uC6A9) 3\uC904 \uC774\uB0B4 "\u2026\uD558\uACE0\uC790 \uD568"\uC73C\uB85C \uB123\uAC70\uB098 summary \uC635\uC158\uC744 \uC9C0\uC815\uD558\uC138\uC694');
|
|
27861
|
+
} else {
|
|
27862
|
+
if (g.noticeHead) paras.push(...buildNoticeHead(g));
|
|
27863
|
+
if (g.approval) paras.push(buildApprovalSeoul(g.approval, null, frame));
|
|
27864
|
+
if (outline.title) paras.push(generateParagraph(outline.title, reg.para({ align: "CENTER", lineSp: scheme.lineSp, before: 400, after: lineHu(scheme.body) }), reg.char({ font: scheme.frame.titleFont, pt: 20, bold: true }), void 0, 1));
|
|
27865
|
+
}
|
|
27866
|
+
const numberer = new GongmunNumberer("standard", g.bullet2);
|
|
27867
|
+
const itemDepths = outline.nodes.filter((n) => n.kind === "item" && !n.legalMarker).map((n) => n.depth);
|
|
27868
|
+
const suppress = !gaejosik && g.suppressSingle ? computeSuppression(itemDepths) : null;
|
|
27869
|
+
let itemSeq = 0;
|
|
27870
|
+
const BOX_BLANK_HU = 1e3;
|
|
27871
|
+
const BOX_GAP_AFTER_BAND = 1200;
|
|
27872
|
+
const BAND_BEFORE_HU = 2e3;
|
|
27873
|
+
let prevKind = frontKind;
|
|
27874
|
+
let prevItemDepth = -1;
|
|
27875
|
+
let nextNode;
|
|
27876
|
+
let tableSeq = 0;
|
|
27877
|
+
let chapterStyle = g.h2Marker === "box" || g.h2Marker === "number" || g.h2Marker === "none" || g.h2Marker === "band" ? g.h2Marker : "roman";
|
|
27878
|
+
if (!gaejosik) chapterStyle = "number";
|
|
27879
|
+
const leadLeft = (depth, pt2) => (scheme.levels[Math.min(depth, 7)]?.leadTa ?? 0) * taHu(pt2);
|
|
27880
|
+
const renderItem = (node, styleId = 0) => {
|
|
27881
|
+
const depth = Math.min(node.depth, 7);
|
|
27882
|
+
const sub = gaejosik && node.legalMarker;
|
|
27883
|
+
const st = sub ? { ...scheme.sub, leadTa: (() => {
|
|
27884
|
+
const d = node.legalMarker ? legalDepthOf(node.legalMarker) : 0;
|
|
27885
|
+
return 2 * d;
|
|
27886
|
+
})() } : scheme.levels[depth];
|
|
27887
|
+
let marker;
|
|
27888
|
+
if (sub) marker = node.legalMarker;
|
|
27889
|
+
else if (gaejosik) marker = scheme.marker(depth, 0);
|
|
27890
|
+
else {
|
|
27891
|
+
const sup = suppress ? suppress[itemSeq] : false;
|
|
27892
|
+
marker = numberer.next(depth, sup);
|
|
27893
|
+
itemSeq++;
|
|
27894
|
+
}
|
|
27895
|
+
const text = marker ? `${marker} ${node.text}` : node.text;
|
|
27896
|
+
const geom = levelGeometry(st, marker);
|
|
27897
|
+
const availFirst = W - geom.left;
|
|
27898
|
+
const availCont = W - geom.left + geom.indent;
|
|
27899
|
+
let pt2 = st.pt, ratio = 100, spacing = 0;
|
|
27900
|
+
if (st.oneLine) {
|
|
27901
|
+
const fit = fitOneLine(plain(text), st.font, st.pt, availFirst, st.pt, 90);
|
|
27902
|
+
if (fit.overflow) warnings.push(`\u25A1 \uD56D\uBAA9\uC774 \uD55C \uC904\uC5D0 \uB2F4\uAE30\uC9C0 \uC54A\uC544 \uB450 \uC904\uB85C \uAEBE\uC785\uB2C8\uB2E4 \u2014 \uBB38\uC7A5\uC744 \uC904\uC774\uC138\uC694(\uC7A5\uD3C9 90%\xB7\uC790\uAC04 -5 \uB85C\uB3C4 \uCD08\uACFC): "${node.text.slice(0, 30)}\u2026"`);
|
|
27903
|
+
else {
|
|
27904
|
+
ratio = fit.ratio;
|
|
27905
|
+
spacing = fit.spacing;
|
|
27906
|
+
}
|
|
27907
|
+
} else if (g.autoFitMinRatio !== null) {
|
|
27908
|
+
const f = fitOrphanLine(plain(text), st.font, st.pt, availFirst, availCont);
|
|
27909
|
+
if (f) {
|
|
27910
|
+
ratio = f.ratio;
|
|
27911
|
+
spacing = f.spacing;
|
|
27912
|
+
}
|
|
27913
|
+
}
|
|
27914
|
+
const base = { font: st.font, pt: pt2, bold: st.bold, ratio, spacing };
|
|
27915
|
+
const geom2 = pt2 !== st.pt ? levelGeometry({ ...st, pt: pt2 }, marker) : geom;
|
|
27916
|
+
const consecutiveBox = prevKind === "item" && prevItemDepth === 0 && depth === 0;
|
|
27917
|
+
const afterBand = depth === 0 && (prevKind === "chapter" && chapterStyle === "band" || prevKind === "title" && !!st.blankBefore);
|
|
27918
|
+
const before = afterBand ? BOX_GAP_AFTER_BAND : st.blankBefore && !consecutiveBox && prevKind !== "chapter" && prevKind !== "start" && prevKind !== "summary" ? BOX_BLANK_HU : 0;
|
|
27919
|
+
const keepNext = !!st.keepWithNext && !!nextNode && (nextNode.kind === "item" && nextNode.depth > depth || nextNode.kind === "ref");
|
|
27920
|
+
const paraId = reg.para({ align: "JUSTIFY", left: geom2.left, indent: geom2.indent, before, lineSp: st.lineSp ?? scheme.lineSp, keepWithNext: keepNext, keepWord: false });
|
|
27921
|
+
return generateParagraph(text, paraId, reg.char(base), inlineMapper(reg, base), styleId);
|
|
27922
|
+
};
|
|
27923
|
+
const renderRef = (node) => {
|
|
27924
|
+
const st = scheme.ref;
|
|
27925
|
+
const marker = "\u203B";
|
|
27926
|
+
const left = leadLeft(node.depth, st.pt);
|
|
27927
|
+
const geom = levelGeometry({ ...st, leadTa: 0 }, marker);
|
|
27928
|
+
const base = { font: st.font, pt: st.pt, bold: st.bold };
|
|
27929
|
+
const f = fitOrphanLine(plain(`${marker} ${node.text}`), st.font, st.pt, W - left, W - left + geom.indent);
|
|
27930
|
+
const baseF = f ? { ...base, ratio: f.ratio, spacing: f.spacing } : base;
|
|
27931
|
+
const paraId = reg.para({ align: "JUSTIFY", left, indent: geom.indent, lineSp: st.lineSp ?? scheme.lineSp, keepWord: false });
|
|
27932
|
+
return generateParagraph(`${marker} ${node.text}`, paraId, reg.char(baseF), inlineMapper(reg, baseF));
|
|
27933
|
+
};
|
|
27934
|
+
const renderPara = (node) => {
|
|
27935
|
+
const st = scheme.body;
|
|
27936
|
+
if (images) {
|
|
27937
|
+
const { text: rest, urls } = splitImageRefs(node.text);
|
|
27938
|
+
if (urls.length > 0 && !rest.trim()) {
|
|
27939
|
+
const pics = urls.map((u) => {
|
|
27940
|
+
const part = images.take(u);
|
|
27941
|
+
return part ? images.inlinePicXml(part) : null;
|
|
27942
|
+
});
|
|
27943
|
+
if (pics.every(Boolean)) return `<hp:p paraPrIDRef="${reg.para({ align: "CENTER", lineSp: scheme.lineSp })}" styleIDRef="0"><hp:run charPrIDRef="${reg.char({ font: st.font, pt: st.pt })}">${pics.join("")}</hp:run></hp:p>`;
|
|
27944
|
+
}
|
|
27945
|
+
}
|
|
27946
|
+
let ratio = 100, spacing = 0;
|
|
27947
|
+
if (!node.align && g.autoFitMinRatio !== null) {
|
|
27948
|
+
const f = fitOrphanLine(plain(node.text), st.font, st.pt, W, W);
|
|
27949
|
+
if (f) {
|
|
27950
|
+
ratio = f.ratio;
|
|
27951
|
+
spacing = f.spacing;
|
|
27952
|
+
}
|
|
27953
|
+
}
|
|
27954
|
+
const base = { font: st.font, pt: st.pt, bold: st.bold, ratio, spacing };
|
|
27955
|
+
const paraId = reg.para({ align: node.align ?? "JUSTIFY", lineSp: scheme.lineSp, keepWord: false });
|
|
27956
|
+
return generateParagraph(node.text, paraId, reg.char(base), inlineMapper(reg, base));
|
|
27957
|
+
};
|
|
27958
|
+
const renderChapter = (node) => {
|
|
27959
|
+
const st = scheme.chapter;
|
|
27960
|
+
if (chapterStyle === "band") {
|
|
27961
|
+
const before2 = prevKind === "start" || prevKind === "title" ? 0 : prevKind === "summary" ? BOX_GAP_AFTER_BAND : BAND_BEFORE_HU;
|
|
27962
|
+
const band = buildChapterBand(chapterLabel(node.index, "roman").replace(/\.$/, ""), plain(node.text), frame, { ...CHAPTER_BAND_DEFAULT, titleFont: st.font, titlePt: st.pt }, before2);
|
|
27963
|
+
if (band.overflow) warnings.push(`\uC7A5 \uC81C\uBAA9\uC774 \uB760 \uD45C \uD55C \uC904\uC5D0 \uB2F4\uAE30\uC9C0 \uC54A\uC544 \uCD95\uC18C\uD588\uC2B5\uB2C8\uB2E4 \u2014 \uC81C\uBAA9\uC744 \uC904\uC774\uC138\uC694: "${node.text.slice(0, 30)}\u2026"`);
|
|
27964
|
+
return band.xml;
|
|
27965
|
+
}
|
|
27966
|
+
const label = chapterStyle === "none" ? "" : chapterLabel(node.index, chapterStyle === "number" ? "number" : "roman");
|
|
27967
|
+
const text = label ? `${label} ${node.text}` : node.text;
|
|
27968
|
+
const fit = fitOneLine(plain(text), st.font, st.pt, W, st.pt - 2);
|
|
27969
|
+
const base = { font: st.font, pt: fit.pt, bold: st.bold, ratio: fit.ratio, spacing: fit.spacing };
|
|
27970
|
+
const before = prevKind === "start" || prevKind === "title" || prevKind === "summary" ? 0 : lineHu(scheme.body);
|
|
27971
|
+
const paraId = reg.para({ align: "LEFT", before, after: Math.round(lineHu(scheme.body) * 0.3), lineSp: st.lineSp ?? scheme.lineSp, keepWithNext: true, keepWord: false });
|
|
27972
|
+
return generateParagraph(text, paraId, reg.char(base), inlineMapper(reg, base), 2);
|
|
27973
|
+
};
|
|
27974
|
+
const renderAttach = (node, first) => {
|
|
27975
|
+
const st = scheme.attach;
|
|
27976
|
+
let text = node.text;
|
|
27977
|
+
if (/^\s*붙\s*임/.test(text)) text = text.replace(/^\s*붙\s*임\s*[::]?\s*/, "\uBD99\uC784 ");
|
|
27978
|
+
else text = text.replace(/\s+$/, "");
|
|
27979
|
+
const paraId = reg.para({ align: "LEFT", before: first ? lineHu(scheme.body) : 0, lineSp: st.lineSp ?? scheme.lineSp, keepWord: false });
|
|
27980
|
+
const base = { font: st.font, pt: st.pt, bold: st.bold };
|
|
27981
|
+
return generateParagraph(text, paraId, reg.char(base), inlineMapper(reg, base));
|
|
27982
|
+
};
|
|
27983
|
+
const renderBlock = (node, idx) => {
|
|
27984
|
+
const b = node.block;
|
|
27985
|
+
switch (b.type) {
|
|
27986
|
+
case "table": {
|
|
27987
|
+
if (!b.rows) return "";
|
|
27988
|
+
let style = tableStyle;
|
|
27989
|
+
const face = faceClassForGen(scheme.table.font);
|
|
27990
|
+
const avail = W - DATA_TABLE_INSET;
|
|
27991
|
+
for (const pt2 of [scheme.table.pt, scheme.table.pt - 1, scheme.table.pt - 2]) {
|
|
27992
|
+
if (pt2 < 9) break;
|
|
27993
|
+
if (requiredTableWidth(b.rows, pt2 * 100, face) <= avail || pt2 === scheme.table.pt - 2) {
|
|
27994
|
+
if (pt2 !== scheme.table.pt) style = { ...tableStyle, charPr: reg.char({ font: scheme.table.font, pt: pt2 }), boldCharPr: reg.char({ font: scheme.table.font, pt: pt2, bold: true }), charHeight: pt2 * 100 };
|
|
27995
|
+
break;
|
|
27996
|
+
}
|
|
27997
|
+
}
|
|
27998
|
+
return generateTable(b.rows, theme, style, remap, tableSeq++, images);
|
|
27999
|
+
}
|
|
28000
|
+
case "html_table": {
|
|
28001
|
+
const tbl2 = generateHtmlTableXml(b.text || "", theme, tableStyle.totalWidth - DATA_TABLE_INSET, tableStyle, remap, tableSeq++, images);
|
|
28002
|
+
if (tbl2) return `<hp:p paraPrIDRef="${tableStyle.rightParaPr}" styleIDRef="0"><hp:run charPrIDRef="${tblCharPr}">${tbl2}</hp:run></hp:p>`;
|
|
28003
|
+
const txt = (b.text || "").replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
|
|
28004
|
+
return txt ? renderPara({ kind: "para", depth: 0, text: txt }) : "";
|
|
28005
|
+
}
|
|
28006
|
+
case "code_block": {
|
|
28007
|
+
if ((b.lang || "").toLowerCase() === "chart") {
|
|
28008
|
+
const fence = parseChartFence(b.text || "");
|
|
28009
|
+
if (fence) {
|
|
28010
|
+
const partName = `Chart/chart${deps.chartParts.length + 1}.xml`;
|
|
28011
|
+
deps.chartParts.push({ name: partName, xml: buildChartSpaceXml(fence) });
|
|
28012
|
+
const el = buildChartElementXml(partName, fence.widthHu, fence.heightHu, CHART_TABLE_ID_BASE + idx);
|
|
28013
|
+
return `<hp:p paraPrIDRef="${tableStyle.centerParaPr}" styleIDRef="0"><hp:run charPrIDRef="${tblCharPr}">${el}</hp:run></hp:p>`;
|
|
28014
|
+
}
|
|
28015
|
+
}
|
|
28016
|
+
return (b.text || "").split("\n").map((line) => `<hp:p paraPrIDRef="${PARA_CODE}" styleIDRef="0"><hp:run charPrIDRef="${CHAR_CODE}"><hp:t>${escapeXml(line || " ")}</hp:t></hp:run></hp:p>`).join("\n ");
|
|
28017
|
+
}
|
|
28018
|
+
case "equation":
|
|
28019
|
+
return generateEquationParagraph(b.text || "", idx);
|
|
28020
|
+
case "hr":
|
|
28021
|
+
return `<hp:p paraPrIDRef="${reg.para({ lineSp: scheme.lineSp })}" styleIDRef="0"><hp:run charPrIDRef="${reg.char({ font: scheme.body.font, pt: scheme.body.pt })}"><hp:t></hp:t></hp:run></hp:p>`;
|
|
28022
|
+
default:
|
|
28023
|
+
return "";
|
|
28024
|
+
}
|
|
28025
|
+
};
|
|
28026
|
+
let lastTextNode = null;
|
|
28027
|
+
let prevAttach = false;
|
|
28028
|
+
let lastItemFromHeading = false;
|
|
28029
|
+
outline.nodes.forEach((node, idx) => {
|
|
28030
|
+
nextNode = outline.nodes[idx + 1];
|
|
28031
|
+
let xml = "";
|
|
28032
|
+
switch (node.kind) {
|
|
28033
|
+
case "title":
|
|
28034
|
+
break;
|
|
28035
|
+
case "summary":
|
|
28036
|
+
if (isReport && !g.summary) {
|
|
28037
|
+
pushSummary(node.text);
|
|
28038
|
+
prevKind = node.kind;
|
|
28039
|
+
return;
|
|
28040
|
+
}
|
|
28041
|
+
xml = renderRef({ kind: "ref", depth: 0, text: node.text });
|
|
28042
|
+
break;
|
|
28043
|
+
case "chapter":
|
|
28044
|
+
lastItemFromHeading = true;
|
|
28045
|
+
if (!gaejosik) {
|
|
28046
|
+
xml = renderItem({ kind: "item", depth: 0, text: node.text }, 2);
|
|
28047
|
+
} else if (chapterStyle === "box") {
|
|
28048
|
+
xml = renderItem({ kind: "item", depth: 0, text: node.text });
|
|
28049
|
+
} else xml = renderChapter(node);
|
|
28050
|
+
lastTextNode = node;
|
|
28051
|
+
break;
|
|
28052
|
+
case "item":
|
|
28053
|
+
xml = renderItem(node);
|
|
28054
|
+
lastTextNode = node;
|
|
28055
|
+
lastItemFromHeading = !!node.fromHeading;
|
|
28056
|
+
break;
|
|
28057
|
+
case "ref":
|
|
28058
|
+
xml = renderRef(node);
|
|
28059
|
+
lastTextNode = node;
|
|
28060
|
+
break;
|
|
28061
|
+
// 마크다운 리스트 사이에 본문 문단이 끼면 번호 재시작(리스트 run 종료). 헤딩(##/###)에서 온
|
|
28062
|
+
// 항목은 그 아래 서술 문단이 내용이므로 번호를 잇는다 (기안문 "1. 개요 / 내용 / 2. 성과")
|
|
28063
|
+
case "para":
|
|
28064
|
+
xml = renderPara(node);
|
|
28065
|
+
if (!node.align && !lastItemFromHeading) numberer.reset();
|
|
28066
|
+
lastTextNode = node;
|
|
28067
|
+
break;
|
|
28068
|
+
case "attach":
|
|
28069
|
+
xml = renderAttach(node, !prevAttach);
|
|
28070
|
+
lastTextNode = node;
|
|
28071
|
+
break;
|
|
28072
|
+
case "block":
|
|
28073
|
+
xml = renderBlock(node, idx);
|
|
28074
|
+
if (node.block.type === "table" || node.block.type === "html_table") lastTextNode = node;
|
|
28075
|
+
break;
|
|
28076
|
+
}
|
|
28077
|
+
prevAttach = node.kind === "attach";
|
|
28078
|
+
if (!xml) return;
|
|
28079
|
+
if (pendingPageBreak) {
|
|
28080
|
+
xml = xml.replace(/^<hp:p /, `<hp:p pageBreak="1" `);
|
|
28081
|
+
pendingPageBreak = false;
|
|
28082
|
+
}
|
|
28083
|
+
paras.push(xml);
|
|
28084
|
+
prevKind = node.kind;
|
|
28085
|
+
if (node.kind === "item") prevItemDepth = node.depth;
|
|
28086
|
+
else if (node.kind === "chapter" && gaejosik && chapterStyle === "box") prevItemDepth = 0;
|
|
28087
|
+
});
|
|
28088
|
+
if (g.noticeHead) paras.push(...buildNoticeFoot(g));
|
|
28089
|
+
if (g.endMark && paras.length > 0) {
|
|
28090
|
+
const lastText = lastTextNode && (lastTextNode.kind === "item" || lastTextNode.kind === "para" || lastTextNode.kind === "attach" || lastTextNode.kind === "ref") ? lastTextNode.text : "";
|
|
28091
|
+
if (!hasEndMark(lastText)) {
|
|
28092
|
+
const st = scheme.body;
|
|
28093
|
+
paras.push(generateParagraph(" \uB05D.", reg.para({ align: "LEFT", lineSp: scheme.lineSp }), reg.char({ font: st.font, pt: st.pt })));
|
|
28094
|
+
}
|
|
28095
|
+
}
|
|
28096
|
+
if (preset === "official" && g.docFoot) {
|
|
28097
|
+
const f = g.docFoot;
|
|
28098
|
+
const approvers = (f.approvers ?? [f.drafter, f.reviewer, f.approver].filter((x) => !!x)).map(splitTitleName);
|
|
28099
|
+
const cooperators = (f.cooperator ? f.cooperator.split(/[,、·]/).map((s) => s.trim()).filter(Boolean) : []).map(splitTitleName);
|
|
28100
|
+
paras.push(buildDocFootTable({
|
|
28101
|
+
sender: f.sender,
|
|
28102
|
+
approvers,
|
|
28103
|
+
cooperators,
|
|
28104
|
+
recipients: f.recipients,
|
|
28105
|
+
docNum: f.docNum,
|
|
28106
|
+
receive: f.receive,
|
|
28107
|
+
zip: f.zip,
|
|
28108
|
+
address: f.address,
|
|
28109
|
+
site: f.site,
|
|
28110
|
+
phone: f.phone,
|
|
28111
|
+
fax: f.fax,
|
|
28112
|
+
email: f.email,
|
|
28113
|
+
disclosure: f.disclosure
|
|
28114
|
+
}, frame, isInternalApproval(g.docHead?.to)));
|
|
28115
|
+
}
|
|
28116
|
+
if (paras.length === 0) paras.push(`<hp:p paraPrIDRef="0" styleIDRef="0"><hp:run charPrIDRef="0"><hp:t></hp:t></hp:run></hp:p>`);
|
|
28117
|
+
paras[0] = paras[0].replace(/<hp:run charPrIDRef="(\d+)">/, `<hp:run charPrIDRef="$1">${generateSecPr(g, page)}`);
|
|
28118
|
+
return {
|
|
28119
|
+
xml: `<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
|
|
28120
|
+
<hs:sec xmlns:hs="${NS_SECTION}" xmlns:hp="${NS_PARA}">
|
|
28121
|
+
${paras.join("\n ")}
|
|
28122
|
+
</hs:sec>`,
|
|
28123
|
+
warnings,
|
|
28124
|
+
scheme
|
|
28125
|
+
};
|
|
28126
|
+
}
|
|
28127
|
+
function legalDepthOf(marker) {
|
|
28128
|
+
if (/^\d{1,2}\.$/.test(marker)) return 0;
|
|
28129
|
+
if (/^[가-힣]\.$/.test(marker)) return 1;
|
|
28130
|
+
if (/^\d{1,2}\)$/.test(marker)) return 2;
|
|
28131
|
+
if (/^[가-힣]\)$/.test(marker)) return 3;
|
|
28132
|
+
if (/^\(\d{1,2}\)$/.test(marker)) return 4;
|
|
28133
|
+
if (/^\([가-힣]\)$/.test(marker)) return 5;
|
|
28134
|
+
return 6;
|
|
28135
|
+
}
|
|
28136
|
+
|
|
26832
28137
|
// src/hwpx/generator.ts
|
|
26833
28138
|
async function markdownToHwpx(markdown, options) {
|
|
26834
28139
|
const theme = resolveTheme(options?.theme);
|
|
@@ -26837,6 +28142,42 @@ async function markdownToHwpx(markdown, options) {
|
|
|
26837
28142
|
const { md: md2, defs } = extractFootnoteDefs(markdown);
|
|
26838
28143
|
beginInlineDoc(defs);
|
|
26839
28144
|
try {
|
|
28145
|
+
if (gongmun && usesV5Engine(gongmun.preset)) {
|
|
28146
|
+
const blocks2 = parseMarkdownToBlocks(md2);
|
|
28147
|
+
const staticBfEnd2 = staticBorderFillNext(gongmun, true);
|
|
28148
|
+
const charBase = charVariantBase(false, true);
|
|
28149
|
+
const fontBase = staticFontNext(gongmun, true);
|
|
28150
|
+
const remap2 = options?.profile ? buildProfileRemap(options.profile, charBase, staticBfEnd2, fontBase) : null;
|
|
28151
|
+
const bfReg2 = new TableBfRegistry(staticBfEnd2 + (remap2?.borderFillXmls.length ?? 0));
|
|
28152
|
+
const reg = new StyleRegistry(charBase + (remap2?.charPrXmls.length ?? 0), 8, fontBase + (remap2?.fontFaces.length ?? 0), ["\uD568\uCD08\uB86C\uBC14\uD0D5", "\uD568\uCD08\uB86C\uB3CB\uC6C0", "HY\uACAC\uACE0\uB515"]);
|
|
28153
|
+
const chartParts2 = [];
|
|
28154
|
+
const supplied2 = options?.images ? new Map(Object.entries(options.images).map(([k, v]) => [k, v instanceof Uint8Array ? v : new Uint8Array(v)])) : void 0;
|
|
28155
|
+
const images2 = new ImageRegistry(supplied2);
|
|
28156
|
+
const res = buildGongmunSectionV5(blocks2, gongmun, { reg, bfReg: bfReg2, remap: remap2, images: images2, page, chartParts: chartParts2 }, theme);
|
|
28157
|
+
if (options?.warnings) options.warnings.push(...res.warnings);
|
|
28158
|
+
if (remap2 && remap2.tables.length > 0 && remap2.tables.every((t) => !t.used)) {
|
|
28159
|
+
console.warn(`[kordoc] format profile: \uD504\uB85C\uD544 \uD45C ${remap2.tables.length}\uAC1C\uAC00 \uBB38\uC11C \uD45C\uC640 \uB9E4\uCE6D\uB418\uC9C0 \uC54A\uC544 \uBBF8\uC801\uC6A9 (\uD589\xB7\uC5F4/\uCCAB \uC140 \uD14D\uC2A4\uD2B8 \uBD88\uC77C\uCE58)`);
|
|
28160
|
+
}
|
|
28161
|
+
const zip2 = new JSZip8();
|
|
28162
|
+
zip2.file("mimetype", "application/hwp+zip", { compression: "STORE" });
|
|
28163
|
+
zip2.file("META-INF/container.xml", generateContainerXml());
|
|
28164
|
+
zip2.file("Contents/content.hpf", generateManifest(chartParts2, images2.manifestItems(), "gongmun"));
|
|
28165
|
+
for (const part of images2.parts) zip2.file(part.name, part.data);
|
|
28166
|
+
zip2.file("Contents/header.xml", generateHeaderXml(
|
|
28167
|
+
theme,
|
|
28168
|
+
gongmun,
|
|
28169
|
+
[],
|
|
28170
|
+
[...remap2?.borderFillXmls ?? [], ...bfReg2.emit()],
|
|
28171
|
+
[...remap2?.charPrXmls ?? [], ...reg.charPrXmls],
|
|
28172
|
+
[],
|
|
28173
|
+
[...remap2?.fontFaces ?? [], ...reg.extraFonts],
|
|
28174
|
+
{ paraPrXmls: reg.paraPrXmls, bodyFace: "\uD568\uCD08\uB86C\uBC14\uD0D5" }
|
|
28175
|
+
));
|
|
28176
|
+
zip2.file("Contents/section0.xml", res.xml);
|
|
28177
|
+
for (const part of chartParts2) zip2.file(part.name, part.xml);
|
|
28178
|
+
zip2.file("Preview/PrvText.txt", buildPrvText(blocks2));
|
|
28179
|
+
return await zip2.generateAsync({ type: "arraybuffer" });
|
|
28180
|
+
}
|
|
26840
28181
|
const measured = !!gongmun && usesReportFonts(gongmun.preset);
|
|
26841
28182
|
const richAssets = !!gongmun && needsGaejosikAssets(gongmun);
|
|
26842
28183
|
const blocks = parseMarkdownToBlocks(md2);
|
|
@@ -26848,10 +28189,15 @@ async function markdownToHwpx(markdown, options) {
|
|
|
26848
28189
|
const dfActive = !!gongmun && docframeActive(gongmun);
|
|
26849
28190
|
const dfBase = charVariantBase(richAssets, !!gongmun) + (fit?.variants?.length ?? 0) * 4 + (remap?.charPrXmls.length ?? 0);
|
|
26850
28191
|
const dfIds = dfActive ? docframeIds(dfBase) : null;
|
|
28192
|
+
const dfXmls = dfActive ? docframeCharPrXmls(dfBase, richAssets) : [];
|
|
28193
|
+
const lvBase = dfBase + dfXmls.length;
|
|
28194
|
+
const lvIds = gongmun ? levelCharIds(gongmun, lvBase) : null;
|
|
28195
|
+
const lvFonts = gongmun ? levelFontFaces(gongmun) : [];
|
|
28196
|
+
const lvXmls = gongmun ? levelCharPrXmls(gongmun, lvBase, staticFontNext(gongmun) + (remap?.fontFaces.length ?? 0), richAssets ? 4 : 0) : [];
|
|
26851
28197
|
const chartParts = [];
|
|
26852
28198
|
const supplied = options?.images ? new Map(Object.entries(options.images).map(([k, v]) => [k, v instanceof Uint8Array ? v : new Uint8Array(v)])) : void 0;
|
|
26853
28199
|
const images = new ImageRegistry(supplied);
|
|
26854
|
-
const sectionXml = blocksToSectionXml(blocks, theme, gongmun, gongmunList, fit, chartParts, bfReg, remap, dfIds, images, page);
|
|
28200
|
+
const sectionXml = blocksToSectionXml(blocks, theme, gongmun, gongmunList, fit, chartParts, bfReg, remap, dfIds, images, page, lvIds);
|
|
26855
28201
|
if (remap && remap.tables.length > 0) {
|
|
26856
28202
|
const unused = remap.tables.filter((t) => !t.used).length;
|
|
26857
28203
|
if (unused === remap.tables.length) {
|
|
@@ -26869,9 +28215,9 @@ async function markdownToHwpx(markdown, options) {
|
|
|
26869
28215
|
gongmun,
|
|
26870
28216
|
fit?.variants ?? [],
|
|
26871
28217
|
extraBorderFills,
|
|
26872
|
-
[...remap?.charPrXmls ?? [], ...
|
|
28218
|
+
[...remap?.charPrXmls ?? [], ...dfXmls, ...lvXmls],
|
|
26873
28219
|
gongmunList?.indentVariants ?? [],
|
|
26874
|
-
remap?.fontFaces ?? []
|
|
28220
|
+
[...remap?.fontFaces ?? [], ...lvFonts]
|
|
26875
28221
|
));
|
|
26876
28222
|
zip.file("Contents/section0.xml", sectionXml);
|
|
26877
28223
|
for (const part of chartParts) zip.file(part.name, part.xml);
|
|
@@ -28023,10 +29369,12 @@ var DISCRIM_FIXES = [
|
|
|
28023
29369
|
var DISCRIM_RE = new RegExp(DISCRIM_FIXES.map(([w]) => w).sort((a, b) => b.length - a.length).join("|"), "g");
|
|
28024
29370
|
var RULES = [
|
|
28025
29371
|
// 날짜 ─ 온점 뒤 한 칸, 0 패딩 금지, 연도 4자리, 끝 마침표
|
|
29372
|
+
// 법령 연혁 표기 `<개정 2012.2.14>`·`삭제 <2016.2.29.>`·`[시행일:2017.9.8.]` 는 법제처 정본 형식(별지서식 표제) —
|
|
29373
|
+
// 편람 날짜 규칙 대상이 아니다 (v4.12.3: 서식 595건 DATE 156건 전부 이 꼴, 기안문 0건)
|
|
28026
29374
|
{
|
|
28027
29375
|
code: "DATE_NO_SPACE",
|
|
28028
29376
|
severity: "error",
|
|
28029
|
-
pattern:
|
|
29377
|
+
pattern: /(?<!<(?:개정|신설|전문개정|전부개정|일부개정|제정)\s*)(?<!삭제\s*<)(?<!시행일\s*:\s*)\b\d{4}\.\d{1,2}\.\d{1,2}\.?/g,
|
|
28030
29378
|
message: "\uB0A0\uC9DC \uC628\uC810 \uB4A4\uC5D0 \uD55C \uCE78\uC529 \uB744\uC6CC\uC57C \uD568",
|
|
28031
29379
|
suggest: "\uC608) 2025. 1. 6."
|
|
28032
29380
|
},
|
|
@@ -28051,6 +29399,16 @@ var RULES = [
|
|
|
28051
29399
|
message: "\uB0A0\uC9DC\uC758 '\uC77C' \uB2E4\uC74C\uC5D0 \uB9C8\uCE68\uD45C(.)\uB97C \uCC0D\uC5B4\uC57C \uD568",
|
|
28052
29400
|
suggest: "\uC608) 2025. 1. 6."
|
|
28053
29401
|
},
|
|
29402
|
+
// 하이픈·ISO 날짜(2026-07-18) — 편람은 온점 구분만 인정. URL 경로·파일명·코드 안(앞뒤에 / = & ? # % . - 영숫자)은
|
|
29403
|
+
// 제외, 표 셀은 서식 기입란 관행이 섞여 건너뜀. 제안은 실제 변환값(0 패딩 제거)
|
|
29404
|
+
{
|
|
29405
|
+
code: "DATE_HYPHEN",
|
|
29406
|
+
severity: "warning",
|
|
29407
|
+
skipTable: true,
|
|
29408
|
+
pattern: /(?<![\/=&?#%.\w-])(?:19|20)\d{2}-\d{1,2}-\d{1,2}(?![\/\w-])/g,
|
|
29409
|
+
message: "\uB0A0\uC9DC\uB294 \uD558\uC774\uD508(-) \uB300\uC2E0 \uC628\uC810\uC73C\uB85C \uAD6C\uBD84\uD558\uACE0 \uC628\uC810 \uB4A4 \uD55C \uCE78 \uB744\uC6C0",
|
|
29410
|
+
suggest: "\uC608) 2026. 7. 18."
|
|
29411
|
+
},
|
|
28054
29412
|
// 시간 ─ 24시각제, 쌍점 붙여쓰기
|
|
28055
29413
|
{
|
|
28056
29414
|
code: "TIME_AMPM",
|
|
@@ -28112,10 +29470,15 @@ var RULES = [
|
|
|
28112
29470
|
suggest: "\uC608) \uC5C5\uBB34 \uD611\uC57D(MOU)"
|
|
28113
29471
|
},
|
|
28114
29472
|
// 쌍점 — URL(https:// 등)·시각(13:20)은 제외
|
|
29473
|
+
// 편람: 쌍점은 앞말에 붙이고 뒤는 1타(원장: 김갑동). 표 셀은 제외(skipTable) — 법정 별지서식
|
|
29474
|
+
// 자체가 "성 명 :"·"주 소 :" 라벨 셀을 쓰므로 서식 라벨은 이 규칙의 대상이 아니다(v4.12.3 실측:
|
|
29475
|
+
// 서식 595건의 표 셀 801건 전부 라벨, 기안문 206건은 본문 414건이 "일 시 : 값" 꼴 — 편람상 위반).
|
|
29476
|
+
// 뒤 글자에서 `<`(HTML 태그)·`*`·`_`(마크다운 강조 닫힘)는 제외 — "성명:</td>"·"과정:** 붙임" 오탐
|
|
28115
29477
|
{
|
|
28116
29478
|
code: "COLON_SPACE",
|
|
28117
29479
|
severity: "warning",
|
|
28118
|
-
|
|
29480
|
+
skipTable: true,
|
|
29481
|
+
pattern: /\S\s+:(?!\/\/)|\S:(?!\/\/)[^\s\d<*_]/g,
|
|
28119
29482
|
message: "\uC30D\uC810\uC740 \uC55E\uB9D0\uC5D0 \uBD99\uC774\uACE0 \uB4A4\uB294 \uD55C \uCE78 \uB744\uC6C0",
|
|
28120
29483
|
suggest: "\uC608) \uC6D0\uC7A5: \uAE40\uAC11\uB3D9"
|
|
28121
29484
|
},
|
|
@@ -28195,7 +29558,9 @@ function lintGongmunText(text, opts) {
|
|
|
28195
29558
|
continue;
|
|
28196
29559
|
}
|
|
28197
29560
|
if (fenceMarker !== null) continue;
|
|
29561
|
+
const tableLine = /^\s*\|/.test(line) || /<t[dhr][\s>]/i.test(line);
|
|
28198
29562
|
for (const r of RULES) {
|
|
29563
|
+
if (r.skipTable && tableLine) continue;
|
|
28199
29564
|
r.pattern.lastIndex = 0;
|
|
28200
29565
|
for (const m of line.matchAll(r.pattern)) {
|
|
28201
29566
|
findings.push({
|
|
@@ -28222,6 +29587,12 @@ function lintGongmunText(text, opts) {
|
|
|
28222
29587
|
return findings;
|
|
28223
29588
|
}
|
|
28224
29589
|
function dictSuggest(code, match) {
|
|
29590
|
+
const m = match.trim();
|
|
29591
|
+
if (code === "MONEY_NO_HANGUL") return `${m} \u2192 ${m}(\uAE08${hangulAmount(m)}\uC6D0)`;
|
|
29592
|
+
if (code === "DATE_HYPHEN") {
|
|
29593
|
+
const d = m.match(/^(\d{4})-(\d{1,2})-(\d{1,2})$/);
|
|
29594
|
+
if (d) return `${m} \u2192 ${d[1]}. ${Number(d[2])}. ${Number(d[3])}.`;
|
|
29595
|
+
}
|
|
28225
29596
|
const table2 = code === "LOANWORD_ERROR" ? LOANWORD_FIXES : code === "DISCRIMINATORY_TERM" ? DISCRIM_FIXES : null;
|
|
28226
29597
|
if (!table2) return void 0;
|
|
28227
29598
|
const hit = table2.find(([w]) => w === match.trim());
|
|
@@ -28258,7 +29629,7 @@ function classify(raw) {
|
|
|
28258
29629
|
if (li) return { kind: indent >= 2 ? "sub" : "item", body: li[1].trim() };
|
|
28259
29630
|
return { kind: "para", body: t };
|
|
28260
29631
|
}
|
|
28261
|
-
function
|
|
29632
|
+
function plain2(s) {
|
|
28262
29633
|
return s.replace(/!?\[([^\]]*)\]\([^)]*\)/g, "$1").replace(/(\*\*|__|~~|`)/g, "").trim();
|
|
28263
29634
|
}
|
|
28264
29635
|
function stripQuoted(s) {
|
|
@@ -28361,11 +29732,11 @@ function lintMuncheText(text) {
|
|
|
28361
29732
|
continue;
|
|
28362
29733
|
}
|
|
28363
29734
|
if (c.kind === "lead") {
|
|
28364
|
-
leadBuf.push({ line: no, body:
|
|
29735
|
+
leadBuf.push({ line: no, body: plain2(c.body) });
|
|
28365
29736
|
continue;
|
|
28366
29737
|
}
|
|
28367
29738
|
flushLead();
|
|
28368
|
-
const b =
|
|
29739
|
+
const b = plain2(c.body);
|
|
28369
29740
|
if (!b) continue;
|
|
28370
29741
|
const q = stripQuoted(b);
|
|
28371
29742
|
if (c.kind === "item" || c.kind === "sub" || c.kind === "concl" || c.kind === "para") {
|
|
@@ -31677,6 +33048,36 @@ function reflowSection(root, styles, geom, mode = "keep") {
|
|
|
31677
33048
|
return counter.n;
|
|
31678
33049
|
}
|
|
31679
33050
|
|
|
33051
|
+
// src/render/scene.ts
|
|
33052
|
+
function regionId(type, seq) {
|
|
33053
|
+
return `${type}-${String(seq).padStart(6, "0")}`;
|
|
33054
|
+
}
|
|
33055
|
+
function unionBBox(a, b) {
|
|
33056
|
+
const x = Math.min(a.x, b.x), y = Math.min(a.y, b.y);
|
|
33057
|
+
return { page: a.page, x, y, width: Math.max(a.x + a.width, b.x + b.width) - x, height: Math.max(a.y + a.height, b.y + b.height) - y };
|
|
33058
|
+
}
|
|
33059
|
+
var RegionCollector = class {
|
|
33060
|
+
regions = [];
|
|
33061
|
+
counters = /* @__PURE__ */ new Map();
|
|
33062
|
+
add(type, bbox, extra = {}) {
|
|
33063
|
+
const seq = (this.counters.get(type) ?? 0) + 1;
|
|
33064
|
+
this.counters.set(type, seq);
|
|
33065
|
+
const id = regionId(type, seq);
|
|
33066
|
+
const region = { id, type, page: bbox.page, regions: [bbox] };
|
|
33067
|
+
if (extra.sourceId) region.sourceId = extra.sourceId;
|
|
33068
|
+
if (extra.parentId) region.parentId = extra.parentId;
|
|
33069
|
+
this.regions.push(region);
|
|
33070
|
+
return id;
|
|
33071
|
+
}
|
|
33072
|
+
addFragment(id, bbox) {
|
|
33073
|
+
const r = this.regions.find((x) => x.id === id);
|
|
33074
|
+
if (!r) return;
|
|
33075
|
+
const same = r.regions.find((f) => f.page === bbox.page);
|
|
33076
|
+
if (same) Object.assign(same, unionBBox(same, bbox));
|
|
33077
|
+
else r.regions.push(bbox);
|
|
33078
|
+
}
|
|
33079
|
+
};
|
|
33080
|
+
|
|
31680
33081
|
// src/render/svg-render.ts
|
|
31681
33082
|
function ln2(el) {
|
|
31682
33083
|
return (el.tagName || "").replace(/^[^:]+:/, "");
|
|
@@ -31702,11 +33103,25 @@ function findFirst(el, name, depth = 0) {
|
|
|
31702
33103
|
}
|
|
31703
33104
|
return null;
|
|
31704
33105
|
}
|
|
31705
|
-
function
|
|
33106
|
+
function escapeXml4(s) {
|
|
31706
33107
|
return s.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F]/g, "").replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
31707
33108
|
}
|
|
31708
33109
|
var OBJ_TAGS = /* @__PURE__ */ new Set(["tbl", "pic", "container", "equation", "rect", "ellipse", "polygon", "curv", "line", "arc", "ole", "textart"]);
|
|
31709
33110
|
var pt = (u) => String(Math.round(u) / 100);
|
|
33111
|
+
var ptNum = (u) => Math.round(u) / 100;
|
|
33112
|
+
function pageNo(ctx) {
|
|
33113
|
+
return ctx.pageBase + ctx.page + 1;
|
|
33114
|
+
}
|
|
33115
|
+
function bboxOf(ctx, x, y, w, h) {
|
|
33116
|
+
const clippedH = Math.max(0, Math.min(h, ctx.pageH - y));
|
|
33117
|
+
return { page: pageNo(ctx), x: ptNum(x), y: ptNum(y), width: ptNum(Math.max(0, w)), height: ptNum(clippedH) };
|
|
33118
|
+
}
|
|
33119
|
+
function regionOpenTag(id, type, page) {
|
|
33120
|
+
return `<g data-kordoc-id="${id}" data-kordoc-type="${type}" data-kordoc-page="${page}">`;
|
|
33121
|
+
}
|
|
33122
|
+
function parentId(ctx) {
|
|
33123
|
+
return ctx.parentStack.length ? ctx.parentStack[ctx.parentStack.length - 1] : void 0;
|
|
33124
|
+
}
|
|
31710
33125
|
function emit(ctx, s) {
|
|
31711
33126
|
ctx.pages[ctx.page].push(s);
|
|
31712
33127
|
}
|
|
@@ -31811,18 +33226,18 @@ function planLines(m, styles) {
|
|
|
31811
33226
|
const nat = lineNaturalWidth(m, styles, start, end);
|
|
31812
33227
|
const isLast = i === m.segs.length - 1;
|
|
31813
33228
|
let xoff = 0;
|
|
31814
|
-
let
|
|
33229
|
+
let scale2 = 1;
|
|
31815
33230
|
const avail = seg.horzsize - nat.obj;
|
|
31816
33231
|
if (nat.text > 0 && (!isLast || align === "DISTRIBUTE" || align === "DISTRIBUTE_SPACE")) {
|
|
31817
|
-
|
|
33232
|
+
scale2 = avail > 0 ? avail / nat.text : 1;
|
|
31818
33233
|
} else if (nat.text + nat.obj > 0 && isLast) {
|
|
31819
33234
|
const w = nat.text + nat.obj;
|
|
31820
33235
|
if (align === "CENTER") xoff = Math.max(0, (seg.horzsize - w) / 2);
|
|
31821
33236
|
else if (align === "RIGHT") xoff = Math.max(0, seg.horzsize - w);
|
|
31822
33237
|
}
|
|
31823
|
-
if (!Number.isFinite(
|
|
31824
|
-
|
|
31825
|
-
plans.push({ seg, xoff, scale, start, end });
|
|
33238
|
+
if (!Number.isFinite(scale2) || scale2 <= 0) scale2 = 1;
|
|
33239
|
+
scale2 = Math.min(4, Math.max(0.25, scale2));
|
|
33240
|
+
plans.push({ seg, xoff, scale: scale2, start, end });
|
|
31826
33241
|
}
|
|
31827
33242
|
return plans;
|
|
31828
33243
|
}
|
|
@@ -31847,10 +33262,23 @@ function drawPara(p, ox, oy, areaW, ctx, depth, segPages) {
|
|
|
31847
33262
|
}
|
|
31848
33263
|
const plans = planLines(m, ctx.styles);
|
|
31849
33264
|
const baseV = m.segs[0].vertpos;
|
|
33265
|
+
const hasText = m.chars.some((c) => c.ch !== "" && c.ch.trim() !== "");
|
|
33266
|
+
let paraRegionId = null;
|
|
33267
|
+
let gOpenOn = -1;
|
|
31850
33268
|
for (let li = 0; li < plans.length; li++) {
|
|
31851
33269
|
const plan = plans[li];
|
|
31852
33270
|
if (segPages && segPages[li] !== void 0) ctx.page = segPages[li];
|
|
31853
33271
|
const { seg } = plan;
|
|
33272
|
+
if (hasText) {
|
|
33273
|
+
const lineBox = bboxOf(ctx, ox + seg.horzpos, oy + seg.vertpos, seg.horzsize, seg.textheight);
|
|
33274
|
+
if (paraRegionId === null) paraRegionId = ctx.regions.add("paragraph", lineBox, { parentId: parentId(ctx) });
|
|
33275
|
+
else ctx.regions.addFragment(paraRegionId, lineBox);
|
|
33276
|
+
if (gOpenOn !== ctx.page) {
|
|
33277
|
+
if (gOpenOn >= 0) ctx.pages[gOpenOn].push("</g>");
|
|
33278
|
+
emit(ctx, regionOpenTag(paraRegionId, "paragraph", pageNo(ctx)));
|
|
33279
|
+
gOpenOn = ctx.page;
|
|
33280
|
+
}
|
|
33281
|
+
}
|
|
31854
33282
|
let i = plan.start;
|
|
31855
33283
|
let cursor = ox + seg.horzpos + plan.xoff;
|
|
31856
33284
|
const y = oy + seg.vertpos + seg.baseline;
|
|
@@ -31886,15 +33314,15 @@ function drawPara(p, ox, oy, areaW, ctx, depth, segPages) {
|
|
|
31886
33314
|
}
|
|
31887
33315
|
if (text.trim().length > 0) {
|
|
31888
33316
|
const attrs = [`x="${pt(cx)}"`, `y="${pt(y)}"`, `font-size="${pt(st.height)}"`];
|
|
31889
|
-
if (st.fontFamily) attrs.push(`font-family="${
|
|
33317
|
+
if (st.fontFamily) attrs.push(`font-family="${escapeXml4(st.fontFamily)}"`);
|
|
31890
33318
|
if ([...text].length > 1 && sw > 50) {
|
|
31891
33319
|
attrs.push(`textLength="${pt(sw)}"`, `lengthAdjust="${plan.scale < 1 ? "spacingAndGlyphs" : "spacing"}"`);
|
|
31892
33320
|
}
|
|
31893
33321
|
if (st.bold) attrs.push(`font-weight="bold"`);
|
|
31894
33322
|
if (st.italic) attrs.push(`font-style="italic"`);
|
|
31895
33323
|
if (st.underline) attrs.push(`text-decoration="underline"`);
|
|
31896
|
-
if (st.color) attrs.push(`fill="${
|
|
31897
|
-
emit(ctx, `<text ${attrs.join(" ")}>${
|
|
33324
|
+
if (st.color) attrs.push(`fill="${escapeXml4(st.color)}"`);
|
|
33325
|
+
emit(ctx, `<text ${attrs.join(" ")}>${escapeXml4(text)}</text>`);
|
|
31898
33326
|
ctx.stats.texts++;
|
|
31899
33327
|
}
|
|
31900
33328
|
return sw;
|
|
@@ -31931,6 +33359,8 @@ function drawPara(p, ox, oy, areaW, ctx, depth, segPages) {
|
|
|
31931
33359
|
i = j;
|
|
31932
33360
|
}
|
|
31933
33361
|
}
|
|
33362
|
+
if (gOpenOn >= 0) ctx.pages[gOpenOn].push("</g>");
|
|
33363
|
+
if (paraRegionId) ctx.parentStack.push(paraRegionId);
|
|
31934
33364
|
for (const o of m.objs) {
|
|
31935
33365
|
if (o.inline) {
|
|
31936
33366
|
let planIdx = 0;
|
|
@@ -31949,6 +33379,7 @@ function drawPara(p, ox, oy, areaW, ctx, depth, segPages) {
|
|
|
31949
33379
|
drawObject(o, x, y, baseV, areaW, ctx, depth);
|
|
31950
33380
|
}
|
|
31951
33381
|
}
|
|
33382
|
+
if (paraRegionId) ctx.parentStack.pop();
|
|
31952
33383
|
}
|
|
31953
33384
|
function anchorObject(o, ox, oy, baseV, areaW, ctx) {
|
|
31954
33385
|
const { PW, PH, ML, MT, BODY_W, BODY_H } = ctx.geom;
|
|
@@ -32025,12 +33456,16 @@ function drawShape(o, x, y, ctx, depth) {
|
|
|
32025
33456
|
const hasStroke = lstyle !== "NONE";
|
|
32026
33457
|
const strokeW = hasStroke ? shapeStrokePt(lineShape ? num3(lineShape, "width") : 33) : 0;
|
|
32027
33458
|
const dash = /DASH|DOT/.test(lstyle) ? ` stroke-dasharray="${lstyle.includes("DOT") ? "1,1.5" : "3,1.5"}"` : "";
|
|
32028
|
-
const strokeAttr = hasStroke ? ` stroke="${
|
|
33459
|
+
const strokeAttr = hasStroke ? ` stroke="${escapeXml4(strokeCol)}" stroke-width="${strokeW.toFixed(2)}"${dash}` : "";
|
|
32029
33460
|
const fillBrush = findChildByLocalName(el, "fillBrush");
|
|
32030
33461
|
const winBrush = fillBrush ? findChildByLocalName(fillBrush, "winBrush") : null;
|
|
32031
33462
|
const face = winBrush?.getAttribute("faceColor");
|
|
32032
33463
|
const fill = face && face.toLowerCase() !== "none" ? face : "none";
|
|
32033
|
-
const fillAttr = ` fill="${fill === "none" ? "none" :
|
|
33464
|
+
const fillAttr = ` fill="${fill === "none" ? "none" : escapeXml4(fill)}"`;
|
|
33465
|
+
ctx.stats.shapes++;
|
|
33466
|
+
const shapeId = ctx.regions.add("shape", bboxOf(ctx, x, y, w, h), { parentId: parentId(ctx) });
|
|
33467
|
+
emit(ctx, regionOpenTag(shapeId, "shape", pageNo(ctx)));
|
|
33468
|
+
ctx.parentStack.push(shapeId);
|
|
32034
33469
|
if (o.tag === "rect") {
|
|
32035
33470
|
emit(ctx, `<rect x="${pt(x)}" y="${pt(y)}" width="${pt(w)}" height="${pt(h)}"${fillAttr}${strokeAttr}/>`);
|
|
32036
33471
|
} else if (o.tag === "ellipse") {
|
|
@@ -32039,19 +33474,21 @@ function drawShape(o, x, y, ctx, depth) {
|
|
|
32039
33474
|
const s = findChildByLocalName(el, "startPt"), e = findChildByLocalName(el, "endPt");
|
|
32040
33475
|
const x1 = x + num3(s, "x") * sx, y1 = y + num3(s, "y") * sy;
|
|
32041
33476
|
const x2 = x + num3(e, "x") * sx, y2 = y + num3(e, "y") * sy;
|
|
32042
|
-
emit(ctx, `<line x1="${pt(x1)}" y1="${pt(y1)}" x2="${pt(x2)}" y2="${pt(y2)}" stroke="${
|
|
33477
|
+
emit(ctx, `<line x1="${pt(x1)}" y1="${pt(y1)}" x2="${pt(x2)}" y2="${pt(y2)}" stroke="${escapeXml4(strokeCol)}" stroke-width="${(strokeW || 0.3).toFixed(2)}"${dash}/>`);
|
|
32043
33478
|
} else if (o.tag === "polygon" || o.tag === "curv") {
|
|
32044
33479
|
const pts = [];
|
|
32045
33480
|
for (const c of elements2(el)) if (ln2(c) === "pt") pts.push(`${pt(x + num3(c, "x") * sx)},${pt(y + num3(c, "y") * sy)}`);
|
|
32046
33481
|
if (pts.length >= 2) emit(ctx, `<polygon points="${pts.join(" ")}"${fillAttr}${strokeAttr}/>`);
|
|
32047
33482
|
} else if (o.tag === "arc") {
|
|
32048
|
-
emit(ctx, `<ellipse cx="${pt(x + w / 2)}" cy="${pt(y + h / 2)}" rx="${pt(w / 2)}" ry="${pt(h / 2)}" fill="none"${strokeAttr || ` stroke="${
|
|
33483
|
+
emit(ctx, `<ellipse cx="${pt(x + w / 2)}" cy="${pt(y + h / 2)}" rx="${pt(w / 2)}" ry="${pt(h / 2)}" fill="none"${strokeAttr || ` stroke="${escapeXml4(strokeCol)}" stroke-width="0.3"`}/>`);
|
|
32049
33484
|
}
|
|
32050
33485
|
const dt = findChildByLocalName(el, "drawText");
|
|
32051
33486
|
const sub = dt ? findChildByLocalName(dt, "subList") : null;
|
|
32052
33487
|
if (sub) {
|
|
32053
33488
|
for (const p of elements2(sub)) if (ln2(p) === "p") drawPara(p, x, y, w, ctx, depth + 1);
|
|
32054
33489
|
}
|
|
33490
|
+
ctx.parentStack.pop();
|
|
33491
|
+
emit(ctx, "</g>");
|
|
32055
33492
|
}
|
|
32056
33493
|
function cellContentExtent(cell2, memo) {
|
|
32057
33494
|
if (!cell2.sub) return 0;
|
|
@@ -32082,7 +33519,7 @@ function cellContentExtent(cell2, memo) {
|
|
|
32082
33519
|
}
|
|
32083
33520
|
function edgeLine(x1, y1, x2, y2, e) {
|
|
32084
33521
|
const dash = /DASH|DOT/.test(e.type) ? ` stroke-dasharray="${e.type.includes("DOT") ? "1,1.5" : "3,1.5"}"` : "";
|
|
32085
|
-
return `<line x1="${pt(x1)}" y1="${pt(y1)}" x2="${pt(x2)}" y2="${pt(y2)}" stroke="${
|
|
33522
|
+
return `<line x1="${pt(x1)}" y1="${pt(y1)}" x2="${pt(x2)}" y2="${pt(y2)}" stroke="${escapeXml4(e.color)}" stroke-width="${e.widthPt.toFixed(2)}"${dash}/>`;
|
|
32086
33523
|
}
|
|
32087
33524
|
function collectCells(tbl2) {
|
|
32088
33525
|
const inMargin = findChildByLocalName(tbl2, "inMargin");
|
|
@@ -32152,6 +33589,11 @@ function drawTable(tbl2, tx, ty, ctx, depth) {
|
|
|
32152
33589
|
);
|
|
32153
33590
|
const rowY = [0];
|
|
32154
33591
|
for (let r = 0; r < nRows; r++) rowY.push(rowY[r] + rowH[r]);
|
|
33592
|
+
const tblH = rowY[nRows];
|
|
33593
|
+
if (ty + tblH > ctx.pageH + 100) warnOnce(ctx, "table-split", "\uD398\uC774\uC9C0\uC5D0 \uAC78\uCE5C \uD45C \u2014 \uBD84\uD560 \uC9C0\uC624\uBA54\uD2B8\uB9AC\uAC00 \uC5C6\uC5B4 \uC2DC\uC791 \uD398\uC774\uC9C0 \uC601\uC5ED\uB9CC region \uC73C\uB85C \uAE30\uB85D");
|
|
33594
|
+
const tblId = ctx.regions.add("table", bboxOf(ctx, tx, ty, colX[nCols], tblH), { sourceId: tbl2.getAttribute("id") ?? void 0, parentId: parentId(ctx) });
|
|
33595
|
+
emit(ctx, regionOpenTag(tblId, "table", pageNo(ctx)));
|
|
33596
|
+
ctx.parentStack.push(tblId);
|
|
32155
33597
|
const geom = cells.map((c) => ({
|
|
32156
33598
|
c,
|
|
32157
33599
|
x: tx + colX[c.ca],
|
|
@@ -32161,7 +33603,7 @@ function drawTable(tbl2, tx, ty, ctx, depth) {
|
|
|
32161
33603
|
}));
|
|
32162
33604
|
for (const g of geom) {
|
|
32163
33605
|
const bf = g.c.bfId != null ? ctx.styles.borderFill.get(g.c.bfId) : void 0;
|
|
32164
|
-
if (bf?.fill) emit(ctx, `<rect x="${pt(g.x)}" y="${pt(g.y)}" width="${pt(g.w)}" height="${pt(g.h)}" fill="${
|
|
33606
|
+
if (bf?.fill) emit(ctx, `<rect x="${pt(g.x)}" y="${pt(g.y)}" width="${pt(g.w)}" height="${pt(g.h)}" fill="${escapeXml4(bf.fill)}"/>`);
|
|
32165
33607
|
}
|
|
32166
33608
|
for (const g of geom) {
|
|
32167
33609
|
const { c } = g;
|
|
@@ -32185,6 +33627,8 @@ function drawTable(tbl2, tx, ty, ctx, depth) {
|
|
|
32185
33627
|
if (bf.left) emit(ctx, edgeLine(g.x, g.y, g.x, g.y + g.h, bf.left));
|
|
32186
33628
|
if (bf.right) emit(ctx, edgeLine(g.x + g.w, g.y, g.x + g.w, g.y + g.h, bf.right));
|
|
32187
33629
|
}
|
|
33630
|
+
ctx.parentStack.pop();
|
|
33631
|
+
emit(ctx, "</g>");
|
|
32188
33632
|
}
|
|
32189
33633
|
function imageSymbol(loaded, ctx) {
|
|
32190
33634
|
if (!loaded.symId) {
|
|
@@ -32201,9 +33645,12 @@ function drawPic(pic, x, y, ctx) {
|
|
|
32201
33645
|
const img = findFirst(pic, "img");
|
|
32202
33646
|
const ref = img?.getAttribute("binaryItemIDRef");
|
|
32203
33647
|
const loaded = ref != null ? ctx.images.get(ref) : void 0;
|
|
33648
|
+
const imgId = ctx.regions.add("image", bboxOf(ctx, x, y, w, h), { sourceId: ref ?? void 0, parentId: parentId(ctx) });
|
|
33649
|
+
emit(ctx, regionOpenTag(imgId, "image", pageNo(ctx)));
|
|
32204
33650
|
if (!loaded) {
|
|
32205
33651
|
emit(ctx, `<rect x="${pt(x)}" y="${pt(y)}" width="${pt(w)}" height="${pt(h)}" fill="#eee" stroke="#c00" stroke-width="0.5"/>`);
|
|
32206
33652
|
warnOnce(ctx, `img:${ref}`, `\uC774\uBBF8\uC9C0 \uBC14\uC774\uB108\uB9AC \uB204\uB77D: ${ref ?? "(ref \uC5C6\uC74C)"}`);
|
|
33653
|
+
emit(ctx, "</g>");
|
|
32207
33654
|
return;
|
|
32208
33655
|
}
|
|
32209
33656
|
ctx.stats.images++;
|
|
@@ -32225,6 +33672,7 @@ function drawPic(pic, x, y, ctx) {
|
|
|
32225
33672
|
} else {
|
|
32226
33673
|
emit(ctx, `<use href="#${symId}" x="${pt(x)}" y="${pt(y)}" width="${pt(w)}" height="${pt(h)}"/>`);
|
|
32227
33674
|
}
|
|
33675
|
+
emit(ctx, "</g>");
|
|
32228
33676
|
}
|
|
32229
33677
|
function sniffMime(name, bytes) {
|
|
32230
33678
|
const lower = name.toLowerCase();
|
|
@@ -32283,20 +33731,22 @@ function renderSectionToPages(root, geom, ctxBase, doReflow, reflowMode) {
|
|
|
32283
33731
|
nPages = Math.max(nPages, cur + 1);
|
|
32284
33732
|
}
|
|
32285
33733
|
}
|
|
33734
|
+
const pageH = nPages === 1 ? Math.max(PH, MT + maxTopV + 2e3) : PH;
|
|
32286
33735
|
const ctx = {
|
|
32287
33736
|
...ctxBase,
|
|
32288
33737
|
pages: Array.from({ length: nPages }, () => []),
|
|
32289
33738
|
page: 0,
|
|
32290
|
-
geom
|
|
33739
|
+
geom,
|
|
33740
|
+
pageH
|
|
32291
33741
|
};
|
|
32292
33742
|
for (const p of elements2(root)) {
|
|
32293
33743
|
if (ln2(p) !== "p") continue;
|
|
32294
33744
|
drawPara(p, ML, MT, BODY_W, ctx, 0, paraSegPages.get(p));
|
|
32295
33745
|
}
|
|
32296
|
-
const pageH = nPages === 1 ? Math.max(PH, MT + maxTopV + 2e3) : PH;
|
|
32297
33746
|
return { pages: ctx.pages, pageH };
|
|
32298
33747
|
}
|
|
32299
|
-
|
|
33748
|
+
var SVG_FONT_FAMILY = `'HCR Batang','\uD568\uCD08\uB86C\uBC14\uD0D5','Hancom Batang',AppleMyungjo,'Noto Serif CJK KR',serif`;
|
|
33749
|
+
async function renderHwpxInternal(input, options) {
|
|
32300
33750
|
const maxImg = options?.maxImageBytes ?? 40 * 1024 * 1024;
|
|
32301
33751
|
const ab = input instanceof Uint8Array ? input.buffer.slice(input.byteOffset, input.byteOffset + input.byteLength) : input;
|
|
32302
33752
|
precheckZipSize(ab, MAX_DECOMPRESS_SIZE, MAX_ZIP_ENTRIES);
|
|
@@ -32363,8 +33813,11 @@ async function renderHwpxToSvg(input, options) {
|
|
|
32363
33813
|
highlights: (options?.highlights ?? []).map((s) => s.trim().toLowerCase()).filter((s) => s.length > 0),
|
|
32364
33814
|
warnings,
|
|
32365
33815
|
warned: /* @__PURE__ */ new Set(),
|
|
32366
|
-
stats: { texts: 0, images: 0, tables: 0 },
|
|
32367
|
-
extentMemo: { cell: /* @__PURE__ */ new WeakMap(), table: /* @__PURE__ */ new WeakMap() }
|
|
33816
|
+
stats: { texts: 0, images: 0, tables: 0, shapes: 0 },
|
|
33817
|
+
extentMemo: { cell: /* @__PURE__ */ new WeakMap(), table: /* @__PURE__ */ new WeakMap() },
|
|
33818
|
+
regions: new RegionCollector(),
|
|
33819
|
+
parentStack: [],
|
|
33820
|
+
pageBase: 0
|
|
32368
33821
|
};
|
|
32369
33822
|
const rendered = [];
|
|
32370
33823
|
let noCacheSkipped = false;
|
|
@@ -32385,6 +33838,7 @@ async function renderHwpxToSvg(input, options) {
|
|
|
32385
33838
|
const geom = readSectionGeom(root);
|
|
32386
33839
|
const { pages, pageH } = renderSectionToPages(root, geom, ctxBase, !!options?.reflow, options?.reflowMode ?? "keep");
|
|
32387
33840
|
rendered.push({ pages, PW: geom.PW, pageH, clipId: `pgclip${si}` });
|
|
33841
|
+
ctxBase.pageBase += pages.length;
|
|
32388
33842
|
}
|
|
32389
33843
|
if (rendered.length === 0) {
|
|
32390
33844
|
if (noCacheSkipped) {
|
|
@@ -32392,19 +33846,23 @@ async function renderHwpxToSvg(input, options) {
|
|
|
32392
33846
|
}
|
|
32393
33847
|
throw new KordocError("\uB80C\uB354\uD560 \uAD6C\uC5ED\uC774 \uC5C6\uC2B5\uB2C8\uB2E4 \u2014 HWPX\uAC00 \uC190\uC0C1\uB418\uC5C8\uC744 \uC218 \uC788\uC2B5\uB2C8\uB2E4");
|
|
32394
33848
|
}
|
|
33849
|
+
return { sections: rendered, defs: ctxBase.defs, warnings, stats: ctxBase.stats, regions: ctxBase.regions.regions };
|
|
33850
|
+
}
|
|
33851
|
+
async function renderHwpxToSvg(input, options) {
|
|
33852
|
+
const r = await renderHwpxInternal(input, options);
|
|
32395
33853
|
const GAP = 2400;
|
|
32396
33854
|
const clipDefs = [];
|
|
32397
33855
|
const groups = [];
|
|
32398
33856
|
let y = 0;
|
|
32399
33857
|
let maxPW = 0;
|
|
32400
|
-
let
|
|
32401
|
-
for (const rs of
|
|
33858
|
+
let pageNo2 = 0;
|
|
33859
|
+
for (const rs of r.sections) {
|
|
32402
33860
|
maxPW = Math.max(maxPW, rs.PW);
|
|
32403
33861
|
clipDefs.push(`<clipPath id="${rs.clipId}"><rect x="0" y="0" width="${pt(rs.PW)}" height="${pt(rs.pageH)}"/></clipPath>`);
|
|
32404
33862
|
for (const buf of rs.pages) {
|
|
32405
|
-
|
|
33863
|
+
pageNo2++;
|
|
32406
33864
|
groups.push(
|
|
32407
|
-
`<g data-page="${
|
|
33865
|
+
`<g data-page="${pageNo2}" transform="translate(0 ${pt(y)})"><rect width="${pt(rs.PW)}" height="${pt(rs.pageH)}" fill="white" stroke="#c9c7c4" stroke-width="0.75"/><g clip-path="url(#${rs.clipId})">
|
|
32408
33866
|
${buf.join("\n")}
|
|
32409
33867
|
</g></g>`
|
|
32410
33868
|
);
|
|
@@ -32412,11 +33870,315 @@ ${buf.join("\n")}
|
|
|
32412
33870
|
}
|
|
32413
33871
|
}
|
|
32414
33872
|
const totalH = Math.max(0, y - GAP);
|
|
32415
|
-
const svg = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${pt(maxPW)} ${pt(totalH)}" width="${pt(maxPW)}pt" height="${pt(totalH)}pt" font-family="
|
|
32416
|
-
<defs>${clipDefs.join("")}${
|
|
33873
|
+
const svg = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${pt(maxPW)} ${pt(totalH)}" width="${pt(maxPW)}pt" height="${pt(totalH)}pt" font-family="${SVG_FONT_FAMILY}" xml:space="preserve">
|
|
33874
|
+
<defs>${clipDefs.join("")}${r.defs.join("")}</defs>
|
|
32417
33875
|
${groups.join("\n")}
|
|
32418
33876
|
</svg>`;
|
|
32419
|
-
return { svg, width: Math.round(maxPW) / 100, height: Math.round(totalH) / 100, pageCount:
|
|
33877
|
+
return { svg, width: Math.round(maxPW) / 100, height: Math.round(totalH) / 100, pageCount: pageNo2, warnings: r.warnings, stats: r.stats, regions: r.regions };
|
|
33878
|
+
}
|
|
33879
|
+
async function renderHwpxPages(input, options, select) {
|
|
33880
|
+
const r = await renderHwpxInternal(input, options);
|
|
33881
|
+
const pages = [];
|
|
33882
|
+
const pageSvgs = /* @__PURE__ */ new Map();
|
|
33883
|
+
const pageCount = r.sections.reduce((n, rs) => n + rs.pages.length, 0);
|
|
33884
|
+
const chosen = typeof select === "function" ? select(pageCount) : select;
|
|
33885
|
+
let pageNo2 = 0;
|
|
33886
|
+
for (const rs of r.sections) {
|
|
33887
|
+
for (const buf of rs.pages) {
|
|
33888
|
+
pageNo2++;
|
|
33889
|
+
pages.push({ page: pageNo2, width: ptNum(rs.PW), height: ptNum(rs.pageH) });
|
|
33890
|
+
if (chosen && !chosen.has(pageNo2)) continue;
|
|
33891
|
+
const body = buf.join("\n");
|
|
33892
|
+
const used = /* @__PURE__ */ new Set();
|
|
33893
|
+
for (const m of body.matchAll(/href="#(bin\d+)"/g)) used.add(m[1]);
|
|
33894
|
+
const defs = r.defs.filter((d) => {
|
|
33895
|
+
const m = /<symbol id="(bin\d+)"/.exec(d);
|
|
33896
|
+
return m ? used.has(m[1]) : true;
|
|
33897
|
+
});
|
|
33898
|
+
const clipId = `pgclip${pageNo2}`;
|
|
33899
|
+
pageSvgs.set(
|
|
33900
|
+
pageNo2,
|
|
33901
|
+
`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${pt(rs.PW)} ${pt(rs.pageH)}" width="${pt(rs.PW)}pt" height="${pt(rs.pageH)}pt" font-family="${SVG_FONT_FAMILY}" xml:space="preserve" data-page="${pageNo2}">
|
|
33902
|
+
<defs><clipPath id="${clipId}"><rect x="0" y="0" width="${pt(rs.PW)}" height="${pt(rs.pageH)}"/></clipPath>${defs.join("")}</defs>
|
|
33903
|
+
<rect width="${pt(rs.PW)}" height="${pt(rs.pageH)}" fill="white"/>
|
|
33904
|
+
<g clip-path="url(#${clipId})">
|
|
33905
|
+
${body}
|
|
33906
|
+
</g>
|
|
33907
|
+
</svg>`
|
|
33908
|
+
);
|
|
33909
|
+
}
|
|
33910
|
+
}
|
|
33911
|
+
const scene = { format: "hwpx", pages, regions: r.regions, warnings: r.warnings, stats: { texts: r.stats.texts, tables: r.stats.tables, images: r.stats.images, shapes: r.stats.shapes } };
|
|
33912
|
+
return { scene, pageSvgs };
|
|
33913
|
+
}
|
|
33914
|
+
|
|
33915
|
+
// src/render/document.ts
|
|
33916
|
+
import { readFile } from "fs/promises";
|
|
33917
|
+
|
|
33918
|
+
// src/render/html.ts
|
|
33919
|
+
function renderSceneToHtml(scene, pageSvgs, options = {}) {
|
|
33920
|
+
const title = escapeXml4(options.title ?? "kordoc");
|
|
33921
|
+
const first = scene.pages.find((p) => pageSvgs.has(p.page)) ?? scene.pages[0];
|
|
33922
|
+
const pageSize = first ? `${first.width}pt ${first.height}pt` : "auto";
|
|
33923
|
+
const pages = [...pageSvgs.keys()].sort((a, b) => a - b).map((n) => {
|
|
33924
|
+
const meta = scene.pages.find((p) => p.page === n);
|
|
33925
|
+
const size = meta ? ` style="width:${meta.width}pt;height:${meta.height}pt"` : "";
|
|
33926
|
+
return `<div class="kordoc-page" data-page="${n}"${size}>${pageSvgs.get(n)}</div>`;
|
|
33927
|
+
});
|
|
33928
|
+
return `<!doctype html>
|
|
33929
|
+
<html lang="ko">
|
|
33930
|
+
<head>
|
|
33931
|
+
<meta charset="utf-8">
|
|
33932
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
33933
|
+
<title>${title}</title>
|
|
33934
|
+
<style>
|
|
33935
|
+
html, body { margin: 0; background: #e9e8e6; }
|
|
33936
|
+
.kordoc-document { display: flex; flex-direction: column; align-items: center; gap: 16px; padding: 16px 0; }
|
|
33937
|
+
.kordoc-page { position: relative; background: white; box-shadow: 0 1px 4px rgba(0,0,0,.25); }
|
|
33938
|
+
.kordoc-page > svg { display: block; width: 100%; height: 100%; }
|
|
33939
|
+
@page { size: ${pageSize}; margin: 0; }
|
|
33940
|
+
@media print {
|
|
33941
|
+
html, body { background: white; }
|
|
33942
|
+
.kordoc-document { gap: 0; padding: 0; }
|
|
33943
|
+
.kordoc-page { box-shadow: none; break-after: page; page-break-after: always; }
|
|
33944
|
+
.kordoc-page:last-child { break-after: auto; page-break-after: auto; }
|
|
33945
|
+
}
|
|
33946
|
+
</style>
|
|
33947
|
+
</head>
|
|
33948
|
+
<body>
|
|
33949
|
+
<div class="kordoc-document">
|
|
33950
|
+
${pages.join("\n")}
|
|
33951
|
+
</div>
|
|
33952
|
+
</body>
|
|
33953
|
+
</html>
|
|
33954
|
+
`;
|
|
33955
|
+
}
|
|
33956
|
+
|
|
33957
|
+
// src/render/rasterize.ts
|
|
33958
|
+
async function loadSharp() {
|
|
33959
|
+
try {
|
|
33960
|
+
const mod = await import("sharp");
|
|
33961
|
+
return mod.default ?? mod;
|
|
33962
|
+
} catch {
|
|
33963
|
+
throw new KordocError(
|
|
33964
|
+
'PNG \uB798\uC2A4\uD130\uC5D0\uB294 sharp\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4 (npm install sharp). sharp \uC5C6\uC774 \uC4F0\uB824\uBA74 format: "svg" + output_path\uB85C SVG \uD30C\uC77C \uC800\uC7A5\uC744 \uC0AC\uC6A9\uD558\uC138\uC694'
|
|
33965
|
+
);
|
|
33966
|
+
}
|
|
33967
|
+
}
|
|
33968
|
+
async function rasterizePageSvg(svg, widthPt, heightPt, options = {}) {
|
|
33969
|
+
const sharp = await loadSharp();
|
|
33970
|
+
if (!(widthPt > 0) || !(heightPt > 0)) throw new KordocError(`\uC798\uBABB\uB41C \uD398\uC774\uC9C0 \uD06C\uAE30: ${widthPt}x${heightPt}pt`);
|
|
33971
|
+
const maxW = options.maxWidthPx ?? 1400;
|
|
33972
|
+
const maxH = options.maxHeightPx ?? 4e3;
|
|
33973
|
+
const scale2 = Math.min(maxW / widthPt, maxH / heightPt, 2);
|
|
33974
|
+
const format = options.format ?? "png";
|
|
33975
|
+
const pipeline = sharp(Buffer.from(svg), { density: 72 * scale2, limitInputPixels: 268402689 });
|
|
33976
|
+
const data = format === "jpeg" ? await pipeline.jpeg({ quality: options.quality ?? 85 }).toBuffer() : await pipeline.png().toBuffer();
|
|
33977
|
+
return { data, mimeType: format === "jpeg" ? "image/jpeg" : "image/png", widthPx: Math.round(widthPt * scale2), heightPx: Math.round(heightPt * scale2), scale: scale2 };
|
|
33978
|
+
}
|
|
33979
|
+
|
|
33980
|
+
// src/render/pdf.ts
|
|
33981
|
+
async function renderHtmlToPdf(html, options = {}) {
|
|
33982
|
+
let puppeteer;
|
|
33983
|
+
try {
|
|
33984
|
+
puppeteer = await import("puppeteer-core");
|
|
33985
|
+
} catch {
|
|
33986
|
+
throw new KordocError("PDF \uB80C\uB354\uC5D0\uB294 puppeteer-core \uAC00 \uD544\uC694\uD569\uB2C8\uB2E4 (npm install puppeteer-core). SVG/HTML/PNG \uB294 Chromium \uC5C6\uC774 \uB429\uB2C8\uB2E4");
|
|
33987
|
+
}
|
|
33988
|
+
const executablePath = options.browserExecutablePath ?? process.env.PUPPETEER_EXECUTABLE_PATH ?? findChromiumPath();
|
|
33989
|
+
if (!executablePath) {
|
|
33990
|
+
throw new KordocError("Chromium \uC2E4\uD589 \uD30C\uC77C\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4 \u2014 browserExecutablePath \uC635\uC158 \uB610\uB294 PUPPETEER_EXECUTABLE_PATH \uD658\uACBD\uBCC0\uC218\uB97C \uC9C0\uC815\uD558\uC138\uC694");
|
|
33991
|
+
}
|
|
33992
|
+
const browser = await puppeteer.default.launch({ executablePath, headless: true, args: ["--no-sandbox", "--disable-setuid-sandbox"] });
|
|
33993
|
+
try {
|
|
33994
|
+
const page = await browser.newPage();
|
|
33995
|
+
await page.setContent(html, { waitUntil: "load" });
|
|
33996
|
+
const pdf = await page.pdf({ printBackground: true, preferCSSPageSize: true });
|
|
33997
|
+
return Buffer.from(pdf);
|
|
33998
|
+
} finally {
|
|
33999
|
+
await browser.close();
|
|
34000
|
+
}
|
|
34001
|
+
}
|
|
34002
|
+
|
|
34003
|
+
// src/render/document.ts
|
|
34004
|
+
async function loadBuffer(input) {
|
|
34005
|
+
if (typeof input === "string") {
|
|
34006
|
+
try {
|
|
34007
|
+
return toArrayBuffer(await readFile(input));
|
|
34008
|
+
} catch (err) {
|
|
34009
|
+
const code = err instanceof Error && "code" in err ? err.code : void 0;
|
|
34010
|
+
throw new KordocError(code === "ENOENT" ? `\uD30C\uC77C\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${input}` : `\uD30C\uC77C \uC77D\uAE30 \uC2E4\uD328: ${input}`);
|
|
34011
|
+
}
|
|
34012
|
+
}
|
|
34013
|
+
if (Buffer.isBuffer(input)) return toArrayBuffer(input);
|
|
34014
|
+
if (input instanceof Uint8Array) return input.buffer.slice(input.byteOffset, input.byteOffset + input.byteLength);
|
|
34015
|
+
return input;
|
|
34016
|
+
}
|
|
34017
|
+
async function sourceFormatOf(buffer) {
|
|
34018
|
+
const format = detectFormat(buffer);
|
|
34019
|
+
if (format === "hwpx") {
|
|
34020
|
+
const zip = await detectZipFormat(buffer);
|
|
34021
|
+
if (zip === "hwpx" || zip === "unknown") return "hwpx";
|
|
34022
|
+
throw new KordocError(`\uB80C\uB354 \uBBF8\uC9C0\uC6D0 \uD615\uC2DD(${zip}) \u2014 \uB808\uC774\uC544\uC6C3 \uB80C\uB354\uB294 HWPX \uB9CC \uC9C0\uC6D0\uD569\uB2C8\uB2E4`);
|
|
34023
|
+
}
|
|
34024
|
+
if (format === "hwp") throw new KordocError("HWP5 \uB808\uC774\uC544\uC6C3 \uB80C\uB354\uB294 \uC544\uC9C1 \uC9C0\uC6D0\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4 \u2014 \uD55C\uCEF4\uC5D0\uC11C HWPX \uB85C \uC800\uC7A5\uD55C \uB4A4 \uB80C\uB354\uD558\uC138\uC694 (#75 \uD6C4\uC18D)");
|
|
34025
|
+
throw new KordocError(`\uB80C\uB354 \uBBF8\uC9C0\uC6D0 \uD615\uC2DD(${format}) \u2014 \uB808\uC774\uC544\uC6C3 \uB80C\uB354\uB294 HWPX \uB9CC \uC9C0\uC6D0\uD569\uB2C8\uB2E4`);
|
|
34026
|
+
}
|
|
34027
|
+
async function renderDocumentToScene(input, options = {}) {
|
|
34028
|
+
const buffer = await loadBuffer(input);
|
|
34029
|
+
const source = await sourceFormatOf(buffer);
|
|
34030
|
+
if (source !== "hwpx") throw new KordocError("HWP5 \uB808\uC774\uC544\uC6C3 \uB80C\uB354\uB294 \uC544\uC9C1 \uC9C0\uC6D0\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4");
|
|
34031
|
+
const select = options.pages !== void 0 ? (pageCount) => {
|
|
34032
|
+
const set = parsePageRange(options.pages, pageCount);
|
|
34033
|
+
if (set.size === 0) throw new KordocError(`\uC120\uD0DD\uD55C \uD398\uC774\uC9C0\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4 \u2014 \uBB38\uC11C\uB294 ${pageCount}\uCABD (pages: ${JSON.stringify(options.pages)})`);
|
|
34034
|
+
return set;
|
|
34035
|
+
} : void 0;
|
|
34036
|
+
const { scene, pageSvgs } = await renderHwpxPages(buffer, {
|
|
34037
|
+
reflow: options.reflow ?? true,
|
|
34038
|
+
reflowMode: options.reflowMode,
|
|
34039
|
+
highlights: options.highlights,
|
|
34040
|
+
maxImageBytes: options.maxImageBytes
|
|
34041
|
+
}, select);
|
|
34042
|
+
return { scene, pageSvgs };
|
|
34043
|
+
}
|
|
34044
|
+
async function renderDocument(input, options) {
|
|
34045
|
+
const { scene, pageSvgs } = await renderDocumentToScene(input, options);
|
|
34046
|
+
const pageNos = [...pageSvgs.keys()].sort((a, b) => a - b);
|
|
34047
|
+
const sizeOf = (n) => scene.pages.find((p) => p.page === n);
|
|
34048
|
+
const assets = [];
|
|
34049
|
+
switch (options.format) {
|
|
34050
|
+
case "svg":
|
|
34051
|
+
for (const n of pageNos) {
|
|
34052
|
+
const p = sizeOf(n);
|
|
34053
|
+
assets.push({ format: "svg", page: n, data: pageSvgs.get(n), width: p.width, height: p.height, mimeType: "image/svg+xml" });
|
|
34054
|
+
}
|
|
34055
|
+
break;
|
|
34056
|
+
case "html":
|
|
34057
|
+
assets.push({ format: "html", data: renderSceneToHtml(scene, pageSvgs, { title: options.title }), mimeType: "text/html" });
|
|
34058
|
+
break;
|
|
34059
|
+
case "png":
|
|
34060
|
+
case "jpeg":
|
|
34061
|
+
for (const n of pageNos) {
|
|
34062
|
+
const p = sizeOf(n);
|
|
34063
|
+
const r = await rasterizePageSvg(pageSvgs.get(n), p.width, p.height, { format: options.format, maxWidthPx: options.maxWidthPx, quality: options.quality });
|
|
34064
|
+
assets.push({ format: options.format, page: n, data: r.data, width: r.widthPx, height: r.heightPx, scale: r.scale, mimeType: r.mimeType });
|
|
34065
|
+
}
|
|
34066
|
+
break;
|
|
34067
|
+
case "pdf": {
|
|
34068
|
+
const html = renderSceneToHtml(scene, pageSvgs, { title: options.title });
|
|
34069
|
+
assets.push({ format: "pdf", data: await renderHtmlToPdf(html, { browserExecutablePath: options.browserExecutablePath }), mimeType: "application/pdf" });
|
|
34070
|
+
break;
|
|
34071
|
+
}
|
|
34072
|
+
default:
|
|
34073
|
+
throw new KordocError(`\uC54C \uC218 \uC5C6\uB294 \uB80C\uB354 \uD3EC\uB9F7: ${String(options.format)} (svg|html|png|jpeg|pdf)`);
|
|
34074
|
+
}
|
|
34075
|
+
return { scene, assets };
|
|
34076
|
+
}
|
|
34077
|
+
|
|
34078
|
+
// src/render/regions.ts
|
|
34079
|
+
function cropRect(bbox, scale2, pageWidthPx, pageHeightPx, paddingPt = 0) {
|
|
34080
|
+
const W = Math.max(1, Math.floor(pageWidthPx)), H = Math.max(1, Math.floor(pageHeightPx));
|
|
34081
|
+
const clamp = (v, hi) => Math.min(Math.max(0, v), hi);
|
|
34082
|
+
let left = clamp(Math.floor((bbox.x - paddingPt) * scale2), W - 1);
|
|
34083
|
+
let top = clamp(Math.floor((bbox.y - paddingPt) * scale2), H - 1);
|
|
34084
|
+
const right = clamp(Math.ceil((bbox.x + bbox.width + paddingPt) * scale2), W);
|
|
34085
|
+
const bottom = clamp(Math.ceil((bbox.y + bbox.height + paddingPt) * scale2), H);
|
|
34086
|
+
const width = Math.max(1, right - left), height = Math.max(1, bottom - top);
|
|
34087
|
+
if (left + width > W) left = W - width;
|
|
34088
|
+
if (top + height > H) top = H - height;
|
|
34089
|
+
return { left, top, width, height };
|
|
34090
|
+
}
|
|
34091
|
+
async function extractRenderedRegions(input, options = {}) {
|
|
34092
|
+
const { scene, pageSvgs } = await renderDocumentToScene(input, { pages: options.pages, reflow: options.reflow, reflowMode: options.reflowMode });
|
|
34093
|
+
const types = options.types ? new Set(options.types) : null;
|
|
34094
|
+
const format = options.format ?? "png";
|
|
34095
|
+
const wanted = scene.regions.filter((r) => (!types || types.has(r.type)) && (!options.filter || options.filter(r)));
|
|
34096
|
+
const byPage = /* @__PURE__ */ new Map();
|
|
34097
|
+
for (const r of wanted) for (const b of r.regions) if (pageSvgs.has(b.page)) (byPage.get(b.page) ?? byPage.set(b.page, []).get(b.page)).push({ region: r, bbox: b });
|
|
34098
|
+
if (byPage.size === 0) return [];
|
|
34099
|
+
const sharp = await loadSharp();
|
|
34100
|
+
const out = [];
|
|
34101
|
+
for (const [page, items] of [...byPage.entries()].sort((a, b) => a[0] - b[0])) {
|
|
34102
|
+
const meta = scene.pages.find((p) => p.page === page);
|
|
34103
|
+
if (!meta) throw new KordocError(`\uD398\uC774\uC9C0 ${page} \uBA54\uD0C0 \uC5C6\uC74C`);
|
|
34104
|
+
const raster = await rasterizePageSvg(pageSvgs.get(page), meta.width, meta.height, { format, maxWidthPx: options.maxWidthPx, quality: options.quality });
|
|
34105
|
+
for (const { region, bbox } of items) {
|
|
34106
|
+
const rect = cropRect(bbox, raster.scale, raster.widthPx, raster.heightPx, options.paddingPt ?? 0);
|
|
34107
|
+
const pipeline = sharp(raster.data).extract(rect);
|
|
34108
|
+
const data = format === "jpeg" ? await pipeline.jpeg({ quality: options.quality ?? 85 }).toBuffer() : await pipeline.png().toBuffer();
|
|
34109
|
+
out.push({ region, page, bbox, mimeType: raster.mimeType, data, widthPx: rect.width, heightPx: rect.height });
|
|
34110
|
+
}
|
|
34111
|
+
}
|
|
34112
|
+
const order = new Map(scene.regions.map((r, i) => [r.id, i]));
|
|
34113
|
+
out.sort((a, b) => order.get(a.region.id) - order.get(b.region.id) || a.page - b.page);
|
|
34114
|
+
return out;
|
|
34115
|
+
}
|
|
34116
|
+
|
|
34117
|
+
// src/table/visual.ts
|
|
34118
|
+
import { readFile as readFile2 } from "fs/promises";
|
|
34119
|
+
function wantsCrop(kind, policy) {
|
|
34120
|
+
if (policy === "none") return false;
|
|
34121
|
+
if (policy === "all") return true;
|
|
34122
|
+
if (kind === "non-tabular-layout") return true;
|
|
34123
|
+
return policy === "non-tabular-and-uncertain" && kind === "uncertain";
|
|
34124
|
+
}
|
|
34125
|
+
async function extractTables(input, options = {}) {
|
|
34126
|
+
const policy = options.policy ?? "non-tabular-and-uncertain";
|
|
34127
|
+
const buffer = typeof input === "string" ? toArrayBuffer(await readFile2(input)) : Buffer.isBuffer(input) ? toArrayBuffer(input) : input;
|
|
34128
|
+
const parsed = await parse(buffer, { classifyTables: true });
|
|
34129
|
+
if (!parsed.success) throw new KordocError(`\uD30C\uC2F1 \uC2E4\uD328: ${parsed.error}`);
|
|
34130
|
+
const blocks = collectTableBlocks(parsed.blocks);
|
|
34131
|
+
const out = blocks.map((b, i) => ({
|
|
34132
|
+
id: b.table.sourceId ?? `tbl-${i + 1}`,
|
|
34133
|
+
page: b.pageNumber ?? 1,
|
|
34134
|
+
table: b.table,
|
|
34135
|
+
classification: b.table.classification,
|
|
34136
|
+
sourceId: b.table.sourceId,
|
|
34137
|
+
regions: [],
|
|
34138
|
+
crops: [],
|
|
34139
|
+
warnings: []
|
|
34140
|
+
}));
|
|
34141
|
+
if (out.length === 0) return out;
|
|
34142
|
+
const format = detectFormat(buffer);
|
|
34143
|
+
if (format !== "hwpx") {
|
|
34144
|
+
for (const t of out) t.warnings.push(format === "hwp" ? "HWP5 \uB294 \uB808\uC774\uC544\uC6C3 \uB80C\uB354 \uBBF8\uC9C0\uC6D0 \u2014 region\xB7crop \uC5C6\uC74C(\uBD84\uB958\uB9CC)" : `${format} \uC740 \uB80C\uB354 \uBBF8\uC9C0\uC6D0 \u2014 region\xB7crop \uC5C6\uC74C`);
|
|
34145
|
+
return out;
|
|
34146
|
+
}
|
|
34147
|
+
let scene;
|
|
34148
|
+
try {
|
|
34149
|
+
scene = (await renderDocumentToScene(buffer, { reflow: true })).scene;
|
|
34150
|
+
} catch (e) {
|
|
34151
|
+
for (const t of out) t.warnings.push(`\uB80C\uB354 \uC2E4\uD328 \u2014 region \uC5C6\uC74C: ${e instanceof Error ? e.message : String(e)}`);
|
|
34152
|
+
return out;
|
|
34153
|
+
}
|
|
34154
|
+
const bySource = /* @__PURE__ */ new Map();
|
|
34155
|
+
for (const r of scene.regions) if (r.type === "table" && r.sourceId) (bySource.get(r.sourceId) ?? bySource.set(r.sourceId, []).get(r.sourceId)).push(r);
|
|
34156
|
+
const cropIds = /* @__PURE__ */ new Set();
|
|
34157
|
+
for (const t of out) {
|
|
34158
|
+
if (!t.sourceId) {
|
|
34159
|
+
t.warnings.push("sourceId \uC5C6\uC74C \u2014 region \uC870\uC778 \uBD88\uAC00");
|
|
34160
|
+
continue;
|
|
34161
|
+
}
|
|
34162
|
+
const hits = bySource.get(t.sourceId) ?? [];
|
|
34163
|
+
if (hits.length === 0) {
|
|
34164
|
+
t.warnings.push(`\uB80C\uB354 region \uBBF8\uB9E4\uCE6D(sourceId ${t.sourceId})`);
|
|
34165
|
+
continue;
|
|
34166
|
+
}
|
|
34167
|
+
if (hits.length > 1) {
|
|
34168
|
+
t.warnings.push(`\uB80C\uB354 region \uC911\uBCF5 \uB9E4\uCE6D(sourceId ${t.sourceId}, ${hits.length}\uAC1C) \u2014 \uCCAB \uAC1C\uCCB4\uB9CC`);
|
|
34169
|
+
}
|
|
34170
|
+
t.regions = hits[0].regions.map((b) => ({ ...b }));
|
|
34171
|
+
t.page = hits[0].page;
|
|
34172
|
+
t.table.regions = t.regions.map((b) => ({ page: b.page, x: b.x, y: b.y, width: b.width, height: b.height }));
|
|
34173
|
+
if (wantsCrop(t.classification.kind, policy)) cropIds.add(hits[0].id);
|
|
34174
|
+
}
|
|
34175
|
+
if (cropIds.size === 0) return out;
|
|
34176
|
+
const assets = await extractRenderedRegions(buffer, { types: ["table"], format: options.format, paddingPt: options.paddingPt, maxWidthPx: options.maxWidthPx, reflow: true, filter: (r) => cropIds.has(r.id) });
|
|
34177
|
+
for (const t of out) {
|
|
34178
|
+
if (!t.sourceId) continue;
|
|
34179
|
+
for (const a of assets) if (a.region.sourceId === t.sourceId) t.crops.push({ page: a.page, bbox: a.bbox, mimeType: a.mimeType, data: a.data });
|
|
34180
|
+
}
|
|
34181
|
+
return out;
|
|
32420
34182
|
}
|
|
32421
34183
|
|
|
32422
34184
|
// src/index.ts
|
|
@@ -32425,7 +34187,7 @@ async function parse(input, options) {
|
|
|
32425
34187
|
const opts = typeof input === "string" && !options?.filePath ? { ...options, filePath: input } : options;
|
|
32426
34188
|
if (typeof input === "string") {
|
|
32427
34189
|
try {
|
|
32428
|
-
const buf = await
|
|
34190
|
+
const buf = await readFile3(input);
|
|
32429
34191
|
buffer = toArrayBuffer(buf);
|
|
32430
34192
|
} catch (err) {
|
|
32431
34193
|
const msg2 = err instanceof Error && "code" in err && err.code === "ENOENT" ? `\uD30C\uC77C\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${input}` : `\uD30C\uC77C \uC77D\uAE30 \uC2E4\uD328: ${input}`;
|
|
@@ -32441,6 +34203,7 @@ async function parse(input, options) {
|
|
|
32441
34203
|
}
|
|
32442
34204
|
const format = detectFormat(buffer);
|
|
32443
34205
|
const result = await dispatch(format, buffer, opts);
|
|
34206
|
+
if (result.success && opts?.classifyTables) classifyTableTree(result.blocks);
|
|
32444
34207
|
if (result.success && !result.pages) {
|
|
32445
34208
|
const pages = blocksToPages(result.blocks);
|
|
32446
34209
|
if (pages) return { ...result, pages };
|
|
@@ -32474,7 +34237,7 @@ async function dispatch(format, buffer, opts) {
|
|
|
32474
34237
|
}
|
|
32475
34238
|
async function parseImage(buffer, options) {
|
|
32476
34239
|
try {
|
|
32477
|
-
const { parseImageDocument } = await import("./image-ocr-
|
|
34240
|
+
const { parseImageDocument } = await import("./image-ocr-X3I2HUK2.js");
|
|
32478
34241
|
const { blocks, warnings } = await parseImageDocument(buffer, options);
|
|
32479
34242
|
return {
|
|
32480
34243
|
success: true,
|
|
@@ -32533,7 +34296,7 @@ async function parseHwp(buffer, options) {
|
|
|
32533
34296
|
async function parsePdf(buffer, options) {
|
|
32534
34297
|
let parsePdfDocument;
|
|
32535
34298
|
try {
|
|
32536
|
-
const mod = await import("./parser-
|
|
34299
|
+
const mod = await import("./parser-XKSOS6GY.js");
|
|
32537
34300
|
parsePdfDocument = mod.parsePdfDocument;
|
|
32538
34301
|
} catch {
|
|
32539
34302
|
return {
|
|
@@ -32586,7 +34349,7 @@ async function parseHwpml(buffer, options) {
|
|
|
32586
34349
|
async function fillForm(input, values, outputFormat = "markdown") {
|
|
32587
34350
|
let buffer;
|
|
32588
34351
|
if (typeof input === "string") {
|
|
32589
|
-
const buf = await
|
|
34352
|
+
const buf = await readFile3(input);
|
|
32590
34353
|
buffer = toArrayBuffer(buf);
|
|
32591
34354
|
} else if (Buffer.isBuffer(input)) {
|
|
32592
34355
|
buffer = toArrayBuffer(input);
|
|
@@ -32639,6 +34402,10 @@ export {
|
|
|
32639
34402
|
buildParagraphSplices,
|
|
32640
34403
|
buildRangeSplices,
|
|
32641
34404
|
charWidthEm1000,
|
|
34405
|
+
chooseTableRepresentation,
|
|
34406
|
+
classifyTable,
|
|
34407
|
+
classifyTableTree,
|
|
34408
|
+
collectTableBlocks,
|
|
32642
34409
|
compare,
|
|
32643
34410
|
detectFormat,
|
|
32644
34411
|
detectOle2Format,
|
|
@@ -32647,6 +34414,8 @@ export {
|
|
|
32647
34414
|
extractClickHereFields,
|
|
32648
34415
|
extractFormFields,
|
|
32649
34416
|
extractFormSchema,
|
|
34417
|
+
extractRenderedRegions,
|
|
34418
|
+
extractTables,
|
|
32650
34419
|
fillForm,
|
|
32651
34420
|
fillFormFields,
|
|
32652
34421
|
fillHwpx,
|
|
@@ -32654,6 +34423,7 @@ export {
|
|
|
32654
34423
|
fitRatioForFewerLines,
|
|
32655
34424
|
formatFillValue,
|
|
32656
34425
|
gongmunLintWarnings,
|
|
34426
|
+
hasStructuredCellContent,
|
|
32657
34427
|
hwpxToProfile,
|
|
32658
34428
|
incompatibleGongmunWarnings,
|
|
32659
34429
|
inferFieldType,
|
|
@@ -32689,8 +34459,11 @@ export {
|
|
|
32689
34459
|
readBuiltinTemplateSample,
|
|
32690
34460
|
redactMarkdown,
|
|
32691
34461
|
redactText,
|
|
34462
|
+
renderDocument,
|
|
34463
|
+
renderDocumentToScene,
|
|
32692
34464
|
renderHtml,
|
|
32693
34465
|
renderHwpxToSvg,
|
|
34466
|
+
renderSceneToHtml,
|
|
32694
34467
|
resolveBuiltinTemplate,
|
|
32695
34468
|
scanSectionXml,
|
|
32696
34469
|
simulateWrap,
|