kordoc 4.16.0 → 4.16.2
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 +344 -1135
- package/dist/{-VQNLLLPE.js → -4G6MA4X6.js} +11 -11
- package/dist/chunk-3IB7LBHC.js +57 -0
- package/dist/chunk-3IB7LBHC.js.map +1 -0
- package/dist/{chunk-6LMKQM7I.js → chunk-4VODJKFL.js} +3 -3
- package/dist/{chunk-UDX7TSOC.js → chunk-5KS5PWJR.js} +2 -2
- package/dist/chunk-62W7QCEX.cjs +57 -0
- package/dist/chunk-62W7QCEX.cjs.map +1 -0
- package/dist/{chunk-NNYF2AGC.js → chunk-7KNGSQU7.js} +4 -4
- package/dist/chunk-7KNGSQU7.js.map +1 -0
- package/dist/{chunk-DYENR3PX.js → chunk-AUWGDP4G.js} +2 -2
- package/dist/{chunk-H6KMTYYZ.cjs → chunk-CEIATRDW.cjs} +3 -3
- package/dist/{chunk-H6KMTYYZ.cjs.map → chunk-CEIATRDW.cjs.map} +1 -1
- package/dist/{chunk-N6TPODYL.js → chunk-DES2LGFZ.js} +2 -2
- package/dist/{chunk-PQRP4NSF.js → chunk-F4NRLLY4.js} +2 -2
- package/dist/{chunk-B5NEGP65.cjs → chunk-FKUDAIJI.cjs} +692 -33
- package/dist/chunk-FKUDAIJI.cjs.map +1 -0
- package/dist/{chunk-XWA625AN.js → chunk-FSDJLAOW.js} +3 -1
- package/dist/chunk-FSDJLAOW.js.map +1 -0
- package/dist/{chunk-PIMB2YNO.js → chunk-GO5IFOUF.js} +2 -2
- package/dist/{chunk-476OYHGZ.js → chunk-IQGIT2VX.js} +37 -7
- package/dist/chunk-IQGIT2VX.js.map +1 -0
- package/dist/chunk-J3IF6KGW.js +59 -0
- package/dist/chunk-J3IF6KGW.js.map +1 -0
- package/dist/{chunk-C75WE52C.js → chunk-JELRWAZC.js} +2 -2
- package/dist/{chunk-46DZF3RK.js → chunk-JFLAM3MM.js} +2 -2
- package/dist/{chunk-A3QDRMVD.js → chunk-KIOQ4N37.js} +2 -2
- package/dist/{chunk-TGD7UFHD.js → chunk-LEIAEWZ7.js} +4 -4
- package/dist/chunk-LEIAEWZ7.js.map +1 -0
- package/dist/{chunk-GOQBON77.js → chunk-MLIS253L.js} +2 -2
- package/dist/{chunk-JB5X5NNL.js → chunk-OXLPVQYK.js} +680 -21
- package/dist/chunk-OXLPVQYK.js.map +1 -0
- package/dist/{chunk-RC4C7Q33.js → chunk-P6ZDXTV6.js} +2 -2
- package/dist/{chunk-CB5XDCSL.js → chunk-QOCS7W7R.js} +5034 -4569
- package/dist/chunk-QOCS7W7R.js.map +1 -0
- package/dist/{chunk-3JWMDL3S.js → chunk-S2PL227Q.js} +2 -2
- package/dist/{chunk-HIVK5V27.js → chunk-SFEQYKYJ.js} +133 -110
- package/dist/chunk-SFEQYKYJ.js.map +1 -0
- package/dist/{chunk-NWODPYBD.cjs → chunk-STM3ORY7.cjs} +2 -2
- package/dist/{chunk-NWODPYBD.cjs.map → chunk-STM3ORY7.cjs.map} +1 -1
- package/dist/{chunk-43FKGDDO.js → chunk-TXUKTOG2.js} +213 -30
- package/dist/chunk-TXUKTOG2.js.map +1 -0
- package/dist/{chunk-XHOZB2WP.js → chunk-UTP2WCHS.js} +681 -22
- package/dist/chunk-UTP2WCHS.js.map +1 -0
- package/dist/{chunk-MH6MAL64.cjs → chunk-WDQTFAXY.cjs} +217 -114
- package/dist/chunk-WDQTFAXY.cjs.map +1 -0
- package/dist/{chunk-DRY3L5KQ.cjs → chunk-XTFN5WKS.cjs} +7 -7
- package/dist/chunk-XTFN5WKS.cjs.map +1 -0
- package/dist/chunks-NUNIKS2J.js +10 -0
- package/dist/cli.js +33 -30
- package/dist/cli.js.map +1 -1
- package/dist/{formula-FNQYHMIY.js → formula-EPKLPDU7.js} +3 -3
- package/dist/{formula-5775PTP2.js → formula-NARUOXRX.js} +4 -4
- package/dist/{formula-VEFKYXPB.cjs → formula-WIMV5TUX.cjs} +9 -9
- package/dist/{formula-VEFKYXPB.cjs.map → formula-WIMV5TUX.cjs.map} +1 -1
- package/dist/{image-ocr-PYZU4TO7.js → image-ocr-3WJ4QH3T.js} +8 -8
- package/dist/{image-ocr-YA2MQBRP.js → image-ocr-5BUJQ4TK.js} +6 -6
- package/dist/{image-ocr-6JZR734G.cjs → image-ocr-AXWXR6JV.cjs} +15 -15
- package/dist/{image-ocr-6JZR734G.cjs.map → image-ocr-AXWXR6JV.cjs.map} +1 -1
- package/dist/index.cjs +1900 -1406
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +24 -6
- package/dist/index.d.ts +24 -6
- package/dist/index.js +1282 -788
- package/dist/index.js.map +1 -1
- package/dist/mcp.js +34 -28
- package/dist/mcp.js.map +1 -1
- package/dist/{model-bundle-O6J7ORVC.js → model-bundle-IMBW7D2Z.js} +5 -5
- package/dist/{models-YMOKZGCI.js → models-SY75IBOE.js} +5 -5
- package/dist/{parser-YAQV7FBO.cjs → parser-2LU4EVU3.cjs} +360 -219
- package/dist/parser-2LU4EVU3.cjs.map +1 -0
- package/dist/{parser-3B6I2XL7.js → parser-N6476XAC.js} +225 -84
- package/dist/parser-N6476XAC.js.map +1 -0
- package/dist/{parser-PXATL5DM.js → parser-ZQM3664Z.js} +222 -81
- package/dist/parser-ZQM3664Z.js.map +1 -0
- package/dist/pdf-ocr-6DEE23IP.js +15 -0
- package/dist/pdf-ocr-GHEJVPX2.cjs +16 -0
- package/dist/{pdf-ocr-T2JXQATA.cjs.map → pdf-ocr-GHEJVPX2.cjs.map} +1 -1
- package/dist/pdf-ocr-MQTWODVU.js +18 -0
- package/dist/{profile-io-ZTV2MKRT.js → profile-io-GJPKCYPT.js} +3 -3
- package/dist/{rasterize-MMUZ537K.js → rasterize-UOALJQDS.js} +3 -3
- package/dist/{redact-doc-S42XGV5D.js → redact-doc-22SYW3KH.js} +11 -11
- package/dist/{render-WCWPZKJ7.js → render-MJ4H3R23.js} +7 -7
- package/dist/seal-XWMRUTI4.js +10 -0
- package/dist/{setup-D35LKZCH.js → setup-2URYVUEO.js} +3 -3
- package/dist/visual-USPS4QRC.js +20 -0
- package/dist/{watch-AX64CIMN.js → watch-ALXZGTOB.js} +12 -12
- package/package.json +1 -1
- package/dist/chunk-43FKGDDO.js.map +0 -1
- package/dist/chunk-476OYHGZ.js.map +0 -1
- package/dist/chunk-B5NEGP65.cjs.map +0 -1
- package/dist/chunk-CB5XDCSL.js.map +0 -1
- package/dist/chunk-DRY3L5KQ.cjs.map +0 -1
- package/dist/chunk-HIVK5V27.js.map +0 -1
- package/dist/chunk-JB5X5NNL.js.map +0 -1
- package/dist/chunk-MH6MAL64.cjs.map +0 -1
- package/dist/chunk-NNYF2AGC.js.map +0 -1
- package/dist/chunk-QJERJZJ6.js +0 -11
- package/dist/chunk-QJERJZJ6.js.map +0 -1
- package/dist/chunk-TGD7UFHD.js.map +0 -1
- package/dist/chunk-UQ6TZDW6.js +0 -13
- package/dist/chunk-UQ6TZDW6.js.map +0 -1
- package/dist/chunk-XHOZB2WP.js.map +0 -1
- package/dist/chunk-XWA625AN.js.map +0 -1
- package/dist/chunk-ZY5K3LPM.cjs +0 -11
- package/dist/chunk-ZY5K3LPM.cjs.map +0 -1
- package/dist/chunks-JD25E5PM.js +0 -10
- package/dist/parser-3B6I2XL7.js.map +0 -1
- package/dist/parser-PXATL5DM.js.map +0 -1
- package/dist/parser-YAQV7FBO.cjs.map +0 -1
- package/dist/pdf-ocr-2TTO5PX2.js +0 -15
- package/dist/pdf-ocr-T2JXQATA.cjs +0 -16
- package/dist/pdf-ocr-WF2NQA6X.js +0 -18
- package/dist/seal-WHK4GOHR.js +0 -10
- package/dist/visual-WL4TDE5B.js +0 -20
- /package/dist/{-VQNLLLPE.js.map → -4G6MA4X6.js.map} +0 -0
- /package/dist/{chunk-6LMKQM7I.js.map → chunk-4VODJKFL.js.map} +0 -0
- /package/dist/{chunk-UDX7TSOC.js.map → chunk-5KS5PWJR.js.map} +0 -0
- /package/dist/{chunk-DYENR3PX.js.map → chunk-AUWGDP4G.js.map} +0 -0
- /package/dist/{chunk-N6TPODYL.js.map → chunk-DES2LGFZ.js.map} +0 -0
- /package/dist/{chunk-PQRP4NSF.js.map → chunk-F4NRLLY4.js.map} +0 -0
- /package/dist/{chunk-PIMB2YNO.js.map → chunk-GO5IFOUF.js.map} +0 -0
- /package/dist/{chunk-C75WE52C.js.map → chunk-JELRWAZC.js.map} +0 -0
- /package/dist/{chunk-46DZF3RK.js.map → chunk-JFLAM3MM.js.map} +0 -0
- /package/dist/{chunk-A3QDRMVD.js.map → chunk-KIOQ4N37.js.map} +0 -0
- /package/dist/{chunk-GOQBON77.js.map → chunk-MLIS253L.js.map} +0 -0
- /package/dist/{chunk-RC4C7Q33.js.map → chunk-P6ZDXTV6.js.map} +0 -0
- /package/dist/{chunk-3JWMDL3S.js.map → chunk-S2PL227Q.js.map} +0 -0
- /package/dist/{chunks-JD25E5PM.js.map → chunks-NUNIKS2J.js.map} +0 -0
- /package/dist/{formula-FNQYHMIY.js.map → formula-EPKLPDU7.js.map} +0 -0
- /package/dist/{formula-5775PTP2.js.map → formula-NARUOXRX.js.map} +0 -0
- /package/dist/{image-ocr-PYZU4TO7.js.map → image-ocr-3WJ4QH3T.js.map} +0 -0
- /package/dist/{image-ocr-YA2MQBRP.js.map → image-ocr-5BUJQ4TK.js.map} +0 -0
- /package/dist/{model-bundle-O6J7ORVC.js.map → model-bundle-IMBW7D2Z.js.map} +0 -0
- /package/dist/{models-YMOKZGCI.js.map → models-SY75IBOE.js.map} +0 -0
- /package/dist/{pdf-ocr-2TTO5PX2.js.map → pdf-ocr-6DEE23IP.js.map} +0 -0
- /package/dist/{pdf-ocr-WF2NQA6X.js.map → pdf-ocr-MQTWODVU.js.map} +0 -0
- /package/dist/{profile-io-ZTV2MKRT.js.map → profile-io-GJPKCYPT.js.map} +0 -0
- /package/dist/{rasterize-MMUZ537K.js.map → rasterize-UOALJQDS.js.map} +0 -0
- /package/dist/{redact-doc-S42XGV5D.js.map → redact-doc-22SYW3KH.js.map} +0 -0
- /package/dist/{render-WCWPZKJ7.js.map → render-MJ4H3R23.js.map} +0 -0
- /package/dist/{seal-WHK4GOHR.js.map → seal-XWMRUTI4.js.map} +0 -0
- /package/dist/{setup-D35LKZCH.js.map → setup-2URYVUEO.js.map} +0 -0
- /package/dist/{visual-WL4TDE5B.js.map → visual-USPS4QRC.js.map} +0 -0
- /package/dist/{watch-AX64CIMN.js.map → watch-ALXZGTOB.js.map} +0 -0
package/dist/index.js
CHANGED
|
@@ -16,15 +16,19 @@ import {
|
|
|
16
16
|
mapPuaText,
|
|
17
17
|
markNonLayoutTable,
|
|
18
18
|
noteSuffix
|
|
19
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-7KNGSQU7.js";
|
|
20
20
|
import {
|
|
21
21
|
parsePageRange
|
|
22
22
|
} from "./chunk-V4BH5SRQ.js";
|
|
23
23
|
import {
|
|
24
24
|
HEADING_RATIO_H1,
|
|
25
25
|
HEADING_RATIO_H2,
|
|
26
|
-
HEADING_RATIO_H3
|
|
27
|
-
|
|
26
|
+
HEADING_RATIO_H3,
|
|
27
|
+
plainScripts,
|
|
28
|
+
stripScriptTags,
|
|
29
|
+
tidyScriptTags,
|
|
30
|
+
wrapScript
|
|
31
|
+
} from "./chunk-3IB7LBHC.js";
|
|
28
32
|
import {
|
|
29
33
|
KordocError,
|
|
30
34
|
OPTIONAL_DEP_INSTALL_HINT,
|
|
@@ -41,17 +45,17 @@ import {
|
|
|
41
45
|
toArrayBuffer,
|
|
42
46
|
unescapeHtml,
|
|
43
47
|
unzipLimitBytes
|
|
44
|
-
} from "./chunk-
|
|
48
|
+
} from "./chunk-MLIS253L.js";
|
|
45
49
|
|
|
46
50
|
// src/plain-markdown.ts
|
|
47
51
|
function toPlainMarkdown(md2) {
|
|
48
|
-
return md2.replace(/!\[[^\]\n]*\]\([^)\n]*\)/g, "").replace(/<img\b[^>]*>/gi, "").replace(/(?<!!)\[([^\]\n]*)\]\((?:https?|mailto|ftp):[^)\s]*\)/g, "$1").replace(/<\/?u>/g, "").replace(/(?<!\\)\*\*(?=\S)([^\n]*?\S)(?<!\\)\*\*/g, "$1").replace(/(<br>)+(?=<\/t[dh]>)/g, "").replace(/[ \t]+$/gm, "").replace(/\n{3,}/g, "\n\n").trim();
|
|
52
|
+
return plainScripts(md2).replace(/!\[[^\]\n]*\]\([^)\n]*\)/g, "").replace(/<img\b[^>]*>/gi, "").replace(/(?<!!)\[([^\]\n]*)\]\((?:https?|mailto|ftp):[^)\s]*\)/g, "$1").replace(/<\/?u>/g, "").replace(/(?<!\\)\*\*(?=\S)([^\n]*?\S)(?<!\\)\*\*/g, "$1").replace(/(<br>)+(?=<\/t[dh]>)/g, "").replace(/[ \t]+$/gm, "").replace(/\n{3,}/g, "\n\n").trim();
|
|
49
53
|
}
|
|
50
54
|
|
|
51
55
|
// src/html-tables.ts
|
|
52
56
|
var TABLE_TAG = /(<\/?(?:table|tr|th|td)\b[^>]*>)/i;
|
|
53
57
|
function pipeCellToHtml(cell2) {
|
|
54
|
-
return cell2.split(/(<br>|<\/?u>|<img\b[^>]*>)/).map((part, i) => i % 2 ? part : part.replace(/\\([\\`*_{}[\]()#+\-.!|~<>$])/g, "$1").replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">")).join("");
|
|
58
|
+
return cell2.split(/(<br>|<\/?(?:u|sup|sub)>|<img\b[^>]*>)/).map((part, i) => i % 2 ? part : part.replace(/\\([\\`*_{}[\]()#+\-.!|~<>$])/g, "$1").replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">")).join("");
|
|
55
59
|
}
|
|
56
60
|
function splitPipeRow(line) {
|
|
57
61
|
const t = line.trim().replace(/^\|/, "").replace(/(?<!\\)\|$/, "");
|
|
@@ -770,7 +774,9 @@ function parseCharProperties(doc, map) {
|
|
|
770
774
|
else if (localTag === "strikeout") {
|
|
771
775
|
const shape = k.getAttribute("shape") || "";
|
|
772
776
|
if (isRealStrikeShape(shape)) prop.strike = true;
|
|
773
|
-
} else if (localTag === "
|
|
777
|
+
} else if (localTag === "supscript") prop.script = "sup";
|
|
778
|
+
else if (localTag === "subscript") prop.script = "sub";
|
|
779
|
+
else if (localTag === "underline") {
|
|
774
780
|
if (k.getAttribute("type") === "BOTTOM") prop.underline = true;
|
|
775
781
|
}
|
|
776
782
|
}
|
|
@@ -1848,6 +1854,7 @@ function extractRunSpans(para2, ctx, mode, requireMixed) {
|
|
|
1848
1854
|
const span2 = { text };
|
|
1849
1855
|
if (mode !== "foreign" && prId === KORDOC_CHAR_CODE) span2.code = true;
|
|
1850
1856
|
else {
|
|
1857
|
+
if (cp?.script) span2.text = wrapScript(text, cp.script);
|
|
1851
1858
|
if (cp?.bold) span2.bold = true;
|
|
1852
1859
|
if (cp?.italic) span2.italic = true;
|
|
1853
1860
|
if (cp?.strike) span2.strike = true;
|
|
@@ -1867,6 +1874,7 @@ function extractRunSpans(para2, ctx, mode, requireMixed) {
|
|
|
1867
1874
|
}
|
|
1868
1875
|
}
|
|
1869
1876
|
if (requireMixed && !merged.some((s) => s.strike || s.underline) && !merged.some((s) => !(s.bold || s.italic || s.code))) return null;
|
|
1877
|
+
for (const s of merged) s.text = tidyScriptTags(s.text);
|
|
1870
1878
|
return merged;
|
|
1871
1879
|
}
|
|
1872
1880
|
function gongmunDepthFromIndent(para2, left, ctx) {
|
|
@@ -2516,6 +2524,16 @@ function extractHyperlinkHref(fieldBegin) {
|
|
|
2516
2524
|
function isInDeletedRange(ctx) {
|
|
2517
2525
|
return (ctx?.shared.track.deleteDepth ?? 0) > 0;
|
|
2518
2526
|
}
|
|
2527
|
+
function runHasOnlyText(run) {
|
|
2528
|
+
const kids = run.childNodes;
|
|
2529
|
+
for (let i = 0; i < (kids?.length ?? 0); i++) {
|
|
2530
|
+
const k = kids[i];
|
|
2531
|
+
if (k.nodeType !== 1) continue;
|
|
2532
|
+
const t = (k.tagName || k.localName || "").replace(/^[^:]+:/, "");
|
|
2533
|
+
if (t !== "t" && t !== "linesegarray") return false;
|
|
2534
|
+
}
|
|
2535
|
+
return true;
|
|
2536
|
+
}
|
|
2519
2537
|
function extractParagraphInfo(para2, styleMap, ctx) {
|
|
2520
2538
|
let text = "";
|
|
2521
2539
|
let href;
|
|
@@ -2772,11 +2790,19 @@ function extractParagraphInfo(para2, styleMap, ctx) {
|
|
|
2772
2790
|
}
|
|
2773
2791
|
break;
|
|
2774
2792
|
}
|
|
2775
|
-
// run
|
|
2776
|
-
|
|
2793
|
+
// run 요소 — hp:r 은 charPrIDRef 를 문단 대표 스타일로. 첨자 글자 모양이면 이 run 이 더한 글을 <sup>·<sub> 로
|
|
2794
|
+
// (개체·각주 등 컨트롤을 품은 run·필드 경계가 걸친 run 은 글 위치가 섞여 감싸지 않는다)
|
|
2795
|
+
case "r":
|
|
2796
|
+
case "run": {
|
|
2777
2797
|
const runCharPr = child.getAttribute("charPrIDRef");
|
|
2778
|
-
if (runCharPr && !charPrId) charPrId = runCharPr;
|
|
2798
|
+
if (tag === "r" && runCharPr && !charPrId) charPrId = runCharPr;
|
|
2799
|
+
const script = runCharPr ? styleMap?.charProperties.get(runCharPr)?.script : void 0;
|
|
2800
|
+
const start = text.length, nLinks = linkRanges.length, nOpen = openFields.length;
|
|
2779
2801
|
walk(child, depth + 1);
|
|
2802
|
+
if (script && linkRanges.length === nLinks && openFields.length === nOpen && runHasOnlyText(child)) {
|
|
2803
|
+
const added = text.slice(start);
|
|
2804
|
+
if (added && !/[\x1E\x1F\n$]/.test(added)) text = text.slice(0, start) + wrapScript(added, script);
|
|
2805
|
+
}
|
|
2780
2806
|
break;
|
|
2781
2807
|
}
|
|
2782
2808
|
default:
|
|
@@ -2800,6 +2826,7 @@ function extractParagraphInfo(para2, styleMap, ctx) {
|
|
|
2800
2826
|
}
|
|
2801
2827
|
const leaderIdx = text.indexOf("");
|
|
2802
2828
|
if (leaderIdx >= 0) text = text.substring(0, leaderIdx);
|
|
2829
|
+
text = tidyScriptTags(text);
|
|
2803
2830
|
const cleanParaText = (raw) => {
|
|
2804
2831
|
let t = raw.replace(/[ \t]+/g, " ").trim();
|
|
2805
2832
|
if (/^그림입니다\.?\s*원본\s*그림의\s*(이름|크기)/.test(t)) t = "";
|
|
@@ -3043,6 +3070,14 @@ function isExtendedOnlyCtrlChar(ch) {
|
|
|
3043
3070
|
function appendParaText(state, data, resolveControl) {
|
|
3044
3071
|
let result = "";
|
|
3045
3072
|
let i = 0;
|
|
3073
|
+
const wbase = state.wpos ?? 0;
|
|
3074
|
+
const setScript = (want) => {
|
|
3075
|
+
const cur = state.script ?? null;
|
|
3076
|
+
if (cur === want) return;
|
|
3077
|
+
if (cur) result += `</${cur}>`;
|
|
3078
|
+
if (want) result += `<${want}>`;
|
|
3079
|
+
state.script = want;
|
|
3080
|
+
};
|
|
3046
3081
|
const base = state.text.length;
|
|
3047
3082
|
const resolveAt = (byteOffset, extended) => {
|
|
3048
3083
|
const ctrlId = data.readUInt32LE(byteOffset);
|
|
@@ -3053,6 +3088,7 @@ function appendParaText(state, data, resolveControl) {
|
|
|
3053
3088
|
};
|
|
3054
3089
|
while (i + 1 < data.length) {
|
|
3055
3090
|
const ch = data.readUInt16LE(i);
|
|
3091
|
+
if (state.scriptAt) setScript(ch >= 32 ? state.scriptAt(wbase + i / 2) : null);
|
|
3056
3092
|
i += 2;
|
|
3057
3093
|
switch (ch) {
|
|
3058
3094
|
// ── char 타입 (2바이트만, 확장 데이터 없음) ──
|
|
@@ -3124,6 +3160,8 @@ function appendParaText(state, data, resolveControl) {
|
|
|
3124
3160
|
break;
|
|
3125
3161
|
}
|
|
3126
3162
|
}
|
|
3163
|
+
if (state.scriptAt) setScript(null);
|
|
3164
|
+
state.wpos = wbase + Math.floor(data.length / 2);
|
|
3127
3165
|
state.text += result;
|
|
3128
3166
|
}
|
|
3129
3167
|
function extractEquationText(data) {
|
|
@@ -5210,6 +5248,7 @@ function parseParagraph(records, start, end, ctx) {
|
|
|
5210
5248
|
const paraShapeId = header.data.length >= 10 ? header.data.readUInt16LE(8) : -1;
|
|
5211
5249
|
const textRecords = [];
|
|
5212
5250
|
const charShapeIds = [];
|
|
5251
|
+
const charShapeRuns = [];
|
|
5213
5252
|
const ctrls = [];
|
|
5214
5253
|
let i = start + 1;
|
|
5215
5254
|
while (i < end) {
|
|
@@ -5228,6 +5267,7 @@ function parseParagraph(records, start, end, ctx) {
|
|
|
5228
5267
|
} else if (rec.tagId === TAG_CHAR_SHAPE && rec.level === baseLevel + 1 && rec.data.length >= 8) {
|
|
5229
5268
|
for (let offset = 0; offset + 7 < rec.data.length; offset += 8) {
|
|
5230
5269
|
charShapeIds.push(rec.data.readUInt32LE(offset + 4));
|
|
5270
|
+
charShapeRuns.push([rec.data.readUInt32LE(offset), rec.data.readUInt32LE(offset + 4)]);
|
|
5231
5271
|
}
|
|
5232
5272
|
}
|
|
5233
5273
|
i++;
|
|
@@ -5241,6 +5281,7 @@ function parseParagraph(records, start, end, ctx) {
|
|
|
5241
5281
|
const state = createParaTextState();
|
|
5242
5282
|
state.leaderMark = true;
|
|
5243
5283
|
state.dollarMark = true;
|
|
5284
|
+
state.scriptAt = scriptLookup(charShapeRuns, ctx.docInfo);
|
|
5244
5285
|
const resolver = (idx, id) => {
|
|
5245
5286
|
let ctrl = idx >= 0 && idx < ctrls.length ? ctrls[idx] : void 0;
|
|
5246
5287
|
if (!ctrl || ctrl.idRaw !== id && ctrl.id !== id) {
|
|
@@ -5282,6 +5323,7 @@ function parseParagraph(records, start, end, ctx) {
|
|
|
5282
5323
|
const leaderAt = text.indexOf(LEADER_TAB_MARK);
|
|
5283
5324
|
if (leaderAt >= 0) text = text.slice(0, leaderAt);
|
|
5284
5325
|
if (text.includes(LITERAL_DOLLAR_MARK)) text = text.replaceAll(LITERAL_DOLLAR_MARK, "\\$");
|
|
5326
|
+
text = tidyScriptTags(text);
|
|
5285
5327
|
let headingLevel = 0;
|
|
5286
5328
|
let headMarker = null;
|
|
5287
5329
|
const ps = ctx.docInfo && paraShapeId >= 0 && paraShapeId < ctx.docInfo.paraShapes.length ? ctx.docInfo.paraShapes[paraShapeId] : null;
|
|
@@ -5647,6 +5689,20 @@ function pictureToImageBlock(data, ctx) {
|
|
|
5647
5689
|
const storageId = item && item.storageId > 0 ? item.storageId : binDataId;
|
|
5648
5690
|
return { type: "image", text: String(storageId), pageNumber: ctx.page };
|
|
5649
5691
|
}
|
|
5692
|
+
function scriptLookup(runs, docInfo) {
|
|
5693
|
+
if (!docInfo || !runs.length) return void 0;
|
|
5694
|
+
const kindOf = (id) => {
|
|
5695
|
+
const f = docInfo.charShapes[id]?.attrFlags ?? 0;
|
|
5696
|
+
return f & 1 << 15 ? "sup" : f & 1 << 16 ? "sub" : null;
|
|
5697
|
+
};
|
|
5698
|
+
if (!runs.some(([, id]) => kindOf(id))) return void 0;
|
|
5699
|
+
let k = -1;
|
|
5700
|
+
return (pos) => {
|
|
5701
|
+
if (k >= 0 && runs[k][0] > pos) k = -1;
|
|
5702
|
+
while (k + 1 < runs.length && runs[k + 1][0] <= pos) k++;
|
|
5703
|
+
return k >= 0 ? kindOf(runs[k][1]) : null;
|
|
5704
|
+
};
|
|
5705
|
+
}
|
|
5650
5706
|
function resolveCharStyle(charShapeIds, docInfo) {
|
|
5651
5707
|
if (charShapeIds.length === 0 || docInfo.charShapes.length === 0) return void 0;
|
|
5652
5708
|
const freq = /* @__PURE__ */ new Map();
|
|
@@ -20734,6 +20790,9 @@ function extractRun(r) {
|
|
|
20734
20790
|
if (rPrEls.length > 0) {
|
|
20735
20791
|
bold = getChildElements(rPrEls[0], "b").length > 0;
|
|
20736
20792
|
italic = getChildElements(rPrEls[0], "i").length > 0;
|
|
20793
|
+
const va = getChildElements(rPrEls[0], "vertAlign")[0];
|
|
20794
|
+
const v = va ? getAttr(va, "val") : null;
|
|
20795
|
+
if (v === "superscript" || v === "subscript") text = wrapScript(text, v === "superscript" ? "sup" : "sub");
|
|
20737
20796
|
}
|
|
20738
20797
|
return { text, bold, italic };
|
|
20739
20798
|
}
|
|
@@ -20831,7 +20890,7 @@ function collectInline(p, footnotes, rels) {
|
|
|
20831
20890
|
if (isDisplayMath(om)) parts.push(" $$" + latex + "$$ ");
|
|
20832
20891
|
else parts.push(" $" + latex + "$ ");
|
|
20833
20892
|
}
|
|
20834
|
-
const text = parts.join("").replace(/[ \t]{2,}/g, " ").trim();
|
|
20893
|
+
const text = tidyScriptTags(parts.join("")).replace(/[ \t]{2,}/g, " ").trim();
|
|
20835
20894
|
return { text, bold, italic, footnoteText };
|
|
20836
20895
|
}
|
|
20837
20896
|
var COUNTERS = /* @__PURE__ */ new WeakMap();
|
|
@@ -21704,7 +21763,7 @@ var ValueCursor = class {
|
|
|
21704
21763
|
}
|
|
21705
21764
|
};
|
|
21706
21765
|
function normalizeLabel(label) {
|
|
21707
|
-
return label.trim().replace(/[::\s()()·]/g, "");
|
|
21766
|
+
return label.trim().replace(/<\/?su[bp]>/g, "").replace(/[::\s()()·]/g, "");
|
|
21708
21767
|
}
|
|
21709
21768
|
function findMatchingKey(cellLabel, values) {
|
|
21710
21769
|
if (values.has(cellLabel)) return cellLabel;
|
|
@@ -25357,293 +25416,6 @@ function bodyTitleGeom(bodyHeight, ov = {}) {
|
|
|
25357
25416
|
return { barH: BODY_TITLE_GEOM.barH, titleH: Math.round(BODY_TITLE_GEOM.titleH * s) };
|
|
25358
25417
|
}
|
|
25359
25418
|
|
|
25360
|
-
// src/hwpx/gongmun.ts
|
|
25361
|
-
var OFFICIAL_MARGINS = { top: 20, bottom: 15, left: 20, right: 15 };
|
|
25362
|
-
var GAEJOSIK_MARGINS = { top: 15, bottom: 15, left: 20, right: 20 };
|
|
25363
|
-
var SEOUL_REPORT_MARGINS = { top: 13, bottom: 13, left: 18, right: 18 };
|
|
25364
|
-
var SEOUL_REPORT_HEADER_FOOTER = 3600;
|
|
25365
|
-
var GAEJOSIK_HEADER_FOOTER = 4251;
|
|
25366
|
-
var MINISTRY_MARGINS = { top: 10, bottom: 10, left: 20, right: 20 };
|
|
25367
|
-
var MINISTRY_HEADER_FOOTER = 2835;
|
|
25368
|
-
var PRESET_DEFAULTS = {
|
|
25369
|
-
// 서울시 실결재 104건 중 12pt 64건(13pt 30, 15pt 5) — 지배값을 기본으로 사용.
|
|
25370
|
-
official: { bodyPt: 12, lineSpacing: 160, numbering: "standard" },
|
|
25371
|
-
report: { bodyPt: 15, lineSpacing: 160, numbering: "report" },
|
|
25372
|
-
// 실측 추진계획안: □ → ㅇ → * 계층.
|
|
25373
|
-
plan: { bodyPt: 15, lineSpacing: 160, numbering: "report" },
|
|
25374
|
-
notice: { bodyPt: 15, lineSpacing: 160, numbering: "standard" },
|
|
25375
|
-
minutes: { bodyPt: 14, lineSpacing: 130, numbering: "standard" },
|
|
25376
|
-
gaejosik: { bodyPt: 15, lineSpacing: 160, numbering: "gaejosik" },
|
|
25377
|
-
// 보도자료 — 실측(국토부 실물): 본문 바탕 14pt 160%, □→ㅇ→*(각주) 부호
|
|
25378
|
-
press: { bodyPt: 14, lineSpacing: 160, numbering: "report" },
|
|
25379
|
-
// 중앙부처 업무보고 — 실측(재경부 2차 업무보고): 함초롬바탕 15pt, 줄피치 21.7pt(≈145%), □→ㅇ→-→*(각주)
|
|
25380
|
-
ministry: { bodyPt: 15, lineSpacing: 145, numbering: "report" }
|
|
25381
|
-
};
|
|
25382
|
-
var PRESET_ALIAS = {
|
|
25383
|
-
official: "official",
|
|
25384
|
-
\uAE30\uC548\uBB38: "official",
|
|
25385
|
-
\uC2DC\uD589\uBB38: "official",
|
|
25386
|
-
\uACF5\uBB38: "official",
|
|
25387
|
-
\uACF5\uBB38\uC11C: "official",
|
|
25388
|
-
report: "report",
|
|
25389
|
-
\uBCF4\uACE0\uC11C: "report",
|
|
25390
|
-
plan: "plan",
|
|
25391
|
-
\uACC4\uD68D\uC11C: "plan",
|
|
25392
|
-
\uACC4\uD68D: "plan",
|
|
25393
|
-
notice: "notice",
|
|
25394
|
-
\uD1B5\uC9C0: "notice",
|
|
25395
|
-
\uC54C\uB9BC: "notice",
|
|
25396
|
-
\uC548\uB0B4: "notice",
|
|
25397
|
-
minutes: "minutes",
|
|
25398
|
-
\uD68C\uC758\uB85D: "minutes",
|
|
25399
|
-
gaejosik: "gaejosik",
|
|
25400
|
-
\uAC1C\uC870\uC2DD: "gaejosik",
|
|
25401
|
-
\uAC1C\uC870\uC2DD\uBCF4\uACE0\uC11C: "gaejosik",
|
|
25402
|
-
\uC815\uBD80\uBCF4\uACE0\uC11C: "gaejosik",
|
|
25403
|
-
\uC815\uBD80\uD45C\uC900\uAC1C\uC870\uC2DD\uBCF4\uACE0\uC11C: "gaejosik",
|
|
25404
|
-
press: "press",
|
|
25405
|
-
\uBCF4\uB3C4\uC790\uB8CC: "press",
|
|
25406
|
-
ministry: "ministry",
|
|
25407
|
-
\uC5C5\uBB34\uBCF4\uACE0: "ministry",
|
|
25408
|
-
\uBD80\uCC98\uC5C5\uBB34\uBCF4\uACE0: "ministry",
|
|
25409
|
-
\uC911\uC559\uBD80\uCC98\uBCF4\uACE0\uC11C: "ministry"
|
|
25410
|
-
};
|
|
25411
|
-
function normalizeGongmunPreset(preset) {
|
|
25412
|
-
if (!preset) return "official";
|
|
25413
|
-
return PRESET_ALIAS[preset.trim()] ?? "official";
|
|
25414
|
-
}
|
|
25415
|
-
function usesReportFonts(preset) {
|
|
25416
|
-
return preset === "gaejosik" || preset === "report" || preset === "plan";
|
|
25417
|
-
}
|
|
25418
|
-
function usesAsteriskThird(preset) {
|
|
25419
|
-
return preset === "plan" || preset === "press";
|
|
25420
|
-
}
|
|
25421
|
-
function needsGaejosikAssets(gongmun) {
|
|
25422
|
-
return usesReportFonts(gongmun.preset) || gongmun.cover !== null || gongmun.toc;
|
|
25423
|
-
}
|
|
25424
|
-
function assertFiniteRange(name, value, min, max) {
|
|
25425
|
-
if (!Number.isFinite(value) || value < min || value > max) {
|
|
25426
|
-
throw new KordocError(`${name} must be a finite number between ${min} and ${max}`);
|
|
25427
|
-
}
|
|
25428
|
-
}
|
|
25429
|
-
function validateGongmunOptions(opts) {
|
|
25430
|
-
if (opts.bodyPt !== void 0) assertFiniteRange("bodyPt", opts.bodyPt, 6, 40);
|
|
25431
|
-
if (opts.lineSpacing !== void 0) assertFiniteRange("lineSpacing", opts.lineSpacing, 50, 300);
|
|
25432
|
-
if (typeof opts.autoFit === "object" && opts.autoFit.minRatio !== void 0) {
|
|
25433
|
-
assertFiniteRange("autoFit.minRatio", opts.autoFit.minRatio, 50, 99);
|
|
25434
|
-
}
|
|
25435
|
-
if (opts.margins) {
|
|
25436
|
-
for (const side of ["top", "bottom", "left", "right"]) {
|
|
25437
|
-
const value = opts.margins[side];
|
|
25438
|
-
assertFiniteRange(`margins.${side}`, value, 0, 200);
|
|
25439
|
-
}
|
|
25440
|
-
if (opts.margins.left + opts.margins.right >= 210) {
|
|
25441
|
-
throw new KordocError("margins.left + margins.right must be less than 210");
|
|
25442
|
-
}
|
|
25443
|
-
if (opts.margins.top + opts.margins.bottom >= 297) {
|
|
25444
|
-
throw new KordocError("margins.top + margins.bottom must be less than 297");
|
|
25445
|
-
}
|
|
25446
|
-
}
|
|
25447
|
-
if (opts.sizes) {
|
|
25448
|
-
for (const [key, value] of Object.entries(opts.sizes)) {
|
|
25449
|
-
if (value !== void 0) assertFiniteRange(`sizes.${key}`, value, 6, 60);
|
|
25450
|
-
}
|
|
25451
|
-
}
|
|
25452
|
-
if (opts.approval && opts.approval.length > 6) {
|
|
25453
|
-
throw new KordocError("approval must contain at most 6 labels");
|
|
25454
|
-
}
|
|
25455
|
-
if (opts.levels) {
|
|
25456
|
-
for (const [key, st] of Object.entries(opts.levels)) {
|
|
25457
|
-
const depth = Number(key);
|
|
25458
|
-
if (!Number.isInteger(depth) || depth < 0 || depth >= GONGMUN_LEVEL_COUNT) {
|
|
25459
|
-
throw new KordocError(`levels: depth must be an integer between 0 and ${GONGMUN_LEVEL_COUNT - 1} (got "${key}")`);
|
|
25460
|
-
}
|
|
25461
|
-
if (!st || typeof st !== "object") throw new KordocError(`levels.${key} must be an object {font, pt, bold}`);
|
|
25462
|
-
if (st.pt !== void 0) assertFiniteRange(`levels.${key}.pt`, st.pt, 6, 60);
|
|
25463
|
-
if (st.font !== void 0 && (typeof st.font !== "string" || !st.font.trim())) {
|
|
25464
|
-
throw new KordocError(`levels.${key}.font must be a non-empty string`);
|
|
25465
|
-
}
|
|
25466
|
-
}
|
|
25467
|
-
}
|
|
25468
|
-
}
|
|
25469
|
-
var GONGMUN_LEVEL_COUNT = 8;
|
|
25470
|
-
function resolveLevels(levels, bodyHeight) {
|
|
25471
|
-
if (!levels) return null;
|
|
25472
|
-
const out = {};
|
|
25473
|
-
for (const [key, st] of Object.entries(levels)) {
|
|
25474
|
-
if (st.font === void 0 && st.pt === void 0 && st.bold === void 0) continue;
|
|
25475
|
-
out[Number(key)] = {
|
|
25476
|
-
font: st.font?.trim() || null,
|
|
25477
|
-
height: st.pt !== void 0 ? Math.round(st.pt * 100) : bodyHeight,
|
|
25478
|
-
bold: !!st.bold
|
|
25479
|
-
};
|
|
25480
|
-
}
|
|
25481
|
-
return Object.keys(out).length > 0 ? out : null;
|
|
25482
|
-
}
|
|
25483
|
-
function levelMarkerHeight(g3, depth) {
|
|
25484
|
-
return g3.levels?.[depth]?.height ?? g3.bodyHeight;
|
|
25485
|
-
}
|
|
25486
|
-
function incompatibleGongmunWarnings(opts) {
|
|
25487
|
-
const warns = [];
|
|
25488
|
-
const preset = normalizeGongmunPreset(opts.preset);
|
|
25489
|
-
if (opts.docHead && preset !== "official") warns.push(`doc_head(\uB450\uBB38)\uB294 \uAE30\uC548\uBB38(official) \uC804\uC6A9 \u2014 '${preset}' \uD504\uB9AC\uC14B\uC5D0\uC11C \uBB34\uC2DC\uB428`);
|
|
25490
|
-
if (opts.docFoot && preset !== "official") warns.push(`doc_foot(\uACB0\uBB38)\uB294 \uAE30\uC548\uBB38(official) \uC804\uC6A9 \u2014 '${preset}' \uD504\uB9AC\uC14B\uC5D0\uC11C \uBB34\uC2DC\uB428`);
|
|
25491
|
-
if (opts.noticeHead && preset !== "notice") warns.push(`notice_head(\uACF5\uACE0\uBC88\uD638\xB7\uBC1C\uC2E0\uBA85\uC758)\uB294 \uD1B5\uC9C0(notice) \uC804\uC6A9 \u2014 '${preset}' \uD504\uB9AC\uC14B\uC5D0\uC11C \uBB34\uC2DC\uB428`);
|
|
25492
|
-
if (opts.press && preset !== "press") warns.push(`press(\uBA38\uB9AC\uBC15\uC2A4\xB7\uBD80\uC81C\xB7\uB2F4\uB2F9)\uB294 \uBCF4\uB3C4\uC790\uB8CC(press) \uC804\uC6A9 \u2014 '${preset}' \uD504\uB9AC\uC14B\uC5D0\uC11C \uBB34\uC2DC\uB428`);
|
|
25493
|
-
if (preset === "press" && (opts.cover === true || typeof opts.cover === "object" || opts.toc === true)) {
|
|
25494
|
-
warns.push("\uBCF4\uB3C4\uC790\uB8CC\uB294 \uBA38\uB9AC\uBC15\uC2A4 \uC11C\uC2DD\uACFC \uC591\uB9BD \uBD88\uAC00\uB77C \uD45C\uC9C0\xB7\uBAA9\uCC28\uAC00 \uBB34\uC2DC\uB428");
|
|
25495
|
-
}
|
|
25496
|
-
if (opts.sizes && Object.keys(opts.sizes).length > 0 && !usesReportFonts(preset)) {
|
|
25497
|
-
warns.push(`sizes(\uAC1C\uC870\uC2DD \uC694\uC18C \uD06C\uAE30)\uB294 \uAC1C\uC870\uC2DD\xB7\uBCF4\uACE0\uC11C\xB7\uACC4\uD68D\uC11C \uC804\uC6A9 \u2014 '${preset}' \uD504\uB9AC\uC14B\uC5D0\uC11C \uBB34\uC2DC\uB428`);
|
|
25498
|
-
}
|
|
25499
|
-
if (opts.suppressSingle && (opts.numbering ?? PRESET_DEFAULTS[preset].numbering) !== "standard") {
|
|
25500
|
-
warns.push(`suppress_single(\uB2E8\uC77C \uD615\uC81C \uBD80\uD638 \uC0DD\uB7B5)\uC740 \uBC95\uC815 \uBC88\uD638(standard) \uC804\uC6A9 \u2014 '${preset}' \uD504\uB9AC\uC14B(\uBD88\uB9BF \uCCB4\uACC4)\uC5D0\uC11C \uBB34\uB3D9\uC791`);
|
|
25501
|
-
}
|
|
25502
|
-
return warns;
|
|
25503
|
-
}
|
|
25504
|
-
function hexColorOption(name, value) {
|
|
25505
|
-
if (value === void 0) return void 0;
|
|
25506
|
-
const v = value.trim();
|
|
25507
|
-
if (!/^#[0-9a-fA-F]{6}$/.test(v)) throw new KordocError(`${name} must be a #RRGGBB color (got "${value}")`);
|
|
25508
|
-
return v.toUpperCase();
|
|
25509
|
-
}
|
|
25510
|
-
function resolveGongmun(opts) {
|
|
25511
|
-
validateGongmunOptions(opts);
|
|
25512
|
-
const preset = normalizeGongmunPreset(opts.preset);
|
|
25513
|
-
const d = PRESET_DEFAULTS[preset];
|
|
25514
|
-
const bodyPt = opts.bodyPt ?? d.bodyPt;
|
|
25515
|
-
const autoFitMinRatio = opts.autoFit === false ? null : typeof opts.autoFit === "object" ? Math.min(Math.max(opts.autoFit.minRatio ?? 90, 50), 99) : 90;
|
|
25516
|
-
const ministry = preset === "ministry";
|
|
25517
|
-
const coverOn = opts.cover !== void 0 ? opts.cover !== false : preset === "gaejosik" || ministry;
|
|
25518
|
-
const coverOpts = typeof opts.cover === "object" ? opts.cover : {};
|
|
25519
|
-
const gaejosik = preset === "gaejosik";
|
|
25520
|
-
const reportFamily = gaejosik || preset === "report" || preset === "plan" || preset === "notice" || preset === "press";
|
|
25521
|
-
return {
|
|
25522
|
-
preset,
|
|
25523
|
-
bodyFont: opts.bodyFont ?? "myeongjo",
|
|
25524
|
-
bodyHeight: Math.round(bodyPt * 100),
|
|
25525
|
-
lineSpacing: opts.lineSpacing ?? d.lineSpacing,
|
|
25526
|
-
numbering: opts.numbering ?? d.numbering,
|
|
25527
|
-
margins: opts.margins ?? (ministry ? MINISTRY_MARGINS : preset === "report" || preset === "plan" ? SEOUL_REPORT_MARGINS : reportFamily ? GAEJOSIK_MARGINS : OFFICIAL_MARGINS),
|
|
25528
|
-
centerTitle: opts.centerTitle ?? true,
|
|
25529
|
-
autoFitMinRatio,
|
|
25530
|
-
// 보도자료는 머리박스가 1페이지 최상단을 차지하는 서식이라 표지·목차와 양립 불가 —
|
|
25531
|
-
// 켜면 머리박스가 표지에 얹히고 25pt 제목·부제가 유실된다 (docHead 프리셋 게이팅과 동일 관례)
|
|
25532
|
-
cover: coverOn && preset !== "press" ? { date: coverOpts.date ?? null, org: coverOpts.org ?? "", ...coverOpts.dept ? { dept: coverOpts.dept } : {}, ...coverOpts.label ? { label: coverOpts.label } : {} } : null,
|
|
25533
|
-
bodyPtExplicit: opts.bodyPt !== void 0,
|
|
25534
|
-
lineSpacingExplicit: opts.lineSpacing !== void 0,
|
|
25535
|
-
bodyFontExplicit: opts.bodyFont !== void 0,
|
|
25536
|
-
summary: opts.summary?.trim() || null,
|
|
25537
|
-
docInfo: opts.docInfo ?? null,
|
|
25538
|
-
toc: preset !== "press" && (opts.toc ?? (gaejosik || ministry)),
|
|
25539
|
-
fonts: opts.fonts ?? {},
|
|
25540
|
-
sizes: opts.sizes ?? {},
|
|
25541
|
-
levels: resolveLevels(opts.levels, Math.round(bodyPt * 100)),
|
|
25542
|
-
// 쪽번호 — 보고서 계열 관행(실측: 2_보고서 양식·추진계획·공고문 전부 하단 중앙)
|
|
25543
|
-
pageNumbers: opts.pageNumbers ?? (gaejosik || ministry || preset === "report" || preset === "plan"),
|
|
25544
|
-
// 머리말·꼬리말 — 실측: 보고서 계열 15mm(GT3·t2·춘천·브라더), 공고·보도 10mm,
|
|
25545
|
-
// 기안문 0(실결재 41/60건 h0/f0)
|
|
25546
|
-
headerFooter: ministry ? MINISTRY_HEADER_FOOTER : preset === "report" || preset === "plan" ? SEOUL_REPORT_HEADER_FOOTER : usesReportFonts(preset) ? GAEJOSIK_HEADER_FOOTER : preset === "notice" || preset === "press" ? 2835 : 0,
|
|
25547
|
-
// "끝." — 기안문 규정(본문 끝 2타+"끝."). 그 외는 opt-in
|
|
25548
|
-
endMark: opts.endMark ?? preset === "official",
|
|
25549
|
-
approval: opts.approval && opts.approval.length > 0 ? opts.approval : null,
|
|
25550
|
-
// 본문 제목박스 — 실측(GT3·GT12): 목차 뒤 본문 시작에 제목 반복. 표지 켜진 개조식 기본
|
|
25551
|
-
bodyTitleBox: opts.bodyTitleBox ?? (gaejosik && coverOn),
|
|
25552
|
-
// h2 말머리 — 실측: 보고서 양식 □ 대항목(QA-2), 공고문 아라비아("1. 사업개요", 바이오헬스 실측)
|
|
25553
|
-
// v5 라운드 3: 보고서·계획서 기본 band(띠 표) — 서울 plan 7/19·교육청 7/18 실측, 실무자 요청
|
|
25554
|
-
h2Marker: opts.h2Marker ?? (preset === "report" || preset === "plan" ? "band" : preset === "notice" ? "number" : "none"),
|
|
25555
|
-
// 띠 제목 색 — 실측 최다 #003366/흰 글자(계획서 띠 표 14개). 교육청형 밝은 띠는 옵션으로
|
|
25556
|
-
bandColor: hexColorOption("bandColor", opts.bandColor) ?? "#003366",
|
|
25557
|
-
bandTextColor: hexColorOption("bandTextColor", opts.bandTextColor) ?? "#FFFFFF",
|
|
25558
|
-
// 2단계 부호 — 실결재 기안문·공고문 ㅇ 지배(60건 중 ㅇ134:○5), 보고서 양식 계열 ○
|
|
25559
|
-
// v5: 서울 실결재 ㅇ(이응) 지배 — 보고서·계획서·통지·보도자료 ㅇ, 중앙부처 개조식 양식만 ○
|
|
25560
|
-
bullet2: opts.bullet2 ?? (preset === "gaejosik" ? "\u25CB" : "\u3147"),
|
|
25561
|
-
// 단일 형제 부호 생략 — 규정이지만 부호 없는 계단이 실무 눈에 어색 (실무자 QA)
|
|
25562
|
-
suppressSingle: opts.suppressSingle ?? false,
|
|
25563
|
-
docHead: preset === "official" && opts.docHead ? opts.docHead : null,
|
|
25564
|
-
docFoot: preset === "official" && opts.docFoot ? opts.docFoot : null,
|
|
25565
|
-
reportInfo: opts.reportInfo?.trim() || null,
|
|
25566
|
-
noticeHead: preset === "notice" && opts.noticeHead ? opts.noticeHead : null,
|
|
25567
|
-
// 보도자료 머리박스는 프리셋 자체가 요구 — 옵션 미지정이어도 빈 객체
|
|
25568
|
-
press: preset === "press" ? opts.press ?? {} : null
|
|
25569
|
-
};
|
|
25570
|
-
}
|
|
25571
|
-
var REPORT_BULLETS = ["\u25A1", "\u25CB", "-", "\u318D"];
|
|
25572
|
-
var ASTERISK_BULLETS = ["\u25A1", "\u25CB", "*", "\u318D"];
|
|
25573
|
-
function standardMarker(depth, n) {
|
|
25574
|
-
switch (depth) {
|
|
25575
|
-
case 0:
|
|
25576
|
-
return `${n + 1}.`;
|
|
25577
|
-
case 1:
|
|
25578
|
-
return `${hangulOrdinal(n)}.`;
|
|
25579
|
-
case 2:
|
|
25580
|
-
return `${n + 1})`;
|
|
25581
|
-
case 3:
|
|
25582
|
-
return `${hangulOrdinal(n)})`;
|
|
25583
|
-
case 4:
|
|
25584
|
-
return `(${n + 1})`;
|
|
25585
|
-
case 5:
|
|
25586
|
-
return `(${hangulOrdinal(n)})`;
|
|
25587
|
-
case 6:
|
|
25588
|
-
return circledNumber(n);
|
|
25589
|
-
case 7:
|
|
25590
|
-
return circledHangul(n);
|
|
25591
|
-
default:
|
|
25592
|
-
return circledHangul(n);
|
|
25593
|
-
}
|
|
25594
|
-
}
|
|
25595
|
-
function reportMarker(depth, bullet2 = "\u25CB", asteriskThird = false) {
|
|
25596
|
-
const bullets = asteriskThird ? ASTERISK_BULLETS : REPORT_BULLETS;
|
|
25597
|
-
const m = bullets[Math.min(depth, bullets.length - 1)];
|
|
25598
|
-
return depth === 1 ? bullet2 : m;
|
|
25599
|
-
}
|
|
25600
|
-
function markerWidth(marker, bodyHeight) {
|
|
25601
|
-
let em = SPACE_EM_FIXED;
|
|
25602
|
-
for (const c of marker) em += charWidthEm1000(c.codePointAt(0));
|
|
25603
|
-
return Math.round(em / 1e3 * bodyHeight);
|
|
25604
|
-
}
|
|
25605
|
-
function levelIndent(depth, bodyHeight, numbering, sizes = {}, bullet2 = "\u25CB", asteriskThird = false, markerHeight = bodyHeight) {
|
|
25606
|
-
if (numbering === "gaejosik") return gaejosikLevelIndent(depth, bodyHeight, sizes, bullet2);
|
|
25607
|
-
const marker = numbering === "report" ? reportMarker(depth, bullet2, asteriskThird) : standardMarker(depth, 0);
|
|
25608
|
-
return { left: Math.round(depth * bodyHeight), indent: -markerWidth(marker, markerHeight) };
|
|
25609
|
-
}
|
|
25610
|
-
function computeSuppression(depths) {
|
|
25611
|
-
const counts = /* @__PURE__ */ new Map();
|
|
25612
|
-
const keys = [];
|
|
25613
|
-
const path = [];
|
|
25614
|
-
for (const depth of depths) {
|
|
25615
|
-
path.length = depth + 1;
|
|
25616
|
-
path[depth] = (path[depth] ?? 0) + 1;
|
|
25617
|
-
const parentKey = path.slice(0, depth).join(".") + "|" + depth;
|
|
25618
|
-
keys.push(parentKey);
|
|
25619
|
-
counts.set(parentKey, (counts.get(parentKey) ?? 0) + 1);
|
|
25620
|
-
}
|
|
25621
|
-
return keys.map((k) => (counts.get(k) ?? 0) <= 1);
|
|
25622
|
-
}
|
|
25623
|
-
var GongmunNumberer = class {
|
|
25624
|
-
constructor(numbering, bullet2 = "\u25CB", asteriskThird = false) {
|
|
25625
|
-
this.numbering = numbering;
|
|
25626
|
-
this.bullet2 = bullet2;
|
|
25627
|
-
this.asteriskThird = asteriskThird;
|
|
25628
|
-
}
|
|
25629
|
-
numbering;
|
|
25630
|
-
bullet2;
|
|
25631
|
-
asteriskThird;
|
|
25632
|
-
counts = [];
|
|
25633
|
-
/** depth 항목 하나에 대한 마커. suppress=true면 빈 문자열(부호 없음) */
|
|
25634
|
-
next(depth, suppress) {
|
|
25635
|
-
this.counts.length = depth + 1;
|
|
25636
|
-
const n = this.counts[depth] ?? 0;
|
|
25637
|
-
this.counts[depth] = n + 1;
|
|
25638
|
-
if (suppress) return "";
|
|
25639
|
-
if (this.numbering === "gaejosik") return gaejosikMarker(depth, this.bullet2);
|
|
25640
|
-
return this.numbering === "report" ? reportMarker(depth, this.bullet2, this.asteriskThird) : standardMarker(depth, n);
|
|
25641
|
-
}
|
|
25642
|
-
reset() {
|
|
25643
|
-
this.counts = [];
|
|
25644
|
-
}
|
|
25645
|
-
};
|
|
25646
|
-
|
|
25647
25419
|
// src/hwpx/gen-ids.ts
|
|
25648
25420
|
var NS_SECTION = "http://www.hancom.co.kr/hwpml/2011/section";
|
|
25649
25421
|
var NS_PARA = "http://www.hancom.co.kr/hwpml/2011/paragraph";
|
|
@@ -25824,6 +25596,1019 @@ function charVariantBase(richAssets, isGongmun = true) {
|
|
|
25824
25596
|
}
|
|
25825
25597
|
var GONGMUN_BODY_RATIO = 95;
|
|
25826
25598
|
|
|
25599
|
+
// src/hwpx/gen-gongmun-extra.ts
|
|
25600
|
+
var extraTableId = EXTRA_TABLE_ID_BASE;
|
|
25601
|
+
function resetExtraTableIds() {
|
|
25602
|
+
extraTableId = EXTRA_TABLE_ID_BASE;
|
|
25603
|
+
}
|
|
25604
|
+
function tbl(rows, w, h, cols) {
|
|
25605
|
+
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>`;
|
|
25606
|
+
}
|
|
25607
|
+
function tc(opts) {
|
|
25608
|
+
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>`;
|
|
25609
|
+
}
|
|
25610
|
+
function para(text, paraPrId, charPrId) {
|
|
25611
|
+
return `<hp:p paraPrIDRef="${paraPrId}" styleIDRef="0"><hp:run charPrIDRef="${charPrId}"><hp:t>${escapeTextXml(text)}</hp:t></hp:run></hp:p>`;
|
|
25612
|
+
}
|
|
25613
|
+
var APPROVAL_COL_W = 7085;
|
|
25614
|
+
var APPROVAL_LABEL_H = 1300;
|
|
25615
|
+
var APPROVAL_SIGN_H = 4200;
|
|
25616
|
+
function buildApprovalTable(labels, reg, approvalCharPr) {
|
|
25617
|
+
const n = labels.length;
|
|
25618
|
+
const w = APPROVAL_COL_W * n;
|
|
25619
|
+
const bfAt = (row, col) => reg.get({
|
|
25620
|
+
t: row === 0 ? "thick" : "thin",
|
|
25621
|
+
b: row === 1 ? "thick" : "thin",
|
|
25622
|
+
l: col === 0 ? "thick" : "thin",
|
|
25623
|
+
r: col === n - 1 ? "thick" : "thin"
|
|
25624
|
+
});
|
|
25625
|
+
const labelRow = labels.map(
|
|
25626
|
+
(label, c) => tc({ bf: bfAt(0, c), row: 0, col: c, w: APPROVAL_COL_W, h: APPROVAL_LABEL_H, paras: para(label, GONGMUN_PARA_APPROVAL, approvalCharPr) })
|
|
25627
|
+
).join("");
|
|
25628
|
+
const signRow = labels.map(
|
|
25629
|
+
(_, c) => tc({ bf: bfAt(1, c), row: 1, col: c, w: APPROVAL_COL_W, h: APPROVAL_SIGN_H, paras: para("", GONGMUN_PARA_APPROVAL, approvalCharPr) })
|
|
25630
|
+
).join("");
|
|
25631
|
+
const table2 = tbl([labelRow, signRow], w, APPROVAL_LABEL_H + APPROVAL_SIGN_H, n);
|
|
25632
|
+
return `<hp:p paraPrIDRef="${GONGMUN_RIGHT}" styleIDRef="0"><hp:run charPrIDRef="${CHAR_NORMAL}">${table2}</hp:run></hp:p>`;
|
|
25633
|
+
}
|
|
25634
|
+
function hasEndMark(lastText) {
|
|
25635
|
+
return /(^|\s)끝\s*\.\s*$/.test(lastText.trim());
|
|
25636
|
+
}
|
|
25637
|
+
function buildEndMark() {
|
|
25638
|
+
return para(" \uB05D.", PARA_NORMAL, CHAR_NORMAL);
|
|
25639
|
+
}
|
|
25640
|
+
var TITLE_BOX = { barH: 382, titleH: 2850, color: "#0080C0", gradient: ["#0080C0", "#3CBFFF"] };
|
|
25641
|
+
function buildTitleBox(title, titleCharPr, barCharPr, bodyWidth, reg) {
|
|
25642
|
+
const w = bodyWidth - 280;
|
|
25643
|
+
const outer = { t: "thin", b: "thin", l: "thin", r: "thin" };
|
|
25644
|
+
const barTop = reg.get({ ...outer, fill: TITLE_BOX.color });
|
|
25645
|
+
const barBottom = reg.get({ ...outer, fill: { gradient: TITLE_BOX.gradient } });
|
|
25646
|
+
const mid = reg.get(outer);
|
|
25647
|
+
const barPara = para("", GJ_PARA_BAR, barCharPr);
|
|
25648
|
+
const rows = [
|
|
25649
|
+
tc({ bf: barTop, row: 0, col: 0, w, h: TITLE_BOX.barH, paras: barPara }),
|
|
25650
|
+
tc({ bf: mid, row: 1, col: 0, w, h: TITLE_BOX.titleH, paras: para(title, GONGMUN_TBL_CENTER, titleCharPr), name: "__kordoc_h1" }),
|
|
25651
|
+
tc({ bf: barBottom, row: 2, col: 0, w, h: TITLE_BOX.barH, paras: barPara })
|
|
25652
|
+
];
|
|
25653
|
+
const table2 = tbl(rows, w, TITLE_BOX.barH * 2 + TITLE_BOX.titleH, 1);
|
|
25654
|
+
return `<hp:p paraPrIDRef="${PARA_NORMAL}" styleIDRef="0"><hp:run charPrIDRef="${CHAR_NORMAL}">${table2}</hp:run></hp:p>`;
|
|
25655
|
+
}
|
|
25656
|
+
|
|
25657
|
+
// src/hwpx/fit-line.ts
|
|
25658
|
+
var RATIOS = [100, 97, 95, 92, 90, 87, 85];
|
|
25659
|
+
var SPACINGS = [0, -3, -5];
|
|
25660
|
+
function safety(faceClass, approx) {
|
|
25661
|
+
return faceClass.startsWith("font:") ? 0.98 : approx;
|
|
25662
|
+
}
|
|
25663
|
+
function fitOneLine(text, font, pt2, availHu, minPt = Math.max(pt2 - 3, 10), minRatio = 85) {
|
|
25664
|
+
const faceClass = faceClassForGen(font);
|
|
25665
|
+
const avail = availHu * safety(faceClass, 0.98);
|
|
25666
|
+
const fits = (p, r, s) => measureTextWidth(text, p * 100, r, { spacingPct: s, faceClass }) <= avail;
|
|
25667
|
+
if (fits(pt2, 100, 0)) return { pt: pt2, ratio: 100, spacing: 0, overflow: false };
|
|
25668
|
+
const ratios = RATIOS.filter((r) => r >= minRatio);
|
|
25669
|
+
for (let p = pt2; p >= minPt; p--) {
|
|
25670
|
+
for (const r of ratios) for (const s of SPACINGS) {
|
|
25671
|
+
if (fits(p, r, s)) return { pt: p, ratio: r, spacing: s, overflow: false };
|
|
25672
|
+
}
|
|
25673
|
+
}
|
|
25674
|
+
return { pt: minPt, ratio: ratios[ratios.length - 1], spacing: SPACINGS[SPACINGS.length - 1], overflow: true };
|
|
25675
|
+
}
|
|
25676
|
+
var SQUEEZE = (() => {
|
|
25677
|
+
const out = [];
|
|
25678
|
+
for (let sp = -1; sp >= -12; sp--) out.push([100, sp]);
|
|
25679
|
+
for (const r of [97, 95, 92, 90, 88]) for (let sp = 0; sp >= -12; sp--) out.push([r, sp]);
|
|
25680
|
+
return out;
|
|
25681
|
+
})();
|
|
25682
|
+
var LOOSE_LIMIT = 1;
|
|
25683
|
+
var PULL_TAIL = 0.5;
|
|
25684
|
+
var PULL_MAX = 0.15;
|
|
25685
|
+
var squeezeOf = (r, sp) => 1 - r / 100 * (1 + sp / 100);
|
|
25686
|
+
var ORPHAN_RATIO = 0.22;
|
|
25687
|
+
var ORPHAN_COST = 1.5;
|
|
25688
|
+
var COMPRESS_COST = 10;
|
|
25689
|
+
function worstLooseness(text, starts, firstW, contW, h, ratio, spacing, faceClass) {
|
|
25690
|
+
const spaceW = 0.5 * h * (ratio / 100) * (1 + spacing / 100);
|
|
25691
|
+
let worst = 0;
|
|
25692
|
+
for (let i = 0; i + 1 < starts.length; i++) {
|
|
25693
|
+
const seg = text.slice(starts[i], starts[i + 1]).replace(/ +$/, "");
|
|
25694
|
+
const slack = (i === 0 ? firstW : contW) - measureTextWidth(seg, h, ratio, { faceClass, spacingPct: spacing });
|
|
25695
|
+
const spaces = (seg.match(/ /g) ?? []).length;
|
|
25696
|
+
worst = Math.max(worst, slack / (Math.max(spaces, 1) * spaceW));
|
|
25697
|
+
}
|
|
25698
|
+
return worst;
|
|
25699
|
+
}
|
|
25700
|
+
function fitParagraph(text, font, pt2, firstW, contW, minRatio = 88, orphanRatio = ORPHAN_RATIO) {
|
|
25701
|
+
const faceClass = faceClassForGen(font);
|
|
25702
|
+
const h = pt2 * 100;
|
|
25703
|
+
const k = safety(faceClass, 0.95);
|
|
25704
|
+
const f = firstW * k, c = contW * k;
|
|
25705
|
+
const ladder = [[100, 0], ...SQUEEZE.filter(([r]) => r >= minRatio)];
|
|
25706
|
+
const base = simulateWrap(text, f, c, h, 100, "keep", { faceClass });
|
|
25707
|
+
if (base.lines < 2) return null;
|
|
25708
|
+
if (base.lastLineWidth <= c * PULL_TAIL) {
|
|
25709
|
+
let pull = null;
|
|
25710
|
+
for (const [r, sp] of ladder) {
|
|
25711
|
+
const amt = squeezeOf(r, sp);
|
|
25712
|
+
if (amt > PULL_MAX + 1e-9 || pull && amt >= pull.amt - 1e-9) continue;
|
|
25713
|
+
const w = simulateWrap(text, f, c, h, r, "keep", { faceClass, spacingPct: sp });
|
|
25714
|
+
if (w.lines < base.lines && worstLooseness(text, w.starts, f, c, h, r, sp, faceClass) <= LOOSE_LIMIT) pull = { r, sp, amt };
|
|
25715
|
+
}
|
|
25716
|
+
if (pull) return { ratio: pull.r, spacing: pull.sp };
|
|
25717
|
+
}
|
|
25718
|
+
let baseLines = 0;
|
|
25719
|
+
let best = null;
|
|
25720
|
+
for (const [r, sp] of ladder) {
|
|
25721
|
+
const w = simulateWrap(text, f, c, h, r, "keep", { faceClass, spacingPct: sp });
|
|
25722
|
+
if (!best) {
|
|
25723
|
+
if (w.lines < 2) return null;
|
|
25724
|
+
baseLines = w.lines;
|
|
25725
|
+
}
|
|
25726
|
+
if (w.lines > baseLines) continue;
|
|
25727
|
+
const orphan = w.lines > 1 && w.lastLineWidth <= contW * orphanRatio;
|
|
25728
|
+
const loose = worstLooseness(text, w.starts, f, c, h, r, sp, faceClass);
|
|
25729
|
+
const cost = Math.max(0, loose - LOOSE_LIMIT) + (orphan ? ORPHAN_COST : 0) + squeezeOf(r, sp) * COMPRESS_COST;
|
|
25730
|
+
if (!best || cost < best.cost - 1e-9) best = { r, sp, cost };
|
|
25731
|
+
}
|
|
25732
|
+
return best && (best.r !== 100 || best.sp !== 0) ? { ratio: best.r, spacing: best.sp } : null;
|
|
25733
|
+
}
|
|
25734
|
+
var LIST_BREAK = /* @__PURE__ */ new Set([..."\xB7,\u3001/"]);
|
|
25735
|
+
function fitCharBreaks(text, font, pt2, firstW, contW, minRatio = 88) {
|
|
25736
|
+
const faceClass = faceClassForGen(font);
|
|
25737
|
+
const h = pt2 * 100;
|
|
25738
|
+
const k = safety(faceClass, 0.95);
|
|
25739
|
+
if (!text.split(/ +/).some((w) => measureTextWidth(w, h, 100, { faceClass }) > contW * k)) return null;
|
|
25740
|
+
let best = null;
|
|
25741
|
+
for (const [r, sp] of [[100, 0], ...SQUEEZE.filter(([r2]) => r2 >= minRatio)]) {
|
|
25742
|
+
const amt = squeezeOf(r, sp);
|
|
25743
|
+
if (amt > PULL_MAX + 1e-9 || best && amt >= best.amt - 1e-9) continue;
|
|
25744
|
+
const wrapAt = (s) => simulateWrap(text, firstW * s, contW * s, h, r, "charAll", { faceClass, spacingPct: sp }).starts;
|
|
25745
|
+
const starts = wrapAt(1);
|
|
25746
|
+
if (wrapAt(0.99).join() !== starts.join() || wrapAt(1.01).join() !== starts.join()) continue;
|
|
25747
|
+
if (!starts.slice(1).every((s) => text[s - 1] === " " || LIST_BREAK.has(text[s - 1]))) continue;
|
|
25748
|
+
if (worstLooseness(text, starts, firstW, contW, h, r, sp, faceClass) > LOOSE_LIMIT) continue;
|
|
25749
|
+
best = { r, sp, amt };
|
|
25750
|
+
}
|
|
25751
|
+
return best ? { ratio: best.r, spacing: best.sp } : null;
|
|
25752
|
+
}
|
|
25753
|
+
|
|
25754
|
+
// src/hwpx/gen-frame-seoul.ts
|
|
25755
|
+
var frameTableId = EXTRA_TABLE_ID_BASE + 500;
|
|
25756
|
+
function resetFrameTableIds() {
|
|
25757
|
+
frameTableId = EXTRA_TABLE_ID_BASE + 500;
|
|
25758
|
+
}
|
|
25759
|
+
var BF_NONE = 1;
|
|
25760
|
+
function ftbl(rows, w, h, cols, opts = {}) {
|
|
25761
|
+
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>`;
|
|
25762
|
+
}
|
|
25763
|
+
function host(tableXml2, paraPrId, charPrId, extra = "") {
|
|
25764
|
+
return `<hp:p paraPrIDRef="${paraPrId}" styleIDRef="0"${extra}><hp:run charPrIDRef="${charPrId}">${tableXml2}</hp:run></hp:p>`;
|
|
25765
|
+
}
|
|
25766
|
+
function scale(cols, w) {
|
|
25767
|
+
const sum = cols.reduce((a, b) => a + b, 0);
|
|
25768
|
+
const out = cols.map((c) => Math.round(c / sum * w));
|
|
25769
|
+
out[out.length - 1] += w - out.reduce((a, b) => a + b, 0);
|
|
25770
|
+
return out;
|
|
25771
|
+
}
|
|
25772
|
+
function spacedOrgName(org) {
|
|
25773
|
+
const t = org.trim();
|
|
25774
|
+
if (/\s/.test(t) || [...t].length > 6) return t;
|
|
25775
|
+
return [...t].join(" ");
|
|
25776
|
+
}
|
|
25777
|
+
function buildDocHeadTable(h, ctx) {
|
|
25778
|
+
const { reg, bf, frame } = ctx;
|
|
25779
|
+
const w = ctx.W - 366;
|
|
25780
|
+
const [cLabel, cValue, cPad] = scale([6949, 35068, 7223], w);
|
|
25781
|
+
const center = reg.para({ align: "CENTER", lineSp: 160 });
|
|
25782
|
+
const left = reg.para({ align: "LEFT", lineSp: 160 });
|
|
25783
|
+
const c10 = reg.char({ font: frame.font, pt: 10 });
|
|
25784
|
+
const c10b = reg.char({ font: frame.font, pt: 10, bold: true });
|
|
25785
|
+
const c20b = reg.char({ font: frame.font, pt: 20, bold: true });
|
|
25786
|
+
const c14 = reg.char({ font: frame.font, pt: 14 });
|
|
25787
|
+
const bottomLine = bf.get({ t: "none", b: "thin", l: "none", r: "none" });
|
|
25788
|
+
const rows = [];
|
|
25789
|
+
let r = 0;
|
|
25790
|
+
if (h.slogan) {
|
|
25791
|
+
rows.push(tc({ bf: BF_NONE, row: r++, col: 0, w, h: 2416, colSpan: 3, paras: para(h.slogan, center, c10) }));
|
|
25792
|
+
}
|
|
25793
|
+
rows.push(
|
|
25794
|
+
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) })
|
|
25795
|
+
);
|
|
25796
|
+
r++;
|
|
25797
|
+
rows.push(tc({ bf: BF_NONE, row: r++, col: 0, w, h: 1563, colSpan: 3, paras: para("", left, c10b) }));
|
|
25798
|
+
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) });
|
|
25799
|
+
rows.push(labeled(" \uC218\uC2E0 ", h.to ?? "", 2097, BF_NONE));
|
|
25800
|
+
r++;
|
|
25801
|
+
rows.push(labeled("(\uACBD\uC720)", h.via ?? "", 2380, BF_NONE));
|
|
25802
|
+
r++;
|
|
25803
|
+
rows.push(labeled(" \uC81C\uBAA9 ", h.title ?? "", 2248, bottomLine));
|
|
25804
|
+
r++;
|
|
25805
|
+
const height = rows.length === 6 ? 16312 : 13896;
|
|
25806
|
+
return host(ftbl(rows, w, height, 3, { bottomGap: 852 }), reg.para({ align: "LEFT", lineSp: 100 }), c10);
|
|
25807
|
+
}
|
|
25808
|
+
function splitTitleName(s) {
|
|
25809
|
+
const t = s.trim();
|
|
25810
|
+
const i = t.lastIndexOf(" ");
|
|
25811
|
+
return i > 0 ? { title: t.slice(0, i).trim(), name: t.slice(i + 1).trim() } : { title: "", name: t };
|
|
25812
|
+
}
|
|
25813
|
+
function buildDocFootTable(f, ctx, internal) {
|
|
25814
|
+
const { reg, bf, frame } = ctx;
|
|
25815
|
+
const w = ctx.W - 86;
|
|
25816
|
+
const GRID = 48;
|
|
25817
|
+
const unit = Math.floor(w / GRID);
|
|
25818
|
+
const c10 = reg.char({ font: frame.font, pt: 10 });
|
|
25819
|
+
const c11b = reg.char({ font: frame.font, pt: 11, bold: true });
|
|
25820
|
+
const c9b = reg.char({ font: frame.font, pt: 9, bold: true });
|
|
25821
|
+
const c18b = reg.char({ font: frame.font, pt: 18, bold: true });
|
|
25822
|
+
const c3 = reg.char({ font: frame.font, pt: 3 });
|
|
25823
|
+
const center = reg.para({ align: "CENTER", lineSp: 100 });
|
|
25824
|
+
const left = reg.para({ align: "LEFT", lineSp: 100 });
|
|
25825
|
+
const gray = bf.get({ t: "none", b: "none", l: "none", r: "none", fill: "#CCCCCC" });
|
|
25826
|
+
const spans = (widths) => {
|
|
25827
|
+
const sum = widths.reduce((a, b) => a + b, 0);
|
|
25828
|
+
const out = widths.map((x) => Math.max(1, Math.round(x / sum * GRID)));
|
|
25829
|
+
let diff = GRID - out.reduce((a, b) => a + b, 0);
|
|
25830
|
+
for (let i = out.length - 1; diff !== 0 && i >= 0; i = (i - 1 + out.length) % out.length) {
|
|
25831
|
+
if (diff > 0) {
|
|
25832
|
+
out[i]++;
|
|
25833
|
+
diff--;
|
|
25834
|
+
} else if (out[i] > 1) {
|
|
25835
|
+
out[i]--;
|
|
25836
|
+
diff++;
|
|
25837
|
+
}
|
|
25838
|
+
}
|
|
25839
|
+
return out;
|
|
25840
|
+
};
|
|
25841
|
+
const rows = [];
|
|
25842
|
+
let r = 0;
|
|
25843
|
+
const row = (h, specs) => {
|
|
25844
|
+
let col = 0;
|
|
25845
|
+
let xml = "";
|
|
25846
|
+
specs.forEach((sp, i) => {
|
|
25847
|
+
const last = i === specs.length - 1;
|
|
25848
|
+
const span2 = last ? GRID - col : sp.span;
|
|
25849
|
+
const cw = last ? w - col * unit : span2 * unit;
|
|
25850
|
+
xml += tc({ bf: sp.bf ?? BF_NONE, row: r, col, w: cw, h, colSpan: span2, rowSpan: sp.rowSpan, vAlign: sp.vAlign, paras: para(sp.text, sp.pp ?? left, sp.cp ?? c10) });
|
|
25851
|
+
col += span2;
|
|
25852
|
+
});
|
|
25853
|
+
rows.push(xml);
|
|
25854
|
+
r++;
|
|
25855
|
+
};
|
|
25856
|
+
{
|
|
25857
|
+
const [a, b, c] = spans([7981, 32099, 9440]);
|
|
25858
|
+
row(4042, [{ span: a, text: "" }, { span: b, text: internal ? "" : f.sender ?? "", cp: c18b, pp: center }, { span: c, text: "" }]);
|
|
25859
|
+
}
|
|
25860
|
+
if (f.recipients) {
|
|
25861
|
+
const [a] = spans([4843, 44677]);
|
|
25862
|
+
row(2880, [{ span: a, text: "\uC218\uC2E0\uC790", vAlign: "TOP" }, { span: GRID - a, text: f.recipients, vAlign: "TOP" }]);
|
|
25863
|
+
} else {
|
|
25864
|
+
row(1280, [{ span: GRID, text: "" }]);
|
|
25865
|
+
}
|
|
25866
|
+
row(1146, [{ span: GRID, text: "", cp: c3, pp: center, bf: gray }]);
|
|
25867
|
+
{
|
|
25868
|
+
const n = Math.max(f.approvers.length, 1);
|
|
25869
|
+
const unitW = [6909, 4625, 983];
|
|
25870
|
+
const titleW = (t) => Math.max(unitW[0], Math.round(measureTextWidth(t, 1e3, 100, { faceClass: "fixedPitch" })) + 280 + unit + 300);
|
|
25871
|
+
const widths = [];
|
|
25872
|
+
for (let i = 0; i < n; i++) {
|
|
25873
|
+
widths.push(titleW(f.approvers[i]?.title ?? ""), unitW[1]);
|
|
25874
|
+
if (i < n - 1) widths.push(unitW[2]);
|
|
25875
|
+
}
|
|
25876
|
+
const used = widths.reduce((a, b) => a + b, 0);
|
|
25877
|
+
if (used < w) widths.push(w - used);
|
|
25878
|
+
const sp = spans(widths);
|
|
25879
|
+
const top = [], bottom = [];
|
|
25880
|
+
let k = 0;
|
|
25881
|
+
for (let i = 0; i < n; i++) {
|
|
25882
|
+
const ap = f.approvers[i] ?? { title: "", name: "" };
|
|
25883
|
+
top.push({ span: sp[k++], text: ap.title, pp: center, rowSpan: 2 });
|
|
25884
|
+
const signSpan = sp[k++];
|
|
25885
|
+
top.push({ span: signSpan, text: ap.date ?? "", cp: c9b, pp: center });
|
|
25886
|
+
bottom.push({ span: signSpan, text: ap.name, cp: c11b, pp: center });
|
|
25887
|
+
if (i < n - 1) top.push({ span: sp[k++], text: "", rowSpan: 2 });
|
|
25888
|
+
}
|
|
25889
|
+
if (k < sp.length) top.push({ span: sp[k], text: "", rowSpan: 2 });
|
|
25890
|
+
let col = 0, xmlTop = "", xmlBottom = "";
|
|
25891
|
+
let bi = 0;
|
|
25892
|
+
top.forEach((s, i) => {
|
|
25893
|
+
const last = i === top.length - 1;
|
|
25894
|
+
const span2 = last ? GRID - col : s.span;
|
|
25895
|
+
const cw = last ? w - col * unit : span2 * unit;
|
|
25896
|
+
xmlTop += tc({ bf: BF_NONE, row: r, col, w: cw, h: s.rowSpan ? 2198 : 1100, colSpan: span2, rowSpan: s.rowSpan, paras: para(s.text, s.pp ?? left, s.cp ?? c10) });
|
|
25897
|
+
if (!s.rowSpan) {
|
|
25898
|
+
const b = bottom[bi++];
|
|
25899
|
+
xmlBottom += tc({ bf: BF_NONE, row: r + 1, col, w: cw, h: 1098, colSpan: span2, paras: para(b.text, b.pp ?? center, b.cp ?? c11b) });
|
|
25900
|
+
}
|
|
25901
|
+
col += span2;
|
|
25902
|
+
});
|
|
25903
|
+
rows.push(xmlTop, xmlBottom);
|
|
25904
|
+
r += 2;
|
|
25905
|
+
}
|
|
25906
|
+
{
|
|
25907
|
+
const [a] = spans([4455, 45065]);
|
|
25908
|
+
const coop = f.cooperators.map((c) => `${c.title} ${c.name}`.trim()).join(" ");
|
|
25909
|
+
row(2700, [{ span: a, text: "\uD611\uC870\uC790" }, { span: GRID - a, text: coop }]);
|
|
25910
|
+
}
|
|
25911
|
+
row(1400, [{ span: GRID, text: "" }]);
|
|
25912
|
+
{
|
|
25913
|
+
const [a, b, c] = spans([3340, 21532, 3301, 21347]);
|
|
25914
|
+
row(2280, [{ span: a, text: "\uC2DC\uD589" }, { span: b, text: f.docNum ?? "" }, { span: c, text: "\uC811\uC218" }, { span: 0, text: f.receive ?? "" }]);
|
|
25915
|
+
}
|
|
25916
|
+
{
|
|
25917
|
+
const [a, b, c, d] = spans([1597, 3902, 21934, 761, 21326]);
|
|
25918
|
+
row(1280, [{ span: a, text: "\uC6B0" }, { span: b, text: f.zip ?? "" }, { span: c, text: f.address ?? "" }, { span: d, text: "/" }, { span: 0, text: f.site ?? "" }]);
|
|
25919
|
+
}
|
|
25920
|
+
{
|
|
25921
|
+
const sp = spans([2750, 8006, 3196, 8311, 899, 14327, 792, 11239]);
|
|
25922
|
+
const vals = ["\uC804\uD654", f.phone ?? "", "/\uC804\uC1A1", f.fax ?? "", "/", f.email ?? "", "/", f.disclosure ?? ""];
|
|
25923
|
+
row(2e3, vals.map((v, i) => ({ span: sp[i], text: v })));
|
|
25924
|
+
}
|
|
25925
|
+
row(1e3, [{ span: GRID, text: "" }]);
|
|
25926
|
+
const height = rows.length * 1500;
|
|
25927
|
+
return host(ftbl(rows, w, height, GRID), reg.para({ align: "LEFT", lineSp: 100 }), c10);
|
|
25928
|
+
}
|
|
25929
|
+
function buildReportTitleTable(title, contact, ctx) {
|
|
25930
|
+
const { reg, bf, frame } = ctx;
|
|
25931
|
+
const w = ctx.W - 566;
|
|
25932
|
+
const fit = fitOneLine(title, frame.titleFont, frame.titlePt, w - 2 * 140 - 400, 20);
|
|
25933
|
+
const cTitle = reg.char({ font: frame.titleFont, pt: fit.pt, bold: true, ratio: fit.ratio, spacing: fit.spacing });
|
|
25934
|
+
const center = reg.para({ align: "CENTER", lineSp: 130 });
|
|
25935
|
+
const rows = [];
|
|
25936
|
+
const titleBf = bf.get({ t: "thick", b: contact ? "mid" : "thick", l: "none", r: "none" });
|
|
25937
|
+
rows.push(tc({ bf: titleBf, row: 0, col: 0, w, h: 4084, paras: para(title, center, cTitle), name: "__kordoc_h1" }));
|
|
25938
|
+
let h = 4084;
|
|
25939
|
+
if (contact) {
|
|
25940
|
+
const cContact = reg.char({ font: frame.contactFont, pt: frame.contactPt });
|
|
25941
|
+
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) }));
|
|
25942
|
+
h += 1943;
|
|
25943
|
+
}
|
|
25944
|
+
return { xml: host(ftbl(rows, w, h, 1, { bottomGap: 600 }), reg.para({ align: "CENTER", lineSp: 100 }), cTitle), overflow: fit.overflow };
|
|
25945
|
+
}
|
|
25946
|
+
var CHAPTER_BAND_DEFAULT = { color: "#003366", textColor: "#FFFFFF", numFont: "HY\uD5E4\uB4DC\uB77C\uC778M", numPt: 17 };
|
|
25947
|
+
function buildChapterBand(roman, title, ctx, spec, before2) {
|
|
25948
|
+
const { reg, bf } = ctx;
|
|
25949
|
+
const w = ctx.W;
|
|
25950
|
+
const numW = 3100, gap = 400, h = 2600;
|
|
25951
|
+
const titleW = w - numW - gap;
|
|
25952
|
+
const fit = fitOneLine(title, spec.titleFont, spec.titlePt, titleW - 700, spec.titlePt - 3);
|
|
25953
|
+
const numBf = bf.get({ t: "none", b: "none", l: "none", r: "none", fill: spec.color });
|
|
25954
|
+
const gapBf = bf.get({ t: "none", b: "none", l: "none", r: "none" });
|
|
25955
|
+
const titleBf = bf.get({ t: "none", b: "thick", l: "none", r: "none" });
|
|
25956
|
+
const cNum = reg.char({ font: spec.numFont, pt: spec.numPt, bold: true, color: spec.textColor });
|
|
25957
|
+
const cTitle = reg.char({ font: spec.titleFont, pt: fit.pt, bold: true, ratio: fit.ratio, spacing: fit.spacing });
|
|
25958
|
+
const center = reg.para({ align: "CENTER", lineSp: 130 });
|
|
25959
|
+
const left = reg.para({ align: "LEFT", lineSp: 130, left: 300 });
|
|
25960
|
+
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" });
|
|
25961
|
+
return { xml: host(ftbl([row], w, h, 3, { bottomGap: 1e3 }), reg.para({ align: "LEFT", lineSp: 100, before: before2, keepWithNext: true }), cTitle), overflow: fit.overflow };
|
|
25962
|
+
}
|
|
25963
|
+
var MM05 = ["0.5 mm", "#000000"];
|
|
25964
|
+
var BANGCHIM_BLUE = "#0000FF";
|
|
25965
|
+
function buildBangchimTitleTable(title, subtitle, contact, ctx) {
|
|
25966
|
+
const { reg, bf, frame } = ctx;
|
|
25967
|
+
const w = ctx.W - 566;
|
|
25968
|
+
const fit = fitOneLine(title, frame.titleFont, frame.titlePt, w - 2 * 141 - 400, 20);
|
|
25969
|
+
const cTitle = reg.char({ font: frame.titleFont, pt: fit.pt, bold: true, ratio: fit.ratio, spacing: fit.spacing });
|
|
25970
|
+
const center = reg.para({ align: "CENTER", lineSp: 100 });
|
|
25971
|
+
let paras = para(title, center, cTitle);
|
|
25972
|
+
let h = Math.round(fit.pt * 100) + 1400;
|
|
25973
|
+
if (subtitle) {
|
|
25974
|
+
paras += para(subtitle, center, reg.char({ font: frame.titleFont, pt: 18, bold: true, color: BANGCHIM_BLUE }));
|
|
25975
|
+
h += 1800;
|
|
25976
|
+
}
|
|
25977
|
+
const rows = [tc({ bf: bf.get({ t: "thick", b: contact ? "mid" : "thick", l: "none", r: "none" }), row: 0, col: 0, w, h, paras, name: "__kordoc_h1" })];
|
|
25978
|
+
if (contact) {
|
|
25979
|
+
const p = reg.para({ align: "DISTRIBUTE", lineSp: 160, left: 300 });
|
|
25980
|
+
rows.push(tc({ bf: bf.get({ t: "mid", b: "thick", l: "none", r: "none" }), row: 1, col: 0, w, h: 1482, paras: para(contact, p, reg.char({ font: frame.contactFont, pt: frame.contactPt })) }));
|
|
25981
|
+
h += 1482;
|
|
25982
|
+
}
|
|
25983
|
+
return { xml: host(ftbl(rows, w, h, 1, { bottomGap: 600 }), reg.para({ align: "CENTER", lineSp: 100 }), cTitle), overflow: fit.overflow };
|
|
25984
|
+
}
|
|
25985
|
+
function buildBangchimSummary(text, ctx) {
|
|
25986
|
+
const { reg, bf, frame } = ctx;
|
|
25987
|
+
const w = ctx.W - 566;
|
|
25988
|
+
const pad = 566, left = 500;
|
|
25989
|
+
const avail = w - 2 * pad - left;
|
|
25990
|
+
const p = reg.para({ align: "DISTRIBUTE", lineSp: 140, left });
|
|
25991
|
+
const c = reg.char({ font: frame.summaryFont, pt: frame.summaryPt, bold: true });
|
|
25992
|
+
const bodies = text.split("\n").map((l) => l.trim().replace(/^[□■○ㅇ◦●\-–ㆍ·•]\s*/u, "")).filter(Boolean);
|
|
25993
|
+
let lines = 0;
|
|
25994
|
+
for (const b of bodies) lines += simulateWrap(b, avail, avail, frame.summaryPt * 100, 100, "keep", { faceClass: faceClassForGen(frame.summaryFont) }).lines;
|
|
25995
|
+
const h = lines * Math.round(frame.summaryPt * 100 * 1.4) + 2 * pad;
|
|
25996
|
+
const row = tc({ bf: bf.get({ t: "thick", b: "thick", l: "thick", r: "thick", fill: frame.summaryFill }), row: 0, col: 0, w, h, paras: bodies.map((b) => para(b, p, c)).join(""), name: "__kordoc_summary" }).replace('<hp:cellMargin left="141" right="141" top="141" bottom="141"/>', `<hp:cellMargin left="${pad}" right="${pad}" top="${pad}" bottom="${pad}"/>`);
|
|
25997
|
+
return { xml: host(ftbl([row], w, h, 1, { bottomGap: 600 }), reg.para({ align: "CENTER", lineSp: 100 }), c), lines };
|
|
25998
|
+
}
|
|
25999
|
+
function buildSquareChapter(label, title, ctx, st, before2) {
|
|
26000
|
+
const { reg, bf } = ctx;
|
|
26001
|
+
const numW = 3223, gapW = 1414, h = 3014;
|
|
26002
|
+
const maxTitle = ctx.W - numW - gapW;
|
|
26003
|
+
const fit = fitOneLine(title, st.font, st.pt, maxTitle - 1200, st.pt - 3);
|
|
26004
|
+
const textW2 = measureTextWidth(title, Math.round(fit.pt * 100), fit.ratio, { faceClass: faceClassForGen(st.font), spacingPct: fit.spacing });
|
|
26005
|
+
const titleW = Math.min(maxTitle, Math.max(14e3, Math.round(textW2) + 1200));
|
|
26006
|
+
const cNum = /^[Ⅰ-Ⅻ]+$/u.test(label) ? reg.char({ font: "HY\uACAC\uBA85\uC870", pt: 19, bold: true }) : reg.char({ font: st.font, pt: st.pt });
|
|
26007
|
+
const cTitle = reg.char({ font: st.font, pt: fit.pt, ratio: fit.ratio, spacing: fit.spacing });
|
|
26008
|
+
const center = reg.para({ align: "CENTER", lineSp: 150, left: 300 });
|
|
26009
|
+
const row = tc({ bf: bf.get({ t: MM05, b: MM05, l: MM05, r: MM05 }), row: 0, col: 0, w: numW, h, paras: para(label, center, cNum) }) + tc({ bf: bf.get({ t: "none", b: "none", l: MM05, r: "none" }), row: 0, col: 1, w: gapW, h, paras: para("", reg.para({ align: "JUSTIFY", lineSp: 160 }), cTitle) }) + tc({ bf: bf.get({ t: MM05, b: MM05, l: "none", r: "none" }), row: 0, col: 2, w: titleW, h, paras: para(title, center, cTitle), name: "__kordoc_h2" });
|
|
26010
|
+
const w = numW + gapW + titleW;
|
|
26011
|
+
return { xml: host(ftbl([row], w, h, 3, { bottomGap: 600 }), reg.para({ align: "LEFT", lineSp: 100, before: before2, keepWithNext: true }), cTitle), overflow: fit.overflow };
|
|
26012
|
+
}
|
|
26013
|
+
function buildBangchimSectionBand(n, title, ctx, before2) {
|
|
26014
|
+
const { reg, bf } = ctx;
|
|
26015
|
+
const w = ctx.W - 720, numW = 3400, gapW = 565, h = 2551;
|
|
26016
|
+
const titleW = w - numW - gapW;
|
|
26017
|
+
const fit = fitOneLine(title, "HY\uACAC\uACE0\uB515", 17, titleW - 700, 14);
|
|
26018
|
+
const cNum = reg.char({ font: "HY\uACAC\uACE0\uB515", pt: 16, color: "#FFFFFF" });
|
|
26019
|
+
const cTitle = reg.char({ font: "HY\uACAC\uACE0\uB515", pt: fit.pt, ratio: fit.ratio, spacing: fit.spacing });
|
|
26020
|
+
const row = tc({ bf: bf.get({ t: "none", b: ["0.1 mm", "#CCCCCC"], l: "none", r: "none", fill: "#437FC1" }), row: 0, col: 0, w: numW, h, paras: para(String(n), reg.para({ align: "CENTER", lineSp: 160 }), cNum) }) + tc({ bf: bf.get({ t: "none", b: "none", l: "none", r: "none" }), row: 0, col: 1, w: gapW, h, paras: para("", reg.para({ align: "CENTER", lineSp: 160 }), cNum) }) + tc({ bf: bf.get({ t: "none", b: "none", l: "none", r: "none", fill: "#E5E5E5" }), row: 0, col: 2, w: titleW, h, paras: para(` ${title}`, reg.para({ align: "JUSTIFY", lineSp: 190 }), cTitle), name: "__kordoc_h3" });
|
|
26021
|
+
return { xml: host(ftbl([row], w, h, 3, { bottomGap: 600 }), reg.para({ align: "LEFT", lineSp: 100, before: before2, keepWithNext: true }), cTitle), overflow: fit.overflow };
|
|
26022
|
+
}
|
|
26023
|
+
function buildBangchimSubhead(n, title, ctx, before2) {
|
|
26024
|
+
const { reg } = ctx;
|
|
26025
|
+
const mark = n >= 1 && n <= 10 ? String.fromCodePoint(10101 + n) : `${n}.`;
|
|
26026
|
+
const p = reg.para({ align: "JUSTIFY", lineSp: 200, before: before2, keepWithNext: true });
|
|
26027
|
+
return `<hp:p paraPrIDRef="${p}" styleIDRef="0"><hp:run charPrIDRef="${reg.char({ font: "HY\uACAC\uACE0\uB515", pt: 16, bold: true })}"><hp:t>${escapeXml(mark)} </hp:t></hp:run><hp:run charPrIDRef="${reg.char({ font: "HY\uACAC\uACE0\uB515", pt: 16 })}"><hp:t>${escapeXml(title)}</hp:t></hp:run></hp:p>`;
|
|
26028
|
+
}
|
|
26029
|
+
var SUMMARY_PAD = 1e3;
|
|
26030
|
+
function buildSummaryBox(text, ctx) {
|
|
26031
|
+
const { reg, bf, frame } = ctx;
|
|
26032
|
+
const w = ctx.W - 566;
|
|
26033
|
+
const avail = w - 280 - SUMMARY_PAD * 2;
|
|
26034
|
+
const p = reg.para({ align: "JUSTIFY", lineSp: 160, left: SUMMARY_PAD, right: SUMMARY_PAD });
|
|
26035
|
+
const bodies = text.split("\n").map((l) => l.trim().replace(/^[□■○ㅇ◦●\-–ㆍ·•]\s*/u, "")).filter(Boolean);
|
|
26036
|
+
let lines = 0;
|
|
26037
|
+
const paras = bodies.map((b) => {
|
|
26038
|
+
const f = fitParagraph(b, frame.summaryFont, frame.summaryPt, avail, avail);
|
|
26039
|
+
const c = reg.char({ font: frame.summaryFont, pt: frame.summaryPt, bold: true, ratio: f?.ratio ?? 100, spacing: f?.spacing ?? 0 });
|
|
26040
|
+
lines += simulateWrap(b, avail * 0.995, avail * 0.995, frame.summaryPt * 100, f?.ratio ?? 100, "keep", { faceClass: faceClassForGen(frame.summaryFont), spacingPct: f?.spacing ?? 0 }).lines;
|
|
26041
|
+
return para(b, p, c);
|
|
26042
|
+
});
|
|
26043
|
+
const h = lines * Math.round(frame.summaryPt * 100 * 1.6) + 280 + 600;
|
|
26044
|
+
const box = bf.get({ t: "thick", b: "thick", l: "thick", r: "thick", fill: frame.summaryFill });
|
|
26045
|
+
const row = tc({ bf: box, row: 0, col: 0, w, h, paras: paras.join(""), name: "__kordoc_summary" });
|
|
26046
|
+
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 };
|
|
26047
|
+
}
|
|
26048
|
+
function buildApprovalSeoul(labels, names, ctx) {
|
|
26049
|
+
const { reg, bf } = ctx;
|
|
26050
|
+
const n = labels.length;
|
|
26051
|
+
const colW = Math.min(8600, Math.floor(ctx.W * 0.55 / n));
|
|
26052
|
+
const w = colW * n;
|
|
26053
|
+
const c12 = reg.char({ font: "\uD55C\uCEF4\uB3CB\uC6C0", pt: 12 });
|
|
26054
|
+
const c12b = reg.char({ font: "\uD55C\uCEF4\uB3CB\uC6C0", pt: 12, bold: true });
|
|
26055
|
+
const center = reg.para({ align: "CENTER", lineSp: 100 });
|
|
26056
|
+
const edge = (row, col) => bf.get({
|
|
26057
|
+
t: row === 0 ? "thick" : "thin",
|
|
26058
|
+
b: row === 1 ? "thick" : "thin",
|
|
26059
|
+
l: col === 0 ? "thick" : "thin",
|
|
26060
|
+
r: col === n - 1 ? "thick" : "thin"
|
|
26061
|
+
});
|
|
26062
|
+
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("");
|
|
26063
|
+
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("");
|
|
26064
|
+
return host(ftbl([top, sign], w, 5965, n, { bottomGap: 600 }), reg.para({ align: "RIGHT", lineSp: 100 }), c12);
|
|
26065
|
+
}
|
|
26066
|
+
function buildReportCover(inp, ctx) {
|
|
26067
|
+
const { reg, bf, frame } = ctx;
|
|
26068
|
+
const out = [];
|
|
26069
|
+
const cv = frame.cover;
|
|
26070
|
+
const c12 = reg.char({ font: cv?.infoFont ?? "\uD55C\uCEF4\uB3CB\uC6C0", pt: cv?.infoValuePt ?? 12 });
|
|
26071
|
+
const c12b = reg.char({ font: cv?.infoFont ?? "\uD55C\uCEF4\uB3CB\uC6C0", pt: cv?.infoLabelPt ?? 12, bold: true });
|
|
26072
|
+
const cAppr = cv ? reg.char({ font: cv.infoFont, pt: cv.approvalPt }) : c12;
|
|
26073
|
+
const center = reg.para({ align: "CENTER", lineSp: 100 });
|
|
26074
|
+
const centerAt = (k) => cv ? reg.para({ align: "CENTER", lineSp: cv.lineSp[k] }) : center;
|
|
26075
|
+
const blank2 = (pt2 = 15) => para("", reg.para({ align: "LEFT", lineSp: 160 }), reg.char({ font: "\uD55C\uCEF4\uB3CB\uC6C0", pt: pt2 }));
|
|
26076
|
+
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 ?? ""]];
|
|
26077
|
+
const infoW = Math.round(ctx.W * 0.36);
|
|
26078
|
+
const [lw, vw] = scale([4200, 12800], infoW);
|
|
26079
|
+
const infoXml = ftbl(infoRows.map(
|
|
26080
|
+
([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, centerAt("info"), 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, centerAt("info"), c12) })
|
|
26081
|
+
), infoW, 1850 * 4, 2);
|
|
26082
|
+
let line = infoXml;
|
|
26083
|
+
if (inp.approval && inp.approval.length) {
|
|
26084
|
+
const n = inp.approval.length;
|
|
26085
|
+
const colW = Math.min(7600, Math.floor(ctx.W * 0.5 / n));
|
|
26086
|
+
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" });
|
|
26087
|
+
const top = inp.approval.map((l, c) => tc({ bf: edge(0, c), row: 0, col: c, w: colW, h: 1765, paras: para(l, centerAt("approval"), cAppr) })).join("");
|
|
26088
|
+
const sign = inp.approval.map((_, c) => tc({ bf: edge(1, c), row: 1, col: c, w: colW, h: 3600, paras: para("", center, c12b) })).join("");
|
|
26089
|
+
line += `</hp:run><hp:run charPrIDRef="${c12}"><hp:t> </hp:t></hp:run><hp:run charPrIDRef="${c12}">` + ftbl([top, sign], colW * n, 5365, n);
|
|
26090
|
+
}
|
|
26091
|
+
out.push(`<hp:p paraPrIDRef="${reg.para({ align: "DISTRIBUTE", lineSp: 100 })}" styleIDRef="0"><hp:run charPrIDRef="${c12}">${line}</hp:run></hp:p>`);
|
|
26092
|
+
for (let i = 0; i < 4; i++) out.push(blank2(20));
|
|
26093
|
+
const w = ctx.W - 1200;
|
|
26094
|
+
const bar = bf.get({ t: "none", b: "none", l: "none", r: "none", fill: cv ? { gradient: [...cv.bar.gradient], type: "LINEAR", angle: 90 } : "#1F2FD6" });
|
|
26095
|
+
const barH = cv?.bar.h ?? 200;
|
|
26096
|
+
const fit = fitOneLine(inp.title, frame.titleFont, 27, w - 800, 22);
|
|
26097
|
+
const cT = reg.char({ font: frame.titleFont, pt: fit.pt, bold: true, ratio: fit.ratio, spacing: fit.spacing });
|
|
26098
|
+
const c1 = reg.char({ font: "\uD55C\uCEF4\uB3CB\uC6C0", pt: 1 });
|
|
26099
|
+
const barP = reg.para({ align: "CENTER", lineSp: 70 });
|
|
26100
|
+
const rows = [
|
|
26101
|
+
tc({ bf: bar, row: 0, col: 0, w, h: barH, paras: para("", barP, c1) }),
|
|
26102
|
+
tc({ bf: BF_NONE, row: 1, col: 0, w, h: 6e3, paras: para(inp.title, centerAt("title"), cT), name: "__kordoc_skip" }),
|
|
26103
|
+
tc({ bf: bar, row: 2, col: 0, w, h: barH, paras: para("", barP, c1) })
|
|
26104
|
+
];
|
|
26105
|
+
out.push(host(ftbl(rows, w, 6e3 + 2 * barH, 1), reg.para({ align: "CENTER", lineSp: 100 }), cT));
|
|
26106
|
+
for (let i = 0; i < 2; i++) out.push(blank2(20));
|
|
26107
|
+
out.push(para(inp.date, centerAt("date"), reg.char(cv ? cv.date : { font: frame.titleFont, pt: 22, bold: true })));
|
|
26108
|
+
for (let i = 0; i < 8; i++) out.push(blank2(20));
|
|
26109
|
+
if (inp.org) out.push(para(spacedOrgName(inp.org), centerAt("org"), reg.char(cv ? cv.org : { font: frame.titleFont, pt: 24, bold: true })));
|
|
26110
|
+
if (inp.dept) out.push(para(`(${inp.dept.replace(/^\(|\)$/g, "")})`, centerAt("dept"), reg.char(cv ? cv.dept : { font: "\uD55C\uCEF4\uB3CB\uC6C0", pt: 22, bold: true })));
|
|
26111
|
+
return out;
|
|
26112
|
+
}
|
|
26113
|
+
|
|
26114
|
+
// src/hwpx/gen-frame-seoul-front.ts
|
|
26115
|
+
var FONT = "\uB9D1\uC740 \uACE0\uB515";
|
|
26116
|
+
var ORANGE = "#FF9D19";
|
|
26117
|
+
var BLUE = "#0000FF";
|
|
26118
|
+
var O = (t) => [t, 4, 100, ORANGE];
|
|
26119
|
+
var HEAD1 = (gap = 4) => [[" ", 4], O("\u25C6"), [" ", gap]];
|
|
26120
|
+
var GROUPS = [
|
|
26121
|
+
{
|
|
26122
|
+
title: "1. \uC77C\uBC18\uC0AC\uD56D",
|
|
26123
|
+
h: 2553,
|
|
26124
|
+
cats: [{
|
|
26125
|
+
label: "\uC815\uCC45\uC77C\uBC18",
|
|
26126
|
+
spacing: 0,
|
|
26127
|
+
items: [
|
|
26128
|
+
{ h: 3914, indent: -2076, lineSp: 150, paras: [[...HEAD1(3), ["\uC81C\uBC18 \uBC95\uADDC\uC640 \uC2E4\uD0DC, \uC2E4\uC81C \uD604\uC7A5\uC758 \uC758\uACAC \uB4F1\uC744 \uAC80\uD1A0\uD558\uC600\uC2B5\uB2C8\uAE4C?", -5, 85], ["(\uBC95\uB839, \uADDC\uCE59, \uD1B5\uACC4\xB7\uBE45\uB370\uC774\uD130, \uC2DC\uBBFC\uC758\uACAC \uB4F1)", 1, 85]]] },
|
|
26129
|
+
{ h: 5031, indent: -2080, lineSp: 160, paras: [[...HEAD1(), ["\uC815\uCC45(\uC0AC\uC5C5) \uC9D1\uD589\uC758 \uC9C1\xB7\uAC04\uC811\uC801 \uC601\uD5A5 \uBC0F \uD6A8\uACFC\uC131\uC744 \uBD84\uC11D\uD558\uC600\uC2B5\uB2C8\uAE4C?(\uAC08\uB4F1, \uC57D\uC790, \uC77C\uC790\uB9AC, \uC548\uC804, \uD0C4\uC18C \uAC10\uCD95 \uB4F1)", -1, 95]], [[" \u203B \uC57D\uC790 : \uD558\uB2E8 \uC138\uBD80 \uAC80\uD1A0\uD56D\uBAA9 \uC810\uAC80", -13, 100, BLUE, 9]]] },
|
|
26130
|
+
{ h: 3914, indent: -2080, lineSp: 150, paras: [[...HEAD1(), ["\uC815\uCC45\xB7\uACC4\uD68D\xB7\uC804\uC2DC\uBB3C\xB7\uD64D\uBCF4\uBB3C \uB4F1\uC774 \uC5ED\uC0AC\uC801 \uC0AC\uC2E4\uC5D0 \uBD80\uD569\uD558\uB294\uC9C0 \uAC80\uD1A0\uD558\uC600\uC2B5\uB2C8\uAE4C?", 1, 90]]] },
|
|
26131
|
+
{ h: 2431, indent: 0, lineSp: 150, paras: [[...HEAD1(), ["\uBD88\uD544\uC694\uD55C \uC678\uAD6D\uC5B4\xB7\uC678\uB798\uC5B4 \uD45C\uD604 \uB300\uC2E0 \uBC14\uB978 \uC6B0\uB9AC\uB9D0\uC744 \uC0AC\uC6A9\uD558\uC600\uC2B5\uB2C8\uAE4C?", -2, 93]]] },
|
|
26132
|
+
{ h: 2431, indent: 0, lineSp: 150, paras: [[...HEAD1(), ["\uC815\uCC45\xB7\uACC4\uD68D \uB4F1\uC758 \uC9C0\uC18D\uAC00\uB2A5\uC131\uC744 \uAC80\uD1A0\uD558\uC600\uC2B5\uB2C8\uAE4C?(\uACBD\uC81C\xB7\uC0AC\uD68C\xB7\uD658\uACBD \uB4F1)", -1, 95]]] }
|
|
26133
|
+
]
|
|
26134
|
+
}]
|
|
26135
|
+
},
|
|
26136
|
+
{
|
|
26137
|
+
title: "2. \uC57D\uC790\uC640\uC758 \uB3D9\uD589",
|
|
26138
|
+
h: 3380,
|
|
26139
|
+
cats: [
|
|
26140
|
+
{
|
|
26141
|
+
label: "\uBAA9\uC801",
|
|
26142
|
+
spacing: -6,
|
|
26143
|
+
items: [
|
|
26144
|
+
{ h: 2431, indent: -2212, lineSp: 150, paras: [[O("\u25C6 "), ["\uACBD\uC81C\uC801\uC73C\uB85C \uCDE8\uC57D\uD55C \uC57D\uC790\uB97C \uC9C0\uC6D0\uD558\uAE30 \uC704\uD55C \uC0AC\uC5C5\uC778\uAC00?", -4, 96]]] },
|
|
26145
|
+
{ h: 3731, indent: -1544, lineSp: 150, paras: [[O("\u25C6"), [" ", -8], ["\uC8FC\uAC70, \uC0DD\uACC4, \uC758\uB8CC, \uAD50\uC721, \uAE30\uD0C0 \uBD84\uC57C \uC911 \uC5B4\uB290 \uAC83\uC5D0 \uD574\uB2F9\uD558\uB294\uAC00?", -11]]], note: ["\uC8FC\uAC70/\uC0DD\uACC4/\uC758\uB8CC/\uAD50\uC721/\uAE30\uD0C0", -14, 86] },
|
|
26146
|
+
{ h: 4014, indent: -1568, indent2: -1568, lineSp: 150, paras: [[O("\u25C6"), [" ", -4], ["\uB2E8\uC21C\uC9C0\uC6D0 \uC0AC\uC5C5\uACFC \uC0AC\uB2E4\uB9AC\uC9C0\uC6D0 \uC0AC\uC5C5 \uC911 \uC5B4\uB290 \uAC83\uC5D0 \uD574\uB2F9\uD558\uB294\uAC00?", -9, 101]], [[" ", -16, 101], ["(\uB2E8\uC21C : \uC5B4\uB824\uC6C0 \uC989\uC2DC \uAC1C\uC120 / \uC0AC\uB2E4\uB9AC : \uACC4\uCE35 \uC0C1\uD5A5 \uB610\uB294 \uC548\uC815 \uC720\uB3C4)", -11, 101]]], note: ["\uB2E8\uC21C\uC9C0\uC6D0/\uC0AC\uB2E4\uB9AC\uC9C0\uC6D0"] },
|
|
26147
|
+
{ h: 3914, indent: -1560, lineSp: 150, paras: [[O("\u25C6 "), ["\uACBD\uC81C\uC801\uC73C\uB85C \uCDE8\uC57D\uD55C \uC57D\uC790\uAC00 \uC544\uB2C8\uB77C \uD558\uB354\uB77C\uB3C4", -7], [", \uB2E4\uC591\uD55C \uBD84\uC57C\uC758 \uC0AC\uD68C\uC801 \uC57D\uC790\uB97C \uBC30\uB824\uD560 \uC218 \uC788\uB294 \uC218\uB2E8\uC744 \uD3EC\uD568\uD558\uC600\uB294\uAC00?", -4]]] }
|
|
26148
|
+
]
|
|
26149
|
+
},
|
|
26150
|
+
{
|
|
26151
|
+
label: "\uACC4\uD68D\uC218\uB9BD",
|
|
26152
|
+
spacing: -6,
|
|
26153
|
+
items: [
|
|
26154
|
+
{ h: 3914, indent: -1560, lineSp: 150, paras: [[O("\u25C6 "), ["\uC0AC\uC5C5 \uB300\uC0C1\uC790\uAC00 \uC27D\uACE0 \uD3B8\uB9AC\uD558\uAC8C \uC11C\uBE44\uC2A4\uB97C \uD65C\uC6A9\uD560 \uC218 \uC788\uB3C4\uB85D \uC0AC\uC5C5\uC744 \uAD6C\uC131\uD558\uC600\uB294\uAC00?", -8]]] },
|
|
26155
|
+
{ h: 4014, indent: -1560, lineSp: 160, paras: [[O("\u25C6"), [" ", 4], ["\uCD95\uC81C\u2027\uD589\uC0AC\uACC4\uD68D\uC5D0 \uC57D\uC790 \uCC38\uC5EC \uAE30\uD68C \uD655\uB300 \uBC0F \uC57D\uC790 \uCE5C\uD654\uC801 \uD658\uACBD \uC870\uC131 \uB4F1 \uC57D\uC790\uB3D9\uD589 \uAD00\uC810\uC744 \uBC18\uC601\uD558\uC600\uB294\uAC00? ", -8], ["\u203B ", -13, 100, void 0, 9], ["\uCD95\uC81C\xB7\uD589\uC0AC\uC5D0 \uD55C\uD568", -8]]] }
|
|
26156
|
+
]
|
|
26157
|
+
},
|
|
26158
|
+
{
|
|
26159
|
+
label: "\uC9D1\uD589\xB7\uD64D\uBCF4",
|
|
26160
|
+
spacing: -6,
|
|
26161
|
+
items: [
|
|
26162
|
+
{ h: 3914, indent: -1560, lineSp: 150, paras: [[O("\u25C6 "), ["\uC9D1\uD589\uACFC\uC815\uC5D0\uC11C \uBC1C\uC0DD\uD560 \uC218 \uC788\uB294 \uC5ED\uD6A8\uACFC\uB97C \uCD5C\uC18C\uD654\uD558\uAE30 \uC704\uD55C \uB300\uC548\uC744 \uACE0\uB824\uD558\uC600\uB294\uAC00?(\u2018\uC57D\uC790\u2019 \uB099\uC778\uD6A8\uACFC, \uC5ED\uCC28\uBCC4 \uB4F1)", -5]]] },
|
|
26163
|
+
{ h: 2431, indent: -1560, lineSp: 150, paras: [[O("\u25C6 "), ["\uC0AC\uC5C5 \uB300\uC0C1\uC790\uC5D0\uAC8C \uC801\uC808\uD55C \uD64D\uBCF4 \uC218\uB2E8\uC744 \uCC44\uD0DD\uD558\uC600\uB294\uAC00?", -5]]] }
|
|
26164
|
+
]
|
|
26165
|
+
},
|
|
26166
|
+
{
|
|
26167
|
+
label: "\uD3C9\uAC00\xB7\uD658\uB958",
|
|
26168
|
+
spacing: -6,
|
|
26169
|
+
items: [
|
|
26170
|
+
{ h: 3914, indent: -1560, lineSp: 150, paras: [[O("\u25C6 "), ["\uC0AC\uC5C5\uD6A8\uACFC\uB97C \uCE21\uC815\uD560 \uC218 \uC788\uB294 \uC9C0\uD45C(\uC0B0\uCD9C, \uACFC\uC815, \uC131\uACFC \uB4F1)\uB294 \uB9C8\uB828\uD558\uC600\uB294\uAC00?", -12], ["\uC544\uB2D0 \uACBD\uC6B0, \uC131\uACFC\uB97C \uD3C9\uAC00\uD560 \uC218 \uC788\uB294 \uB300\uCCB4\xB7\uBCF4\uC644 \uC218\uB2E8\uC774 \uC874\uC7AC\uD558\uB294\uAC00? ", -5]]] }
|
|
26171
|
+
]
|
|
26172
|
+
}
|
|
26173
|
+
]
|
|
26174
|
+
}
|
|
26175
|
+
];
|
|
26176
|
+
var CHECKLIST_ITEMS = GROUPS.reduce((n, g3) => n + g3.cats.reduce((m, c) => m + c.items.length, 0), 0);
|
|
26177
|
+
var COLS = [5781, 30517, 3130, 3130, 5960];
|
|
26178
|
+
var W = 48518;
|
|
26179
|
+
var LINE = (w, c) => [w, c];
|
|
26180
|
+
var OR01 = LINE("0.1 mm", "#FFA500");
|
|
26181
|
+
var OR07 = LINE("0.7 mm", "#FFA500");
|
|
26182
|
+
var OR10 = LINE("1.0 mm", "#FFA500");
|
|
26183
|
+
var OR025 = LINE("0.25 mm", "#FFA500");
|
|
26184
|
+
var WH01 = LINE("0.1 mm", "#FFFFFF");
|
|
26185
|
+
var WH02 = LINE("0.2 mm", "#FFFFFF");
|
|
26186
|
+
var GOLD01 = LINE("0.1 mm", "#DE9E17");
|
|
26187
|
+
var GRAY = "#E5E5E5";
|
|
26188
|
+
var WHITE = "#FFFFFF";
|
|
26189
|
+
function buildSeoulChecklist(inp, ctx) {
|
|
26190
|
+
const { reg, bf } = ctx;
|
|
26191
|
+
const runXml = (r, bold = false) => `<hp:run charPrIDRef="${reg.char({ font: FONT, pt: r[4] ?? 10, bold, spacing: r[1] ?? 0, ratio: r[2] ?? 100, ...r[3] ? { color: r[3] } : {} })}"><hp:t>${escapeTextXml(r[0])}</hp:t></hp:run>`;
|
|
26192
|
+
const p = (runs, spec) => `<hp:p paraPrIDRef="${reg.para(spec)}" styleIDRef="0">${runs}</hp:p>`;
|
|
26193
|
+
const center = (lineSp = 160) => ({ align: "CENTER", lineSp });
|
|
26194
|
+
const rows = [];
|
|
26195
|
+
const banner = bf.get({ t: "none", b: "none", l: "none", r: "none", fill: { gradient: ["#FED12E", "#FC8F00"], type: "LINEAR", angle: 90 } });
|
|
26196
|
+
rows.push(tc({ bf: banner, row: 0, col: 0, colSpan: 5, w: W, h: 3814, paras: p(runXml(["\u2611 \uC0AC\uC804 \uAC80\uD1A0\uD56D\uBAA9", 0, 100, "#3A2A00", 20], true), center()) }));
|
|
26197
|
+
const guide = [["\u261E \uD574\uB2F9\uC0AC\uD56D\uC774 \uC788\uB294 \uBD80\uBD84\uC5D0 \u2018 ", 0, 95, BLUE, 12], ["\u2019 \uD45C\uC2DC\uD558\uC2DC\uAE30 \uBC14\uB78D\uB2C8\uB2E4. ", 0, 95, BLUE, 12]];
|
|
26198
|
+
const guideXml = runXml(guide[0]) + runXml(["\u25A0 ", -10, 85, void 0, 12], true) + runXml(guide[1]) + `<hp:run charPrIDRef="${reg.char({ font: FONT, pt: 12, italic: true, spacing: -2, ratio: 90, color: BLUE })}"><hp:t>(</hp:t></hp:run><hp:run charPrIDRef="${reg.char({ font: FONT, pt: 12, italic: true, spacing: -2, ratio: 90, color: "#FF0000" })}"><hp:t>\u203B \uBE44\uACE0 : \uD544\uC694\uC2DC \uAC80\uD1A0\uB0B4\uC6A9 \uAE30\uC7AC</hp:t></hp:run><hp:run charPrIDRef="${reg.char({ font: FONT, pt: 12, italic: true, spacing: -2, ratio: 90, color: BLUE })}"><hp:t>)</hp:t></hp:run>`;
|
|
26199
|
+
rows.push(tc({ bf: bf.get({ t: "none", b: OR10, l: "none", r: "none" }), row: 1, col: 0, colSpan: 5, w: W, h: 2382, paras: p(guideXml, { align: "LEFT", lineSp: 180 }) }));
|
|
26200
|
+
const hdr = [
|
|
26201
|
+
["\uAD6C \uBD84", 11, true, ORANGE, "DISTRIBUTE", bf.get({ t: "none", r: WH01, b: OR07, l: "none", fill: GRAY })],
|
|
26202
|
+
["\uC0AC\uC804 \uAC80\uD1A0\uD56D\uBAA9 \uC810\uAC80 \uC0AC\uD56D", 15, false, void 0, "CENTER", bf.get({ l: WH01, r: GOLD01, t: OR025, b: OR07, fill: WHITE })],
|
|
26203
|
+
["\uAC80\uD1A0\uC644\uB8CC", 12, false, void 0, "CENTER", bf.get({ l: GOLD01, r: GOLD01, t: "none", b: OR07, fill: WHITE })],
|
|
26204
|
+
["\uD574\uB2F9\uC5C6\uC74C", 12, false, void 0, "CENTER", bf.get({ l: GOLD01, r: GOLD01, t: "none", b: OR07, fill: WHITE })],
|
|
26205
|
+
["\uBE44 \uACE0", 12, false, void 0, "CENTER", bf.get({ l: GOLD01, r: "none", t: "none", b: OR07, fill: WHITE })]
|
|
26206
|
+
];
|
|
26207
|
+
rows.push(hdr.map(([t, pt2, bold, color, align, b], c) => tc({
|
|
26208
|
+
bf: b,
|
|
26209
|
+
row: 2,
|
|
26210
|
+
col: c,
|
|
26211
|
+
w: COLS[c],
|
|
26212
|
+
h: 3142,
|
|
26213
|
+
paras: p(runXml([t, 0, 100, color, pt2], bold), align === "DISTRIBUTE" ? { align: "DISTRIBUTE", left: 500, right: 500, lineSp: 160 } : center(c === 1 ? 160 : 110))
|
|
26214
|
+
})).join(""));
|
|
26215
|
+
let row = 3, no = 0;
|
|
26216
|
+
const last = GROUPS.at(-1).cats.at(-1).items.at(-1);
|
|
26217
|
+
for (const g3 of GROUPS) {
|
|
26218
|
+
rows.push(tc({
|
|
26219
|
+
bf: bf.get({ t: WH02, b: OR01, l: "none", r: "none", fill: GRAY }),
|
|
26220
|
+
row,
|
|
26221
|
+
col: 0,
|
|
26222
|
+
colSpan: 5,
|
|
26223
|
+
w: W,
|
|
26224
|
+
h: g3.h,
|
|
26225
|
+
paras: p(runXml([g3.title, 0, 100, void 0, 14], true), { align: "LEFT", left: 500, right: 500, lineSp: 160 })
|
|
26226
|
+
}));
|
|
26227
|
+
row++;
|
|
26228
|
+
for (const cat of g3.cats) {
|
|
26229
|
+
cat.items.forEach((it, k) => {
|
|
26230
|
+
no++;
|
|
26231
|
+
const end = it === last;
|
|
26232
|
+
const bottom = end ? OR10 : OR01;
|
|
26233
|
+
const cells = [];
|
|
26234
|
+
if (k === 0) {
|
|
26235
|
+
const h2 = cat.items.reduce((s, x) => s + x.h, 0);
|
|
26236
|
+
cells.push(tc({
|
|
26237
|
+
bf: bf.get({ t: WH02, b: cat.items.at(-1) === last ? OR10 : WH02, l: "none", r: "none", fill: GRAY }),
|
|
26238
|
+
row,
|
|
26239
|
+
col: 0,
|
|
26240
|
+
rowSpan: cat.items.length,
|
|
26241
|
+
w: COLS[0],
|
|
26242
|
+
h: h2,
|
|
26243
|
+
paras: p(runXml([cat.label, cat.spacing, 100, ORANGE, 11], true), { align: "DISTRIBUTE", left: 500, right: 500, lineSp: 160 })
|
|
26244
|
+
}));
|
|
26245
|
+
}
|
|
26246
|
+
const q = it.paras.map((runs, i) => p(runs.map((r) => runXml(r)).join(""), { align: "LEFT", indent: i === 0 ? it.indent : it.indent2 ?? 0, lineSp: it.lineSp })).join("");
|
|
26247
|
+
cells.push(tc({ bf: bf.get({ t: OR01, b: bottom, l: "none", r: OR01, fill: WHITE }), row, col: 1, w: COLS[1], h: it.h, paras: q }));
|
|
26248
|
+
const mark = inp.marks.get(no);
|
|
26249
|
+
for (const [c, on] of [[2, mark === "done"], [3, mark === "na"]]) {
|
|
26250
|
+
cells.push(tc({
|
|
26251
|
+
bf: bf.get({ t: OR01, b: bottom, l: OR01, r: OR01, fill: WHITE }),
|
|
26252
|
+
row,
|
|
26253
|
+
col: c,
|
|
26254
|
+
w: COLS[c],
|
|
26255
|
+
h: it.h,
|
|
26256
|
+
paras: p(on ? runXml(["\u25A0", -5, 80, void 0, 13]) : runXml(["\u25A1", 0, 100, void 0, 13]), center())
|
|
26257
|
+
}));
|
|
26258
|
+
}
|
|
26259
|
+
const note = inp.notes.has(no) ? [inp.notes.get(no)] : it.note ?? [""];
|
|
26260
|
+
cells.push(tc({ bf: bf.get({ t: OR01, b: bottom, l: OR01, r: "none", fill: WHITE }), row, col: 4, w: COLS[4], h: it.h, paras: p(runXml(note), center()) }));
|
|
26261
|
+
rows.push(cells.join(""));
|
|
26262
|
+
row++;
|
|
26263
|
+
});
|
|
26264
|
+
}
|
|
26265
|
+
}
|
|
26266
|
+
const h = 3814 + 2382 + 3142 + GROUPS.reduce((s, g3) => s + g3.h + g3.cats.reduce((m, c) => m + c.items.reduce((a, x) => a + x.h, 0), 0), 0);
|
|
26267
|
+
return host(ftbl(rows, W, h, 5), reg.para({ align: "CENTER", lineSp: 100 }), reg.char({ font: FONT, pt: 10 }));
|
|
26268
|
+
}
|
|
26269
|
+
function buildBangchimToc(chapters, ctx) {
|
|
26270
|
+
const { reg, bf } = ctx;
|
|
26271
|
+
const cols = [3650, 39336, 4464], w = 47450;
|
|
26272
|
+
const thin = ["0.12 mm", "#000000"];
|
|
26273
|
+
const none = bf.get({ t: "none", b: "none", l: "none", r: "none" });
|
|
26274
|
+
const under = bf.get({ t: "none", b: thin, l: "none", r: "none" });
|
|
26275
|
+
const pageBox = bf.get({ t: "none", b: thin, l: "none", r: thin });
|
|
26276
|
+
const pCenter = (lineSp) => reg.para({ align: "CENTER", lineSp });
|
|
26277
|
+
const blank2 = reg.char({ font: "HY\uD5E4\uB4DC\uB77C\uC778M", pt: 10 });
|
|
26278
|
+
const spacer = (row, h2) => cols.map((cw, c) => tc({ bf: none, row, col: c, w: cw, h: h2, paras: para("", pCenter(160), blank2) })).join("");
|
|
26279
|
+
const rows = [spacer(0, 1729)];
|
|
26280
|
+
let h = 1729;
|
|
26281
|
+
chapters.forEach((ch, i) => {
|
|
26282
|
+
if (i > 0) {
|
|
26283
|
+
rows.push(spacer(rows.length, 3112));
|
|
26284
|
+
h += 3112;
|
|
26285
|
+
}
|
|
26286
|
+
const roman = /^[Ⅰ-Ⅻ]+$/u.test(ch.label);
|
|
26287
|
+
const cNum = roman ? reg.char({ font: "HY\uACAC\uBA85\uC870", pt: 20, bold: true }) : reg.char({ font: "HY\uACAC\uACE0\uB515", pt: 18 });
|
|
26288
|
+
const titleFont = roman ? "HY\uD5E4\uB4DC\uB77C\uC778M" : "HY\uACAC\uACE0\uB515";
|
|
26289
|
+
const fit = fitOneLine(ch.title, titleFont, 18, cols[1] - 400, 15);
|
|
26290
|
+
const cTitle = reg.char({ font: titleFont, pt: fit.pt, ratio: fit.ratio, spacing: fit.spacing });
|
|
26291
|
+
const row = rows.length;
|
|
26292
|
+
rows.push(tc({ bf: none, row, col: 0, w: cols[0], h: 5859, paras: para(roman ? `${ch.label}.` : ch.label, pCenter(roman ? 150 : 160), cNum) }) + tc({ bf: under, row, col: 1, w: cols[1], h: 5859, paras: para(ch.title, reg.para({ align: roman ? "JUSTIFY" : "LEFT", lineSp: 160 }), cTitle) }) + tc({ bf: pageBox, row, col: 2, w: cols[2], h: 5859, paras: para("", pCenter(160), reg.char({ font: "HY\uACAC\uACE0\uB515", pt: 18 })) }));
|
|
26293
|
+
h += 5859;
|
|
26294
|
+
});
|
|
26295
|
+
const head = para("\uBAA9 \uCC28", reg.para({ align: "JUSTIFY", left: 3e3, before: 5e3, lineSp: 70 }), reg.char({ font: "HY\uD5E4\uB4DC\uB77C\uC778M", pt: 29, italic: true }));
|
|
26296
|
+
return [head, host(ftbl(rows, w, h, 3), reg.para({ align: "CENTER", lineSp: 100 }), blank2)];
|
|
26297
|
+
}
|
|
26298
|
+
|
|
26299
|
+
// src/hwpx/gongmun.ts
|
|
26300
|
+
var OFFICIAL_MARGINS = { top: 20, bottom: 15, left: 20, right: 15 };
|
|
26301
|
+
var GAEJOSIK_MARGINS = { top: 15, bottom: 15, left: 20, right: 20 };
|
|
26302
|
+
var SEOUL_REPORT_MARGINS = { top: 13, bottom: 13, left: 18, right: 18 };
|
|
26303
|
+
var SEOUL_REPORT_HEADER_FOOTER = 3600;
|
|
26304
|
+
var GAEJOSIK_HEADER_FOOTER = 4251;
|
|
26305
|
+
var MINISTRY_MARGINS = { top: 10, bottom: 10, left: 20, right: 20 };
|
|
26306
|
+
var MINISTRY_HEADER_FOOTER = 2835;
|
|
26307
|
+
var PRESET_DEFAULTS = {
|
|
26308
|
+
// 서울시 실결재 104건 중 12pt 64건(13pt 30, 15pt 5) — 지배값을 기본으로 사용.
|
|
26309
|
+
official: { bodyPt: 12, lineSpacing: 160, numbering: "standard" },
|
|
26310
|
+
report: { bodyPt: 15, lineSpacing: 160, numbering: "report" },
|
|
26311
|
+
// 실측 추진계획안: □ → ㅇ → * 계층.
|
|
26312
|
+
plan: { bodyPt: 15, lineSpacing: 160, numbering: "report" },
|
|
26313
|
+
notice: { bodyPt: 15, lineSpacing: 160, numbering: "standard" },
|
|
26314
|
+
minutes: { bodyPt: 14, lineSpacing: 130, numbering: "standard" },
|
|
26315
|
+
gaejosik: { bodyPt: 15, lineSpacing: 160, numbering: "gaejosik" },
|
|
26316
|
+
// 보도자료 — 실측(국토부 실물): 본문 바탕 14pt 160%, □→ㅇ→*(각주) 부호
|
|
26317
|
+
press: { bodyPt: 14, lineSpacing: 160, numbering: "report" },
|
|
26318
|
+
// 중앙부처 업무보고 — 실측(재경부 2차 업무보고): 함초롬바탕 15pt, 줄피치 21.7pt(≈145%), □→ㅇ→-→*(각주)
|
|
26319
|
+
ministry: { bodyPt: 15, lineSpacing: 145, numbering: "report" },
|
|
26320
|
+
// 서울 방침서 — 실측(시장방침 「청년취업사관학교 2.0」 추진계획 외 4건): □ HY견고딕 17 · ㅇ 한컴돋움 15b · - 휴먼명조 14, 줄간격 200%
|
|
26321
|
+
bangchim: { bodyPt: 15, lineSpacing: 190, numbering: "report" }
|
|
26322
|
+
};
|
|
26323
|
+
var PRESET_ALIAS = {
|
|
26324
|
+
official: "official",
|
|
26325
|
+
\uAE30\uC548\uBB38: "official",
|
|
26326
|
+
\uC2DC\uD589\uBB38: "official",
|
|
26327
|
+
\uACF5\uBB38: "official",
|
|
26328
|
+
\uACF5\uBB38\uC11C: "official",
|
|
26329
|
+
report: "report",
|
|
26330
|
+
\uBCF4\uACE0\uC11C: "report",
|
|
26331
|
+
plan: "plan",
|
|
26332
|
+
\uACC4\uD68D\uC11C: "plan",
|
|
26333
|
+
\uACC4\uD68D: "plan",
|
|
26334
|
+
notice: "notice",
|
|
26335
|
+
\uD1B5\uC9C0: "notice",
|
|
26336
|
+
\uC54C\uB9BC: "notice",
|
|
26337
|
+
\uC548\uB0B4: "notice",
|
|
26338
|
+
minutes: "minutes",
|
|
26339
|
+
\uD68C\uC758\uB85D: "minutes",
|
|
26340
|
+
gaejosik: "gaejosik",
|
|
26341
|
+
\uAC1C\uC870\uC2DD: "gaejosik",
|
|
26342
|
+
\uAC1C\uC870\uC2DD\uBCF4\uACE0\uC11C: "gaejosik",
|
|
26343
|
+
\uC815\uBD80\uBCF4\uACE0\uC11C: "gaejosik",
|
|
26344
|
+
\uC815\uBD80\uD45C\uC900\uAC1C\uC870\uC2DD\uBCF4\uACE0\uC11C: "gaejosik",
|
|
26345
|
+
press: "press",
|
|
26346
|
+
\uBCF4\uB3C4\uC790\uB8CC: "press",
|
|
26347
|
+
ministry: "ministry",
|
|
26348
|
+
\uC5C5\uBB34\uBCF4\uACE0: "ministry",
|
|
26349
|
+
\uBD80\uCC98\uC5C5\uBB34\uBCF4\uACE0: "ministry",
|
|
26350
|
+
\uC911\uC559\uBD80\uCC98\uBCF4\uACE0\uC11C: "ministry",
|
|
26351
|
+
bangchim: "bangchim",
|
|
26352
|
+
\uC11C\uC6B8\uBC29\uCE68: "bangchim",
|
|
26353
|
+
\uBC29\uCE68\uC11C: "bangchim",
|
|
26354
|
+
\uBC29\uCE68: "bangchim"
|
|
26355
|
+
};
|
|
26356
|
+
function normalizeGongmunPreset(preset) {
|
|
26357
|
+
if (!preset) return "official";
|
|
26358
|
+
return PRESET_ALIAS[preset.trim()] ?? "official";
|
|
26359
|
+
}
|
|
26360
|
+
function usesReportFonts(preset) {
|
|
26361
|
+
return preset === "gaejosik" || preset === "report" || preset === "plan" || preset === "bangchim";
|
|
26362
|
+
}
|
|
26363
|
+
function usesAsteriskThird(preset) {
|
|
26364
|
+
return preset === "plan" || preset === "press";
|
|
26365
|
+
}
|
|
26366
|
+
function needsGaejosikAssets(gongmun) {
|
|
26367
|
+
return usesReportFonts(gongmun.preset) || gongmun.cover !== null || gongmun.toc;
|
|
26368
|
+
}
|
|
26369
|
+
function assertFiniteRange(name, value, min, max) {
|
|
26370
|
+
if (!Number.isFinite(value) || value < min || value > max) {
|
|
26371
|
+
throw new KordocError(`${name} must be a finite number between ${min} and ${max}`);
|
|
26372
|
+
}
|
|
26373
|
+
}
|
|
26374
|
+
function validateGongmunOptions(opts) {
|
|
26375
|
+
if (opts.bodyPt !== void 0) assertFiniteRange("bodyPt", opts.bodyPt, 6, 40);
|
|
26376
|
+
if (opts.lineSpacing !== void 0) assertFiniteRange("lineSpacing", opts.lineSpacing, 50, 300);
|
|
26377
|
+
if (typeof opts.autoFit === "object" && opts.autoFit.minRatio !== void 0) {
|
|
26378
|
+
assertFiniteRange("autoFit.minRatio", opts.autoFit.minRatio, 50, 99);
|
|
26379
|
+
}
|
|
26380
|
+
if (opts.margins) {
|
|
26381
|
+
for (const side of ["top", "bottom", "left", "right"]) {
|
|
26382
|
+
const value = opts.margins[side];
|
|
26383
|
+
assertFiniteRange(`margins.${side}`, value, 0, 200);
|
|
26384
|
+
}
|
|
26385
|
+
if (opts.margins.left + opts.margins.right >= 210) {
|
|
26386
|
+
throw new KordocError("margins.left + margins.right must be less than 210");
|
|
26387
|
+
}
|
|
26388
|
+
if (opts.margins.top + opts.margins.bottom >= 297) {
|
|
26389
|
+
throw new KordocError("margins.top + margins.bottom must be less than 297");
|
|
26390
|
+
}
|
|
26391
|
+
}
|
|
26392
|
+
if (opts.sizes) {
|
|
26393
|
+
for (const [key, value] of Object.entries(opts.sizes)) {
|
|
26394
|
+
if (value !== void 0) assertFiniteRange(`sizes.${key}`, value, 6, 60);
|
|
26395
|
+
}
|
|
26396
|
+
}
|
|
26397
|
+
if (opts.approval && opts.approval.length > 6) {
|
|
26398
|
+
throw new KordocError("approval must contain at most 6 labels");
|
|
26399
|
+
}
|
|
26400
|
+
if (opts.checklist && typeof opts.checklist === "object") {
|
|
26401
|
+
const nums = [...opts.checklist.na ?? [], ...Object.keys(opts.checklist.notes ?? {}).map(Number)];
|
|
26402
|
+
for (const n of nums) {
|
|
26403
|
+
if (!Number.isInteger(n) || n < 1 || n > CHECKLIST_ITEMS) throw new KordocError(`checklist: item number must be an integer between 1 and ${CHECKLIST_ITEMS} (got ${n})`);
|
|
26404
|
+
}
|
|
26405
|
+
}
|
|
26406
|
+
if (opts.levels) {
|
|
26407
|
+
for (const [key, st] of Object.entries(opts.levels)) {
|
|
26408
|
+
const depth = Number(key);
|
|
26409
|
+
if (!Number.isInteger(depth) || depth < 0 || depth >= GONGMUN_LEVEL_COUNT) {
|
|
26410
|
+
throw new KordocError(`levels: depth must be an integer between 0 and ${GONGMUN_LEVEL_COUNT - 1} (got "${key}")`);
|
|
26411
|
+
}
|
|
26412
|
+
if (!st || typeof st !== "object") throw new KordocError(`levels.${key} must be an object {font, pt, bold}`);
|
|
26413
|
+
if (st.pt !== void 0) assertFiniteRange(`levels.${key}.pt`, st.pt, 6, 60);
|
|
26414
|
+
if (st.font !== void 0 && (typeof st.font !== "string" || !st.font.trim())) {
|
|
26415
|
+
throw new KordocError(`levels.${key}.font must be a non-empty string`);
|
|
26416
|
+
}
|
|
26417
|
+
}
|
|
26418
|
+
}
|
|
26419
|
+
}
|
|
26420
|
+
var GONGMUN_LEVEL_COUNT = 8;
|
|
26421
|
+
function resolveLevels(levels, bodyHeight) {
|
|
26422
|
+
if (!levels) return null;
|
|
26423
|
+
const out = {};
|
|
26424
|
+
for (const [key, st] of Object.entries(levels)) {
|
|
26425
|
+
if (st.font === void 0 && st.pt === void 0 && st.bold === void 0) continue;
|
|
26426
|
+
out[Number(key)] = {
|
|
26427
|
+
font: st.font?.trim() || null,
|
|
26428
|
+
height: st.pt !== void 0 ? Math.round(st.pt * 100) : bodyHeight,
|
|
26429
|
+
bold: !!st.bold
|
|
26430
|
+
};
|
|
26431
|
+
}
|
|
26432
|
+
return Object.keys(out).length > 0 ? out : null;
|
|
26433
|
+
}
|
|
26434
|
+
function levelMarkerHeight(g3, depth) {
|
|
26435
|
+
return g3.levels?.[depth]?.height ?? g3.bodyHeight;
|
|
26436
|
+
}
|
|
26437
|
+
function incompatibleGongmunWarnings(opts) {
|
|
26438
|
+
const warns = [];
|
|
26439
|
+
const preset = normalizeGongmunPreset(opts.preset);
|
|
26440
|
+
if (opts.docHead && preset !== "official") warns.push(`doc_head(\uB450\uBB38)\uB294 \uAE30\uC548\uBB38(official) \uC804\uC6A9 \u2014 '${preset}' \uD504\uB9AC\uC14B\uC5D0\uC11C \uBB34\uC2DC\uB428`);
|
|
26441
|
+
if (opts.docFoot && preset !== "official") warns.push(`doc_foot(\uACB0\uBB38)\uB294 \uAE30\uC548\uBB38(official) \uC804\uC6A9 \u2014 '${preset}' \uD504\uB9AC\uC14B\uC5D0\uC11C \uBB34\uC2DC\uB428`);
|
|
26442
|
+
if (opts.noticeHead && preset !== "notice") warns.push(`notice_head(\uACF5\uACE0\uBC88\uD638\xB7\uBC1C\uC2E0\uBA85\uC758)\uB294 \uD1B5\uC9C0(notice) \uC804\uC6A9 \u2014 '${preset}' \uD504\uB9AC\uC14B\uC5D0\uC11C \uBB34\uC2DC\uB428`);
|
|
26443
|
+
if (opts.press && preset !== "press") warns.push(`press(\uBA38\uB9AC\uBC15\uC2A4\xB7\uBD80\uC81C\xB7\uB2F4\uB2F9)\uB294 \uBCF4\uB3C4\uC790\uB8CC(press) \uC804\uC6A9 \u2014 '${preset}' \uD504\uB9AC\uC14B\uC5D0\uC11C \uBB34\uC2DC\uB428`);
|
|
26444
|
+
if (opts.checklist && !SEOUL_REPORT_PRESETS.has(preset)) warns.push(`checklist(\uC0AC\uC804 \uAC80\uD1A0\uD56D\uBAA9 \uC810\uAC80\uD45C)\uB294 \uBCF4\uACE0\uC11C\xB7\uACC4\uD68D\uC11C\xB7\uBC29\uCE68\uC11C \uC804\uC6A9 \u2014 '${preset}' \uD504\uB9AC\uC14B\uC5D0\uC11C \uBB34\uC2DC\uB428`);
|
|
26445
|
+
if (preset === "press" && (opts.cover === true || typeof opts.cover === "object" || opts.toc === true)) {
|
|
26446
|
+
warns.push("\uBCF4\uB3C4\uC790\uB8CC\uB294 \uBA38\uB9AC\uBC15\uC2A4 \uC11C\uC2DD\uACFC \uC591\uB9BD \uBD88\uAC00\uB77C \uD45C\uC9C0\xB7\uBAA9\uCC28\uAC00 \uBB34\uC2DC\uB428");
|
|
26447
|
+
}
|
|
26448
|
+
if (opts.sizes && Object.keys(opts.sizes).length > 0 && !usesReportFonts(preset)) {
|
|
26449
|
+
warns.push(`sizes(\uAC1C\uC870\uC2DD \uC694\uC18C \uD06C\uAE30)\uB294 \uAC1C\uC870\uC2DD\xB7\uBCF4\uACE0\uC11C\xB7\uACC4\uD68D\uC11C \uC804\uC6A9 \u2014 '${preset}' \uD504\uB9AC\uC14B\uC5D0\uC11C \uBB34\uC2DC\uB428`);
|
|
26450
|
+
}
|
|
26451
|
+
if (opts.suppressSingle && (opts.numbering ?? PRESET_DEFAULTS[preset].numbering) !== "standard") {
|
|
26452
|
+
warns.push(`suppress_single(\uB2E8\uC77C \uD615\uC81C \uBD80\uD638 \uC0DD\uB7B5)\uC740 \uBC95\uC815 \uBC88\uD638(standard) \uC804\uC6A9 \u2014 '${preset}' \uD504\uB9AC\uC14B(\uBD88\uB9BF \uCCB4\uACC4)\uC5D0\uC11C \uBB34\uB3D9\uC791`);
|
|
26453
|
+
}
|
|
26454
|
+
return warns;
|
|
26455
|
+
}
|
|
26456
|
+
var SEOUL_REPORT_PRESETS = /* @__PURE__ */ new Set(["report", "plan", "bangchim"]);
|
|
26457
|
+
function resolveChecklist(c, preset) {
|
|
26458
|
+
if (!c || !SEOUL_REPORT_PRESETS.has(preset)) return null;
|
|
26459
|
+
const marks = /* @__PURE__ */ new Map(), notes = /* @__PURE__ */ new Map();
|
|
26460
|
+
if (typeof c === "object") {
|
|
26461
|
+
const na = new Set(c.na ?? []);
|
|
26462
|
+
for (let i = 1; i <= CHECKLIST_ITEMS; i++) marks.set(i, na.has(i) ? "na" : "done");
|
|
26463
|
+
for (const [k, v] of Object.entries(c.notes ?? {})) notes.set(Number(k), String(v));
|
|
26464
|
+
}
|
|
26465
|
+
return { marks, notes };
|
|
26466
|
+
}
|
|
26467
|
+
function hexColorOption(name, value) {
|
|
26468
|
+
if (value === void 0) return void 0;
|
|
26469
|
+
const v = value.trim();
|
|
26470
|
+
if (!/^#[0-9a-fA-F]{6}$/.test(v)) throw new KordocError(`${name} must be a #RRGGBB color (got "${value}")`);
|
|
26471
|
+
return v.toUpperCase();
|
|
26472
|
+
}
|
|
26473
|
+
function resolveGongmun(opts) {
|
|
26474
|
+
validateGongmunOptions(opts);
|
|
26475
|
+
const preset = normalizeGongmunPreset(opts.preset);
|
|
26476
|
+
const d = PRESET_DEFAULTS[preset];
|
|
26477
|
+
const bodyPt = opts.bodyPt ?? d.bodyPt;
|
|
26478
|
+
const autoFitMinRatio = opts.autoFit === false ? null : typeof opts.autoFit === "object" ? Math.min(Math.max(opts.autoFit.minRatio ?? 90, 50), 99) : 90;
|
|
26479
|
+
const ministry = preset === "ministry";
|
|
26480
|
+
const coverOn = opts.cover !== void 0 ? opts.cover !== false : preset === "gaejosik" || ministry;
|
|
26481
|
+
const coverOpts = typeof opts.cover === "object" ? opts.cover : {};
|
|
26482
|
+
const gaejosik = preset === "gaejosik";
|
|
26483
|
+
const reportFamily = gaejosik || preset === "report" || preset === "plan" || preset === "notice" || preset === "press";
|
|
26484
|
+
return {
|
|
26485
|
+
preset,
|
|
26486
|
+
bodyFont: opts.bodyFont ?? "myeongjo",
|
|
26487
|
+
bodyHeight: Math.round(bodyPt * 100),
|
|
26488
|
+
lineSpacing: opts.lineSpacing ?? d.lineSpacing,
|
|
26489
|
+
numbering: opts.numbering ?? d.numbering,
|
|
26490
|
+
margins: opts.margins ?? (ministry ? MINISTRY_MARGINS : preset === "report" || preset === "plan" || preset === "bangchim" ? SEOUL_REPORT_MARGINS : reportFamily ? GAEJOSIK_MARGINS : OFFICIAL_MARGINS),
|
|
26491
|
+
centerTitle: opts.centerTitle ?? true,
|
|
26492
|
+
autoFitMinRatio,
|
|
26493
|
+
// 보도자료는 머리박스가 1페이지 최상단을 차지하는 서식이라 표지·목차와 양립 불가 —
|
|
26494
|
+
// 켜면 머리박스가 표지에 얹히고 25pt 제목·부제가 유실된다 (docHead 프리셋 게이팅과 동일 관례)
|
|
26495
|
+
cover: coverOn && preset !== "press" ? { date: coverOpts.date ?? null, org: coverOpts.org ?? "", ...coverOpts.dept ? { dept: coverOpts.dept } : {}, ...coverOpts.label ? { label: coverOpts.label } : {} } : null,
|
|
26496
|
+
bodyPtExplicit: opts.bodyPt !== void 0,
|
|
26497
|
+
lineSpacingExplicit: opts.lineSpacing !== void 0,
|
|
26498
|
+
bodyFontExplicit: opts.bodyFont !== void 0,
|
|
26499
|
+
summary: opts.summary?.trim() || null,
|
|
26500
|
+
docInfo: opts.docInfo ?? null,
|
|
26501
|
+
checklist: resolveChecklist(opts.checklist, preset),
|
|
26502
|
+
toc: preset !== "press" && (opts.toc ?? (gaejosik || ministry)),
|
|
26503
|
+
fonts: opts.fonts ?? {},
|
|
26504
|
+
sizes: opts.sizes ?? {},
|
|
26505
|
+
levels: resolveLevels(opts.levels, Math.round(bodyPt * 100)),
|
|
26506
|
+
// 쪽번호 — 보고서 계열 관행(실측: 2_보고서 양식·추진계획·공고문 전부 하단 중앙)
|
|
26507
|
+
pageNumbers: opts.pageNumbers ?? (gaejosik || ministry || preset === "report" || preset === "plan" || preset === "bangchim"),
|
|
26508
|
+
// 머리말·꼬리말 — 실측: 보고서 계열 15mm(GT3·t2·춘천·브라더), 공고·보도 10mm,
|
|
26509
|
+
// 기안문 0(실결재 41/60건 h0/f0)
|
|
26510
|
+
headerFooter: ministry ? MINISTRY_HEADER_FOOTER : preset === "report" || preset === "plan" || preset === "bangchim" ? SEOUL_REPORT_HEADER_FOOTER : usesReportFonts(preset) ? GAEJOSIK_HEADER_FOOTER : preset === "notice" || preset === "press" ? 2835 : 0,
|
|
26511
|
+
// "끝." — 기안문 규정(본문 끝 2타+"끝."). 그 외는 opt-in
|
|
26512
|
+
endMark: opts.endMark ?? preset === "official",
|
|
26513
|
+
approval: opts.approval && opts.approval.length > 0 ? opts.approval : null,
|
|
26514
|
+
// 본문 제목박스 — 실측(GT3·GT12): 목차 뒤 본문 시작에 제목 반복. 표지 켜진 개조식 기본
|
|
26515
|
+
bodyTitleBox: opts.bodyTitleBox ?? (gaejosik && coverOn),
|
|
26516
|
+
chapterFit: opts.chapterFit ?? false,
|
|
26517
|
+
// h2 말머리 — 실측: 보고서 양식 □ 대항목(QA-2), 공고문 아라비아("1. 사업개요", 바이오헬스 실측)
|
|
26518
|
+
// v5 라운드 3: 보고서·계획서 기본 band(띠 표) — 서울 plan 7/19·교육청 7/18 실측, 실무자 요청
|
|
26519
|
+
h2Marker: opts.h2Marker ?? (preset === "report" || preset === "plan" ? "band" : preset === "bangchim" ? "square" : preset === "notice" ? "number" : "none"),
|
|
26520
|
+
// 띠 제목 색 — 실측 최다 #003366/흰 글자(계획서 띠 표 14개). 교육청형 밝은 띠는 옵션으로
|
|
26521
|
+
bandColor: hexColorOption("bandColor", opts.bandColor) ?? "#003366",
|
|
26522
|
+
bandTextColor: hexColorOption("bandTextColor", opts.bandTextColor) ?? "#FFFFFF",
|
|
26523
|
+
// 2단계 부호 — 실결재 기안문·공고문 ㅇ 지배(60건 중 ㅇ134:○5), 보고서 양식 계열 ○
|
|
26524
|
+
// v5: 서울 실결재 ㅇ(이응) 지배 — 보고서·계획서·통지·보도자료 ㅇ, 중앙부처 개조식 양식만 ○
|
|
26525
|
+
bullet2: opts.bullet2 ?? (preset === "gaejosik" ? "\u25CB" : "\u3147"),
|
|
26526
|
+
// 단일 형제 부호 생략 — 규정이지만 부호 없는 계단이 실무 눈에 어색 (실무자 QA)
|
|
26527
|
+
suppressSingle: opts.suppressSingle ?? false,
|
|
26528
|
+
docHead: preset === "official" && opts.docHead ? opts.docHead : null,
|
|
26529
|
+
docFoot: preset === "official" && opts.docFoot ? opts.docFoot : null,
|
|
26530
|
+
reportInfo: opts.reportInfo?.trim() || null,
|
|
26531
|
+
noticeHead: preset === "notice" && opts.noticeHead ? opts.noticeHead : null,
|
|
26532
|
+
// 보도자료 머리박스는 프리셋 자체가 요구 — 옵션 미지정이어도 빈 객체
|
|
26533
|
+
press: preset === "press" ? opts.press ?? {} : null
|
|
26534
|
+
};
|
|
26535
|
+
}
|
|
26536
|
+
var REPORT_BULLETS = ["\u25A1", "\u25CB", "-", "\u318D"];
|
|
26537
|
+
var ASTERISK_BULLETS = ["\u25A1", "\u25CB", "*", "\u318D"];
|
|
26538
|
+
function standardMarker(depth, n) {
|
|
26539
|
+
switch (depth) {
|
|
26540
|
+
case 0:
|
|
26541
|
+
return `${n + 1}.`;
|
|
26542
|
+
case 1:
|
|
26543
|
+
return `${hangulOrdinal(n)}.`;
|
|
26544
|
+
case 2:
|
|
26545
|
+
return `${n + 1})`;
|
|
26546
|
+
case 3:
|
|
26547
|
+
return `${hangulOrdinal(n)})`;
|
|
26548
|
+
case 4:
|
|
26549
|
+
return `(${n + 1})`;
|
|
26550
|
+
case 5:
|
|
26551
|
+
return `(${hangulOrdinal(n)})`;
|
|
26552
|
+
case 6:
|
|
26553
|
+
return circledNumber(n);
|
|
26554
|
+
case 7:
|
|
26555
|
+
return circledHangul(n);
|
|
26556
|
+
default:
|
|
26557
|
+
return circledHangul(n);
|
|
26558
|
+
}
|
|
26559
|
+
}
|
|
26560
|
+
function reportMarker(depth, bullet2 = "\u25CB", asteriskThird = false) {
|
|
26561
|
+
const bullets = asteriskThird ? ASTERISK_BULLETS : REPORT_BULLETS;
|
|
26562
|
+
const m = bullets[Math.min(depth, bullets.length - 1)];
|
|
26563
|
+
return depth === 1 ? bullet2 : m;
|
|
26564
|
+
}
|
|
26565
|
+
function markerWidth(marker, bodyHeight) {
|
|
26566
|
+
let em = SPACE_EM_FIXED;
|
|
26567
|
+
for (const c of marker) em += charWidthEm1000(c.codePointAt(0));
|
|
26568
|
+
return Math.round(em / 1e3 * bodyHeight);
|
|
26569
|
+
}
|
|
26570
|
+
function levelIndent(depth, bodyHeight, numbering, sizes = {}, bullet2 = "\u25CB", asteriskThird = false, markerHeight = bodyHeight) {
|
|
26571
|
+
if (numbering === "gaejosik") return gaejosikLevelIndent(depth, bodyHeight, sizes, bullet2);
|
|
26572
|
+
const marker = numbering === "report" ? reportMarker(depth, bullet2, asteriskThird) : standardMarker(depth, 0);
|
|
26573
|
+
return { left: Math.round(depth * bodyHeight), indent: -markerWidth(marker, markerHeight) };
|
|
26574
|
+
}
|
|
26575
|
+
function computeSuppression(depths) {
|
|
26576
|
+
const counts = /* @__PURE__ */ new Map();
|
|
26577
|
+
const keys = [];
|
|
26578
|
+
const path = [];
|
|
26579
|
+
for (const depth of depths) {
|
|
26580
|
+
path.length = depth + 1;
|
|
26581
|
+
path[depth] = (path[depth] ?? 0) + 1;
|
|
26582
|
+
const parentKey = path.slice(0, depth).join(".") + "|" + depth;
|
|
26583
|
+
keys.push(parentKey);
|
|
26584
|
+
counts.set(parentKey, (counts.get(parentKey) ?? 0) + 1);
|
|
26585
|
+
}
|
|
26586
|
+
return keys.map((k) => (counts.get(k) ?? 0) <= 1);
|
|
26587
|
+
}
|
|
26588
|
+
var GongmunNumberer = class {
|
|
26589
|
+
constructor(numbering, bullet2 = "\u25CB", asteriskThird = false) {
|
|
26590
|
+
this.numbering = numbering;
|
|
26591
|
+
this.bullet2 = bullet2;
|
|
26592
|
+
this.asteriskThird = asteriskThird;
|
|
26593
|
+
}
|
|
26594
|
+
numbering;
|
|
26595
|
+
bullet2;
|
|
26596
|
+
asteriskThird;
|
|
26597
|
+
counts = [];
|
|
26598
|
+
/** depth 항목 하나에 대한 마커. suppress=true면 빈 문자열(부호 없음) */
|
|
26599
|
+
next(depth, suppress) {
|
|
26600
|
+
this.counts.length = depth + 1;
|
|
26601
|
+
const n = this.counts[depth] ?? 0;
|
|
26602
|
+
this.counts[depth] = n + 1;
|
|
26603
|
+
if (suppress) return "";
|
|
26604
|
+
if (this.numbering === "gaejosik") return gaejosikMarker(depth, this.bullet2);
|
|
26605
|
+
return this.numbering === "report" ? reportMarker(depth, this.bullet2, this.asteriskThird) : standardMarker(depth, n);
|
|
26606
|
+
}
|
|
26607
|
+
reset() {
|
|
26608
|
+
this.counts = [];
|
|
26609
|
+
}
|
|
26610
|
+
};
|
|
26611
|
+
|
|
25827
26612
|
// src/hwpx/md-runs.ts
|
|
25828
26613
|
var HYPERLINK_FIELDID = 627600491;
|
|
25829
26614
|
var FIELD_ID_BASE = 18e8;
|
|
@@ -26054,6 +26839,7 @@ function parseInlineMarkdown(text) {
|
|
|
26054
26839
|
text = text.replace(/!\[([^\]]*)\]\([^)]*\)/g, "$1");
|
|
26055
26840
|
text = text.replace(/~~([^~]+)~~/g, "$1");
|
|
26056
26841
|
text = text.replace(/<u>([^<]*)<\/u>/g, "$1");
|
|
26842
|
+
text = text.replace(/<(sup|sub)>([^<]*)<\/\1>/g, "$2");
|
|
26057
26843
|
let spans;
|
|
26058
26844
|
if (inlineDoc) {
|
|
26059
26845
|
spans = [];
|
|
@@ -26997,8 +27783,8 @@ function cell(opts) {
|
|
|
26997
27783
|
const paras = opts.paras ?? emptyPara();
|
|
26998
27784
|
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.vertAlign ?? "CENTER"}" linkListIDRef="0" linkListNextIDRef="0" textWidth="0" textHeight="0" hasTextRef="0" hasNumRef="0">${paras}</hp:subList><hp:cellAddr colAddr="${opts.col}" rowAddr="${opts.row ?? 0}"/><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>`;
|
|
26999
27785
|
}
|
|
27000
|
-
function table(rows, w, h, cols, tblBf = 1, bodyWidth = GAEJOSIK_BASE_WIDTH) {
|
|
27001
|
-
const outLR =
|
|
27786
|
+
function table(rows, w, h, cols, tblBf = 1, bodyWidth = GAEJOSIK_BASE_WIDTH, marginW = w) {
|
|
27787
|
+
const outLR = marginW + 566 > bodyWidth ? 0 : 283;
|
|
27002
27788
|
return `<hp:tbl id="${++gjTableId}" 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="${tblBf}" 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="${outLR}" right="${outLR}" 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>`;
|
|
27003
27789
|
}
|
|
27004
27790
|
function buildGaejosikCover(title, gongmun, bodyWidth = GAEJOSIK_BASE_WIDTH) {
|
|
@@ -27064,12 +27850,19 @@ function buildGaejosikBodyTitle(title, gongmun, bodyWidth = GAEJOSIK_BASE_WIDTH)
|
|
|
27064
27850
|
const box = table([topRow, titleRow, botRow], g3.totalW, bt.barH * 2 + bt.titleH, 3, 1, bodyWidth);
|
|
27065
27851
|
return `<hp:p paraPrIDRef="${PARA_NORMAL}" styleIDRef="0"><hp:run charPrIDRef="${CHAR_NORMAL}">${box}</hp:run></hp:p>`;
|
|
27066
27852
|
}
|
|
27853
|
+
var CHAPTER_FIT_PAD = 282 + 300;
|
|
27067
27854
|
function buildGaejosikChapter(n, title, gongmun, bodyWidth = GAEJOSIK_BASE_WIDTH, srcLevel = 2) {
|
|
27068
27855
|
const g3 = gongmun ? chapterGeom(gongmun.bodyHeight, gongmun.sizes, bodyWidth) : chapterGeom(1500, {}, bodyWidth);
|
|
27856
|
+
const titleW = gongmun?.chapterFit ? Math.min(g3.titleW, Math.ceil(measureTextWidth(
|
|
27857
|
+
` ${title} `,
|
|
27858
|
+
gaejosikSizes(gongmun.bodyHeight, gongmun.sizes).chapter,
|
|
27859
|
+
100,
|
|
27860
|
+
{ faceClass: faceClassForGen(gongmun.fonts.heading ?? "HY\uD5E4\uB4DC\uB77C\uC778M") }
|
|
27861
|
+
)) + CHAPTER_FIT_PAD) : g3.titleW;
|
|
27069
27862
|
const numPara = `<hp:p paraPrIDRef="${GONGMUN_CENTER}" styleIDRef="0"><hp:run charPrIDRef="${GJ_CHAR_CHAPTER_NUM}"><hp:t>${chapterRoman(n)}</hp:t></hp:run></hp:p>`;
|
|
27070
27863
|
const titlePara = `<hp:p paraPrIDRef="${PARA_NORMAL}" styleIDRef="0"><hp:run charPrIDRef="${GJ_CHAR_CHAPTER_TITLE}"><hp:t> ${escapeTextXml(title)}</hp:t></hp:run></hp:p>`;
|
|
27071
|
-
const row = cell({ bf: GJ_BF_CHAPTER_NUM, col: 0, w: g3.numW, h: g3.rowH, paras: numPara }) + cell({ bf: GJ_BF_CHAPTER_GAP, col: 1, w: g3.gapW, h: g3.rowH }) + cell({ bf: GJ_BF_CHAPTER_TITLE, col: 2, w:
|
|
27072
|
-
const tbl2 = table([row], g3.numW + g3.gapW +
|
|
27864
|
+
const row = cell({ bf: GJ_BF_CHAPTER_NUM, col: 0, w: g3.numW, h: g3.rowH, paras: numPara }) + cell({ bf: GJ_BF_CHAPTER_GAP, col: 1, w: g3.gapW, h: g3.rowH }) + cell({ bf: GJ_BF_CHAPTER_TITLE, col: 2, w: titleW, h: g3.rowH, paras: titlePara, name: `__kordoc_h${Math.min(Math.max(srcLevel, 1), 6)}` });
|
|
27865
|
+
const tbl2 = table([row], g3.numW + g3.gapW + titleW, g3.rowH, 3, 1, bodyWidth, g3.numW + g3.gapW + g3.titleW);
|
|
27073
27866
|
return `<hp:p paraPrIDRef="${GJ_PARA_CHAPTER}" styleIDRef="0"><hp:run charPrIDRef="${CHAR_NORMAL}">${tbl2}</hp:run></hp:p>`;
|
|
27074
27867
|
}
|
|
27075
27868
|
|
|
@@ -28399,64 +29192,6 @@ function generateHtmlTableXml(rawHtml, theme, totalWidth = 44e3, style = null, r
|
|
|
28399
29192
|
return `<hp:tbl id="${tblId}" zOrder="0" numberingType="TABLE"${float ? FLOAT_TBL_ATTRS : ""} pageBreak="CELL" repeatHeader="${style ? 1 : 0}" rowCnt="${rowCnt}" colCnt="${colCnt}" cellSpacing="0" borderFillIDRef="2" noShading="0"><hp:sz width="${tblW}" widthRelTo="ABSOLUTE" height="${tableH}" heightRelTo="ABSOLUTE" protect="0"/>` + tablePosXml(float, !!reg && style?.rightParaPr !== void 0) + tableOutMarginXml(float, measureH) + `<hp:inMargin left="510" right="510" top="141" bottom="141"/>` + trXmls.join("") + `</hp:tbl>`;
|
|
28400
29193
|
}
|
|
28401
29194
|
|
|
28402
|
-
// src/hwpx/gen-gongmun-extra.ts
|
|
28403
|
-
var extraTableId = EXTRA_TABLE_ID_BASE;
|
|
28404
|
-
function resetExtraTableIds() {
|
|
28405
|
-
extraTableId = EXTRA_TABLE_ID_BASE;
|
|
28406
|
-
}
|
|
28407
|
-
function tbl(rows, w, h, cols) {
|
|
28408
|
-
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>`;
|
|
28409
|
-
}
|
|
28410
|
-
function tc(opts) {
|
|
28411
|
-
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>`;
|
|
28412
|
-
}
|
|
28413
|
-
function para(text, paraPrId, charPrId) {
|
|
28414
|
-
return `<hp:p paraPrIDRef="${paraPrId}" styleIDRef="0"><hp:run charPrIDRef="${charPrId}"><hp:t>${escapeTextXml(text)}</hp:t></hp:run></hp:p>`;
|
|
28415
|
-
}
|
|
28416
|
-
var APPROVAL_COL_W = 7085;
|
|
28417
|
-
var APPROVAL_LABEL_H = 1300;
|
|
28418
|
-
var APPROVAL_SIGN_H = 4200;
|
|
28419
|
-
function buildApprovalTable(labels, reg, approvalCharPr) {
|
|
28420
|
-
const n = labels.length;
|
|
28421
|
-
const w = APPROVAL_COL_W * n;
|
|
28422
|
-
const bfAt = (row, col) => reg.get({
|
|
28423
|
-
t: row === 0 ? "thick" : "thin",
|
|
28424
|
-
b: row === 1 ? "thick" : "thin",
|
|
28425
|
-
l: col === 0 ? "thick" : "thin",
|
|
28426
|
-
r: col === n - 1 ? "thick" : "thin"
|
|
28427
|
-
});
|
|
28428
|
-
const labelRow = labels.map(
|
|
28429
|
-
(label, c) => tc({ bf: bfAt(0, c), row: 0, col: c, w: APPROVAL_COL_W, h: APPROVAL_LABEL_H, paras: para(label, GONGMUN_PARA_APPROVAL, approvalCharPr) })
|
|
28430
|
-
).join("");
|
|
28431
|
-
const signRow = labels.map(
|
|
28432
|
-
(_, c) => tc({ bf: bfAt(1, c), row: 1, col: c, w: APPROVAL_COL_W, h: APPROVAL_SIGN_H, paras: para("", GONGMUN_PARA_APPROVAL, approvalCharPr) })
|
|
28433
|
-
).join("");
|
|
28434
|
-
const table2 = tbl([labelRow, signRow], w, APPROVAL_LABEL_H + APPROVAL_SIGN_H, n);
|
|
28435
|
-
return `<hp:p paraPrIDRef="${GONGMUN_RIGHT}" styleIDRef="0"><hp:run charPrIDRef="${CHAR_NORMAL}">${table2}</hp:run></hp:p>`;
|
|
28436
|
-
}
|
|
28437
|
-
function hasEndMark(lastText) {
|
|
28438
|
-
return /(^|\s)끝\s*\.\s*$/.test(lastText.trim());
|
|
28439
|
-
}
|
|
28440
|
-
function buildEndMark() {
|
|
28441
|
-
return para(" \uB05D.", PARA_NORMAL, CHAR_NORMAL);
|
|
28442
|
-
}
|
|
28443
|
-
var TITLE_BOX = { barH: 382, titleH: 2850, color: "#0080C0", gradient: ["#0080C0", "#3CBFFF"] };
|
|
28444
|
-
function buildTitleBox(title, titleCharPr, barCharPr, bodyWidth, reg) {
|
|
28445
|
-
const w = bodyWidth - 280;
|
|
28446
|
-
const outer = { t: "thin", b: "thin", l: "thin", r: "thin" };
|
|
28447
|
-
const barTop = reg.get({ ...outer, fill: TITLE_BOX.color });
|
|
28448
|
-
const barBottom = reg.get({ ...outer, fill: { gradient: TITLE_BOX.gradient } });
|
|
28449
|
-
const mid = reg.get(outer);
|
|
28450
|
-
const barPara = para("", GJ_PARA_BAR, barCharPr);
|
|
28451
|
-
const rows = [
|
|
28452
|
-
tc({ bf: barTop, row: 0, col: 0, w, h: TITLE_BOX.barH, paras: barPara }),
|
|
28453
|
-
tc({ bf: mid, row: 1, col: 0, w, h: TITLE_BOX.titleH, paras: para(title, GONGMUN_TBL_CENTER, titleCharPr), name: "__kordoc_h1" }),
|
|
28454
|
-
tc({ bf: barBottom, row: 2, col: 0, w, h: TITLE_BOX.barH, paras: barPara })
|
|
28455
|
-
];
|
|
28456
|
-
const table2 = tbl(rows, w, TITLE_BOX.barH * 2 + TITLE_BOX.titleH, 1);
|
|
28457
|
-
return `<hp:p paraPrIDRef="${PARA_NORMAL}" styleIDRef="0"><hp:run charPrIDRef="${CHAR_NORMAL}">${table2}</hp:run></hp:p>`;
|
|
28458
|
-
}
|
|
28459
|
-
|
|
28460
29195
|
// src/hwpx/gen-docframe.ts
|
|
28461
29196
|
function docframeIds(base) {
|
|
28462
29197
|
return { org: base, sender: base + 1, foot: base + 2, small: base + 3, pressLabel: base + 4, pressHead: base + 5, pressTitle: base + 6, pressSub: base + 7, pressContact: base + 8 };
|
|
@@ -29519,6 +30254,63 @@ function ministryScheme(bodyPt = 15, lineSp = 145) {
|
|
|
29519
30254
|
blankBetweenTop: false
|
|
29520
30255
|
};
|
|
29521
30256
|
}
|
|
30257
|
+
var BANGCHIM_FRAME = {
|
|
30258
|
+
font: "\uD55C\uCEF4\uB3CB\uC6C0",
|
|
30259
|
+
titleFont: "HY\uD5E4\uB4DC\uB77C\uC778M",
|
|
30260
|
+
titlePt: 26,
|
|
30261
|
+
contactFont: "\uD734\uBA3C\uBA85\uC870",
|
|
30262
|
+
contactPt: 12,
|
|
30263
|
+
summaryFont: "\uD55C\uCEF4\uB3CB\uC6C0",
|
|
30264
|
+
summaryPt: 15,
|
|
30265
|
+
summaryFill: "#DFE6F7",
|
|
30266
|
+
// 간이기안 표지 — 방침서 16건: 문서정보 굴림체(16건) 이름칸 10 굵게(11건)·값칸 9, 결재선 굴림체 9(15건 중 11)·8,
|
|
30267
|
+
// 제목 띠 그라데이션 #3057B9→#A0B4E6(15건) 높이 663(11건), 날짜 HY견명조 21b(13건), 기관 HY헤드라인M 26b(11건), 부서 HY견명조 22(14건 중 12)
|
|
30268
|
+
cover: {
|
|
30269
|
+
infoFont: "\uAD74\uB9BC\uCCB4",
|
|
30270
|
+
infoLabelPt: 10,
|
|
30271
|
+
infoValuePt: 9,
|
|
30272
|
+
approvalPt: 9,
|
|
30273
|
+
bar: { gradient: ["#3057B9", "#A0B4E6"], h: 663 },
|
|
30274
|
+
date: { font: "HY\uACAC\uBA85\uC870", pt: 21, bold: true },
|
|
30275
|
+
org: { font: "HY\uD5E4\uB4DC\uB77C\uC778M", pt: 26, bold: true },
|
|
30276
|
+
dept: { font: "HY\uACAC\uBA85\uC870", pt: 22, bold: false },
|
|
30277
|
+
// 줄간격 — 문서정보 160(16건)·결재선 130(16건)·제목 120(최다 5건)·날짜 170(11)·기관 170(12)·부서 190(11)
|
|
30278
|
+
lineSp: { info: 160, approval: 130, title: 120, date: 170, org: 170, dept: 190 }
|
|
30279
|
+
}
|
|
30280
|
+
};
|
|
30281
|
+
function seoulBangchimScheme(bodyPt = 15, lineSp = 190) {
|
|
30282
|
+
const d = bodyPt - 15;
|
|
30283
|
+
const lv = (font, pt2, bold, leadTa, extra = {}) => ({ font, pt: pt2 + d, bold, leadTa, ...extra });
|
|
30284
|
+
return {
|
|
30285
|
+
kind: "gaejosik",
|
|
30286
|
+
lineSp,
|
|
30287
|
+
body: lv("HY\uACAC\uACE0\uB515", 16, true, 0),
|
|
30288
|
+
levels: [
|
|
30289
|
+
lv("HY\uACAC\uACE0\uB515", 17, false, 0, { oneLine: true, keepWithNext: true, blankBefore: true }),
|
|
30290
|
+
lv("\uD55C\uCEF4\uB3CB\uC6C0", 15, true, 1),
|
|
30291
|
+
lv("\uD734\uBA3C\uBA85\uC870", 14, false, 3),
|
|
30292
|
+
lv("\uD55C\uCEF4\uB3CB\uC6C0", 13, false, 4, { align: "LEFT", lineSp: 180 }),
|
|
30293
|
+
lv("\uD55C\uCEF4\uB3CB\uC6C0", 13, false, 5, { align: "LEFT", lineSp: 180 }),
|
|
30294
|
+
lv("\uD55C\uCEF4\uB3CB\uC6C0", 13, false, 6, { align: "LEFT", lineSp: 180 }),
|
|
30295
|
+
lv("\uD55C\uCEF4\uB3CB\uC6C0", 13, false, 7, { align: "LEFT", lineSp: 180 }),
|
|
30296
|
+
lv("\uD55C\uCEF4\uB3CB\uC6C0", 13, false, 8, { align: "LEFT", lineSp: 180 })
|
|
30297
|
+
],
|
|
30298
|
+
ref: lv("\uD55C\uCEF4\uB3CB\uC6C0", 13, false, 0, { align: "LEFT" }),
|
|
30299
|
+
sub: lv("\uD55C\uCEF4\uB3CB\uC6C0", 13, false, 2),
|
|
30300
|
+
attach: lv("\uD55C\uCEF4\uB3CB\uC6C0", 15, true, 0),
|
|
30301
|
+
chapter: lv("HY\uACAC\uACE0\uB515", 20, false, 0, { oneLine: true, keepWithNext: true }),
|
|
30302
|
+
marker: (depth) => depth === 0 ? "\u25A1" : depth === 1 ? "\u3147" : depth === 2 ? "-" : "\u25B8",
|
|
30303
|
+
table: { ...SEOUL_TABLE, pt: SEOUL_TABLE.pt + d },
|
|
30304
|
+
frame: BANGCHIM_FRAME,
|
|
30305
|
+
blankBetweenTop: false
|
|
30306
|
+
};
|
|
30307
|
+
}
|
|
30308
|
+
function bangchimNumbered(s) {
|
|
30309
|
+
const levels = s.levels.map((l) => ({ ...l }));
|
|
30310
|
+
levels[2] = { ...levels[2], leadTa: 2 };
|
|
30311
|
+
levels[3] = { ...levels[2], leadTa: 4 };
|
|
30312
|
+
return { ...s, levels, marker: (depth, n) => depth >= 2 ? "-" : s.marker(depth, n) };
|
|
30313
|
+
}
|
|
29522
30314
|
function seoulLegalScheme(bodyFont = "\uAD74\uB9BC\uCCB4", bodyPt = 12, lineSp = 160) {
|
|
29523
30315
|
const lv = (leadTa, extra = {}) => ({ font: bodyFont, pt: bodyPt, bold: false, leadTa, ...extra });
|
|
29524
30316
|
return {
|
|
@@ -29563,7 +30355,11 @@ function applySchemeOverrides(s, g3) {
|
|
|
29563
30355
|
function taHu(pt2) {
|
|
29564
30356
|
return Math.round(pt2 * 50);
|
|
29565
30357
|
}
|
|
29566
|
-
function pickScheme(g3, bodyHasBoxMarkers) {
|
|
30358
|
+
function pickScheme(g3, bodyHasBoxMarkers, numbered = false) {
|
|
30359
|
+
if (g3.preset === "bangchim") {
|
|
30360
|
+
const s = seoulBangchimScheme(g3.bodyPtExplicit ? g3.bodyHeight / 100 : 15, g3.lineSpacingExplicit ? g3.lineSpacing : 190);
|
|
30361
|
+
return applySchemeOverrides(numbered ? bangchimNumbered(s) : s, g3);
|
|
30362
|
+
}
|
|
29567
30363
|
if (g3.preset === "ministry") {
|
|
29568
30364
|
return applySchemeOverrides(ministryScheme(g3.bodyPtExplicit ? g3.bodyHeight / 100 : 15, g3.lineSpacingExplicit ? g3.lineSpacing : 145), g3);
|
|
29569
30365
|
}
|
|
@@ -29599,12 +30395,14 @@ var BOX_MARKERS = {
|
|
|
29599
30395
|
"\u2219": 3
|
|
29600
30396
|
};
|
|
29601
30397
|
var LEGAL_RE = /^(\d{1,2}\.|[가-힣]\.|\d{1,2}\)|[가-힣]\)|\(\d{1,2}\)|\([가-힣]\)|[①-⑳]|[㉮-㉻])\s+/u;
|
|
29602
|
-
var KEEP_MARKERS = { "\u21D2": 0, "\u21B3": 1, "\u261E": 0 };
|
|
29603
|
-
var KEEP_RE = /^([
|
|
30398
|
+
var KEEP_MARKERS = { "\u21D2": 0, "\u21B3": 1, "\u261E": 0, "\u25B8": 3 };
|
|
30399
|
+
var KEEP_RE = /^([❶-❿➊-➓⇒↳☞▸])\s*/u;
|
|
29604
30400
|
var BOX_RE = /^([□■❑❏ㅁ○ㅇ◦●❍◎\-–―—ㅡ‣▪▫ㆍ·•∙])\s+/u;
|
|
30401
|
+
var BOX_TIGHT_RE = /^([□■ㅇ○])(?=[「『‘“(([【])/u;
|
|
29605
30402
|
var REF_RE = /^(※|*|\*(?=\s))\s*/u;
|
|
29606
30403
|
var BUNIM_RE = /^붙\s*임(?:\s|:|$)/;
|
|
29607
30404
|
var SOURCE_RE = /^(출처|자료|근거|참고)\s*[::]/;
|
|
30405
|
+
var CHAPTER_LABEL_RE = /^([ⅠⅡⅢⅣⅤⅥⅦⅧⅨⅩⅪⅫ]+|\d{1,2}|[가-하])[.)]\s+/u;
|
|
29608
30406
|
var LAW_CODE_RE = /((?:법률|기본법|특별법|법|령|규칙|조례|규정|고시|훈령|예규|지침)[」』]?)\s*\((\d{5,8})\)/gu;
|
|
29609
30407
|
function stripLawCodes(text) {
|
|
29610
30408
|
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();
|
|
@@ -29620,19 +30418,33 @@ function legalMarkerDepth(marker) {
|
|
|
29620
30418
|
return 7;
|
|
29621
30419
|
}
|
|
29622
30420
|
function parseLeadingMarker(text, keepMarkers = false) {
|
|
29623
|
-
const t = text.replace(/^[\s ]+/, "");
|
|
30421
|
+
const t = text.replace(/^[\s ]+/, "").replace(/^\*\*([□■ㅇ○◦●❍◎▸※-])\s*(?=\S)/u, "$1 **");
|
|
29624
30422
|
const ref = REF_RE.exec(t);
|
|
29625
30423
|
if (ref) return { kind: "ref", depth: 0, marker: ref[1], rest: t.slice(ref[0].length).trim() };
|
|
29626
30424
|
if (keepMarkers) {
|
|
29627
30425
|
const keep = KEEP_RE.exec(t);
|
|
29628
30426
|
if (keep) return { kind: "keep", depth: KEEP_MARKERS[keep[1]] ?? 0, marker: keep[1], rest: t.slice(keep[0].length).trim() };
|
|
29629
30427
|
}
|
|
29630
|
-
const box = BOX_RE.exec(t);
|
|
30428
|
+
const box = BOX_RE.exec(t) ?? BOX_TIGHT_RE.exec(t);
|
|
29631
30429
|
if (box) return { kind: "box", depth: BOX_MARKERS[box[1]] ?? 3, marker: box[1], rest: t.slice(box[0].length).trim() };
|
|
29632
30430
|
const legal = LEGAL_RE.exec(t);
|
|
29633
30431
|
if (legal) return { kind: "legal", depth: legalMarkerDepth(legal[1]), marker: legal[1], rest: t.slice(legal[0].length).trim() };
|
|
29634
30432
|
return { kind: null, depth: 0, marker: "", rest: t };
|
|
29635
30433
|
}
|
|
30434
|
+
function numberClass(marker) {
|
|
30435
|
+
return marker.replace(/\d{1,2}/, "1").replace(/[가-힣]/, "\uAC00").replace(/[①-⑳]/u, "\u2460").replace(/[㉮-㉻]/u, "\u326E");
|
|
30436
|
+
}
|
|
30437
|
+
function numberRanks(blocks) {
|
|
30438
|
+
const order = [];
|
|
30439
|
+
for (const b of blocks) {
|
|
30440
|
+
if (b.type !== "list_item" && b.type !== "paragraph") continue;
|
|
30441
|
+
const lm = parseLeadingMarker((b.text ?? "").trim());
|
|
30442
|
+
if (lm.kind === "box" && lm.depth <= 1) return null;
|
|
30443
|
+
const m = b.type === "list_item" && b.ordered ? b.marker : lm.kind === "legal" ? lm.marker : null;
|
|
30444
|
+
if (m && !order.includes(numberClass(m))) order.push(numberClass(m));
|
|
30445
|
+
}
|
|
30446
|
+
return order.length >= 2 ? new Map(order.map((c, i) => [c, i])) : null;
|
|
30447
|
+
}
|
|
29636
30448
|
function buildOutline(blocks, opts) {
|
|
29637
30449
|
const nodes = [];
|
|
29638
30450
|
let title = null;
|
|
@@ -29653,6 +30465,7 @@ function buildOutline(blocks, opts) {
|
|
|
29653
30465
|
}
|
|
29654
30466
|
};
|
|
29655
30467
|
const keep = !!opts.keepMarkers;
|
|
30468
|
+
const ranks = opts.numbered ? numberRanks(blocks) : null;
|
|
29656
30469
|
for (let i = 0; i < blocks.length; i++) {
|
|
29657
30470
|
const b = blocks[i];
|
|
29658
30471
|
switch (b.type) {
|
|
@@ -29664,7 +30477,8 @@ function buildOutline(blocks, opts) {
|
|
|
29664
30477
|
break;
|
|
29665
30478
|
}
|
|
29666
30479
|
if (lvl <= 2) {
|
|
29667
|
-
|
|
30480
|
+
const label = CHAPTER_LABEL_RE.exec(raw)?.[1];
|
|
30481
|
+
nodes.push({ kind: "chapter", text: stripChapterNumber(raw) || raw, index: ++chapters, ...label ? { label } : {} });
|
|
29668
30482
|
lastExplicitDepth = -1;
|
|
29669
30483
|
listBase = -1;
|
|
29670
30484
|
headingDepth = opts.gaejosik ? -1 : 0;
|
|
@@ -29702,6 +30516,10 @@ function buildOutline(blocks, opts) {
|
|
|
29702
30516
|
nodes.push({ kind: "ref", depth: lastItemDepth + 1, text: lm.rest });
|
|
29703
30517
|
break;
|
|
29704
30518
|
}
|
|
30519
|
+
if (ranks && b.ordered && b.marker) {
|
|
30520
|
+
pushItem(Math.min(ranks.get(numberClass(b.marker)) ?? 0, 7), text, void 0, false, b.marker);
|
|
30521
|
+
break;
|
|
30522
|
+
}
|
|
29705
30523
|
if (lm.kind === "box") {
|
|
29706
30524
|
hasBoxMarkers = true;
|
|
29707
30525
|
pushItem(lm.depth, lm.rest);
|
|
@@ -29716,7 +30534,7 @@ function buildOutline(blocks, opts) {
|
|
|
29716
30534
|
break;
|
|
29717
30535
|
}
|
|
29718
30536
|
let base = headingDepth >= 0 ? headingDepth + 1 : 0;
|
|
29719
|
-
if (opts.headingFrames) {
|
|
30537
|
+
if (opts.headingFrames || opts.listUnderMarker) {
|
|
29720
30538
|
if (listBase < 0) listBase = lastExplicitDepth + 1;
|
|
29721
30539
|
base = listBase;
|
|
29722
30540
|
}
|
|
@@ -29724,7 +30542,7 @@ function buildOutline(blocks, opts) {
|
|
|
29724
30542
|
break;
|
|
29725
30543
|
}
|
|
29726
30544
|
case "paragraph": {
|
|
29727
|
-
const text = stripLawCodes((b.text ?? "").trim());
|
|
30545
|
+
const text = stripLawCodes((b.text ?? "").trim()).replace(/^\*\*(붙\s*임[^*]*)\*\*$/, "$1");
|
|
29728
30546
|
if (!text) break;
|
|
29729
30547
|
if (BUNIM_RE.test(text) || nodes.length && nodes[nodes.length - 1].kind === "attach" && /^\s/.test(b.text ?? "")) {
|
|
29730
30548
|
nodes.push({ kind: "attach", text: b.text ?? "" });
|
|
@@ -29759,7 +30577,8 @@ function buildOutline(blocks, opts) {
|
|
|
29759
30577
|
break;
|
|
29760
30578
|
}
|
|
29761
30579
|
if (lm.kind === "legal") {
|
|
29762
|
-
if (
|
|
30580
|
+
if (ranks) pushItem(Math.min(ranks.get(numberClass(lm.marker)) ?? 0, 7), lm.rest, void 0, false, lm.marker);
|
|
30581
|
+
else if (opts.gaejosik) pushItem(Math.max(lastItemDepth, 0), lm.rest, lm.marker);
|
|
29763
30582
|
else pushItem(lm.depth, lm.rest);
|
|
29764
30583
|
break;
|
|
29765
30584
|
}
|
|
@@ -29781,104 +30600,7 @@ function buildOutline(blocks, opts) {
|
|
|
29781
30600
|
seenBody = true;
|
|
29782
30601
|
}
|
|
29783
30602
|
}
|
|
29784
|
-
return { title, nodes, hasBoxMarkers, chapters };
|
|
29785
|
-
}
|
|
29786
|
-
|
|
29787
|
-
// src/hwpx/fit-line.ts
|
|
29788
|
-
var RATIOS = [100, 97, 95, 92, 90, 87, 85];
|
|
29789
|
-
var SPACINGS = [0, -3, -5];
|
|
29790
|
-
function safety(faceClass, approx) {
|
|
29791
|
-
return faceClass.startsWith("font:") ? 0.98 : approx;
|
|
29792
|
-
}
|
|
29793
|
-
function fitOneLine(text, font, pt2, availHu, minPt = Math.max(pt2 - 3, 10), minRatio = 85) {
|
|
29794
|
-
const faceClass = faceClassForGen(font);
|
|
29795
|
-
const avail = availHu * safety(faceClass, 0.98);
|
|
29796
|
-
const fits = (p, r, s) => measureTextWidth(text, p * 100, r, { spacingPct: s, faceClass }) <= avail;
|
|
29797
|
-
if (fits(pt2, 100, 0)) return { pt: pt2, ratio: 100, spacing: 0, overflow: false };
|
|
29798
|
-
const ratios = RATIOS.filter((r) => r >= minRatio);
|
|
29799
|
-
for (let p = pt2; p >= minPt; p--) {
|
|
29800
|
-
for (const r of ratios) for (const s of SPACINGS) {
|
|
29801
|
-
if (fits(p, r, s)) return { pt: p, ratio: r, spacing: s, overflow: false };
|
|
29802
|
-
}
|
|
29803
|
-
}
|
|
29804
|
-
return { pt: minPt, ratio: ratios[ratios.length - 1], spacing: SPACINGS[SPACINGS.length - 1], overflow: true };
|
|
29805
|
-
}
|
|
29806
|
-
var SQUEEZE = (() => {
|
|
29807
|
-
const out = [];
|
|
29808
|
-
for (let sp = -1; sp >= -12; sp--) out.push([100, sp]);
|
|
29809
|
-
for (const r of [97, 95, 92, 90, 88]) for (let sp = 0; sp >= -12; sp--) out.push([r, sp]);
|
|
29810
|
-
return out;
|
|
29811
|
-
})();
|
|
29812
|
-
var LOOSE_LIMIT = 1;
|
|
29813
|
-
var PULL_TAIL = 0.5;
|
|
29814
|
-
var PULL_MAX = 0.15;
|
|
29815
|
-
var squeezeOf = (r, sp) => 1 - r / 100 * (1 + sp / 100);
|
|
29816
|
-
var ORPHAN_RATIO = 0.22;
|
|
29817
|
-
var ORPHAN_COST = 1.5;
|
|
29818
|
-
var COMPRESS_COST = 10;
|
|
29819
|
-
function worstLooseness(text, starts, firstW, contW, h, ratio, spacing, faceClass) {
|
|
29820
|
-
const spaceW = 0.5 * h * (ratio / 100) * (1 + spacing / 100);
|
|
29821
|
-
let worst = 0;
|
|
29822
|
-
for (let i = 0; i + 1 < starts.length; i++) {
|
|
29823
|
-
const seg = text.slice(starts[i], starts[i + 1]).replace(/ +$/, "");
|
|
29824
|
-
const slack = (i === 0 ? firstW : contW) - measureTextWidth(seg, h, ratio, { faceClass, spacingPct: spacing });
|
|
29825
|
-
const spaces = (seg.match(/ /g) ?? []).length;
|
|
29826
|
-
worst = Math.max(worst, slack / (Math.max(spaces, 1) * spaceW));
|
|
29827
|
-
}
|
|
29828
|
-
return worst;
|
|
29829
|
-
}
|
|
29830
|
-
function fitParagraph(text, font, pt2, firstW, contW, minRatio = 88, orphanRatio = ORPHAN_RATIO) {
|
|
29831
|
-
const faceClass = faceClassForGen(font);
|
|
29832
|
-
const h = pt2 * 100;
|
|
29833
|
-
const k = safety(faceClass, 0.95);
|
|
29834
|
-
const f = firstW * k, c = contW * k;
|
|
29835
|
-
const ladder = [[100, 0], ...SQUEEZE.filter(([r]) => r >= minRatio)];
|
|
29836
|
-
const base = simulateWrap(text, f, c, h, 100, "keep", { faceClass });
|
|
29837
|
-
if (base.lines < 2) return null;
|
|
29838
|
-
if (base.lastLineWidth <= c * PULL_TAIL) {
|
|
29839
|
-
let pull = null;
|
|
29840
|
-
for (const [r, sp] of ladder) {
|
|
29841
|
-
const amt = squeezeOf(r, sp);
|
|
29842
|
-
if (amt > PULL_MAX + 1e-9 || pull && amt >= pull.amt - 1e-9) continue;
|
|
29843
|
-
const w = simulateWrap(text, f, c, h, r, "keep", { faceClass, spacingPct: sp });
|
|
29844
|
-
if (w.lines < base.lines && worstLooseness(text, w.starts, f, c, h, r, sp, faceClass) <= LOOSE_LIMIT) pull = { r, sp, amt };
|
|
29845
|
-
}
|
|
29846
|
-
if (pull) return { ratio: pull.r, spacing: pull.sp };
|
|
29847
|
-
}
|
|
29848
|
-
let baseLines = 0;
|
|
29849
|
-
let best = null;
|
|
29850
|
-
for (const [r, sp] of ladder) {
|
|
29851
|
-
const w = simulateWrap(text, f, c, h, r, "keep", { faceClass, spacingPct: sp });
|
|
29852
|
-
if (!best) {
|
|
29853
|
-
if (w.lines < 2) return null;
|
|
29854
|
-
baseLines = w.lines;
|
|
29855
|
-
}
|
|
29856
|
-
if (w.lines > baseLines) continue;
|
|
29857
|
-
const orphan = w.lines > 1 && w.lastLineWidth <= contW * orphanRatio;
|
|
29858
|
-
const loose = worstLooseness(text, w.starts, f, c, h, r, sp, faceClass);
|
|
29859
|
-
const cost = Math.max(0, loose - LOOSE_LIMIT) + (orphan ? ORPHAN_COST : 0) + squeezeOf(r, sp) * COMPRESS_COST;
|
|
29860
|
-
if (!best || cost < best.cost - 1e-9) best = { r, sp, cost };
|
|
29861
|
-
}
|
|
29862
|
-
return best && (best.r !== 100 || best.sp !== 0) ? { ratio: best.r, spacing: best.sp } : null;
|
|
29863
|
-
}
|
|
29864
|
-
var LIST_BREAK = /* @__PURE__ */ new Set([..."\xB7,\u3001/"]);
|
|
29865
|
-
function fitCharBreaks(text, font, pt2, firstW, contW, minRatio = 88) {
|
|
29866
|
-
const faceClass = faceClassForGen(font);
|
|
29867
|
-
const h = pt2 * 100;
|
|
29868
|
-
const k = safety(faceClass, 0.95);
|
|
29869
|
-
if (!text.split(/ +/).some((w) => measureTextWidth(w, h, 100, { faceClass }) > contW * k)) return null;
|
|
29870
|
-
let best = null;
|
|
29871
|
-
for (const [r, sp] of [[100, 0], ...SQUEEZE.filter(([r2]) => r2 >= minRatio)]) {
|
|
29872
|
-
const amt = squeezeOf(r, sp);
|
|
29873
|
-
if (amt > PULL_MAX + 1e-9 || best && amt >= best.amt - 1e-9) continue;
|
|
29874
|
-
const wrapAt = (s) => simulateWrap(text, firstW * s, contW * s, h, r, "charAll", { faceClass, spacingPct: sp }).starts;
|
|
29875
|
-
const starts = wrapAt(1);
|
|
29876
|
-
if (wrapAt(0.99).join() !== starts.join() || wrapAt(1.01).join() !== starts.join()) continue;
|
|
29877
|
-
if (!starts.slice(1).every((s) => text[s - 1] === " " || LIST_BREAK.has(text[s - 1]))) continue;
|
|
29878
|
-
if (worstLooseness(text, starts, firstW, contW, h, r, sp, faceClass) > LOOSE_LIMIT) continue;
|
|
29879
|
-
best = { r, sp, amt };
|
|
29880
|
-
}
|
|
29881
|
-
return best ? { ratio: best.r, spacing: best.sp } : null;
|
|
30603
|
+
return { title, nodes, hasBoxMarkers, numbered: !!ranks, chapters };
|
|
29882
30604
|
}
|
|
29883
30605
|
|
|
29884
30606
|
// src/hwpx/gen-marker.ts
|
|
@@ -29938,296 +30660,6 @@ function polishGongmunBlock(b) {
|
|
|
29938
30660
|
return b;
|
|
29939
30661
|
}
|
|
29940
30662
|
|
|
29941
|
-
// src/hwpx/gen-frame-seoul.ts
|
|
29942
|
-
var frameTableId = EXTRA_TABLE_ID_BASE + 500;
|
|
29943
|
-
function resetFrameTableIds() {
|
|
29944
|
-
frameTableId = EXTRA_TABLE_ID_BASE + 500;
|
|
29945
|
-
}
|
|
29946
|
-
var BF_NONE = 1;
|
|
29947
|
-
function ftbl(rows, w, h, cols, opts = {}) {
|
|
29948
|
-
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>`;
|
|
29949
|
-
}
|
|
29950
|
-
function host(tableXml2, paraPrId, charPrId, extra = "") {
|
|
29951
|
-
return `<hp:p paraPrIDRef="${paraPrId}" styleIDRef="0"${extra}><hp:run charPrIDRef="${charPrId}">${tableXml2}</hp:run></hp:p>`;
|
|
29952
|
-
}
|
|
29953
|
-
function scale(cols, w) {
|
|
29954
|
-
const sum = cols.reduce((a, b) => a + b, 0);
|
|
29955
|
-
const out = cols.map((c) => Math.round(c / sum * w));
|
|
29956
|
-
out[out.length - 1] += w - out.reduce((a, b) => a + b, 0);
|
|
29957
|
-
return out;
|
|
29958
|
-
}
|
|
29959
|
-
function spacedOrgName(org) {
|
|
29960
|
-
const t = org.trim();
|
|
29961
|
-
if (/\s/.test(t) || [...t].length > 6) return t;
|
|
29962
|
-
return [...t].join(" ");
|
|
29963
|
-
}
|
|
29964
|
-
function buildDocHeadTable(h, ctx) {
|
|
29965
|
-
const { reg, bf, frame } = ctx;
|
|
29966
|
-
const w = ctx.W - 366;
|
|
29967
|
-
const [cLabel, cValue, cPad] = scale([6949, 35068, 7223], w);
|
|
29968
|
-
const center = reg.para({ align: "CENTER", lineSp: 160 });
|
|
29969
|
-
const left = reg.para({ align: "LEFT", lineSp: 160 });
|
|
29970
|
-
const c10 = reg.char({ font: frame.font, pt: 10 });
|
|
29971
|
-
const c10b = reg.char({ font: frame.font, pt: 10, bold: true });
|
|
29972
|
-
const c20b = reg.char({ font: frame.font, pt: 20, bold: true });
|
|
29973
|
-
const c14 = reg.char({ font: frame.font, pt: 14 });
|
|
29974
|
-
const bottomLine = bf.get({ t: "none", b: "thin", l: "none", r: "none" });
|
|
29975
|
-
const rows = [];
|
|
29976
|
-
let r = 0;
|
|
29977
|
-
if (h.slogan) {
|
|
29978
|
-
rows.push(tc({ bf: BF_NONE, row: r++, col: 0, w, h: 2416, colSpan: 3, paras: para(h.slogan, center, c10) }));
|
|
29979
|
-
}
|
|
29980
|
-
rows.push(
|
|
29981
|
-
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) })
|
|
29982
|
-
);
|
|
29983
|
-
r++;
|
|
29984
|
-
rows.push(tc({ bf: BF_NONE, row: r++, col: 0, w, h: 1563, colSpan: 3, paras: para("", left, c10b) }));
|
|
29985
|
-
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) });
|
|
29986
|
-
rows.push(labeled(" \uC218\uC2E0 ", h.to ?? "", 2097, BF_NONE));
|
|
29987
|
-
r++;
|
|
29988
|
-
rows.push(labeled("(\uACBD\uC720)", h.via ?? "", 2380, BF_NONE));
|
|
29989
|
-
r++;
|
|
29990
|
-
rows.push(labeled(" \uC81C\uBAA9 ", h.title ?? "", 2248, bottomLine));
|
|
29991
|
-
r++;
|
|
29992
|
-
const height = rows.length === 6 ? 16312 : 13896;
|
|
29993
|
-
return host(ftbl(rows, w, height, 3, { bottomGap: 852 }), reg.para({ align: "LEFT", lineSp: 100 }), c10);
|
|
29994
|
-
}
|
|
29995
|
-
function splitTitleName(s) {
|
|
29996
|
-
const t = s.trim();
|
|
29997
|
-
const i = t.lastIndexOf(" ");
|
|
29998
|
-
return i > 0 ? { title: t.slice(0, i).trim(), name: t.slice(i + 1).trim() } : { title: "", name: t };
|
|
29999
|
-
}
|
|
30000
|
-
function buildDocFootTable(f, ctx, internal) {
|
|
30001
|
-
const { reg, bf, frame } = ctx;
|
|
30002
|
-
const w = ctx.W - 86;
|
|
30003
|
-
const GRID = 48;
|
|
30004
|
-
const unit = Math.floor(w / GRID);
|
|
30005
|
-
const c10 = reg.char({ font: frame.font, pt: 10 });
|
|
30006
|
-
const c11b = reg.char({ font: frame.font, pt: 11, bold: true });
|
|
30007
|
-
const c9b = reg.char({ font: frame.font, pt: 9, bold: true });
|
|
30008
|
-
const c18b = reg.char({ font: frame.font, pt: 18, bold: true });
|
|
30009
|
-
const c3 = reg.char({ font: frame.font, pt: 3 });
|
|
30010
|
-
const center = reg.para({ align: "CENTER", lineSp: 100 });
|
|
30011
|
-
const left = reg.para({ align: "LEFT", lineSp: 100 });
|
|
30012
|
-
const gray = bf.get({ t: "none", b: "none", l: "none", r: "none", fill: "#CCCCCC" });
|
|
30013
|
-
const spans = (widths) => {
|
|
30014
|
-
const sum = widths.reduce((a, b) => a + b, 0);
|
|
30015
|
-
const out = widths.map((x) => Math.max(1, Math.round(x / sum * GRID)));
|
|
30016
|
-
let diff = GRID - out.reduce((a, b) => a + b, 0);
|
|
30017
|
-
for (let i = out.length - 1; diff !== 0 && i >= 0; i = (i - 1 + out.length) % out.length) {
|
|
30018
|
-
if (diff > 0) {
|
|
30019
|
-
out[i]++;
|
|
30020
|
-
diff--;
|
|
30021
|
-
} else if (out[i] > 1) {
|
|
30022
|
-
out[i]--;
|
|
30023
|
-
diff++;
|
|
30024
|
-
}
|
|
30025
|
-
}
|
|
30026
|
-
return out;
|
|
30027
|
-
};
|
|
30028
|
-
const rows = [];
|
|
30029
|
-
let r = 0;
|
|
30030
|
-
const row = (h, specs) => {
|
|
30031
|
-
let col = 0;
|
|
30032
|
-
let xml = "";
|
|
30033
|
-
specs.forEach((sp, i) => {
|
|
30034
|
-
const last = i === specs.length - 1;
|
|
30035
|
-
const span2 = last ? GRID - col : sp.span;
|
|
30036
|
-
const cw = last ? w - col * unit : span2 * unit;
|
|
30037
|
-
xml += tc({ bf: sp.bf ?? BF_NONE, row: r, col, w: cw, h, colSpan: span2, rowSpan: sp.rowSpan, vAlign: sp.vAlign, paras: para(sp.text, sp.pp ?? left, sp.cp ?? c10) });
|
|
30038
|
-
col += span2;
|
|
30039
|
-
});
|
|
30040
|
-
rows.push(xml);
|
|
30041
|
-
r++;
|
|
30042
|
-
};
|
|
30043
|
-
{
|
|
30044
|
-
const [a, b, c] = spans([7981, 32099, 9440]);
|
|
30045
|
-
row(4042, [{ span: a, text: "" }, { span: b, text: internal ? "" : f.sender ?? "", cp: c18b, pp: center }, { span: c, text: "" }]);
|
|
30046
|
-
}
|
|
30047
|
-
if (f.recipients) {
|
|
30048
|
-
const [a] = spans([4843, 44677]);
|
|
30049
|
-
row(2880, [{ span: a, text: "\uC218\uC2E0\uC790", vAlign: "TOP" }, { span: GRID - a, text: f.recipients, vAlign: "TOP" }]);
|
|
30050
|
-
} else {
|
|
30051
|
-
row(1280, [{ span: GRID, text: "" }]);
|
|
30052
|
-
}
|
|
30053
|
-
row(1146, [{ span: GRID, text: "", cp: c3, pp: center, bf: gray }]);
|
|
30054
|
-
{
|
|
30055
|
-
const n = Math.max(f.approvers.length, 1);
|
|
30056
|
-
const unitW = [6909, 4625, 983];
|
|
30057
|
-
const titleW = (t) => Math.max(unitW[0], Math.round(measureTextWidth(t, 1e3, 100, { faceClass: "fixedPitch" })) + 280 + unit + 300);
|
|
30058
|
-
const widths = [];
|
|
30059
|
-
for (let i = 0; i < n; i++) {
|
|
30060
|
-
widths.push(titleW(f.approvers[i]?.title ?? ""), unitW[1]);
|
|
30061
|
-
if (i < n - 1) widths.push(unitW[2]);
|
|
30062
|
-
}
|
|
30063
|
-
const used = widths.reduce((a, b) => a + b, 0);
|
|
30064
|
-
if (used < w) widths.push(w - used);
|
|
30065
|
-
const sp = spans(widths);
|
|
30066
|
-
const top = [], bottom = [];
|
|
30067
|
-
let k = 0;
|
|
30068
|
-
for (let i = 0; i < n; i++) {
|
|
30069
|
-
const ap = f.approvers[i] ?? { title: "", name: "" };
|
|
30070
|
-
top.push({ span: sp[k++], text: ap.title, pp: center, rowSpan: 2 });
|
|
30071
|
-
const signSpan = sp[k++];
|
|
30072
|
-
top.push({ span: signSpan, text: ap.date ?? "", cp: c9b, pp: center });
|
|
30073
|
-
bottom.push({ span: signSpan, text: ap.name, cp: c11b, pp: center });
|
|
30074
|
-
if (i < n - 1) top.push({ span: sp[k++], text: "", rowSpan: 2 });
|
|
30075
|
-
}
|
|
30076
|
-
if (k < sp.length) top.push({ span: sp[k], text: "", rowSpan: 2 });
|
|
30077
|
-
let col = 0, xmlTop = "", xmlBottom = "";
|
|
30078
|
-
let bi = 0;
|
|
30079
|
-
top.forEach((s, i) => {
|
|
30080
|
-
const last = i === top.length - 1;
|
|
30081
|
-
const span2 = last ? GRID - col : s.span;
|
|
30082
|
-
const cw = last ? w - col * unit : span2 * unit;
|
|
30083
|
-
xmlTop += tc({ bf: BF_NONE, row: r, col, w: cw, h: s.rowSpan ? 2198 : 1100, colSpan: span2, rowSpan: s.rowSpan, paras: para(s.text, s.pp ?? left, s.cp ?? c10) });
|
|
30084
|
-
if (!s.rowSpan) {
|
|
30085
|
-
const b = bottom[bi++];
|
|
30086
|
-
xmlBottom += tc({ bf: BF_NONE, row: r + 1, col, w: cw, h: 1098, colSpan: span2, paras: para(b.text, b.pp ?? center, b.cp ?? c11b) });
|
|
30087
|
-
}
|
|
30088
|
-
col += span2;
|
|
30089
|
-
});
|
|
30090
|
-
rows.push(xmlTop, xmlBottom);
|
|
30091
|
-
r += 2;
|
|
30092
|
-
}
|
|
30093
|
-
{
|
|
30094
|
-
const [a] = spans([4455, 45065]);
|
|
30095
|
-
const coop = f.cooperators.map((c) => `${c.title} ${c.name}`.trim()).join(" ");
|
|
30096
|
-
row(2700, [{ span: a, text: "\uD611\uC870\uC790" }, { span: GRID - a, text: coop }]);
|
|
30097
|
-
}
|
|
30098
|
-
row(1400, [{ span: GRID, text: "" }]);
|
|
30099
|
-
{
|
|
30100
|
-
const [a, b, c] = spans([3340, 21532, 3301, 21347]);
|
|
30101
|
-
row(2280, [{ span: a, text: "\uC2DC\uD589" }, { span: b, text: f.docNum ?? "" }, { span: c, text: "\uC811\uC218" }, { span: 0, text: f.receive ?? "" }]);
|
|
30102
|
-
}
|
|
30103
|
-
{
|
|
30104
|
-
const [a, b, c, d] = spans([1597, 3902, 21934, 761, 21326]);
|
|
30105
|
-
row(1280, [{ span: a, text: "\uC6B0" }, { span: b, text: f.zip ?? "" }, { span: c, text: f.address ?? "" }, { span: d, text: "/" }, { span: 0, text: f.site ?? "" }]);
|
|
30106
|
-
}
|
|
30107
|
-
{
|
|
30108
|
-
const sp = spans([2750, 8006, 3196, 8311, 899, 14327, 792, 11239]);
|
|
30109
|
-
const vals = ["\uC804\uD654", f.phone ?? "", "/\uC804\uC1A1", f.fax ?? "", "/", f.email ?? "", "/", f.disclosure ?? ""];
|
|
30110
|
-
row(2e3, vals.map((v, i) => ({ span: sp[i], text: v })));
|
|
30111
|
-
}
|
|
30112
|
-
row(1e3, [{ span: GRID, text: "" }]);
|
|
30113
|
-
const height = rows.length * 1500;
|
|
30114
|
-
return host(ftbl(rows, w, height, GRID), reg.para({ align: "LEFT", lineSp: 100 }), c10);
|
|
30115
|
-
}
|
|
30116
|
-
function buildReportTitleTable(title, contact, ctx) {
|
|
30117
|
-
const { reg, bf, frame } = ctx;
|
|
30118
|
-
const w = ctx.W - 566;
|
|
30119
|
-
const fit = fitOneLine(title, frame.titleFont, frame.titlePt, w - 2 * 140 - 400, 20);
|
|
30120
|
-
const cTitle = reg.char({ font: frame.titleFont, pt: fit.pt, bold: true, ratio: fit.ratio, spacing: fit.spacing });
|
|
30121
|
-
const center = reg.para({ align: "CENTER", lineSp: 130 });
|
|
30122
|
-
const rows = [];
|
|
30123
|
-
const titleBf = bf.get({ t: "thick", b: contact ? "mid" : "thick", l: "none", r: "none" });
|
|
30124
|
-
rows.push(tc({ bf: titleBf, row: 0, col: 0, w, h: 4084, paras: para(title, center, cTitle), name: "__kordoc_h1" }));
|
|
30125
|
-
let h = 4084;
|
|
30126
|
-
if (contact) {
|
|
30127
|
-
const cContact = reg.char({ font: frame.contactFont, pt: frame.contactPt });
|
|
30128
|
-
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) }));
|
|
30129
|
-
h += 1943;
|
|
30130
|
-
}
|
|
30131
|
-
return { xml: host(ftbl(rows, w, h, 1, { bottomGap: 600 }), reg.para({ align: "CENTER", lineSp: 100 }), cTitle), overflow: fit.overflow };
|
|
30132
|
-
}
|
|
30133
|
-
var CHAPTER_BAND_DEFAULT = { color: "#003366", textColor: "#FFFFFF", numFont: "HY\uD5E4\uB4DC\uB77C\uC778M", numPt: 17 };
|
|
30134
|
-
function buildChapterBand(roman, title, ctx, spec, before2) {
|
|
30135
|
-
const { reg, bf } = ctx;
|
|
30136
|
-
const w = ctx.W;
|
|
30137
|
-
const numW = 3100, gap = 400, h = 2600;
|
|
30138
|
-
const titleW = w - numW - gap;
|
|
30139
|
-
const fit = fitOneLine(title, spec.titleFont, spec.titlePt, titleW - 700, spec.titlePt - 3);
|
|
30140
|
-
const numBf = bf.get({ t: "none", b: "none", l: "none", r: "none", fill: spec.color });
|
|
30141
|
-
const gapBf = bf.get({ t: "none", b: "none", l: "none", r: "none" });
|
|
30142
|
-
const titleBf = bf.get({ t: "none", b: "thick", l: "none", r: "none" });
|
|
30143
|
-
const cNum = reg.char({ font: spec.numFont, pt: spec.numPt, bold: true, color: spec.textColor });
|
|
30144
|
-
const cTitle = reg.char({ font: spec.titleFont, pt: fit.pt, bold: true, ratio: fit.ratio, spacing: fit.spacing });
|
|
30145
|
-
const center = reg.para({ align: "CENTER", lineSp: 130 });
|
|
30146
|
-
const left = reg.para({ align: "LEFT", lineSp: 130, left: 300 });
|
|
30147
|
-
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" });
|
|
30148
|
-
return { xml: host(ftbl([row], w, h, 3, { bottomGap: 1e3 }), reg.para({ align: "LEFT", lineSp: 100, before: before2, keepWithNext: true }), cTitle), overflow: fit.overflow };
|
|
30149
|
-
}
|
|
30150
|
-
var SUMMARY_PAD = 1e3;
|
|
30151
|
-
function buildSummaryBox(text, ctx) {
|
|
30152
|
-
const { reg, bf, frame } = ctx;
|
|
30153
|
-
const w = ctx.W - 566;
|
|
30154
|
-
const avail = w - 280 - SUMMARY_PAD * 2;
|
|
30155
|
-
const p = reg.para({ align: "JUSTIFY", lineSp: 160, left: SUMMARY_PAD, right: SUMMARY_PAD });
|
|
30156
|
-
const bodies = text.split("\n").map((l) => l.trim().replace(/^[□■○ㅇ◦●\-–ㆍ·•]\s*/u, "")).filter(Boolean);
|
|
30157
|
-
let lines = 0;
|
|
30158
|
-
const paras = bodies.map((b) => {
|
|
30159
|
-
const f = fitParagraph(b, frame.summaryFont, frame.summaryPt, avail, avail);
|
|
30160
|
-
const c = reg.char({ font: frame.summaryFont, pt: frame.summaryPt, bold: true, ratio: f?.ratio ?? 100, spacing: f?.spacing ?? 0 });
|
|
30161
|
-
lines += simulateWrap(b, avail * 0.995, avail * 0.995, frame.summaryPt * 100, f?.ratio ?? 100, "keep", { faceClass: faceClassForGen(frame.summaryFont), spacingPct: f?.spacing ?? 0 }).lines;
|
|
30162
|
-
return para(b, p, c);
|
|
30163
|
-
});
|
|
30164
|
-
const h = lines * Math.round(frame.summaryPt * 100 * 1.6) + 280 + 600;
|
|
30165
|
-
const box = bf.get({ t: "thick", b: "thick", l: "thick", r: "thick", fill: frame.summaryFill });
|
|
30166
|
-
const row = tc({ bf: box, row: 0, col: 0, w, h, paras: paras.join(""), name: "__kordoc_summary" });
|
|
30167
|
-
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 };
|
|
30168
|
-
}
|
|
30169
|
-
function buildApprovalSeoul(labels, names, ctx) {
|
|
30170
|
-
const { reg, bf } = ctx;
|
|
30171
|
-
const n = labels.length;
|
|
30172
|
-
const colW = Math.min(8600, Math.floor(ctx.W * 0.55 / n));
|
|
30173
|
-
const w = colW * n;
|
|
30174
|
-
const c12 = reg.char({ font: "\uD55C\uCEF4\uB3CB\uC6C0", pt: 12 });
|
|
30175
|
-
const c12b = reg.char({ font: "\uD55C\uCEF4\uB3CB\uC6C0", pt: 12, bold: true });
|
|
30176
|
-
const center = reg.para({ align: "CENTER", lineSp: 100 });
|
|
30177
|
-
const edge = (row, col) => bf.get({
|
|
30178
|
-
t: row === 0 ? "thick" : "thin",
|
|
30179
|
-
b: row === 1 ? "thick" : "thin",
|
|
30180
|
-
l: col === 0 ? "thick" : "thin",
|
|
30181
|
-
r: col === n - 1 ? "thick" : "thin"
|
|
30182
|
-
});
|
|
30183
|
-
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("");
|
|
30184
|
-
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("");
|
|
30185
|
-
return host(ftbl([top, sign], w, 5965, n, { bottomGap: 600 }), reg.para({ align: "RIGHT", lineSp: 100 }), c12);
|
|
30186
|
-
}
|
|
30187
|
-
function buildReportCover(inp, ctx) {
|
|
30188
|
-
const { reg, bf, frame } = ctx;
|
|
30189
|
-
const out = [];
|
|
30190
|
-
const c12 = reg.char({ font: "\uD55C\uCEF4\uB3CB\uC6C0", pt: 12 });
|
|
30191
|
-
const c12b = reg.char({ font: "\uD55C\uCEF4\uB3CB\uC6C0", pt: 12, bold: true });
|
|
30192
|
-
const center = reg.para({ align: "CENTER", lineSp: 100 });
|
|
30193
|
-
const blank2 = (pt2 = 15) => para("", reg.para({ align: "LEFT", lineSp: 160 }), reg.char({ font: "\uD55C\uCEF4\uB3CB\uC6C0", pt: pt2 }));
|
|
30194
|
-
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 ?? ""]];
|
|
30195
|
-
const infoW = Math.round(ctx.W * 0.36);
|
|
30196
|
-
const [lw, vw] = scale([4200, 12800], infoW);
|
|
30197
|
-
const infoXml = ftbl(infoRows.map(
|
|
30198
|
-
([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) })
|
|
30199
|
-
), infoW, 1850 * 4, 2);
|
|
30200
|
-
let line = infoXml;
|
|
30201
|
-
if (inp.approval && inp.approval.length) {
|
|
30202
|
-
const n = inp.approval.length;
|
|
30203
|
-
const colW = Math.min(7600, Math.floor(ctx.W * 0.5 / n));
|
|
30204
|
-
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" });
|
|
30205
|
-
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("");
|
|
30206
|
-
const sign = inp.approval.map((_, c) => tc({ bf: edge(1, c), row: 1, col: c, w: colW, h: 3600, paras: para("", center, c12b) })).join("");
|
|
30207
|
-
line += `</hp:run><hp:run charPrIDRef="${c12}"><hp:t> </hp:t></hp:run><hp:run charPrIDRef="${c12}">` + ftbl([top, sign], colW * n, 5365, n);
|
|
30208
|
-
}
|
|
30209
|
-
out.push(`<hp:p paraPrIDRef="${reg.para({ align: "DISTRIBUTE", lineSp: 100 })}" styleIDRef="0"><hp:run charPrIDRef="${c12}">${line}</hp:run></hp:p>`);
|
|
30210
|
-
for (let i = 0; i < 4; i++) out.push(blank2(20));
|
|
30211
|
-
const w = ctx.W - 1200;
|
|
30212
|
-
const bar = bf.get({ t: "none", b: "none", l: "none", r: "none", fill: "#1F2FD6" });
|
|
30213
|
-
const fit = fitOneLine(inp.title, frame.titleFont, 27, w - 800, 22);
|
|
30214
|
-
const cT = reg.char({ font: frame.titleFont, pt: fit.pt, bold: true, ratio: fit.ratio, spacing: fit.spacing });
|
|
30215
|
-
const c1 = reg.char({ font: "\uD55C\uCEF4\uB3CB\uC6C0", pt: 1 });
|
|
30216
|
-
const barP = reg.para({ align: "CENTER", lineSp: 70 });
|
|
30217
|
-
const rows = [
|
|
30218
|
-
tc({ bf: bar, row: 0, col: 0, w, h: 200, paras: para("", barP, c1) }),
|
|
30219
|
-
tc({ bf: BF_NONE, row: 1, col: 0, w, h: 6e3, paras: para(inp.title, center, cT), name: "__kordoc_skip" }),
|
|
30220
|
-
tc({ bf: bar, row: 2, col: 0, w, h: 200, paras: para("", barP, c1) })
|
|
30221
|
-
];
|
|
30222
|
-
out.push(host(ftbl(rows, w, 6400, 1), reg.para({ align: "CENTER", lineSp: 100 }), cT));
|
|
30223
|
-
for (let i = 0; i < 2; i++) out.push(blank2(20));
|
|
30224
|
-
out.push(para(inp.date, center, reg.char({ font: frame.titleFont, pt: 22, bold: true })));
|
|
30225
|
-
for (let i = 0; i < 8; i++) out.push(blank2(20));
|
|
30226
|
-
if (inp.org) out.push(para(spacedOrgName(inp.org), center, reg.char({ font: frame.titleFont, pt: 24, bold: true })));
|
|
30227
|
-
if (inp.dept) out.push(para(`(${inp.dept.replace(/^\(|\)$/g, "")})`, center, reg.char({ font: "\uD55C\uCEF4\uB3CB\uC6C0", pt: 22, bold: true })));
|
|
30228
|
-
return out;
|
|
30229
|
-
}
|
|
30230
|
-
|
|
30231
30663
|
// src/hwpx/gen-frame-ministry.ts
|
|
30232
30664
|
var MINISTRY = {
|
|
30233
30665
|
blue: "#0000FF",
|
|
@@ -30447,7 +30879,7 @@ function isAttachHeading(text) {
|
|
|
30447
30879
|
}
|
|
30448
30880
|
|
|
30449
30881
|
// src/hwpx/gen-gongmun.ts
|
|
30450
|
-
var V5_PRESETS = /* @__PURE__ */ new Set(["official", "report", "plan", "notice", "minutes", "ministry"]);
|
|
30882
|
+
var V5_PRESETS = /* @__PURE__ */ new Set(["official", "report", "plan", "notice", "minutes", "ministry", "bangchim"]);
|
|
30451
30883
|
function usesV5Engine(preset) {
|
|
30452
30884
|
return V5_PRESETS.has(preset);
|
|
30453
30885
|
}
|
|
@@ -30465,21 +30897,37 @@ function buildGongmunSectionV5(blocks, gongmun, deps, theme) {
|
|
|
30465
30897
|
const warnings = [];
|
|
30466
30898
|
const g3 = gongmun;
|
|
30467
30899
|
const preset = g3.preset;
|
|
30468
|
-
const
|
|
30900
|
+
const isBangchim = preset === "bangchim";
|
|
30901
|
+
const isReport = preset === "report" || preset === "plan" || isBangchim;
|
|
30469
30902
|
const isMinistry = preset === "ministry";
|
|
30470
|
-
const
|
|
30471
|
-
|
|
30472
|
-
|
|
30903
|
+
const W2 = mmToHwpunit(210 - g3.margins.left - g3.margins.right);
|
|
30904
|
+
let bangchimSub = null, bangchimContact = null;
|
|
30905
|
+
if (isBangchim) {
|
|
30906
|
+
const h1 = blocks.findIndex((b) => b.type === "heading" && (b.level ?? 1) === 1);
|
|
30907
|
+
let k = h1 + 1;
|
|
30908
|
+
while (h1 >= 0 && k < blocks.length && k <= h1 + 2) {
|
|
30909
|
+
const b = blocks[k], t = (b.text ?? "").trim();
|
|
30910
|
+
if (b.type !== "paragraph" && b.type !== "list_item") break;
|
|
30911
|
+
const sub = b.type === "list_item" ? `- ${t}` : t;
|
|
30912
|
+
if (!bangchimSub && /^-\s*\S.*\s-$/.test(sub) && sub.length < 60) bangchimSub = sub;
|
|
30913
|
+
else if (!bangchimContact && /☎/.test(t)) bangchimContact = t;
|
|
30914
|
+
else break;
|
|
30915
|
+
k++;
|
|
30916
|
+
}
|
|
30917
|
+
if (k > h1 + 1) blocks = [...blocks.slice(0, h1 + 1), ...blocks.slice(k)];
|
|
30918
|
+
}
|
|
30919
|
+
const pre = buildOutline(blocks, isMinistry ? { gaejosik: true, consumeTitle: true, summaryFromQuote: true, quoteBox: true, headingFrames: true, keepMarkers: true } : { gaejosik: true, consumeTitle: true, summaryFromQuote: isReport, ...isBangchim ? { headingFrames: true, keepMarkers: true, numbered: true } : {} });
|
|
30920
|
+
const scheme = pickScheme(g3, pre.hasBoxMarkers, pre.numbered);
|
|
30473
30921
|
const gaejosik = scheme.kind === "gaejosik";
|
|
30474
30922
|
const raw = gaejosik ? pre : buildOutline(blocks, { gaejosik: false, consumeTitle: true, summaryFromQuote: false });
|
|
30475
30923
|
const outline = { ...raw, title: raw.title && polishGongmunText(raw.title), nodes: raw.nodes.map(polishNode) };
|
|
30476
|
-
const frame = { reg, bf: bfReg, frame: scheme.frame, W };
|
|
30924
|
+
const frame = { reg, bf: bfReg, frame: scheme.frame, W: W2 };
|
|
30477
30925
|
const lineHu = (st) => Math.round(st.pt * 100 * ((st.lineSp ?? scheme.lineSp) / 100));
|
|
30478
30926
|
const paras = [];
|
|
30479
30927
|
let pendingPageBreak = false;
|
|
30480
30928
|
const tblCharPr = reg.char({ font: scheme.table.font, pt: scheme.table.pt });
|
|
30481
30929
|
const tableStyle = {
|
|
30482
|
-
totalWidth:
|
|
30930
|
+
totalWidth: W2,
|
|
30483
30931
|
charPr: tblCharPr,
|
|
30484
30932
|
boldCharPr: reg.char({ font: scheme.table.font, pt: scheme.table.pt, bold: true }),
|
|
30485
30933
|
charHeight: scheme.table.pt * 100,
|
|
@@ -30496,7 +30944,7 @@ function buildGongmunSectionV5(blocks, gongmun, deps, theme) {
|
|
|
30496
30944
|
let frontKind = "start";
|
|
30497
30945
|
const pushSummary = (t) => {
|
|
30498
30946
|
frontKind = "summary";
|
|
30499
|
-
const box = buildSummaryBox(polishGongmunText(t), frame);
|
|
30947
|
+
const box = (isBangchim ? buildBangchimSummary : buildSummaryBox)(polishGongmunText(t), frame);
|
|
30500
30948
|
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`);
|
|
30501
30949
|
paras.push(box.xml);
|
|
30502
30950
|
};
|
|
@@ -30543,8 +30991,23 @@ function buildGongmunSectionV5(blocks, gongmun, deps, theme) {
|
|
|
30543
30991
|
} else if (g3.approval) {
|
|
30544
30992
|
paras.push(buildApprovalSeoul(g3.approval, null, frame));
|
|
30545
30993
|
}
|
|
30994
|
+
if (g3.checklist) {
|
|
30995
|
+
let xml = buildSeoulChecklist(g3.checklist, frame);
|
|
30996
|
+
if (g3.pageNumbers) xml = xml.replace(/<hp:run charPrIDRef="(\d+)">/, `<hp:run charPrIDRef="$1">${pageHidingCtrl()}`);
|
|
30997
|
+
paras.push(pendingPageBreak ? xml.replace(/^<hp:p /, `<hp:p pageBreak="1" `) : xml);
|
|
30998
|
+
pendingPageBreak = true;
|
|
30999
|
+
}
|
|
31000
|
+
if (isBangchim && g3.toc && outline.chapters > 0) {
|
|
31001
|
+
const chs = outline.nodes.filter((n) => n.kind === "chapter").map((n) => ({ label: n.label ?? chapterLabel(n.index, "roman").replace(/\.$/, ""), title: n.label ? plain2(n.text).replace(CHAPTER_LABEL_RE, "") : plain2(n.text) }));
|
|
31002
|
+
const toc = buildBangchimToc(chs, frame);
|
|
31003
|
+
if (g3.pageNumbers) toc[0] = toc[0].replace(/<hp:run charPrIDRef="(\d+)">/, `<hp:run charPrIDRef="$1">${pageHidingCtrl()}`);
|
|
31004
|
+
if (pendingPageBreak) toc[0] = toc[0].replace(/^<hp:p /, `<hp:p pageBreak="1" `);
|
|
31005
|
+
paras.push(...toc);
|
|
31006
|
+
warnings.push("\uBAA9\uCC28 \uCABD\uBC88\uD638 \uCE78\uC740 \uBE44\uC6CC \uB461\uB2C8\uB2E4 \u2014 \uCABD\uC740 \uC870\uD310 \uB4A4\uC5D0 \uC815\uD574\uC9C0\uBBC0\uB85C \uD55C\uAE00\uC5D0\uC11C \uCC44\uC6B0\uC138\uC694");
|
|
31007
|
+
pendingPageBreak = true;
|
|
31008
|
+
}
|
|
30546
31009
|
if (docTitle) {
|
|
30547
|
-
const t = buildReportTitleTable(docTitle, reportInfo, frame);
|
|
31010
|
+
const t = isBangchim ? buildBangchimTitleTable(docTitle, bangchimSub, reportInfo ?? bangchimContact, frame) : buildReportTitleTable(docTitle, reportInfo, frame);
|
|
30548
31011
|
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"`);
|
|
30549
31012
|
if (pendingPageBreak) {
|
|
30550
31013
|
paras.push(t.xml.replace(/^<hp:p /, `<hp:p pageBreak="1" `).replace(/<hp:run charPrIDRef="(\d+)">/, `<hp:run charPrIDRef="$1">${newPageNumCtrl(1)}`));
|
|
@@ -30573,7 +31036,7 @@ function buildGongmunSectionV5(blocks, gongmun, deps, theme) {
|
|
|
30573
31036
|
let tableSeq = 0;
|
|
30574
31037
|
let sectionSeq = 0, subheadSeq = 0, itemBandSeq = 0;
|
|
30575
31038
|
let pendingNewPageNum = isMinistry && (!!g3.cover || !!g3.toc);
|
|
30576
|
-
let chapterStyle = g3.h2Marker === "box" || g3.h2Marker === "number" || g3.h2Marker === "none" || g3.h2Marker === "band" ? g3.h2Marker : "roman";
|
|
31039
|
+
let chapterStyle = g3.h2Marker === "box" || g3.h2Marker === "number" || g3.h2Marker === "none" || g3.h2Marker === "band" || g3.h2Marker === "square" ? g3.h2Marker : "roman";
|
|
30577
31040
|
if (!gaejosik) chapterStyle = "number";
|
|
30578
31041
|
const leadLeft = (depth, pt2) => (scheme.levels[Math.min(depth, 7)]?.leadTa ?? 0) * taHu(pt2);
|
|
30579
31042
|
const renderItem = (node, styleId = 0) => {
|
|
@@ -30591,9 +31054,9 @@ function buildGongmunSectionV5(blocks, gongmun, deps, theme) {
|
|
|
30591
31054
|
marker = numberer.next(depth, sup);
|
|
30592
31055
|
itemSeq++;
|
|
30593
31056
|
}
|
|
30594
|
-
const mk = isMinistry && node.marker ? node.marker : marker;
|
|
31057
|
+
const mk = (isMinistry || isBangchim) && node.marker ? node.marker : marker;
|
|
30595
31058
|
const lay = markerLayout(st.font, st.pt, st.leadTa, mk);
|
|
30596
|
-
const textW2 =
|
|
31059
|
+
const textW2 = W2 - lay.left - lay.hang;
|
|
30597
31060
|
let ratio = 100, spacing = 0, charBreaks = false;
|
|
30598
31061
|
if (st.oneLine) {
|
|
30599
31062
|
const fit = fitOneLine(plain2(node.text), st.font, st.pt, textW2, st.pt, 90);
|
|
@@ -30614,10 +31077,10 @@ function buildGongmunSectionV5(blocks, gongmun, deps, theme) {
|
|
|
30614
31077
|
}
|
|
30615
31078
|
const base = { font: st.font, pt: st.pt, bold: st.bold, ratio, spacing };
|
|
30616
31079
|
const consecutiveBox = prevKind === "item" && prevItemDepth === 0 && depth === 0;
|
|
30617
|
-
const afterBand = depth === 0 && (prevKind === "chapter" && chapterStyle === "band" || prevKind === "title" && !!st.blankBefore);
|
|
31080
|
+
const afterBand = depth === 0 && (prevKind === "chapter" && (chapterStyle === "band" || chapterStyle === "square") || prevKind === "title" && !!st.blankBefore);
|
|
30618
31081
|
const before2 = afterBand ? BOX_GAP_AFTER_BAND : st.blankBefore && !consecutiveBox && prevKind !== "chapter" && prevKind !== "start" && prevKind !== "summary" ? BOX_BLANK_HU : 0;
|
|
30619
31082
|
const keepNext = !!st.keepWithNext && !!nextNode && (nextNode.kind === "item" && nextNode.depth > depth || nextNode.kind === "ref");
|
|
30620
|
-
const paraSpec = { align: "JUSTIFY", left: lay.left, indent: -lay.hang, before: before2, lineSp: st.lineSp ?? scheme.lineSp, keepWithNext: keepNext, keepWord: !charBreaks, autoTab: lay.hang > 0, widowOrphan: true };
|
|
31083
|
+
const paraSpec = { align: st.align ?? "JUSTIFY", left: lay.left, indent: -lay.hang, before: before2, lineSp: st.lineSp ?? scheme.lineSp, keepWithNext: keepNext, keepWord: !charBreaks, autoTab: lay.hang > 0, widowOrphan: true };
|
|
30621
31084
|
const markerRun = mk ? markerRunXml(mk, reg.char({ font: st.font, pt: st.pt, bold: st.bold }), lay) : "";
|
|
30622
31085
|
if (isMinistry) {
|
|
30623
31086
|
const paraId2 = reg.para({ ...paraSpec, after: 600 });
|
|
@@ -30632,12 +31095,12 @@ function buildGongmunSectionV5(blocks, gongmun, deps, theme) {
|
|
|
30632
31095
|
const marker = isMinistry ? "*" : "\u203B";
|
|
30633
31096
|
const lay = markerLayout(st.font, st.pt, 0, marker);
|
|
30634
31097
|
const left = isMinistry ? st.leadTa * taHu(st.pt) : leadLeft(node.depth, st.pt);
|
|
30635
|
-
const textW2 =
|
|
31098
|
+
const textW2 = W2 - left - lay.hang;
|
|
30636
31099
|
const t = plain2(node.text);
|
|
30637
31100
|
const cb = fitCharBreaks(t, st.font, st.pt, textW2, textW2, g3.autoFitMinRatio ?? 101);
|
|
30638
31101
|
const f = cb ?? (g3.autoFitMinRatio !== null ? fitParagraph(t, st.font, st.pt, textW2, textW2, g3.autoFitMinRatio) : null);
|
|
30639
31102
|
const base = { font: st.font, pt: st.pt, bold: st.bold, ratio: f?.ratio ?? 100, spacing: f?.spacing ?? 0 };
|
|
30640
|
-
const paraId = reg.para({ align: "JUSTIFY", left, indent: -lay.hang, after: isMinistry ? 400 : 0, lineSp: isMinistry ? 130 : st.lineSp ?? scheme.lineSp, keepWord: !cb, autoTab: true, widowOrphan: true });
|
|
31103
|
+
const paraId = reg.para({ align: st.align ?? "JUSTIFY", left, indent: -lay.hang, after: isMinistry ? 400 : 0, lineSp: isMinistry ? 130 : st.lineSp ?? scheme.lineSp, keepWord: !cb, autoTab: true, widowOrphan: true });
|
|
30641
31104
|
const markerRun = markerRunXml(marker, reg.char({ font: st.font, pt: st.pt, bold: st.bold }), lay);
|
|
30642
31105
|
return `<hp:p paraPrIDRef="${paraId}" styleIDRef="0">${markerRun}${generateRuns(node.text, reg.char(base), inlineMapper(reg, base))}</hp:p>`;
|
|
30643
31106
|
};
|
|
@@ -30653,11 +31116,19 @@ function buildGongmunSectionV5(blocks, gongmun, deps, theme) {
|
|
|
30653
31116
|
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>`;
|
|
30654
31117
|
}
|
|
30655
31118
|
}
|
|
31119
|
+
if (isBangchim && !node.align) {
|
|
31120
|
+
const t = plain2(node.text);
|
|
31121
|
+
const cap = /^<\s*[^<>]{2,60}\s*>$/.test(t) ? { font: "\uD55C\uCEF4\uB3CB\uC6C0", pt: 13, bold: true, align: "CENTER" } : /^【[^】]{1,30}】/.test(t) ? { font: "HY\uD5E4\uB4DC\uB77C\uC778M", pt: 15, bold: false, align: "LEFT" } : null;
|
|
31122
|
+
if (cap) {
|
|
31123
|
+
const base2 = { font: cap.font, pt: cap.pt, bold: cap.bold };
|
|
31124
|
+
return generateParagraph(node.text, reg.para({ align: cap.align, lineSp: scheme.lineSp, keepWithNext: true }), reg.char(base2), inlineMapper(reg, base2));
|
|
31125
|
+
}
|
|
31126
|
+
}
|
|
30656
31127
|
let ratio = 100, spacing = 0, charBreaks = false;
|
|
30657
31128
|
if (!node.align) {
|
|
30658
31129
|
const t = plain2(node.text);
|
|
30659
|
-
const cb = fitCharBreaks(t, st.font, st.pt,
|
|
30660
|
-
const f = cb ?? (g3.autoFitMinRatio !== null ? fitParagraph(t, st.font, st.pt,
|
|
31130
|
+
const cb = fitCharBreaks(t, st.font, st.pt, W2, W2, g3.autoFitMinRatio ?? 101);
|
|
31131
|
+
const f = cb ?? (g3.autoFitMinRatio !== null ? fitParagraph(t, st.font, st.pt, W2, W2, g3.autoFitMinRatio) : null);
|
|
30661
31132
|
if (f) {
|
|
30662
31133
|
ratio = f.ratio;
|
|
30663
31134
|
spacing = f.spacing;
|
|
@@ -30682,6 +31153,15 @@ function buildGongmunSectionV5(blocks, gongmun, deps, theme) {
|
|
|
30682
31153
|
})();
|
|
30683
31154
|
return first ? xml : xml.replace(/^<hp:p /, `<hp:p pageBreak="1" `);
|
|
30684
31155
|
}
|
|
31156
|
+
if (chapterStyle === "square") {
|
|
31157
|
+
sectionSeq = 0;
|
|
31158
|
+
subheadSeq = 0;
|
|
31159
|
+
const before3 = prevKind === "start" || prevKind === "title" ? 0 : prevKind === "summary" ? BOX_GAP_AFTER_BAND : BAND_BEFORE_HU;
|
|
31160
|
+
const title = node.label ? plain2(node.text).replace(CHAPTER_LABEL_RE, "") : plain2(node.text);
|
|
31161
|
+
const box = buildSquareChapter(node.label ?? chapterLabel(node.index, "roman").replace(/\.$/, ""), title, frame, st, before3);
|
|
31162
|
+
if (box.overflow) warnings.push(`\uC7A5 \uC81C\uBAA9\uC774 \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"`);
|
|
31163
|
+
return box.xml;
|
|
31164
|
+
}
|
|
30685
31165
|
if (chapterStyle === "band") {
|
|
30686
31166
|
const before3 = prevKind === "start" || prevKind === "title" ? 0 : prevKind === "summary" ? BOX_GAP_AFTER_BAND : BAND_BEFORE_HU;
|
|
30687
31167
|
const band = buildChapterBand(chapterLabel(node.index, "roman").replace(/\.$/, ""), plain2(node.text), frame, { ...CHAPTER_BAND_DEFAULT, color: g3.bandColor, textColor: g3.bandTextColor, titleFont: st.font, titlePt: st.pt }, before3);
|
|
@@ -30690,7 +31170,7 @@ function buildGongmunSectionV5(blocks, gongmun, deps, theme) {
|
|
|
30690
31170
|
}
|
|
30691
31171
|
const label = chapterStyle === "none" ? "" : chapterLabel(node.index, chapterStyle === "number" ? "number" : "roman");
|
|
30692
31172
|
const text = label ? `${label} ${node.text}` : node.text;
|
|
30693
|
-
const fit = fitOneLine(plain2(text), st.font, st.pt,
|
|
31173
|
+
const fit = fitOneLine(plain2(text), st.font, st.pt, W2, st.pt - 2);
|
|
30694
31174
|
const base = { font: st.font, pt: fit.pt, bold: st.bold, ratio: fit.ratio, spacing: fit.spacing };
|
|
30695
31175
|
const before2 = prevKind === "start" || prevKind === "title" || prevKind === "summary" ? 0 : lineHu(scheme.body);
|
|
30696
31176
|
const paraId = reg.para({ align: "LEFT", before: before2, after: Math.round(lineHu(scheme.body) * 0.3), lineSp: st.lineSp ?? scheme.lineSp, keepWithNext: true });
|
|
@@ -30712,7 +31192,7 @@ function buildGongmunSectionV5(blocks, gongmun, deps, theme) {
|
|
|
30712
31192
|
if (!b.rows) return "";
|
|
30713
31193
|
let style = tableStyle;
|
|
30714
31194
|
const face = faceClassForGen(scheme.table.font);
|
|
30715
|
-
const avail =
|
|
31195
|
+
const avail = W2 - DATA_TABLE_INSET;
|
|
30716
31196
|
for (const pt2 of [scheme.table.pt, scheme.table.pt - 1, scheme.table.pt - 2]) {
|
|
30717
31197
|
if (pt2 < 9) break;
|
|
30718
31198
|
if (requiredTableWidth(b.rows, pt2 * 100, face) <= avail || pt2 === scheme.table.pt - 2) {
|
|
@@ -30771,6 +31251,16 @@ function buildGongmunSectionV5(blocks, gongmun, deps, theme) {
|
|
|
30771
31251
|
break;
|
|
30772
31252
|
case "heading": {
|
|
30773
31253
|
const before2 = prevKind === "chapter" || prevKind === "heading" || prevKind === "start" ? 0 : BAND_BEFORE_HU;
|
|
31254
|
+
if (isBangchim) {
|
|
31255
|
+
if (node.level <= 3) {
|
|
31256
|
+
const band = buildBangchimSectionBand(++sectionSeq, plain2(node.text), frame, before2);
|
|
31257
|
+
if (band.overflow) warnings.push(`\uC808 \uC81C\uBAA9\uC774 \uB760 \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"`);
|
|
31258
|
+
xml = band.xml;
|
|
31259
|
+
} else xml = buildBangchimSubhead(++subheadSeq, node.text, frame, prevKind === "heading" ? 0 : Math.round(before2 / 2));
|
|
31260
|
+
lastItemFromHeading = true;
|
|
31261
|
+
lastTextNode = node;
|
|
31262
|
+
break;
|
|
31263
|
+
}
|
|
30774
31264
|
if (node.level <= 3) {
|
|
30775
31265
|
subheadSeq = 0;
|
|
30776
31266
|
itemBandSeq = 0;
|
|
@@ -39927,14 +40417,14 @@ async function renderDocument(input, options) {
|
|
|
39927
40417
|
|
|
39928
40418
|
// src/render/regions.ts
|
|
39929
40419
|
function cropRect(bbox, scale2, pageWidthPx, pageHeightPx, paddingPt = 0) {
|
|
39930
|
-
const
|
|
40420
|
+
const W2 = Math.max(1, Math.floor(pageWidthPx)), H = Math.max(1, Math.floor(pageHeightPx));
|
|
39931
40421
|
const clamp = (v, hi) => Math.min(Math.max(0, v), hi);
|
|
39932
|
-
let left = clamp(Math.floor((bbox.x - paddingPt) * scale2),
|
|
40422
|
+
let left = clamp(Math.floor((bbox.x - paddingPt) * scale2), W2 - 1);
|
|
39933
40423
|
let top = clamp(Math.floor((bbox.y - paddingPt) * scale2), H - 1);
|
|
39934
|
-
const right = clamp(Math.ceil((bbox.x + bbox.width + paddingPt) * scale2),
|
|
40424
|
+
const right = clamp(Math.ceil((bbox.x + bbox.width + paddingPt) * scale2), W2);
|
|
39935
40425
|
const bottom = clamp(Math.ceil((bbox.y + bbox.height + paddingPt) * scale2), H);
|
|
39936
40426
|
const width = Math.max(1, right - left), height = Math.max(1, bottom - top);
|
|
39937
|
-
if (left + width >
|
|
40427
|
+
if (left + width > W2) left = W2 - width;
|
|
39938
40428
|
if (top + height > H) top = H - height;
|
|
39939
40429
|
return { left, top, width, height };
|
|
39940
40430
|
}
|
|
@@ -40110,10 +40600,14 @@ async function dispatch(format2, buffer, opts) {
|
|
|
40110
40600
|
return { success: false, fileType: "unknown", error: "\uC9C0\uC6D0\uD558\uC9C0 \uC54A\uB294 \uD30C\uC77C \uD615\uC2DD\uC785\uB2C8\uB2E4.", code: "UNSUPPORTED_FORMAT" };
|
|
40111
40601
|
}
|
|
40112
40602
|
}
|
|
40603
|
+
function scriptsOff(r, off) {
|
|
40604
|
+
return off ? stripScriptTags(r) : r;
|
|
40605
|
+
}
|
|
40113
40606
|
async function parseImage(buffer, options) {
|
|
40114
40607
|
try {
|
|
40115
|
-
const { parseImageDocument } = await import("./image-ocr-
|
|
40608
|
+
const { parseImageDocument } = await import("./image-ocr-5BUJQ4TK.js");
|
|
40116
40609
|
const { blocks, warnings } = await parseImageDocument(buffer, options);
|
|
40610
|
+
stripScriptTags({ blocks });
|
|
40117
40611
|
return {
|
|
40118
40612
|
success: true,
|
|
40119
40613
|
fileType: "image",
|
|
@@ -40137,7 +40631,7 @@ async function parseHwp3(buffer, options) {
|
|
|
40137
40631
|
async function parseHwpx(buffer, options) {
|
|
40138
40632
|
try {
|
|
40139
40633
|
const { markdown, blocks, metadata, outline, warnings, images } = await parseHwpxDocument(buffer, options);
|
|
40140
|
-
return { success: true, fileType: "hwpx", markdown, blocks, metadata, outline, warnings, images: images?.length ? images : void 0, pageCount: metadata?.pageCount };
|
|
40634
|
+
return scriptsOff({ success: true, fileType: "hwpx", markdown, blocks, metadata, outline, warnings, images: images?.length ? images : void 0, pageCount: metadata?.pageCount }, options?.scriptTags === false);
|
|
40141
40635
|
} catch (err) {
|
|
40142
40636
|
return { success: false, fileType: "hwpx", error: sanitizeError(err), code: classifyError(err) };
|
|
40143
40637
|
}
|
|
@@ -40163,7 +40657,7 @@ async function parseHwp(buffer, options) {
|
|
|
40163
40657
|
} catch {
|
|
40164
40658
|
}
|
|
40165
40659
|
}
|
|
40166
|
-
return { success: true, fileType: "hwp", markdown, blocks, metadata, outline, warnings, images: images?.length ? images : void 0, pageCount: metadata?.pageCount };
|
|
40660
|
+
return scriptsOff({ success: true, fileType: "hwp", markdown, blocks, metadata, outline, warnings, images: images?.length ? images : void 0, pageCount: metadata?.pageCount }, options?.scriptTags === false);
|
|
40167
40661
|
} catch (err) {
|
|
40168
40662
|
return { success: false, fileType: "hwp", error: sanitizeError(err), code: classifyError(err) };
|
|
40169
40663
|
}
|
|
@@ -40171,7 +40665,7 @@ async function parseHwp(buffer, options) {
|
|
|
40171
40665
|
async function parsePdf(buffer, options) {
|
|
40172
40666
|
let parsePdfDocument;
|
|
40173
40667
|
try {
|
|
40174
|
-
const mod = await import("./parser-
|
|
40668
|
+
const mod = await import("./parser-ZQM3664Z.js");
|
|
40175
40669
|
parsePdfDocument = mod.parsePdfDocument;
|
|
40176
40670
|
} catch {
|
|
40177
40671
|
return {
|
|
@@ -40183,7 +40677,7 @@ async function parsePdf(buffer, options) {
|
|
|
40183
40677
|
}
|
|
40184
40678
|
try {
|
|
40185
40679
|
const { markdown, blocks, metadata, outline, warnings, isImageBased, pageQuality, qualitySummary, images, pages } = await parsePdfDocument(buffer, options);
|
|
40186
|
-
return { success: true, fileType: "pdf", markdown, blocks, metadata, outline, warnings, isImageBased, pageQuality, qualitySummary, images, pages, pageCount: metadata?.pageCount };
|
|
40680
|
+
return scriptsOff({ success: true, fileType: "pdf", markdown, blocks, metadata, outline, warnings, isImageBased, pageQuality, qualitySummary, images, pages, pageCount: metadata?.pageCount }, options?.scriptTags !== true);
|
|
40187
40681
|
} catch (err) {
|
|
40188
40682
|
const isImageBased = err instanceof Error && "isImageBased" in err ? true : void 0;
|
|
40189
40683
|
return { success: false, fileType: "pdf", error: sanitizeError(err), code: classifyError(err), isImageBased };
|
|
@@ -40208,7 +40702,7 @@ async function parseXls(buffer, options) {
|
|
|
40208
40702
|
async function parseDocx(buffer, options) {
|
|
40209
40703
|
try {
|
|
40210
40704
|
const { markdown, blocks, metadata, outline, warnings, images } = await parseDocxDocument(buffer, options);
|
|
40211
|
-
return { success: true, fileType: "docx", markdown, blocks, metadata, outline, warnings, images: images?.length ? images : void 0, pageCount: metadata?.pageCount };
|
|
40705
|
+
return scriptsOff({ success: true, fileType: "docx", markdown, blocks, metadata, outline, warnings, images: images?.length ? images : void 0, pageCount: metadata?.pageCount }, options?.scriptTags === false);
|
|
40212
40706
|
} catch (err) {
|
|
40213
40707
|
return { success: false, fileType: "docx", error: sanitizeError(err), code: classifyError(err) };
|
|
40214
40708
|
}
|