kordoc 4.0.2 → 4.0.4
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 +800 -768
- package/dist/{-XMKVL3NX.js → -Q4ZCVQFI.js} +10 -8
- package/dist/chunk-2RIOXYGA.js +92 -0
- package/dist/chunk-2RIOXYGA.js.map +1 -0
- package/dist/chunk-5RPYBC2Q.js +88 -0
- package/dist/chunk-5RPYBC2Q.js.map +1 -0
- package/dist/chunk-DCZVOIEO.cjs.map +1 -1
- package/dist/{chunk-IKBIIEVJ.js → chunk-DKMKJMR6.js} +5 -4
- package/dist/chunk-DKMKJMR6.js.map +1 -0
- package/dist/chunk-GS7T56RP.cjs.map +1 -1
- package/dist/{chunk-STMLZDKR.js → chunk-KA5AN73R.js} +2 -2
- package/dist/chunk-KA5AN73R.js.map +1 -0
- package/dist/{chunk-3IYQYBUF.js → chunk-KSVLQ5BC.js} +33 -4
- package/dist/chunk-KSVLQ5BC.js.map +1 -0
- package/dist/{chunk-5CJGKKMZ.js → chunk-MEPHGCPQ.js} +1 -1
- package/dist/chunk-MEPHGCPQ.js.map +1 -0
- package/dist/{chunk-AHWRAGXN.js → chunk-MLNHZN32.js} +1876 -965
- package/dist/chunk-MLNHZN32.js.map +1 -0
- package/dist/chunk-MOL7MDBG.js +0 -0
- package/dist/{chunk-WN7LZHII.cjs → chunk-NIW7CZYX.cjs} +37 -8
- package/dist/chunk-NIW7CZYX.cjs.map +1 -0
- package/dist/{chunk-6SD4X5PT.js → chunk-S3KGEEBQ.js} +76 -25
- package/dist/chunk-S3KGEEBQ.js.map +1 -0
- package/dist/{chunk-67RANGFQ.js → chunk-TXNRIVWA.js} +3 -3
- package/dist/chunk-TXNRIVWA.js.map +1 -0
- package/dist/{chunk-6YIYFWO2.js → chunk-ZJXR4PWY.js} +33 -4
- package/dist/chunk-ZJXR4PWY.js.map +1 -0
- package/dist/cli.js +120 -73
- package/dist/cli.js.map +1 -1
- package/dist/{detect-PJZMUL2Z.js → detect-RI2MQ33K.js} +2 -2
- package/dist/formula-5NKVS2LR.cjs.map +1 -1
- package/dist/formula-JCNF43NE.js +0 -0
- package/dist/index.cjs +21139 -20176
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +52 -5
- package/dist/index.d.ts +52 -5
- package/dist/index.js +20969 -20006
- package/dist/index.js.map +1 -1
- package/dist/mcp.js +113 -90
- package/dist/mcp.js.map +1 -1
- package/dist/page-range-737B4EZW.js +0 -0
- package/dist/page-range-P7SDW6LR.cjs.map +1 -1
- package/dist/{parser-6AYYGX6J.cjs → parser-2ZUTCU2G.cjs} +108 -19
- package/dist/parser-2ZUTCU2G.cjs.map +1 -0
- package/dist/{parser-3HM2VYKK.js → parser-46JGSTPR.js} +97 -8
- package/dist/parser-46JGSTPR.js.map +1 -0
- package/dist/{parser-2Q2YKEJG.js → parser-TLCC67W6.js} +96 -7
- package/dist/parser-TLCC67W6.js.map +1 -0
- package/dist/profile-io-MRVIJ5T4.js +11 -0
- package/dist/{provider-H4WWSPOV.js → provider-4ZJKV3DC.js} +1 -1
- package/dist/provider-4ZJKV3DC.js.map +1 -0
- package/dist/{provider-7H4CPZYS.js → provider-AKROB7WQ.js} +1 -1
- package/dist/provider-AKROB7WQ.js.map +1 -0
- package/dist/{provider-5K7O3Z2O.cjs → provider-G4C2V2PD.cjs} +1 -1
- package/dist/provider-G4C2V2PD.cjs.map +1 -0
- package/dist/render-TTOG6CHW.js +10 -0
- package/dist/seal-NMLZ3M3K.js +10 -0
- package/dist/seal-NMLZ3M3K.js.map +1 -0
- package/dist/setup-57FB3LSP.js +0 -0
- package/dist/{watch-6OHF727O.js → watch-NZNVJ4YD.js} +8 -8
- package/dist/watch-NZNVJ4YD.js.map +1 -0
- package/package.json +4 -4
- package/dist/chunk-3IYQYBUF.js.map +0 -1
- package/dist/chunk-5CJGKKMZ.js.map +0 -1
- package/dist/chunk-67RANGFQ.js.map +0 -1
- package/dist/chunk-6SD4X5PT.js.map +0 -1
- package/dist/chunk-6YIYFWO2.js.map +0 -1
- package/dist/chunk-AHWRAGXN.js.map +0 -1
- package/dist/chunk-IKBIIEVJ.js.map +0 -1
- package/dist/chunk-STMLZDKR.js.map +0 -1
- package/dist/chunk-WN7LZHII.cjs.map +0 -1
- package/dist/parser-2Q2YKEJG.js.map +0 -1
- package/dist/parser-3HM2VYKK.js.map +0 -1
- package/dist/parser-6AYYGX6J.cjs.map +0 -1
- package/dist/provider-5K7O3Z2O.cjs.map +0 -1
- package/dist/provider-7H4CPZYS.js.map +0 -1
- package/dist/provider-H4WWSPOV.js.map +0 -1
- package/dist/render-W4VCJE6N.js +0 -10
- package/dist/seal-DRPTNHOP.js +0 -10
- package/dist/watch-6OHF727O.js.map +0 -1
- /package/dist/{-XMKVL3NX.js.map → -Q4ZCVQFI.js.map} +0 -0
- /package/dist/{detect-PJZMUL2Z.js.map → detect-RI2MQ33K.js.map} +0 -0
- /package/dist/{render-W4VCJE6N.js.map → profile-io-MRVIJ5T4.js.map} +0 -0
- /package/dist/{seal-DRPTNHOP.js.map → render-TTOG6CHW.js.map} +0 -0
|
@@ -11,13 +11,13 @@ import {
|
|
|
11
11
|
dedupeRunningHeaders,
|
|
12
12
|
flattenLayoutTables,
|
|
13
13
|
mapPuaText
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-ZJXR4PWY.js";
|
|
15
15
|
import {
|
|
16
16
|
detectFormat,
|
|
17
17
|
detectOle2Format,
|
|
18
18
|
detectZipFormat,
|
|
19
19
|
parseLenientCfb
|
|
20
|
-
} from "./chunk-
|
|
20
|
+
} from "./chunk-MEPHGCPQ.js";
|
|
21
21
|
import {
|
|
22
22
|
parsePageRange
|
|
23
23
|
} from "./chunk-MOL7MDBG.js";
|
|
@@ -32,14 +32,14 @@ import {
|
|
|
32
32
|
paraTextPureT,
|
|
33
33
|
patchZipEntries,
|
|
34
34
|
scanSectionXml
|
|
35
|
-
} from "./chunk-
|
|
35
|
+
} from "./chunk-TXNRIVWA.js";
|
|
36
36
|
import {
|
|
37
37
|
SPACE_EM_FIXED,
|
|
38
38
|
charWidthEm1000,
|
|
39
39
|
fitRatioForFewerLines,
|
|
40
40
|
measureTextWidth,
|
|
41
41
|
simulateWrap
|
|
42
|
-
} from "./chunk-
|
|
42
|
+
} from "./chunk-S3KGEEBQ.js";
|
|
43
43
|
import {
|
|
44
44
|
MAX_DECOMPRESS_SIZE,
|
|
45
45
|
MAX_XML_DEPTH,
|
|
@@ -50,7 +50,7 @@ import {
|
|
|
50
50
|
createXmlParser,
|
|
51
51
|
extractTextFromNode,
|
|
52
52
|
findChildByLocalName
|
|
53
|
-
} from "./chunk-
|
|
53
|
+
} from "./chunk-DKMKJMR6.js";
|
|
54
54
|
import {
|
|
55
55
|
KordocError,
|
|
56
56
|
classifyError,
|
|
@@ -61,7 +61,7 @@ import {
|
|
|
61
61
|
sanitizeHref,
|
|
62
62
|
stripDtd,
|
|
63
63
|
toArrayBuffer
|
|
64
|
-
} from "./chunk-
|
|
64
|
+
} from "./chunk-KA5AN73R.js";
|
|
65
65
|
|
|
66
66
|
// src/index.ts
|
|
67
67
|
import { readFile } from "fs/promises";
|
|
@@ -183,7 +183,8 @@ async function extractHwpxStyles(zip, decompressed) {
|
|
|
183
183
|
styles: /* @__PURE__ */ new Map(),
|
|
184
184
|
numberings: /* @__PURE__ */ new Map(),
|
|
185
185
|
bullets: /* @__PURE__ */ new Map(),
|
|
186
|
-
paraHeadings: /* @__PURE__ */ new Map()
|
|
186
|
+
paraHeadings: /* @__PURE__ */ new Map(),
|
|
187
|
+
paraIndents: /* @__PURE__ */ new Map()
|
|
187
188
|
};
|
|
188
189
|
const headerPaths = ["Contents/header.xml", "header.xml", "Contents/head.xml", "head.xml"];
|
|
189
190
|
for (const hp of headerPaths) {
|
|
@@ -199,12 +200,13 @@ async function extractHwpxStyles(zip, decompressed) {
|
|
|
199
200
|
const parser = createXmlParser();
|
|
200
201
|
const doc = parser.parseFromString(stripDtd(xml), "text/xml");
|
|
201
202
|
if (!doc.documentElement) continue;
|
|
202
|
-
parseCharProperties(doc, result.charProperties);
|
|
203
|
-
parseStyleElements(doc, result.styles);
|
|
204
203
|
const domDoc = doc;
|
|
204
|
+
parseCharProperties(domDoc, result.charProperties);
|
|
205
|
+
parseStyleElements(domDoc, result.styles);
|
|
205
206
|
parseNumberings(domDoc, result.numberings);
|
|
206
207
|
parseBullets(domDoc, result.bullets);
|
|
207
208
|
parseParaHeadings(domDoc, result.paraHeadings);
|
|
209
|
+
parseParaIndents(domDoc, result.paraIndents);
|
|
208
210
|
break;
|
|
209
211
|
} catch {
|
|
210
212
|
continue;
|
|
@@ -232,6 +234,14 @@ function parseCharProperties(doc, map) {
|
|
|
232
234
|
if (bold === "true" || bold === "1") prop.bold = true;
|
|
233
235
|
const italic = el.getAttribute("italic");
|
|
234
236
|
if (italic === "true" || italic === "1") prop.italic = true;
|
|
237
|
+
const kids2 = el.childNodes;
|
|
238
|
+
for (let j = 0; j < (kids2?.length ?? 0); j++) {
|
|
239
|
+
const k = kids2[j];
|
|
240
|
+
if (k.nodeType !== 1) continue;
|
|
241
|
+
const localTag = (k.tagName || "").replace(/^[^:]+:/, "");
|
|
242
|
+
if (localTag === "bold") prop.bold = true;
|
|
243
|
+
else if (localTag === "italic") prop.italic = true;
|
|
244
|
+
}
|
|
235
245
|
const fontFaces = el.getElementsByTagName("*");
|
|
236
246
|
for (let j = 0; j < fontFaces.length; j++) {
|
|
237
247
|
const ff = fontFaces[j];
|
|
@@ -304,6 +314,28 @@ function parseBullets(doc, map) {
|
|
|
304
314
|
if (map.size > 0) break;
|
|
305
315
|
}
|
|
306
316
|
}
|
|
317
|
+
function parseParaIndents(doc, map) {
|
|
318
|
+
const tagNames = ["hh:paraPr", "paraPr"];
|
|
319
|
+
for (const tagName of tagNames) {
|
|
320
|
+
const elements = doc.getElementsByTagName(tagName);
|
|
321
|
+
for (let i = 0; i < elements.length; i++) {
|
|
322
|
+
const el = elements[i];
|
|
323
|
+
const id = el.getAttribute("id") || "";
|
|
324
|
+
if (!id) continue;
|
|
325
|
+
const margin = findChildByLocalName(el, "margin");
|
|
326
|
+
if (!margin) continue;
|
|
327
|
+
const readHu = (name) => {
|
|
328
|
+
const child = findChildByLocalName(margin, name);
|
|
329
|
+
const v = parseInt(child?.getAttribute("value") || child?.textContent || "", 10);
|
|
330
|
+
return Number.isFinite(v) ? v : 0;
|
|
331
|
+
};
|
|
332
|
+
const left = readHu("left");
|
|
333
|
+
const intent = readHu("intent");
|
|
334
|
+
if (left !== 0 || intent !== 0) map.set(id, { left, intent });
|
|
335
|
+
}
|
|
336
|
+
if (map.size > 0) break;
|
|
337
|
+
}
|
|
338
|
+
}
|
|
307
339
|
function parseParaHeadings(doc, map) {
|
|
308
340
|
const tagNames = ["hh:paraPr", "paraPr"];
|
|
309
341
|
for (const tagName of tagNames) {
|
|
@@ -832,95 +864,479 @@ function hmlToLatex(hmlEqStr) {
|
|
|
832
864
|
return out;
|
|
833
865
|
}
|
|
834
866
|
|
|
835
|
-
// src/hwpx/
|
|
836
|
-
var
|
|
837
|
-
var
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
];
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
867
|
+
// src/hwpx/geometry.ts
|
|
868
|
+
var A4_W_HU = 59528;
|
|
869
|
+
var A4_H_HU = 84188;
|
|
870
|
+
var HU_PER_MM = 7200 / 25.4;
|
|
871
|
+
function mmToHwpunit(mm) {
|
|
872
|
+
return Math.round(mm * 7200 / 25.4);
|
|
873
|
+
}
|
|
874
|
+
var BODY_WIDTH_20MM = mmToHwpunit(170);
|
|
875
|
+
var COVER_MEASURED_W = 48180;
|
|
876
|
+
var DATA_TABLE_ID_BASE = 1e3;
|
|
877
|
+
var CHART_TABLE_ID_BASE = 91e5;
|
|
878
|
+
var GJ_TABLE_ID_BASE = 92e5;
|
|
879
|
+
var EXTRA_TABLE_ID_BASE = 93e5;
|
|
880
|
+
var PIC_ID_BASE = 94e5;
|
|
881
|
+
|
|
882
|
+
// src/hwpx/gaejosik.ts
|
|
883
|
+
var GAEJOSIK_BULLETS = ["\u25A1", "\u25CB", "-", "\u318D"];
|
|
884
|
+
function gaejosikMarker(depth, bullet2 = "\u25CB") {
|
|
885
|
+
if (depth === 1) return bullet2;
|
|
886
|
+
return GAEJOSIK_BULLETS[Math.min(depth, GAEJOSIK_BULLETS.length - 1)];
|
|
887
|
+
}
|
|
888
|
+
function gaejosikSizes(bodyHeight, ov = {}) {
|
|
889
|
+
const s = bodyHeight / 1500;
|
|
890
|
+
const r = (v, pt) => pt ? Math.round(pt * 100) : Math.round(v * s);
|
|
891
|
+
return {
|
|
892
|
+
/** □ 대항목 — HY헤드라인M 16pt */
|
|
893
|
+
dae: r(1600, ov.dae),
|
|
894
|
+
/** ※ 참고 — 한양중고딕 13pt */
|
|
895
|
+
cham: r(1300, ov.cham),
|
|
896
|
+
/** 장 헤더(로마숫자·제목) 17pt */
|
|
897
|
+
chapter: r(1700, ov.chapter),
|
|
898
|
+
/** 표지 제목 30pt */
|
|
899
|
+
coverTitle: r(3e3, ov.coverTitle),
|
|
900
|
+
/** 표지 날짜·기관명 25pt */
|
|
901
|
+
coverSub: r(2500, ov.coverSub),
|
|
902
|
+
/** 목차 라벨 "목 차" 28pt */
|
|
903
|
+
tocLabel: r(2800, ov.tocLabel),
|
|
904
|
+
/** 목차 로마숫자 21pt(한양신명조) */
|
|
905
|
+
tocRoman: r(2100, ov.tocRoman),
|
|
906
|
+
/** 목차 항목 18pt */
|
|
907
|
+
tocItem: r(1800, ov.tocItem),
|
|
908
|
+
/** 표 셀 — 맑은 고딕 12pt (실측: 정부 양식 표) */
|
|
909
|
+
table: r(1200, ov.table),
|
|
910
|
+
/** 표지 장식 바 셀 빈 문단 — 6pt (실측: 바 높이 818 안에 줄높이 수납) */
|
|
911
|
+
bar: r(600),
|
|
912
|
+
/** 본문 첫 페이지 제목 박스 — HY헤드라인M 22pt (실측: GT3 표④) */
|
|
913
|
+
bodyTitle: r(2200, ov.bodyTitle)
|
|
914
|
+
};
|
|
915
|
+
}
|
|
916
|
+
function gaejosikSpaceBefore(depth, bodyHeight) {
|
|
917
|
+
const s = bodyHeight / 1500;
|
|
918
|
+
const ui = [3e3, 2e3, 1200, 600][Math.min(depth, 3)];
|
|
919
|
+
return Math.round(ui * s);
|
|
920
|
+
}
|
|
921
|
+
function gaejosikLevelIndent(depth, bodyHeight, sizes = {}, bullet2 = "\u25CB") {
|
|
922
|
+
const lefts = [0, 1, 1.5, 2];
|
|
923
|
+
const left = depth <= 3 ? Math.round(lefts[depth] * bodyHeight) : Math.round((2 + (depth - 3) * 0.5) * bodyHeight);
|
|
924
|
+
const h = depth === 0 ? gaejosikSizes(bodyHeight, sizes).dae : bodyHeight;
|
|
925
|
+
return { left, indent: -markerWidth(gaejosikMarker(depth, bullet2), h) };
|
|
926
|
+
}
|
|
927
|
+
function gaejosikChamIndent(bodyHeight, sizes = {}) {
|
|
928
|
+
const cham = gaejosikSizes(bodyHeight, sizes).cham;
|
|
929
|
+
return { left: Math.round(2.5 * cham), indent: -markerWidth("\u203B", cham) };
|
|
930
|
+
}
|
|
931
|
+
function gaejosikTocItemIndent(bodyHeight, sizes = {}) {
|
|
932
|
+
const sz = gaejosikSizes(bodyHeight, sizes);
|
|
933
|
+
const hang = markerWidth("\u2167.", sz.tocRoman);
|
|
934
|
+
return { left: 2e3 + hang, indent: -hang };
|
|
935
|
+
}
|
|
936
|
+
function formatGaejosikDate(d) {
|
|
937
|
+
return `${d.getFullYear()}. ${d.getMonth() + 1}. ${d.getDate()}.`;
|
|
938
|
+
}
|
|
939
|
+
function stripChapterNumber(title) {
|
|
940
|
+
return title.replace(/^\s*[□○ㅇㆍ-]\s+/u, "").replace(/^\s*(?:\d+(?:\.\d+)*|[Ⅰ-Ⅻ]+)\s*[.)]\s*/u, "").trim();
|
|
941
|
+
}
|
|
942
|
+
var GAEJOSIK_COLORS = {
|
|
943
|
+
/** 장헤더 로마숫자 셀·표지 진한 바 음영 */
|
|
944
|
+
primary: "#193AAA",
|
|
945
|
+
/** 장헤더 셀 테두리 */
|
|
946
|
+
border: "#006699",
|
|
947
|
+
/** 표지 연한 바 */
|
|
948
|
+
accent: "#A0B4E6",
|
|
949
|
+
/** 장헤더 제목 셀 음영 */
|
|
950
|
+
titleFill: "#F2F2F2",
|
|
951
|
+
/** 장헤더 제목 셀 상하선 */
|
|
952
|
+
titleLine: "#A6A6A6",
|
|
953
|
+
/** 목차 박스 테두리 */
|
|
954
|
+
tocBorder: "#514BAC",
|
|
955
|
+
/** 목차 배너 라벤더 스트라이프 (실측: GT3 표②) */
|
|
956
|
+
tocStripe: "#E0E5FA"
|
|
957
|
+
};
|
|
958
|
+
var GAEJOSIK_BASE_WIDTH = BODY_WIDTH_20MM;
|
|
959
|
+
var CHAPTER_GEOM = { numW: 3327, gapW: 848, titleW: 43513, rowH: 2832 };
|
|
960
|
+
var COVER_GEOM = {
|
|
961
|
+
totalW: COVER_MEASURED_W,
|
|
962
|
+
topDarkW: 38219,
|
|
963
|
+
topLightW: 9961,
|
|
964
|
+
barH: 818,
|
|
965
|
+
titleH: 11060,
|
|
966
|
+
botDarkW: 10466,
|
|
967
|
+
botLightW: 37714,
|
|
968
|
+
botBarH: 812
|
|
969
|
+
};
|
|
970
|
+
var TOC_GEOM = { boxW: 46492, boxH: 57840 };
|
|
971
|
+
function chapterGeom(bodyHeight, ov = {}, bodyWidth = GAEJOSIK_BASE_WIDTH) {
|
|
972
|
+
const s = gaejosikSizes(bodyHeight, ov).chapter / 1700;
|
|
973
|
+
const ws = bodyWidth / GAEJOSIK_BASE_WIDTH;
|
|
974
|
+
const numW = Math.round(CHAPTER_GEOM.numW * ws);
|
|
975
|
+
const gapW = Math.round(CHAPTER_GEOM.gapW * ws);
|
|
976
|
+
const titleW = Math.round((CHAPTER_GEOM.numW + CHAPTER_GEOM.gapW + CHAPTER_GEOM.titleW) * ws) - numW - gapW;
|
|
977
|
+
return { numW, gapW, titleW, rowH: Math.round(CHAPTER_GEOM.rowH * s) };
|
|
978
|
+
}
|
|
979
|
+
function coverGeom(bodyHeight, ov = {}, bodyWidth = GAEJOSIK_BASE_WIDTH) {
|
|
980
|
+
const s = gaejosikSizes(bodyHeight, ov).coverTitle / 3e3;
|
|
981
|
+
const w = (v) => Math.round(v * bodyWidth / GAEJOSIK_BASE_WIDTH);
|
|
982
|
+
const totalW = w(COVER_GEOM.totalW);
|
|
983
|
+
return {
|
|
984
|
+
...COVER_GEOM,
|
|
985
|
+
totalW,
|
|
986
|
+
topDarkW: w(COVER_GEOM.topDarkW),
|
|
987
|
+
topLightW: totalW - w(COVER_GEOM.topDarkW),
|
|
988
|
+
botDarkW: w(COVER_GEOM.botDarkW),
|
|
989
|
+
botLightW: totalW - w(COVER_GEOM.botDarkW),
|
|
990
|
+
titleH: Math.round(COVER_GEOM.titleH * s)
|
|
991
|
+
};
|
|
992
|
+
}
|
|
993
|
+
var TOC_BANNER_GEOM = {
|
|
994
|
+
colWs: [565, 565, 1414, 13191, 1414, 565, 565],
|
|
995
|
+
h: 3931
|
|
996
|
+
};
|
|
997
|
+
function tocBannerGeom(bodyHeight, ov = {}) {
|
|
998
|
+
const s = gaejosikSizes(bodyHeight, ov).tocLabel / 2800;
|
|
999
|
+
return { colWs: TOC_BANNER_GEOM.colWs, h: Math.round(TOC_BANNER_GEOM.h * s) };
|
|
1000
|
+
}
|
|
1001
|
+
var BODY_TITLE_GEOM = { barH: 600, titleH: 3566 };
|
|
1002
|
+
function bodyTitleGeom(bodyHeight, ov = {}) {
|
|
1003
|
+
const s = gaejosikSizes(bodyHeight, ov).bodyTitle / 2200;
|
|
1004
|
+
return { barH: BODY_TITLE_GEOM.barH, titleH: Math.round(BODY_TITLE_GEOM.titleH * s) };
|
|
1005
|
+
}
|
|
1006
|
+
|
|
1007
|
+
// src/hwpx/gongmun.ts
|
|
1008
|
+
var OFFICIAL_MARGINS = { top: 20, bottom: 15, left: 20, right: 15 };
|
|
1009
|
+
var GAEJOSIK_MARGINS = { top: 15, bottom: 15, left: 20, right: 20 };
|
|
1010
|
+
var GAEJOSIK_HEADER_FOOTER = 4251;
|
|
1011
|
+
var PRESET_DEFAULTS = {
|
|
1012
|
+
// 서울시 실결재 104건 중 12pt 64건(13pt 30, 15pt 5) — 지배값을 기본으로 사용.
|
|
1013
|
+
official: { bodyPt: 12, lineSpacing: 160, numbering: "standard" },
|
|
1014
|
+
report: { bodyPt: 15, lineSpacing: 160, numbering: "report" },
|
|
1015
|
+
// 실측 추진계획안: □ → ㅇ → * 계층.
|
|
1016
|
+
plan: { bodyPt: 15, lineSpacing: 160, numbering: "report" },
|
|
1017
|
+
notice: { bodyPt: 15, lineSpacing: 160, numbering: "standard" },
|
|
1018
|
+
minutes: { bodyPt: 14, lineSpacing: 130, numbering: "standard" },
|
|
1019
|
+
gaejosik: { bodyPt: 15, lineSpacing: 160, numbering: "gaejosik" },
|
|
1020
|
+
// 보도자료 — 실측(국토부 실물): 본문 바탕 14pt 160%, □→ㅇ→*(각주) 부호
|
|
1021
|
+
press: { bodyPt: 14, lineSpacing: 160, numbering: "report" }
|
|
1022
|
+
};
|
|
1023
|
+
var PRESET_ALIAS = {
|
|
1024
|
+
official: "official",
|
|
1025
|
+
\uAE30\uC548\uBB38: "official",
|
|
1026
|
+
\uC2DC\uD589\uBB38: "official",
|
|
1027
|
+
\uACF5\uBB38: "official",
|
|
1028
|
+
\uACF5\uBB38\uC11C: "official",
|
|
1029
|
+
report: "report",
|
|
1030
|
+
\uBCF4\uACE0\uC11C: "report",
|
|
1031
|
+
plan: "plan",
|
|
1032
|
+
\uACC4\uD68D\uC11C: "plan",
|
|
1033
|
+
\uACC4\uD68D: "plan",
|
|
1034
|
+
notice: "notice",
|
|
1035
|
+
\uD1B5\uC9C0: "notice",
|
|
1036
|
+
\uC54C\uB9BC: "notice",
|
|
1037
|
+
\uC548\uB0B4: "notice",
|
|
1038
|
+
minutes: "minutes",
|
|
1039
|
+
\uD68C\uC758\uB85D: "minutes",
|
|
1040
|
+
gaejosik: "gaejosik",
|
|
1041
|
+
\uAC1C\uC870\uC2DD: "gaejosik",
|
|
1042
|
+
\uAC1C\uC870\uC2DD\uBCF4\uACE0\uC11C: "gaejosik",
|
|
1043
|
+
\uC815\uBD80\uBCF4\uACE0\uC11C: "gaejosik",
|
|
1044
|
+
\uC815\uBD80\uD45C\uC900\uAC1C\uC870\uC2DD\uBCF4\uACE0\uC11C: "gaejosik",
|
|
1045
|
+
press: "press",
|
|
1046
|
+
\uBCF4\uB3C4\uC790\uB8CC: "press"
|
|
1047
|
+
};
|
|
1048
|
+
function normalizeGongmunPreset(preset) {
|
|
1049
|
+
if (!preset) return "official";
|
|
1050
|
+
return PRESET_ALIAS[preset.trim()] ?? "official";
|
|
1051
|
+
}
|
|
1052
|
+
function usesReportFonts(preset) {
|
|
1053
|
+
return preset === "gaejosik" || preset === "report" || preset === "plan";
|
|
1054
|
+
}
|
|
1055
|
+
function usesAsteriskThird(preset) {
|
|
1056
|
+
return preset === "plan" || preset === "press";
|
|
1057
|
+
}
|
|
1058
|
+
function needsGaejosikAssets(gongmun) {
|
|
1059
|
+
return usesReportFonts(gongmun.preset) || gongmun.cover !== null || gongmun.toc;
|
|
1060
|
+
}
|
|
1061
|
+
function assertFiniteRange(name, value, min, max) {
|
|
1062
|
+
if (!Number.isFinite(value) || value < min || value > max) {
|
|
1063
|
+
throw new KordocError(`${name} must be a finite number between ${min} and ${max}`);
|
|
861
1064
|
}
|
|
862
|
-
return out;
|
|
863
1065
|
}
|
|
864
|
-
function
|
|
865
|
-
if (
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
case "ROMAN_SMALL":
|
|
890
|
-
return toRoman(n).toLowerCase();
|
|
891
|
-
default:
|
|
892
|
-
return String(n);
|
|
1066
|
+
function validateGongmunOptions(opts) {
|
|
1067
|
+
if (opts.bodyPt !== void 0) assertFiniteRange("bodyPt", opts.bodyPt, 6, 40);
|
|
1068
|
+
if (opts.lineSpacing !== void 0) assertFiniteRange("lineSpacing", opts.lineSpacing, 50, 300);
|
|
1069
|
+
if (typeof opts.autoFit === "object" && opts.autoFit.minRatio !== void 0) {
|
|
1070
|
+
assertFiniteRange("autoFit.minRatio", opts.autoFit.minRatio, 50, 99);
|
|
1071
|
+
}
|
|
1072
|
+
if (opts.margins) {
|
|
1073
|
+
for (const side of ["top", "bottom", "left", "right"]) {
|
|
1074
|
+
const value = opts.margins[side];
|
|
1075
|
+
assertFiniteRange(`margins.${side}`, value, 0, 200);
|
|
1076
|
+
}
|
|
1077
|
+
if (opts.margins.left + opts.margins.right >= 210) {
|
|
1078
|
+
throw new KordocError("margins.left + margins.right must be less than 210");
|
|
1079
|
+
}
|
|
1080
|
+
if (opts.margins.top + opts.margins.bottom >= 297) {
|
|
1081
|
+
throw new KordocError("margins.top + margins.bottom must be less than 297");
|
|
1082
|
+
}
|
|
1083
|
+
}
|
|
1084
|
+
if (opts.sizes) {
|
|
1085
|
+
for (const [key, value] of Object.entries(opts.sizes)) {
|
|
1086
|
+
if (value !== void 0) assertFiniteRange(`sizes.${key}`, value, 6, 60);
|
|
1087
|
+
}
|
|
1088
|
+
}
|
|
1089
|
+
if (opts.approval && opts.approval.length > 6) {
|
|
1090
|
+
throw new KordocError("approval must contain at most 6 labels");
|
|
893
1091
|
}
|
|
894
1092
|
}
|
|
895
|
-
function
|
|
896
|
-
const
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
if (
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
1093
|
+
function incompatibleGongmunWarnings(opts) {
|
|
1094
|
+
const warns = [];
|
|
1095
|
+
const preset = normalizeGongmunPreset(opts.preset);
|
|
1096
|
+
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`);
|
|
1097
|
+
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`);
|
|
1098
|
+
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`);
|
|
1099
|
+
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`);
|
|
1100
|
+
if (preset === "press" && (opts.cover === true || typeof opts.cover === "object" || opts.toc === true)) {
|
|
1101
|
+
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");
|
|
904
1102
|
}
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
const ref = sm.paraHeadings.get(prId);
|
|
908
|
-
if (!ref) return styleHeading ? { headingLevel: styleHeading } : null;
|
|
909
|
-
if (ref.type === "BULLET") {
|
|
910
|
-
const char = sm.bullets.get(ref.idRef);
|
|
911
|
-
return char ? { prefix: char } : null;
|
|
1103
|
+
if (opts.sizes && Object.keys(opts.sizes).length > 0 && !usesReportFonts(preset)) {
|
|
1104
|
+
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`);
|
|
912
1105
|
}
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
const headingLevel = ref.type === "OUTLINE" ? Math.min(ref.level + 1, 6) : void 0;
|
|
916
|
-
const numDef = sm.numberings.get(numId);
|
|
917
|
-
if (!numDef) return headingLevel ? { headingLevel } : null;
|
|
918
|
-
let counters = ctx.shared.numState.get(numId);
|
|
919
|
-
if (!counters) {
|
|
920
|
-
counters = new Array(11).fill(0);
|
|
921
|
-
ctx.shared.numState.set(numId, counters);
|
|
1106
|
+
if (opts.suppressSingle && (opts.numbering ?? PRESET_DEFAULTS[preset].numbering) !== "standard") {
|
|
1107
|
+
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`);
|
|
922
1108
|
}
|
|
923
|
-
|
|
1109
|
+
return warns;
|
|
1110
|
+
}
|
|
1111
|
+
function resolveGongmun(opts) {
|
|
1112
|
+
validateGongmunOptions(opts);
|
|
1113
|
+
const preset = normalizeGongmunPreset(opts.preset);
|
|
1114
|
+
const d = PRESET_DEFAULTS[preset];
|
|
1115
|
+
const bodyPt = opts.bodyPt ?? d.bodyPt;
|
|
1116
|
+
const autoFitMinRatio = opts.autoFit === false ? null : typeof opts.autoFit === "object" ? Math.min(Math.max(opts.autoFit.minRatio ?? 90, 50), 99) : 90;
|
|
1117
|
+
const coverOn = opts.cover !== void 0 ? opts.cover !== false : preset === "gaejosik";
|
|
1118
|
+
const coverOpts = typeof opts.cover === "object" ? opts.cover : {};
|
|
1119
|
+
const gaejosik = preset === "gaejosik";
|
|
1120
|
+
const reportFamily = gaejosik || preset === "report" || preset === "plan" || preset === "notice" || preset === "press";
|
|
1121
|
+
return {
|
|
1122
|
+
preset,
|
|
1123
|
+
bodyFont: opts.bodyFont ?? "myeongjo",
|
|
1124
|
+
bodyHeight: Math.round(bodyPt * 100),
|
|
1125
|
+
lineSpacing: opts.lineSpacing ?? d.lineSpacing,
|
|
1126
|
+
numbering: opts.numbering ?? d.numbering,
|
|
1127
|
+
margins: opts.margins ?? (reportFamily ? GAEJOSIK_MARGINS : OFFICIAL_MARGINS),
|
|
1128
|
+
centerTitle: opts.centerTitle ?? true,
|
|
1129
|
+
autoFitMinRatio,
|
|
1130
|
+
// 보도자료는 머리박스가 1페이지 최상단을 차지하는 서식이라 표지·목차와 양립 불가 —
|
|
1131
|
+
// 켜면 머리박스가 표지에 얹히고 25pt 제목·부제가 유실된다 (docHead 프리셋 게이팅과 동일 관례)
|
|
1132
|
+
cover: coverOn && preset !== "press" ? { date: coverOpts.date ?? null, org: coverOpts.org ?? "" } : null,
|
|
1133
|
+
toc: preset !== "press" && (opts.toc ?? gaejosik),
|
|
1134
|
+
fonts: opts.fonts ?? {},
|
|
1135
|
+
sizes: opts.sizes ?? {},
|
|
1136
|
+
// 쪽번호 — 보고서 계열 관행(실측: 2_보고서 양식·추진계획·공고문 전부 하단 중앙)
|
|
1137
|
+
pageNumbers: opts.pageNumbers ?? (gaejosik || preset === "report" || preset === "plan"),
|
|
1138
|
+
// 머리말·꼬리말 — 실측: 보고서 계열 15mm(GT3·t2·춘천·브라더), 공고·보도 10mm,
|
|
1139
|
+
// 기안문 0(실결재 41/60건 h0/f0)
|
|
1140
|
+
headerFooter: usesReportFonts(preset) ? GAEJOSIK_HEADER_FOOTER : preset === "notice" || preset === "press" ? 2835 : 0,
|
|
1141
|
+
// "끝." — 기안문 규정(본문 끝 2타+"끝."). 그 외는 opt-in
|
|
1142
|
+
endMark: opts.endMark ?? preset === "official",
|
|
1143
|
+
approval: opts.approval && opts.approval.length > 0 ? opts.approval : null,
|
|
1144
|
+
// 본문 제목박스 — 실측(GT3·GT12): 목차 뒤 본문 시작에 제목 반복. 표지 켜진 개조식 기본
|
|
1145
|
+
bodyTitleBox: opts.bodyTitleBox ?? (gaejosik && coverOn),
|
|
1146
|
+
// h2 말머리 — 실측: 보고서 양식 □ 대항목(QA-2), 공고문 아라비아("1. 사업개요", 바이오헬스 실측)
|
|
1147
|
+
h2Marker: opts.h2Marker ?? (preset === "report" || preset === "plan" ? "box" : preset === "notice" ? "number" : "none"),
|
|
1148
|
+
// 2단계 부호 — 실결재 기안문·공고문 ㅇ 지배(60건 중 ㅇ134:○5), 보고서 양식 계열 ○
|
|
1149
|
+
bullet2: opts.bullet2 ?? (preset === "plan" || preset === "notice" || preset === "press" ? "\u3147" : "\u25CB"),
|
|
1150
|
+
// 단일 형제 부호 생략 — 규정이지만 부호 없는 계단이 실무 눈에 어색 (실무자 QA)
|
|
1151
|
+
suppressSingle: opts.suppressSingle ?? false,
|
|
1152
|
+
docHead: preset === "official" && opts.docHead ? opts.docHead : null,
|
|
1153
|
+
docFoot: preset === "official" && opts.docFoot ? opts.docFoot : null,
|
|
1154
|
+
reportInfo: opts.reportInfo?.trim() || null,
|
|
1155
|
+
noticeHead: preset === "notice" && opts.noticeHead ? opts.noticeHead : null,
|
|
1156
|
+
// 보도자료 머리박스는 프리셋 자체가 요구 — 옵션 미지정이어도 빈 객체
|
|
1157
|
+
press: preset === "press" ? opts.press ?? {} : null
|
|
1158
|
+
};
|
|
1159
|
+
}
|
|
1160
|
+
var HANGUL_INITIALS = [0, 2, 3, 5, 6, 7, 9, 11, 12, 14, 15, 16, 17, 18];
|
|
1161
|
+
var HANGUL_MEDIALS = [0, 4, 8, 13, 18, 20];
|
|
1162
|
+
function hangulOrdinal(n) {
|
|
1163
|
+
const cols = HANGUL_INITIALS.length;
|
|
1164
|
+
const vowel = HANGUL_MEDIALS[Math.min(Math.floor(n / cols), HANGUL_MEDIALS.length - 1)];
|
|
1165
|
+
const init = HANGUL_INITIALS[n % cols];
|
|
1166
|
+
return String.fromCodePoint(44032 + init * 588 + vowel * 28);
|
|
1167
|
+
}
|
|
1168
|
+
function circledNumber(n) {
|
|
1169
|
+
if (n < 20) return String.fromCodePoint(9312 + n);
|
|
1170
|
+
if (n < 35) return String.fromCodePoint(12881 + (n - 20));
|
|
1171
|
+
if (n < 50) return String.fromCodePoint(12977 + (n - 35));
|
|
1172
|
+
return `(${n + 1})`;
|
|
1173
|
+
}
|
|
1174
|
+
function circledHangul(n) {
|
|
1175
|
+
return n < 14 ? String.fromCodePoint(12910 + n) : hangulOrdinal(n);
|
|
1176
|
+
}
|
|
1177
|
+
var REPORT_BULLETS = ["\u25A1", "\u25CB", "-", "\u318D"];
|
|
1178
|
+
var ASTERISK_BULLETS = ["\u25A1", "\u25CB", "*", "\u318D"];
|
|
1179
|
+
function standardMarker(depth, n) {
|
|
1180
|
+
switch (depth) {
|
|
1181
|
+
case 0:
|
|
1182
|
+
return `${n + 1}.`;
|
|
1183
|
+
case 1:
|
|
1184
|
+
return `${hangulOrdinal(n)}.`;
|
|
1185
|
+
case 2:
|
|
1186
|
+
return `${n + 1})`;
|
|
1187
|
+
case 3:
|
|
1188
|
+
return `${hangulOrdinal(n)})`;
|
|
1189
|
+
case 4:
|
|
1190
|
+
return `(${n + 1})`;
|
|
1191
|
+
case 5:
|
|
1192
|
+
return `(${hangulOrdinal(n)})`;
|
|
1193
|
+
case 6:
|
|
1194
|
+
return circledNumber(n);
|
|
1195
|
+
case 7:
|
|
1196
|
+
return circledHangul(n);
|
|
1197
|
+
default:
|
|
1198
|
+
return circledHangul(n);
|
|
1199
|
+
}
|
|
1200
|
+
}
|
|
1201
|
+
function reportMarker(depth, bullet2 = "\u25CB", asteriskThird = false) {
|
|
1202
|
+
const bullets = asteriskThird ? ASTERISK_BULLETS : REPORT_BULLETS;
|
|
1203
|
+
const m = bullets[Math.min(depth, bullets.length - 1)];
|
|
1204
|
+
return depth === 1 ? bullet2 : m;
|
|
1205
|
+
}
|
|
1206
|
+
function markerWidth(marker, bodyHeight) {
|
|
1207
|
+
let em = SPACE_EM_FIXED;
|
|
1208
|
+
for (const c of marker) em += charWidthEm1000(c.codePointAt(0));
|
|
1209
|
+
return Math.round(em / 1e3 * bodyHeight);
|
|
1210
|
+
}
|
|
1211
|
+
function levelIndent(depth, bodyHeight, numbering, sizes = {}, bullet2 = "\u25CB", asteriskThird = false) {
|
|
1212
|
+
if (numbering === "gaejosik") return gaejosikLevelIndent(depth, bodyHeight, sizes, bullet2);
|
|
1213
|
+
const marker = numbering === "report" ? reportMarker(depth, bullet2, asteriskThird) : standardMarker(depth, 0);
|
|
1214
|
+
return { left: Math.round(depth * bodyHeight), indent: -markerWidth(marker, bodyHeight) };
|
|
1215
|
+
}
|
|
1216
|
+
function computeSuppression(depths) {
|
|
1217
|
+
const counts = /* @__PURE__ */ new Map();
|
|
1218
|
+
const keys = [];
|
|
1219
|
+
const path = [];
|
|
1220
|
+
for (const depth of depths) {
|
|
1221
|
+
path.length = depth + 1;
|
|
1222
|
+
path[depth] = (path[depth] ?? 0) + 1;
|
|
1223
|
+
const parentKey = path.slice(0, depth).join(".") + "|" + depth;
|
|
1224
|
+
keys.push(parentKey);
|
|
1225
|
+
counts.set(parentKey, (counts.get(parentKey) ?? 0) + 1);
|
|
1226
|
+
}
|
|
1227
|
+
return keys.map((k) => (counts.get(k) ?? 0) <= 1);
|
|
1228
|
+
}
|
|
1229
|
+
var GongmunNumberer = class {
|
|
1230
|
+
constructor(numbering, bullet2 = "\u25CB", asteriskThird = false) {
|
|
1231
|
+
this.numbering = numbering;
|
|
1232
|
+
this.bullet2 = bullet2;
|
|
1233
|
+
this.asteriskThird = asteriskThird;
|
|
1234
|
+
}
|
|
1235
|
+
counts = [];
|
|
1236
|
+
/** depth 항목 하나에 대한 마커. suppress=true면 빈 문자열(부호 없음) */
|
|
1237
|
+
next(depth, suppress) {
|
|
1238
|
+
this.counts.length = depth + 1;
|
|
1239
|
+
const n = this.counts[depth] ?? 0;
|
|
1240
|
+
this.counts[depth] = n + 1;
|
|
1241
|
+
if (suppress) return "";
|
|
1242
|
+
if (this.numbering === "gaejosik") return gaejosikMarker(depth, this.bullet2);
|
|
1243
|
+
return this.numbering === "report" ? reportMarker(depth, this.bullet2, this.asteriskThird) : standardMarker(depth, n);
|
|
1244
|
+
}
|
|
1245
|
+
reset() {
|
|
1246
|
+
this.counts = [];
|
|
1247
|
+
}
|
|
1248
|
+
};
|
|
1249
|
+
|
|
1250
|
+
// src/hwpx/para-heading.ts
|
|
1251
|
+
var HANGUL_JAMO_SEQ = "\u3131\u3134\u3137\u3139\u3141\u3142\u3145\u3147\u3148\u314A\u314B\u314C\u314D\u314E";
|
|
1252
|
+
function toRoman(n) {
|
|
1253
|
+
if (n <= 0 || n >= 4e3) return String(n);
|
|
1254
|
+
const table2 = [
|
|
1255
|
+
[1e3, "M"],
|
|
1256
|
+
[900, "CM"],
|
|
1257
|
+
[500, "D"],
|
|
1258
|
+
[400, "CD"],
|
|
1259
|
+
[100, "C"],
|
|
1260
|
+
[90, "XC"],
|
|
1261
|
+
[50, "L"],
|
|
1262
|
+
[40, "XL"],
|
|
1263
|
+
[10, "X"],
|
|
1264
|
+
[9, "IX"],
|
|
1265
|
+
[5, "V"],
|
|
1266
|
+
[4, "IV"],
|
|
1267
|
+
[1, "I"]
|
|
1268
|
+
];
|
|
1269
|
+
let out = "";
|
|
1270
|
+
for (const [v, s] of table2) {
|
|
1271
|
+
while (n >= v) {
|
|
1272
|
+
out += s;
|
|
1273
|
+
n -= v;
|
|
1274
|
+
}
|
|
1275
|
+
}
|
|
1276
|
+
return out;
|
|
1277
|
+
}
|
|
1278
|
+
function formatHeadNumber(n, numFormat) {
|
|
1279
|
+
if (n <= 0) n = 1;
|
|
1280
|
+
switch (numFormat) {
|
|
1281
|
+
case "DIGIT":
|
|
1282
|
+
return String(n);
|
|
1283
|
+
// 서수 시퀀스는 생성기(gongmun.ts)와 단일 소스 (v4.0.5) — 종전 mod-14 순환은
|
|
1284
|
+
// 15번째 형제를 다시 '가'로 만들어 한글 실렌더(가→하→거→너 단모음 연속)와 어긋났다
|
|
1285
|
+
case "CIRCLED_DIGIT":
|
|
1286
|
+
return n <= 50 ? circledNumber(n - 1) : `(${n})`;
|
|
1287
|
+
case "HANGUL_SYLLABLE":
|
|
1288
|
+
return hangulOrdinal(n - 1);
|
|
1289
|
+
case "CIRCLED_HANGUL_SYLLABLE":
|
|
1290
|
+
return n <= 14 ? circledHangul(n - 1) : hangulOrdinal(n - 1);
|
|
1291
|
+
case "HANGUL_JAMO":
|
|
1292
|
+
return HANGUL_JAMO_SEQ[(n - 1) % HANGUL_JAMO_SEQ.length];
|
|
1293
|
+
case "CIRCLED_HANGUL_JAMO":
|
|
1294
|
+
return n <= 14 ? String.fromCodePoint(12896 + n - 1) : HANGUL_JAMO_SEQ[(n - 1) % 14];
|
|
1295
|
+
case "LATIN_CAPITAL":
|
|
1296
|
+
return String.fromCharCode(65 + (n - 1) % 26);
|
|
1297
|
+
case "LATIN_SMALL":
|
|
1298
|
+
return String.fromCharCode(97 + (n - 1) % 26);
|
|
1299
|
+
case "CIRCLED_LATIN_CAPITAL":
|
|
1300
|
+
return n <= 26 ? String.fromCodePoint(9398 + n - 1) : String.fromCharCode(65 + (n - 1) % 26);
|
|
1301
|
+
case "CIRCLED_LATIN_SMALL":
|
|
1302
|
+
return n <= 26 ? String.fromCodePoint(9424 + n - 1) : String.fromCharCode(97 + (n - 1) % 26);
|
|
1303
|
+
case "ROMAN_CAPITAL":
|
|
1304
|
+
return toRoman(n);
|
|
1305
|
+
case "ROMAN_SMALL":
|
|
1306
|
+
return toRoman(n).toLowerCase();
|
|
1307
|
+
default:
|
|
1308
|
+
return String(n);
|
|
1309
|
+
}
|
|
1310
|
+
}
|
|
1311
|
+
function resolveParaHeading(paraEl, ctx) {
|
|
1312
|
+
const sm = ctx.styleMap;
|
|
1313
|
+
if (!sm) return null;
|
|
1314
|
+
const styleId = paraEl.getAttribute("styleIDRef");
|
|
1315
|
+
let styleHeading;
|
|
1316
|
+
if (styleId && styleId !== "0") {
|
|
1317
|
+
const st = sm.styles.get(styleId);
|
|
1318
|
+
const m = st && /^개요\s*([1-6])$/.exec(st.name);
|
|
1319
|
+
if (m) styleHeading = Math.min(parseInt(m[1], 10), 6);
|
|
1320
|
+
}
|
|
1321
|
+
const prId = paraEl.getAttribute("paraPrIDRef");
|
|
1322
|
+
if (!prId) return styleHeading ? { headingLevel: styleHeading } : null;
|
|
1323
|
+
const ref = sm.paraHeadings.get(prId);
|
|
1324
|
+
if (!ref) return styleHeading ? { headingLevel: styleHeading } : null;
|
|
1325
|
+
if (ref.type === "BULLET") {
|
|
1326
|
+
const char = sm.bullets.get(ref.idRef);
|
|
1327
|
+
return char ? { prefix: char } : null;
|
|
1328
|
+
}
|
|
1329
|
+
const numId = ref.type === "OUTLINE" ? ctx.outlineNumId || "1" : ref.idRef;
|
|
1330
|
+
const level = Math.min(ref.level + 1, 10);
|
|
1331
|
+
const headingLevel = ref.type === "OUTLINE" ? Math.min(ref.level + 1, 6) : void 0;
|
|
1332
|
+
const numDef = sm.numberings.get(numId);
|
|
1333
|
+
if (!numDef) return headingLevel ? { headingLevel } : null;
|
|
1334
|
+
let counters = ctx.shared.numState.get(numId);
|
|
1335
|
+
if (!counters) {
|
|
1336
|
+
counters = new Array(11).fill(0);
|
|
1337
|
+
ctx.shared.numState.set(numId, counters);
|
|
1338
|
+
}
|
|
1339
|
+
const head = numDef.heads.get(level);
|
|
924
1340
|
counters[level] = counters[level] === 0 ? head?.start ?? 1 : counters[level] + 1;
|
|
925
1341
|
for (let l = level + 1; l <= 10; l++) counters[l] = 0;
|
|
926
1342
|
const fmtText = head ? head.text.trim() : `^${level}.`;
|
|
@@ -963,7 +1379,7 @@ function buildTableWithCellMeta(state) {
|
|
|
963
1379
|
for (const row of state.rows) {
|
|
964
1380
|
for (const src of row) {
|
|
965
1381
|
flatIdx++;
|
|
966
|
-
const needsBlocks = src.
|
|
1382
|
+
const needsBlocks = !!src.blocks && src.blocks.length > 0 && (src.hasStructure || src.blocks.some((b) => b.spans));
|
|
967
1383
|
if (!needsBlocks && !src.isHeader) continue;
|
|
968
1384
|
let target;
|
|
969
1385
|
const trimmed = src.text.trim();
|
|
@@ -1062,6 +1478,15 @@ function walkSection(node, blocks, tableCtx, tableStack, ctx, depth = 0) {
|
|
|
1062
1478
|
const localTag = tag.replace(/^[^:]+:/, "");
|
|
1063
1479
|
switch (localTag) {
|
|
1064
1480
|
case "tbl": {
|
|
1481
|
+
if (!tableCtx) {
|
|
1482
|
+
const chan = kordocTableChannel(el, ctx);
|
|
1483
|
+
if (chan) {
|
|
1484
|
+
if (chan.kind === "heading" && chan.text) {
|
|
1485
|
+
blocks.push({ type: "heading", level: chan.level, text: chan.text, pageNumber: ctx.sectionNum });
|
|
1486
|
+
}
|
|
1487
|
+
break;
|
|
1488
|
+
}
|
|
1489
|
+
}
|
|
1065
1490
|
if (tableCtx) tableStack.push(tableCtx);
|
|
1066
1491
|
const newTable = { rows: [], currentRow: [], cell: null };
|
|
1067
1492
|
walkSection(el, blocks, newTable, tableStack, ctx, depth + 1);
|
|
@@ -1069,12 +1494,17 @@ function walkSection(node, blocks, tableCtx, tableStack, ctx, depth = 0) {
|
|
|
1069
1494
|
break;
|
|
1070
1495
|
}
|
|
1071
1496
|
// 표/도표 캡션 — IRTable.caption으로 보존 (v3.0, 기존 무음 드롭 수정)
|
|
1072
|
-
case "caption":
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
if (
|
|
1497
|
+
case "caption": {
|
|
1498
|
+
const capText = collectSubListText(el, ctx);
|
|
1499
|
+
if (capText) {
|
|
1500
|
+
if (tableCtx) {
|
|
1501
|
+
tableCtx.caption = (tableCtx.caption ? tableCtx.caption + "\n" : "") + capText;
|
|
1502
|
+
} else {
|
|
1503
|
+
blocks.push({ type: "paragraph", text: capText, pageNumber: ctx.sectionNum });
|
|
1504
|
+
}
|
|
1076
1505
|
}
|
|
1077
1506
|
break;
|
|
1507
|
+
}
|
|
1078
1508
|
case "tr":
|
|
1079
1509
|
if (tableCtx) {
|
|
1080
1510
|
tableCtx.currentRow = [];
|
|
@@ -1116,8 +1546,8 @@ function walkSection(node, blocks, tableCtx, tableStack, ctx, depth = 0) {
|
|
|
1116
1546
|
const { text: rawText, href, footnote, style } = extractParagraphInfo(el, ctx.styleMap, ctx);
|
|
1117
1547
|
let text = rawText;
|
|
1118
1548
|
let headingLevel;
|
|
1549
|
+
const ph = resolveParaHeading(el, ctx);
|
|
1119
1550
|
if (text) {
|
|
1120
|
-
const ph = resolveParaHeading(el, ctx);
|
|
1121
1551
|
if (ph?.prefix) text = ph.prefix + " " + text;
|
|
1122
1552
|
headingLevel = ph?.headingLevel;
|
|
1123
1553
|
}
|
|
@@ -1126,13 +1556,40 @@ function walkSection(node, blocks, tableCtx, tableStack, ctx, depth = 0) {
|
|
|
1126
1556
|
const cell2 = tableCtx.cell;
|
|
1127
1557
|
if (footnote) text += ` (\uC8FC: ${footnote})`;
|
|
1128
1558
|
cell2.text += (cell2.text ? "\n" : "") + text;
|
|
1129
|
-
|
|
1559
|
+
const cellBlock = { type: "paragraph", text, pageNumber: ctx.sectionNum };
|
|
1560
|
+
if (ctx.shared.kordocLayout === "default" && !ph?.prefix) {
|
|
1561
|
+
const spans = extractRunSpans(el, ctx);
|
|
1562
|
+
if (spans && spans.map((s) => s.text).join("").replace(/[ \t]+/g, " ").trim() === text) {
|
|
1563
|
+
cellBlock.spans = spans;
|
|
1564
|
+
}
|
|
1565
|
+
}
|
|
1566
|
+
;
|
|
1567
|
+
(cell2.blocks ??= []).push(cellBlock);
|
|
1130
1568
|
} else if (!tableCtx) {
|
|
1569
|
+
if (/^─{10,}$/.test(text)) {
|
|
1570
|
+
blocks.push({ type: "separator", pageNumber: ctx.sectionNum });
|
|
1571
|
+
tableCtx = walkParagraphChildren(el, blocks, tableCtx, tableStack, ctx, depth + 1);
|
|
1572
|
+
break;
|
|
1573
|
+
}
|
|
1131
1574
|
const block = { type: headingLevel ? "heading" : "paragraph", text, pageNumber: ctx.sectionNum };
|
|
1132
1575
|
if (headingLevel) block.level = headingLevel;
|
|
1133
1576
|
if (style) block.style = style;
|
|
1134
1577
|
if (href) block.href = href;
|
|
1135
1578
|
if (footnote) block.footnoteText = footnote;
|
|
1579
|
+
const ind = ctx.styleMap?.paraIndents.get(el.getAttribute("paraPrIDRef") ?? "");
|
|
1580
|
+
if (ind) {
|
|
1581
|
+
const eff = ind.left + Math.max(0, ind.intent);
|
|
1582
|
+
if (eff > 0) block.indent = eff;
|
|
1583
|
+
}
|
|
1584
|
+
if (!headingLevel && ctx.shared.kordocLayout === "default") {
|
|
1585
|
+
if (!ph?.prefix) {
|
|
1586
|
+
const spans = extractRunSpans(el, ctx);
|
|
1587
|
+
if (spans && spans.map((s) => s.text).join("").replace(/[ \t]+/g, " ").trim() === text) {
|
|
1588
|
+
block.spans = spans;
|
|
1589
|
+
}
|
|
1590
|
+
}
|
|
1591
|
+
if (el.getAttribute("paraPrIDRef") === KORDOC_PARA_QUOTE) block.quote = true;
|
|
1592
|
+
}
|
|
1136
1593
|
blocks.push(block);
|
|
1137
1594
|
} else {
|
|
1138
1595
|
blocks.push({ type: "paragraph", text, pageNumber: ctx.sectionNum });
|
|
@@ -1214,6 +1671,34 @@ function mergeBlocksIntoCell(cell2, sink) {
|
|
|
1214
1671
|
}
|
|
1215
1672
|
}
|
|
1216
1673
|
}
|
|
1674
|
+
function kordocTableChannel(tblEl, ctx) {
|
|
1675
|
+
const found = findKordocMarkedCell(tblEl, 0);
|
|
1676
|
+
if (!found) return null;
|
|
1677
|
+
const m = found.name.match(/^__kordoc_(?:h([1-6])|(toc|skip))$/);
|
|
1678
|
+
if (!m) return null;
|
|
1679
|
+
if (m[2]) return { kind: "skip" };
|
|
1680
|
+
const text = collectSubListText(found.cell, ctx).trim();
|
|
1681
|
+
return { kind: "heading", level: Number(m[1]), text };
|
|
1682
|
+
}
|
|
1683
|
+
function findKordocMarkedCell(el, depth) {
|
|
1684
|
+
if (depth > 6) return null;
|
|
1685
|
+
const children = el.childNodes;
|
|
1686
|
+
if (!children) return null;
|
|
1687
|
+
for (let i = 0; i < children.length; i++) {
|
|
1688
|
+
const ch = children[i];
|
|
1689
|
+
if (ch.nodeType !== 1) continue;
|
|
1690
|
+
const tag = (ch.tagName || ch.localName || "").replace(/^[^:]+:/, "");
|
|
1691
|
+
if (tag === "tc") {
|
|
1692
|
+
const name = ch.getAttribute("name") || "";
|
|
1693
|
+
if (name.startsWith("__kordoc_")) return { cell: ch, name };
|
|
1694
|
+
continue;
|
|
1695
|
+
}
|
|
1696
|
+
if (tag === "tbl" && depth > 0) continue;
|
|
1697
|
+
const found = findKordocMarkedCell(ch, depth + 1);
|
|
1698
|
+
if (found) return found;
|
|
1699
|
+
}
|
|
1700
|
+
return null;
|
|
1701
|
+
}
|
|
1217
1702
|
function collectSubListText(el, ctx, depth = 0) {
|
|
1218
1703
|
if (depth > 10) return "";
|
|
1219
1704
|
const parts = [];
|
|
@@ -1239,7 +1724,7 @@ function walkParagraphChildren(node, blocks, tableCtx, tableStack, ctx, depth =
|
|
|
1239
1724
|
if (depth > MAX_XML_DEPTH) return tableCtx;
|
|
1240
1725
|
const children = node.childNodes;
|
|
1241
1726
|
if (!children) return tableCtx;
|
|
1242
|
-
const walkChildren = (parent, d) => {
|
|
1727
|
+
const walkChildren = (parent, d, inShape = false) => {
|
|
1243
1728
|
if (d > MAX_XML_DEPTH) return;
|
|
1244
1729
|
const kids2 = parent.childNodes;
|
|
1245
1730
|
if (!kids2) return;
|
|
@@ -1249,10 +1734,26 @@ function walkParagraphChildren(node, blocks, tableCtx, tableStack, ctx, depth =
|
|
|
1249
1734
|
const tag = el.tagName || el.localName || "";
|
|
1250
1735
|
const localTag = tag.replace(/^[^:]+:/, "");
|
|
1251
1736
|
if (localTag === "tbl") {
|
|
1737
|
+
if (!tableCtx) {
|
|
1738
|
+
const chan = kordocTableChannel(el, ctx);
|
|
1739
|
+
if (chan) {
|
|
1740
|
+
if (chan.kind === "heading" && chan.text) {
|
|
1741
|
+
blocks.push({ type: "heading", level: chan.level, text: chan.text, pageNumber: ctx.sectionNum });
|
|
1742
|
+
}
|
|
1743
|
+
continue;
|
|
1744
|
+
}
|
|
1745
|
+
}
|
|
1252
1746
|
if (tableCtx) tableStack.push(tableCtx);
|
|
1253
1747
|
const newTable = { rows: [], currentRow: [], cell: null };
|
|
1254
1748
|
walkSection(el, blocks, newTable, tableStack, ctx, d + 1);
|
|
1255
1749
|
tableCtx = completeTable(newTable, tableStack, blocks, ctx);
|
|
1750
|
+
} else if (localTag === "caption" && !inShape) {
|
|
1751
|
+
const capText = collectSubListText(el, ctx);
|
|
1752
|
+
if (capText) {
|
|
1753
|
+
if (tableCtx?.cell) mergeBlocksIntoCell(tableCtx.cell, [{ type: "paragraph", text: capText, pageNumber: ctx.sectionNum }]);
|
|
1754
|
+
else if (tableCtx) tableCtx.caption = (tableCtx.caption ? tableCtx.caption + "\n" : "") + capText;
|
|
1755
|
+
else blocks.push({ type: "paragraph", text: capText, pageNumber: ctx.sectionNum });
|
|
1756
|
+
}
|
|
1256
1757
|
} else if (localTag === "pic" || localTag === "shape" || localTag === "drawingObject") {
|
|
1257
1758
|
if (tableCtx?.cell) {
|
|
1258
1759
|
const sink = [];
|
|
@@ -1269,8 +1770,10 @@ function walkParagraphChildren(node, blocks, tableCtx, tableStack, ctx, depth =
|
|
|
1269
1770
|
} else {
|
|
1270
1771
|
extractDrawTextBlocks(el, blocks, ctx);
|
|
1271
1772
|
}
|
|
1272
|
-
} else if (localTag === "r" || localTag === "run" || localTag === "ctrl"
|
|
1273
|
-
walkChildren(el, d + 1);
|
|
1773
|
+
} else if (localTag === "r" || localTag === "run" || localTag === "ctrl") {
|
|
1774
|
+
walkChildren(el, d + 1, inShape);
|
|
1775
|
+
} else if (localTag === "rect" || localTag === "ellipse" || localTag === "polygon" || localTag === "line" || localTag === "arc" || localTag === "curve" || localTag === "connectLine" || localTag === "container") {
|
|
1776
|
+
walkChildren(el, d + 1, true);
|
|
1274
1777
|
}
|
|
1275
1778
|
}
|
|
1276
1779
|
};
|
|
@@ -1406,6 +1909,9 @@ function extractParagraphInfo(para2, styleMap, ctx) {
|
|
|
1406
1909
|
case "indexmark":
|
|
1407
1910
|
case "colPr":
|
|
1408
1911
|
break;
|
|
1912
|
+
// 캡션 — walkParagraphChildren의 caption 분기가 보존하므로 손실 경고 대상 아님
|
|
1913
|
+
case "caption":
|
|
1914
|
+
break;
|
|
1409
1915
|
// 미지원 요소 — 텍스트를 가졌으면 무음 손실 대신 경고
|
|
1410
1916
|
default: {
|
|
1411
1917
|
if (ctx?.warnings && extractTextFromNode(k)) {
|
|
@@ -1570,6 +2076,55 @@ function extractParagraphInfo(para2, styleMap, ctx) {
|
|
|
1570
2076
|
}
|
|
1571
2077
|
return { text: cleanText, href, footnote, style };
|
|
1572
2078
|
}
|
|
2079
|
+
var KORDOC_CHAR_CODE = "4";
|
|
2080
|
+
var KORDOC_PARA_QUOTE = "6";
|
|
2081
|
+
function extractRunSpans(para2, ctx) {
|
|
2082
|
+
const styleMap = ctx.styleMap;
|
|
2083
|
+
if (!styleMap) return null;
|
|
2084
|
+
const spans = [];
|
|
2085
|
+
let styled = false;
|
|
2086
|
+
const kids2 = para2.childNodes;
|
|
2087
|
+
if (!kids2) return null;
|
|
2088
|
+
for (let i = 0; i < kids2.length; i++) {
|
|
2089
|
+
const child = kids2[i];
|
|
2090
|
+
if (child.nodeType !== 1) continue;
|
|
2091
|
+
const tag = (child.tagName || child.localName || "").replace(/^[^:]+:/, "");
|
|
2092
|
+
if (tag === "linesegarray") continue;
|
|
2093
|
+
if (tag !== "run" && tag !== "r") return null;
|
|
2094
|
+
let text = "";
|
|
2095
|
+
const rkids = child.childNodes;
|
|
2096
|
+
for (let j = 0; j < (rkids?.length ?? 0); j++) {
|
|
2097
|
+
const rc = rkids[j];
|
|
2098
|
+
if (rc.nodeType !== 1) continue;
|
|
2099
|
+
const rtag = (rc.tagName || rc.localName || "").replace(/^[^:]+:/, "");
|
|
2100
|
+
if (rtag === "t") {
|
|
2101
|
+
const tkids = rc.childNodes;
|
|
2102
|
+
for (let k = 0; k < (tkids?.length ?? 0); k++) {
|
|
2103
|
+
const tk = tkids[k];
|
|
2104
|
+
if (tk.nodeType === 3) text += tk.textContent || "";
|
|
2105
|
+
else if (tk.nodeType === 1) return null;
|
|
2106
|
+
}
|
|
2107
|
+
} else if (rtag === "secPr" || rtag === "colPr") {
|
|
2108
|
+
} else if (rtag === "ctrl") {
|
|
2109
|
+
if (extractTextFromNode(rc)) return null;
|
|
2110
|
+
} else {
|
|
2111
|
+
return null;
|
|
2112
|
+
}
|
|
2113
|
+
}
|
|
2114
|
+
if (!text) continue;
|
|
2115
|
+
const prId = child.getAttribute("charPrIDRef") ?? "";
|
|
2116
|
+
const cp = styleMap.charProperties.get(prId);
|
|
2117
|
+
const span = { text };
|
|
2118
|
+
if (prId === KORDOC_CHAR_CODE) span.code = true;
|
|
2119
|
+
else {
|
|
2120
|
+
if (cp?.bold) span.bold = true;
|
|
2121
|
+
if (cp?.italic) span.italic = true;
|
|
2122
|
+
}
|
|
2123
|
+
if (span.bold || span.italic || span.code) styled = true;
|
|
2124
|
+
spans.push(span);
|
|
2125
|
+
}
|
|
2126
|
+
return styled && spans.length > 0 ? spans : null;
|
|
2127
|
+
}
|
|
1573
2128
|
|
|
1574
2129
|
// src/hwpx/images.ts
|
|
1575
2130
|
function imageExtToMime(ext) {
|
|
@@ -1759,9 +2314,20 @@ function extractFromBrokenZip(buffer) {
|
|
|
1759
2314
|
const markdown = blocksToMarkdown(blocks);
|
|
1760
2315
|
return { markdown, blocks, warnings: warnings.length > 0 ? warnings : void 0 };
|
|
1761
2316
|
}
|
|
1762
|
-
async function
|
|
1763
|
-
const
|
|
1764
|
-
|
|
2317
|
+
async function readKordocLayout(zip) {
|
|
2318
|
+
const file = zip.file("Contents/content.hpf") ?? zip.file("content.hpf");
|
|
2319
|
+
if (!file) return null;
|
|
2320
|
+
try {
|
|
2321
|
+
const xml = await file.async("text");
|
|
2322
|
+
if (!/<opf:meta\b[^>]*name="generator"[^>]*content="kordoc"/.test(xml)) return null;
|
|
2323
|
+
return xml.match(/<opf:meta\b[^>]*name="kordoc-layout"[^>]*content="([^"]*)"/)?.[1] ?? null;
|
|
2324
|
+
} catch {
|
|
2325
|
+
return null;
|
|
2326
|
+
}
|
|
2327
|
+
}
|
|
2328
|
+
async function resolveSectionPaths(zip) {
|
|
2329
|
+
const manifestPaths = ["Contents/content.hpf", "content.hpf"];
|
|
2330
|
+
for (const mp of manifestPaths) {
|
|
1765
2331
|
const mpLower = mp.toLowerCase();
|
|
1766
2332
|
const file = zip.file(mp) || Object.values(zip.files).find((f) => f.name.toLowerCase() === mpLower) || null;
|
|
1767
2333
|
if (!file) continue;
|
|
@@ -1889,6 +2455,7 @@ async function parseHwpxDocument(buffer, options) {
|
|
|
1889
2455
|
const totalTarget = pageFilter ? pageFilter.size : sectionPaths.length;
|
|
1890
2456
|
const blocks = [];
|
|
1891
2457
|
const shared = createSectionShared();
|
|
2458
|
+
shared.kordocLayout = await readKordocLayout(zip);
|
|
1892
2459
|
let parsedSections = 0;
|
|
1893
2460
|
for (let si = 0; si < sectionPaths.length; si++) {
|
|
1894
2461
|
if (pageFilter && !pageFilter.has(si + 1)) continue;
|
|
@@ -19591,452 +20158,160 @@ async function fillHwpx(hwpxBuffer, values, blockedLabels) {
|
|
|
19591
20158
|
row: rowIdx,
|
|
19592
20159
|
col: colIdx,
|
|
19593
20160
|
key: matchKey
|
|
19594
|
-
});
|
|
19595
|
-
} else {
|
|
19596
|
-
const paras = valueCell.paragraphs;
|
|
19597
|
-
if (paras.length === 0) continue;
|
|
19598
|
-
const newValue = cursor.consume(matchKey);
|
|
19599
|
-
if (newValue === void 0) continue;
|
|
19600
|
-
const l0 = led(paras[0]);
|
|
19601
|
-
l0.fullText = newValue;
|
|
19602
|
-
l0.ranges = [];
|
|
19603
|
-
l0.filledIdx.push(filled.length);
|
|
19604
|
-
l0.matchKeys.push(matchKey);
|
|
19605
|
-
for (let k = 1; k < paras.length; k++) {
|
|
19606
|
-
const lk = led(paras[k]);
|
|
19607
|
-
lk.fullText = "";
|
|
19608
|
-
lk.ranges = [];
|
|
19609
|
-
}
|
|
19610
|
-
matchedLabels.add(matchKey);
|
|
19611
|
-
filled.push({
|
|
19612
|
-
label: labelText.trim().replace(/[::]\s*$/, ""),
|
|
19613
|
-
value: newValue,
|
|
19614
|
-
row: rowIdx,
|
|
19615
|
-
col: colIdx,
|
|
19616
|
-
key: matchKey
|
|
19617
|
-
});
|
|
19618
|
-
}
|
|
19619
|
-
}
|
|
19620
|
-
}
|
|
19621
|
-
if (table2.rows.length >= 2) {
|
|
19622
|
-
const headerCells = table2.rows[0];
|
|
19623
|
-
const allLabels = headerCells.length > 0 && headerCells.every((cell2) => {
|
|
19624
|
-
const t = cellLabelText(cell2).trim();
|
|
19625
|
-
return t.length > 0 && t.length <= 20 && isLabelCell(t);
|
|
19626
|
-
});
|
|
19627
|
-
if (allLabels) {
|
|
19628
|
-
for (let rowIdx = 1; rowIdx < table2.rows.length; rowIdx++) {
|
|
19629
|
-
const dataCells = table2.rows[rowIdx];
|
|
19630
|
-
for (let colIdx = 0; colIdx < Math.min(headerCells.length, dataCells.length); colIdx++) {
|
|
19631
|
-
const headerLabel = normalizeLabel(cellLabelText(headerCells[colIdx]));
|
|
19632
|
-
if (blockedLabels?.has(headerLabel)) continue;
|
|
19633
|
-
const matchKey = findMatchingKey(headerLabel, cursor);
|
|
19634
|
-
if (matchKey === void 0) continue;
|
|
19635
|
-
if (!cursor.isArray(matchKey) && matchedLabels.has(matchKey)) continue;
|
|
19636
|
-
const newValue = cursor.consume(matchKey);
|
|
19637
|
-
if (newValue === void 0) continue;
|
|
19638
|
-
const paras = dataCells[colIdx].paragraphs;
|
|
19639
|
-
if (paras.length === 0) continue;
|
|
19640
|
-
const l0 = led(paras[0]);
|
|
19641
|
-
l0.fullText = newValue;
|
|
19642
|
-
l0.ranges = [];
|
|
19643
|
-
l0.filledIdx.push(filled.length);
|
|
19644
|
-
l0.matchKeys.push(matchKey);
|
|
19645
|
-
for (let k = 1; k < paras.length; k++) {
|
|
19646
|
-
const lk = led(paras[k]);
|
|
19647
|
-
lk.fullText = "";
|
|
19648
|
-
lk.ranges = [];
|
|
19649
|
-
}
|
|
19650
|
-
matchedLabels.add(matchKey);
|
|
19651
|
-
filled.push({
|
|
19652
|
-
label: cellLabelText(headerCells[colIdx]).trim(),
|
|
19653
|
-
value: newValue,
|
|
19654
|
-
row: rowIdx,
|
|
19655
|
-
col: colIdx,
|
|
19656
|
-
key: matchKey
|
|
19657
|
-
});
|
|
19658
|
-
}
|
|
19659
|
-
}
|
|
19660
|
-
}
|
|
19661
|
-
}
|
|
19662
|
-
}
|
|
19663
|
-
for (const para2 of [...scan.bodyParagraphs, ...scan.excludedParagraphs]) {
|
|
19664
|
-
const existing = ledger.get(para2);
|
|
19665
|
-
if (existing?.fullText !== void 0) continue;
|
|
19666
|
-
const text = matchText(para2);
|
|
19667
|
-
for (const seg of scanInlineSegments(text)) {
|
|
19668
|
-
const nlabel = normalizeLabel(seg.label);
|
|
19669
|
-
if (blockedLabels?.has(nlabel)) continue;
|
|
19670
|
-
const matchKey = findMatchingKey(nlabel, cursor);
|
|
19671
|
-
if (matchKey === void 0) continue;
|
|
19672
|
-
const newValue = cursor.consume(matchKey);
|
|
19673
|
-
if (newValue === void 0) continue;
|
|
19674
|
-
const replacement = seg.valueStart === seg.valueEnd ? padInsertion(text, seg.valueStart, newValue) : newValue;
|
|
19675
|
-
const l = led(para2);
|
|
19676
|
-
l.ranges.push({ start: seg.valueStart, end: seg.valueEnd, replacement });
|
|
19677
|
-
matchedLabels.add(matchKey);
|
|
19678
|
-
l.filledIdx.push(filled.length);
|
|
19679
|
-
l.matchKeys.push(matchKey);
|
|
19680
|
-
filled.push({ label: seg.label.trim(), value: newValue, row: -1, col: -1, key: matchKey });
|
|
19681
|
-
}
|
|
19682
|
-
}
|
|
19683
|
-
const splices = [];
|
|
19684
|
-
for (const [para2, l] of ledger) {
|
|
19685
|
-
let paraSplices = null;
|
|
19686
|
-
if (l.fullText !== void 0) {
|
|
19687
|
-
paraSplices = buildParagraphSplices(para2, l.fullText, xml);
|
|
19688
|
-
} else if (l.ranges.length > 0) {
|
|
19689
|
-
const sorted = [...l.ranges].sort((a, b) => a.start - b.start || a.end - b.end);
|
|
19690
|
-
const merged = [];
|
|
19691
|
-
for (const r of sorted) {
|
|
19692
|
-
const prev = merged[merged.length - 1];
|
|
19693
|
-
if (prev && r.start < prev.end) continue;
|
|
19694
|
-
merged.push(r);
|
|
19695
|
-
}
|
|
19696
|
-
if (paraTText(para2, xml) !== null) {
|
|
19697
|
-
const precise = [];
|
|
19698
|
-
let ok = true;
|
|
19699
|
-
for (const r of merged) {
|
|
19700
|
-
const sp = buildRangeSplices(para2, xml, r.start, r.end, r.replacement);
|
|
19701
|
-
if (!sp) {
|
|
19702
|
-
ok = false;
|
|
19703
|
-
break;
|
|
19704
|
-
}
|
|
19705
|
-
precise.push(...sp);
|
|
19706
|
-
}
|
|
19707
|
-
paraSplices = ok ? precise : null;
|
|
19708
|
-
} else if (paraTextPureT(para2, xml)) {
|
|
19709
|
-
let text = para2.text;
|
|
19710
|
-
for (let k = merged.length - 1; k >= 0; k--) {
|
|
19711
|
-
const r = merged[k];
|
|
19712
|
-
text = text.slice(0, r.start) + r.replacement + text.slice(r.end);
|
|
19713
|
-
}
|
|
19714
|
-
paraSplices = buildParagraphSplices(para2, text, xml);
|
|
19715
|
-
} else {
|
|
19716
|
-
paraSplices = null;
|
|
19717
|
-
}
|
|
19718
|
-
}
|
|
19719
|
-
if (paraSplices === null) {
|
|
19720
|
-
for (const idx of l.filledIdx) filled[idx] = null;
|
|
19721
|
-
for (const k of l.matchKeys) failedKeys.add(k);
|
|
19722
|
-
continue;
|
|
19723
|
-
}
|
|
19724
|
-
for (const k of l.matchKeys) succeededKeys.add(k);
|
|
19725
|
-
splices.push(...paraSplices);
|
|
19726
|
-
}
|
|
19727
|
-
if (splices.length > 0) {
|
|
19728
|
-
splices.push(...allLinesegRemovalSplices(xml));
|
|
19729
|
-
replacements.set(sectionPaths[si], encoder.encode(applySplices(xml, splices)));
|
|
19730
|
-
}
|
|
19731
|
-
}
|
|
19732
|
-
for (const k of failedKeys) {
|
|
19733
|
-
if (!succeededKeys.has(k)) matchedLabels.delete(k);
|
|
19734
|
-
}
|
|
19735
|
-
const cleanFilled = filled.filter((f) => f !== null);
|
|
19736
|
-
const unmatched = resolveUnmatched(normalizedValues, matchedLabels, values);
|
|
19737
|
-
const out = replacements.size > 0 ? patchZipEntries(u8, replacements) : new Uint8Array(u8);
|
|
19738
|
-
return {
|
|
19739
|
-
buffer: out.buffer.slice(out.byteOffset, out.byteOffset + out.byteLength),
|
|
19740
|
-
filled: cleanFilled,
|
|
19741
|
-
unmatched
|
|
19742
|
-
};
|
|
19743
|
-
}
|
|
19744
|
-
|
|
19745
|
-
// src/hwpx/generator.ts
|
|
19746
|
-
import JSZip6 from "jszip";
|
|
19747
|
-
|
|
19748
|
-
// src/hwpx/gaejosik.ts
|
|
19749
|
-
var GAEJOSIK_BULLETS = ["\u25A1", "\u25CB", "-", "\u318D"];
|
|
19750
|
-
function gaejosikMarker(depth, bullet2 = "\u25CB") {
|
|
19751
|
-
if (depth === 1) return bullet2;
|
|
19752
|
-
return GAEJOSIK_BULLETS[Math.min(depth, GAEJOSIK_BULLETS.length - 1)];
|
|
19753
|
-
}
|
|
19754
|
-
function gaejosikSizes(bodyHeight, ov = {}) {
|
|
19755
|
-
const s = bodyHeight / 1500;
|
|
19756
|
-
const r = (v, pt) => pt ? Math.round(pt * 100) : Math.round(v * s);
|
|
19757
|
-
return {
|
|
19758
|
-
/** □ 대항목 — HY헤드라인M 16pt */
|
|
19759
|
-
dae: r(1600, ov.dae),
|
|
19760
|
-
/** ※ 참고 — 한양중고딕 13pt */
|
|
19761
|
-
cham: r(1300, ov.cham),
|
|
19762
|
-
/** 장 헤더(로마숫자·제목) 17pt */
|
|
19763
|
-
chapter: r(1700, ov.chapter),
|
|
19764
|
-
/** 표지 제목 30pt */
|
|
19765
|
-
coverTitle: r(3e3, ov.coverTitle),
|
|
19766
|
-
/** 표지 날짜·기관명 25pt */
|
|
19767
|
-
coverSub: r(2500, ov.coverSub),
|
|
19768
|
-
/** 목차 라벨 "목 차" 28pt */
|
|
19769
|
-
tocLabel: r(2800, ov.tocLabel),
|
|
19770
|
-
/** 목차 로마숫자 21pt(한양신명조) */
|
|
19771
|
-
tocRoman: r(2100, ov.tocRoman),
|
|
19772
|
-
/** 목차 항목 18pt */
|
|
19773
|
-
tocItem: r(1800, ov.tocItem),
|
|
19774
|
-
/** 표 셀 — 맑은 고딕 12pt (실측: 정부 양식 표) */
|
|
19775
|
-
table: r(1200, ov.table),
|
|
19776
|
-
/** 표지 장식 바 셀 빈 문단 — 6pt (실측: 바 높이 818 안에 줄높이 수납) */
|
|
19777
|
-
bar: r(600),
|
|
19778
|
-
/** 본문 첫 페이지 제목 박스 — HY헤드라인M 22pt (실측: GT3 표④) */
|
|
19779
|
-
bodyTitle: r(2200, ov.bodyTitle)
|
|
19780
|
-
};
|
|
19781
|
-
}
|
|
19782
|
-
function gaejosikSpaceBefore(depth, bodyHeight) {
|
|
19783
|
-
const s = bodyHeight / 1500;
|
|
19784
|
-
const ui = [3e3, 2e3, 1200, 600][Math.min(depth, 3)];
|
|
19785
|
-
return Math.round(ui * s);
|
|
19786
|
-
}
|
|
19787
|
-
function gaejosikLevelIndent(depth, bodyHeight, sizes = {}, bullet2 = "\u25CB") {
|
|
19788
|
-
const lefts = [0, 1, 1.5, 2];
|
|
19789
|
-
const left = depth <= 3 ? Math.round(lefts[depth] * bodyHeight) : Math.round((2 + (depth - 3) * 0.5) * bodyHeight);
|
|
19790
|
-
const h = depth === 0 ? gaejosikSizes(bodyHeight, sizes).dae : bodyHeight;
|
|
19791
|
-
return { left, indent: -markerWidth(gaejosikMarker(depth, bullet2), h) };
|
|
19792
|
-
}
|
|
19793
|
-
function gaejosikChamIndent(bodyHeight, sizes = {}) {
|
|
19794
|
-
const cham = gaejosikSizes(bodyHeight, sizes).cham;
|
|
19795
|
-
return { left: Math.round(2.5 * cham), indent: -markerWidth("\u203B", cham) };
|
|
19796
|
-
}
|
|
19797
|
-
function gaejosikTocItemIndent(bodyHeight, sizes = {}) {
|
|
19798
|
-
const sz = gaejosikSizes(bodyHeight, sizes);
|
|
19799
|
-
const hang = markerWidth("\u2167.", sz.tocRoman);
|
|
19800
|
-
return { left: 2e3 + hang, indent: -hang };
|
|
19801
|
-
}
|
|
19802
|
-
function formatGaejosikDate(d) {
|
|
19803
|
-
return `${d.getFullYear()}. ${d.getMonth() + 1}. ${d.getDate()}.`;
|
|
19804
|
-
}
|
|
19805
|
-
function stripChapterNumber(title) {
|
|
19806
|
-
return title.replace(/^\s*(?:\d+|[ⅠⅡⅢⅣⅤⅥⅦⅧⅨⅩ]+)\s*[.)]\s*/u, "").trim();
|
|
19807
|
-
}
|
|
19808
|
-
var GAEJOSIK_COLORS = {
|
|
19809
|
-
/** 장헤더 로마숫자 셀·표지 진한 바 음영 */
|
|
19810
|
-
primary: "#193AAA",
|
|
19811
|
-
/** 장헤더 셀 테두리 */
|
|
19812
|
-
border: "#006699",
|
|
19813
|
-
/** 표지 연한 바 */
|
|
19814
|
-
accent: "#A0B4E6",
|
|
19815
|
-
/** 장헤더 제목 셀 음영 */
|
|
19816
|
-
titleFill: "#F2F2F2",
|
|
19817
|
-
/** 장헤더 제목 셀 상하선 */
|
|
19818
|
-
titleLine: "#A6A6A6",
|
|
19819
|
-
/** 목차 박스 테두리 */
|
|
19820
|
-
tocBorder: "#514BAC",
|
|
19821
|
-
/** 목차 배너 라벤더 스트라이프 (실측: GT3 표②) */
|
|
19822
|
-
tocStripe: "#E0E5FA"
|
|
19823
|
-
};
|
|
19824
|
-
var CHAPTER_GEOM = { numW: 3327, gapW: 848, titleW: 43513, rowH: 2832 };
|
|
19825
|
-
var COVER_GEOM = {
|
|
19826
|
-
totalW: 48180,
|
|
19827
|
-
topDarkW: 38219,
|
|
19828
|
-
topLightW: 9961,
|
|
19829
|
-
barH: 818,
|
|
19830
|
-
titleH: 11060,
|
|
19831
|
-
botDarkW: 10466,
|
|
19832
|
-
botLightW: 37714,
|
|
19833
|
-
botBarH: 812
|
|
19834
|
-
};
|
|
19835
|
-
var TOC_GEOM = { boxW: 46492, boxH: 57840 };
|
|
19836
|
-
function chapterGeom(bodyHeight, ov = {}) {
|
|
19837
|
-
const s = gaejosikSizes(bodyHeight, ov).chapter / 1700;
|
|
19838
|
-
return { ...CHAPTER_GEOM, rowH: Math.round(CHAPTER_GEOM.rowH * s) };
|
|
19839
|
-
}
|
|
19840
|
-
function coverGeom(bodyHeight, ov = {}) {
|
|
19841
|
-
const s = gaejosikSizes(bodyHeight, ov).coverTitle / 3e3;
|
|
19842
|
-
return { ...COVER_GEOM, titleH: Math.round(COVER_GEOM.titleH * s) };
|
|
19843
|
-
}
|
|
19844
|
-
var TOC_BANNER_GEOM = {
|
|
19845
|
-
colWs: [565, 565, 1414, 13191, 1414, 565, 565],
|
|
19846
|
-
h: 3931
|
|
19847
|
-
};
|
|
19848
|
-
function tocBannerGeom(bodyHeight, ov = {}) {
|
|
19849
|
-
const s = gaejosikSizes(bodyHeight, ov).tocLabel / 2800;
|
|
19850
|
-
return { colWs: TOC_BANNER_GEOM.colWs, h: Math.round(TOC_BANNER_GEOM.h * s) };
|
|
19851
|
-
}
|
|
19852
|
-
var BODY_TITLE_GEOM = { barH: 600, titleH: 3566 };
|
|
19853
|
-
function bodyTitleGeom(bodyHeight, ov = {}) {
|
|
19854
|
-
const s = gaejosikSizes(bodyHeight, ov).bodyTitle / 2200;
|
|
19855
|
-
return { barH: BODY_TITLE_GEOM.barH, titleH: Math.round(BODY_TITLE_GEOM.titleH * s) };
|
|
19856
|
-
}
|
|
19857
|
-
|
|
19858
|
-
// src/hwpx/gongmun.ts
|
|
19859
|
-
var OFFICIAL_MARGINS = { top: 20, bottom: 15, left: 20, right: 15 };
|
|
19860
|
-
var GAEJOSIK_MARGINS = { top: 15, bottom: 15, left: 20, right: 20 };
|
|
19861
|
-
var GAEJOSIK_HEADER_FOOTER = 4251;
|
|
19862
|
-
var PRESET_DEFAULTS = {
|
|
19863
|
-
official: { bodyPt: 15, lineSpacing: 160, numbering: "standard" },
|
|
19864
|
-
report: { bodyPt: 15, lineSpacing: 160, numbering: "report" },
|
|
19865
|
-
plan: { bodyPt: 15, lineSpacing: 160, numbering: "standard" },
|
|
19866
|
-
notice: { bodyPt: 15, lineSpacing: 160, numbering: "standard" },
|
|
19867
|
-
minutes: { bodyPt: 14, lineSpacing: 130, numbering: "standard" },
|
|
19868
|
-
gaejosik: { bodyPt: 15, lineSpacing: 160, numbering: "gaejosik" },
|
|
19869
|
-
// 보도자료 — 실측(국토부 실물): 본문 바탕 14pt 160%, □→ㅇ→*(각주) 부호
|
|
19870
|
-
press: { bodyPt: 14, lineSpacing: 160, numbering: "report" }
|
|
19871
|
-
};
|
|
19872
|
-
var PRESET_ALIAS = {
|
|
19873
|
-
official: "official",
|
|
19874
|
-
\uAE30\uC548\uBB38: "official",
|
|
19875
|
-
\uC2DC\uD589\uBB38: "official",
|
|
19876
|
-
\uACF5\uBB38: "official",
|
|
19877
|
-
\uACF5\uBB38\uC11C: "official",
|
|
19878
|
-
report: "report",
|
|
19879
|
-
\uBCF4\uACE0\uC11C: "report",
|
|
19880
|
-
plan: "plan",
|
|
19881
|
-
\uACC4\uD68D\uC11C: "plan",
|
|
19882
|
-
\uACC4\uD68D: "plan",
|
|
19883
|
-
notice: "notice",
|
|
19884
|
-
\uD1B5\uC9C0: "notice",
|
|
19885
|
-
\uC54C\uB9BC: "notice",
|
|
19886
|
-
\uC548\uB0B4: "notice",
|
|
19887
|
-
minutes: "minutes",
|
|
19888
|
-
\uD68C\uC758\uB85D: "minutes",
|
|
19889
|
-
gaejosik: "gaejosik",
|
|
19890
|
-
\uAC1C\uC870\uC2DD: "gaejosik",
|
|
19891
|
-
\uAC1C\uC870\uC2DD\uBCF4\uACE0\uC11C: "gaejosik",
|
|
19892
|
-
\uC815\uBD80\uBCF4\uACE0\uC11C: "gaejosik",
|
|
19893
|
-
\uC815\uBD80\uD45C\uC900\uAC1C\uC870\uC2DD\uBCF4\uACE0\uC11C: "gaejosik",
|
|
19894
|
-
press: "press",
|
|
19895
|
-
\uBCF4\uB3C4\uC790\uB8CC: "press"
|
|
19896
|
-
};
|
|
19897
|
-
function normalizeGongmunPreset(preset) {
|
|
19898
|
-
if (!preset) return "official";
|
|
19899
|
-
return PRESET_ALIAS[preset.trim()] ?? "official";
|
|
19900
|
-
}
|
|
19901
|
-
function usesReportFonts(preset) {
|
|
19902
|
-
return preset === "gaejosik" || preset === "report" || preset === "plan";
|
|
19903
|
-
}
|
|
19904
|
-
function resolveGongmun(opts) {
|
|
19905
|
-
const preset = normalizeGongmunPreset(opts.preset);
|
|
19906
|
-
const d = PRESET_DEFAULTS[preset];
|
|
19907
|
-
const bodyPt = opts.bodyPt ?? d.bodyPt;
|
|
19908
|
-
const autoFitMinRatio = opts.autoFit === false ? null : typeof opts.autoFit === "object" ? Math.min(Math.max(opts.autoFit.minRatio ?? 90, 50), 99) : 90;
|
|
19909
|
-
const coverOn = opts.cover !== void 0 ? opts.cover !== false : preset === "gaejosik";
|
|
19910
|
-
const coverOpts = typeof opts.cover === "object" ? opts.cover : {};
|
|
19911
|
-
const gaejosik = preset === "gaejosik";
|
|
19912
|
-
const reportFamily = gaejosik || preset === "report" || preset === "plan" || preset === "notice" || preset === "press";
|
|
19913
|
-
return {
|
|
19914
|
-
preset,
|
|
19915
|
-
bodyFont: opts.bodyFont ?? "myeongjo",
|
|
19916
|
-
bodyHeight: Math.round(bodyPt * 100),
|
|
19917
|
-
lineSpacing: opts.lineSpacing ?? d.lineSpacing,
|
|
19918
|
-
numbering: opts.numbering ?? d.numbering,
|
|
19919
|
-
margins: opts.margins ?? (reportFamily ? GAEJOSIK_MARGINS : OFFICIAL_MARGINS),
|
|
19920
|
-
centerTitle: opts.centerTitle ?? true,
|
|
19921
|
-
autoFitMinRatio,
|
|
19922
|
-
cover: coverOn ? { date: coverOpts.date ?? null, org: coverOpts.org ?? "" } : null,
|
|
19923
|
-
toc: opts.toc ?? gaejosik,
|
|
19924
|
-
fonts: opts.fonts ?? {},
|
|
19925
|
-
sizes: opts.sizes ?? {},
|
|
19926
|
-
// 쪽번호 — 보고서 계열 관행(실측: 2_보고서 양식·추진계획·공고문 전부 하단 중앙)
|
|
19927
|
-
pageNumbers: opts.pageNumbers ?? (gaejosik || preset === "report" || preset === "plan"),
|
|
19928
|
-
// 머리말·꼬리말 — 실측: 보고서 계열 15mm(GT3·t2·춘천·브라더), 공고·보도 10mm,
|
|
19929
|
-
// 기안문 0(실결재 41/60건 h0/f0)
|
|
19930
|
-
headerFooter: usesReportFonts(preset) ? GAEJOSIK_HEADER_FOOTER : preset === "notice" || preset === "press" ? 2835 : 0,
|
|
19931
|
-
// "끝." — 기안문 규정(본문 끝 2타+"끝."). 그 외는 opt-in
|
|
19932
|
-
endMark: opts.endMark ?? preset === "official",
|
|
19933
|
-
approval: opts.approval && opts.approval.length > 0 ? opts.approval : null,
|
|
19934
|
-
// 본문 제목박스 — 실측(GT3·GT12): 목차 뒤 본문 시작에 제목 반복. 표지 켜진 개조식 기본
|
|
19935
|
-
bodyTitleBox: opts.bodyTitleBox ?? (gaejosik && coverOn),
|
|
19936
|
-
// h2 말머리 — 실측: 보고서 양식 □ 대항목(QA-2), 공고문 아라비아("1. 사업개요", 바이오헬스 실측)
|
|
19937
|
-
h2Marker: opts.h2Marker ?? (preset === "report" || preset === "plan" ? "box" : preset === "notice" ? "number" : "none"),
|
|
19938
|
-
// 2단계 부호 — 실결재 기안문·공고문 ㅇ 지배(60건 중 ㅇ134:○5), 보고서 양식 계열 ○
|
|
19939
|
-
bullet2: opts.bullet2 ?? (preset === "notice" || preset === "press" ? "\u3147" : "\u25CB"),
|
|
19940
|
-
// 단일 형제 부호 생략 — 규정이지만 부호 없는 계단이 실무 눈에 어색 (실무자 QA)
|
|
19941
|
-
suppressSingle: opts.suppressSingle ?? false,
|
|
19942
|
-
docHead: preset === "official" && opts.docHead ? opts.docHead : null,
|
|
19943
|
-
docFoot: preset === "official" && opts.docFoot ? opts.docFoot : null,
|
|
19944
|
-
reportInfo: opts.reportInfo?.trim() || null,
|
|
19945
|
-
noticeHead: preset === "notice" && opts.noticeHead ? opts.noticeHead : null,
|
|
19946
|
-
// 보도자료 머리박스는 프리셋 자체가 요구 — 옵션 미지정이어도 빈 객체
|
|
19947
|
-
press: preset === "press" ? opts.press ?? {} : null
|
|
19948
|
-
};
|
|
19949
|
-
}
|
|
19950
|
-
var HANGUL_INITIALS = [0, 2, 3, 5, 6, 7, 9, 11, 12, 14, 15, 16, 17, 18];
|
|
19951
|
-
var HANGUL_MEDIALS = [0, 4, 8, 13, 18, 20];
|
|
19952
|
-
function hangulOrdinal(n) {
|
|
19953
|
-
const cols = HANGUL_INITIALS.length;
|
|
19954
|
-
const vowel = HANGUL_MEDIALS[Math.min(Math.floor(n / cols), HANGUL_MEDIALS.length - 1)];
|
|
19955
|
-
const init = HANGUL_INITIALS[n % cols];
|
|
19956
|
-
return String.fromCodePoint(44032 + init * 588 + vowel * 28);
|
|
19957
|
-
}
|
|
19958
|
-
function circledNumber(n) {
|
|
19959
|
-
return String.fromCodePoint(9312 + n % 20);
|
|
19960
|
-
}
|
|
19961
|
-
function circledHangul(n) {
|
|
19962
|
-
return String.fromCodePoint(12910 + n % 14);
|
|
19963
|
-
}
|
|
19964
|
-
var REPORT_BULLETS = ["\u25A1", "\u25CB", "-", "\u318D"];
|
|
19965
|
-
var PRESS_BULLETS = ["\u25A1", "\u3147", "*", "\u318D"];
|
|
19966
|
-
function standardMarker(depth, n) {
|
|
19967
|
-
switch (depth) {
|
|
19968
|
-
case 0:
|
|
19969
|
-
return `${n + 1}.`;
|
|
19970
|
-
case 1:
|
|
19971
|
-
return `${hangulOrdinal(n)}.`;
|
|
19972
|
-
case 2:
|
|
19973
|
-
return `${n + 1})`;
|
|
19974
|
-
case 3:
|
|
19975
|
-
return `${hangulOrdinal(n)})`;
|
|
19976
|
-
case 4:
|
|
19977
|
-
return `(${n + 1})`;
|
|
19978
|
-
case 5:
|
|
19979
|
-
return `(${hangulOrdinal(n)})`;
|
|
19980
|
-
case 6:
|
|
19981
|
-
return circledNumber(n);
|
|
19982
|
-
case 7:
|
|
19983
|
-
return circledHangul(n);
|
|
19984
|
-
default:
|
|
19985
|
-
return circledHangul(n);
|
|
19986
|
-
}
|
|
19987
|
-
}
|
|
19988
|
-
function reportMarker(depth, bullet2 = "\u25CB", press = false) {
|
|
19989
|
-
const bullets = press ? PRESS_BULLETS : REPORT_BULLETS;
|
|
19990
|
-
const m = bullets[Math.min(depth, bullets.length - 1)];
|
|
19991
|
-
return depth === 1 && !press ? bullet2 : m;
|
|
19992
|
-
}
|
|
19993
|
-
function markerWidth(marker, bodyHeight) {
|
|
19994
|
-
let em = SPACE_EM_FIXED;
|
|
19995
|
-
for (const c of marker) em += charWidthEm1000(c.codePointAt(0));
|
|
19996
|
-
return Math.round(em / 1e3 * bodyHeight);
|
|
19997
|
-
}
|
|
19998
|
-
function levelIndent(depth, bodyHeight, numbering, sizes = {}, bullet2 = "\u25CB", press = false) {
|
|
19999
|
-
if (numbering === "gaejosik") return gaejosikLevelIndent(depth, bodyHeight, sizes, bullet2);
|
|
20000
|
-
const marker = numbering === "report" ? reportMarker(depth, bullet2, press) : standardMarker(depth, 0);
|
|
20001
|
-
return { left: Math.round(depth * bodyHeight), indent: -markerWidth(marker, bodyHeight) };
|
|
20002
|
-
}
|
|
20003
|
-
function computeSuppression(depths) {
|
|
20004
|
-
const counts = /* @__PURE__ */ new Map();
|
|
20005
|
-
const keys = [];
|
|
20006
|
-
const path = [];
|
|
20007
|
-
for (const depth of depths) {
|
|
20008
|
-
path.length = depth + 1;
|
|
20009
|
-
path[depth] = (path[depth] ?? 0) + 1;
|
|
20010
|
-
const parentKey = path.slice(0, depth).join(".") + "|" + depth;
|
|
20011
|
-
keys.push(parentKey);
|
|
20012
|
-
counts.set(parentKey, (counts.get(parentKey) ?? 0) + 1);
|
|
20013
|
-
}
|
|
20014
|
-
return keys.map((k) => (counts.get(k) ?? 0) <= 1);
|
|
20015
|
-
}
|
|
20016
|
-
var GongmunNumberer = class {
|
|
20017
|
-
constructor(numbering, bullet2 = "\u25CB", press = false) {
|
|
20018
|
-
this.numbering = numbering;
|
|
20019
|
-
this.bullet2 = bullet2;
|
|
20020
|
-
this.press = press;
|
|
20021
|
-
}
|
|
20022
|
-
counts = [];
|
|
20023
|
-
/** depth 항목 하나에 대한 마커. suppress=true면 빈 문자열(부호 없음) */
|
|
20024
|
-
next(depth, suppress) {
|
|
20025
|
-
this.counts.length = depth + 1;
|
|
20026
|
-
const n = this.counts[depth] ?? 0;
|
|
20027
|
-
this.counts[depth] = n + 1;
|
|
20028
|
-
if (suppress) return "";
|
|
20029
|
-
if (this.numbering === "gaejosik") return gaejosikMarker(depth, this.bullet2);
|
|
20030
|
-
return this.numbering === "report" ? reportMarker(depth, this.bullet2, this.press) : standardMarker(depth, n);
|
|
20161
|
+
});
|
|
20162
|
+
} else {
|
|
20163
|
+
const paras = valueCell.paragraphs;
|
|
20164
|
+
if (paras.length === 0) continue;
|
|
20165
|
+
const newValue = cursor.consume(matchKey);
|
|
20166
|
+
if (newValue === void 0) continue;
|
|
20167
|
+
const l0 = led(paras[0]);
|
|
20168
|
+
l0.fullText = newValue;
|
|
20169
|
+
l0.ranges = [];
|
|
20170
|
+
l0.filledIdx.push(filled.length);
|
|
20171
|
+
l0.matchKeys.push(matchKey);
|
|
20172
|
+
for (let k = 1; k < paras.length; k++) {
|
|
20173
|
+
const lk = led(paras[k]);
|
|
20174
|
+
lk.fullText = "";
|
|
20175
|
+
lk.ranges = [];
|
|
20176
|
+
}
|
|
20177
|
+
matchedLabels.add(matchKey);
|
|
20178
|
+
filled.push({
|
|
20179
|
+
label: labelText.trim().replace(/[::]\s*$/, ""),
|
|
20180
|
+
value: newValue,
|
|
20181
|
+
row: rowIdx,
|
|
20182
|
+
col: colIdx,
|
|
20183
|
+
key: matchKey
|
|
20184
|
+
});
|
|
20185
|
+
}
|
|
20186
|
+
}
|
|
20187
|
+
}
|
|
20188
|
+
if (table2.rows.length >= 2) {
|
|
20189
|
+
const headerCells = table2.rows[0];
|
|
20190
|
+
const allLabels = headerCells.length > 0 && headerCells.every((cell2) => {
|
|
20191
|
+
const t = cellLabelText(cell2).trim();
|
|
20192
|
+
return t.length > 0 && t.length <= 20 && isLabelCell(t);
|
|
20193
|
+
});
|
|
20194
|
+
if (allLabels) {
|
|
20195
|
+
for (let rowIdx = 1; rowIdx < table2.rows.length; rowIdx++) {
|
|
20196
|
+
const dataCells = table2.rows[rowIdx];
|
|
20197
|
+
for (let colIdx = 0; colIdx < Math.min(headerCells.length, dataCells.length); colIdx++) {
|
|
20198
|
+
const headerLabel = normalizeLabel(cellLabelText(headerCells[colIdx]));
|
|
20199
|
+
if (blockedLabels?.has(headerLabel)) continue;
|
|
20200
|
+
const matchKey = findMatchingKey(headerLabel, cursor);
|
|
20201
|
+
if (matchKey === void 0) continue;
|
|
20202
|
+
if (!cursor.isArray(matchKey) && matchedLabels.has(matchKey)) continue;
|
|
20203
|
+
const newValue = cursor.consume(matchKey);
|
|
20204
|
+
if (newValue === void 0) continue;
|
|
20205
|
+
const paras = dataCells[colIdx].paragraphs;
|
|
20206
|
+
if (paras.length === 0) continue;
|
|
20207
|
+
const l0 = led(paras[0]);
|
|
20208
|
+
l0.fullText = newValue;
|
|
20209
|
+
l0.ranges = [];
|
|
20210
|
+
l0.filledIdx.push(filled.length);
|
|
20211
|
+
l0.matchKeys.push(matchKey);
|
|
20212
|
+
for (let k = 1; k < paras.length; k++) {
|
|
20213
|
+
const lk = led(paras[k]);
|
|
20214
|
+
lk.fullText = "";
|
|
20215
|
+
lk.ranges = [];
|
|
20216
|
+
}
|
|
20217
|
+
matchedLabels.add(matchKey);
|
|
20218
|
+
filled.push({
|
|
20219
|
+
label: cellLabelText(headerCells[colIdx]).trim(),
|
|
20220
|
+
value: newValue,
|
|
20221
|
+
row: rowIdx,
|
|
20222
|
+
col: colIdx,
|
|
20223
|
+
key: matchKey
|
|
20224
|
+
});
|
|
20225
|
+
}
|
|
20226
|
+
}
|
|
20227
|
+
}
|
|
20228
|
+
}
|
|
20229
|
+
}
|
|
20230
|
+
for (const para2 of [...scan.bodyParagraphs, ...scan.excludedParagraphs]) {
|
|
20231
|
+
const existing = ledger.get(para2);
|
|
20232
|
+
if (existing?.fullText !== void 0) continue;
|
|
20233
|
+
const text = matchText(para2);
|
|
20234
|
+
for (const seg of scanInlineSegments(text)) {
|
|
20235
|
+
const nlabel = normalizeLabel(seg.label);
|
|
20236
|
+
if (blockedLabels?.has(nlabel)) continue;
|
|
20237
|
+
const matchKey = findMatchingKey(nlabel, cursor);
|
|
20238
|
+
if (matchKey === void 0) continue;
|
|
20239
|
+
const newValue = cursor.consume(matchKey);
|
|
20240
|
+
if (newValue === void 0) continue;
|
|
20241
|
+
const replacement = seg.valueStart === seg.valueEnd ? padInsertion(text, seg.valueStart, newValue) : newValue;
|
|
20242
|
+
const l = led(para2);
|
|
20243
|
+
l.ranges.push({ start: seg.valueStart, end: seg.valueEnd, replacement });
|
|
20244
|
+
matchedLabels.add(matchKey);
|
|
20245
|
+
l.filledIdx.push(filled.length);
|
|
20246
|
+
l.matchKeys.push(matchKey);
|
|
20247
|
+
filled.push({ label: seg.label.trim(), value: newValue, row: -1, col: -1, key: matchKey });
|
|
20248
|
+
}
|
|
20249
|
+
}
|
|
20250
|
+
const splices = [];
|
|
20251
|
+
for (const [para2, l] of ledger) {
|
|
20252
|
+
let paraSplices = null;
|
|
20253
|
+
if (l.fullText !== void 0) {
|
|
20254
|
+
paraSplices = buildParagraphSplices(para2, l.fullText, xml);
|
|
20255
|
+
} else if (l.ranges.length > 0) {
|
|
20256
|
+
const sorted = [...l.ranges].sort((a, b) => a.start - b.start || a.end - b.end);
|
|
20257
|
+
const merged = [];
|
|
20258
|
+
for (const r of sorted) {
|
|
20259
|
+
const prev = merged[merged.length - 1];
|
|
20260
|
+
if (prev && r.start < prev.end) continue;
|
|
20261
|
+
merged.push(r);
|
|
20262
|
+
}
|
|
20263
|
+
if (paraTText(para2, xml) !== null) {
|
|
20264
|
+
const precise = [];
|
|
20265
|
+
let ok = true;
|
|
20266
|
+
for (const r of merged) {
|
|
20267
|
+
const sp = buildRangeSplices(para2, xml, r.start, r.end, r.replacement);
|
|
20268
|
+
if (!sp) {
|
|
20269
|
+
ok = false;
|
|
20270
|
+
break;
|
|
20271
|
+
}
|
|
20272
|
+
precise.push(...sp);
|
|
20273
|
+
}
|
|
20274
|
+
paraSplices = ok ? precise : null;
|
|
20275
|
+
} else if (paraTextPureT(para2, xml)) {
|
|
20276
|
+
let text = para2.text;
|
|
20277
|
+
for (let k = merged.length - 1; k >= 0; k--) {
|
|
20278
|
+
const r = merged[k];
|
|
20279
|
+
text = text.slice(0, r.start) + r.replacement + text.slice(r.end);
|
|
20280
|
+
}
|
|
20281
|
+
paraSplices = buildParagraphSplices(para2, text, xml);
|
|
20282
|
+
} else {
|
|
20283
|
+
paraSplices = null;
|
|
20284
|
+
}
|
|
20285
|
+
}
|
|
20286
|
+
if (paraSplices === null) {
|
|
20287
|
+
for (const idx of l.filledIdx) filled[idx] = null;
|
|
20288
|
+
for (const k of l.matchKeys) failedKeys.add(k);
|
|
20289
|
+
continue;
|
|
20290
|
+
}
|
|
20291
|
+
for (const k of l.matchKeys) succeededKeys.add(k);
|
|
20292
|
+
splices.push(...paraSplices);
|
|
20293
|
+
}
|
|
20294
|
+
if (splices.length > 0) {
|
|
20295
|
+
splices.push(...allLinesegRemovalSplices(xml));
|
|
20296
|
+
replacements.set(sectionPaths[si], encoder.encode(applySplices(xml, splices)));
|
|
20297
|
+
}
|
|
20031
20298
|
}
|
|
20032
|
-
|
|
20033
|
-
|
|
20299
|
+
for (const k of failedKeys) {
|
|
20300
|
+
if (!succeededKeys.has(k)) matchedLabels.delete(k);
|
|
20034
20301
|
}
|
|
20035
|
-
|
|
20036
|
-
|
|
20037
|
-
|
|
20302
|
+
const cleanFilled = filled.filter((f) => f !== null);
|
|
20303
|
+
const unmatched = resolveUnmatched(normalizedValues, matchedLabels, values);
|
|
20304
|
+
const out = replacements.size > 0 ? patchZipEntries(u8, replacements) : new Uint8Array(u8);
|
|
20305
|
+
return {
|
|
20306
|
+
buffer: out.buffer.slice(out.byteOffset, out.byteOffset + out.byteLength),
|
|
20307
|
+
filled: cleanFilled,
|
|
20308
|
+
unmatched
|
|
20309
|
+
};
|
|
20038
20310
|
}
|
|
20039
20311
|
|
|
20312
|
+
// src/hwpx/generator.ts
|
|
20313
|
+
import JSZip6 from "jszip";
|
|
20314
|
+
|
|
20040
20315
|
// src/hwpx/gen-ids.ts
|
|
20041
20316
|
var NS_SECTION = "http://www.hancom.co.kr/hwpml/2011/section";
|
|
20042
20317
|
var NS_PARA = "http://www.hancom.co.kr/hwpml/2011/paragraph";
|
|
@@ -20080,7 +20355,7 @@ function resolveTheme(theme) {
|
|
|
20080
20355
|
};
|
|
20081
20356
|
}
|
|
20082
20357
|
function escapeXml(text) {
|
|
20083
|
-
return text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
20358
|
+
return text.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F]/g, "").replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
20084
20359
|
}
|
|
20085
20360
|
function headingParaPrId(level) {
|
|
20086
20361
|
if (level === 1) return PARA_H1;
|
|
@@ -20099,7 +20374,7 @@ function charPr(id, height, bold, italic, fontId = 0, textColor = DEFAULT_TEXT_C
|
|
|
20099
20374
|
const italicAttr = italic ? ` italic="1"` : "";
|
|
20100
20375
|
const effFont = fontId;
|
|
20101
20376
|
const boldEl = bold ? `<hh:bold/>` : "";
|
|
20102
|
-
return ` <hh:charPr id="${id}" height="${height}" textColor="${textColor}" shadeColor="none" useFontSpace="0" useKerning="0" symMark="NONE" borderFillIDRef="1"${boldAttr}${italicAttr}>
|
|
20377
|
+
return ` <hh:charPr id="${id}" height="${height}" textColor="${escapeXml(textColor)}" shadeColor="none" useFontSpace="0" useKerning="0" symMark="NONE" borderFillIDRef="1"${boldAttr}${italicAttr}>
|
|
20103
20378
|
<hh:fontRef hangul="${effFont}" latin="${effFont}" hanja="${effFont}" japanese="${effFont}" other="${effFont}" symbol="${effFont}" user="${effFont}"/>
|
|
20104
20379
|
<hh:ratio hangul="${ratioPct}" latin="${ratioPct}" hanja="${ratioPct}" japanese="100" other="100" symbol="100" user="100"/>
|
|
20105
20380
|
<hh:spacing hangul="0" latin="0" hanja="0" japanese="0" other="0" symbol="0" user="0"/>
|
|
@@ -20143,13 +20418,17 @@ var GJ_CHAR_TABLE = 22;
|
|
|
20143
20418
|
var GJ_CHAR_TABLE_BOLD = 23;
|
|
20144
20419
|
var GJ_CHAR_BAR = 24;
|
|
20145
20420
|
var GJ_CHAR_BODY_TITLE = 25;
|
|
20146
|
-
var
|
|
20421
|
+
var GJ_CHAR_TITLE_BAR = 26;
|
|
20422
|
+
var GJ_CHAR_APPROVAL = 27;
|
|
20423
|
+
var GJ_CHAR_COUNT = GJ_CHAR_APPROVAL - GJ_CHAR_DAE + 1;
|
|
20147
20424
|
var GJ_PARA_CHAM = 20;
|
|
20148
20425
|
var GJ_PARA_COVER = 21;
|
|
20149
20426
|
var GJ_PARA_TOC_ITEM = 22;
|
|
20150
20427
|
var GJ_PARA_CHAPTER = 23;
|
|
20151
20428
|
var GJ_PARA_BAR = 24;
|
|
20152
20429
|
var GONGMUN_LIST_PLAIN_BASE = 25;
|
|
20430
|
+
var GONGMUN_PARA_APPROVAL = GONGMUN_LIST_PLAIN_BASE + GONGMUN_LIST_LEVELS;
|
|
20431
|
+
var GONGMUN_LIST_VARIANT_BASE = GONGMUN_PARA_APPROVAL + 1;
|
|
20153
20432
|
var GJ_BF_CHAPTER_NUM = 3;
|
|
20154
20433
|
var GJ_BF_CHAPTER_GAP = 4;
|
|
20155
20434
|
var GJ_BF_CHAPTER_TITLE = 5;
|
|
@@ -20157,8 +20436,8 @@ var GJ_BF_BAR_DARK = 6;
|
|
|
20157
20436
|
var GJ_BF_BAR_LIGHT = 7;
|
|
20158
20437
|
var GJ_BF_TOC_BOX = 8;
|
|
20159
20438
|
var GJ_BF_TOC_STRIPE = 9;
|
|
20160
|
-
function gongmunTableHeaderBf(
|
|
20161
|
-
return
|
|
20439
|
+
function gongmunTableHeaderBf(richAssets) {
|
|
20440
|
+
return richAssets ? 10 : 3;
|
|
20162
20441
|
}
|
|
20163
20442
|
function borderFillEntry(id, b, fill) {
|
|
20164
20443
|
const side = (name, v) => v ? ` <hh:${name} type="${v[2] ?? "SOLID"}" width="${v[0]}" color="${v[1]}"/>` : ` <hh:${name} type="NONE" width="0.1 mm" color="#000000"/>`;
|
|
@@ -20191,10 +20470,12 @@ function pageHidingCtrl(hideHeader = false) {
|
|
|
20191
20470
|
var GONGMUN_TBL_CHAR = 11;
|
|
20192
20471
|
var GONGMUN_TBL_CHAR_BOLD = 12;
|
|
20193
20472
|
var GONGMUN_TBL_PT = 1200;
|
|
20194
|
-
var
|
|
20195
|
-
|
|
20473
|
+
var GONGMUN_TITLE_BAR_CHAR = 13;
|
|
20474
|
+
var GONGMUN_APPROVAL_CHAR = 14;
|
|
20475
|
+
var CHAR_VARIANT_BASE = CHAR_QUOTE + 1;
|
|
20476
|
+
function charVariantBase(richAssets, isGongmun = true) {
|
|
20196
20477
|
if (!isGongmun) return CHAR_VARIANT_BASE;
|
|
20197
|
-
return
|
|
20478
|
+
return richAssets ? CHAR_VARIANT_BASE + GJ_CHAR_COUNT : CHAR_VARIANT_BASE + (GONGMUN_APPROVAL_CHAR - GONGMUN_TBL_CHAR + 1);
|
|
20198
20479
|
}
|
|
20199
20480
|
var GONGMUN_BODY_RATIO = 95;
|
|
20200
20481
|
|
|
@@ -20227,6 +20508,7 @@ function parseMarkdownToBlocks(md2) {
|
|
|
20227
20508
|
const lines = md2.replace(/\r\n?/g, "\n").split("\n");
|
|
20228
20509
|
const blocks = [];
|
|
20229
20510
|
let i = 0;
|
|
20511
|
+
const listStack = [];
|
|
20230
20512
|
while (i < lines.length) {
|
|
20231
20513
|
const line = lines[i];
|
|
20232
20514
|
if (!line.trim()) {
|
|
@@ -20315,7 +20597,8 @@ function parseMarkdownToBlocks(md2) {
|
|
|
20315
20597
|
const tableRows = [];
|
|
20316
20598
|
while (i < lines.length && lines[i].trimStart().startsWith("|")) {
|
|
20317
20599
|
const row = lines[i];
|
|
20318
|
-
|
|
20600
|
+
const sepCells = row.trim().replace(/^\|/, "").replace(/\|$/, "").split("|");
|
|
20601
|
+
if (sepCells.every((c) => /^\s*:?-+:?\s*$/.test(c))) {
|
|
20319
20602
|
i++;
|
|
20320
20603
|
continue;
|
|
20321
20604
|
}
|
|
@@ -20329,17 +20612,36 @@ function parseMarkdownToBlocks(md2) {
|
|
|
20329
20612
|
if (line.trimStart().startsWith("> ")) {
|
|
20330
20613
|
const quoteLines = [];
|
|
20331
20614
|
while (i < lines.length && (lines[i].trimStart().startsWith("> ") || lines[i].trimStart().startsWith(">"))) {
|
|
20332
|
-
quoteLines.push(lines[i].replace(/^>\s?/, ""));
|
|
20615
|
+
quoteLines.push(lines[i].replace(/^>\s?/, "").trim());
|
|
20333
20616
|
i++;
|
|
20334
20617
|
}
|
|
20618
|
+
let joined = [];
|
|
20335
20619
|
for (const ql of quoteLines) {
|
|
20336
|
-
|
|
20620
|
+
if (ql) {
|
|
20621
|
+
joined.push(ql);
|
|
20622
|
+
continue;
|
|
20623
|
+
}
|
|
20624
|
+
if (joined.length) blocks.push({ type: "blockquote", text: joined.join("\n") });
|
|
20625
|
+
joined = [];
|
|
20337
20626
|
}
|
|
20627
|
+
if (joined.length) blocks.push({ type: "blockquote", text: joined.join("\n") });
|
|
20338
20628
|
continue;
|
|
20339
20629
|
}
|
|
20340
|
-
const
|
|
20630
|
+
const listLine = line.replace(/^[\t ]+/, (ws) => ws.replace(/\t/g, " "));
|
|
20631
|
+
const listMatch = listLine.match(/^(\s*)([-*+]|\d+[.)]) (.+)$/);
|
|
20341
20632
|
if (listMatch) {
|
|
20342
|
-
|
|
20633
|
+
if (blocks.length === 0 || blocks[blocks.length - 1].type !== "list_item") listStack.length = 0;
|
|
20634
|
+
const phys = listMatch[1].length;
|
|
20635
|
+
let indent;
|
|
20636
|
+
if (listStack.length === 0) {
|
|
20637
|
+
indent = Math.floor(phys / 2);
|
|
20638
|
+
for (let d = 0; d < indent; d++) listStack.push(d * 2);
|
|
20639
|
+
listStack.push(phys);
|
|
20640
|
+
} else {
|
|
20641
|
+
while (listStack.length > 1 && phys < listStack[listStack.length - 1]) listStack.pop();
|
|
20642
|
+
if (phys > listStack[listStack.length - 1]) listStack.push(phys);
|
|
20643
|
+
indent = listStack.length - 1;
|
|
20644
|
+
}
|
|
20343
20645
|
const ordered = /\d/.test(listMatch[2]);
|
|
20344
20646
|
blocks.push({ type: "list_item", text: listMatch[3].trim(), ordered, indent, marker: listMatch[2] });
|
|
20345
20647
|
i++;
|
|
@@ -20360,7 +20662,7 @@ function parseInlineMarkdown(text) {
|
|
|
20360
20662
|
text = text.replace(/\[([^\]]*)\]\(([^)]*)\)/g, (_, t, u) => t || u);
|
|
20361
20663
|
text = text.replace(/~~([^~]+)~~/g, "$1");
|
|
20362
20664
|
const spans = [];
|
|
20363
|
-
const regex = /(`[^`]+`|\*{3}[^*]+\*{3}|\*{2}[^*]+\*{2}|\*[^*]+\*|_{2}[^_]+_{2}|_[^_]+_)/
|
|
20665
|
+
const regex = /(`[^`]+`|\*{3}[^*]+\*{3}|\*{2}[^*]+\*{2}|\*[^*]+\*|(?<![^\s\p{P}\p{S}\x00])(?<!_)_{2}(?=\S)[^_]+(?<=\S)_{2}(?!_)(?![^\s\p{P}\p{S}\x00])|(?<![^\s\p{P}\p{S}\x00])(?<!_)_(?=\S)[^_]+(?<=\S)_(?!_)(?![^\s\p{P}\p{S}\x00]))/gu;
|
|
20364
20666
|
let lastIdx = 0;
|
|
20365
20667
|
for (const match of text.matchAll(regex)) {
|
|
20366
20668
|
const idx = match.index;
|
|
@@ -20426,14 +20728,20 @@ function generateContainerXml() {
|
|
|
20426
20728
|
</ocf:rootfiles>
|
|
20427
20729
|
</ocf:container>`;
|
|
20428
20730
|
}
|
|
20429
|
-
function generateManifest(chartParts = []) {
|
|
20731
|
+
function generateManifest(chartParts = [], imageItems = [], layout = "default") {
|
|
20430
20732
|
const chartItems = chartParts.map((p, i) => `
|
|
20431
20733
|
<opf:item id="chart${i + 1}" href="${p.name}" media-type="application/xml"/>`).join("");
|
|
20734
|
+
const imgItems = imageItems.map((x) => `
|
|
20735
|
+
${x}`).join("");
|
|
20432
20736
|
return `<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
|
|
20433
20737
|
<opf:package xmlns:opf="${NS_OPF}" xmlns:hpf="${NS_HPF}" xmlns:hh="${NS_HEAD}">
|
|
20738
|
+
<opf:metadata>
|
|
20739
|
+
<opf:meta name="generator" content="kordoc"/>
|
|
20740
|
+
<opf:meta name="kordoc-layout" content="${escapeXml(layout)}"/>
|
|
20741
|
+
</opf:metadata>
|
|
20434
20742
|
<opf:manifest>
|
|
20435
20743
|
<opf:item id="header" href="Contents/header.xml" media-type="application/xml"/>
|
|
20436
|
-
<opf:item id="section0" href="Contents/section0.xml" media-type="application/xml"/>${chartItems}
|
|
20744
|
+
<opf:item id="section0" href="Contents/section0.xml" media-type="application/xml"/>${chartItems}${imgItems}
|
|
20437
20745
|
</opf:manifest>
|
|
20438
20746
|
<opf:spine>
|
|
20439
20747
|
<opf:itemref idref="header" linear="no"/>
|
|
@@ -20441,18 +20749,30 @@ function generateManifest(chartParts = []) {
|
|
|
20441
20749
|
</opf:spine>
|
|
20442
20750
|
</opf:package>`;
|
|
20443
20751
|
}
|
|
20752
|
+
function assertSequentialIds(rows, label, startId) {
|
|
20753
|
+
for (let i = 0; i < rows.length; i++) {
|
|
20754
|
+
const m = rows[i].match(/\bid="(\d+)"/);
|
|
20755
|
+
const got = m ? Number(m[1]) : NaN;
|
|
20756
|
+
if (got !== startId + i) {
|
|
20757
|
+
throw new KordocError(
|
|
20758
|
+
`\uB0B4\uBD80 \uC624\uB958: ${label} id \uD30C\uD2F0\uC158 \uB4DC\uB9AC\uD504\uD2B8 \u2014 ${startId + i} \uC704\uCE58\uC5D0 id=${m ? m[1] : "?"} \uBC29\uCD9C. id \uC0C1\uC218(gen-ids)\uC640 \uC2E4\uC81C \uBC29\uCD9C \uC218\uAC00 \uC5B4\uAE0B\uB0AC\uB2E4 (GJ_CHAR_COUNT\uB958 \uCE74\uC6B4\uD2B8\xB7base \uC0B0\uC220 \uD655\uC778)`
|
|
20759
|
+
);
|
|
20760
|
+
}
|
|
20761
|
+
}
|
|
20762
|
+
}
|
|
20444
20763
|
function buildCharProperties(theme, gongmun, ratioVariants = [], extraCharPrXmls = []) {
|
|
20445
20764
|
const measured = !!gongmun && usesReportFonts(gongmun.preset);
|
|
20765
|
+
const richAssets = !!gongmun && needsGaejosikAssets(gongmun);
|
|
20446
20766
|
let body = 1e3, code = 900, h1 = 1800, h2 = 1400, h3 = 1200, h4 = 1100;
|
|
20447
20767
|
if (gongmun) {
|
|
20448
20768
|
body = gongmun.bodyHeight;
|
|
20449
20769
|
code = Math.max(body - 200, 900);
|
|
20450
|
-
h1 = gongmun.preset === "report" || gongmun.preset === "plan" ? 2e3 : 1700;
|
|
20451
|
-
h2 = 1600;
|
|
20770
|
+
h1 = Math.max(gongmun.preset === "report" || gongmun.preset === "plan" ? 2e3 : 1700, body);
|
|
20771
|
+
h2 = Math.max(1600, body);
|
|
20452
20772
|
h3 = body;
|
|
20453
|
-
h4 = Math.max(body - 100, 1300);
|
|
20773
|
+
h4 = Math.min(h3, Math.max(body - 100, 1300));
|
|
20454
20774
|
}
|
|
20455
|
-
const bodyFont =
|
|
20775
|
+
const bodyFont = richAssets ? 4 : 0;
|
|
20456
20776
|
const hFont = measured ? 3 : 1;
|
|
20457
20777
|
const hBold = !measured;
|
|
20458
20778
|
const bodyRatio = gongmun ? GONGMUN_BODY_RATIO : 100;
|
|
@@ -20469,13 +20789,15 @@ function buildCharProperties(theme, gongmun, ratioVariants = [], extraCharPrXmls
|
|
|
20469
20789
|
charPr(CHAR_TABLE_HEADER, body, theme.tableHeaderBold, false, bodyFont, theme.tableHeader),
|
|
20470
20790
|
charPr(CHAR_QUOTE, body, false, true, bodyFont, theme.quote)
|
|
20471
20791
|
];
|
|
20472
|
-
if (gongmun && !
|
|
20792
|
+
if (gongmun && !richAssets) {
|
|
20473
20793
|
rows.push(
|
|
20474
20794
|
charPr(11, GONGMUN_TBL_PT, false, false, bodyFont, theme.body),
|
|
20475
|
-
charPr(12, GONGMUN_TBL_PT, true, false, bodyFont, theme.body)
|
|
20795
|
+
charPr(12, GONGMUN_TBL_PT, true, false, bodyFont, theme.body),
|
|
20796
|
+
charPr(GONGMUN_TITLE_BAR_CHAR, 100, false, false, bodyFont, theme.body),
|
|
20797
|
+
charPr(GONGMUN_APPROVAL_CHAR, 1e3, false, false, bodyFont, theme.body)
|
|
20476
20798
|
);
|
|
20477
20799
|
}
|
|
20478
|
-
if (
|
|
20800
|
+
if (richAssets) {
|
|
20479
20801
|
const sz = gaejosikSizes(body, gongmun.sizes);
|
|
20480
20802
|
rows.push(
|
|
20481
20803
|
charPr(11, sz.dae, false, false, 3),
|
|
@@ -20506,8 +20828,18 @@ function buildCharProperties(theme, gongmun, ratioVariants = [], extraCharPrXmls
|
|
|
20506
20828
|
// 표 셀 bold
|
|
20507
20829
|
charPr(24, sz.bar, false, false, 4),
|
|
20508
20830
|
// 표지 바 셀 빈 문단(6pt)
|
|
20509
|
-
charPr(25, sz.bodyTitle, false, false, 3)
|
|
20831
|
+
charPr(25, sz.bodyTitle, false, false, 3),
|
|
20510
20832
|
// 본문 제목박스(HY헤드라인M 22pt, 실측 GT3 표④)
|
|
20833
|
+
charPr(GJ_CHAR_TITLE_BAR, 100, false, false, 4),
|
|
20834
|
+
// 1페이지형 제목박스 바(1pt)
|
|
20835
|
+
charPr(GJ_CHAR_APPROVAL, 1e3, false, false, 7)
|
|
20836
|
+
// 결재란 라벨(맑은 고딕 10pt)
|
|
20837
|
+
);
|
|
20838
|
+
}
|
|
20839
|
+
const expectedBase = charVariantBase(richAssets, !!gongmun);
|
|
20840
|
+
if (rows.length !== expectedBase) {
|
|
20841
|
+
throw new KordocError(
|
|
20842
|
+
`\uB0B4\uBD80 \uC624\uB958: charPr \uAE30\uBCF8 \uBE14\uB85D \uBC29\uCD9C \uC218(${rows.length}) \u2260 charVariantBase(${expectedBase}) \u2014 gen-header \uBC29\uCD9C \uD589\uACFC gen-ids \uCE74\uC6B4\uD2B8 \uC0C1\uC218\uAC00 \uC5B4\uAE0B\uB0AC\uB2E4`
|
|
20511
20843
|
);
|
|
20512
20844
|
}
|
|
20513
20845
|
for (const r of ratioVariants) {
|
|
@@ -20519,11 +20851,12 @@ function buildCharProperties(theme, gongmun, ratioVariants = [], extraCharPrXmls
|
|
|
20519
20851
|
);
|
|
20520
20852
|
}
|
|
20521
20853
|
rows.push(...extraCharPrXmls);
|
|
20854
|
+
assertSequentialIds(rows, "charPr", 0);
|
|
20522
20855
|
return `<hh:charProperties itemCnt="${rows.length}">
|
|
20523
20856
|
${rows.join("\n")}
|
|
20524
20857
|
</hh:charProperties>`;
|
|
20525
20858
|
}
|
|
20526
|
-
function buildParaProperties(gongmun) {
|
|
20859
|
+
function buildParaProperties(gongmun, listIndentVariants = []) {
|
|
20527
20860
|
if (!gongmun) {
|
|
20528
20861
|
const base2 = [
|
|
20529
20862
|
paraPr(0, { keepWord: true }),
|
|
@@ -20535,6 +20868,7 @@ function buildParaProperties(gongmun) {
|
|
|
20535
20868
|
paraPr(6, { align: "LEFT", lineSpacing: 150, indent: 600, keepWord: true }),
|
|
20536
20869
|
paraPr(7, { align: "LEFT", lineSpacing: 160, indent: 600, keepWord: true })
|
|
20537
20870
|
];
|
|
20871
|
+
assertSequentialIds(base2, "paraPr", 0);
|
|
20538
20872
|
return `<hh:paraProperties itemCnt="${base2.length}">
|
|
20539
20873
|
${base2.join("\n")}
|
|
20540
20874
|
</hh:paraProperties>`;
|
|
@@ -20553,7 +20887,7 @@ ${base2.join("\n")}
|
|
|
20553
20887
|
paraPr(7, { align: "LEFT", lineSpacing: ls, indent: 600, keepWord: true })
|
|
20554
20888
|
];
|
|
20555
20889
|
for (let d = 0; d < GONGMUN_LIST_LEVELS; d++) {
|
|
20556
|
-
const { left, indent } = levelIndent(d, gongmun.bodyHeight, gongmun.numbering, gongmun.sizes, gongmun.bullet2, gongmun.preset
|
|
20890
|
+
const { left, indent } = levelIndent(d, gongmun.bodyHeight, gongmun.numbering, gongmun.sizes, gongmun.bullet2, usesAsteriskThird(gongmun.preset));
|
|
20557
20891
|
const sectionGap = gongmun.numbering === "gaejosik" || gongmun.numbering === "report" ? gaejosikSpaceBefore(d, gongmun.bodyHeight) : 0;
|
|
20558
20892
|
const keepNext = (gongmun.numbering === "gaejosik" || gongmun.numbering === "report") && d === 0;
|
|
20559
20893
|
base.push(paraPr(GONGMUN_LIST_BASE + d, { align: "JUSTIFY", lineSpacing: ls, left, indent, spaceBefore: sectionGap, keepWord: true, keepWithNext: keepNext }));
|
|
@@ -20576,10 +20910,19 @@ ${base2.join("\n")}
|
|
|
20576
20910
|
);
|
|
20577
20911
|
}
|
|
20578
20912
|
for (let d = 0; d < GONGMUN_LIST_LEVELS; d++) {
|
|
20579
|
-
const { left } = levelIndent(d, gongmun.bodyHeight, gongmun.numbering, gongmun.sizes, gongmun.bullet2, gongmun.preset
|
|
20913
|
+
const { left } = levelIndent(d, gongmun.bodyHeight, gongmun.numbering, gongmun.sizes, gongmun.bullet2, usesAsteriskThird(gongmun.preset));
|
|
20580
20914
|
const sectionGap = gongmun.numbering === "gaejosik" || gongmun.numbering === "report" ? gaejosikSpaceBefore(d, gongmun.bodyHeight) : 0;
|
|
20581
20915
|
base.push(paraPr(GONGMUN_LIST_PLAIN_BASE + d, { align: "JUSTIFY", lineSpacing: ls, left, indent: 0, spaceBefore: sectionGap, keepWord: true }));
|
|
20582
20916
|
}
|
|
20917
|
+
base.push(paraPr(GONGMUN_PARA_APPROVAL, { align: "CENTER", lineSpacing: 100, keepWord: true }));
|
|
20918
|
+
for (let vi = 0; vi < listIndentVariants.length; vi++) {
|
|
20919
|
+
const { depth, widthHu } = listIndentVariants[vi];
|
|
20920
|
+
const { left } = levelIndent(depth, gongmun.bodyHeight, gongmun.numbering, gongmun.sizes, gongmun.bullet2, usesAsteriskThird(gongmun.preset));
|
|
20921
|
+
const sectionGap = gongmun.numbering === "gaejosik" || gongmun.numbering === "report" ? gaejosikSpaceBefore(depth, gongmun.bodyHeight) : 0;
|
|
20922
|
+
const keepNext = (gongmun.numbering === "gaejosik" || gongmun.numbering === "report") && depth === 0;
|
|
20923
|
+
base.push(paraPr(GONGMUN_LIST_VARIANT_BASE + vi, { align: "JUSTIFY", lineSpacing: ls, left, indent: -widthHu, spaceBefore: sectionGap, keepWord: true, keepWithNext: keepNext }));
|
|
20924
|
+
}
|
|
20925
|
+
assertSequentialIds(base, "paraPr", 0);
|
|
20583
20926
|
return `<hh:paraProperties itemCnt="${base.length}">
|
|
20584
20927
|
${base.join("\n")}
|
|
20585
20928
|
</hh:paraProperties>`;
|
|
@@ -20596,14 +20939,17 @@ ${heads}
|
|
|
20596
20939
|
</hh:numberings>`;
|
|
20597
20940
|
}
|
|
20598
20941
|
function fontEntry(id, face, weight = 6) {
|
|
20599
|
-
return ` <hh:font id="${id}" face="${face}" type="TTF" isEmbedded="0">
|
|
20942
|
+
return ` <hh:font id="${id}" face="${escapeXml(face)}" type="TTF" isEmbedded="0">
|
|
20600
20943
|
<hh:typeInfo familyType="FCAT_GOTHIC" weight="${weight}" proportion="4" contrast="0" strokeVariation="1" armStyle="1" letterform="1" midline="1" xHeight="1"/>
|
|
20601
20944
|
</hh:font>`;
|
|
20602
20945
|
}
|
|
20603
|
-
function
|
|
20604
|
-
|
|
20946
|
+
function staticFontNext(gongmun) {
|
|
20947
|
+
return gongmun && needsGaejosikAssets(gongmun) ? 8 : 3;
|
|
20948
|
+
}
|
|
20949
|
+
function buildFontFaces(gongmun, bodyFace, extraFonts = []) {
|
|
20950
|
+
if (gongmun && needsGaejosikAssets(gongmun)) {
|
|
20605
20951
|
const ov = gongmun.fonts;
|
|
20606
|
-
const bodyDefault = gongmun.preset !== "gaejosik" && gongmun.bodyFont === "gothic" ? "\uB9D1\uC740 \uACE0\uB515" : "\uD734\uBA3C\uBA85\uC870";
|
|
20952
|
+
const bodyDefault = usesReportFonts(gongmun.preset) ? gongmun.preset !== "gaejosik" && gongmun.bodyFont === "gothic" ? "\uB9D1\uC740 \uACE0\uB515" : "\uD734\uBA3C\uBA85\uC870" : bodyFace;
|
|
20607
20953
|
const faces = [
|
|
20608
20954
|
"\uD568\uCD08\uB86C\uBC14\uD0D5",
|
|
20609
20955
|
"\uD568\uCD08\uB86C\uB3CB\uC6C0",
|
|
@@ -20614,21 +20960,27 @@ function buildFontFaces(gongmun, bodyFace) {
|
|
|
20614
20960
|
"\uD55C\uC591\uC2E0\uBA85\uC870",
|
|
20615
20961
|
ov.table ?? "\uB9D1\uC740 \uACE0\uB515"
|
|
20616
20962
|
];
|
|
20617
|
-
|
|
20963
|
+
if (faces.length !== staticFontNext(gongmun)) {
|
|
20964
|
+
throw new Error(`fontface \uC815\uC801 \uAC1C\uC218 \uB4DC\uB9AC\uD504\uD2B8: ${faces.length} \u2260 staticFontNext ${staticFontNext(gongmun)}`);
|
|
20965
|
+
}
|
|
20966
|
+
const list = faces.map((f, i) => fontEntry(i, f, i === 2 || i === 3 ? 9 : 6)).concat(extraFonts.map((f, i) => fontEntry(faces.length + i, f))).join("\n");
|
|
20618
20967
|
const langs = ["HANGUL", "LATIN", "HANJA", "JAPANESE", "OTHER", "SYMBOL", "USER"];
|
|
20619
20968
|
return `<hh:fontfaces itemCnt="${langs.length}">
|
|
20620
|
-
` + langs.map((l) => ` <hh:fontface lang="${l}" fontCnt="${faces.length}">
|
|
20969
|
+
` + langs.map((l) => ` <hh:fontface lang="${l}" fontCnt="${faces.length + extraFonts.length}">
|
|
20621
20970
|
${list}
|
|
20622
20971
|
</hh:fontface>`).join("\n") + `
|
|
20623
20972
|
</hh:fontfaces>`;
|
|
20624
20973
|
}
|
|
20974
|
+
const extraH = extraFonts.map((f, i) => fontEntry(3 + i, f)).join("\n");
|
|
20975
|
+
const extraBlock = extraH ? `
|
|
20976
|
+
${extraH}` : "";
|
|
20625
20977
|
return `<hh:fontfaces itemCnt="7">
|
|
20626
|
-
<hh:fontface lang="HANGUL" fontCnt="3">
|
|
20978
|
+
<hh:fontface lang="HANGUL" fontCnt="${3 + extraFonts.length}">
|
|
20627
20979
|
${fontEntry(0, bodyFace)}
|
|
20628
20980
|
${fontEntry(1, "\uD568\uCD08\uB86C\uB3CB\uC6C0")}
|
|
20629
|
-
${fontEntry(2, "HY\uACAC\uACE0\uB515", 9)}
|
|
20981
|
+
${fontEntry(2, "HY\uACAC\uACE0\uB515", 9)}${extraBlock}
|
|
20630
20982
|
</hh:fontface>
|
|
20631
|
-
<hh:fontface lang="LATIN" fontCnt="3">
|
|
20983
|
+
<hh:fontface lang="LATIN" fontCnt="${3 + extraFonts.length}">
|
|
20632
20984
|
<hh:font id="0" face="Times New Roman" type="TTF" isEmbedded="0">
|
|
20633
20985
|
<hh:typeInfo familyType="FCAT_OLDSTYLE" weight="5" proportion="4" contrast="2" strokeVariation="0" armStyle="0" letterform="0" midline="0" xHeight="4"/>
|
|
20634
20986
|
</hh:font>
|
|
@@ -20637,7 +20989,7 @@ ${fontEntry(2, "HY\uACAC\uACE0\uB515", 9)}
|
|
|
20637
20989
|
</hh:font>
|
|
20638
20990
|
<hh:font id="2" face="Arial Black" type="TTF" isEmbedded="0">
|
|
20639
20991
|
<hh:typeInfo familyType="FCAT_GOTHIC" weight="9" proportion="0" contrast="0" strokeVariation="0" armStyle="0" letterform="0" midline="0" xHeight="0"/>
|
|
20640
|
-
</hh:font
|
|
20992
|
+
</hh:font>${extraBlock}
|
|
20641
20993
|
</hh:fontface>
|
|
20642
20994
|
<hh:fontface lang="HANJA" fontCnt="1">
|
|
20643
20995
|
${fontEntry(0, "\uD568\uCD08\uB86C\uBC14\uD0D5")}
|
|
@@ -20656,13 +21008,13 @@ ${fontEntry(0, "\uAD74\uB9BC")}
|
|
|
20656
21008
|
</hh:fontface>
|
|
20657
21009
|
</hh:fontfaces>`;
|
|
20658
21010
|
}
|
|
20659
|
-
function
|
|
21011
|
+
function staticBorderFillItems(gongmun) {
|
|
20660
21012
|
const thin = ["0.12 mm", "#000000"];
|
|
20661
21013
|
const items = [
|
|
20662
21014
|
borderFillEntry(1, {}),
|
|
20663
21015
|
borderFillEntry(2, { l: thin, r: thin, t: thin, b: thin })
|
|
20664
21016
|
];
|
|
20665
|
-
if (gongmun
|
|
21017
|
+
if (gongmun && needsGaejosikAssets(gongmun)) {
|
|
20666
21018
|
const c = GAEJOSIK_COLORS;
|
|
20667
21019
|
const edge = ["0.12 mm", c.border];
|
|
20668
21020
|
const grayLine = ["0.1 mm", c.titleLine];
|
|
@@ -20687,7 +21039,14 @@ function buildBorderFills(gongmun, extra = []) {
|
|
|
20687
21039
|
if (gongmun) {
|
|
20688
21040
|
items.push(borderFillEntry(items.length + 1, { l: thin, r: thin, t: thin, b: thin }, "#E6E6E6"));
|
|
20689
21041
|
}
|
|
20690
|
-
items
|
|
21042
|
+
return items;
|
|
21043
|
+
}
|
|
21044
|
+
function staticBorderFillNext(gongmun) {
|
|
21045
|
+
return staticBorderFillItems(gongmun).length + 1;
|
|
21046
|
+
}
|
|
21047
|
+
function buildBorderFills(gongmun, extra = []) {
|
|
21048
|
+
const items = [...staticBorderFillItems(gongmun), ...extra];
|
|
21049
|
+
assertSequentialIds(items, "borderFill", 1);
|
|
20691
21050
|
return `<hh:borderFills itemCnt="${items.length}">
|
|
20692
21051
|
${items.join("\n")}
|
|
20693
21052
|
</hh:borderFills>`;
|
|
@@ -20705,15 +21064,15 @@ function buildStyles(gongmun) {
|
|
|
20705
21064
|
${items.join("\n ")}
|
|
20706
21065
|
</hh:styles>`;
|
|
20707
21066
|
}
|
|
20708
|
-
function generateHeaderXml(theme, gongmun, ratioVariants = [], extraBorderFills = [], extraCharPrXmls = []) {
|
|
21067
|
+
function generateHeaderXml(theme, gongmun, ratioVariants = [], extraBorderFills = [], extraCharPrXmls = [], listIndentVariants = [], extraFonts = []) {
|
|
20709
21068
|
const bodyFace = gongmun?.fonts.body ?? (gongmun?.bodyFont === "gothic" ? "\uB9D1\uC740 \uACE0\uB515" : "\uD568\uCD08\uB86C\uBC14\uD0D5");
|
|
20710
21069
|
const charPropsXml = buildCharProperties(theme, gongmun, ratioVariants, extraCharPrXmls);
|
|
20711
|
-
const paraPropsXml = buildParaProperties(gongmun);
|
|
21070
|
+
const paraPropsXml = buildParaProperties(gongmun, gongmun ? listIndentVariants : []);
|
|
20712
21071
|
return `<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
|
|
20713
21072
|
<hh:head xmlns:hh="${NS_HEAD}" xmlns:hp="${NS_PARA}" xmlns:hc="${NS_CORE}" version="1.4" secCnt="1">
|
|
20714
21073
|
<hh:beginNum page="1" footnote="1" endnote="1" pic="1" tbl="1" equation="1"/>
|
|
20715
21074
|
<hh:refList>
|
|
20716
|
-
${buildFontFaces(gongmun, bodyFace)}
|
|
21075
|
+
${buildFontFaces(gongmun, bodyFace, extraFonts)}
|
|
20717
21076
|
${buildBorderFills(gongmun, extraBorderFills)}
|
|
20718
21077
|
${charPropsXml}
|
|
20719
21078
|
<hh:tabProperties itemCnt="0"/>
|
|
@@ -20787,7 +21146,6 @@ function chartSpec(t) {
|
|
|
20787
21146
|
const key = CHART_ALIAS[t.toLowerCase()] ?? Number(t);
|
|
20788
21147
|
return CHART_TYPES[key] ?? CHART_TYPES[0];
|
|
20789
21148
|
}
|
|
20790
|
-
var HU_PER_MM = 7200 / 25.4;
|
|
20791
21149
|
var RESERVED_KEYS = /* @__PURE__ */ new Set(["type", "cat", "size", "colors", "point_colors", "title"]);
|
|
20792
21150
|
function parseChartFence(text) {
|
|
20793
21151
|
let type;
|
|
@@ -20946,7 +21304,7 @@ function plainRenderText(text) {
|
|
|
20946
21304
|
function computeGongmunFitPlan(blocks, gongmun, gongmunList) {
|
|
20947
21305
|
const minRatio = gongmun.autoFitMinRatio;
|
|
20948
21306
|
if (minRatio === null || minRatio >= GONGMUN_BODY_RATIO) return null;
|
|
20949
|
-
const pageW =
|
|
21307
|
+
const pageW = A4_W_HU - mmToHwpunit(gongmun.margins.left) - mmToHwpunit(gongmun.margins.right);
|
|
20950
21308
|
const ratioByBlock = /* @__PURE__ */ new Map();
|
|
20951
21309
|
const variants = [];
|
|
20952
21310
|
for (let i = 0; i < blocks.length; i++) {
|
|
@@ -20954,14 +21312,14 @@ function computeGongmunFitPlan(blocks, gongmun, gongmunList) {
|
|
|
20954
21312
|
let text;
|
|
20955
21313
|
let firstW;
|
|
20956
21314
|
let contW;
|
|
20957
|
-
if (block.type === "list_item" && gongmunList.has(i)) {
|
|
20958
|
-
const { marker, depth } = gongmunList.get(i);
|
|
21315
|
+
if (block.type === "list_item" && gongmunList.items.has(i)) {
|
|
21316
|
+
const { marker, depth, indentVariant } = gongmunList.items.get(i);
|
|
20959
21317
|
if (gongmun.numbering === "gaejosik" && (depth === 0 || (block.text || "").trimStart().startsWith("\u203B"))) continue;
|
|
20960
21318
|
const content = plainRenderText(block.text || "");
|
|
20961
21319
|
text = marker ? `${marker} ${content}` : content;
|
|
20962
|
-
const li = levelIndent(depth, gongmun.bodyHeight, gongmun.numbering, gongmun.sizes, gongmun.bullet2, gongmun.preset
|
|
21320
|
+
const li = levelIndent(depth, gongmun.bodyHeight, gongmun.numbering, gongmun.sizes, gongmun.bullet2, usesAsteriskThird(gongmun.preset));
|
|
20963
21321
|
const left = li.left;
|
|
20964
|
-
const indent = marker ? li.indent : 0;
|
|
21322
|
+
const indent = marker ? indentVariant !== void 0 ? -gongmunList.indentVariants[indentVariant].widthHu : li.indent : 0;
|
|
20965
21323
|
firstW = pageW - left - Math.max(indent, 0);
|
|
20966
21324
|
contW = pageW - left - Math.max(-indent, 0);
|
|
20967
21325
|
} else if (block.type === "paragraph") {
|
|
@@ -20987,9 +21345,55 @@ function variantMapper(fit, blockIdx, base = CHAR_VARIANT_BASE) {
|
|
|
20987
21345
|
const vi = fit.variants.indexOf(r);
|
|
20988
21346
|
return (id) => id >= 0 && id <= 3 ? base + vi * 4 + id : id;
|
|
20989
21347
|
}
|
|
21348
|
+
var ORDINAL_SYLLABLES = new Set(Array.from({ length: 84 }, (_, i) => hangulOrdinal(i)));
|
|
21349
|
+
function literalMarkerDepth(text) {
|
|
21350
|
+
const t = text.trimStart();
|
|
21351
|
+
let m;
|
|
21352
|
+
if ((m = /^([가-힣])\.\s+(.*)$/s.exec(t)) && ORDINAL_SYLLABLES.has(m[1])) return { depth: 1, content: m[2] };
|
|
21353
|
+
if (m = /^(\d{1,3})\)\s+(.*)$/s.exec(t)) return { depth: 2, content: m[2] };
|
|
21354
|
+
if ((m = /^([가-힣])\)\s+(.*)$/s.exec(t)) && ORDINAL_SYLLABLES.has(m[1])) return { depth: 3, content: m[2] };
|
|
21355
|
+
if (m = /^\((\d{1,3})\)\s+(.*)$/s.exec(t)) return { depth: 4, content: m[2] };
|
|
21356
|
+
if ((m = /^\(([가-힣])\)\s+(.*)$/s.exec(t)) && ORDINAL_SYLLABLES.has(m[1])) return { depth: 5, content: m[2] };
|
|
21357
|
+
if (m = /^([①-⑳㉑-㉟㊱-㊿])\s+(.*)$/s.exec(t)) return { depth: 6, content: m[2] };
|
|
21358
|
+
if (m = /^([㉮-㉻])\s+(.*)$/s.exec(t)) return { depth: 7, content: m[2] };
|
|
21359
|
+
if (m = /^□\s+(.*)$/s.exec(t)) return { depth: 0, content: m[1] };
|
|
21360
|
+
if (m = /^[○ㅇ]\s+(.*)$/s.exec(t)) return { depth: 1, content: m[1] };
|
|
21361
|
+
if (m = /^ㆍ\s+(.*)$/s.exec(t)) return { depth: 3, content: m[1] };
|
|
21362
|
+
return null;
|
|
21363
|
+
}
|
|
21364
|
+
function reclassifyLiteralItems(blocks, depthOffset) {
|
|
21365
|
+
for (const b of blocks) {
|
|
21366
|
+
if (b.type !== "paragraph") continue;
|
|
21367
|
+
const raw = (b.text || "").trimStart();
|
|
21368
|
+
if (!raw || raw.startsWith("\u203B") || /^\*\s/.test(raw) || /^<(center|right)>/i.test(raw)) continue;
|
|
21369
|
+
const lit = literalMarkerDepth(raw);
|
|
21370
|
+
if (!lit || !lit.content.trim()) continue;
|
|
21371
|
+
b.type = "list_item";
|
|
21372
|
+
b.text = lit.content;
|
|
21373
|
+
b.indent = Math.max(lit.depth - depthOffset, 0);
|
|
21374
|
+
b.ordered = lit.depth !== 0;
|
|
21375
|
+
}
|
|
21376
|
+
}
|
|
20990
21377
|
function precomputeGongmunList(blocks, gongmun) {
|
|
20991
21378
|
const result = /* @__PURE__ */ new Map();
|
|
21379
|
+
const indentVariants = [];
|
|
21380
|
+
const variantIdx = /* @__PURE__ */ new Map();
|
|
21381
|
+
const variantOf = (marker, depth) => {
|
|
21382
|
+
if (!marker || gongmun.numbering !== "standard") return void 0;
|
|
21383
|
+
const w = markerWidth(marker, gongmun.bodyHeight);
|
|
21384
|
+
const rep = -levelIndent(depth, gongmun.bodyHeight, gongmun.numbering, gongmun.sizes, gongmun.bullet2, usesAsteriskThird(gongmun.preset)).indent;
|
|
21385
|
+
if (w === rep) return void 0;
|
|
21386
|
+
const key = `${depth}:${w}`;
|
|
21387
|
+
let vi = variantIdx.get(key);
|
|
21388
|
+
if (vi === void 0) {
|
|
21389
|
+
vi = indentVariants.length;
|
|
21390
|
+
indentVariants.push({ depth, widthHu: w });
|
|
21391
|
+
variantIdx.set(key, vi);
|
|
21392
|
+
}
|
|
21393
|
+
return vi;
|
|
21394
|
+
};
|
|
20992
21395
|
const depthOffset = gongmun.numbering === "gaejosik" && blocks.some((b) => b.type === "heading" && b.level === 3) || gongmun.numbering === "standard" && gongmun.h2Marker === "number" && blocks.some((b) => b.type === "heading" && b.level === 2) ? 1 : 0;
|
|
21396
|
+
reclassifyLiteralItems(blocks, depthOffset);
|
|
20993
21397
|
let i = 0;
|
|
20994
21398
|
while (i < blocks.length) {
|
|
20995
21399
|
if (blocks[i].type !== "list_item") {
|
|
@@ -21016,14 +21420,21 @@ function precomputeGongmunList(blocks, gongmun) {
|
|
|
21016
21420
|
break;
|
|
21017
21421
|
}
|
|
21018
21422
|
const depths = run.map((bi) => Math.min(Math.max((blocks[bi].indent || 0) + depthOffset, 0), GONGMUN_LIST_LEVELS - 1));
|
|
21019
|
-
const
|
|
21020
|
-
const
|
|
21423
|
+
const measured = usesReportFonts(gongmun.preset);
|
|
21424
|
+
const isRef = (bi) => measured && ((blocks[bi].text || "").trimStart().startsWith("\u203B") || blocks[bi].marker === "*");
|
|
21425
|
+
const activeK = run.map((_, k) => k).filter((k) => !isRef(run[k]));
|
|
21426
|
+
const activeDepths = activeK.map((k) => depths[k]);
|
|
21427
|
+
const suppress = gongmun.suppressSingle && gongmun.numbering === "standard" ? computeSuppression(activeDepths) : activeDepths.map(() => false);
|
|
21428
|
+
const numberer = new GongmunNumberer(gongmun.numbering, gongmun.bullet2, usesAsteriskThird(gongmun.preset));
|
|
21021
21429
|
run.forEach((bi, k) => {
|
|
21022
|
-
|
|
21023
|
-
|
|
21430
|
+
if (isRef(run[k])) result.set(bi, { marker: "", depth: depths[k] });
|
|
21431
|
+
});
|
|
21432
|
+
activeK.forEach((k, j) => {
|
|
21433
|
+
const marker = numberer.next(activeDepths[j], suppress[j]);
|
|
21434
|
+
result.set(run[k], { marker, depth: depths[k], indentVariant: variantOf(marker, depths[k]) });
|
|
21024
21435
|
});
|
|
21025
21436
|
}
|
|
21026
|
-
return result;
|
|
21437
|
+
return { items: result, indentVariants };
|
|
21027
21438
|
}
|
|
21028
21439
|
|
|
21029
21440
|
// src/hwpx/gen-gaejosik.ts
|
|
@@ -21041,26 +21452,29 @@ function emptyPara(paraPrId = PARA_NORMAL, pageBreak = false, charPrId = CHAR_NO
|
|
|
21041
21452
|
const brk = pageBreak ? ` pageBreak="1"` : "";
|
|
21042
21453
|
return `<hp:p paraPrIDRef="${paraPrId}" styleIDRef="0"${brk}><hp:run charPrIDRef="${charPrId}"><hp:t></hp:t></hp:run></hp:p>`;
|
|
21043
21454
|
}
|
|
21044
|
-
var gjTableId =
|
|
21455
|
+
var gjTableId = GJ_TABLE_ID_BASE;
|
|
21456
|
+
function resetGjTableIds() {
|
|
21457
|
+
gjTableId = GJ_TABLE_ID_BASE;
|
|
21458
|
+
}
|
|
21045
21459
|
function cell(opts) {
|
|
21046
21460
|
const paras = opts.paras ?? emptyPara();
|
|
21047
|
-
return `<hp:tc 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>`;
|
|
21461
|
+
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>`;
|
|
21048
21462
|
}
|
|
21049
|
-
function table(rows, w, h, cols, tblBf = 1) {
|
|
21050
|
-
const outLR = w + 566 >
|
|
21463
|
+
function table(rows, w, h, cols, tblBf = 1, bodyWidth = GAEJOSIK_BASE_WIDTH) {
|
|
21464
|
+
const outLR = w + 566 > bodyWidth ? 0 : 283;
|
|
21051
21465
|
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>`;
|
|
21052
21466
|
}
|
|
21053
|
-
function buildGaejosikCover(title, gongmun) {
|
|
21054
|
-
const g = coverGeom(gongmun.bodyHeight, gongmun.sizes);
|
|
21467
|
+
function buildGaejosikCover(title, gongmun, bodyWidth = GAEJOSIK_BASE_WIDTH) {
|
|
21468
|
+
const g = coverGeom(gongmun.bodyHeight, gongmun.sizes, bodyWidth);
|
|
21055
21469
|
const sz = gaejosikSizes(gongmun.bodyHeight, gongmun.sizes);
|
|
21056
21470
|
const innerW = g.totalW - 282;
|
|
21057
21471
|
const titleCharPr = simulateWrap(title, innerW, innerW, sz.coverTitle, 100, "keep").lines > 2 ? GJ_CHAR_COVER_SUB : GJ_CHAR_COVER_TITLE;
|
|
21058
21472
|
const titlePara = `<hp:p paraPrIDRef="${GJ_PARA_COVER}" styleIDRef="0"><hp:run charPrIDRef="${titleCharPr}"><hp:t>${escapeXml(title)}</hp:t></hp:run></hp:p>`;
|
|
21059
21473
|
const barEmpty = emptyPara(GJ_PARA_BAR, false, GJ_CHAR_BAR);
|
|
21060
21474
|
const topRow = cell({ bf: GJ_BF_BAR_DARK, col: 0, colSpan: 2, row: 0, w: g.topDarkW, h: g.barH, paras: barEmpty }) + cell({ bf: GJ_BF_BAR_LIGHT, col: 2, row: 0, w: g.topLightW, h: g.barH, paras: barEmpty });
|
|
21061
|
-
const titleRow = cell({ bf: 1, col: 0, colSpan: 3, row: 1, w: g.totalW, h: g.titleH, paras: titlePara });
|
|
21475
|
+
const titleRow = cell({ bf: 1, col: 0, colSpan: 3, row: 1, w: g.totalW, h: g.titleH, paras: titlePara, name: "__kordoc_h1" });
|
|
21062
21476
|
const botRow = cell({ bf: GJ_BF_BAR_DARK, col: 0, row: 2, w: g.botDarkW, h: g.botBarH, paras: barEmpty }) + cell({ bf: GJ_BF_BAR_LIGHT, col: 1, colSpan: 2, row: 2, w: g.botLightW, h: g.botBarH, paras: barEmpty });
|
|
21063
|
-
const tbl2 = table([topRow, titleRow, botRow], g.totalW, g.barH + g.titleH + g.botBarH, 3);
|
|
21477
|
+
const tbl2 = table([topRow, titleRow, botRow], g.totalW, g.barH + g.titleH + g.botBarH, 3, 1, bodyWidth);
|
|
21064
21478
|
const host = `<hp:p paraPrIDRef="${PARA_NORMAL}" styleIDRef="0"><hp:run charPrIDRef="${CHAR_NORMAL}">${tbl2}</hp:run></hp:p>`;
|
|
21065
21479
|
const date = gongmun.cover?.date ?? formatGaejosikDate(/* @__PURE__ */ new Date());
|
|
21066
21480
|
const org = gongmun.cover?.org ?? "";
|
|
@@ -21074,48 +21488,51 @@ function buildGaejosikCover(title, gongmun) {
|
|
|
21074
21488
|
...org ? [...Array(4).fill(subEmpty), sub(org)] : []
|
|
21075
21489
|
];
|
|
21076
21490
|
}
|
|
21077
|
-
function buildTocBanner(gongmun) {
|
|
21491
|
+
function buildTocBanner(gongmun, bodyWidth = GAEJOSIK_BASE_WIDTH) {
|
|
21078
21492
|
const g = tocBannerGeom(gongmun.bodyHeight, gongmun.sizes);
|
|
21079
21493
|
const bfs = [GJ_BF_BAR_DARK, 1, GJ_BF_TOC_STRIPE, 1, GJ_BF_TOC_STRIPE, 1, GJ_BF_BAR_DARK];
|
|
21080
21494
|
const filler = emptyPara(GJ_PARA_BAR, false, GJ_CHAR_BAR);
|
|
21081
21495
|
const label = `<hp:p paraPrIDRef="${GONGMUN_CENTER}" styleIDRef="0"><hp:run charPrIDRef="${GJ_CHAR_TOC_LABEL}"><hp:t>\uBAA9 \uCC28</hp:t></hp:run></hp:p>`;
|
|
21082
21496
|
const row = g.colWs.map(
|
|
21083
|
-
(w, c) => cell({ bf: bfs[c], col: c, w, h: g.h, paras: c === 3 ? label : filler })
|
|
21497
|
+
(w, c) => cell({ bf: bfs[c], col: c, w, h: g.h, paras: c === 3 ? label : filler, name: c === 3 ? "__kordoc_toc" : void 0 })
|
|
21084
21498
|
).join("");
|
|
21085
|
-
const banner = table([row], g.colWs.reduce((a, b) => a + b, 0), g.h, 7, 2);
|
|
21499
|
+
const banner = table([row], g.colWs.reduce((a, b) => a + b, 0), g.h, 7, 2, bodyWidth);
|
|
21086
21500
|
return `<hp:p paraPrIDRef="${GJ_PARA_BAR}" styleIDRef="0" pageBreak="1"><hp:run charPrIDRef="${CHAR_NORMAL}">${banner}</hp:run></hp:p>`;
|
|
21087
21501
|
}
|
|
21088
|
-
function buildGaejosikToc(chapters, gongmun) {
|
|
21089
|
-
const
|
|
21502
|
+
function buildGaejosikToc(chapters, gongmun, bodyWidth = GAEJOSIK_BASE_WIDTH) {
|
|
21503
|
+
const boxW = Math.round(TOC_GEOM.boxW * bodyWidth / GAEJOSIK_BASE_WIDTH);
|
|
21504
|
+
const label = buildTocBanner(gongmun, bodyWidth);
|
|
21090
21505
|
const items = chapters.map(
|
|
21091
21506
|
(title, i) => `<hp:p paraPrIDRef="${GJ_PARA_TOC_ITEM}" styleIDRef="0"><hp:run charPrIDRef="${GJ_CHAR_TOC_ROMAN}"><hp:t>${chapterRoman(i + 1)}</hp:t></hp:run><hp:run charPrIDRef="${GJ_CHAR_TOC_ITEM}"><hp:t>. ${escapeXml(title)}</hp:t></hp:run></hp:p>`
|
|
21092
21507
|
).join("");
|
|
21093
21508
|
const box = table(
|
|
21094
|
-
[cell({ bf: GJ_BF_TOC_BOX, col: 0, w:
|
|
21095
|
-
|
|
21509
|
+
[cell({ bf: GJ_BF_TOC_BOX, col: 0, w: boxW, h: TOC_GEOM.boxH, paras: items, name: "__kordoc_toc" })],
|
|
21510
|
+
boxW,
|
|
21096
21511
|
TOC_GEOM.boxH,
|
|
21097
|
-
1
|
|
21512
|
+
1,
|
|
21513
|
+
1,
|
|
21514
|
+
bodyWidth
|
|
21098
21515
|
);
|
|
21099
21516
|
const host = `<hp:p paraPrIDRef="${GJ_PARA_BAR}" styleIDRef="0"><hp:run charPrIDRef="${CHAR_NORMAL}">${box}</hp:run></hp:p>`;
|
|
21100
21517
|
return [label, emptyPara(GJ_PARA_BAR, false, GJ_CHAR_BAR), host];
|
|
21101
21518
|
}
|
|
21102
|
-
function buildGaejosikBodyTitle(title, gongmun) {
|
|
21103
|
-
const g =
|
|
21519
|
+
function buildGaejosikBodyTitle(title, gongmun, bodyWidth = GAEJOSIK_BASE_WIDTH) {
|
|
21520
|
+
const g = coverGeom(gongmun.bodyHeight, gongmun.sizes, bodyWidth);
|
|
21104
21521
|
const bt = bodyTitleGeom(gongmun.bodyHeight, gongmun.sizes);
|
|
21105
21522
|
const barEmpty = emptyPara(GJ_PARA_BAR, false, GJ_CHAR_BAR);
|
|
21106
21523
|
const titlePara = `<hp:p paraPrIDRef="${GONGMUN_CENTER}" styleIDRef="0"><hp:run charPrIDRef="${GJ_CHAR_BODY_TITLE}"><hp:t>${escapeXml(title)}</hp:t></hp:run></hp:p>`;
|
|
21107
21524
|
const topRow = cell({ bf: GJ_BF_BAR_DARK, col: 0, colSpan: 2, row: 0, w: g.topDarkW, h: bt.barH, paras: barEmpty }) + cell({ bf: GJ_BF_BAR_LIGHT, col: 2, row: 0, w: g.topLightW, h: bt.barH, paras: barEmpty });
|
|
21108
|
-
const titleRow = cell({ bf: 1, col: 0, colSpan: 3, row: 1, w: g.totalW, h: bt.titleH, paras: titlePara });
|
|
21525
|
+
const titleRow = cell({ bf: 1, col: 0, colSpan: 3, row: 1, w: g.totalW, h: bt.titleH, paras: titlePara, name: "__kordoc_skip" });
|
|
21109
21526
|
const botRow = cell({ bf: GJ_BF_BAR_DARK, col: 0, row: 2, w: g.botDarkW, h: bt.barH, paras: barEmpty }) + cell({ bf: GJ_BF_BAR_LIGHT, col: 1, colSpan: 2, row: 2, w: g.botLightW, h: bt.barH, paras: barEmpty });
|
|
21110
|
-
const box = table([topRow, titleRow, botRow], g.totalW, bt.barH * 2 + bt.titleH, 3);
|
|
21527
|
+
const box = table([topRow, titleRow, botRow], g.totalW, bt.barH * 2 + bt.titleH, 3, 1, bodyWidth);
|
|
21111
21528
|
return `<hp:p paraPrIDRef="${PARA_NORMAL}" styleIDRef="0"><hp:run charPrIDRef="${CHAR_NORMAL}">${box}</hp:run></hp:p>`;
|
|
21112
21529
|
}
|
|
21113
|
-
function buildGaejosikChapter(n, title, gongmun) {
|
|
21114
|
-
const g = gongmun ? chapterGeom(gongmun.bodyHeight, gongmun.sizes) : chapterGeom(1500);
|
|
21530
|
+
function buildGaejosikChapter(n, title, gongmun, bodyWidth = GAEJOSIK_BASE_WIDTH, srcLevel = 2) {
|
|
21531
|
+
const g = gongmun ? chapterGeom(gongmun.bodyHeight, gongmun.sizes, bodyWidth) : chapterGeom(1500, {}, bodyWidth);
|
|
21115
21532
|
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>`;
|
|
21116
21533
|
const titlePara = `<hp:p paraPrIDRef="${PARA_NORMAL}" styleIDRef="0"><hp:run charPrIDRef="${GJ_CHAR_CHAPTER_TITLE}"><hp:t> ${escapeXml(title)}</hp:t></hp:run></hp:p>`;
|
|
21117
|
-
const row = cell({ bf: GJ_BF_CHAPTER_NUM, col: 0, w: g.numW, h: g.rowH, paras: numPara }) + cell({ bf: GJ_BF_CHAPTER_GAP, col: 1, w: g.gapW, h: g.rowH }) + cell({ bf: GJ_BF_CHAPTER_TITLE, col: 2, w: g.titleW, h: g.rowH, paras: titlePara });
|
|
21118
|
-
const tbl2 = table([row], g.numW + g.gapW + g.titleW, g.rowH, 3);
|
|
21534
|
+
const row = cell({ bf: GJ_BF_CHAPTER_NUM, col: 0, w: g.numW, h: g.rowH, paras: numPara }) + cell({ bf: GJ_BF_CHAPTER_GAP, col: 1, w: g.gapW, h: g.rowH }) + cell({ bf: GJ_BF_CHAPTER_TITLE, col: 2, w: g.titleW, h: g.rowH, paras: titlePara, name: `__kordoc_h${Math.min(Math.max(srcLevel, 1), 6)}` });
|
|
21535
|
+
const tbl2 = table([row], g.numW + g.gapW + g.titleW, g.rowH, 3, 1, bodyWidth);
|
|
21119
21536
|
return `<hp:p paraPrIDRef="${GJ_PARA_CHAPTER}" styleIDRef="0"><hp:run charPrIDRef="${CHAR_NORMAL}">${tbl2}</hp:run></hp:p>`;
|
|
21120
21537
|
}
|
|
21121
21538
|
|
|
@@ -21483,16 +21900,22 @@ var AUTONUM_PREFIX_RE = /^(?:[0-90-9a-zA-Z가-힣]{1,6}[.)\]:]|[([][0-90-
|
|
|
21483
21900
|
function htmlCellInnerToLines(inner) {
|
|
21484
21901
|
let hadNonText = false;
|
|
21485
21902
|
let work = inner;
|
|
21903
|
+
const imgSrcs = [];
|
|
21486
21904
|
if (/<table[\s>]/i.test(work)) {
|
|
21487
21905
|
hadNonText = true;
|
|
21488
21906
|
work = removeNestedTables(work);
|
|
21489
21907
|
}
|
|
21490
21908
|
if (/<img\s/i.test(work)) {
|
|
21491
21909
|
hadNonText = true;
|
|
21492
|
-
work = work.replace(/<img\s(?:"[^"]*"|'[^']*'|[^>"'])*?>/gi,
|
|
21910
|
+
work = work.replace(/<img\s(?:"[^"]*"|'[^']*'|[^>"'])*?>/gi, (tag) => {
|
|
21911
|
+
const m = /\bsrc\s*=\s*(?:"([^"]*)"|'([^']*)')/i.exec(tag);
|
|
21912
|
+
const src = m?.[1] ?? m?.[2];
|
|
21913
|
+
if (src) imgSrcs.push(src);
|
|
21914
|
+
return "";
|
|
21915
|
+
});
|
|
21493
21916
|
}
|
|
21494
21917
|
const lines = work.split(/<br\s*\/?>/gi).map((s) => s.trim()).filter((s) => s.length > 0);
|
|
21495
|
-
return { lines, hadNonText };
|
|
21918
|
+
return { lines, hadNonText, imgSrcs };
|
|
21496
21919
|
}
|
|
21497
21920
|
function extractTopLevelTables(html) {
|
|
21498
21921
|
const result = [];
|
|
@@ -21587,13 +22010,19 @@ function dataCellSpec(opts) {
|
|
|
21587
22010
|
function normalizeAnchor(s) {
|
|
21588
22011
|
return s.toLowerCase().replace(/[^\p{L}\p{N}]/gu, "").slice(0, 24);
|
|
21589
22012
|
}
|
|
21590
|
-
function
|
|
22013
|
+
function normalizeRowAnchor(cells) {
|
|
22014
|
+
const j = cells.map(normalizeAnchor).join("|");
|
|
22015
|
+
return /[\p{L}\p{N}]/u.test(j) ? j.slice(0, 64) : "";
|
|
22016
|
+
}
|
|
22017
|
+
function takeProfile(remap, rows, cols, anchor, seq, rowAnchor = "") {
|
|
21591
22018
|
if (!remap) return null;
|
|
21592
22019
|
for (const t of remap.tables) {
|
|
21593
22020
|
if (t.used) continue;
|
|
21594
22021
|
if (t.rows !== rows || t.cols !== cols) continue;
|
|
21595
22022
|
if (t.anchor && anchor) {
|
|
21596
22023
|
if (t.anchor !== anchor) continue;
|
|
22024
|
+
} else if (t.anchorRow && rowAnchor) {
|
|
22025
|
+
if (t.anchorRow !== rowAnchor) continue;
|
|
21597
22026
|
} else if (t.index !== seq) {
|
|
21598
22027
|
continue;
|
|
21599
22028
|
}
|
|
@@ -21608,10 +22037,10 @@ function parseHu(s) {
|
|
|
21608
22037
|
return Number.isFinite(n) ? n : void 0;
|
|
21609
22038
|
}
|
|
21610
22039
|
function edgeXml(tag, d) {
|
|
21611
|
-
return d ? `<hh:${tag} type="${d.type}" width="${d.width}" color="${d.color}"/>` : `<hh:${tag} type="NONE" width="0.1 mm" color="#000000"/>`;
|
|
22040
|
+
return d ? `<hh:${tag} type="${escapeXml(d.type)}" width="${escapeXml(d.width)}" color="${escapeXml(d.color)}"/>` : `<hh:${tag} type="NONE" width="0.1 mm" color="#000000"/>`;
|
|
21612
22041
|
}
|
|
21613
22042
|
function borderFillDefToXml(id, def) {
|
|
21614
|
-
const fill = def.fill?.faceColor ? `<hh:fillBrush><hh:winBrush faceColor="${def.fill.faceColor}" hatchColor="#000000" alpha="0"/></hh:fillBrush>` : "";
|
|
22043
|
+
const fill = def.fill?.faceColor ? `<hh:fillBrush><hh:winBrush faceColor="${escapeXml(def.fill.faceColor)}" hatchColor="#000000" alpha="0"/></hh:fillBrush>` : "";
|
|
21615
22044
|
return ` <hh:borderFill id="${id}" threeD="0" shadow="0" centerLine="NONE" breakCellSeparateLine="0">
|
|
21616
22045
|
<hh:slash type="NONE" Crooked="0" isCounter="0"/>
|
|
21617
22046
|
<hh:backSlash type="NONE" Crooked="0" isCounter="0"/>
|
|
@@ -21622,28 +22051,45 @@ function borderFillDefToXml(id, def) {
|
|
|
21622
22051
|
${fill}` : ""}
|
|
21623
22052
|
</hh:borderFill>`;
|
|
21624
22053
|
}
|
|
21625
|
-
var
|
|
21626
|
-
function profileCharPrXml(id, def) {
|
|
21627
|
-
const height = parseHu(def.height_hwpunit) ?? 1e3;
|
|
21628
|
-
const color = def.textColor ?? "#000000";
|
|
21629
|
-
|
|
21630
|
-
|
|
22054
|
+
var LEGACY_PROFILE_FONT_MAX = 2;
|
|
22055
|
+
function profileCharPrXml(id, def, namedFontId) {
|
|
22056
|
+
const height = Math.max(parseHu(def.height_hwpunit) ?? 1e3, 100);
|
|
22057
|
+
const color = escapeXml(def.textColor ?? "#000000");
|
|
22058
|
+
let font = 0;
|
|
22059
|
+
let restFont = 0;
|
|
22060
|
+
if (namedFontId != null) {
|
|
22061
|
+
font = namedFontId;
|
|
22062
|
+
} else {
|
|
22063
|
+
const rawFont = def.fontRef_hangul != null ? parseInt(def.fontRef_hangul, 10) || 0 : 0;
|
|
22064
|
+
font = rawFont >= 0 && rawFont <= LEGACY_PROFILE_FONT_MAX ? rawFont : 0;
|
|
22065
|
+
restFont = font;
|
|
22066
|
+
}
|
|
21631
22067
|
const boldAttr = def.bold ? ` bold="1"` : "";
|
|
21632
22068
|
const italicAttr = def.italic ? ` italic="1"` : "";
|
|
21633
22069
|
const underline = def.underline ? `
|
|
21634
22070
|
<hh:underline type="BOTTOM" shape="SOLID" color="${color}"/>` : "";
|
|
21635
22071
|
return ` <hh:charPr id="${id}" height="${height}" textColor="${color}" shadeColor="none" useFontSpace="0" useKerning="0" symMark="NONE" borderFillIDRef="1"${boldAttr}${italicAttr}>
|
|
21636
|
-
<hh:fontRef hangul="${font}" latin="${font}" hanja="${
|
|
22072
|
+
<hh:fontRef hangul="${font}" latin="${font}" hanja="${restFont}" japanese="${restFont}" other="${restFont}" symbol="${restFont}" user="${restFont}"/>
|
|
21637
22073
|
<hh:ratio hangul="100" latin="100" hanja="100" japanese="100" other="100" symbol="100" user="100"/>
|
|
21638
22074
|
<hh:spacing hangul="0" latin="0" hanja="0" japanese="0" other="0" symbol="0" user="0"/>
|
|
21639
22075
|
<hh:relSz hangul="100" latin="100" hanja="100" japanese="100" other="100" symbol="100" user="100"/>
|
|
21640
22076
|
<hh:offset hangul="0" latin="0" hanja="0" japanese="0" other="0" symbol="0" user="0"/>${underline}
|
|
21641
22077
|
</hh:charPr>`;
|
|
21642
22078
|
}
|
|
21643
|
-
function buildProfileRemap(profile, charPrBase, borderFillBase = 3) {
|
|
21644
|
-
const remap = { borderFillXmls: [], charPrXmls: [], tables: [] };
|
|
22079
|
+
function buildProfileRemap(profile, charPrBase, borderFillBase = 3, fontBase = 3) {
|
|
22080
|
+
const remap = { borderFillXmls: [], charPrXmls: [], fontFaces: [], tables: [] };
|
|
21645
22081
|
let bfNext = borderFillBase;
|
|
21646
22082
|
let charNext = charPrBase;
|
|
22083
|
+
const fontIds = /* @__PURE__ */ new Map();
|
|
22084
|
+
const fontIdOf = (name) => {
|
|
22085
|
+
let id = fontIds.get(name);
|
|
22086
|
+
if (id == null) {
|
|
22087
|
+
id = fontBase + remap.fontFaces.length;
|
|
22088
|
+
remap.fontFaces.push(name);
|
|
22089
|
+
fontIds.set(name, id);
|
|
22090
|
+
}
|
|
22091
|
+
return id;
|
|
22092
|
+
};
|
|
21647
22093
|
for (const t of profile.tables) {
|
|
21648
22094
|
const localBf = {};
|
|
21649
22095
|
for (const [key, def] of Object.entries(t.used_border_fills ?? {})) {
|
|
@@ -21654,7 +22100,7 @@ function buildProfileRemap(profile, charPrBase, borderFillBase = 3) {
|
|
|
21654
22100
|
const localChar = {};
|
|
21655
22101
|
for (const [key, def] of Object.entries(t.used_char_prs ?? {})) {
|
|
21656
22102
|
const gid = charNext++;
|
|
21657
|
-
remap.charPrXmls.push(profileCharPrXml(gid, def));
|
|
22103
|
+
remap.charPrXmls.push(profileCharPrXml(gid, def, def.fontName_hangul ? fontIdOf(def.fontName_hangul) : void 0));
|
|
21658
22104
|
localChar[key] = gid;
|
|
21659
22105
|
}
|
|
21660
22106
|
let colWidths;
|
|
@@ -21668,6 +22114,7 @@ function buildProfileRemap(profile, charPrBase, borderFillBase = 3) {
|
|
|
21668
22114
|
cols: t.cols,
|
|
21669
22115
|
// 재정규화 — 추출기는 정규화해 담지만 손편집된 프로필 JSON도 같은 키 공간으로
|
|
21670
22116
|
anchor: t.anchor_text ? normalizeAnchor(t.anchor_text) : void 0,
|
|
22117
|
+
anchorRow: t.anchor_row ? normalizeRowAnchor(t.anchor_row.split("|")) || void 0 : void 0,
|
|
21671
22118
|
width: parseHu(t.width_hwpunit),
|
|
21672
22119
|
colWidths,
|
|
21673
22120
|
cellBf: /* @__PURE__ */ new Map(),
|
|
@@ -21685,17 +22132,208 @@ function buildProfileRemap(profile, charPrBase, borderFillBase = 3) {
|
|
|
21685
22132
|
const h = parseHu(cell2.height_hwpunit);
|
|
21686
22133
|
if (h != null) tr.cellH.set(k, h);
|
|
21687
22134
|
}
|
|
21688
|
-
remap.tables.push(tr);
|
|
22135
|
+
remap.tables.push(tr);
|
|
22136
|
+
}
|
|
22137
|
+
return remap;
|
|
22138
|
+
}
|
|
22139
|
+
|
|
22140
|
+
// src/hwpx/gen-image.ts
|
|
22141
|
+
var PLACEHOLDER_BMP = Uint8Array.from([
|
|
22142
|
+
66,
|
|
22143
|
+
77,
|
|
22144
|
+
58,
|
|
22145
|
+
0,
|
|
22146
|
+
0,
|
|
22147
|
+
0,
|
|
22148
|
+
0,
|
|
22149
|
+
0,
|
|
22150
|
+
0,
|
|
22151
|
+
0,
|
|
22152
|
+
54,
|
|
22153
|
+
0,
|
|
22154
|
+
0,
|
|
22155
|
+
0,
|
|
22156
|
+
40,
|
|
22157
|
+
0,
|
|
22158
|
+
0,
|
|
22159
|
+
0,
|
|
22160
|
+
1,
|
|
22161
|
+
0,
|
|
22162
|
+
0,
|
|
22163
|
+
0,
|
|
22164
|
+
1,
|
|
22165
|
+
0,
|
|
22166
|
+
0,
|
|
22167
|
+
0,
|
|
22168
|
+
1,
|
|
22169
|
+
0,
|
|
22170
|
+
24,
|
|
22171
|
+
0,
|
|
22172
|
+
0,
|
|
22173
|
+
0,
|
|
22174
|
+
0,
|
|
22175
|
+
0,
|
|
22176
|
+
4,
|
|
22177
|
+
0,
|
|
22178
|
+
0,
|
|
22179
|
+
0,
|
|
22180
|
+
19,
|
|
22181
|
+
11,
|
|
22182
|
+
0,
|
|
22183
|
+
0,
|
|
22184
|
+
19,
|
|
22185
|
+
11,
|
|
22186
|
+
0,
|
|
22187
|
+
0,
|
|
22188
|
+
0,
|
|
22189
|
+
0,
|
|
22190
|
+
0,
|
|
22191
|
+
0,
|
|
22192
|
+
0,
|
|
22193
|
+
0,
|
|
22194
|
+
0,
|
|
22195
|
+
0,
|
|
22196
|
+
255,
|
|
22197
|
+
255,
|
|
22198
|
+
255,
|
|
22199
|
+
0
|
|
22200
|
+
]);
|
|
22201
|
+
var PLACEHOLDER_PNG = Uint8Array.from([
|
|
22202
|
+
137,
|
|
22203
|
+
80,
|
|
22204
|
+
78,
|
|
22205
|
+
71,
|
|
22206
|
+
13,
|
|
22207
|
+
10,
|
|
22208
|
+
26,
|
|
22209
|
+
10,
|
|
22210
|
+
0,
|
|
22211
|
+
0,
|
|
22212
|
+
0,
|
|
22213
|
+
13,
|
|
22214
|
+
73,
|
|
22215
|
+
72,
|
|
22216
|
+
68,
|
|
22217
|
+
82,
|
|
22218
|
+
0,
|
|
22219
|
+
0,
|
|
22220
|
+
0,
|
|
22221
|
+
1,
|
|
22222
|
+
0,
|
|
22223
|
+
0,
|
|
22224
|
+
0,
|
|
22225
|
+
1,
|
|
22226
|
+
8,
|
|
22227
|
+
6,
|
|
22228
|
+
0,
|
|
22229
|
+
0,
|
|
22230
|
+
0,
|
|
22231
|
+
31,
|
|
22232
|
+
21,
|
|
22233
|
+
196,
|
|
22234
|
+
137,
|
|
22235
|
+
0,
|
|
22236
|
+
0,
|
|
22237
|
+
0,
|
|
22238
|
+
11,
|
|
22239
|
+
73,
|
|
22240
|
+
68,
|
|
22241
|
+
65,
|
|
22242
|
+
84,
|
|
22243
|
+
120,
|
|
22244
|
+
156,
|
|
22245
|
+
99,
|
|
22246
|
+
0,
|
|
22247
|
+
1,
|
|
22248
|
+
0,
|
|
22249
|
+
0,
|
|
22250
|
+
5,
|
|
22251
|
+
0,
|
|
22252
|
+
1,
|
|
22253
|
+
13,
|
|
22254
|
+
10,
|
|
22255
|
+
45,
|
|
22256
|
+
180,
|
|
22257
|
+
0,
|
|
22258
|
+
0,
|
|
22259
|
+
0,
|
|
22260
|
+
0,
|
|
22261
|
+
73,
|
|
22262
|
+
69,
|
|
22263
|
+
78,
|
|
22264
|
+
68,
|
|
22265
|
+
174,
|
|
22266
|
+
66,
|
|
22267
|
+
96,
|
|
22268
|
+
130
|
|
22269
|
+
]);
|
|
22270
|
+
var IMAGE_EXT_MIME = {
|
|
22271
|
+
png: "image/png",
|
|
22272
|
+
jpg: "image/jpeg",
|
|
22273
|
+
jpeg: "image/jpeg",
|
|
22274
|
+
gif: "image/gif",
|
|
22275
|
+
bmp: "image/bmp",
|
|
22276
|
+
tif: "image/tiff",
|
|
22277
|
+
tiff: "image/tiff"
|
|
22278
|
+
};
|
|
22279
|
+
var ImageRegistry = class {
|
|
22280
|
+
byUrl = /* @__PURE__ */ new Map();
|
|
22281
|
+
ids = /* @__PURE__ */ new Set();
|
|
22282
|
+
parts = [];
|
|
22283
|
+
picSeq = 0;
|
|
22284
|
+
/** url 등록(중복 시 기존 파트) — 수용 불가면 null */
|
|
22285
|
+
take(url) {
|
|
22286
|
+
const cached = this.byUrl.get(url);
|
|
22287
|
+
if (cached !== void 0) return cached;
|
|
22288
|
+
let part = null;
|
|
22289
|
+
const m = /^([A-Za-z0-9._-]+)\.([A-Za-z0-9]+)$/.exec(url);
|
|
22290
|
+
const mime = m ? IMAGE_EXT_MIME[m[2].toLowerCase()] : void 0;
|
|
22291
|
+
if (m && mime && !url.includes("..")) {
|
|
22292
|
+
let itemId = m[1].replace(/[^A-Za-z0-9_]/g, "_");
|
|
22293
|
+
let n = 1;
|
|
22294
|
+
while (this.ids.has(itemId)) itemId = `${m[1].replace(/[^A-Za-z0-9_]/g, "_")}_${n++}`;
|
|
22295
|
+
this.ids.add(itemId);
|
|
22296
|
+
part = {
|
|
22297
|
+
name: `BinData/${url}`,
|
|
22298
|
+
itemId,
|
|
22299
|
+
mime,
|
|
22300
|
+
data: m[2].toLowerCase() === "bmp" ? PLACEHOLDER_BMP : PLACEHOLDER_PNG
|
|
22301
|
+
};
|
|
22302
|
+
this.parts.push(part);
|
|
22303
|
+
}
|
|
22304
|
+
this.byUrl.set(url, part);
|
|
22305
|
+
return part;
|
|
21689
22306
|
}
|
|
21690
|
-
|
|
22307
|
+
/** manifest <opf:item> 조각들 (실측: isEmbeded="1") */
|
|
22308
|
+
manifestItems() {
|
|
22309
|
+
return this.parts.map((p) => `<opf:item id="${p.itemId}" href="${p.name}" media-type="${p.mime}" isEmbeded="1"/>`);
|
|
22310
|
+
}
|
|
22311
|
+
/** 인라인 <hp:pic> XML — 실측 저장본 미러, treatAsChar=1 (셀·문단 안 배치) */
|
|
22312
|
+
inlinePicXml(part) {
|
|
22313
|
+
const id = PIC_ID_BASE + ++this.picSeq;
|
|
22314
|
+
const s = 1130;
|
|
22315
|
+
return `<hp:pic id="${id}" zOrder="0" numberingType="PICTURE" textWrap="TOP_AND_BOTTOM" textFlow="BOTH_SIDES" lock="0" dropcapstyle="None" href="" groupLevel="0" instid="${id}" reverse="0" xmlns:hc="http://www.hancom.co.kr/hwpml/2011/core"><hp:offset x="0" y="0"/><hp:orgSz width="${s}" height="${s}"/><hp:curSz width="${s}" height="${s}"/><hp:flip horizontal="0" vertical="0"/><hp:rotationInfo angle="0" centerX="${s / 2}" centerY="${s / 2}" rotateimage="1"/><hp:renderingInfo><hc:transMatrix e1="1" e2="0" e3="0" e4="0" e5="1" e6="0"/><hc:scaMatrix e1="1" e2="0" e3="0" e4="0" e5="1" e6="0"/><hc:rotMatrix e1="1" e2="0" e3="0" e4="0" e5="1" e6="0"/></hp:renderingInfo><hp:imgRect><hc:pt0 x="0" y="0"/><hc:pt1 x="${s}" y="0"/><hc:pt2 x="${s}" y="${s}"/><hc:pt3 x="0" y="${s}"/></hp:imgRect><hp:imgClip left="0" right="${s}" top="0" bottom="${s}"/><hp:inMargin left="0" right="0" top="0" bottom="0"/><hp:imgDim dimwidth="${s}" dimheight="${s}"/><hc:img binaryItemIDRef="${part.itemId}" bright="0" contrast="0" effect="REAL_PIC" alpha="0"/><hp:effects/><hp:sz width="${s}" widthRelTo="ABSOLUTE" height="${s}" 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="0"/></hp:pic>`;
|
|
22316
|
+
}
|
|
22317
|
+
};
|
|
22318
|
+
var MD_IMAGE_RE = /!\[[^\]]*\]\(([^)\s]+)\)/g;
|
|
22319
|
+
function splitImageRefs(text) {
|
|
22320
|
+
const urls = [];
|
|
22321
|
+
const out = text.replace(MD_IMAGE_RE, (_, url) => {
|
|
22322
|
+
urls.push(url);
|
|
22323
|
+
return "";
|
|
22324
|
+
});
|
|
22325
|
+
return { text: out, urls };
|
|
21691
22326
|
}
|
|
21692
22327
|
|
|
21693
22328
|
// src/hwpx/gen-table.ts
|
|
21694
|
-
var TABLE_ID_BASE =
|
|
22329
|
+
var TABLE_ID_BASE = DATA_TABLE_ID_BASE;
|
|
21695
22330
|
var tableIdCounter = TABLE_ID_BASE;
|
|
21696
22331
|
function nextTableId() {
|
|
21697
22332
|
return ++tableIdCounter;
|
|
21698
22333
|
}
|
|
22334
|
+
function resetTableIds() {
|
|
22335
|
+
tableIdCounter = TABLE_ID_BASE;
|
|
22336
|
+
}
|
|
21699
22337
|
var DATA_TABLE_INSET = 1800;
|
|
21700
22338
|
function anchorOfMarkdownCell(cell2) {
|
|
21701
22339
|
return normalizeAnchor(cell2.replace(/!\[[^\]]*\]\([^)]*\)/g, ""));
|
|
@@ -21714,7 +22352,7 @@ function profileColWidths(tp, colCnt) {
|
|
|
21714
22352
|
var CELL_PAD = 1200;
|
|
21715
22353
|
function cellContentWidth(text, charHeight) {
|
|
21716
22354
|
let max = 0;
|
|
21717
|
-
for (const seg of text.replace(/\*\*|__|`/g, "").split(/<br\s*\/?>/i)) {
|
|
22355
|
+
for (const seg of text.replace(/!\[[^\]]*\]\([^)\s]+\)/g, "").replace(/\*\*|__|`/g, "").split(/<br\s*\/?>/i)) {
|
|
21718
22356
|
const w = measureTextWidth(seg.trim(), charHeight, 100);
|
|
21719
22357
|
if (w > max) max = w;
|
|
21720
22358
|
}
|
|
@@ -21722,7 +22360,7 @@ function cellContentWidth(text, charHeight) {
|
|
|
21722
22360
|
}
|
|
21723
22361
|
function cellMinWordWidth(text, charHeight) {
|
|
21724
22362
|
let max = 0;
|
|
21725
|
-
for (const seg of text.replace(/\*\*|__|`/g, "").split(/<br\s*\/?>/i)) {
|
|
22363
|
+
for (const seg of text.replace(/!\[[^\]]*\]\([^)\s]+\)/g, "").replace(/\*\*|__|`/g, "").split(/<br\s*\/?>/i)) {
|
|
21726
22364
|
for (const word of seg.trim().split(/\s+/)) {
|
|
21727
22365
|
const w = measureTextWidth(word, charHeight, 100);
|
|
21728
22366
|
if (w > max) max = w;
|
|
@@ -21752,7 +22390,7 @@ function computeColWidths(colMax, totalWidth, colMinWord = []) {
|
|
|
21752
22390
|
const sumFreeRaw = [...free].reduce((a, i) => a + raw[i], 0);
|
|
21753
22391
|
if (budget > sumFreeRaw * 1.6) {
|
|
21754
22392
|
const sumRaw = raw.reduce((a, b) => a + b, 0);
|
|
21755
|
-
for (let i = 0; i < colCnt; i++) widths[i] = raw[i] + Math.floor(raw[i] / sumRaw * (totalWidth - sumRaw));
|
|
22393
|
+
for (let i = 0; i < colCnt; i++) widths[i] = Math.min(cap, Math.round(raw[i]) + Math.floor(raw[i] / sumRaw * (totalWidth - sumRaw)));
|
|
21756
22394
|
free.clear();
|
|
21757
22395
|
} else {
|
|
21758
22396
|
for (; ; ) {
|
|
@@ -21773,7 +22411,23 @@ function computeColWidths(colMax, totalWidth, colMinWord = []) {
|
|
|
21773
22411
|
}
|
|
21774
22412
|
let rem = totalWidth - widths.reduce((a, b) => a + b, 0);
|
|
21775
22413
|
const order = [...raw.keys()].sort((a, b) => raw[b] - raw[a]);
|
|
21776
|
-
for (let k = 0; rem > 0; k = (k + 1) % colCnt, rem--)
|
|
22414
|
+
for (let k = 0, skipped = 0; rem > 0; k = (k + 1) % colCnt, rem--) {
|
|
22415
|
+
while (widths[order[k]] >= cap && skipped < colCnt) {
|
|
22416
|
+
skipped++;
|
|
22417
|
+
k = (k + 1) % colCnt;
|
|
22418
|
+
}
|
|
22419
|
+
skipped = 0;
|
|
22420
|
+
widths[order[k]]++;
|
|
22421
|
+
}
|
|
22422
|
+
for (let k = 0, spins = 0; rem < 0; k = (k + 1) % colCnt, rem++) {
|
|
22423
|
+
while (widths[order[k]] <= 1 && spins < colCnt) {
|
|
22424
|
+
spins++;
|
|
22425
|
+
k = (k + 1) % colCnt;
|
|
22426
|
+
}
|
|
22427
|
+
if (widths[order[k]] <= 1) break;
|
|
22428
|
+
spins = 0;
|
|
22429
|
+
widths[order[k]]--;
|
|
22430
|
+
}
|
|
21777
22431
|
return widths;
|
|
21778
22432
|
}
|
|
21779
22433
|
function estimateRowHeight(cells, widths, charHeight) {
|
|
@@ -21788,13 +22442,14 @@ function estimateRowHeight(cells, widths, charHeight) {
|
|
|
21788
22442
|
});
|
|
21789
22443
|
return maxLines * Math.round(charHeight * 1.6) + 282;
|
|
21790
22444
|
}
|
|
21791
|
-
function generateTable(rows, theme, style = null, remap = null, seq = 0) {
|
|
22445
|
+
function generateTable(rows, theme, style = null, remap = null, seq = 0, images = null) {
|
|
21792
22446
|
const rowCnt = rows.length;
|
|
21793
22447
|
const colCnt = Math.max(...rows.map((r) => r.length), 1);
|
|
21794
22448
|
const reg = style?.bfRegistry ?? null;
|
|
21795
22449
|
const totalW = style ? reg ? style.totalWidth - DATA_TABLE_INSET : style.totalWidth : 44e3;
|
|
21796
22450
|
const measureH = style?.charHeight ?? 1e3;
|
|
21797
|
-
const
|
|
22451
|
+
const rowAnchor = normalizeRowAnchor((rows[0] ?? []).map((c) => c.replace(/!\[[^\]]*\]\([^)]*\)/g, "")));
|
|
22452
|
+
const prof = takeProfile(remap, rowCnt, colCnt, anchorOfMarkdownCell(rows[0]?.[0] ?? ""), seq, rowAnchor);
|
|
21798
22453
|
const colMax = Array(colCnt).fill(0);
|
|
21799
22454
|
const colMaxBody = Array(colCnt).fill(0);
|
|
21800
22455
|
const colMinWord = Array(colCnt).fill(0);
|
|
@@ -21826,8 +22481,23 @@ function generateTable(rows, theme, style = null, remap = null, seq = 0) {
|
|
|
21826
22481
|
const centered = isHeaderRow || colCentered[colIdx];
|
|
21827
22482
|
const paraPrId = style ? centered ? style.tblCenterParaPr ?? style.centerParaPr : reg ? style.tblLeftParaPr ?? PARA_NORMAL : PARA_NORMAL : PARA_NORMAL;
|
|
21828
22483
|
const p = cell2.split(/<br\s*\/?>/i).map((seg) => {
|
|
22484
|
+
let picRuns = "";
|
|
22485
|
+
if (images) {
|
|
22486
|
+
const { text: rest, urls } = splitImageRefs(seg);
|
|
22487
|
+
if (urls.length > 0) {
|
|
22488
|
+
const pics = urls.map((u) => {
|
|
22489
|
+
const part = images.take(u);
|
|
22490
|
+
return part ? images.inlinePicXml(part) : null;
|
|
22491
|
+
});
|
|
22492
|
+
if (pics.every(Boolean)) {
|
|
22493
|
+
seg = rest;
|
|
22494
|
+
picRuns = `<hp:run charPrIDRef="${cellCharPr}">${pics.join("")}</hp:run>`;
|
|
22495
|
+
}
|
|
22496
|
+
}
|
|
22497
|
+
}
|
|
21829
22498
|
const runs = generateRuns(seg, cellCharPr, prof?.cellChar.has(k) ? void 0 : mapId);
|
|
21830
|
-
|
|
22499
|
+
const body = (runs || (picRuns ? "" : `<hp:run charPrIDRef="${cellCharPr}"><hp:t></hp:t></hp:run>`)) + picRuns;
|
|
22500
|
+
return `<hp:p paraPrIDRef="${paraPrId}" styleIDRef="0">${body}</hp:p>`;
|
|
21831
22501
|
}).join("");
|
|
21832
22502
|
const bf = prof?.cellBf.get(k) ?? (reg ? reg.get(dataCellSpec({
|
|
21833
22503
|
row: rowIdx,
|
|
@@ -21869,23 +22539,37 @@ function layoutHtmlRows(rows) {
|
|
|
21869
22539
|
colCnt = Math.max(colCnt, c);
|
|
21870
22540
|
}
|
|
21871
22541
|
}
|
|
22542
|
+
for (let r = 0; r < rows.length; r++) {
|
|
22543
|
+
for (let c = 0; c < colCnt; c++) {
|
|
22544
|
+
if (!occupied.has(`${r},${c}`)) {
|
|
22545
|
+
placed.push({ r, c, colSpan: 1, rowSpan: 1, inner: "", isHeader: rows[r].tag === "th" });
|
|
22546
|
+
occupied.add(`${r},${c}`);
|
|
22547
|
+
}
|
|
22548
|
+
}
|
|
22549
|
+
}
|
|
22550
|
+
placed.sort((a, b) => a.r - b.r || a.c - b.c);
|
|
21872
22551
|
return { placed, rowCnt: rows.length, colCnt };
|
|
21873
22552
|
}
|
|
21874
22553
|
function unescapeHtml(s) {
|
|
21875
22554
|
return s.replace(/</g, "<").replace(/>/g, ">").replace(/"/g, '"').replace(/'/g, "'").replace(/&/g, "&");
|
|
21876
22555
|
}
|
|
21877
|
-
function generateHtmlTableXml(rawHtml, theme, totalWidth = 44e3, style = null, remap = null, seq = 0) {
|
|
22556
|
+
function generateHtmlTableXml(rawHtml, theme, totalWidth = 44e3, style = null, remap = null, seq = 0, images = null) {
|
|
21878
22557
|
const rows = parseHtmlTable(rawHtml);
|
|
21879
22558
|
if (!rows || rows.length === 0) return null;
|
|
21880
22559
|
const { placed, rowCnt, colCnt } = layoutHtmlRows(rows);
|
|
21881
22560
|
if (rowCnt === 0 || colCnt === 0) return null;
|
|
21882
22561
|
const measureH = style?.charHeight ?? 1e3;
|
|
21883
22562
|
const first = placed.find((p) => p.r === 0 && p.c === 0) ?? placed[0];
|
|
21884
|
-
const
|
|
22563
|
+
const row0 = Array.from({ length: colCnt }, () => "");
|
|
22564
|
+
for (const cell2 of placed) {
|
|
22565
|
+
if (cell2.r === 0 && cell2.c < colCnt) row0[cell2.c] = anchorOfHtmlCell(cell2.inner);
|
|
22566
|
+
}
|
|
22567
|
+
const prof = takeProfile(remap, rowCnt, colCnt, first ? anchorOfHtmlCell(first.inner) : "", seq, normalizeRowAnchor(row0));
|
|
21885
22568
|
const colMax = Array(colCnt).fill(0);
|
|
21886
22569
|
const colMaxBody = Array(colCnt).fill(0);
|
|
21887
22570
|
const colMinWord = Array(colCnt).fill(0);
|
|
21888
|
-
const
|
|
22571
|
+
const cellParsed = placed.map((cell2) => htmlCellInnerToLines(cell2.inner));
|
|
22572
|
+
const cellLines = cellParsed.map((p) => p.lines);
|
|
21889
22573
|
placed.forEach((cell2, i) => {
|
|
21890
22574
|
const w = Math.max(...cellLines[i].map((l) => measureTextWidth(unescapeHtml(l).trim(), measureH, 100)), 0) / cell2.colSpan;
|
|
21891
22575
|
const mw = cell2.colSpan === 1 ? Math.max(...cellLines[i].map((l) => cellMinWordWidth(unescapeHtml(l), measureH)), 0) : 0;
|
|
@@ -21906,7 +22590,33 @@ function generateHtmlTableXml(rawHtml, theme, totalWidth = 44e3, style = null, r
|
|
|
21906
22590
|
const reg = style?.bfRegistry ?? null;
|
|
21907
22591
|
let htmlHeaderRows = 0;
|
|
21908
22592
|
while (htmlHeaderRows < rows.length && rows[htmlHeaderRows].tag === "th") htmlHeaderRows++;
|
|
21909
|
-
const
|
|
22593
|
+
const tableRowHeights = Array(rowCnt).fill(cellH);
|
|
22594
|
+
const meta = placed.map((cell2, i) => {
|
|
22595
|
+
const rowSpan = Math.min(cell2.rowSpan, rowCnt - cell2.r);
|
|
22596
|
+
const lines = cellLines[i];
|
|
22597
|
+
let nestedH = 0;
|
|
22598
|
+
const nestedXmls = [];
|
|
22599
|
+
for (const nested of extractTopLevelTables(cell2.inner)) {
|
|
22600
|
+
const sw = spanW(cell2);
|
|
22601
|
+
const nestedW = Math.max(Math.min(Math.max(sw - 1020, 4e3), sw - 282), 500);
|
|
22602
|
+
const nestedXml = generateHtmlTableXml(nested, theme, nestedW, style ? { ...style, totalWidth: nestedW } : null);
|
|
22603
|
+
if (nestedXml) {
|
|
22604
|
+
nestedXmls.push(nestedXml);
|
|
22605
|
+
const szH = nestedXml.match(/<hp:sz [^>]*height="(\d+)"/)?.[1];
|
|
22606
|
+
nestedH += (szH ? Number(szH) : (nested.match(/<tr[\s>]/gi) ?? []).length * cellH) + 300;
|
|
22607
|
+
}
|
|
22608
|
+
}
|
|
22609
|
+
const usable = Math.max(spanW(cell2) - CELL_PAD, 1e3);
|
|
22610
|
+
let wrapLines = 0;
|
|
22611
|
+
for (const line of lines) wrapLines += Math.max(1, Math.ceil(measureTextWidth(unescapeHtml(line).trim(), measureH, 100) / usable));
|
|
22612
|
+
const lineH = style ? Math.round(measureH * 1.6) : 800;
|
|
22613
|
+
const contentH = Math.max(cellH * rowSpan, Math.max(wrapLines, 1) * lineH + nestedH);
|
|
22614
|
+
const cellHeight = Math.max(prof?.cellH.get(`${cell2.r},${cell2.c}`) ?? 0, contentH);
|
|
22615
|
+
const perRow = Math.ceil(cellHeight / rowSpan);
|
|
22616
|
+
for (let r = cell2.r; r < cell2.r + rowSpan; r++) tableRowHeights[r] = Math.max(tableRowHeights[r], perRow);
|
|
22617
|
+
return { cell: cell2, rowSpan, lines, nestedXmls, imgSrcs: cellParsed[i].imgSrcs };
|
|
22618
|
+
});
|
|
22619
|
+
const tcXmls = meta.map(({ cell: cell2, rowSpan, lines, nestedXmls, imgSrcs }) => {
|
|
21910
22620
|
const k = `${cell2.r},${cell2.c}`;
|
|
21911
22621
|
const isHeader = cell2.isHeader;
|
|
21912
22622
|
const baseCharPr = style ? style.charPr : CHAR_NORMAL;
|
|
@@ -21915,27 +22625,42 @@ function generateHtmlTableXml(rawHtml, theme, totalWidth = 44e3, style = null, r
|
|
|
21915
22625
|
const charPrId = prof?.cellChar.get(k) ?? defaultCharPr;
|
|
21916
22626
|
const centered = isHeader || colCentered[cell2.c];
|
|
21917
22627
|
const paraPrId = style ? centered ? reg ? style.tblCenterParaPr ?? style.centerParaPr : style.centerParaPr : reg ? style.tblLeftParaPr ?? PARA_NORMAL : PARA_NORMAL : PARA_NORMAL;
|
|
21918
|
-
const
|
|
21919
|
-
const paras =
|
|
21920
|
-
|
|
21921
|
-
|
|
21922
|
-
|
|
21923
|
-
|
|
21924
|
-
|
|
21925
|
-
|
|
21926
|
-
|
|
21927
|
-
|
|
21928
|
-
|
|
22628
|
+
const picUrls = images ? [...imgSrcs] : [];
|
|
22629
|
+
const paras = [];
|
|
22630
|
+
for (const line of lines) {
|
|
22631
|
+
let text = unescapeHtml(line);
|
|
22632
|
+
if (images) {
|
|
22633
|
+
const { text: rest, urls } = splitImageRefs(text);
|
|
22634
|
+
if (urls.length > 0) {
|
|
22635
|
+
picUrls.push(...urls);
|
|
22636
|
+
text = rest;
|
|
22637
|
+
}
|
|
22638
|
+
}
|
|
22639
|
+
if (!images || text.trim() || picUrls.length === 0) {
|
|
22640
|
+
paras.push(`<hp:p paraPrIDRef="${paraPrId}" styleIDRef="0"><hp:run charPrIDRef="${charPrId}"><hp:t>${escapeXml(text)}</hp:t></hp:run></hp:p>`);
|
|
22641
|
+
}
|
|
22642
|
+
}
|
|
22643
|
+
if (images && picUrls.length > 0) {
|
|
22644
|
+
const pics = picUrls.map((u) => {
|
|
22645
|
+
const part = images.take(u);
|
|
22646
|
+
return part ? images.inlinePicXml(part) : null;
|
|
22647
|
+
}).filter(Boolean);
|
|
22648
|
+
if (pics.length > 0) {
|
|
22649
|
+
paras.push(`<hp:p paraPrIDRef="${paraPrId}" styleIDRef="0"><hp:run charPrIDRef="${charPrId}">${pics.join("")}</hp:run></hp:p>`);
|
|
21929
22650
|
}
|
|
21930
22651
|
}
|
|
22652
|
+
for (const nestedXml of nestedXmls) {
|
|
22653
|
+
paras.push(`<hp:p paraPrIDRef="0" styleIDRef="0"><hp:run charPrIDRef="0">${nestedXml}</hp:run></hp:p>`);
|
|
22654
|
+
}
|
|
21931
22655
|
if (paras.length === 0) {
|
|
21932
22656
|
paras.push(`<hp:p paraPrIDRef="${paraPrId}" styleIDRef="0"><hp:run charPrIDRef="${charPrId}"><hp:t></hp:t></hp:run></hp:p>`);
|
|
21933
22657
|
}
|
|
21934
|
-
|
|
21935
|
-
|
|
22658
|
+
let cellHeight = 0;
|
|
22659
|
+
for (let r = cell2.r; r < cell2.r + rowSpan; r++) cellHeight += tableRowHeights[r];
|
|
22660
|
+
const rowEnd = cell2.r + rowSpan - 1;
|
|
21936
22661
|
const bf = prof?.cellBf.get(k) ?? (reg ? reg.get(dataCellSpec({
|
|
21937
22662
|
row: cell2.r,
|
|
21938
|
-
rowEnd
|
|
22663
|
+
rowEnd,
|
|
21939
22664
|
col: cell2.c,
|
|
21940
22665
|
colEnd: cell2.c + cell2.colSpan - 1,
|
|
21941
22666
|
rowCnt,
|
|
@@ -21943,31 +22668,35 @@ function generateHtmlTableXml(rawHtml, theme, totalWidth = 44e3, style = null, r
|
|
|
21943
22668
|
headerRows: htmlHeaderRows,
|
|
21944
22669
|
fill: isHeader ? style.headerFill ?? "#E6E6E6" : void 0
|
|
21945
22670
|
})) : style && isHeader ? style.headerBf : 2);
|
|
21946
|
-
return `<hp:tc name="" header="${isHeader ? 1 : 0}" hasMargin="0" protect="0" editable="1" dirty="0" borderFillIDRef="${bf}"><hp:subList id="" textDirection="HORIZONTAL" lineWrap="BREAK" vertAlign="${style ? "CENTER" : "TOP"}" linkListIDRef="0" linkListNextIDRef="0" textWidth="0" textHeight="0" hasTextRef="0" hasNumRef="0">${paras.join("")}</hp:subList><hp:cellAddr colAddr="${cell2.c}" rowAddr="${cell2.r}"/><hp:cellSpan colSpan="${cell2.colSpan}" rowSpan="${
|
|
22671
|
+
return `<hp:tc name="" header="${isHeader ? 1 : 0}" hasMargin="0" protect="0" editable="1" dirty="0" borderFillIDRef="${bf}"><hp:subList id="" textDirection="HORIZONTAL" lineWrap="BREAK" vertAlign="${style ? "CENTER" : "TOP"}" linkListIDRef="0" linkListNextIDRef="0" textWidth="0" textHeight="0" hasTextRef="0" hasNumRef="0">${paras.join("")}</hp:subList><hp:cellAddr colAddr="${cell2.c}" rowAddr="${cell2.r}"/><hp:cellSpan colSpan="${cell2.colSpan}" rowSpan="${rowSpan}"/><hp:cellSz width="${spanW(cell2)}" height="${cellHeight}"/><hp:cellMargin left="141" right="141" top="141" bottom="141"/></hp:tc>`;
|
|
21947
22672
|
});
|
|
21948
22673
|
const trXmls = [];
|
|
21949
22674
|
for (let r = 0; r < rowCnt; r++) {
|
|
21950
22675
|
const rowTcs = tcXmls.filter((_, i) => placed[i].r === r);
|
|
21951
22676
|
trXmls.push(`<hp:tr>${rowTcs.join("")}</hp:tr>`);
|
|
21952
22677
|
}
|
|
21953
|
-
|
|
22678
|
+
const tableH = tableRowHeights.reduce((sum, height) => sum + height, 0);
|
|
22679
|
+
return `<hp:tbl id="${tblId}" zOrder="0" numberingType="TABLE" 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"/><hp:pos treatAsChar="1" affectLSpacing="0" flowWithText="0" allowOverlap="0" holdAnchorAndSO="0" vertRelTo="PARA" horzRelTo="PARA" vertAlign="TOP" horzAlign="LEFT" vertOffset="0" horzOffset="0"/><hp:outMargin left="0" right="0" top="0" bottom="0"/><hp:inMargin left="510" right="510" top="141" bottom="141"/>` + trXmls.join("") + `</hp:tbl>`;
|
|
21954
22680
|
}
|
|
21955
22681
|
|
|
21956
22682
|
// src/hwpx/gen-gongmun-extra.ts
|
|
21957
|
-
var extraTableId =
|
|
22683
|
+
var extraTableId = EXTRA_TABLE_ID_BASE;
|
|
22684
|
+
function resetExtraTableIds() {
|
|
22685
|
+
extraTableId = EXTRA_TABLE_ID_BASE;
|
|
22686
|
+
}
|
|
21958
22687
|
function tbl(rows, w, h, cols) {
|
|
21959
22688
|
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>`;
|
|
21960
22689
|
}
|
|
21961
22690
|
function tc(opts) {
|
|
21962
|
-
return `<hp:tc name="" header="0" hasMargin="0" protect="0" editable="1" dirty="0" borderFillIDRef="${opts.bf}"><hp:subList id="" textDirection="HORIZONTAL" lineWrap="BREAK" vertAlign="CENTER" linkListIDRef="0" linkListNextIDRef="0" textWidth="0" textHeight="0" hasTextRef="0" hasNumRef="0">${opts.paras}</hp:subList><hp:cellAddr colAddr="${opts.col}" rowAddr="${opts.row}"/><hp:cellSpan colSpan="${opts.colSpan ?? 1}" rowSpan="1"/><hp:cellSz width="${opts.w}" height="${opts.h}"/><hp:cellMargin left="141" right="141" top="141" bottom="141"/></hp:tc>`;
|
|
22691
|
+
return `<hp:tc name="${opts.name ?? ""}" header="0" hasMargin="0" protect="0" editable="1" dirty="0" borderFillIDRef="${opts.bf}"><hp:subList id="" textDirection="HORIZONTAL" lineWrap="BREAK" vertAlign="CENTER" linkListIDRef="0" linkListNextIDRef="0" textWidth="0" textHeight="0" hasTextRef="0" hasNumRef="0">${opts.paras}</hp:subList><hp:cellAddr colAddr="${opts.col}" rowAddr="${opts.row}"/><hp:cellSpan colSpan="${opts.colSpan ?? 1}" rowSpan="1"/><hp:cellSz width="${opts.w}" height="${opts.h}"/><hp:cellMargin left="141" right="141" top="141" bottom="141"/></hp:tc>`;
|
|
21963
22692
|
}
|
|
21964
22693
|
function para(text, paraPrId, charPrId) {
|
|
21965
22694
|
return `<hp:p paraPrIDRef="${paraPrId}" styleIDRef="0"><hp:run charPrIDRef="${charPrId}"><hp:t>${escapeXml(text)}</hp:t></hp:run></hp:p>`;
|
|
21966
22695
|
}
|
|
21967
22696
|
var APPROVAL_COL_W = 7085;
|
|
21968
|
-
var APPROVAL_LABEL_H =
|
|
22697
|
+
var APPROVAL_LABEL_H = 1300;
|
|
21969
22698
|
var APPROVAL_SIGN_H = 4200;
|
|
21970
|
-
function buildApprovalTable(labels,
|
|
22699
|
+
function buildApprovalTable(labels, reg, approvalCharPr) {
|
|
21971
22700
|
const n = labels.length;
|
|
21972
22701
|
const w = APPROVAL_COL_W * n;
|
|
21973
22702
|
const bfAt = (row, col) => reg.get({
|
|
@@ -21977,31 +22706,31 @@ function buildApprovalTable(labels, gongmun, reg) {
|
|
|
21977
22706
|
r: col === n - 1 ? "thick" : "thin"
|
|
21978
22707
|
});
|
|
21979
22708
|
const labelRow = labels.map(
|
|
21980
|
-
(label, c) => tc({ bf: bfAt(0, c), row: 0, col: c, w: APPROVAL_COL_W, h: APPROVAL_LABEL_H, paras: para(label,
|
|
22709
|
+
(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) })
|
|
21981
22710
|
).join("");
|
|
21982
22711
|
const signRow = labels.map(
|
|
21983
|
-
(_, c) => tc({ bf: bfAt(1, c), row: 1, col: c, w: APPROVAL_COL_W, h: APPROVAL_SIGN_H, paras: para("",
|
|
22712
|
+
(_, c) => tc({ bf: bfAt(1, c), row: 1, col: c, w: APPROVAL_COL_W, h: APPROVAL_SIGN_H, paras: para("", GONGMUN_PARA_APPROVAL, approvalCharPr) })
|
|
21984
22713
|
).join("");
|
|
21985
22714
|
const table2 = tbl([labelRow, signRow], w, APPROVAL_LABEL_H + APPROVAL_SIGN_H, n);
|
|
21986
22715
|
return `<hp:p paraPrIDRef="${GONGMUN_RIGHT}" styleIDRef="0"><hp:run charPrIDRef="${CHAR_NORMAL}">${table2}</hp:run></hp:p>`;
|
|
21987
22716
|
}
|
|
21988
22717
|
function hasEndMark(lastText) {
|
|
21989
|
-
return
|
|
22718
|
+
return /(^|\s)끝\s*\.\s*$/.test(lastText.trim());
|
|
21990
22719
|
}
|
|
21991
22720
|
function buildEndMark() {
|
|
21992
22721
|
return para(" \uB05D.", PARA_NORMAL, CHAR_NORMAL);
|
|
21993
22722
|
}
|
|
21994
22723
|
var TITLE_BOX = { barH: 382, titleH: 2850, color: "#0080C0", gradient: ["#0080C0", "#3CBFFF"] };
|
|
21995
|
-
function buildTitleBox(title, titleCharPr, bodyWidth, reg) {
|
|
22724
|
+
function buildTitleBox(title, titleCharPr, barCharPr, bodyWidth, reg) {
|
|
21996
22725
|
const w = bodyWidth - 280;
|
|
21997
22726
|
const outer = { t: "thin", b: "thin", l: "thin", r: "thin" };
|
|
21998
22727
|
const barTop = reg.get({ ...outer, fill: TITLE_BOX.color });
|
|
21999
22728
|
const barBottom = reg.get({ ...outer, fill: { gradient: TITLE_BOX.gradient } });
|
|
22000
22729
|
const mid = reg.get(outer);
|
|
22001
|
-
const barPara =
|
|
22730
|
+
const barPara = para("", GJ_PARA_BAR, barCharPr);
|
|
22002
22731
|
const rows = [
|
|
22003
22732
|
tc({ bf: barTop, row: 0, col: 0, w, h: TITLE_BOX.barH, paras: barPara }),
|
|
22004
|
-
tc({ bf: mid, row: 1, col: 0, w, h: TITLE_BOX.titleH, paras: para(title, GONGMUN_TBL_CENTER, titleCharPr) }),
|
|
22733
|
+
tc({ bf: mid, row: 1, col: 0, w, h: TITLE_BOX.titleH, paras: para(title, GONGMUN_TBL_CENTER, titleCharPr), name: "__kordoc_h1" }),
|
|
22005
22734
|
tc({ bf: barBottom, row: 2, col: 0, w, h: TITLE_BOX.barH, paras: barPara })
|
|
22006
22735
|
];
|
|
22007
22736
|
const table2 = tbl(rows, w, TITLE_BOX.barH * 2 + TITLE_BOX.titleH, 1);
|
|
@@ -22015,8 +22744,8 @@ function docframeIds(base) {
|
|
|
22015
22744
|
function docframeActive(g) {
|
|
22016
22745
|
return !!(g.docHead || g.docFoot || g.reportInfo || g.noticeHead || g.press);
|
|
22017
22746
|
}
|
|
22018
|
-
function docframeCharPrXmls(base,
|
|
22019
|
-
const body =
|
|
22747
|
+
function docframeCharPrXmls(base, richAssets) {
|
|
22748
|
+
const body = richAssets ? 4 : 0;
|
|
22020
22749
|
const ids = docframeIds(base);
|
|
22021
22750
|
return [
|
|
22022
22751
|
charPr(ids.org, 1800, true, false, body),
|
|
@@ -22048,7 +22777,10 @@ function buildDocHead(g, ids) {
|
|
|
22048
22777
|
out.push(blank());
|
|
22049
22778
|
return out;
|
|
22050
22779
|
}
|
|
22051
|
-
|
|
22780
|
+
function footSep(g) {
|
|
22781
|
+
const bodyMm = 210 - g.margins.left - g.margins.right;
|
|
22782
|
+
return "\u2500".repeat(Math.max(10, Math.round(bodyMm * (46 / 175))));
|
|
22783
|
+
}
|
|
22052
22784
|
function buildDocFoot(g, ids) {
|
|
22053
22785
|
const f = g.docFoot;
|
|
22054
22786
|
const out = [blank()];
|
|
@@ -22056,7 +22788,7 @@ function buildDocFoot(g, ids) {
|
|
|
22056
22788
|
out.push(para(f.sender, GONGMUN_CENTER, ids.sender), blank());
|
|
22057
22789
|
}
|
|
22058
22790
|
const foot = (text) => para(text, GONGMUN_TBL_LEFT, ids.foot);
|
|
22059
|
-
out.push(foot(
|
|
22791
|
+
out.push(foot(footSep(g)));
|
|
22060
22792
|
if (f.drafter || f.reviewer || f.approver)
|
|
22061
22793
|
out.push(foot(`\uAE30\uC548\uC790 ${f.drafter ?? ""} \uAC80\uD1A0\uC790 ${f.reviewer ?? ""} \uACB0\uC7AC\uAD8C\uC790 ${f.approver ?? ""}`));
|
|
22062
22794
|
if (f.cooperator) out.push(foot(`\uD611\uC870\uC790 ${f.cooperator}`));
|
|
@@ -22205,6 +22937,54 @@ var COMMAND_MAP = {
|
|
|
22205
22937
|
vdots: "VDOTS",
|
|
22206
22938
|
ddots: "DDOTS"
|
|
22207
22939
|
};
|
|
22940
|
+
var EQEDIT_FUNCTIONS = /* @__PURE__ */ new Set([
|
|
22941
|
+
"sin",
|
|
22942
|
+
"cos",
|
|
22943
|
+
"tan",
|
|
22944
|
+
"cot",
|
|
22945
|
+
"sec",
|
|
22946
|
+
"csc",
|
|
22947
|
+
"arcsin",
|
|
22948
|
+
"arccos",
|
|
22949
|
+
"arctan",
|
|
22950
|
+
"sinh",
|
|
22951
|
+
"cosh",
|
|
22952
|
+
"tanh",
|
|
22953
|
+
"coth",
|
|
22954
|
+
"log",
|
|
22955
|
+
"ln",
|
|
22956
|
+
"exp",
|
|
22957
|
+
"det",
|
|
22958
|
+
"gcd",
|
|
22959
|
+
"mod",
|
|
22960
|
+
"max",
|
|
22961
|
+
"min",
|
|
22962
|
+
"arg",
|
|
22963
|
+
"deg",
|
|
22964
|
+
"hom",
|
|
22965
|
+
"ker",
|
|
22966
|
+
"Pr"
|
|
22967
|
+
]);
|
|
22968
|
+
{
|
|
22969
|
+
const reverse = /* @__PURE__ */ new Map();
|
|
22970
|
+
const prefer = (name, a, b) => {
|
|
22971
|
+
if (a === name) return a;
|
|
22972
|
+
if (b === name) return b;
|
|
22973
|
+
const aAlpha = /^[A-Za-z]+$/.test(a);
|
|
22974
|
+
const bAlpha = /^[A-Za-z]+$/.test(b);
|
|
22975
|
+
if (aAlpha !== bAlpha) return aAlpha ? a : b;
|
|
22976
|
+
return a < b ? a : b;
|
|
22977
|
+
};
|
|
22978
|
+
for (const [token, latex] of Object.entries(CONVERT_MAP)) {
|
|
22979
|
+
const m = /^\\([A-Za-z]+)$/.exec(latex);
|
|
22980
|
+
if (!m) continue;
|
|
22981
|
+
const name = m[1];
|
|
22982
|
+
if (name in COMMAND_MAP) continue;
|
|
22983
|
+
const prev = reverse.get(name);
|
|
22984
|
+
reverse.set(name, prev === void 0 ? token : prefer(name, token, prev));
|
|
22985
|
+
}
|
|
22986
|
+
for (const [name, token] of reverse) COMMAND_MAP[name] = token;
|
|
22987
|
+
}
|
|
22208
22988
|
var ACCENT_COMMANDS = {
|
|
22209
22989
|
bar: "bar",
|
|
22210
22990
|
overline: "bar",
|
|
@@ -22298,8 +23078,20 @@ function readCommand(input, idx, depth) {
|
|
|
22298
23078
|
const endIdx = input.indexOf(endTag, env.next);
|
|
22299
23079
|
if (endIdx === -1) return { value: env.value, next: env.next };
|
|
22300
23080
|
const body = convertLatexFragment(input.slice(env.next, endIdx), depth + 1);
|
|
22301
|
-
|
|
22302
|
-
|
|
23081
|
+
const ENV_TOKEN = {
|
|
23082
|
+
matrix: "matrix",
|
|
23083
|
+
pmatrix: "pmatrix",
|
|
23084
|
+
bmatrix: "bmatrix",
|
|
23085
|
+
vmatrix: "dmatrix",
|
|
23086
|
+
cases: "cases",
|
|
23087
|
+
align: "eqalign",
|
|
23088
|
+
"align*": "eqalign",
|
|
23089
|
+
aligned: "eqalign"
|
|
23090
|
+
};
|
|
23091
|
+
const tok = ENV_TOKEN[env.value];
|
|
23092
|
+
if (tok) return { value: `{${tok}{${body}}}`, next: endIdx + endTag.length };
|
|
23093
|
+
if (env.value === "Bmatrix") {
|
|
23094
|
+
return { value: `LEFT { {matrix{${body}}} RIGHT }`, next: endIdx + endTag.length };
|
|
22303
23095
|
}
|
|
22304
23096
|
return { value: body, next: endIdx + endTag.length };
|
|
22305
23097
|
}
|
|
@@ -22331,7 +23123,11 @@ function readCommand(input, idx, depth) {
|
|
|
22331
23123
|
const tok = readGroupOrToken(input, start, depth);
|
|
22332
23124
|
return { value: `"${tok.value}"`, next: tok.next };
|
|
22333
23125
|
}
|
|
22334
|
-
|
|
23126
|
+
const mapped = COMMAND_MAP[command];
|
|
23127
|
+
if (mapped !== void 0) return { value: mapped, next: name.next };
|
|
23128
|
+
if (EQEDIT_FUNCTIONS.has(command)) return { value: command, next: name.next };
|
|
23129
|
+
if (/^[A-Za-z]{2,}$/.test(command)) return { value: `"${command}"`, next: name.next };
|
|
23130
|
+
return { value: command, next: name.next };
|
|
22335
23131
|
}
|
|
22336
23132
|
function convertLatexFragment(input, depth) {
|
|
22337
23133
|
if (depth > MAX_GROUP_DEPTH) return normalizeEqEdit(input);
|
|
@@ -22411,48 +23207,52 @@ function generateSecPr(gongmun) {
|
|
|
22411
23207
|
m.header = gongmun.headerFooter;
|
|
22412
23208
|
m.footer = gongmun.headerFooter;
|
|
22413
23209
|
}
|
|
22414
|
-
const secPr = `<hp:secPr textDirection="HORIZONTAL" spaceColumns="1134" tabStop="8000" outlineShapeIDRef="1" memoShapeIDRef="0" textVerticalWidthHead="0" masterPageCnt="0"><hp:grid lineGrid="0" charGrid="0" wonggojiFormat="0"/><hp:startNum pageStartsOn="BOTH" page="0" pic="0" tbl="0" equation="0"/><hp:visibility hideFirstHeader="0" hideFirstFooter="0" hideFirstMasterPage="0" border="SHOW_ALL" fill="SHOW_ALL" hideFirstPageNum="0" hideFirstEmptyLine="0" showLineNumber="0"/><hp:pagePr landscape="WIDELY" width="
|
|
23210
|
+
const secPr = `<hp:secPr textDirection="HORIZONTAL" spaceColumns="1134" tabStop="8000" outlineShapeIDRef="1" memoShapeIDRef="0" textVerticalWidthHead="0" masterPageCnt="0"><hp:grid lineGrid="0" charGrid="0" wonggojiFormat="0"/><hp:startNum pageStartsOn="BOTH" page="0" pic="0" tbl="0" equation="0"/><hp:visibility hideFirstHeader="0" hideFirstFooter="0" hideFirstMasterPage="0" border="SHOW_ALL" fill="SHOW_ALL" hideFirstPageNum="0" hideFirstEmptyLine="0" showLineNumber="0"/><hp:pagePr landscape="WIDELY" width="${A4_W_HU}" height="${A4_H_HU}" gutterType="LEFT_ONLY"><hp:margin header="${m.header}" footer="${m.footer}" gutter="0" left="${m.left}" right="${m.right}" top="${m.top}" bottom="${m.bottom}"/></hp:pagePr><hp:footNotePr><hp:autoNumFormat type="DIGIT" userChar="" prefixChar="" suffixChar=")" supscript="0"/><hp:noteLine length="-1" type="SOLID" width="0.12 mm" color="#000000"/><hp:noteSpacing betweenNotes="283" belowLine="567" aboveLine="850"/><hp:numbering type="CONTINUOUS" newNum="1"/><hp:placement place="EACH_COLUMN" beneathText="0"/></hp:footNotePr><hp:endNotePr><hp:autoNumFormat type="DIGIT" userChar="" prefixChar="" suffixChar=")" supscript="0"/><hp:noteLine length="14692344" type="SOLID" width="0.12 mm" color="#000000"/><hp:noteSpacing betweenNotes="0" belowLine="567" aboveLine="850"/><hp:numbering type="CONTINUOUS" newNum="1"/><hp:placement place="END_OF_DOCUMENT" beneathText="0"/></hp:endNotePr></hp:secPr>`;
|
|
22415
23211
|
const colPr = `<hp:ctrl><hp:colPr id="" type="NEWSPAPER" layout="LEFT" colCount="1" sameSz="1" sameGap="0"/></hp:ctrl>`;
|
|
22416
23212
|
return secPr + colPr + (gongmun?.pageNumbers ? pageNumCtrl() : "");
|
|
22417
23213
|
}
|
|
22418
|
-
|
|
22419
|
-
|
|
22420
|
-
|
|
22421
|
-
|
|
22422
|
-
|
|
22423
|
-
|
|
22424
|
-
|
|
22425
|
-
|
|
22426
|
-
|
|
22427
|
-
|
|
22428
|
-
|
|
22429
|
-
|
|
22430
|
-
|
|
22431
|
-
|
|
22432
|
-
|
|
22433
|
-
|
|
22434
|
-
|
|
22435
|
-
|
|
22436
|
-
|
|
22437
|
-
|
|
22438
|
-
|
|
22439
|
-
|
|
22440
|
-
|
|
22441
|
-
|
|
22442
|
-
|
|
22443
|
-
|
|
22444
|
-
|
|
22445
|
-
|
|
22446
|
-
|
|
22447
|
-
|
|
22448
|
-
|
|
22449
|
-
|
|
23214
|
+
var SectionOpener = class {
|
|
23215
|
+
constructor(gongmun) {
|
|
23216
|
+
this.gongmun = gongmun;
|
|
23217
|
+
}
|
|
23218
|
+
pending = true;
|
|
23219
|
+
/** 아직 secPr를 나를 첫 문단이 안 나왔는가 */
|
|
23220
|
+
get isFirst() {
|
|
23221
|
+
return this.pending;
|
|
23222
|
+
}
|
|
23223
|
+
/** 일반 문단 XML의 첫 run에 secPr 주입 (소비) */
|
|
23224
|
+
inject(xml) {
|
|
23225
|
+
this.pending = false;
|
|
23226
|
+
return xml.replace(
|
|
23227
|
+
/<hp:run charPrIDRef="(\d+)">/,
|
|
23228
|
+
`<hp:run charPrIDRef="$1">${generateSecPr(this.gongmun)}`
|
|
23229
|
+
);
|
|
23230
|
+
}
|
|
23231
|
+
/**
|
|
23232
|
+
* 표·차트·수식처럼 첫 run에 직접 못 싣는 블록 앞에 secPr 전용 빈 문단을 방출 (소비).
|
|
23233
|
+
* 이미 소비됐으면 아무것도 안 한다.
|
|
23234
|
+
*/
|
|
23235
|
+
emitCarrier(paraXmls) {
|
|
23236
|
+
if (!this.pending) return;
|
|
23237
|
+
this.pending = false;
|
|
23238
|
+
const secRun = `<hp:run charPrIDRef="0">${generateSecPr(this.gongmun)}<hp:t></hp:t></hp:run>`;
|
|
23239
|
+
paraXmls.push(`<hp:p paraPrIDRef="0" styleIDRef="0">${secRun}</hp:p>`);
|
|
23240
|
+
}
|
|
23241
|
+
/** 빈 문서 폴백 — secPr를 실은 빈 단락 XML */
|
|
23242
|
+
emptyDoc() {
|
|
23243
|
+
this.pending = false;
|
|
23244
|
+
return `<hp:p paraPrIDRef="0" styleIDRef="0"><hp:run charPrIDRef="0">${generateSecPr(this.gongmun)}<hp:t></hp:t></hp:run></hp:p>`;
|
|
23245
|
+
}
|
|
23246
|
+
};
|
|
23247
|
+
function buildPreamble(blocks, ctx) {
|
|
23248
|
+
const { gongmun, dfIds, bfReg, tableStyle, richAssets, gjBodyW } = ctx;
|
|
22450
23249
|
const preamble = [];
|
|
23250
|
+
let hasFrontPages = false;
|
|
22451
23251
|
if (gongmun?.reportInfo && dfIds) {
|
|
22452
23252
|
preamble.push(buildReportInfo(gongmun, dfIds));
|
|
22453
23253
|
}
|
|
22454
23254
|
if (gongmun?.approval && bfReg) {
|
|
22455
|
-
preamble.push(buildApprovalTable(gongmun.approval,
|
|
23255
|
+
preamble.push(buildApprovalTable(gongmun.approval, bfReg, richAssets ? GJ_CHAR_APPROVAL : GONGMUN_APPROVAL_CHAR));
|
|
22456
23256
|
}
|
|
22457
23257
|
if (gongmun?.docHead && dfIds) {
|
|
22458
23258
|
preamble.push(...buildDocHead(gongmun, dfIds));
|
|
@@ -22463,273 +23263,342 @@ function blocksToSectionXml(blocks, theme, gongmun, gongmunList = gongmun ? prec
|
|
|
22463
23263
|
if (gongmun?.press && dfIds && bfReg && tableStyle) {
|
|
22464
23264
|
preamble.push(...buildPressHead(gongmun, dfIds, tableStyle.totalWidth, bfReg));
|
|
22465
23265
|
}
|
|
22466
|
-
if (
|
|
23266
|
+
if (gongmun && (gongmun.cover || gongmun.toc)) {
|
|
22467
23267
|
const h1Idx = blocks.findIndex((b) => b.type === "heading" && (b.level ?? 1) === 1);
|
|
22468
|
-
const chapters = blocks.filter((b) => b.type === "heading" && b.level === 2).map((b) => stripChapterNumber(b.text || ""));
|
|
22469
23268
|
let coverTitle = "";
|
|
22470
23269
|
const hide = (xml, hideHeader) => gongmun.pageNumbers ? xml.replace(/<hp:run charPrIDRef="(\d+)">/, `<hp:run charPrIDRef="$1">${pageHidingCtrl(hideHeader)}`) : xml;
|
|
22471
23270
|
if (gongmun.cover && h1Idx >= 0) {
|
|
22472
23271
|
coverTitle = (blocks[h1Idx].text || "").trim();
|
|
22473
|
-
const coverPart = buildGaejosikCover(coverTitle, gongmun);
|
|
23272
|
+
const coverPart = buildGaejosikCover(coverTitle, gongmun, gjBodyW);
|
|
22474
23273
|
coverPart[0] = hide(coverPart[0], true);
|
|
22475
23274
|
preamble.push(...coverPart);
|
|
22476
|
-
coverH1Idx = h1Idx;
|
|
23275
|
+
ctx.coverH1Idx = h1Idx;
|
|
22477
23276
|
hasFrontPages = true;
|
|
22478
23277
|
}
|
|
22479
|
-
|
|
22480
|
-
|
|
23278
|
+
ctx.gjChapterIdxs = blocks.map((b, i) => b.type === "heading" && (b.level ?? 1) <= 2 && i !== ctx.coverH1Idx ? i : -1).filter((i) => i >= 0);
|
|
23279
|
+
if (gongmun.toc && ctx.gjChapterIdxs.length > 0) {
|
|
23280
|
+
const chapters = ctx.gjChapterIdxs.map((i) => stripChapterNumber(blocks[i].text || ""));
|
|
23281
|
+
const tocPart = buildGaejosikToc(chapters, gongmun, gjBodyW);
|
|
22481
23282
|
tocPart[0] = hide(tocPart[0], false);
|
|
22482
23283
|
preamble.push(...tocPart);
|
|
22483
23284
|
hasFrontPages = true;
|
|
22484
23285
|
}
|
|
22485
23286
|
if (gongmun.bodyTitleBox && coverTitle && hasFrontPages) {
|
|
22486
|
-
preamble.push(buildGaejosikBodyTitle(coverTitle, gongmun).replace(/^<hp:p /, `<hp:p pageBreak="1" `));
|
|
23287
|
+
preamble.push(buildGaejosikBodyTitle(coverTitle, gongmun, gjBodyW).replace(/^<hp:p /, `<hp:p pageBreak="1" `));
|
|
22487
23288
|
}
|
|
22488
|
-
}
|
|
22489
|
-
|
|
22490
|
-
|
|
22491
|
-
|
|
23289
|
+
}
|
|
23290
|
+
if (gongmun && !ctx.gaejosik && ctx.coverH1Idx < 0 && bfReg && (gongmun.preset === "report" || gongmun.preset === "plan" || gongmun.preset === "notice")) {
|
|
23291
|
+
ctx.titleBoxH1Idx = blocks.findIndex((b) => b.type === "heading" && (b.level ?? 1) === 1);
|
|
23292
|
+
} else if (gongmun?.press && ctx.coverH1Idx < 0 && dfIds) {
|
|
23293
|
+
ctx.pressH1Idx = blocks.findIndex((b) => b.type === "heading" && (b.level ?? 1) === 1);
|
|
22492
23294
|
}
|
|
22493
23295
|
if (preamble.length > 0) {
|
|
22494
|
-
preamble[0] = preamble[0].replace(` pageBreak="1"`, "")
|
|
22495
|
-
|
|
22496
|
-
|
|
22497
|
-
|
|
22498
|
-
|
|
23296
|
+
preamble[0] = ctx.opener.inject(preamble[0].replace(` pageBreak="1"`, ""));
|
|
23297
|
+
ctx.pendingPageBreak = hasFrontPages && !preamble[preamble.length - 1].includes(`pageBreak="1"`);
|
|
23298
|
+
ctx.pendingNewNum = !!gongmun.pageNumbers && hasFrontPages;
|
|
23299
|
+
ctx.paraXmls.push(...preamble);
|
|
23300
|
+
}
|
|
23301
|
+
}
|
|
23302
|
+
function renderHeading(block, blockIdx, ctx) {
|
|
23303
|
+
const { gongmun, dfIds, tableStyle, bfReg, measured, richAssets, gjBodyW } = ctx;
|
|
23304
|
+
if (gongmun && blockIdx === ctx.coverH1Idx) return "";
|
|
23305
|
+
if (gongmun && blockIdx === ctx.pressH1Idx && dfIds) {
|
|
23306
|
+
const parts = [generateParagraph((block.text || "").trim(), GONGMUN_CENTER, dfIds.pressTitle)];
|
|
23307
|
+
for (const s of gongmun.press?.sub ?? []) {
|
|
23308
|
+
parts.push(generateParagraph(`- ${s} -`, GONGMUN_CENTER, dfIds.pressSub));
|
|
23309
|
+
}
|
|
23310
|
+
return parts.join("\n ");
|
|
23311
|
+
}
|
|
23312
|
+
if (gongmun && blockIdx === ctx.titleBoxH1Idx && tableStyle && bfReg) {
|
|
23313
|
+
return buildTitleBox(
|
|
23314
|
+
(block.text || "").trim(),
|
|
23315
|
+
measured ? GJ_CHAR_BODY_TITLE : CHAR_H1,
|
|
23316
|
+
richAssets ? GJ_CHAR_TITLE_BAR : GONGMUN_TITLE_BAR_CHAR,
|
|
23317
|
+
tableStyle.totalWidth,
|
|
23318
|
+
bfReg
|
|
23319
|
+
);
|
|
23320
|
+
}
|
|
23321
|
+
if (ctx.gaejosik) {
|
|
23322
|
+
const lvl = block.level || 1;
|
|
23323
|
+
if (lvl <= 2) {
|
|
23324
|
+
const no = ctx.gjChapterIdxs.indexOf(blockIdx);
|
|
23325
|
+
ctx.chapterNo = no >= 0 ? no + 1 : ctx.chapterNo + 1;
|
|
23326
|
+
return buildGaejosikChapter(ctx.chapterNo, stripChapterNumber(block.text || ""), gongmun, gjBodyW, lvl);
|
|
23327
|
+
} else if (lvl === 3) {
|
|
23328
|
+
return generateParagraph(
|
|
23329
|
+
`\u25A1 ${block.text || ""}`,
|
|
23330
|
+
GONGMUN_LIST_BASE,
|
|
23331
|
+
GJ_CHAR_DAE,
|
|
23332
|
+
(id) => id === CHAR_BOLD ? GJ_CHAR_DAE_BOLD : id
|
|
23333
|
+
);
|
|
23334
|
+
}
|
|
23335
|
+
return generateParagraph(`${gongmun.bullet2} ${block.text || ""}`, GONGMUN_LIST_BASE + 1, CHAR_NORMAL);
|
|
23336
|
+
}
|
|
23337
|
+
const pId = headingParaPrId(block.level || 1);
|
|
23338
|
+
const cId = headingCharPrId(block.level || 1);
|
|
23339
|
+
const styleId = gongmun ? Math.min(block.level || 1, 4) : 0;
|
|
23340
|
+
let hText = block.text || "";
|
|
23341
|
+
if (gongmun && (block.level || 1) === 2 && gongmun.h2Marker !== "none") {
|
|
23342
|
+
const title = stripChapterNumber(hText);
|
|
23343
|
+
hText = gongmun.h2Marker === "box" ? `\u25A1 ${title}` : `${++ctx.h2Seq}. ${title}`;
|
|
23344
|
+
}
|
|
23345
|
+
return generateParagraph(hText, pId, cId, void 0, styleId);
|
|
23346
|
+
}
|
|
23347
|
+
function renderParagraph(block, blockIdx, ctx) {
|
|
23348
|
+
const { gongmun, measured, fit, vBase } = ctx;
|
|
23349
|
+
if (ctx.images) {
|
|
23350
|
+
const { text: rest, urls } = splitImageRefs(block.text || "");
|
|
23351
|
+
if (urls.length > 0 && !rest.trim()) {
|
|
23352
|
+
const pics = urls.map((u) => {
|
|
23353
|
+
const part = ctx.images.take(u);
|
|
23354
|
+
return part ? ctx.images.inlinePicXml(part) : null;
|
|
23355
|
+
});
|
|
23356
|
+
if (pics.every(Boolean)) {
|
|
23357
|
+
return `<hp:p paraPrIDRef="${PARA_NORMAL}" styleIDRef="0"><hp:run charPrIDRef="${CHAR_NORMAL}">${pics.join("")}</hp:run></hp:p>`;
|
|
23358
|
+
}
|
|
23359
|
+
}
|
|
23360
|
+
}
|
|
23361
|
+
const pTrim = (block.text || "").trimStart();
|
|
23362
|
+
if (measured && (pTrim.startsWith("\u203B") || /^\*\s/.test(pTrim))) {
|
|
23363
|
+
return generateParagraph((block.text || "").trim(), GJ_PARA_CHAM, GJ_CHAR_CHAM, ctx.chamMap);
|
|
23364
|
+
}
|
|
23365
|
+
const ctr = gongmun && /^<center>([\s\S]*)<\/center>$/i.exec((block.text || "").trim());
|
|
23366
|
+
const rgt = gongmun && /^<right>([\s\S]*)<\/right>$/i.exec((block.text || "").trim());
|
|
23367
|
+
if (ctr) return generateParagraph(ctr[1].trim(), GONGMUN_CENTER);
|
|
23368
|
+
if (rgt) return generateParagraph(rgt[1].trim(), GONGMUN_RIGHT);
|
|
23369
|
+
return generateParagraph(block.text || "", PARA_NORMAL, CHAR_NORMAL, fit ? variantMapper(fit, blockIdx, vBase) : void 0);
|
|
23370
|
+
}
|
|
23371
|
+
function renderCodeBlock(block, blockIdx, ctx) {
|
|
23372
|
+
if (ctx.chartParts !== null && (block.lang || "").toLowerCase() === "chart") {
|
|
23373
|
+
const fence = parseChartFence(block.text || "");
|
|
23374
|
+
if (fence) {
|
|
23375
|
+
const partName = `Chart/chart${ctx.chartParts.length + 1}.xml`;
|
|
23376
|
+
ctx.chartParts.push({ name: partName, xml: buildChartSpaceXml(fence) });
|
|
23377
|
+
const chartEl = buildChartElementXml(partName, fence.widthHu, fence.heightHu, CHART_TABLE_ID_BASE + blockIdx);
|
|
23378
|
+
ctx.opener.emitCarrier(ctx.paraXmls);
|
|
23379
|
+
return `<hp:p paraPrIDRef="${PARA_NORMAL}" styleIDRef="0"><hp:run charPrIDRef="${CHAR_NORMAL}">${chartEl}</hp:run></hp:p>`;
|
|
23380
|
+
}
|
|
23381
|
+
}
|
|
23382
|
+
const codeLines = (block.text || "").split("\n");
|
|
23383
|
+
return codeLines.map((line) => generateParagraph(line || " ", PARA_CODE)).join("\n ");
|
|
23384
|
+
}
|
|
23385
|
+
function renderEquation(block, blockIdx, ctx) {
|
|
23386
|
+
ctx.opener.emitCarrier(ctx.paraXmls);
|
|
23387
|
+
return generateEquationParagraph(block.text || "", blockIdx);
|
|
23388
|
+
}
|
|
23389
|
+
function renderBlockquote(block, ctx) {
|
|
23390
|
+
if (ctx.measured) {
|
|
23391
|
+
const t = (block.text || "").replace(/\n+/g, " ").trim();
|
|
23392
|
+
return t ? generateParagraph(t.startsWith("\u203B") ? t : `\u203B ${t}`, GJ_PARA_CHAM, GJ_CHAR_CHAM, ctx.chamMap) : "";
|
|
23393
|
+
}
|
|
23394
|
+
const quoteChar = ctx.theme.hasQuoteOption ? CHAR_QUOTE : CHAR_NORMAL;
|
|
23395
|
+
return (block.text || "").split("\n").map((line) => generateParagraph(line, PARA_QUOTE, quoteChar)).join("\n ");
|
|
23396
|
+
}
|
|
23397
|
+
function renderListItem(block, blockIdx, ctx) {
|
|
23398
|
+
const { gongmun, gongmunList, measured, fit, vBase, dfIds } = ctx;
|
|
23399
|
+
if (gongmun && gongmunList) {
|
|
23400
|
+
const info = gongmunList.items.get(blockIdx);
|
|
23401
|
+
const depth = info?.depth ?? 0;
|
|
23402
|
+
const marker2 = info?.marker ?? "";
|
|
23403
|
+
const content = block.text || "";
|
|
23404
|
+
if (measured && content.trimStart().startsWith("\u203B")) {
|
|
23405
|
+
return generateParagraph(content.trim(), GJ_PARA_CHAM, GJ_CHAR_CHAM, ctx.chamMap);
|
|
23406
|
+
}
|
|
23407
|
+
if (measured && block.marker === "*") {
|
|
23408
|
+
return generateParagraph(`* ${content.trim()}`, GJ_PARA_CHAM, GJ_CHAR_CHAM, ctx.chamMap);
|
|
23409
|
+
}
|
|
23410
|
+
const text = marker2 ? `${marker2} ${content}` : content;
|
|
23411
|
+
const listParaPr = marker2 ? info?.indentVariant !== void 0 ? GONGMUN_LIST_VARIANT_BASE + info.indentVariant : GONGMUN_LIST_BASE + depth : GONGMUN_LIST_PLAIN_BASE + depth;
|
|
23412
|
+
let listCharPr = gongmun.numbering === "report" && depth === 0 ? CHAR_BOLD : CHAR_NORMAL;
|
|
23413
|
+
let mapId = fit ? variantMapper(fit, blockIdx, vBase) : void 0;
|
|
23414
|
+
if (measured && depth === 0 && gongmun.numbering !== "standard") {
|
|
23415
|
+
listCharPr = GJ_CHAR_DAE;
|
|
23416
|
+
mapId = (id) => id === CHAR_BOLD ? GJ_CHAR_DAE_BOLD : id;
|
|
23417
|
+
}
|
|
23418
|
+
if (gongmun.preset === "press") {
|
|
23419
|
+
if (depth === 0) listCharPr = CHAR_NORMAL;
|
|
23420
|
+
if (depth >= 2 && dfIds) {
|
|
23421
|
+
listCharPr = dfIds.small;
|
|
23422
|
+
mapId = void 0;
|
|
23423
|
+
}
|
|
23424
|
+
}
|
|
23425
|
+
return generateParagraph(text, listParaPr, listCharPr, mapId);
|
|
23426
|
+
}
|
|
23427
|
+
const indent = block.indent || 0;
|
|
23428
|
+
let marker;
|
|
23429
|
+
if (block.marker) {
|
|
23430
|
+
marker = `${block.marker} `;
|
|
23431
|
+
ctx.prevWasOrdered = !!block.ordered;
|
|
23432
|
+
} else if (block.ordered) {
|
|
23433
|
+
ctx.orderedCounters[indent] = (ctx.orderedCounters[indent] || 0) + 1;
|
|
23434
|
+
for (const k of Object.keys(ctx.orderedCounters)) {
|
|
23435
|
+
if (+k > indent) delete ctx.orderedCounters[+k];
|
|
23436
|
+
}
|
|
23437
|
+
marker = `${ctx.orderedCounters[indent]}. `;
|
|
23438
|
+
ctx.prevWasOrdered = true;
|
|
23439
|
+
} else {
|
|
23440
|
+
marker = "\xB7 ";
|
|
23441
|
+
if (ctx.prevWasOrdered) {
|
|
23442
|
+
for (const k of Object.keys(ctx.orderedCounters)) delete ctx.orderedCounters[+k];
|
|
23443
|
+
}
|
|
23444
|
+
ctx.prevWasOrdered = false;
|
|
23445
|
+
}
|
|
23446
|
+
const indentPrefix = " ".repeat(indent);
|
|
23447
|
+
return generateParagraph(indentPrefix + marker + (block.text || ""), PARA_LIST);
|
|
23448
|
+
}
|
|
23449
|
+
function renderHr(ctx) {
|
|
23450
|
+
return ctx.gongmun ? `<hp:p paraPrIDRef="${PARA_NORMAL}" styleIDRef="0"><hp:run charPrIDRef="${CHAR_NORMAL}"><hp:t></hp:t></hp:run></hp:p>` : `<hp:p paraPrIDRef="0" styleIDRef="0"><hp:run charPrIDRef="0"><hp:t>\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500</hp:t></hp:run></hp:p>`;
|
|
23451
|
+
}
|
|
23452
|
+
function renderTable(block, ctx) {
|
|
23453
|
+
if (!block.rows) return "";
|
|
23454
|
+
ctx.opener.emitCarrier(ctx.paraXmls);
|
|
23455
|
+
return generateTable(block.rows, ctx.theme, ctx.tableStyle, ctx.remap, ctx.tableSeq++, ctx.images);
|
|
23456
|
+
}
|
|
23457
|
+
function renderHtmlTable(block, ctx) {
|
|
23458
|
+
const { tableStyle, bfReg } = ctx;
|
|
23459
|
+
const htmlW = tableStyle ? bfReg ? tableStyle.totalWidth - DATA_TABLE_INSET : tableStyle.totalWidth : 44e3;
|
|
23460
|
+
const tbl2 = generateHtmlTableXml(block.text || "", ctx.theme, htmlW, tableStyle, ctx.remap, ctx.tableSeq++, ctx.images);
|
|
23461
|
+
if (tbl2) {
|
|
23462
|
+
ctx.opener.emitCarrier(ctx.paraXmls);
|
|
23463
|
+
return `<hp:p paraPrIDRef="${tableStyle && bfReg ? GONGMUN_RIGHT : 0}" styleIDRef="0"><hp:run charPrIDRef="0">${tbl2}</hp:run></hp:p>`;
|
|
23464
|
+
}
|
|
23465
|
+
const plain = (block.text || "").replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
|
|
23466
|
+
return plain ? generateParagraph(plain) : "";
|
|
23467
|
+
}
|
|
23468
|
+
function appendPostamble(blocks, ctx) {
|
|
23469
|
+
const { gongmun, dfIds, bfReg, tableStyle, paraXmls } = ctx;
|
|
23470
|
+
if (gongmun?.noticeHead && dfIds) {
|
|
23471
|
+
paraXmls.push(...buildNoticeFoot(gongmun));
|
|
23472
|
+
}
|
|
23473
|
+
if (gongmun?.endMark && paraXmls.length > 0) {
|
|
23474
|
+
const lastBlock = [...blocks].reverse().find((b) => (b.text || "").trim() || b.type === "table" && b.rows?.length);
|
|
23475
|
+
const lastText = lastBlock && (lastBlock.type === "paragraph" || lastBlock.type === "list_item") ? lastBlock.text || "" : "";
|
|
23476
|
+
if (!hasEndMark(lastText)) paraXmls.push(buildEndMark());
|
|
23477
|
+
}
|
|
23478
|
+
if (gongmun?.docFoot && dfIds) {
|
|
23479
|
+
paraXmls.push(...buildDocFoot(gongmun, dfIds));
|
|
23480
|
+
}
|
|
23481
|
+
if (gongmun?.press && dfIds && bfReg && tableStyle) {
|
|
23482
|
+
paraXmls.push(...buildPressContact(gongmun, dfIds, tableStyle.totalWidth, bfReg));
|
|
22499
23483
|
}
|
|
23484
|
+
}
|
|
23485
|
+
function blocksToSectionXml(blocks, theme, gongmun, gongmunList = gongmun ? precomputeGongmunList(blocks, gongmun) : null, fit = null, chartParts = null, bfReg = null, remap = null, dfIds = null, images = null) {
|
|
23486
|
+
resetTableIds();
|
|
23487
|
+
resetGjTableIds();
|
|
23488
|
+
resetExtraTableIds();
|
|
23489
|
+
const measured = !!gongmun && usesReportFonts(gongmun.preset);
|
|
23490
|
+
const richAssets = !!gongmun && needsGaejosikAssets(gongmun);
|
|
23491
|
+
const ctx = {
|
|
23492
|
+
theme,
|
|
23493
|
+
gongmun,
|
|
23494
|
+
gongmunList,
|
|
23495
|
+
fit,
|
|
23496
|
+
chartParts,
|
|
23497
|
+
bfReg,
|
|
23498
|
+
remap,
|
|
23499
|
+
dfIds,
|
|
23500
|
+
images,
|
|
23501
|
+
gaejosik: gongmun?.preset === "gaejosik",
|
|
23502
|
+
measured,
|
|
23503
|
+
richAssets,
|
|
23504
|
+
vBase: charVariantBase(richAssets, !!gongmun),
|
|
23505
|
+
// 공문서 표 스타일 — 본문 폭 맞춤 + 실측 정부 양식(헤더 음영·실측 프리셋 맑은 고딕 12pt)
|
|
23506
|
+
// bfReg가 있으면 실측 테두리 위계(외곽 0.4/내부 0.12/헤더 이중선)·셀 문단·우측 배치 적용
|
|
23507
|
+
tableStyle: gongmun ? {
|
|
23508
|
+
totalWidth: mmToHwpunit(210 - gongmun.margins.left - gongmun.margins.right),
|
|
23509
|
+
// 표 셀 크기 — 실측: 실측 프리셋 맑은 고딕 12pt(22·23), 비실측도 12pt(11·12,
|
|
23510
|
+
// 실결재 지배값 — 본문 크기 셀은 열폭 부족으로 서술 열 세로 신장, v4.0.2 QA)
|
|
23511
|
+
charPr: richAssets ? GJ_CHAR_TABLE : GONGMUN_TBL_CHAR,
|
|
23512
|
+
boldCharPr: richAssets ? GJ_CHAR_TABLE_BOLD : GONGMUN_TBL_CHAR_BOLD,
|
|
23513
|
+
charHeight: richAssets ? gaejosikSizes(gongmun.bodyHeight, gongmun.sizes).table : GONGMUN_TBL_PT,
|
|
23514
|
+
headerBf: gongmunTableHeaderBf(richAssets),
|
|
23515
|
+
centerParaPr: GONGMUN_CENTER,
|
|
23516
|
+
tblCenterParaPr: GONGMUN_TBL_CENTER,
|
|
23517
|
+
tblLeftParaPr: GONGMUN_TBL_LEFT,
|
|
23518
|
+
bfRegistry: bfReg ?? void 0,
|
|
23519
|
+
rightParaPr: GONGMUN_RIGHT
|
|
23520
|
+
} : null,
|
|
23521
|
+
// 개조식 장식표(표지·목차·장헤더·제목박스) 폭 스케일용 본문폭 — margins 오버라이드 대응
|
|
23522
|
+
gjBodyW: gongmun ? mmToHwpunit(210 - gongmun.margins.left - gongmun.margins.right) : GAEJOSIK_BASE_WIDTH,
|
|
23523
|
+
chamMap: (id) => id === CHAR_BOLD ? GJ_CHAR_CHAM_BOLD : id,
|
|
23524
|
+
opener: new SectionOpener(gongmun),
|
|
23525
|
+
paraXmls: [],
|
|
23526
|
+
// 표 방출 순번 — 생성 성공 여부와 무관하게 '시도' 기준으로 센다. 실패한 표가 이후
|
|
23527
|
+
// 표들의 순번을 밀면 앵커 없는 프로필(손편집·구버전)의 table_index 매칭이 어긋난다.
|
|
23528
|
+
tableSeq: 0,
|
|
23529
|
+
chapterNo: 0,
|
|
23530
|
+
h2Seq: 0,
|
|
23531
|
+
// h2 말머리 'number' 모드 아라비아 순번 (QA-2)
|
|
23532
|
+
coverH1Idx: -1,
|
|
23533
|
+
titleBoxH1Idx: -1,
|
|
23534
|
+
pressH1Idx: -1,
|
|
23535
|
+
gjChapterIdxs: [],
|
|
23536
|
+
pendingPageBreak: false,
|
|
23537
|
+
pendingNewNum: false,
|
|
23538
|
+
// 순서 있는 목록 카운터 — indent 레벨별 별도 유지. 다른 블록 만나면 해당 레벨 리셋.
|
|
23539
|
+
orderedCounters: {},
|
|
23540
|
+
prevWasOrdered: false
|
|
23541
|
+
};
|
|
23542
|
+
buildPreamble(blocks, ctx);
|
|
22500
23543
|
for (let blockIdx = 0; blockIdx < blocks.length; blockIdx++) {
|
|
22501
23544
|
const block = blocks[blockIdx];
|
|
22502
|
-
let xml = "";
|
|
22503
23545
|
if (block.type !== "list_item" || !block.ordered) {
|
|
22504
|
-
if (prevWasOrdered) {
|
|
22505
|
-
for (const k of Object.keys(orderedCounters)) delete orderedCounters[+k];
|
|
23546
|
+
if (ctx.prevWasOrdered) {
|
|
23547
|
+
for (const k of Object.keys(ctx.orderedCounters)) delete ctx.orderedCounters[+k];
|
|
22506
23548
|
}
|
|
22507
|
-
prevWasOrdered = false;
|
|
23549
|
+
ctx.prevWasOrdered = false;
|
|
22508
23550
|
}
|
|
23551
|
+
let xml = "";
|
|
22509
23552
|
switch (block.type) {
|
|
22510
|
-
case "heading":
|
|
22511
|
-
|
|
22512
|
-
const parts = [generateParagraph((block.text || "").trim(), GONGMUN_CENTER, dfIds.pressTitle)];
|
|
22513
|
-
for (const s of gongmun.press?.sub ?? []) {
|
|
22514
|
-
parts.push(generateParagraph(`- ${s} -`, GONGMUN_CENTER, dfIds.pressSub));
|
|
22515
|
-
}
|
|
22516
|
-
xml = parts.join("\n ");
|
|
22517
|
-
break;
|
|
22518
|
-
}
|
|
22519
|
-
if (gongmun && blockIdx === titleBoxH1Idx && tableStyle && bfReg) {
|
|
22520
|
-
xml = buildTitleBox((block.text || "").trim(), measured ? GJ_CHAR_BODY_TITLE : CHAR_H1, tableStyle.totalWidth, bfReg);
|
|
22521
|
-
break;
|
|
22522
|
-
}
|
|
22523
|
-
if (gaejosik) {
|
|
22524
|
-
const lvl = block.level || 1;
|
|
22525
|
-
if (blockIdx === coverH1Idx) break;
|
|
22526
|
-
if (lvl <= 2) {
|
|
22527
|
-
chapterNo++;
|
|
22528
|
-
xml = buildGaejosikChapter(chapterNo, stripChapterNumber(block.text || ""), gongmun);
|
|
22529
|
-
} else if (lvl === 3) {
|
|
22530
|
-
xml = generateParagraph(
|
|
22531
|
-
`\u25A1 ${block.text || ""}`,
|
|
22532
|
-
GONGMUN_LIST_BASE,
|
|
22533
|
-
GJ_CHAR_DAE,
|
|
22534
|
-
(id) => id === CHAR_BOLD ? GJ_CHAR_DAE_BOLD : id
|
|
22535
|
-
);
|
|
22536
|
-
} else {
|
|
22537
|
-
xml = generateParagraph(`${gongmun.bullet2} ${block.text || ""}`, GONGMUN_LIST_BASE + 1, CHAR_NORMAL);
|
|
22538
|
-
}
|
|
22539
|
-
break;
|
|
22540
|
-
}
|
|
22541
|
-
const pId = headingParaPrId(block.level || 1);
|
|
22542
|
-
const cId = headingCharPrId(block.level || 1);
|
|
22543
|
-
const styleId = gongmun ? Math.min(block.level || 1, 4) : 0;
|
|
22544
|
-
let hText = block.text || "";
|
|
22545
|
-
if (gongmun && (block.level || 1) === 2 && gongmun.h2Marker !== "none") {
|
|
22546
|
-
const title = stripChapterNumber(hText);
|
|
22547
|
-
hText = gongmun.h2Marker === "box" ? `\u25A1 ${title}` : `${++h2Seq}. ${title}`;
|
|
22548
|
-
}
|
|
22549
|
-
xml = generateParagraph(hText, pId, cId, void 0, styleId);
|
|
23553
|
+
case "heading":
|
|
23554
|
+
xml = renderHeading(block, blockIdx, ctx);
|
|
22550
23555
|
break;
|
|
22551
|
-
|
|
22552
|
-
|
|
22553
|
-
const pTrim = (block.text || "").trimStart();
|
|
22554
|
-
if (measured && (pTrim.startsWith("\u203B") || /^\*\s/.test(pTrim))) {
|
|
22555
|
-
xml = generateParagraph((block.text || "").trim(), GJ_PARA_CHAM, GJ_CHAR_CHAM, chamMap);
|
|
22556
|
-
break;
|
|
22557
|
-
}
|
|
22558
|
-
const ctr = gongmun && /^<center>([\s\S]*)<\/center>$/i.exec((block.text || "").trim());
|
|
22559
|
-
const rgt = gongmun && /^<right>([\s\S]*)<\/right>$/i.exec((block.text || "").trim());
|
|
22560
|
-
if (ctr) {
|
|
22561
|
-
xml = generateParagraph(ctr[1].trim(), GONGMUN_CENTER);
|
|
22562
|
-
} else if (rgt) {
|
|
22563
|
-
xml = generateParagraph(rgt[1].trim(), GONGMUN_RIGHT);
|
|
22564
|
-
} else {
|
|
22565
|
-
xml = generateParagraph(block.text || "", PARA_NORMAL, CHAR_NORMAL, fit ? variantMapper(fit, blockIdx, vBase) : void 0);
|
|
22566
|
-
}
|
|
23556
|
+
case "paragraph":
|
|
23557
|
+
xml = renderParagraph(block, blockIdx, ctx);
|
|
22567
23558
|
break;
|
|
22568
|
-
|
|
22569
|
-
|
|
22570
|
-
if (chartParts !== null && (block.lang || "").toLowerCase() === "chart") {
|
|
22571
|
-
const fence = parseChartFence(block.text || "");
|
|
22572
|
-
if (fence) {
|
|
22573
|
-
const partName = `Chart/chart${chartParts.length + 1}.xml`;
|
|
22574
|
-
chartParts.push({ name: partName, xml: buildChartSpaceXml(fence) });
|
|
22575
|
-
const chartEl = buildChartElementXml(partName, fence.widthHu, fence.heightHu, 91e5 + blockIdx);
|
|
22576
|
-
if (isFirst) {
|
|
22577
|
-
const secRun = `<hp:run charPrIDRef="0">${generateSecPr(gongmun)}<hp:t></hp:t></hp:run>`;
|
|
22578
|
-
paraXmls.push(`<hp:p paraPrIDRef="0" styleIDRef="0">${secRun}</hp:p>`);
|
|
22579
|
-
isFirst = false;
|
|
22580
|
-
}
|
|
22581
|
-
xml = `<hp:p paraPrIDRef="${PARA_NORMAL}" styleIDRef="0"><hp:run charPrIDRef="${CHAR_NORMAL}">${chartEl}</hp:run></hp:p>`;
|
|
22582
|
-
break;
|
|
22583
|
-
}
|
|
22584
|
-
}
|
|
22585
|
-
const codeLines = (block.text || "").split("\n");
|
|
22586
|
-
xml = codeLines.map((line) => generateParagraph(line || " ", PARA_CODE)).join("\n ");
|
|
23559
|
+
case "code_block":
|
|
23560
|
+
xml = renderCodeBlock(block, blockIdx, ctx);
|
|
22587
23561
|
break;
|
|
22588
|
-
|
|
22589
|
-
|
|
22590
|
-
if (isFirst) {
|
|
22591
|
-
const secRun = `<hp:run charPrIDRef="0">${generateSecPr(gongmun)}<hp:t></hp:t></hp:run>`;
|
|
22592
|
-
paraXmls.push(`<hp:p paraPrIDRef="0" styleIDRef="0">${secRun}</hp:p>`);
|
|
22593
|
-
isFirst = false;
|
|
22594
|
-
}
|
|
22595
|
-
xml = generateEquationParagraph(block.text || "", blockIdx);
|
|
23562
|
+
case "equation":
|
|
23563
|
+
xml = renderEquation(block, blockIdx, ctx);
|
|
22596
23564
|
break;
|
|
22597
|
-
|
|
22598
|
-
|
|
22599
|
-
if (measured) {
|
|
22600
|
-
const t = (block.text || "").trim();
|
|
22601
|
-
if (t) xml = generateParagraph(t.startsWith("\u203B") ? t : `\u203B ${t}`, GJ_PARA_CHAM, GJ_CHAR_CHAM, chamMap);
|
|
22602
|
-
break;
|
|
22603
|
-
}
|
|
22604
|
-
xml = generateParagraph(
|
|
22605
|
-
block.text || "",
|
|
22606
|
-
PARA_QUOTE,
|
|
22607
|
-
theme.hasQuoteOption ? CHAR_QUOTE : CHAR_NORMAL
|
|
22608
|
-
);
|
|
23565
|
+
case "blockquote":
|
|
23566
|
+
xml = renderBlockquote(block, ctx);
|
|
22609
23567
|
break;
|
|
22610
|
-
|
|
22611
|
-
|
|
22612
|
-
if (gongmun && gongmunList) {
|
|
22613
|
-
const info = gongmunList.get(blockIdx);
|
|
22614
|
-
const depth = info?.depth ?? 0;
|
|
22615
|
-
const marker2 = info?.marker ?? "";
|
|
22616
|
-
const content = block.text || "";
|
|
22617
|
-
if (measured && content.trimStart().startsWith("\u203B")) {
|
|
22618
|
-
xml = generateParagraph(content.trim(), GJ_PARA_CHAM, GJ_CHAR_CHAM, chamMap);
|
|
22619
|
-
break;
|
|
22620
|
-
}
|
|
22621
|
-
if (measured && block.marker === "*") {
|
|
22622
|
-
xml = generateParagraph(`* ${content.trim()}`, GJ_PARA_CHAM, GJ_CHAR_CHAM, chamMap);
|
|
22623
|
-
break;
|
|
22624
|
-
}
|
|
22625
|
-
const text = marker2 ? `${marker2} ${content}` : content;
|
|
22626
|
-
const listParaPr = marker2 ? GONGMUN_LIST_BASE + depth : GONGMUN_LIST_PLAIN_BASE + depth;
|
|
22627
|
-
let listCharPr = gongmun.numbering === "report" && depth === 0 ? CHAR_BOLD : CHAR_NORMAL;
|
|
22628
|
-
let mapId = fit ? variantMapper(fit, blockIdx, vBase) : void 0;
|
|
22629
|
-
if (measured && depth === 0 && gongmun.numbering !== "standard") {
|
|
22630
|
-
listCharPr = GJ_CHAR_DAE;
|
|
22631
|
-
mapId = (id) => id === CHAR_BOLD ? GJ_CHAR_DAE_BOLD : id;
|
|
22632
|
-
}
|
|
22633
|
-
if (gongmun.preset === "press") {
|
|
22634
|
-
if (depth === 0) listCharPr = CHAR_NORMAL;
|
|
22635
|
-
if (depth >= 2 && dfIds) {
|
|
22636
|
-
listCharPr = dfIds.small;
|
|
22637
|
-
mapId = void 0;
|
|
22638
|
-
}
|
|
22639
|
-
}
|
|
22640
|
-
xml = generateParagraph(text, listParaPr, listCharPr, mapId);
|
|
22641
|
-
break;
|
|
22642
|
-
}
|
|
22643
|
-
const indent = block.indent || 0;
|
|
22644
|
-
let marker;
|
|
22645
|
-
if (block.marker) {
|
|
22646
|
-
marker = `${block.marker} `;
|
|
22647
|
-
prevWasOrdered = !!block.ordered;
|
|
22648
|
-
} else if (block.ordered) {
|
|
22649
|
-
orderedCounters[indent] = (orderedCounters[indent] || 0) + 1;
|
|
22650
|
-
for (const k of Object.keys(orderedCounters)) {
|
|
22651
|
-
if (+k > indent) delete orderedCounters[+k];
|
|
22652
|
-
}
|
|
22653
|
-
marker = `${orderedCounters[indent]}. `;
|
|
22654
|
-
prevWasOrdered = true;
|
|
22655
|
-
} else {
|
|
22656
|
-
marker = "\xB7 ";
|
|
22657
|
-
if (prevWasOrdered) {
|
|
22658
|
-
for (const k of Object.keys(orderedCounters)) delete orderedCounters[+k];
|
|
22659
|
-
}
|
|
22660
|
-
prevWasOrdered = false;
|
|
22661
|
-
}
|
|
22662
|
-
const indentPrefix = " ".repeat(indent);
|
|
22663
|
-
xml = generateParagraph(indentPrefix + marker + (block.text || ""), PARA_LIST);
|
|
23568
|
+
case "list_item":
|
|
23569
|
+
xml = renderListItem(block, blockIdx, ctx);
|
|
22664
23570
|
break;
|
|
22665
|
-
}
|
|
22666
23571
|
case "hr":
|
|
22667
|
-
xml =
|
|
23572
|
+
xml = renderHr(ctx);
|
|
22668
23573
|
break;
|
|
22669
23574
|
case "table":
|
|
22670
|
-
|
|
22671
|
-
if (isFirst) {
|
|
22672
|
-
const secRun = `<hp:run charPrIDRef="0">${generateSecPr(gongmun)}<hp:t></hp:t></hp:run>`;
|
|
22673
|
-
paraXmls.push(`<hp:p paraPrIDRef="0" styleIDRef="0">${secRun}</hp:p>`);
|
|
22674
|
-
isFirst = false;
|
|
22675
|
-
}
|
|
22676
|
-
xml = generateTable(block.rows, theme, tableStyle, remap, tableSeq++);
|
|
22677
|
-
}
|
|
23575
|
+
xml = renderTable(block, ctx);
|
|
22678
23576
|
break;
|
|
22679
|
-
case "html_table":
|
|
22680
|
-
|
|
22681
|
-
const tbl2 = generateHtmlTableXml(block.text || "", theme, htmlW, tableStyle, remap, tableSeq++);
|
|
22682
|
-
if (tbl2) {
|
|
22683
|
-
if (isFirst) {
|
|
22684
|
-
const secRun = `<hp:run charPrIDRef="0">${generateSecPr(gongmun)}<hp:t></hp:t></hp:run>`;
|
|
22685
|
-
paraXmls.push(`<hp:p paraPrIDRef="0" styleIDRef="0">${secRun}</hp:p>`);
|
|
22686
|
-
isFirst = false;
|
|
22687
|
-
}
|
|
22688
|
-
xml = `<hp:p paraPrIDRef="${tableStyle && bfReg ? GONGMUN_RIGHT : 0}" styleIDRef="0"><hp:run charPrIDRef="0">${tbl2}</hp:run></hp:p>`;
|
|
22689
|
-
} else {
|
|
22690
|
-
const plain = (block.text || "").replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
|
|
22691
|
-
xml = plain ? generateParagraph(plain) : "";
|
|
22692
|
-
}
|
|
23577
|
+
case "html_table":
|
|
23578
|
+
xml = renderHtmlTable(block, ctx);
|
|
22693
23579
|
break;
|
|
22694
|
-
}
|
|
22695
23580
|
}
|
|
22696
23581
|
if (!xml) continue;
|
|
22697
|
-
if (isFirst && block.type !== "table") {
|
|
22698
|
-
xml =
|
|
22699
|
-
/<hp:run charPrIDRef="(\d+)">/,
|
|
22700
|
-
`<hp:run charPrIDRef="$1">${generateSecPr(gongmun)}`
|
|
22701
|
-
);
|
|
22702
|
-
isFirst = false;
|
|
23582
|
+
if (ctx.opener.isFirst && block.type !== "table") {
|
|
23583
|
+
xml = ctx.opener.inject(xml);
|
|
22703
23584
|
}
|
|
22704
|
-
if (pendingPageBreak) {
|
|
23585
|
+
if (ctx.pendingPageBreak) {
|
|
22705
23586
|
xml = xml.replace(/^<hp:p /, `<hp:p pageBreak="1" `);
|
|
22706
|
-
pendingPageBreak = false;
|
|
23587
|
+
ctx.pendingPageBreak = false;
|
|
22707
23588
|
}
|
|
22708
|
-
if (pendingNewNum) {
|
|
23589
|
+
if (ctx.pendingNewNum) {
|
|
22709
23590
|
xml = xml.replace(/<hp:run charPrIDRef="(\d+)">/, `<hp:run charPrIDRef="$1">${newPageNumCtrl(1)}`);
|
|
22710
|
-
pendingNewNum = false;
|
|
23591
|
+
ctx.pendingNewNum = false;
|
|
22711
23592
|
}
|
|
22712
|
-
paraXmls.push(xml);
|
|
22713
|
-
}
|
|
22714
|
-
if (gongmun?.noticeHead && dfIds) {
|
|
22715
|
-
paraXmls.push(...buildNoticeFoot(gongmun));
|
|
22716
|
-
}
|
|
22717
|
-
if (gongmun?.endMark && paraXmls.length > 0) {
|
|
22718
|
-
const lastText = [...blocks].reverse().find((b) => (b.type === "paragraph" || b.type === "list_item") && (b.text || "").trim())?.text ?? "";
|
|
22719
|
-
if (!hasEndMark(lastText)) paraXmls.push(buildEndMark());
|
|
22720
|
-
}
|
|
22721
|
-
if (gongmun?.docFoot && dfIds) {
|
|
22722
|
-
paraXmls.push(...buildDocFoot(gongmun, dfIds));
|
|
22723
|
-
}
|
|
22724
|
-
if (gongmun?.press && dfIds && bfReg && tableStyle) {
|
|
22725
|
-
paraXmls.push(...buildPressContact(gongmun, dfIds, tableStyle.totalWidth, bfReg));
|
|
23593
|
+
ctx.paraXmls.push(xml);
|
|
22726
23594
|
}
|
|
22727
|
-
|
|
22728
|
-
|
|
23595
|
+
appendPostamble(blocks, ctx);
|
|
23596
|
+
if (ctx.paraXmls.length === 0) {
|
|
23597
|
+
ctx.paraXmls.push(ctx.opener.emptyDoc());
|
|
22729
23598
|
}
|
|
22730
23599
|
return `<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
|
|
22731
23600
|
<hs:sec xmlns:hs="${NS_SECTION}" xmlns:hp="${NS_PARA}">
|
|
22732
|
-
${paraXmls.join("\n ")}
|
|
23601
|
+
${ctx.paraXmls.join("\n ")}
|
|
22733
23602
|
</hs:sec>`;
|
|
22734
23603
|
}
|
|
22735
23604
|
|
|
@@ -22737,19 +23606,20 @@ function blocksToSectionXml(blocks, theme, gongmun, gongmunList = gongmun ? prec
|
|
|
22737
23606
|
async function markdownToHwpx(markdown, options) {
|
|
22738
23607
|
const theme = resolveTheme(options?.theme);
|
|
22739
23608
|
const gongmun = options?.gongmun ? resolveGongmun(options.gongmun) : null;
|
|
22740
|
-
const gaejosik = gongmun?.preset === "gaejosik";
|
|
22741
23609
|
const measured = !!gongmun && usesReportFonts(gongmun.preset);
|
|
23610
|
+
const richAssets = !!gongmun && needsGaejosikAssets(gongmun);
|
|
22742
23611
|
const blocks = parseMarkdownToBlocks(markdown);
|
|
22743
23612
|
const gongmunList = gongmun ? precomputeGongmunList(blocks, gongmun) : null;
|
|
22744
23613
|
const fit = gongmun && gongmunList ? computeGongmunFitPlan(blocks, gongmun, gongmunList) : null;
|
|
22745
|
-
const staticBfEnd = gongmun
|
|
22746
|
-
const remap = options?.profile ? buildProfileRemap(options.profile, charVariantBase(
|
|
23614
|
+
const staticBfEnd = staticBorderFillNext(gongmun);
|
|
23615
|
+
const remap = options?.profile ? buildProfileRemap(options.profile, charVariantBase(richAssets, !!gongmun) + (fit?.variants?.length ?? 0) * 4, staticBfEnd, staticFontNext(gongmun)) : null;
|
|
22747
23616
|
const bfReg = gongmun ? new TableBfRegistry(staticBfEnd + (remap?.borderFillXmls.length ?? 0)) : null;
|
|
22748
23617
|
const dfActive = !!gongmun && docframeActive(gongmun);
|
|
22749
|
-
const dfBase = charVariantBase(
|
|
23618
|
+
const dfBase = charVariantBase(richAssets, !!gongmun) + (fit?.variants?.length ?? 0) * 4 + (remap?.charPrXmls.length ?? 0);
|
|
22750
23619
|
const dfIds = dfActive ? docframeIds(dfBase) : null;
|
|
22751
23620
|
const chartParts = [];
|
|
22752
|
-
const
|
|
23621
|
+
const images = new ImageRegistry();
|
|
23622
|
+
const sectionXml = blocksToSectionXml(blocks, theme, gongmun, gongmunList, fit, chartParts, bfReg, remap, dfIds, images);
|
|
22753
23623
|
if (remap && remap.tables.length > 0) {
|
|
22754
23624
|
const unused = remap.tables.filter((t) => !t.used).length;
|
|
22755
23625
|
if (unused === remap.tables.length) {
|
|
@@ -22760,13 +23630,16 @@ async function markdownToHwpx(markdown, options) {
|
|
|
22760
23630
|
const zip = new JSZip6();
|
|
22761
23631
|
zip.file("mimetype", "application/hwp+zip", { compression: "STORE" });
|
|
22762
23632
|
zip.file("META-INF/container.xml", generateContainerXml());
|
|
22763
|
-
zip.file("Contents/content.hpf", generateManifest(chartParts));
|
|
23633
|
+
zip.file("Contents/content.hpf", generateManifest(chartParts, images.manifestItems(), gongmun ? "gongmun" : "default"));
|
|
23634
|
+
for (const part of images.parts) zip.file(part.name, part.data);
|
|
22764
23635
|
zip.file("Contents/header.xml", generateHeaderXml(
|
|
22765
23636
|
theme,
|
|
22766
23637
|
gongmun,
|
|
22767
23638
|
fit?.variants ?? [],
|
|
22768
23639
|
extraBorderFills,
|
|
22769
|
-
[...remap?.charPrXmls ?? [], ...dfActive ? docframeCharPrXmls(dfBase,
|
|
23640
|
+
[...remap?.charPrXmls ?? [], ...dfActive ? docframeCharPrXmls(dfBase, richAssets) : []],
|
|
23641
|
+
gongmunList?.indentVariants ?? [],
|
|
23642
|
+
remap?.fontFaces ?? []
|
|
22770
23643
|
));
|
|
22771
23644
|
zip.file("Contents/section0.xml", sectionXml);
|
|
22772
23645
|
for (const part of chartParts) zip.file(part.name, part.xml);
|
|
@@ -22946,7 +23819,19 @@ function parseBorderFills(headerDoc) {
|
|
|
22946
23819
|
}
|
|
22947
23820
|
return map;
|
|
22948
23821
|
}
|
|
22949
|
-
function
|
|
23822
|
+
function parseHangulFonts(headerDoc) {
|
|
23823
|
+
const map = /* @__PURE__ */ new Map();
|
|
23824
|
+
for (const ff of elemsByLocal(headerDoc, "fontface")) {
|
|
23825
|
+
if (ff.getAttribute("lang") !== "HANGUL") continue;
|
|
23826
|
+
for (const font of elemsByLocal(ff, "font")) {
|
|
23827
|
+
const id = font.getAttribute("id");
|
|
23828
|
+
const face = font.getAttribute("face");
|
|
23829
|
+
if (id && face) map.set(id, face);
|
|
23830
|
+
}
|
|
23831
|
+
}
|
|
23832
|
+
return map;
|
|
23833
|
+
}
|
|
23834
|
+
function parseCharPrs(headerDoc, hangulFonts) {
|
|
22950
23835
|
const map = /* @__PURE__ */ new Map();
|
|
22951
23836
|
for (const cp of elemsByLocal(headerDoc, "charPr")) {
|
|
22952
23837
|
const id = cp.getAttribute("id");
|
|
@@ -22961,7 +23846,11 @@ function parseCharPrs(headerDoc) {
|
|
|
22961
23846
|
if (findChildByLocalName(cp, "underline")) def.underline = true;
|
|
22962
23847
|
const fontRef = findChildByLocalName(cp, "fontRef");
|
|
22963
23848
|
const hangul = fontRef?.getAttribute("hangul");
|
|
22964
|
-
if (hangul)
|
|
23849
|
+
if (hangul) {
|
|
23850
|
+
def.fontRef_hangul = hangul;
|
|
23851
|
+
const face = hangulFonts.get(hangul);
|
|
23852
|
+
if (face) def.fontName_hangul = face;
|
|
23853
|
+
}
|
|
22965
23854
|
map.set(id, def);
|
|
22966
23855
|
}
|
|
22967
23856
|
return map;
|
|
@@ -22989,6 +23878,8 @@ function parseTable3(tbl2, tableIndex, borderFills, charPrs) {
|
|
|
22989
23878
|
const usedBf = /* @__PURE__ */ new Set();
|
|
22990
23879
|
const usedCp = /* @__PURE__ */ new Set();
|
|
22991
23880
|
const colWidths = new Array(cols).fill(void 0);
|
|
23881
|
+
const spanCells = [];
|
|
23882
|
+
const row0Texts = /* @__PURE__ */ new Map();
|
|
22992
23883
|
let anchorText = "";
|
|
22993
23884
|
for (const tc2 of elemsByLocal(tbl2, "tc")) {
|
|
22994
23885
|
if (nearestTable(tc2) !== tbl2) continue;
|
|
@@ -22998,6 +23889,7 @@ function parseTable3(tbl2, tableIndex, borderFills, charPrs) {
|
|
|
22998
23889
|
const row = num(addr?.getAttribute("rowAddr")) ?? 0;
|
|
22999
23890
|
const col = num(addr?.getAttribute("colAddr")) ?? 0;
|
|
23000
23891
|
if (row === 0 && col === 0 && !anchorText) anchorText = directCellText(tc2);
|
|
23892
|
+
if (row === 0) row0Texts.set(col, directCellText(tc2));
|
|
23001
23893
|
const colSpan = num(span?.getAttribute("colSpan")) ?? 1;
|
|
23002
23894
|
const rowSpan = num(span?.getAttribute("rowSpan")) ?? 1;
|
|
23003
23895
|
const bfId = tc2.getAttribute("borderFillIDRef") || void 0;
|
|
@@ -23016,7 +23908,20 @@ function parseTable3(tbl2, tableIndex, borderFills, charPrs) {
|
|
|
23016
23908
|
usedCp.add(cpId);
|
|
23017
23909
|
}
|
|
23018
23910
|
cells.push(cell2);
|
|
23019
|
-
|
|
23911
|
+
const wNum = num(w);
|
|
23912
|
+
if (wNum != null && col < cols) {
|
|
23913
|
+
if (colSpan === 1) {
|
|
23914
|
+
if (colWidths[col] == null) colWidths[col] = wNum;
|
|
23915
|
+
} else spanCells.push({ col, colSpan, w: wNum });
|
|
23916
|
+
}
|
|
23917
|
+
}
|
|
23918
|
+
for (const s of spanCells) {
|
|
23919
|
+
const covered = Array.from({ length: s.colSpan }, (_, i) => s.col + i).filter((c) => c < cols);
|
|
23920
|
+
const unknown = covered.filter((c) => colWidths[c] == null);
|
|
23921
|
+
if (unknown.length === 0) continue;
|
|
23922
|
+
const known = covered.reduce((sum, c) => sum + (colWidths[c] ?? 0), 0);
|
|
23923
|
+
const each = Math.floor((s.w - known) / unknown.length);
|
|
23924
|
+
if (each > 0) for (const c of unknown) colWidths[c] = each;
|
|
23020
23925
|
}
|
|
23021
23926
|
const table2 = {
|
|
23022
23927
|
table_index: tableIndex,
|
|
@@ -23027,8 +23932,10 @@ function parseTable3(tbl2, tableIndex, borderFills, charPrs) {
|
|
|
23027
23932
|
};
|
|
23028
23933
|
const anchor = normalizeAnchor(anchorText);
|
|
23029
23934
|
if (anchor) table2.anchor_text = anchor;
|
|
23935
|
+
const rowAnchor = normalizeRowAnchor(Array.from({ length: cols }, (_, c) => row0Texts.get(c) ?? ""));
|
|
23936
|
+
if (rowAnchor) table2.anchor_row = rowAnchor;
|
|
23030
23937
|
if (width) table2.width_hwpunit = width;
|
|
23031
|
-
if (colWidths.every((w) => w != null)) table2.col_widths_hwpunit = colWidths;
|
|
23938
|
+
if (colWidths.every((w) => w != null)) table2.col_widths_hwpunit = colWidths.map(String);
|
|
23032
23939
|
const cp = pick(charPrs, usedCp);
|
|
23033
23940
|
if (Object.keys(cp).length) table2.used_char_prs = cp;
|
|
23034
23941
|
return table2;
|
|
@@ -23085,7 +23992,7 @@ async function hwpxToProfile(input) {
|
|
|
23085
23992
|
if (headerFile) headerXml = await headerFile.async("text");
|
|
23086
23993
|
const headerDoc = parser.parseFromString(headerXml, "text/xml");
|
|
23087
23994
|
const borderFills = parseBorderFills(headerDoc);
|
|
23088
|
-
const charPrs = parseCharPrs(headerDoc);
|
|
23995
|
+
const charPrs = parseCharPrs(headerDoc, parseHangulFonts(headerDoc));
|
|
23089
23996
|
const sectionFiles = zip.file(/[Ss]ection\d+\.xml$/).sort((a, b) => (num(a.name.match(/(\d+)\.xml$/)?.[1] ?? null) ?? 0) - (num(b.name.match(/(\d+)\.xml$/)?.[1] ?? null) ?? 0));
|
|
23090
23997
|
const tables = [];
|
|
23091
23998
|
let tableIndex = 0;
|
|
@@ -23096,7 +24003,7 @@ async function hwpxToProfile(input) {
|
|
|
23096
24003
|
tables.push(parseTable3(tbl2, tableIndex++, borderFills, charPrs));
|
|
23097
24004
|
}
|
|
23098
24005
|
}
|
|
23099
|
-
return { schema_version: "0.
|
|
24006
|
+
return { schema_version: "0.3.0", tables };
|
|
23100
24007
|
}
|
|
23101
24008
|
|
|
23102
24009
|
// src/hwpx/font-catalog.ts
|
|
@@ -23236,7 +24143,7 @@ var COMMON_FREE = [
|
|
|
23236
24143
|
"\uC724\uBA85\uC870"
|
|
23237
24144
|
];
|
|
23238
24145
|
function norm(name) {
|
|
23239
|
-
return name.replace(/\s+/g, "").toLowerCase();
|
|
24146
|
+
return name.normalize("NFC").replace(/\s+/g, "").toLowerCase();
|
|
23240
24147
|
}
|
|
23241
24148
|
var KNOWN = new Set([...HANCOM_BUNDLED, ...WINDOWS_DEFAULT, ...COMMON_FREE].map(norm));
|
|
23242
24149
|
function isKnownFont(name) {
|
|
@@ -23266,7 +24173,7 @@ var RULES = [
|
|
|
23266
24173
|
{
|
|
23267
24174
|
code: "DATE_ZERO_PAD",
|
|
23268
24175
|
severity: "error",
|
|
23269
|
-
pattern: /\b\d{4}\.\s*0\d
|
|
24176
|
+
pattern: /\b\d{4}\.\s*0\d\.|\b\d{4}\.\s*\d{1,2}\.\s*0\d/g,
|
|
23270
24177
|
message: "\uC6D4\xB7\uC77C \uC55E\uC758 '0'\uC740 \uD45C\uAE30\uD558\uC9C0 \uC54A\uC74C",
|
|
23271
24178
|
suggest: "\uC608) 2025. 1. 6. (2025. 01. 06. \u2715)"
|
|
23272
24179
|
},
|
|
@@ -23348,22 +24255,25 @@ var RULES = [
|
|
|
23348
24255
|
{
|
|
23349
24256
|
code: "COLON_SPACE",
|
|
23350
24257
|
severity: "warning",
|
|
23351
|
-
pattern: /\S\s
|
|
24258
|
+
pattern: /\S\s+:(?!\/\/)|\S:(?!\/\/)[^\s\d]/g,
|
|
23352
24259
|
message: "\uC30D\uC810\uC740 \uC55E\uB9D0\uC5D0 \uBD99\uC774\uACE0 \uB4A4\uB294 \uD55C \uCE78 \uB744\uC6C0",
|
|
23353
24260
|
suggest: "\uC608) \uC6D0\uC7A5: \uAE40\uAC11\uB3D9"
|
|
23354
24261
|
}
|
|
23355
24262
|
];
|
|
23356
24263
|
function lintGongmunText(text) {
|
|
23357
24264
|
const findings = [];
|
|
23358
|
-
let
|
|
24265
|
+
let fenceMarker = null;
|
|
23359
24266
|
const lines = text.split(/\r?\n/);
|
|
23360
24267
|
for (let i = 0; i < lines.length; i++) {
|
|
23361
24268
|
const line = lines[i];
|
|
23362
|
-
|
|
23363
|
-
|
|
24269
|
+
const fence = line.match(/^\s*(```+|~~~+)/);
|
|
24270
|
+
if (fence) {
|
|
24271
|
+
const kind = fence[1][0];
|
|
24272
|
+
if (fenceMarker === null) fenceMarker = kind;
|
|
24273
|
+
else if (kind === fenceMarker) fenceMarker = null;
|
|
23364
24274
|
continue;
|
|
23365
24275
|
}
|
|
23366
|
-
if (
|
|
24276
|
+
if (fenceMarker !== null) continue;
|
|
23367
24277
|
for (const r of RULES) {
|
|
23368
24278
|
r.pattern.lastIndex = 0;
|
|
23369
24279
|
for (const m of line.matchAll(r.pattern)) {
|
|
@@ -26120,7 +27030,7 @@ async function parseHwp(buffer, options) {
|
|
|
26120
27030
|
async function parsePdf(buffer, options) {
|
|
26121
27031
|
let parsePdfDocument;
|
|
26122
27032
|
try {
|
|
26123
|
-
const mod = await import("./parser-
|
|
27033
|
+
const mod = await import("./parser-46JGSTPR.js");
|
|
26124
27034
|
parsePdfDocument = mod.parsePdfDocument;
|
|
26125
27035
|
} catch {
|
|
26126
27036
|
return {
|
|
@@ -26211,6 +27121,9 @@ async function fillForm(input, values, outputFormat = "markdown") {
|
|
|
26211
27121
|
}
|
|
26212
27122
|
|
|
26213
27123
|
export {
|
|
27124
|
+
PRESET_ALIAS,
|
|
27125
|
+
normalizeGongmunPreset,
|
|
27126
|
+
incompatibleGongmunWarnings,
|
|
26214
27127
|
extractHwpxMetadataOnly,
|
|
26215
27128
|
extractHwp5MetadataOnly,
|
|
26216
27129
|
formatFillValue,
|
|
@@ -26222,8 +27135,6 @@ export {
|
|
|
26222
27135
|
extractFormSchema,
|
|
26223
27136
|
fillFormFields,
|
|
26224
27137
|
fillHwpx,
|
|
26225
|
-
PRESET_ALIAS,
|
|
26226
|
-
normalizeGongmunPreset,
|
|
26227
27138
|
markdownToHwpx,
|
|
26228
27139
|
compare,
|
|
26229
27140
|
diffBlocks,
|
|
@@ -26252,4 +27163,4 @@ export {
|
|
|
26252
27163
|
parseHwpml,
|
|
26253
27164
|
fillForm
|
|
26254
27165
|
};
|
|
26255
|
-
//# sourceMappingURL=chunk-
|
|
27166
|
+
//# sourceMappingURL=chunk-MLNHZN32.js.map
|