kordoc 4.0.3 → 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 +8 -0
- package/dist/{-QSF64HNK.js → -Q4ZCVQFI.js} +9 -7
- 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-AUJICYJG.js → chunk-DKMKJMR6.js} +4 -3
- package/dist/chunk-DKMKJMR6.js.map +1 -0
- package/dist/{chunk-6JSZBSOD.js → chunk-KA5AN73R.js} +2 -2
- package/dist/{chunk-TQYX5E32.js → chunk-KSVLQ5BC.js} +33 -4
- package/dist/chunk-KSVLQ5BC.js.map +1 -0
- package/dist/{chunk-K2SETHJ6.js → chunk-MLNHZN32.js} +1589 -765
- package/dist/chunk-MLNHZN32.js.map +1 -0
- package/dist/{chunk-OVESZY5R.cjs → chunk-NIW7CZYX.cjs} +37 -8
- package/dist/chunk-NIW7CZYX.cjs.map +1 -0
- package/dist/{chunk-MQLKSRIK.js → chunk-S3KGEEBQ.js} +76 -25
- package/dist/chunk-S3KGEEBQ.js.map +1 -0
- package/dist/{chunk-RO2GVZ3I.js → chunk-TXNRIVWA.js} +3 -3
- package/dist/{chunk-PNQDQ3SM.js → chunk-ZJXR4PWY.js} +33 -4
- package/dist/chunk-ZJXR4PWY.js.map +1 -0
- package/dist/cli.js +119 -72
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +21122 -20246
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +47 -1
- package/dist/index.d.ts +47 -1
- package/dist/index.js +20944 -20068
- package/dist/index.js.map +1 -1
- package/dist/mcp.js +106 -83
- package/dist/mcp.js.map +1 -1
- package/dist/{parser-FMONFTAN.cjs → parser-2ZUTCU2G.cjs} +106 -17
- package/dist/parser-2ZUTCU2G.cjs.map +1 -0
- package/dist/{parser-AODAQQLQ.js → parser-46JGSTPR.js} +95 -6
- package/dist/parser-46JGSTPR.js.map +1 -0
- package/dist/{parser-J7YREDNS.js → parser-TLCC67W6.js} +94 -5
- package/dist/parser-TLCC67W6.js.map +1 -0
- package/dist/profile-io-MRVIJ5T4.js +11 -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/{watch-P65Z24AL.js → watch-NZNVJ4YD.js} +7 -7
- package/package.json +1 -1
- package/dist/chunk-AUJICYJG.js.map +0 -1
- package/dist/chunk-K2SETHJ6.js.map +0 -1
- package/dist/chunk-MQLKSRIK.js.map +0 -1
- package/dist/chunk-OVESZY5R.cjs.map +0 -1
- package/dist/chunk-PNQDQ3SM.js.map +0 -1
- package/dist/chunk-TQYX5E32.js.map +0 -1
- package/dist/parser-AODAQQLQ.js.map +0 -1
- package/dist/parser-FMONFTAN.cjs.map +0 -1
- package/dist/parser-J7YREDNS.js.map +0 -1
- package/dist/render-JPP7YTV3.js +0 -10
- package/dist/seal-MDE4DXZQ.js +0 -10
- /package/dist/{-QSF64HNK.js.map → -Q4ZCVQFI.js.map} +0 -0
- /package/dist/{chunk-6JSZBSOD.js.map → chunk-KA5AN73R.js.map} +0 -0
- /package/dist/{chunk-RO2GVZ3I.js.map → chunk-TXNRIVWA.js.map} +0 -0
- /package/dist/{render-JPP7YTV3.js.map → profile-io-MRVIJ5T4.js.map} +0 -0
- /package/dist/{seal-MDE4DXZQ.js.map → render-TTOG6CHW.js.map} +0 -0
- /package/dist/{watch-P65Z24AL.js.map → watch-NZNVJ4YD.js.map} +0 -0
|
@@ -11,7 +11,7 @@ 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,
|
|
@@ -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) {
|
|
@@ -205,6 +206,7 @@ async function extractHwpxStyles(zip, decompressed) {
|
|
|
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,8 +864,390 @@ function hmlToLatex(hmlEqStr) {
|
|
|
832
864
|
return out;
|
|
833
865
|
}
|
|
834
866
|
|
|
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}`);
|
|
1064
|
+
}
|
|
1065
|
+
}
|
|
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");
|
|
1091
|
+
}
|
|
1092
|
+
}
|
|
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");
|
|
1102
|
+
}
|
|
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`);
|
|
1105
|
+
}
|
|
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`);
|
|
1108
|
+
}
|
|
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
|
+
|
|
835
1250
|
// src/hwpx/para-heading.ts
|
|
836
|
-
var HANGUL_SYLLABLE_SEQ = "\uAC00\uB098\uB2E4\uB77C\uB9C8\uBC14\uC0AC\uC544\uC790\uCC28\uCE74\uD0C0\uD30C\uD558";
|
|
837
1251
|
var HANGUL_JAMO_SEQ = "\u3131\u3134\u3137\u3139\u3141\u3142\u3145\u3147\u3148\u314A\u314B\u314C\u314D\u314E";
|
|
838
1252
|
function toRoman(n) {
|
|
839
1253
|
if (n <= 0 || n >= 4e3) return String(n);
|
|
@@ -866,12 +1280,14 @@ function formatHeadNumber(n, numFormat) {
|
|
|
866
1280
|
switch (numFormat) {
|
|
867
1281
|
case "DIGIT":
|
|
868
1282
|
return String(n);
|
|
1283
|
+
// 서수 시퀀스는 생성기(gongmun.ts)와 단일 소스 (v4.0.5) — 종전 mod-14 순환은
|
|
1284
|
+
// 15번째 형제를 다시 '가'로 만들어 한글 실렌더(가→하→거→너 단모음 연속)와 어긋났다
|
|
869
1285
|
case "CIRCLED_DIGIT":
|
|
870
|
-
return n <=
|
|
1286
|
+
return n <= 50 ? circledNumber(n - 1) : `(${n})`;
|
|
871
1287
|
case "HANGUL_SYLLABLE":
|
|
872
|
-
return
|
|
1288
|
+
return hangulOrdinal(n - 1);
|
|
873
1289
|
case "CIRCLED_HANGUL_SYLLABLE":
|
|
874
|
-
return n <= 14 ?
|
|
1290
|
+
return n <= 14 ? circledHangul(n - 1) : hangulOrdinal(n - 1);
|
|
875
1291
|
case "HANGUL_JAMO":
|
|
876
1292
|
return HANGUL_JAMO_SEQ[(n - 1) % HANGUL_JAMO_SEQ.length];
|
|
877
1293
|
case "CIRCLED_HANGUL_JAMO":
|
|
@@ -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);
|
|
@@ -1121,8 +1546,8 @@ function walkSection(node, blocks, tableCtx, tableStack, ctx, depth = 0) {
|
|
|
1121
1546
|
const { text: rawText, href, footnote, style } = extractParagraphInfo(el, ctx.styleMap, ctx);
|
|
1122
1547
|
let text = rawText;
|
|
1123
1548
|
let headingLevel;
|
|
1549
|
+
const ph = resolveParaHeading(el, ctx);
|
|
1124
1550
|
if (text) {
|
|
1125
|
-
const ph = resolveParaHeading(el, ctx);
|
|
1126
1551
|
if (ph?.prefix) text = ph.prefix + " " + text;
|
|
1127
1552
|
headingLevel = ph?.headingLevel;
|
|
1128
1553
|
}
|
|
@@ -1131,13 +1556,40 @@ function walkSection(node, blocks, tableCtx, tableStack, ctx, depth = 0) {
|
|
|
1131
1556
|
const cell2 = tableCtx.cell;
|
|
1132
1557
|
if (footnote) text += ` (\uC8FC: ${footnote})`;
|
|
1133
1558
|
cell2.text += (cell2.text ? "\n" : "") + text;
|
|
1134
|
-
|
|
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);
|
|
1135
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
|
+
}
|
|
1136
1574
|
const block = { type: headingLevel ? "heading" : "paragraph", text, pageNumber: ctx.sectionNum };
|
|
1137
1575
|
if (headingLevel) block.level = headingLevel;
|
|
1138
1576
|
if (style) block.style = style;
|
|
1139
1577
|
if (href) block.href = href;
|
|
1140
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
|
+
}
|
|
1141
1593
|
blocks.push(block);
|
|
1142
1594
|
} else {
|
|
1143
1595
|
blocks.push({ type: "paragraph", text, pageNumber: ctx.sectionNum });
|
|
@@ -1219,6 +1671,34 @@ function mergeBlocksIntoCell(cell2, sink) {
|
|
|
1219
1671
|
}
|
|
1220
1672
|
}
|
|
1221
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
|
+
}
|
|
1222
1702
|
function collectSubListText(el, ctx, depth = 0) {
|
|
1223
1703
|
if (depth > 10) return "";
|
|
1224
1704
|
const parts = [];
|
|
@@ -1254,6 +1734,15 @@ function walkParagraphChildren(node, blocks, tableCtx, tableStack, ctx, depth =
|
|
|
1254
1734
|
const tag = el.tagName || el.localName || "";
|
|
1255
1735
|
const localTag = tag.replace(/^[^:]+:/, "");
|
|
1256
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
|
+
}
|
|
1257
1746
|
if (tableCtx) tableStack.push(tableCtx);
|
|
1258
1747
|
const newTable = { rows: [], currentRow: [], cell: null };
|
|
1259
1748
|
walkSection(el, blocks, newTable, tableStack, ctx, d + 1);
|
|
@@ -1587,6 +2076,55 @@ function extractParagraphInfo(para2, styleMap, ctx) {
|
|
|
1587
2076
|
}
|
|
1588
2077
|
return { text: cleanText, href, footnote, style };
|
|
1589
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
|
+
}
|
|
1590
2128
|
|
|
1591
2129
|
// src/hwpx/images.ts
|
|
1592
2130
|
function imageExtToMime(ext) {
|
|
@@ -1776,6 +2314,17 @@ function extractFromBrokenZip(buffer) {
|
|
|
1776
2314
|
const markdown = blocksToMarkdown(blocks);
|
|
1777
2315
|
return { markdown, blocks, warnings: warnings.length > 0 ? warnings : void 0 };
|
|
1778
2316
|
}
|
|
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
|
+
}
|
|
1779
2328
|
async function resolveSectionPaths(zip) {
|
|
1780
2329
|
const manifestPaths = ["Contents/content.hpf", "content.hpf"];
|
|
1781
2330
|
for (const mp of manifestPaths) {
|
|
@@ -1906,6 +2455,7 @@ async function parseHwpxDocument(buffer, options) {
|
|
|
1906
2455
|
const totalTarget = pageFilter ? pageFilter.size : sectionPaths.length;
|
|
1907
2456
|
const blocks = [];
|
|
1908
2457
|
const shared = createSectionShared();
|
|
2458
|
+
shared.kordocLayout = await readKordocLayout(zip);
|
|
1909
2459
|
let parsedSections = 0;
|
|
1910
2460
|
for (let si = 0; si < sectionPaths.length; si++) {
|
|
1911
2461
|
if (pageFilter && !pageFilter.has(si + 1)) continue;
|
|
@@ -19738,364 +20288,29 @@ async function fillHwpx(hwpxBuffer, values, blockedLabels) {
|
|
|
19738
20288
|
for (const k of l.matchKeys) failedKeys.add(k);
|
|
19739
20289
|
continue;
|
|
19740
20290
|
}
|
|
19741
|
-
for (const k of l.matchKeys) succeededKeys.add(k);
|
|
19742
|
-
splices.push(...paraSplices);
|
|
19743
|
-
}
|
|
19744
|
-
if (splices.length > 0) {
|
|
19745
|
-
splices.push(...allLinesegRemovalSplices(xml));
|
|
19746
|
-
replacements.set(sectionPaths[si], encoder.encode(applySplices(xml, splices)));
|
|
19747
|
-
}
|
|
19748
|
-
}
|
|
19749
|
-
for (const k of failedKeys) {
|
|
19750
|
-
if (!succeededKeys.has(k)) matchedLabels.delete(k);
|
|
19751
|
-
}
|
|
19752
|
-
const cleanFilled = filled.filter((f) => f !== null);
|
|
19753
|
-
const unmatched = resolveUnmatched(normalizedValues, matchedLabels, values);
|
|
19754
|
-
const out = replacements.size > 0 ? patchZipEntries(u8, replacements) : new Uint8Array(u8);
|
|
19755
|
-
return {
|
|
19756
|
-
buffer: out.buffer.slice(out.byteOffset, out.byteOffset + out.byteLength),
|
|
19757
|
-
filled: cleanFilled,
|
|
19758
|
-
unmatched
|
|
19759
|
-
};
|
|
19760
|
-
}
|
|
19761
|
-
|
|
19762
|
-
// src/hwpx/generator.ts
|
|
19763
|
-
import JSZip6 from "jszip";
|
|
19764
|
-
|
|
19765
|
-
// src/hwpx/gaejosik.ts
|
|
19766
|
-
var GAEJOSIK_BULLETS = ["\u25A1", "\u25CB", "-", "\u318D"];
|
|
19767
|
-
function gaejosikMarker(depth, bullet2 = "\u25CB") {
|
|
19768
|
-
if (depth === 1) return bullet2;
|
|
19769
|
-
return GAEJOSIK_BULLETS[Math.min(depth, GAEJOSIK_BULLETS.length - 1)];
|
|
19770
|
-
}
|
|
19771
|
-
function gaejosikSizes(bodyHeight, ov = {}) {
|
|
19772
|
-
const s = bodyHeight / 1500;
|
|
19773
|
-
const r = (v, pt) => pt ? Math.round(pt * 100) : Math.round(v * s);
|
|
19774
|
-
return {
|
|
19775
|
-
/** □ 대항목 — HY헤드라인M 16pt */
|
|
19776
|
-
dae: r(1600, ov.dae),
|
|
19777
|
-
/** ※ 참고 — 한양중고딕 13pt */
|
|
19778
|
-
cham: r(1300, ov.cham),
|
|
19779
|
-
/** 장 헤더(로마숫자·제목) 17pt */
|
|
19780
|
-
chapter: r(1700, ov.chapter),
|
|
19781
|
-
/** 표지 제목 30pt */
|
|
19782
|
-
coverTitle: r(3e3, ov.coverTitle),
|
|
19783
|
-
/** 표지 날짜·기관명 25pt */
|
|
19784
|
-
coverSub: r(2500, ov.coverSub),
|
|
19785
|
-
/** 목차 라벨 "목 차" 28pt */
|
|
19786
|
-
tocLabel: r(2800, ov.tocLabel),
|
|
19787
|
-
/** 목차 로마숫자 21pt(한양신명조) */
|
|
19788
|
-
tocRoman: r(2100, ov.tocRoman),
|
|
19789
|
-
/** 목차 항목 18pt */
|
|
19790
|
-
tocItem: r(1800, ov.tocItem),
|
|
19791
|
-
/** 표 셀 — 맑은 고딕 12pt (실측: 정부 양식 표) */
|
|
19792
|
-
table: r(1200, ov.table),
|
|
19793
|
-
/** 표지 장식 바 셀 빈 문단 — 6pt (실측: 바 높이 818 안에 줄높이 수납) */
|
|
19794
|
-
bar: r(600),
|
|
19795
|
-
/** 본문 첫 페이지 제목 박스 — HY헤드라인M 22pt (실측: GT3 표④) */
|
|
19796
|
-
bodyTitle: r(2200, ov.bodyTitle)
|
|
19797
|
-
};
|
|
19798
|
-
}
|
|
19799
|
-
function gaejosikSpaceBefore(depth, bodyHeight) {
|
|
19800
|
-
const s = bodyHeight / 1500;
|
|
19801
|
-
const ui = [3e3, 2e3, 1200, 600][Math.min(depth, 3)];
|
|
19802
|
-
return Math.round(ui * s);
|
|
19803
|
-
}
|
|
19804
|
-
function gaejosikLevelIndent(depth, bodyHeight, sizes = {}, bullet2 = "\u25CB") {
|
|
19805
|
-
const lefts = [0, 1, 1.5, 2];
|
|
19806
|
-
const left = depth <= 3 ? Math.round(lefts[depth] * bodyHeight) : Math.round((2 + (depth - 3) * 0.5) * bodyHeight);
|
|
19807
|
-
const h = depth === 0 ? gaejosikSizes(bodyHeight, sizes).dae : bodyHeight;
|
|
19808
|
-
return { left, indent: -markerWidth(gaejosikMarker(depth, bullet2), h) };
|
|
19809
|
-
}
|
|
19810
|
-
function gaejosikChamIndent(bodyHeight, sizes = {}) {
|
|
19811
|
-
const cham = gaejosikSizes(bodyHeight, sizes).cham;
|
|
19812
|
-
return { left: Math.round(2.5 * cham), indent: -markerWidth("\u203B", cham) };
|
|
19813
|
-
}
|
|
19814
|
-
function gaejosikTocItemIndent(bodyHeight, sizes = {}) {
|
|
19815
|
-
const sz = gaejosikSizes(bodyHeight, sizes);
|
|
19816
|
-
const hang = markerWidth("\u2167.", sz.tocRoman);
|
|
19817
|
-
return { left: 2e3 + hang, indent: -hang };
|
|
19818
|
-
}
|
|
19819
|
-
function formatGaejosikDate(d) {
|
|
19820
|
-
return `${d.getFullYear()}. ${d.getMonth() + 1}. ${d.getDate()}.`;
|
|
19821
|
-
}
|
|
19822
|
-
function stripChapterNumber(title) {
|
|
19823
|
-
return title.replace(/^\s*(?:\d+|[ⅠⅡⅢⅣⅤⅥⅦⅧⅨⅩ]+)\s*[.)]\s*/u, "").trim();
|
|
19824
|
-
}
|
|
19825
|
-
var GAEJOSIK_COLORS = {
|
|
19826
|
-
/** 장헤더 로마숫자 셀·표지 진한 바 음영 */
|
|
19827
|
-
primary: "#193AAA",
|
|
19828
|
-
/** 장헤더 셀 테두리 */
|
|
19829
|
-
border: "#006699",
|
|
19830
|
-
/** 표지 연한 바 */
|
|
19831
|
-
accent: "#A0B4E6",
|
|
19832
|
-
/** 장헤더 제목 셀 음영 */
|
|
19833
|
-
titleFill: "#F2F2F2",
|
|
19834
|
-
/** 장헤더 제목 셀 상하선 */
|
|
19835
|
-
titleLine: "#A6A6A6",
|
|
19836
|
-
/** 목차 박스 테두리 */
|
|
19837
|
-
tocBorder: "#514BAC",
|
|
19838
|
-
/** 목차 배너 라벤더 스트라이프 (실측: GT3 표②) */
|
|
19839
|
-
tocStripe: "#E0E5FA"
|
|
19840
|
-
};
|
|
19841
|
-
var CHAPTER_GEOM = { numW: 3327, gapW: 848, titleW: 43513, rowH: 2832 };
|
|
19842
|
-
var COVER_GEOM = {
|
|
19843
|
-
totalW: 48180,
|
|
19844
|
-
topDarkW: 38219,
|
|
19845
|
-
topLightW: 9961,
|
|
19846
|
-
barH: 818,
|
|
19847
|
-
titleH: 11060,
|
|
19848
|
-
botDarkW: 10466,
|
|
19849
|
-
botLightW: 37714,
|
|
19850
|
-
botBarH: 812
|
|
19851
|
-
};
|
|
19852
|
-
var TOC_GEOM = { boxW: 46492, boxH: 57840 };
|
|
19853
|
-
function chapterGeom(bodyHeight, ov = {}) {
|
|
19854
|
-
const s = gaejosikSizes(bodyHeight, ov).chapter / 1700;
|
|
19855
|
-
return { ...CHAPTER_GEOM, rowH: Math.round(CHAPTER_GEOM.rowH * s) };
|
|
19856
|
-
}
|
|
19857
|
-
function coverGeom(bodyHeight, ov = {}) {
|
|
19858
|
-
const s = gaejosikSizes(bodyHeight, ov).coverTitle / 3e3;
|
|
19859
|
-
return { ...COVER_GEOM, titleH: Math.round(COVER_GEOM.titleH * s) };
|
|
19860
|
-
}
|
|
19861
|
-
var TOC_BANNER_GEOM = {
|
|
19862
|
-
colWs: [565, 565, 1414, 13191, 1414, 565, 565],
|
|
19863
|
-
h: 3931
|
|
19864
|
-
};
|
|
19865
|
-
function tocBannerGeom(bodyHeight, ov = {}) {
|
|
19866
|
-
const s = gaejosikSizes(bodyHeight, ov).tocLabel / 2800;
|
|
19867
|
-
return { colWs: TOC_BANNER_GEOM.colWs, h: Math.round(TOC_BANNER_GEOM.h * s) };
|
|
19868
|
-
}
|
|
19869
|
-
var BODY_TITLE_GEOM = { barH: 600, titleH: 3566 };
|
|
19870
|
-
function bodyTitleGeom(bodyHeight, ov = {}) {
|
|
19871
|
-
const s = gaejosikSizes(bodyHeight, ov).bodyTitle / 2200;
|
|
19872
|
-
return { barH: BODY_TITLE_GEOM.barH, titleH: Math.round(BODY_TITLE_GEOM.titleH * s) };
|
|
19873
|
-
}
|
|
19874
|
-
|
|
19875
|
-
// src/hwpx/gongmun.ts
|
|
19876
|
-
var OFFICIAL_MARGINS = { top: 20, bottom: 15, left: 20, right: 15 };
|
|
19877
|
-
var GAEJOSIK_MARGINS = { top: 15, bottom: 15, left: 20, right: 20 };
|
|
19878
|
-
var GAEJOSIK_HEADER_FOOTER = 4251;
|
|
19879
|
-
var PRESET_DEFAULTS = {
|
|
19880
|
-
// 서울시 실결재 104건 중 12pt 64건(13pt 30, 15pt 5) — 지배값을 기본으로 사용.
|
|
19881
|
-
official: { bodyPt: 12, lineSpacing: 160, numbering: "standard" },
|
|
19882
|
-
report: { bodyPt: 15, lineSpacing: 160, numbering: "report" },
|
|
19883
|
-
// 실측 추진계획안: □ → ㅇ → * 계층.
|
|
19884
|
-
plan: { bodyPt: 15, lineSpacing: 160, numbering: "report" },
|
|
19885
|
-
notice: { bodyPt: 15, lineSpacing: 160, numbering: "standard" },
|
|
19886
|
-
minutes: { bodyPt: 14, lineSpacing: 130, numbering: "standard" },
|
|
19887
|
-
gaejosik: { bodyPt: 15, lineSpacing: 160, numbering: "gaejosik" },
|
|
19888
|
-
// 보도자료 — 실측(국토부 실물): 본문 바탕 14pt 160%, □→ㅇ→*(각주) 부호
|
|
19889
|
-
press: { bodyPt: 14, lineSpacing: 160, numbering: "report" }
|
|
19890
|
-
};
|
|
19891
|
-
var PRESET_ALIAS = {
|
|
19892
|
-
official: "official",
|
|
19893
|
-
\uAE30\uC548\uBB38: "official",
|
|
19894
|
-
\uC2DC\uD589\uBB38: "official",
|
|
19895
|
-
\uACF5\uBB38: "official",
|
|
19896
|
-
\uACF5\uBB38\uC11C: "official",
|
|
19897
|
-
report: "report",
|
|
19898
|
-
\uBCF4\uACE0\uC11C: "report",
|
|
19899
|
-
plan: "plan",
|
|
19900
|
-
\uACC4\uD68D\uC11C: "plan",
|
|
19901
|
-
\uACC4\uD68D: "plan",
|
|
19902
|
-
notice: "notice",
|
|
19903
|
-
\uD1B5\uC9C0: "notice",
|
|
19904
|
-
\uC54C\uB9BC: "notice",
|
|
19905
|
-
\uC548\uB0B4: "notice",
|
|
19906
|
-
minutes: "minutes",
|
|
19907
|
-
\uD68C\uC758\uB85D: "minutes",
|
|
19908
|
-
gaejosik: "gaejosik",
|
|
19909
|
-
\uAC1C\uC870\uC2DD: "gaejosik",
|
|
19910
|
-
\uAC1C\uC870\uC2DD\uBCF4\uACE0\uC11C: "gaejosik",
|
|
19911
|
-
\uC815\uBD80\uBCF4\uACE0\uC11C: "gaejosik",
|
|
19912
|
-
\uC815\uBD80\uD45C\uC900\uAC1C\uC870\uC2DD\uBCF4\uACE0\uC11C: "gaejosik",
|
|
19913
|
-
press: "press",
|
|
19914
|
-
\uBCF4\uB3C4\uC790\uB8CC: "press"
|
|
19915
|
-
};
|
|
19916
|
-
function normalizeGongmunPreset(preset) {
|
|
19917
|
-
if (!preset) return "official";
|
|
19918
|
-
return PRESET_ALIAS[preset.trim()] ?? "official";
|
|
19919
|
-
}
|
|
19920
|
-
function usesReportFonts(preset) {
|
|
19921
|
-
return preset === "gaejosik" || preset === "report" || preset === "plan";
|
|
19922
|
-
}
|
|
19923
|
-
function usesAsteriskThird(preset) {
|
|
19924
|
-
return preset === "plan" || preset === "press";
|
|
19925
|
-
}
|
|
19926
|
-
function needsGaejosikAssets(gongmun) {
|
|
19927
|
-
return usesReportFonts(gongmun.preset) || gongmun.cover !== null || gongmun.toc;
|
|
19928
|
-
}
|
|
19929
|
-
function assertFiniteRange(name, value, min, max) {
|
|
19930
|
-
if (!Number.isFinite(value) || value < min || value > max) {
|
|
19931
|
-
throw new KordocError(`${name} must be a finite number between ${min} and ${max}`);
|
|
19932
|
-
}
|
|
19933
|
-
}
|
|
19934
|
-
function validateGongmunOptions(opts) {
|
|
19935
|
-
if (opts.bodyPt !== void 0) assertFiniteRange("bodyPt", opts.bodyPt, 6, 40);
|
|
19936
|
-
if (opts.lineSpacing !== void 0) assertFiniteRange("lineSpacing", opts.lineSpacing, 50, 300);
|
|
19937
|
-
if (typeof opts.autoFit === "object" && opts.autoFit.minRatio !== void 0) {
|
|
19938
|
-
assertFiniteRange("autoFit.minRatio", opts.autoFit.minRatio, 50, 99);
|
|
19939
|
-
}
|
|
19940
|
-
if (opts.margins) {
|
|
19941
|
-
for (const side of ["top", "bottom", "left", "right"]) {
|
|
19942
|
-
const value = opts.margins[side];
|
|
19943
|
-
assertFiniteRange(`margins.${side}`, value, 0, 200);
|
|
19944
|
-
}
|
|
19945
|
-
if (opts.margins.left + opts.margins.right >= 210) {
|
|
19946
|
-
throw new KordocError("margins.left + margins.right must be less than 210");
|
|
19947
|
-
}
|
|
19948
|
-
if (opts.margins.top + opts.margins.bottom >= 297) {
|
|
19949
|
-
throw new KordocError("margins.top + margins.bottom must be less than 297");
|
|
19950
|
-
}
|
|
19951
|
-
}
|
|
19952
|
-
if (opts.sizes) {
|
|
19953
|
-
for (const [key, value] of Object.entries(opts.sizes)) {
|
|
19954
|
-
if (value !== void 0) assertFiniteRange(`sizes.${key}`, value, 6, 60);
|
|
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)));
|
|
19955
20297
|
}
|
|
19956
20298
|
}
|
|
19957
|
-
|
|
19958
|
-
|
|
20299
|
+
for (const k of failedKeys) {
|
|
20300
|
+
if (!succeededKeys.has(k)) matchedLabels.delete(k);
|
|
19959
20301
|
}
|
|
19960
|
-
|
|
19961
|
-
|
|
19962
|
-
|
|
19963
|
-
const preset = normalizeGongmunPreset(opts.preset);
|
|
19964
|
-
const d = PRESET_DEFAULTS[preset];
|
|
19965
|
-
const bodyPt = opts.bodyPt ?? d.bodyPt;
|
|
19966
|
-
const autoFitMinRatio = opts.autoFit === false ? null : typeof opts.autoFit === "object" ? Math.min(Math.max(opts.autoFit.minRatio ?? 90, 50), 99) : 90;
|
|
19967
|
-
const coverOn = opts.cover !== void 0 ? opts.cover !== false : preset === "gaejosik";
|
|
19968
|
-
const coverOpts = typeof opts.cover === "object" ? opts.cover : {};
|
|
19969
|
-
const gaejosik = preset === "gaejosik";
|
|
19970
|
-
const reportFamily = gaejosik || preset === "report" || preset === "plan" || preset === "notice" || preset === "press";
|
|
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);
|
|
19971
20305
|
return {
|
|
19972
|
-
|
|
19973
|
-
|
|
19974
|
-
|
|
19975
|
-
lineSpacing: opts.lineSpacing ?? d.lineSpacing,
|
|
19976
|
-
numbering: opts.numbering ?? d.numbering,
|
|
19977
|
-
margins: opts.margins ?? (reportFamily ? GAEJOSIK_MARGINS : OFFICIAL_MARGINS),
|
|
19978
|
-
centerTitle: opts.centerTitle ?? true,
|
|
19979
|
-
autoFitMinRatio,
|
|
19980
|
-
// 보도자료는 머리박스가 1페이지 최상단을 차지하는 서식이라 표지·목차와 양립 불가 —
|
|
19981
|
-
// 켜면 머리박스가 표지에 얹히고 25pt 제목·부제가 유실된다 (docHead 프리셋 게이팅과 동일 관례)
|
|
19982
|
-
cover: coverOn && preset !== "press" ? { date: coverOpts.date ?? null, org: coverOpts.org ?? "" } : null,
|
|
19983
|
-
toc: preset !== "press" && (opts.toc ?? gaejosik),
|
|
19984
|
-
fonts: opts.fonts ?? {},
|
|
19985
|
-
sizes: opts.sizes ?? {},
|
|
19986
|
-
// 쪽번호 — 보고서 계열 관행(실측: 2_보고서 양식·추진계획·공고문 전부 하단 중앙)
|
|
19987
|
-
pageNumbers: opts.pageNumbers ?? (gaejosik || preset === "report" || preset === "plan"),
|
|
19988
|
-
// 머리말·꼬리말 — 실측: 보고서 계열 15mm(GT3·t2·춘천·브라더), 공고·보도 10mm,
|
|
19989
|
-
// 기안문 0(실결재 41/60건 h0/f0)
|
|
19990
|
-
headerFooter: usesReportFonts(preset) ? GAEJOSIK_HEADER_FOOTER : preset === "notice" || preset === "press" ? 2835 : 0,
|
|
19991
|
-
// "끝." — 기안문 규정(본문 끝 2타+"끝."). 그 외는 opt-in
|
|
19992
|
-
endMark: opts.endMark ?? preset === "official",
|
|
19993
|
-
approval: opts.approval && opts.approval.length > 0 ? opts.approval : null,
|
|
19994
|
-
// 본문 제목박스 — 실측(GT3·GT12): 목차 뒤 본문 시작에 제목 반복. 표지 켜진 개조식 기본
|
|
19995
|
-
bodyTitleBox: opts.bodyTitleBox ?? (gaejosik && coverOn),
|
|
19996
|
-
// h2 말머리 — 실측: 보고서 양식 □ 대항목(QA-2), 공고문 아라비아("1. 사업개요", 바이오헬스 실측)
|
|
19997
|
-
h2Marker: opts.h2Marker ?? (preset === "report" || preset === "plan" ? "box" : preset === "notice" ? "number" : "none"),
|
|
19998
|
-
// 2단계 부호 — 실결재 기안문·공고문 ㅇ 지배(60건 중 ㅇ134:○5), 보고서 양식 계열 ○
|
|
19999
|
-
bullet2: opts.bullet2 ?? (preset === "plan" || preset === "notice" || preset === "press" ? "\u3147" : "\u25CB"),
|
|
20000
|
-
// 단일 형제 부호 생략 — 규정이지만 부호 없는 계단이 실무 눈에 어색 (실무자 QA)
|
|
20001
|
-
suppressSingle: opts.suppressSingle ?? false,
|
|
20002
|
-
docHead: preset === "official" && opts.docHead ? opts.docHead : null,
|
|
20003
|
-
docFoot: preset === "official" && opts.docFoot ? opts.docFoot : null,
|
|
20004
|
-
reportInfo: opts.reportInfo?.trim() || null,
|
|
20005
|
-
noticeHead: preset === "notice" && opts.noticeHead ? opts.noticeHead : null,
|
|
20006
|
-
// 보도자료 머리박스는 프리셋 자체가 요구 — 옵션 미지정이어도 빈 객체
|
|
20007
|
-
press: preset === "press" ? opts.press ?? {} : null
|
|
20306
|
+
buffer: out.buffer.slice(out.byteOffset, out.byteOffset + out.byteLength),
|
|
20307
|
+
filled: cleanFilled,
|
|
20308
|
+
unmatched
|
|
20008
20309
|
};
|
|
20009
20310
|
}
|
|
20010
|
-
|
|
20011
|
-
|
|
20012
|
-
|
|
20013
|
-
const cols = HANGUL_INITIALS.length;
|
|
20014
|
-
const vowel = HANGUL_MEDIALS[Math.min(Math.floor(n / cols), HANGUL_MEDIALS.length - 1)];
|
|
20015
|
-
const init = HANGUL_INITIALS[n % cols];
|
|
20016
|
-
return String.fromCodePoint(44032 + init * 588 + vowel * 28);
|
|
20017
|
-
}
|
|
20018
|
-
function circledNumber(n) {
|
|
20019
|
-
return String.fromCodePoint(9312 + n % 20);
|
|
20020
|
-
}
|
|
20021
|
-
function circledHangul(n) {
|
|
20022
|
-
return String.fromCodePoint(12910 + n % 14);
|
|
20023
|
-
}
|
|
20024
|
-
var REPORT_BULLETS = ["\u25A1", "\u25CB", "-", "\u318D"];
|
|
20025
|
-
var ASTERISK_BULLETS = ["\u25A1", "\u25CB", "*", "\u318D"];
|
|
20026
|
-
function standardMarker(depth, n) {
|
|
20027
|
-
switch (depth) {
|
|
20028
|
-
case 0:
|
|
20029
|
-
return `${n + 1}.`;
|
|
20030
|
-
case 1:
|
|
20031
|
-
return `${hangulOrdinal(n)}.`;
|
|
20032
|
-
case 2:
|
|
20033
|
-
return `${n + 1})`;
|
|
20034
|
-
case 3:
|
|
20035
|
-
return `${hangulOrdinal(n)})`;
|
|
20036
|
-
case 4:
|
|
20037
|
-
return `(${n + 1})`;
|
|
20038
|
-
case 5:
|
|
20039
|
-
return `(${hangulOrdinal(n)})`;
|
|
20040
|
-
case 6:
|
|
20041
|
-
return circledNumber(n);
|
|
20042
|
-
case 7:
|
|
20043
|
-
return circledHangul(n);
|
|
20044
|
-
default:
|
|
20045
|
-
return circledHangul(n);
|
|
20046
|
-
}
|
|
20047
|
-
}
|
|
20048
|
-
function reportMarker(depth, bullet2 = "\u25CB", asteriskThird = false) {
|
|
20049
|
-
const bullets = asteriskThird ? ASTERISK_BULLETS : REPORT_BULLETS;
|
|
20050
|
-
const m = bullets[Math.min(depth, bullets.length - 1)];
|
|
20051
|
-
return depth === 1 ? bullet2 : m;
|
|
20052
|
-
}
|
|
20053
|
-
function markerWidth(marker, bodyHeight) {
|
|
20054
|
-
let em = SPACE_EM_FIXED;
|
|
20055
|
-
for (const c of marker) em += charWidthEm1000(c.codePointAt(0));
|
|
20056
|
-
return Math.round(em / 1e3 * bodyHeight);
|
|
20057
|
-
}
|
|
20058
|
-
function levelIndent(depth, bodyHeight, numbering, sizes = {}, bullet2 = "\u25CB", asteriskThird = false) {
|
|
20059
|
-
if (numbering === "gaejosik") return gaejosikLevelIndent(depth, bodyHeight, sizes, bullet2);
|
|
20060
|
-
const marker = numbering === "report" ? reportMarker(depth, bullet2, asteriskThird) : standardMarker(depth, 0);
|
|
20061
|
-
return { left: Math.round(depth * bodyHeight), indent: -markerWidth(marker, bodyHeight) };
|
|
20062
|
-
}
|
|
20063
|
-
function computeSuppression(depths) {
|
|
20064
|
-
const counts = /* @__PURE__ */ new Map();
|
|
20065
|
-
const keys = [];
|
|
20066
|
-
const path = [];
|
|
20067
|
-
for (const depth of depths) {
|
|
20068
|
-
path.length = depth + 1;
|
|
20069
|
-
path[depth] = (path[depth] ?? 0) + 1;
|
|
20070
|
-
const parentKey = path.slice(0, depth).join(".") + "|" + depth;
|
|
20071
|
-
keys.push(parentKey);
|
|
20072
|
-
counts.set(parentKey, (counts.get(parentKey) ?? 0) + 1);
|
|
20073
|
-
}
|
|
20074
|
-
return keys.map((k) => (counts.get(k) ?? 0) <= 1);
|
|
20075
|
-
}
|
|
20076
|
-
var GongmunNumberer = class {
|
|
20077
|
-
constructor(numbering, bullet2 = "\u25CB", asteriskThird = false) {
|
|
20078
|
-
this.numbering = numbering;
|
|
20079
|
-
this.bullet2 = bullet2;
|
|
20080
|
-
this.asteriskThird = asteriskThird;
|
|
20081
|
-
}
|
|
20082
|
-
counts = [];
|
|
20083
|
-
/** depth 항목 하나에 대한 마커. suppress=true면 빈 문자열(부호 없음) */
|
|
20084
|
-
next(depth, suppress) {
|
|
20085
|
-
this.counts.length = depth + 1;
|
|
20086
|
-
const n = this.counts[depth] ?? 0;
|
|
20087
|
-
this.counts[depth] = n + 1;
|
|
20088
|
-
if (suppress) return "";
|
|
20089
|
-
if (this.numbering === "gaejosik") return gaejosikMarker(depth, this.bullet2);
|
|
20090
|
-
return this.numbering === "report" ? reportMarker(depth, this.bullet2, this.asteriskThird) : standardMarker(depth, n);
|
|
20091
|
-
}
|
|
20092
|
-
reset() {
|
|
20093
|
-
this.counts = [];
|
|
20094
|
-
}
|
|
20095
|
-
};
|
|
20096
|
-
function mmToHwpunit(mm) {
|
|
20097
|
-
return Math.round(mm * 7200 / 25.4);
|
|
20098
|
-
}
|
|
20311
|
+
|
|
20312
|
+
// src/hwpx/generator.ts
|
|
20313
|
+
import JSZip6 from "jszip";
|
|
20099
20314
|
|
|
20100
20315
|
// src/hwpx/gen-ids.ts
|
|
20101
20316
|
var NS_SECTION = "http://www.hancom.co.kr/hwpml/2011/section";
|
|
@@ -20140,7 +20355,7 @@ function resolveTheme(theme) {
|
|
|
20140
20355
|
};
|
|
20141
20356
|
}
|
|
20142
20357
|
function escapeXml(text) {
|
|
20143
|
-
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, """);
|
|
20144
20359
|
}
|
|
20145
20360
|
function headingParaPrId(level) {
|
|
20146
20361
|
if (level === 1) return PARA_H1;
|
|
@@ -20159,7 +20374,7 @@ function charPr(id, height, bold, italic, fontId = 0, textColor = DEFAULT_TEXT_C
|
|
|
20159
20374
|
const italicAttr = italic ? ` italic="1"` : "";
|
|
20160
20375
|
const effFont = fontId;
|
|
20161
20376
|
const boldEl = bold ? `<hh:bold/>` : "";
|
|
20162
|
-
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}>
|
|
20163
20378
|
<hh:fontRef hangul="${effFont}" latin="${effFont}" hanja="${effFont}" japanese="${effFont}" other="${effFont}" symbol="${effFont}" user="${effFont}"/>
|
|
20164
20379
|
<hh:ratio hangul="${ratioPct}" latin="${ratioPct}" hanja="${ratioPct}" japanese="100" other="100" symbol="100" user="100"/>
|
|
20165
20380
|
<hh:spacing hangul="0" latin="0" hanja="0" japanese="0" other="0" symbol="0" user="0"/>
|
|
@@ -20205,7 +20420,7 @@ var GJ_CHAR_BAR = 24;
|
|
|
20205
20420
|
var GJ_CHAR_BODY_TITLE = 25;
|
|
20206
20421
|
var GJ_CHAR_TITLE_BAR = 26;
|
|
20207
20422
|
var GJ_CHAR_APPROVAL = 27;
|
|
20208
|
-
var GJ_CHAR_COUNT =
|
|
20423
|
+
var GJ_CHAR_COUNT = GJ_CHAR_APPROVAL - GJ_CHAR_DAE + 1;
|
|
20209
20424
|
var GJ_PARA_CHAM = 20;
|
|
20210
20425
|
var GJ_PARA_COVER = 21;
|
|
20211
20426
|
var GJ_PARA_TOC_ITEM = 22;
|
|
@@ -20213,6 +20428,7 @@ var GJ_PARA_CHAPTER = 23;
|
|
|
20213
20428
|
var GJ_PARA_BAR = 24;
|
|
20214
20429
|
var GONGMUN_LIST_PLAIN_BASE = 25;
|
|
20215
20430
|
var GONGMUN_PARA_APPROVAL = GONGMUN_LIST_PLAIN_BASE + GONGMUN_LIST_LEVELS;
|
|
20431
|
+
var GONGMUN_LIST_VARIANT_BASE = GONGMUN_PARA_APPROVAL + 1;
|
|
20216
20432
|
var GJ_BF_CHAPTER_NUM = 3;
|
|
20217
20433
|
var GJ_BF_CHAPTER_GAP = 4;
|
|
20218
20434
|
var GJ_BF_CHAPTER_TITLE = 5;
|
|
@@ -20256,10 +20472,10 @@ var GONGMUN_TBL_CHAR_BOLD = 12;
|
|
|
20256
20472
|
var GONGMUN_TBL_PT = 1200;
|
|
20257
20473
|
var GONGMUN_TITLE_BAR_CHAR = 13;
|
|
20258
20474
|
var GONGMUN_APPROVAL_CHAR = 14;
|
|
20259
|
-
var CHAR_VARIANT_BASE =
|
|
20475
|
+
var CHAR_VARIANT_BASE = CHAR_QUOTE + 1;
|
|
20260
20476
|
function charVariantBase(richAssets, isGongmun = true) {
|
|
20261
20477
|
if (!isGongmun) return CHAR_VARIANT_BASE;
|
|
20262
|
-
return richAssets ?
|
|
20478
|
+
return richAssets ? CHAR_VARIANT_BASE + GJ_CHAR_COUNT : CHAR_VARIANT_BASE + (GONGMUN_APPROVAL_CHAR - GONGMUN_TBL_CHAR + 1);
|
|
20263
20479
|
}
|
|
20264
20480
|
var GONGMUN_BODY_RATIO = 95;
|
|
20265
20481
|
|
|
@@ -20292,6 +20508,7 @@ function parseMarkdownToBlocks(md2) {
|
|
|
20292
20508
|
const lines = md2.replace(/\r\n?/g, "\n").split("\n");
|
|
20293
20509
|
const blocks = [];
|
|
20294
20510
|
let i = 0;
|
|
20511
|
+
const listStack = [];
|
|
20295
20512
|
while (i < lines.length) {
|
|
20296
20513
|
const line = lines[i];
|
|
20297
20514
|
if (!line.trim()) {
|
|
@@ -20380,7 +20597,8 @@ function parseMarkdownToBlocks(md2) {
|
|
|
20380
20597
|
const tableRows = [];
|
|
20381
20598
|
while (i < lines.length && lines[i].trimStart().startsWith("|")) {
|
|
20382
20599
|
const row = lines[i];
|
|
20383
|
-
|
|
20600
|
+
const sepCells = row.trim().replace(/^\|/, "").replace(/\|$/, "").split("|");
|
|
20601
|
+
if (sepCells.every((c) => /^\s*:?-+:?\s*$/.test(c))) {
|
|
20384
20602
|
i++;
|
|
20385
20603
|
continue;
|
|
20386
20604
|
}
|
|
@@ -20394,17 +20612,36 @@ function parseMarkdownToBlocks(md2) {
|
|
|
20394
20612
|
if (line.trimStart().startsWith("> ")) {
|
|
20395
20613
|
const quoteLines = [];
|
|
20396
20614
|
while (i < lines.length && (lines[i].trimStart().startsWith("> ") || lines[i].trimStart().startsWith(">"))) {
|
|
20397
|
-
quoteLines.push(lines[i].replace(/^>\s?/, ""));
|
|
20615
|
+
quoteLines.push(lines[i].replace(/^>\s?/, "").trim());
|
|
20398
20616
|
i++;
|
|
20399
20617
|
}
|
|
20618
|
+
let joined = [];
|
|
20400
20619
|
for (const ql of quoteLines) {
|
|
20401
|
-
|
|
20620
|
+
if (ql) {
|
|
20621
|
+
joined.push(ql);
|
|
20622
|
+
continue;
|
|
20623
|
+
}
|
|
20624
|
+
if (joined.length) blocks.push({ type: "blockquote", text: joined.join("\n") });
|
|
20625
|
+
joined = [];
|
|
20402
20626
|
}
|
|
20627
|
+
if (joined.length) blocks.push({ type: "blockquote", text: joined.join("\n") });
|
|
20403
20628
|
continue;
|
|
20404
20629
|
}
|
|
20405
|
-
const
|
|
20630
|
+
const listLine = line.replace(/^[\t ]+/, (ws) => ws.replace(/\t/g, " "));
|
|
20631
|
+
const listMatch = listLine.match(/^(\s*)([-*+]|\d+[.)]) (.+)$/);
|
|
20406
20632
|
if (listMatch) {
|
|
20407
|
-
|
|
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
|
+
}
|
|
20408
20645
|
const ordered = /\d/.test(listMatch[2]);
|
|
20409
20646
|
blocks.push({ type: "list_item", text: listMatch[3].trim(), ordered, indent, marker: listMatch[2] });
|
|
20410
20647
|
i++;
|
|
@@ -20425,7 +20662,7 @@ function parseInlineMarkdown(text) {
|
|
|
20425
20662
|
text = text.replace(/\[([^\]]*)\]\(([^)]*)\)/g, (_, t, u) => t || u);
|
|
20426
20663
|
text = text.replace(/~~([^~]+)~~/g, "$1");
|
|
20427
20664
|
const spans = [];
|
|
20428
|
-
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;
|
|
20429
20666
|
let lastIdx = 0;
|
|
20430
20667
|
for (const match of text.matchAll(regex)) {
|
|
20431
20668
|
const idx = match.index;
|
|
@@ -20491,14 +20728,20 @@ function generateContainerXml() {
|
|
|
20491
20728
|
</ocf:rootfiles>
|
|
20492
20729
|
</ocf:container>`;
|
|
20493
20730
|
}
|
|
20494
|
-
function generateManifest(chartParts = []) {
|
|
20731
|
+
function generateManifest(chartParts = [], imageItems = [], layout = "default") {
|
|
20495
20732
|
const chartItems = chartParts.map((p, i) => `
|
|
20496
20733
|
<opf:item id="chart${i + 1}" href="${p.name}" media-type="application/xml"/>`).join("");
|
|
20734
|
+
const imgItems = imageItems.map((x) => `
|
|
20735
|
+
${x}`).join("");
|
|
20497
20736
|
return `<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
|
|
20498
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>
|
|
20499
20742
|
<opf:manifest>
|
|
20500
20743
|
<opf:item id="header" href="Contents/header.xml" media-type="application/xml"/>
|
|
20501
|
-
<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}
|
|
20502
20745
|
</opf:manifest>
|
|
20503
20746
|
<opf:spine>
|
|
20504
20747
|
<opf:itemref idref="header" linear="no"/>
|
|
@@ -20506,6 +20749,17 @@ function generateManifest(chartParts = []) {
|
|
|
20506
20749
|
</opf:spine>
|
|
20507
20750
|
</opf:package>`;
|
|
20508
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
|
+
}
|
|
20509
20763
|
function buildCharProperties(theme, gongmun, ratioVariants = [], extraCharPrXmls = []) {
|
|
20510
20764
|
const measured = !!gongmun && usesReportFonts(gongmun.preset);
|
|
20511
20765
|
const richAssets = !!gongmun && needsGaejosikAssets(gongmun);
|
|
@@ -20513,8 +20767,8 @@ function buildCharProperties(theme, gongmun, ratioVariants = [], extraCharPrXmls
|
|
|
20513
20767
|
if (gongmun) {
|
|
20514
20768
|
body = gongmun.bodyHeight;
|
|
20515
20769
|
code = Math.max(body - 200, 900);
|
|
20516
|
-
h1 = gongmun.preset === "report" || gongmun.preset === "plan" ? 2e3 : 1700;
|
|
20517
|
-
h2 = 1600;
|
|
20770
|
+
h1 = Math.max(gongmun.preset === "report" || gongmun.preset === "plan" ? 2e3 : 1700, body);
|
|
20771
|
+
h2 = Math.max(1600, body);
|
|
20518
20772
|
h3 = body;
|
|
20519
20773
|
h4 = Math.min(h3, Math.max(body - 100, 1300));
|
|
20520
20774
|
}
|
|
@@ -20582,6 +20836,12 @@ function buildCharProperties(theme, gongmun, ratioVariants = [], extraCharPrXmls
|
|
|
20582
20836
|
// 결재란 라벨(맑은 고딕 10pt)
|
|
20583
20837
|
);
|
|
20584
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`
|
|
20843
|
+
);
|
|
20844
|
+
}
|
|
20585
20845
|
for (const r of ratioVariants) {
|
|
20586
20846
|
rows.push(
|
|
20587
20847
|
charPr(rows.length, body, false, false, bodyFont, theme.body, r),
|
|
@@ -20591,11 +20851,12 @@ function buildCharProperties(theme, gongmun, ratioVariants = [], extraCharPrXmls
|
|
|
20591
20851
|
);
|
|
20592
20852
|
}
|
|
20593
20853
|
rows.push(...extraCharPrXmls);
|
|
20854
|
+
assertSequentialIds(rows, "charPr", 0);
|
|
20594
20855
|
return `<hh:charProperties itemCnt="${rows.length}">
|
|
20595
20856
|
${rows.join("\n")}
|
|
20596
20857
|
</hh:charProperties>`;
|
|
20597
20858
|
}
|
|
20598
|
-
function buildParaProperties(gongmun) {
|
|
20859
|
+
function buildParaProperties(gongmun, listIndentVariants = []) {
|
|
20599
20860
|
if (!gongmun) {
|
|
20600
20861
|
const base2 = [
|
|
20601
20862
|
paraPr(0, { keepWord: true }),
|
|
@@ -20607,6 +20868,7 @@ function buildParaProperties(gongmun) {
|
|
|
20607
20868
|
paraPr(6, { align: "LEFT", lineSpacing: 150, indent: 600, keepWord: true }),
|
|
20608
20869
|
paraPr(7, { align: "LEFT", lineSpacing: 160, indent: 600, keepWord: true })
|
|
20609
20870
|
];
|
|
20871
|
+
assertSequentialIds(base2, "paraPr", 0);
|
|
20610
20872
|
return `<hh:paraProperties itemCnt="${base2.length}">
|
|
20611
20873
|
${base2.join("\n")}
|
|
20612
20874
|
</hh:paraProperties>`;
|
|
@@ -20653,6 +20915,14 @@ ${base2.join("\n")}
|
|
|
20653
20915
|
base.push(paraPr(GONGMUN_LIST_PLAIN_BASE + d, { align: "JUSTIFY", lineSpacing: ls, left, indent: 0, spaceBefore: sectionGap, keepWord: true }));
|
|
20654
20916
|
}
|
|
20655
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);
|
|
20656
20926
|
return `<hh:paraProperties itemCnt="${base.length}">
|
|
20657
20927
|
${base.join("\n")}
|
|
20658
20928
|
</hh:paraProperties>`;
|
|
@@ -20673,7 +20943,10 @@ function fontEntry(id, face, weight = 6) {
|
|
|
20673
20943
|
<hh:typeInfo familyType="FCAT_GOTHIC" weight="${weight}" proportion="4" contrast="0" strokeVariation="1" armStyle="1" letterform="1" midline="1" xHeight="1"/>
|
|
20674
20944
|
</hh:font>`;
|
|
20675
20945
|
}
|
|
20676
|
-
function
|
|
20946
|
+
function staticFontNext(gongmun) {
|
|
20947
|
+
return gongmun && needsGaejosikAssets(gongmun) ? 8 : 3;
|
|
20948
|
+
}
|
|
20949
|
+
function buildFontFaces(gongmun, bodyFace, extraFonts = []) {
|
|
20677
20950
|
if (gongmun && needsGaejosikAssets(gongmun)) {
|
|
20678
20951
|
const ov = gongmun.fonts;
|
|
20679
20952
|
const bodyDefault = usesReportFonts(gongmun.preset) ? gongmun.preset !== "gaejosik" && gongmun.bodyFont === "gothic" ? "\uB9D1\uC740 \uACE0\uB515" : "\uD734\uBA3C\uBA85\uC870" : bodyFace;
|
|
@@ -20687,21 +20960,27 @@ function buildFontFaces(gongmun, bodyFace) {
|
|
|
20687
20960
|
"\uD55C\uC591\uC2E0\uBA85\uC870",
|
|
20688
20961
|
ov.table ?? "\uB9D1\uC740 \uACE0\uB515"
|
|
20689
20962
|
];
|
|
20690
|
-
|
|
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");
|
|
20691
20967
|
const langs = ["HANGUL", "LATIN", "HANJA", "JAPANESE", "OTHER", "SYMBOL", "USER"];
|
|
20692
20968
|
return `<hh:fontfaces itemCnt="${langs.length}">
|
|
20693
|
-
` + langs.map((l) => ` <hh:fontface lang="${l}" fontCnt="${faces.length}">
|
|
20969
|
+
` + langs.map((l) => ` <hh:fontface lang="${l}" fontCnt="${faces.length + extraFonts.length}">
|
|
20694
20970
|
${list}
|
|
20695
20971
|
</hh:fontface>`).join("\n") + `
|
|
20696
20972
|
</hh:fontfaces>`;
|
|
20697
20973
|
}
|
|
20974
|
+
const extraH = extraFonts.map((f, i) => fontEntry(3 + i, f)).join("\n");
|
|
20975
|
+
const extraBlock = extraH ? `
|
|
20976
|
+
${extraH}` : "";
|
|
20698
20977
|
return `<hh:fontfaces itemCnt="7">
|
|
20699
|
-
<hh:fontface lang="HANGUL" fontCnt="3">
|
|
20978
|
+
<hh:fontface lang="HANGUL" fontCnt="${3 + extraFonts.length}">
|
|
20700
20979
|
${fontEntry(0, bodyFace)}
|
|
20701
20980
|
${fontEntry(1, "\uD568\uCD08\uB86C\uB3CB\uC6C0")}
|
|
20702
|
-
${fontEntry(2, "HY\uACAC\uACE0\uB515", 9)}
|
|
20981
|
+
${fontEntry(2, "HY\uACAC\uACE0\uB515", 9)}${extraBlock}
|
|
20703
20982
|
</hh:fontface>
|
|
20704
|
-
<hh:fontface lang="LATIN" fontCnt="3">
|
|
20983
|
+
<hh:fontface lang="LATIN" fontCnt="${3 + extraFonts.length}">
|
|
20705
20984
|
<hh:font id="0" face="Times New Roman" type="TTF" isEmbedded="0">
|
|
20706
20985
|
<hh:typeInfo familyType="FCAT_OLDSTYLE" weight="5" proportion="4" contrast="2" strokeVariation="0" armStyle="0" letterform="0" midline="0" xHeight="4"/>
|
|
20707
20986
|
</hh:font>
|
|
@@ -20710,7 +20989,7 @@ ${fontEntry(2, "HY\uACAC\uACE0\uB515", 9)}
|
|
|
20710
20989
|
</hh:font>
|
|
20711
20990
|
<hh:font id="2" face="Arial Black" type="TTF" isEmbedded="0">
|
|
20712
20991
|
<hh:typeInfo familyType="FCAT_GOTHIC" weight="9" proportion="0" contrast="0" strokeVariation="0" armStyle="0" letterform="0" midline="0" xHeight="0"/>
|
|
20713
|
-
</hh:font
|
|
20992
|
+
</hh:font>${extraBlock}
|
|
20714
20993
|
</hh:fontface>
|
|
20715
20994
|
<hh:fontface lang="HANJA" fontCnt="1">
|
|
20716
20995
|
${fontEntry(0, "\uD568\uCD08\uB86C\uBC14\uD0D5")}
|
|
@@ -20729,7 +21008,7 @@ ${fontEntry(0, "\uAD74\uB9BC")}
|
|
|
20729
21008
|
</hh:fontface>
|
|
20730
21009
|
</hh:fontfaces>`;
|
|
20731
21010
|
}
|
|
20732
|
-
function
|
|
21011
|
+
function staticBorderFillItems(gongmun) {
|
|
20733
21012
|
const thin = ["0.12 mm", "#000000"];
|
|
20734
21013
|
const items = [
|
|
20735
21014
|
borderFillEntry(1, {}),
|
|
@@ -20760,7 +21039,14 @@ function buildBorderFills(gongmun, extra = []) {
|
|
|
20760
21039
|
if (gongmun) {
|
|
20761
21040
|
items.push(borderFillEntry(items.length + 1, { l: thin, r: thin, t: thin, b: thin }, "#E6E6E6"));
|
|
20762
21041
|
}
|
|
20763
|
-
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);
|
|
20764
21050
|
return `<hh:borderFills itemCnt="${items.length}">
|
|
20765
21051
|
${items.join("\n")}
|
|
20766
21052
|
</hh:borderFills>`;
|
|
@@ -20778,15 +21064,15 @@ function buildStyles(gongmun) {
|
|
|
20778
21064
|
${items.join("\n ")}
|
|
20779
21065
|
</hh:styles>`;
|
|
20780
21066
|
}
|
|
20781
|
-
function generateHeaderXml(theme, gongmun, ratioVariants = [], extraBorderFills = [], extraCharPrXmls = []) {
|
|
21067
|
+
function generateHeaderXml(theme, gongmun, ratioVariants = [], extraBorderFills = [], extraCharPrXmls = [], listIndentVariants = [], extraFonts = []) {
|
|
20782
21068
|
const bodyFace = gongmun?.fonts.body ?? (gongmun?.bodyFont === "gothic" ? "\uB9D1\uC740 \uACE0\uB515" : "\uD568\uCD08\uB86C\uBC14\uD0D5");
|
|
20783
21069
|
const charPropsXml = buildCharProperties(theme, gongmun, ratioVariants, extraCharPrXmls);
|
|
20784
|
-
const paraPropsXml = buildParaProperties(gongmun);
|
|
21070
|
+
const paraPropsXml = buildParaProperties(gongmun, gongmun ? listIndentVariants : []);
|
|
20785
21071
|
return `<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
|
|
20786
21072
|
<hh:head xmlns:hh="${NS_HEAD}" xmlns:hp="${NS_PARA}" xmlns:hc="${NS_CORE}" version="1.4" secCnt="1">
|
|
20787
21073
|
<hh:beginNum page="1" footnote="1" endnote="1" pic="1" tbl="1" equation="1"/>
|
|
20788
21074
|
<hh:refList>
|
|
20789
|
-
${buildFontFaces(gongmun, bodyFace)}
|
|
21075
|
+
${buildFontFaces(gongmun, bodyFace, extraFonts)}
|
|
20790
21076
|
${buildBorderFills(gongmun, extraBorderFills)}
|
|
20791
21077
|
${charPropsXml}
|
|
20792
21078
|
<hh:tabProperties itemCnt="0"/>
|
|
@@ -20860,7 +21146,6 @@ function chartSpec(t) {
|
|
|
20860
21146
|
const key = CHART_ALIAS[t.toLowerCase()] ?? Number(t);
|
|
20861
21147
|
return CHART_TYPES[key] ?? CHART_TYPES[0];
|
|
20862
21148
|
}
|
|
20863
|
-
var HU_PER_MM = 7200 / 25.4;
|
|
20864
21149
|
var RESERVED_KEYS = /* @__PURE__ */ new Set(["type", "cat", "size", "colors", "point_colors", "title"]);
|
|
20865
21150
|
function parseChartFence(text) {
|
|
20866
21151
|
let type;
|
|
@@ -21019,7 +21304,7 @@ function plainRenderText(text) {
|
|
|
21019
21304
|
function computeGongmunFitPlan(blocks, gongmun, gongmunList) {
|
|
21020
21305
|
const minRatio = gongmun.autoFitMinRatio;
|
|
21021
21306
|
if (minRatio === null || minRatio >= GONGMUN_BODY_RATIO) return null;
|
|
21022
|
-
const pageW =
|
|
21307
|
+
const pageW = A4_W_HU - mmToHwpunit(gongmun.margins.left) - mmToHwpunit(gongmun.margins.right);
|
|
21023
21308
|
const ratioByBlock = /* @__PURE__ */ new Map();
|
|
21024
21309
|
const variants = [];
|
|
21025
21310
|
for (let i = 0; i < blocks.length; i++) {
|
|
@@ -21027,14 +21312,14 @@ function computeGongmunFitPlan(blocks, gongmun, gongmunList) {
|
|
|
21027
21312
|
let text;
|
|
21028
21313
|
let firstW;
|
|
21029
21314
|
let contW;
|
|
21030
|
-
if (block.type === "list_item" && gongmunList.has(i)) {
|
|
21031
|
-
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);
|
|
21032
21317
|
if (gongmun.numbering === "gaejosik" && (depth === 0 || (block.text || "").trimStart().startsWith("\u203B"))) continue;
|
|
21033
21318
|
const content = plainRenderText(block.text || "");
|
|
21034
21319
|
text = marker ? `${marker} ${content}` : content;
|
|
21035
21320
|
const li = levelIndent(depth, gongmun.bodyHeight, gongmun.numbering, gongmun.sizes, gongmun.bullet2, usesAsteriskThird(gongmun.preset));
|
|
21036
21321
|
const left = li.left;
|
|
21037
|
-
const indent = marker ? li.indent : 0;
|
|
21322
|
+
const indent = marker ? indentVariant !== void 0 ? -gongmunList.indentVariants[indentVariant].widthHu : li.indent : 0;
|
|
21038
21323
|
firstW = pageW - left - Math.max(indent, 0);
|
|
21039
21324
|
contW = pageW - left - Math.max(-indent, 0);
|
|
21040
21325
|
} else if (block.type === "paragraph") {
|
|
@@ -21060,9 +21345,55 @@ function variantMapper(fit, blockIdx, base = CHAR_VARIANT_BASE) {
|
|
|
21060
21345
|
const vi = fit.variants.indexOf(r);
|
|
21061
21346
|
return (id) => id >= 0 && id <= 3 ? base + vi * 4 + id : id;
|
|
21062
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
|
+
}
|
|
21063
21377
|
function precomputeGongmunList(blocks, gongmun) {
|
|
21064
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
|
+
};
|
|
21065
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);
|
|
21066
21397
|
let i = 0;
|
|
21067
21398
|
while (i < blocks.length) {
|
|
21068
21399
|
if (blocks[i].type !== "list_item") {
|
|
@@ -21089,14 +21420,21 @@ function precomputeGongmunList(blocks, gongmun) {
|
|
|
21089
21420
|
break;
|
|
21090
21421
|
}
|
|
21091
21422
|
const depths = run.map((bi) => Math.min(Math.max((blocks[bi].indent || 0) + depthOffset, 0), GONGMUN_LIST_LEVELS - 1));
|
|
21092
|
-
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);
|
|
21093
21428
|
const numberer = new GongmunNumberer(gongmun.numbering, gongmun.bullet2, usesAsteriskThird(gongmun.preset));
|
|
21094
21429
|
run.forEach((bi, k) => {
|
|
21095
|
-
|
|
21096
|
-
|
|
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]) });
|
|
21097
21435
|
});
|
|
21098
21436
|
}
|
|
21099
|
-
return result;
|
|
21437
|
+
return { items: result, indentVariants };
|
|
21100
21438
|
}
|
|
21101
21439
|
|
|
21102
21440
|
// src/hwpx/gen-gaejosik.ts
|
|
@@ -21114,26 +21452,29 @@ function emptyPara(paraPrId = PARA_NORMAL, pageBreak = false, charPrId = CHAR_NO
|
|
|
21114
21452
|
const brk = pageBreak ? ` pageBreak="1"` : "";
|
|
21115
21453
|
return `<hp:p paraPrIDRef="${paraPrId}" styleIDRef="0"${brk}><hp:run charPrIDRef="${charPrId}"><hp:t></hp:t></hp:run></hp:p>`;
|
|
21116
21454
|
}
|
|
21117
|
-
var gjTableId =
|
|
21455
|
+
var gjTableId = GJ_TABLE_ID_BASE;
|
|
21456
|
+
function resetGjTableIds() {
|
|
21457
|
+
gjTableId = GJ_TABLE_ID_BASE;
|
|
21458
|
+
}
|
|
21118
21459
|
function cell(opts) {
|
|
21119
21460
|
const paras = opts.paras ?? emptyPara();
|
|
21120
|
-
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>`;
|
|
21121
21462
|
}
|
|
21122
|
-
function table(rows, w, h, cols, tblBf = 1) {
|
|
21123
|
-
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;
|
|
21124
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>`;
|
|
21125
21466
|
}
|
|
21126
|
-
function buildGaejosikCover(title, gongmun) {
|
|
21127
|
-
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);
|
|
21128
21469
|
const sz = gaejosikSizes(gongmun.bodyHeight, gongmun.sizes);
|
|
21129
21470
|
const innerW = g.totalW - 282;
|
|
21130
21471
|
const titleCharPr = simulateWrap(title, innerW, innerW, sz.coverTitle, 100, "keep").lines > 2 ? GJ_CHAR_COVER_SUB : GJ_CHAR_COVER_TITLE;
|
|
21131
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>`;
|
|
21132
21473
|
const barEmpty = emptyPara(GJ_PARA_BAR, false, GJ_CHAR_BAR);
|
|
21133
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 });
|
|
21134
|
-
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" });
|
|
21135
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 });
|
|
21136
|
-
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);
|
|
21137
21478
|
const host = `<hp:p paraPrIDRef="${PARA_NORMAL}" styleIDRef="0"><hp:run charPrIDRef="${CHAR_NORMAL}">${tbl2}</hp:run></hp:p>`;
|
|
21138
21479
|
const date = gongmun.cover?.date ?? formatGaejosikDate(/* @__PURE__ */ new Date());
|
|
21139
21480
|
const org = gongmun.cover?.org ?? "";
|
|
@@ -21147,48 +21488,51 @@ function buildGaejosikCover(title, gongmun) {
|
|
|
21147
21488
|
...org ? [...Array(4).fill(subEmpty), sub(org)] : []
|
|
21148
21489
|
];
|
|
21149
21490
|
}
|
|
21150
|
-
function buildTocBanner(gongmun) {
|
|
21491
|
+
function buildTocBanner(gongmun, bodyWidth = GAEJOSIK_BASE_WIDTH) {
|
|
21151
21492
|
const g = tocBannerGeom(gongmun.bodyHeight, gongmun.sizes);
|
|
21152
21493
|
const bfs = [GJ_BF_BAR_DARK, 1, GJ_BF_TOC_STRIPE, 1, GJ_BF_TOC_STRIPE, 1, GJ_BF_BAR_DARK];
|
|
21153
21494
|
const filler = emptyPara(GJ_PARA_BAR, false, GJ_CHAR_BAR);
|
|
21154
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>`;
|
|
21155
21496
|
const row = g.colWs.map(
|
|
21156
|
-
(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 })
|
|
21157
21498
|
).join("");
|
|
21158
|
-
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);
|
|
21159
21500
|
return `<hp:p paraPrIDRef="${GJ_PARA_BAR}" styleIDRef="0" pageBreak="1"><hp:run charPrIDRef="${CHAR_NORMAL}">${banner}</hp:run></hp:p>`;
|
|
21160
21501
|
}
|
|
21161
|
-
function buildGaejosikToc(chapters, gongmun) {
|
|
21162
|
-
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);
|
|
21163
21505
|
const items = chapters.map(
|
|
21164
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>`
|
|
21165
21507
|
).join("");
|
|
21166
21508
|
const box = table(
|
|
21167
|
-
[cell({ bf: GJ_BF_TOC_BOX, col: 0, w:
|
|
21168
|
-
|
|
21509
|
+
[cell({ bf: GJ_BF_TOC_BOX, col: 0, w: boxW, h: TOC_GEOM.boxH, paras: items, name: "__kordoc_toc" })],
|
|
21510
|
+
boxW,
|
|
21169
21511
|
TOC_GEOM.boxH,
|
|
21170
|
-
1
|
|
21512
|
+
1,
|
|
21513
|
+
1,
|
|
21514
|
+
bodyWidth
|
|
21171
21515
|
);
|
|
21172
21516
|
const host = `<hp:p paraPrIDRef="${GJ_PARA_BAR}" styleIDRef="0"><hp:run charPrIDRef="${CHAR_NORMAL}">${box}</hp:run></hp:p>`;
|
|
21173
21517
|
return [label, emptyPara(GJ_PARA_BAR, false, GJ_CHAR_BAR), host];
|
|
21174
21518
|
}
|
|
21175
|
-
function buildGaejosikBodyTitle(title, gongmun) {
|
|
21176
|
-
const g =
|
|
21519
|
+
function buildGaejosikBodyTitle(title, gongmun, bodyWidth = GAEJOSIK_BASE_WIDTH) {
|
|
21520
|
+
const g = coverGeom(gongmun.bodyHeight, gongmun.sizes, bodyWidth);
|
|
21177
21521
|
const bt = bodyTitleGeom(gongmun.bodyHeight, gongmun.sizes);
|
|
21178
21522
|
const barEmpty = emptyPara(GJ_PARA_BAR, false, GJ_CHAR_BAR);
|
|
21179
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>`;
|
|
21180
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 });
|
|
21181
|
-
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" });
|
|
21182
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 });
|
|
21183
|
-
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);
|
|
21184
21528
|
return `<hp:p paraPrIDRef="${PARA_NORMAL}" styleIDRef="0"><hp:run charPrIDRef="${CHAR_NORMAL}">${box}</hp:run></hp:p>`;
|
|
21185
21529
|
}
|
|
21186
|
-
function buildGaejosikChapter(n, title, gongmun) {
|
|
21187
|
-
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);
|
|
21188
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>`;
|
|
21189
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>`;
|
|
21190
|
-
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 });
|
|
21191
|
-
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);
|
|
21192
21536
|
return `<hp:p paraPrIDRef="${GJ_PARA_CHAPTER}" styleIDRef="0"><hp:run charPrIDRef="${CHAR_NORMAL}">${tbl2}</hp:run></hp:p>`;
|
|
21193
21537
|
}
|
|
21194
21538
|
|
|
@@ -21556,16 +21900,22 @@ var AUTONUM_PREFIX_RE = /^(?:[0-90-9a-zA-Z가-힣]{1,6}[.)\]:]|[([][0-90-
|
|
|
21556
21900
|
function htmlCellInnerToLines(inner) {
|
|
21557
21901
|
let hadNonText = false;
|
|
21558
21902
|
let work = inner;
|
|
21903
|
+
const imgSrcs = [];
|
|
21559
21904
|
if (/<table[\s>]/i.test(work)) {
|
|
21560
21905
|
hadNonText = true;
|
|
21561
21906
|
work = removeNestedTables(work);
|
|
21562
21907
|
}
|
|
21563
21908
|
if (/<img\s/i.test(work)) {
|
|
21564
21909
|
hadNonText = true;
|
|
21565
|
-
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
|
+
});
|
|
21566
21916
|
}
|
|
21567
21917
|
const lines = work.split(/<br\s*\/?>/gi).map((s) => s.trim()).filter((s) => s.length > 0);
|
|
21568
|
-
return { lines, hadNonText };
|
|
21918
|
+
return { lines, hadNonText, imgSrcs };
|
|
21569
21919
|
}
|
|
21570
21920
|
function extractTopLevelTables(html) {
|
|
21571
21921
|
const result = [];
|
|
@@ -21660,13 +22010,19 @@ function dataCellSpec(opts) {
|
|
|
21660
22010
|
function normalizeAnchor(s) {
|
|
21661
22011
|
return s.toLowerCase().replace(/[^\p{L}\p{N}]/gu, "").slice(0, 24);
|
|
21662
22012
|
}
|
|
21663
|
-
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 = "") {
|
|
21664
22018
|
if (!remap) return null;
|
|
21665
22019
|
for (const t of remap.tables) {
|
|
21666
22020
|
if (t.used) continue;
|
|
21667
22021
|
if (t.rows !== rows || t.cols !== cols) continue;
|
|
21668
22022
|
if (t.anchor && anchor) {
|
|
21669
22023
|
if (t.anchor !== anchor) continue;
|
|
22024
|
+
} else if (t.anchorRow && rowAnchor) {
|
|
22025
|
+
if (t.anchorRow !== rowAnchor) continue;
|
|
21670
22026
|
} else if (t.index !== seq) {
|
|
21671
22027
|
continue;
|
|
21672
22028
|
}
|
|
@@ -21681,10 +22037,10 @@ function parseHu(s) {
|
|
|
21681
22037
|
return Number.isFinite(n) ? n : void 0;
|
|
21682
22038
|
}
|
|
21683
22039
|
function edgeXml(tag, d) {
|
|
21684
|
-
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"/>`;
|
|
21685
22041
|
}
|
|
21686
22042
|
function borderFillDefToXml(id, def) {
|
|
21687
|
-
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>` : "";
|
|
21688
22044
|
return ` <hh:borderFill id="${id}" threeD="0" shadow="0" centerLine="NONE" breakCellSeparateLine="0">
|
|
21689
22045
|
<hh:slash type="NONE" Crooked="0" isCounter="0"/>
|
|
21690
22046
|
<hh:backSlash type="NONE" Crooked="0" isCounter="0"/>
|
|
@@ -21695,28 +22051,45 @@ function borderFillDefToXml(id, def) {
|
|
|
21695
22051
|
${fill}` : ""}
|
|
21696
22052
|
</hh:borderFill>`;
|
|
21697
22053
|
}
|
|
21698
|
-
var
|
|
21699
|
-
function profileCharPrXml(id, def) {
|
|
21700
|
-
const height = parseHu(def.height_hwpunit) ?? 1e3;
|
|
21701
|
-
const color = def.textColor ?? "#000000";
|
|
21702
|
-
|
|
21703
|
-
|
|
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
|
+
}
|
|
21704
22067
|
const boldAttr = def.bold ? ` bold="1"` : "";
|
|
21705
22068
|
const italicAttr = def.italic ? ` italic="1"` : "";
|
|
21706
22069
|
const underline = def.underline ? `
|
|
21707
22070
|
<hh:underline type="BOTTOM" shape="SOLID" color="${color}"/>` : "";
|
|
21708
22071
|
return ` <hh:charPr id="${id}" height="${height}" textColor="${color}" shadeColor="none" useFontSpace="0" useKerning="0" symMark="NONE" borderFillIDRef="1"${boldAttr}${italicAttr}>
|
|
21709
|
-
<hh:fontRef hangul="${font}" latin="${font}" hanja="${
|
|
22072
|
+
<hh:fontRef hangul="${font}" latin="${font}" hanja="${restFont}" japanese="${restFont}" other="${restFont}" symbol="${restFont}" user="${restFont}"/>
|
|
21710
22073
|
<hh:ratio hangul="100" latin="100" hanja="100" japanese="100" other="100" symbol="100" user="100"/>
|
|
21711
22074
|
<hh:spacing hangul="0" latin="0" hanja="0" japanese="0" other="0" symbol="0" user="0"/>
|
|
21712
22075
|
<hh:relSz hangul="100" latin="100" hanja="100" japanese="100" other="100" symbol="100" user="100"/>
|
|
21713
22076
|
<hh:offset hangul="0" latin="0" hanja="0" japanese="0" other="0" symbol="0" user="0"/>${underline}
|
|
21714
22077
|
</hh:charPr>`;
|
|
21715
22078
|
}
|
|
21716
|
-
function buildProfileRemap(profile, charPrBase, borderFillBase = 3) {
|
|
21717
|
-
const remap = { borderFillXmls: [], charPrXmls: [], tables: [] };
|
|
22079
|
+
function buildProfileRemap(profile, charPrBase, borderFillBase = 3, fontBase = 3) {
|
|
22080
|
+
const remap = { borderFillXmls: [], charPrXmls: [], fontFaces: [], tables: [] };
|
|
21718
22081
|
let bfNext = borderFillBase;
|
|
21719
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
|
+
};
|
|
21720
22093
|
for (const t of profile.tables) {
|
|
21721
22094
|
const localBf = {};
|
|
21722
22095
|
for (const [key, def] of Object.entries(t.used_border_fills ?? {})) {
|
|
@@ -21727,7 +22100,7 @@ function buildProfileRemap(profile, charPrBase, borderFillBase = 3) {
|
|
|
21727
22100
|
const localChar = {};
|
|
21728
22101
|
for (const [key, def] of Object.entries(t.used_char_prs ?? {})) {
|
|
21729
22102
|
const gid = charNext++;
|
|
21730
|
-
remap.charPrXmls.push(profileCharPrXml(gid, def));
|
|
22103
|
+
remap.charPrXmls.push(profileCharPrXml(gid, def, def.fontName_hangul ? fontIdOf(def.fontName_hangul) : void 0));
|
|
21731
22104
|
localChar[key] = gid;
|
|
21732
22105
|
}
|
|
21733
22106
|
let colWidths;
|
|
@@ -21741,6 +22114,7 @@ function buildProfileRemap(profile, charPrBase, borderFillBase = 3) {
|
|
|
21741
22114
|
cols: t.cols,
|
|
21742
22115
|
// 재정규화 — 추출기는 정규화해 담지만 손편집된 프로필 JSON도 같은 키 공간으로
|
|
21743
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,
|
|
21744
22118
|
width: parseHu(t.width_hwpunit),
|
|
21745
22119
|
colWidths,
|
|
21746
22120
|
cellBf: /* @__PURE__ */ new Map(),
|
|
@@ -21758,17 +22132,208 @@ function buildProfileRemap(profile, charPrBase, borderFillBase = 3) {
|
|
|
21758
22132
|
const h = parseHu(cell2.height_hwpunit);
|
|
21759
22133
|
if (h != null) tr.cellH.set(k, h);
|
|
21760
22134
|
}
|
|
21761
|
-
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;
|
|
21762
22306
|
}
|
|
21763
|
-
|
|
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 };
|
|
21764
22326
|
}
|
|
21765
22327
|
|
|
21766
22328
|
// src/hwpx/gen-table.ts
|
|
21767
|
-
var TABLE_ID_BASE =
|
|
22329
|
+
var TABLE_ID_BASE = DATA_TABLE_ID_BASE;
|
|
21768
22330
|
var tableIdCounter = TABLE_ID_BASE;
|
|
21769
22331
|
function nextTableId() {
|
|
21770
22332
|
return ++tableIdCounter;
|
|
21771
22333
|
}
|
|
22334
|
+
function resetTableIds() {
|
|
22335
|
+
tableIdCounter = TABLE_ID_BASE;
|
|
22336
|
+
}
|
|
21772
22337
|
var DATA_TABLE_INSET = 1800;
|
|
21773
22338
|
function anchorOfMarkdownCell(cell2) {
|
|
21774
22339
|
return normalizeAnchor(cell2.replace(/!\[[^\]]*\]\([^)]*\)/g, ""));
|
|
@@ -21787,7 +22352,7 @@ function profileColWidths(tp, colCnt) {
|
|
|
21787
22352
|
var CELL_PAD = 1200;
|
|
21788
22353
|
function cellContentWidth(text, charHeight) {
|
|
21789
22354
|
let max = 0;
|
|
21790
|
-
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)) {
|
|
21791
22356
|
const w = measureTextWidth(seg.trim(), charHeight, 100);
|
|
21792
22357
|
if (w > max) max = w;
|
|
21793
22358
|
}
|
|
@@ -21795,7 +22360,7 @@ function cellContentWidth(text, charHeight) {
|
|
|
21795
22360
|
}
|
|
21796
22361
|
function cellMinWordWidth(text, charHeight) {
|
|
21797
22362
|
let max = 0;
|
|
21798
|
-
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)) {
|
|
21799
22364
|
for (const word of seg.trim().split(/\s+/)) {
|
|
21800
22365
|
const w = measureTextWidth(word, charHeight, 100);
|
|
21801
22366
|
if (w > max) max = w;
|
|
@@ -21825,7 +22390,7 @@ function computeColWidths(colMax, totalWidth, colMinWord = []) {
|
|
|
21825
22390
|
const sumFreeRaw = [...free].reduce((a, i) => a + raw[i], 0);
|
|
21826
22391
|
if (budget > sumFreeRaw * 1.6) {
|
|
21827
22392
|
const sumRaw = raw.reduce((a, b) => a + b, 0);
|
|
21828
|
-
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)));
|
|
21829
22394
|
free.clear();
|
|
21830
22395
|
} else {
|
|
21831
22396
|
for (; ; ) {
|
|
@@ -21846,7 +22411,23 @@ function computeColWidths(colMax, totalWidth, colMinWord = []) {
|
|
|
21846
22411
|
}
|
|
21847
22412
|
let rem = totalWidth - widths.reduce((a, b) => a + b, 0);
|
|
21848
22413
|
const order = [...raw.keys()].sort((a, b) => raw[b] - raw[a]);
|
|
21849
|
-
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
|
+
}
|
|
21850
22431
|
return widths;
|
|
21851
22432
|
}
|
|
21852
22433
|
function estimateRowHeight(cells, widths, charHeight) {
|
|
@@ -21861,13 +22442,14 @@ function estimateRowHeight(cells, widths, charHeight) {
|
|
|
21861
22442
|
});
|
|
21862
22443
|
return maxLines * Math.round(charHeight * 1.6) + 282;
|
|
21863
22444
|
}
|
|
21864
|
-
function generateTable(rows, theme, style = null, remap = null, seq = 0) {
|
|
22445
|
+
function generateTable(rows, theme, style = null, remap = null, seq = 0, images = null) {
|
|
21865
22446
|
const rowCnt = rows.length;
|
|
21866
22447
|
const colCnt = Math.max(...rows.map((r) => r.length), 1);
|
|
21867
22448
|
const reg = style?.bfRegistry ?? null;
|
|
21868
22449
|
const totalW = style ? reg ? style.totalWidth - DATA_TABLE_INSET : style.totalWidth : 44e3;
|
|
21869
22450
|
const measureH = style?.charHeight ?? 1e3;
|
|
21870
|
-
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);
|
|
21871
22453
|
const colMax = Array(colCnt).fill(0);
|
|
21872
22454
|
const colMaxBody = Array(colCnt).fill(0);
|
|
21873
22455
|
const colMinWord = Array(colCnt).fill(0);
|
|
@@ -21899,8 +22481,23 @@ function generateTable(rows, theme, style = null, remap = null, seq = 0) {
|
|
|
21899
22481
|
const centered = isHeaderRow || colCentered[colIdx];
|
|
21900
22482
|
const paraPrId = style ? centered ? style.tblCenterParaPr ?? style.centerParaPr : reg ? style.tblLeftParaPr ?? PARA_NORMAL : PARA_NORMAL : PARA_NORMAL;
|
|
21901
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
|
+
}
|
|
21902
22498
|
const runs = generateRuns(seg, cellCharPr, prof?.cellChar.has(k) ? void 0 : mapId);
|
|
21903
|
-
|
|
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>`;
|
|
21904
22501
|
}).join("");
|
|
21905
22502
|
const bf = prof?.cellBf.get(k) ?? (reg ? reg.get(dataCellSpec({
|
|
21906
22503
|
row: rowIdx,
|
|
@@ -21942,23 +22539,37 @@ function layoutHtmlRows(rows) {
|
|
|
21942
22539
|
colCnt = Math.max(colCnt, c);
|
|
21943
22540
|
}
|
|
21944
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);
|
|
21945
22551
|
return { placed, rowCnt: rows.length, colCnt };
|
|
21946
22552
|
}
|
|
21947
22553
|
function unescapeHtml(s) {
|
|
21948
22554
|
return s.replace(/</g, "<").replace(/>/g, ">").replace(/"/g, '"').replace(/'/g, "'").replace(/&/g, "&");
|
|
21949
22555
|
}
|
|
21950
|
-
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) {
|
|
21951
22557
|
const rows = parseHtmlTable(rawHtml);
|
|
21952
22558
|
if (!rows || rows.length === 0) return null;
|
|
21953
22559
|
const { placed, rowCnt, colCnt } = layoutHtmlRows(rows);
|
|
21954
22560
|
if (rowCnt === 0 || colCnt === 0) return null;
|
|
21955
22561
|
const measureH = style?.charHeight ?? 1e3;
|
|
21956
22562
|
const first = placed.find((p) => p.r === 0 && p.c === 0) ?? placed[0];
|
|
21957
|
-
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));
|
|
21958
22568
|
const colMax = Array(colCnt).fill(0);
|
|
21959
22569
|
const colMaxBody = Array(colCnt).fill(0);
|
|
21960
22570
|
const colMinWord = Array(colCnt).fill(0);
|
|
21961
|
-
const
|
|
22571
|
+
const cellParsed = placed.map((cell2) => htmlCellInnerToLines(cell2.inner));
|
|
22572
|
+
const cellLines = cellParsed.map((p) => p.lines);
|
|
21962
22573
|
placed.forEach((cell2, i) => {
|
|
21963
22574
|
const w = Math.max(...cellLines[i].map((l) => measureTextWidth(unescapeHtml(l).trim(), measureH, 100)), 0) / cell2.colSpan;
|
|
21964
22575
|
const mw = cell2.colSpan === 1 ? Math.max(...cellLines[i].map((l) => cellMinWordWidth(unescapeHtml(l), measureH)), 0) : 0;
|
|
@@ -21980,7 +22591,32 @@ function generateHtmlTableXml(rawHtml, theme, totalWidth = 44e3, style = null, r
|
|
|
21980
22591
|
let htmlHeaderRows = 0;
|
|
21981
22592
|
while (htmlHeaderRows < rows.length && rows[htmlHeaderRows].tag === "th") htmlHeaderRows++;
|
|
21982
22593
|
const tableRowHeights = Array(rowCnt).fill(cellH);
|
|
21983
|
-
const
|
|
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 }) => {
|
|
21984
22620
|
const k = `${cell2.r},${cell2.c}`;
|
|
21985
22621
|
const isHeader = cell2.isHeader;
|
|
21986
22622
|
const baseCharPr = style ? style.charPr : CHAR_NORMAL;
|
|
@@ -21989,31 +22625,42 @@ function generateHtmlTableXml(rawHtml, theme, totalWidth = 44e3, style = null, r
|
|
|
21989
22625
|
const charPrId = prof?.cellChar.get(k) ?? defaultCharPr;
|
|
21990
22626
|
const centered = isHeader || colCentered[cell2.c];
|
|
21991
22627
|
const paraPrId = style ? centered ? reg ? style.tblCenterParaPr ?? style.centerParaPr : style.centerParaPr : reg ? style.tblLeftParaPr ?? PARA_NORMAL : PARA_NORMAL : PARA_NORMAL;
|
|
21992
|
-
const
|
|
21993
|
-
const paras =
|
|
21994
|
-
|
|
21995
|
-
|
|
21996
|
-
|
|
21997
|
-
|
|
21998
|
-
|
|
21999
|
-
|
|
22000
|
-
|
|
22001
|
-
|
|
22002
|
-
|
|
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>`);
|
|
22003
22650
|
}
|
|
22004
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
|
+
}
|
|
22005
22655
|
if (paras.length === 0) {
|
|
22006
22656
|
paras.push(`<hp:p paraPrIDRef="${paraPrId}" styleIDRef="0"><hp:run charPrIDRef="${charPrId}"><hp:t></hp:t></hp:run></hp:p>`);
|
|
22007
22657
|
}
|
|
22008
|
-
|
|
22009
|
-
|
|
22010
|
-
const
|
|
22011
|
-
for (let r = cell2.r; r < Math.min(cell2.r + cell2.rowSpan, rowCnt); r++) {
|
|
22012
|
-
tableRowHeights[r] = Math.max(tableRowHeights[r], perRow);
|
|
22013
|
-
}
|
|
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;
|
|
22014
22661
|
const bf = prof?.cellBf.get(k) ?? (reg ? reg.get(dataCellSpec({
|
|
22015
22662
|
row: cell2.r,
|
|
22016
|
-
rowEnd
|
|
22663
|
+
rowEnd,
|
|
22017
22664
|
col: cell2.c,
|
|
22018
22665
|
colEnd: cell2.c + cell2.colSpan - 1,
|
|
22019
22666
|
rowCnt,
|
|
@@ -22021,7 +22668,7 @@ function generateHtmlTableXml(rawHtml, theme, totalWidth = 44e3, style = null, r
|
|
|
22021
22668
|
headerRows: htmlHeaderRows,
|
|
22022
22669
|
fill: isHeader ? style.headerFill ?? "#E6E6E6" : void 0
|
|
22023
22670
|
})) : style && isHeader ? style.headerBf : 2);
|
|
22024
|
-
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>`;
|
|
22025
22672
|
});
|
|
22026
22673
|
const trXmls = [];
|
|
22027
22674
|
for (let r = 0; r < rowCnt; r++) {
|
|
@@ -22033,12 +22680,15 @@ function generateHtmlTableXml(rawHtml, theme, totalWidth = 44e3, style = null, r
|
|
|
22033
22680
|
}
|
|
22034
22681
|
|
|
22035
22682
|
// src/hwpx/gen-gongmun-extra.ts
|
|
22036
|
-
var extraTableId =
|
|
22683
|
+
var extraTableId = EXTRA_TABLE_ID_BASE;
|
|
22684
|
+
function resetExtraTableIds() {
|
|
22685
|
+
extraTableId = EXTRA_TABLE_ID_BASE;
|
|
22686
|
+
}
|
|
22037
22687
|
function tbl(rows, w, h, cols) {
|
|
22038
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>`;
|
|
22039
22689
|
}
|
|
22040
22690
|
function tc(opts) {
|
|
22041
|
-
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>`;
|
|
22042
22692
|
}
|
|
22043
22693
|
function para(text, paraPrId, charPrId) {
|
|
22044
22694
|
return `<hp:p paraPrIDRef="${paraPrId}" styleIDRef="0"><hp:run charPrIDRef="${charPrId}"><hp:t>${escapeXml(text)}</hp:t></hp:run></hp:p>`;
|
|
@@ -22065,7 +22715,7 @@ function buildApprovalTable(labels, reg, approvalCharPr) {
|
|
|
22065
22715
|
return `<hp:p paraPrIDRef="${GONGMUN_RIGHT}" styleIDRef="0"><hp:run charPrIDRef="${CHAR_NORMAL}">${table2}</hp:run></hp:p>`;
|
|
22066
22716
|
}
|
|
22067
22717
|
function hasEndMark(lastText) {
|
|
22068
|
-
return
|
|
22718
|
+
return /(^|\s)끝\s*\.\s*$/.test(lastText.trim());
|
|
22069
22719
|
}
|
|
22070
22720
|
function buildEndMark() {
|
|
22071
22721
|
return para(" \uB05D.", PARA_NORMAL, CHAR_NORMAL);
|
|
@@ -22080,7 +22730,7 @@ function buildTitleBox(title, titleCharPr, barCharPr, bodyWidth, reg) {
|
|
|
22080
22730
|
const barPara = para("", GJ_PARA_BAR, barCharPr);
|
|
22081
22731
|
const rows = [
|
|
22082
22732
|
tc({ bf: barTop, row: 0, col: 0, w, h: TITLE_BOX.barH, paras: barPara }),
|
|
22083
|
-
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" }),
|
|
22084
22734
|
tc({ bf: barBottom, row: 2, col: 0, w, h: TITLE_BOX.barH, paras: barPara })
|
|
22085
22735
|
];
|
|
22086
22736
|
const table2 = tbl(rows, w, TITLE_BOX.barH * 2 + TITLE_BOX.titleH, 1);
|
|
@@ -22127,7 +22777,10 @@ function buildDocHead(g, ids) {
|
|
|
22127
22777
|
out.push(blank());
|
|
22128
22778
|
return out;
|
|
22129
22779
|
}
|
|
22130
|
-
|
|
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
|
+
}
|
|
22131
22784
|
function buildDocFoot(g, ids) {
|
|
22132
22785
|
const f = g.docFoot;
|
|
22133
22786
|
const out = [blank()];
|
|
@@ -22135,7 +22788,7 @@ function buildDocFoot(g, ids) {
|
|
|
22135
22788
|
out.push(para(f.sender, GONGMUN_CENTER, ids.sender), blank());
|
|
22136
22789
|
}
|
|
22137
22790
|
const foot = (text) => para(text, GONGMUN_TBL_LEFT, ids.foot);
|
|
22138
|
-
out.push(foot(
|
|
22791
|
+
out.push(foot(footSep(g)));
|
|
22139
22792
|
if (f.drafter || f.reviewer || f.approver)
|
|
22140
22793
|
out.push(foot(`\uAE30\uC548\uC790 ${f.drafter ?? ""} \uAC80\uD1A0\uC790 ${f.reviewer ?? ""} \uACB0\uC7AC\uAD8C\uC790 ${f.approver ?? ""}`));
|
|
22141
22794
|
if (f.cooperator) out.push(foot(`\uD611\uC870\uC790 ${f.cooperator}`));
|
|
@@ -22284,6 +22937,54 @@ var COMMAND_MAP = {
|
|
|
22284
22937
|
vdots: "VDOTS",
|
|
22285
22938
|
ddots: "DDOTS"
|
|
22286
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
|
+
}
|
|
22287
22988
|
var ACCENT_COMMANDS = {
|
|
22288
22989
|
bar: "bar",
|
|
22289
22990
|
overline: "bar",
|
|
@@ -22377,8 +23078,20 @@ function readCommand(input, idx, depth) {
|
|
|
22377
23078
|
const endIdx = input.indexOf(endTag, env.next);
|
|
22378
23079
|
if (endIdx === -1) return { value: env.value, next: env.next };
|
|
22379
23080
|
const body = convertLatexFragment(input.slice(env.next, endIdx), depth + 1);
|
|
22380
|
-
|
|
22381
|
-
|
|
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 };
|
|
22382
23095
|
}
|
|
22383
23096
|
return { value: body, next: endIdx + endTag.length };
|
|
22384
23097
|
}
|
|
@@ -22410,7 +23123,11 @@ function readCommand(input, idx, depth) {
|
|
|
22410
23123
|
const tok = readGroupOrToken(input, start, depth);
|
|
22411
23124
|
return { value: `"${tok.value}"`, next: tok.next };
|
|
22412
23125
|
}
|
|
22413
|
-
|
|
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 };
|
|
22414
23131
|
}
|
|
22415
23132
|
function convertLatexFragment(input, depth) {
|
|
22416
23133
|
if (depth > MAX_GROUP_DEPTH) return normalizeEqEdit(input);
|
|
@@ -22490,44 +23207,47 @@ function generateSecPr(gongmun) {
|
|
|
22490
23207
|
m.header = gongmun.headerFooter;
|
|
22491
23208
|
m.footer = gongmun.headerFooter;
|
|
22492
23209
|
}
|
|
22493
|
-
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>`;
|
|
22494
23211
|
const colPr = `<hp:ctrl><hp:colPr id="" type="NEWSPAPER" layout="LEFT" colCount="1" sameSz="1" sameGap="0"/></hp:ctrl>`;
|
|
22495
23212
|
return secPr + colPr + (gongmun?.pageNumbers ? pageNumCtrl() : "");
|
|
22496
23213
|
}
|
|
22497
|
-
|
|
22498
|
-
|
|
22499
|
-
|
|
22500
|
-
|
|
22501
|
-
|
|
22502
|
-
|
|
22503
|
-
|
|
22504
|
-
|
|
22505
|
-
|
|
22506
|
-
|
|
22507
|
-
|
|
22508
|
-
|
|
22509
|
-
|
|
22510
|
-
|
|
22511
|
-
|
|
22512
|
-
|
|
22513
|
-
|
|
22514
|
-
|
|
22515
|
-
|
|
22516
|
-
|
|
22517
|
-
|
|
22518
|
-
|
|
22519
|
-
|
|
22520
|
-
|
|
22521
|
-
|
|
22522
|
-
|
|
22523
|
-
|
|
22524
|
-
|
|
22525
|
-
|
|
22526
|
-
|
|
22527
|
-
|
|
22528
|
-
|
|
22529
|
-
|
|
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;
|
|
22530
23249
|
const preamble = [];
|
|
23250
|
+
let hasFrontPages = false;
|
|
22531
23251
|
if (gongmun?.reportInfo && dfIds) {
|
|
22532
23252
|
preamble.push(buildReportInfo(gongmun, dfIds));
|
|
22533
23253
|
}
|
|
@@ -22545,278 +23265,340 @@ function blocksToSectionXml(blocks, theme, gongmun, gongmunList = gongmun ? prec
|
|
|
22545
23265
|
}
|
|
22546
23266
|
if (gongmun && (gongmun.cover || gongmun.toc)) {
|
|
22547
23267
|
const h1Idx = blocks.findIndex((b) => b.type === "heading" && (b.level ?? 1) === 1);
|
|
22548
|
-
const chapters = blocks.filter((b) => b.type === "heading" && b.level === 2).map((b) => stripChapterNumber(b.text || ""));
|
|
22549
23268
|
let coverTitle = "";
|
|
22550
23269
|
const hide = (xml, hideHeader) => gongmun.pageNumbers ? xml.replace(/<hp:run charPrIDRef="(\d+)">/, `<hp:run charPrIDRef="$1">${pageHidingCtrl(hideHeader)}`) : xml;
|
|
22551
23270
|
if (gongmun.cover && h1Idx >= 0) {
|
|
22552
23271
|
coverTitle = (blocks[h1Idx].text || "").trim();
|
|
22553
|
-
const coverPart = buildGaejosikCover(coverTitle, gongmun);
|
|
23272
|
+
const coverPart = buildGaejosikCover(coverTitle, gongmun, gjBodyW);
|
|
22554
23273
|
coverPart[0] = hide(coverPart[0], true);
|
|
22555
23274
|
preamble.push(...coverPart);
|
|
22556
|
-
coverH1Idx = h1Idx;
|
|
23275
|
+
ctx.coverH1Idx = h1Idx;
|
|
22557
23276
|
hasFrontPages = true;
|
|
22558
23277
|
}
|
|
22559
|
-
|
|
22560
|
-
|
|
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);
|
|
22561
23282
|
tocPart[0] = hide(tocPart[0], false);
|
|
22562
23283
|
preamble.push(...tocPart);
|
|
22563
23284
|
hasFrontPages = true;
|
|
22564
23285
|
}
|
|
22565
23286
|
if (gongmun.bodyTitleBox && coverTitle && hasFrontPages) {
|
|
22566
|
-
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" `));
|
|
22567
23288
|
}
|
|
22568
23289
|
}
|
|
22569
|
-
if (gongmun && !gaejosik && coverH1Idx < 0 && bfReg && (gongmun.preset === "report" || gongmun.preset === "plan" || gongmun.preset === "notice")) {
|
|
22570
|
-
titleBoxH1Idx = blocks.findIndex((b) => b.type === "heading" && (b.level ?? 1) === 1);
|
|
22571
|
-
} else if (gongmun?.press && coverH1Idx < 0 && dfIds) {
|
|
22572
|
-
pressH1Idx = blocks.findIndex((b) => b.type === "heading" && (b.level ?? 1) === 1);
|
|
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);
|
|
22573
23294
|
}
|
|
22574
23295
|
if (preamble.length > 0) {
|
|
22575
|
-
preamble[0] = preamble[0].replace(` pageBreak="1"`, "")
|
|
22576
|
-
|
|
22577
|
-
|
|
22578
|
-
|
|
22579
|
-
|
|
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));
|
|
22580
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);
|
|
22581
23543
|
for (let blockIdx = 0; blockIdx < blocks.length; blockIdx++) {
|
|
22582
23544
|
const block = blocks[blockIdx];
|
|
22583
|
-
let xml = "";
|
|
22584
23545
|
if (block.type !== "list_item" || !block.ordered) {
|
|
22585
|
-
if (prevWasOrdered) {
|
|
22586
|
-
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];
|
|
22587
23548
|
}
|
|
22588
|
-
prevWasOrdered = false;
|
|
23549
|
+
ctx.prevWasOrdered = false;
|
|
22589
23550
|
}
|
|
23551
|
+
let xml = "";
|
|
22590
23552
|
switch (block.type) {
|
|
22591
|
-
case "heading":
|
|
22592
|
-
|
|
22593
|
-
if (gongmun && blockIdx === pressH1Idx && dfIds) {
|
|
22594
|
-
const parts = [generateParagraph((block.text || "").trim(), GONGMUN_CENTER, dfIds.pressTitle)];
|
|
22595
|
-
for (const s of gongmun.press?.sub ?? []) {
|
|
22596
|
-
parts.push(generateParagraph(`- ${s} -`, GONGMUN_CENTER, dfIds.pressSub));
|
|
22597
|
-
}
|
|
22598
|
-
xml = parts.join("\n ");
|
|
22599
|
-
break;
|
|
22600
|
-
}
|
|
22601
|
-
if (gongmun && blockIdx === titleBoxH1Idx && tableStyle && bfReg) {
|
|
22602
|
-
xml = buildTitleBox(
|
|
22603
|
-
(block.text || "").trim(),
|
|
22604
|
-
measured ? GJ_CHAR_BODY_TITLE : CHAR_H1,
|
|
22605
|
-
richAssets ? GJ_CHAR_TITLE_BAR : GONGMUN_TITLE_BAR_CHAR,
|
|
22606
|
-
tableStyle.totalWidth,
|
|
22607
|
-
bfReg
|
|
22608
|
-
);
|
|
22609
|
-
break;
|
|
22610
|
-
}
|
|
22611
|
-
if (gaejosik) {
|
|
22612
|
-
const lvl = block.level || 1;
|
|
22613
|
-
if (lvl <= 2) {
|
|
22614
|
-
chapterNo++;
|
|
22615
|
-
xml = buildGaejosikChapter(chapterNo, stripChapterNumber(block.text || ""), gongmun);
|
|
22616
|
-
} else if (lvl === 3) {
|
|
22617
|
-
xml = generateParagraph(
|
|
22618
|
-
`\u25A1 ${block.text || ""}`,
|
|
22619
|
-
GONGMUN_LIST_BASE,
|
|
22620
|
-
GJ_CHAR_DAE,
|
|
22621
|
-
(id) => id === CHAR_BOLD ? GJ_CHAR_DAE_BOLD : id
|
|
22622
|
-
);
|
|
22623
|
-
} else {
|
|
22624
|
-
xml = generateParagraph(`${gongmun.bullet2} ${block.text || ""}`, GONGMUN_LIST_BASE + 1, CHAR_NORMAL);
|
|
22625
|
-
}
|
|
22626
|
-
break;
|
|
22627
|
-
}
|
|
22628
|
-
const pId = headingParaPrId(block.level || 1);
|
|
22629
|
-
const cId = headingCharPrId(block.level || 1);
|
|
22630
|
-
const styleId = gongmun ? Math.min(block.level || 1, 4) : 0;
|
|
22631
|
-
let hText = block.text || "";
|
|
22632
|
-
if (gongmun && (block.level || 1) === 2 && gongmun.h2Marker !== "none") {
|
|
22633
|
-
const title = stripChapterNumber(hText);
|
|
22634
|
-
hText = gongmun.h2Marker === "box" ? `\u25A1 ${title}` : `${++h2Seq}. ${title}`;
|
|
22635
|
-
}
|
|
22636
|
-
xml = generateParagraph(hText, pId, cId, void 0, styleId);
|
|
23553
|
+
case "heading":
|
|
23554
|
+
xml = renderHeading(block, blockIdx, ctx);
|
|
22637
23555
|
break;
|
|
22638
|
-
|
|
22639
|
-
|
|
22640
|
-
const pTrim = (block.text || "").trimStart();
|
|
22641
|
-
if (measured && (pTrim.startsWith("\u203B") || /^\*\s/.test(pTrim))) {
|
|
22642
|
-
xml = generateParagraph((block.text || "").trim(), GJ_PARA_CHAM, GJ_CHAR_CHAM, chamMap);
|
|
22643
|
-
break;
|
|
22644
|
-
}
|
|
22645
|
-
const ctr = gongmun && /^<center>([\s\S]*)<\/center>$/i.exec((block.text || "").trim());
|
|
22646
|
-
const rgt = gongmun && /^<right>([\s\S]*)<\/right>$/i.exec((block.text || "").trim());
|
|
22647
|
-
if (ctr) {
|
|
22648
|
-
xml = generateParagraph(ctr[1].trim(), GONGMUN_CENTER);
|
|
22649
|
-
} else if (rgt) {
|
|
22650
|
-
xml = generateParagraph(rgt[1].trim(), GONGMUN_RIGHT);
|
|
22651
|
-
} else {
|
|
22652
|
-
xml = generateParagraph(block.text || "", PARA_NORMAL, CHAR_NORMAL, fit ? variantMapper(fit, blockIdx, vBase) : void 0);
|
|
22653
|
-
}
|
|
23556
|
+
case "paragraph":
|
|
23557
|
+
xml = renderParagraph(block, blockIdx, ctx);
|
|
22654
23558
|
break;
|
|
22655
|
-
|
|
22656
|
-
|
|
22657
|
-
if (chartParts !== null && (block.lang || "").toLowerCase() === "chart") {
|
|
22658
|
-
const fence = parseChartFence(block.text || "");
|
|
22659
|
-
if (fence) {
|
|
22660
|
-
const partName = `Chart/chart${chartParts.length + 1}.xml`;
|
|
22661
|
-
chartParts.push({ name: partName, xml: buildChartSpaceXml(fence) });
|
|
22662
|
-
const chartEl = buildChartElementXml(partName, fence.widthHu, fence.heightHu, 91e5 + blockIdx);
|
|
22663
|
-
if (isFirst) {
|
|
22664
|
-
const secRun = `<hp:run charPrIDRef="0">${generateSecPr(gongmun)}<hp:t></hp:t></hp:run>`;
|
|
22665
|
-
paraXmls.push(`<hp:p paraPrIDRef="0" styleIDRef="0">${secRun}</hp:p>`);
|
|
22666
|
-
isFirst = false;
|
|
22667
|
-
}
|
|
22668
|
-
xml = `<hp:p paraPrIDRef="${PARA_NORMAL}" styleIDRef="0"><hp:run charPrIDRef="${CHAR_NORMAL}">${chartEl}</hp:run></hp:p>`;
|
|
22669
|
-
break;
|
|
22670
|
-
}
|
|
22671
|
-
}
|
|
22672
|
-
const codeLines = (block.text || "").split("\n");
|
|
22673
|
-
xml = codeLines.map((line) => generateParagraph(line || " ", PARA_CODE)).join("\n ");
|
|
23559
|
+
case "code_block":
|
|
23560
|
+
xml = renderCodeBlock(block, blockIdx, ctx);
|
|
22674
23561
|
break;
|
|
22675
|
-
|
|
22676
|
-
|
|
22677
|
-
if (isFirst) {
|
|
22678
|
-
const secRun = `<hp:run charPrIDRef="0">${generateSecPr(gongmun)}<hp:t></hp:t></hp:run>`;
|
|
22679
|
-
paraXmls.push(`<hp:p paraPrIDRef="0" styleIDRef="0">${secRun}</hp:p>`);
|
|
22680
|
-
isFirst = false;
|
|
22681
|
-
}
|
|
22682
|
-
xml = generateEquationParagraph(block.text || "", blockIdx);
|
|
23562
|
+
case "equation":
|
|
23563
|
+
xml = renderEquation(block, blockIdx, ctx);
|
|
22683
23564
|
break;
|
|
22684
|
-
|
|
22685
|
-
|
|
22686
|
-
if (measured) {
|
|
22687
|
-
const t = (block.text || "").trim();
|
|
22688
|
-
if (t) xml = generateParagraph(t.startsWith("\u203B") ? t : `\u203B ${t}`, GJ_PARA_CHAM, GJ_CHAR_CHAM, chamMap);
|
|
22689
|
-
break;
|
|
22690
|
-
}
|
|
22691
|
-
xml = generateParagraph(
|
|
22692
|
-
block.text || "",
|
|
22693
|
-
PARA_QUOTE,
|
|
22694
|
-
theme.hasQuoteOption ? CHAR_QUOTE : CHAR_NORMAL
|
|
22695
|
-
);
|
|
23565
|
+
case "blockquote":
|
|
23566
|
+
xml = renderBlockquote(block, ctx);
|
|
22696
23567
|
break;
|
|
22697
|
-
|
|
22698
|
-
|
|
22699
|
-
if (gongmun && gongmunList) {
|
|
22700
|
-
const info = gongmunList.get(blockIdx);
|
|
22701
|
-
const depth = info?.depth ?? 0;
|
|
22702
|
-
const marker2 = info?.marker ?? "";
|
|
22703
|
-
const content = block.text || "";
|
|
22704
|
-
if (measured && content.trimStart().startsWith("\u203B")) {
|
|
22705
|
-
xml = generateParagraph(content.trim(), GJ_PARA_CHAM, GJ_CHAR_CHAM, chamMap);
|
|
22706
|
-
break;
|
|
22707
|
-
}
|
|
22708
|
-
if (measured && block.marker === "*") {
|
|
22709
|
-
xml = generateParagraph(`* ${content.trim()}`, GJ_PARA_CHAM, GJ_CHAR_CHAM, chamMap);
|
|
22710
|
-
break;
|
|
22711
|
-
}
|
|
22712
|
-
const text = marker2 ? `${marker2} ${content}` : content;
|
|
22713
|
-
const listParaPr = marker2 ? GONGMUN_LIST_BASE + depth : GONGMUN_LIST_PLAIN_BASE + depth;
|
|
22714
|
-
let listCharPr = gongmun.numbering === "report" && depth === 0 ? CHAR_BOLD : CHAR_NORMAL;
|
|
22715
|
-
let mapId = fit ? variantMapper(fit, blockIdx, vBase) : void 0;
|
|
22716
|
-
if (measured && depth === 0 && gongmun.numbering !== "standard") {
|
|
22717
|
-
listCharPr = GJ_CHAR_DAE;
|
|
22718
|
-
mapId = (id) => id === CHAR_BOLD ? GJ_CHAR_DAE_BOLD : id;
|
|
22719
|
-
}
|
|
22720
|
-
if (gongmun.preset === "press") {
|
|
22721
|
-
if (depth === 0) listCharPr = CHAR_NORMAL;
|
|
22722
|
-
if (depth >= 2 && dfIds) {
|
|
22723
|
-
listCharPr = dfIds.small;
|
|
22724
|
-
mapId = void 0;
|
|
22725
|
-
}
|
|
22726
|
-
}
|
|
22727
|
-
xml = generateParagraph(text, listParaPr, listCharPr, mapId);
|
|
22728
|
-
break;
|
|
22729
|
-
}
|
|
22730
|
-
const indent = block.indent || 0;
|
|
22731
|
-
let marker;
|
|
22732
|
-
if (block.marker) {
|
|
22733
|
-
marker = `${block.marker} `;
|
|
22734
|
-
prevWasOrdered = !!block.ordered;
|
|
22735
|
-
} else if (block.ordered) {
|
|
22736
|
-
orderedCounters[indent] = (orderedCounters[indent] || 0) + 1;
|
|
22737
|
-
for (const k of Object.keys(orderedCounters)) {
|
|
22738
|
-
if (+k > indent) delete orderedCounters[+k];
|
|
22739
|
-
}
|
|
22740
|
-
marker = `${orderedCounters[indent]}. `;
|
|
22741
|
-
prevWasOrdered = true;
|
|
22742
|
-
} else {
|
|
22743
|
-
marker = "\xB7 ";
|
|
22744
|
-
if (prevWasOrdered) {
|
|
22745
|
-
for (const k of Object.keys(orderedCounters)) delete orderedCounters[+k];
|
|
22746
|
-
}
|
|
22747
|
-
prevWasOrdered = false;
|
|
22748
|
-
}
|
|
22749
|
-
const indentPrefix = " ".repeat(indent);
|
|
22750
|
-
xml = generateParagraph(indentPrefix + marker + (block.text || ""), PARA_LIST);
|
|
23568
|
+
case "list_item":
|
|
23569
|
+
xml = renderListItem(block, blockIdx, ctx);
|
|
22751
23570
|
break;
|
|
22752
|
-
}
|
|
22753
23571
|
case "hr":
|
|
22754
|
-
xml =
|
|
23572
|
+
xml = renderHr(ctx);
|
|
22755
23573
|
break;
|
|
22756
23574
|
case "table":
|
|
22757
|
-
|
|
22758
|
-
if (isFirst) {
|
|
22759
|
-
const secRun = `<hp:run charPrIDRef="0">${generateSecPr(gongmun)}<hp:t></hp:t></hp:run>`;
|
|
22760
|
-
paraXmls.push(`<hp:p paraPrIDRef="0" styleIDRef="0">${secRun}</hp:p>`);
|
|
22761
|
-
isFirst = false;
|
|
22762
|
-
}
|
|
22763
|
-
xml = generateTable(block.rows, theme, tableStyle, remap, tableSeq++);
|
|
22764
|
-
}
|
|
23575
|
+
xml = renderTable(block, ctx);
|
|
22765
23576
|
break;
|
|
22766
|
-
case "html_table":
|
|
22767
|
-
|
|
22768
|
-
const tbl2 = generateHtmlTableXml(block.text || "", theme, htmlW, tableStyle, remap, tableSeq++);
|
|
22769
|
-
if (tbl2) {
|
|
22770
|
-
if (isFirst) {
|
|
22771
|
-
const secRun = `<hp:run charPrIDRef="0">${generateSecPr(gongmun)}<hp:t></hp:t></hp:run>`;
|
|
22772
|
-
paraXmls.push(`<hp:p paraPrIDRef="0" styleIDRef="0">${secRun}</hp:p>`);
|
|
22773
|
-
isFirst = false;
|
|
22774
|
-
}
|
|
22775
|
-
xml = `<hp:p paraPrIDRef="${tableStyle && bfReg ? GONGMUN_RIGHT : 0}" styleIDRef="0"><hp:run charPrIDRef="0">${tbl2}</hp:run></hp:p>`;
|
|
22776
|
-
} else {
|
|
22777
|
-
const plain = (block.text || "").replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
|
|
22778
|
-
xml = plain ? generateParagraph(plain) : "";
|
|
22779
|
-
}
|
|
23577
|
+
case "html_table":
|
|
23578
|
+
xml = renderHtmlTable(block, ctx);
|
|
22780
23579
|
break;
|
|
22781
|
-
}
|
|
22782
23580
|
}
|
|
22783
23581
|
if (!xml) continue;
|
|
22784
|
-
if (isFirst && block.type !== "table") {
|
|
22785
|
-
xml =
|
|
22786
|
-
/<hp:run charPrIDRef="(\d+)">/,
|
|
22787
|
-
`<hp:run charPrIDRef="$1">${generateSecPr(gongmun)}`
|
|
22788
|
-
);
|
|
22789
|
-
isFirst = false;
|
|
23582
|
+
if (ctx.opener.isFirst && block.type !== "table") {
|
|
23583
|
+
xml = ctx.opener.inject(xml);
|
|
22790
23584
|
}
|
|
22791
|
-
if (pendingPageBreak) {
|
|
23585
|
+
if (ctx.pendingPageBreak) {
|
|
22792
23586
|
xml = xml.replace(/^<hp:p /, `<hp:p pageBreak="1" `);
|
|
22793
|
-
pendingPageBreak = false;
|
|
23587
|
+
ctx.pendingPageBreak = false;
|
|
22794
23588
|
}
|
|
22795
|
-
if (pendingNewNum) {
|
|
23589
|
+
if (ctx.pendingNewNum) {
|
|
22796
23590
|
xml = xml.replace(/<hp:run charPrIDRef="(\d+)">/, `<hp:run charPrIDRef="$1">${newPageNumCtrl(1)}`);
|
|
22797
|
-
pendingNewNum = false;
|
|
23591
|
+
ctx.pendingNewNum = false;
|
|
22798
23592
|
}
|
|
22799
|
-
paraXmls.push(xml);
|
|
22800
|
-
}
|
|
22801
|
-
if (gongmun?.noticeHead && dfIds) {
|
|
22802
|
-
paraXmls.push(...buildNoticeFoot(gongmun));
|
|
22803
|
-
}
|
|
22804
|
-
if (gongmun?.endMark && paraXmls.length > 0) {
|
|
22805
|
-
const lastText = [...blocks].reverse().find((b) => (b.type === "paragraph" || b.type === "list_item") && (b.text || "").trim())?.text ?? "";
|
|
22806
|
-
if (!hasEndMark(lastText)) paraXmls.push(buildEndMark());
|
|
22807
|
-
}
|
|
22808
|
-
if (gongmun?.docFoot && dfIds) {
|
|
22809
|
-
paraXmls.push(...buildDocFoot(gongmun, dfIds));
|
|
22810
|
-
}
|
|
22811
|
-
if (gongmun?.press && dfIds && bfReg && tableStyle) {
|
|
22812
|
-
paraXmls.push(...buildPressContact(gongmun, dfIds, tableStyle.totalWidth, bfReg));
|
|
23593
|
+
ctx.paraXmls.push(xml);
|
|
22813
23594
|
}
|
|
22814
|
-
|
|
22815
|
-
|
|
23595
|
+
appendPostamble(blocks, ctx);
|
|
23596
|
+
if (ctx.paraXmls.length === 0) {
|
|
23597
|
+
ctx.paraXmls.push(ctx.opener.emptyDoc());
|
|
22816
23598
|
}
|
|
22817
23599
|
return `<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
|
|
22818
23600
|
<hs:sec xmlns:hs="${NS_SECTION}" xmlns:hp="${NS_PARA}">
|
|
22819
|
-
${paraXmls.join("\n ")}
|
|
23601
|
+
${ctx.paraXmls.join("\n ")}
|
|
22820
23602
|
</hs:sec>`;
|
|
22821
23603
|
}
|
|
22822
23604
|
|
|
@@ -22829,14 +23611,15 @@ async function markdownToHwpx(markdown, options) {
|
|
|
22829
23611
|
const blocks = parseMarkdownToBlocks(markdown);
|
|
22830
23612
|
const gongmunList = gongmun ? precomputeGongmunList(blocks, gongmun) : null;
|
|
22831
23613
|
const fit = gongmun && gongmunList ? computeGongmunFitPlan(blocks, gongmun, gongmunList) : null;
|
|
22832
|
-
const staticBfEnd = gongmun
|
|
22833
|
-
const remap = options?.profile ? buildProfileRemap(options.profile, charVariantBase(richAssets, !!gongmun) + (fit?.variants?.length ?? 0) * 4, staticBfEnd) : null;
|
|
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;
|
|
22834
23616
|
const bfReg = gongmun ? new TableBfRegistry(staticBfEnd + (remap?.borderFillXmls.length ?? 0)) : null;
|
|
22835
23617
|
const dfActive = !!gongmun && docframeActive(gongmun);
|
|
22836
23618
|
const dfBase = charVariantBase(richAssets, !!gongmun) + (fit?.variants?.length ?? 0) * 4 + (remap?.charPrXmls.length ?? 0);
|
|
22837
23619
|
const dfIds = dfActive ? docframeIds(dfBase) : null;
|
|
22838
23620
|
const chartParts = [];
|
|
22839
|
-
const
|
|
23621
|
+
const images = new ImageRegistry();
|
|
23622
|
+
const sectionXml = blocksToSectionXml(blocks, theme, gongmun, gongmunList, fit, chartParts, bfReg, remap, dfIds, images);
|
|
22840
23623
|
if (remap && remap.tables.length > 0) {
|
|
22841
23624
|
const unused = remap.tables.filter((t) => !t.used).length;
|
|
22842
23625
|
if (unused === remap.tables.length) {
|
|
@@ -22847,13 +23630,16 @@ async function markdownToHwpx(markdown, options) {
|
|
|
22847
23630
|
const zip = new JSZip6();
|
|
22848
23631
|
zip.file("mimetype", "application/hwp+zip", { compression: "STORE" });
|
|
22849
23632
|
zip.file("META-INF/container.xml", generateContainerXml());
|
|
22850
|
-
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);
|
|
22851
23635
|
zip.file("Contents/header.xml", generateHeaderXml(
|
|
22852
23636
|
theme,
|
|
22853
23637
|
gongmun,
|
|
22854
23638
|
fit?.variants ?? [],
|
|
22855
23639
|
extraBorderFills,
|
|
22856
|
-
[...remap?.charPrXmls ?? [], ...dfActive ? docframeCharPrXmls(dfBase, richAssets) : []]
|
|
23640
|
+
[...remap?.charPrXmls ?? [], ...dfActive ? docframeCharPrXmls(dfBase, richAssets) : []],
|
|
23641
|
+
gongmunList?.indentVariants ?? [],
|
|
23642
|
+
remap?.fontFaces ?? []
|
|
22857
23643
|
));
|
|
22858
23644
|
zip.file("Contents/section0.xml", sectionXml);
|
|
22859
23645
|
for (const part of chartParts) zip.file(part.name, part.xml);
|
|
@@ -23033,7 +23819,19 @@ function parseBorderFills(headerDoc) {
|
|
|
23033
23819
|
}
|
|
23034
23820
|
return map;
|
|
23035
23821
|
}
|
|
23036
|
-
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) {
|
|
23037
23835
|
const map = /* @__PURE__ */ new Map();
|
|
23038
23836
|
for (const cp of elemsByLocal(headerDoc, "charPr")) {
|
|
23039
23837
|
const id = cp.getAttribute("id");
|
|
@@ -23048,7 +23846,11 @@ function parseCharPrs(headerDoc) {
|
|
|
23048
23846
|
if (findChildByLocalName(cp, "underline")) def.underline = true;
|
|
23049
23847
|
const fontRef = findChildByLocalName(cp, "fontRef");
|
|
23050
23848
|
const hangul = fontRef?.getAttribute("hangul");
|
|
23051
|
-
if (hangul)
|
|
23849
|
+
if (hangul) {
|
|
23850
|
+
def.fontRef_hangul = hangul;
|
|
23851
|
+
const face = hangulFonts.get(hangul);
|
|
23852
|
+
if (face) def.fontName_hangul = face;
|
|
23853
|
+
}
|
|
23052
23854
|
map.set(id, def);
|
|
23053
23855
|
}
|
|
23054
23856
|
return map;
|
|
@@ -23076,6 +23878,8 @@ function parseTable3(tbl2, tableIndex, borderFills, charPrs) {
|
|
|
23076
23878
|
const usedBf = /* @__PURE__ */ new Set();
|
|
23077
23879
|
const usedCp = /* @__PURE__ */ new Set();
|
|
23078
23880
|
const colWidths = new Array(cols).fill(void 0);
|
|
23881
|
+
const spanCells = [];
|
|
23882
|
+
const row0Texts = /* @__PURE__ */ new Map();
|
|
23079
23883
|
let anchorText = "";
|
|
23080
23884
|
for (const tc2 of elemsByLocal(tbl2, "tc")) {
|
|
23081
23885
|
if (nearestTable(tc2) !== tbl2) continue;
|
|
@@ -23085,6 +23889,7 @@ function parseTable3(tbl2, tableIndex, borderFills, charPrs) {
|
|
|
23085
23889
|
const row = num(addr?.getAttribute("rowAddr")) ?? 0;
|
|
23086
23890
|
const col = num(addr?.getAttribute("colAddr")) ?? 0;
|
|
23087
23891
|
if (row === 0 && col === 0 && !anchorText) anchorText = directCellText(tc2);
|
|
23892
|
+
if (row === 0) row0Texts.set(col, directCellText(tc2));
|
|
23088
23893
|
const colSpan = num(span?.getAttribute("colSpan")) ?? 1;
|
|
23089
23894
|
const rowSpan = num(span?.getAttribute("rowSpan")) ?? 1;
|
|
23090
23895
|
const bfId = tc2.getAttribute("borderFillIDRef") || void 0;
|
|
@@ -23103,7 +23908,20 @@ function parseTable3(tbl2, tableIndex, borderFills, charPrs) {
|
|
|
23103
23908
|
usedCp.add(cpId);
|
|
23104
23909
|
}
|
|
23105
23910
|
cells.push(cell2);
|
|
23106
|
-
|
|
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;
|
|
23107
23925
|
}
|
|
23108
23926
|
const table2 = {
|
|
23109
23927
|
table_index: tableIndex,
|
|
@@ -23114,8 +23932,10 @@ function parseTable3(tbl2, tableIndex, borderFills, charPrs) {
|
|
|
23114
23932
|
};
|
|
23115
23933
|
const anchor = normalizeAnchor(anchorText);
|
|
23116
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;
|
|
23117
23937
|
if (width) table2.width_hwpunit = width;
|
|
23118
|
-
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);
|
|
23119
23939
|
const cp = pick(charPrs, usedCp);
|
|
23120
23940
|
if (Object.keys(cp).length) table2.used_char_prs = cp;
|
|
23121
23941
|
return table2;
|
|
@@ -23172,7 +23992,7 @@ async function hwpxToProfile(input) {
|
|
|
23172
23992
|
if (headerFile) headerXml = await headerFile.async("text");
|
|
23173
23993
|
const headerDoc = parser.parseFromString(headerXml, "text/xml");
|
|
23174
23994
|
const borderFills = parseBorderFills(headerDoc);
|
|
23175
|
-
const charPrs = parseCharPrs(headerDoc);
|
|
23995
|
+
const charPrs = parseCharPrs(headerDoc, parseHangulFonts(headerDoc));
|
|
23176
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));
|
|
23177
23997
|
const tables = [];
|
|
23178
23998
|
let tableIndex = 0;
|
|
@@ -23183,7 +24003,7 @@ async function hwpxToProfile(input) {
|
|
|
23183
24003
|
tables.push(parseTable3(tbl2, tableIndex++, borderFills, charPrs));
|
|
23184
24004
|
}
|
|
23185
24005
|
}
|
|
23186
|
-
return { schema_version: "0.
|
|
24006
|
+
return { schema_version: "0.3.0", tables };
|
|
23187
24007
|
}
|
|
23188
24008
|
|
|
23189
24009
|
// src/hwpx/font-catalog.ts
|
|
@@ -23323,7 +24143,7 @@ var COMMON_FREE = [
|
|
|
23323
24143
|
"\uC724\uBA85\uC870"
|
|
23324
24144
|
];
|
|
23325
24145
|
function norm(name) {
|
|
23326
|
-
return name.replace(/\s+/g, "").toLowerCase();
|
|
24146
|
+
return name.normalize("NFC").replace(/\s+/g, "").toLowerCase();
|
|
23327
24147
|
}
|
|
23328
24148
|
var KNOWN = new Set([...HANCOM_BUNDLED, ...WINDOWS_DEFAULT, ...COMMON_FREE].map(norm));
|
|
23329
24149
|
function isKnownFont(name) {
|
|
@@ -23353,7 +24173,7 @@ var RULES = [
|
|
|
23353
24173
|
{
|
|
23354
24174
|
code: "DATE_ZERO_PAD",
|
|
23355
24175
|
severity: "error",
|
|
23356
|
-
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,
|
|
23357
24177
|
message: "\uC6D4\xB7\uC77C \uC55E\uC758 '0'\uC740 \uD45C\uAE30\uD558\uC9C0 \uC54A\uC74C",
|
|
23358
24178
|
suggest: "\uC608) 2025. 1. 6. (2025. 01. 06. \u2715)"
|
|
23359
24179
|
},
|
|
@@ -23435,22 +24255,25 @@ var RULES = [
|
|
|
23435
24255
|
{
|
|
23436
24256
|
code: "COLON_SPACE",
|
|
23437
24257
|
severity: "warning",
|
|
23438
|
-
pattern: /\S\s
|
|
24258
|
+
pattern: /\S\s+:(?!\/\/)|\S:(?!\/\/)[^\s\d]/g,
|
|
23439
24259
|
message: "\uC30D\uC810\uC740 \uC55E\uB9D0\uC5D0 \uBD99\uC774\uACE0 \uB4A4\uB294 \uD55C \uCE78 \uB744\uC6C0",
|
|
23440
24260
|
suggest: "\uC608) \uC6D0\uC7A5: \uAE40\uAC11\uB3D9"
|
|
23441
24261
|
}
|
|
23442
24262
|
];
|
|
23443
24263
|
function lintGongmunText(text) {
|
|
23444
24264
|
const findings = [];
|
|
23445
|
-
let
|
|
24265
|
+
let fenceMarker = null;
|
|
23446
24266
|
const lines = text.split(/\r?\n/);
|
|
23447
24267
|
for (let i = 0; i < lines.length; i++) {
|
|
23448
24268
|
const line = lines[i];
|
|
23449
|
-
|
|
23450
|
-
|
|
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;
|
|
23451
24274
|
continue;
|
|
23452
24275
|
}
|
|
23453
|
-
if (
|
|
24276
|
+
if (fenceMarker !== null) continue;
|
|
23454
24277
|
for (const r of RULES) {
|
|
23455
24278
|
r.pattern.lastIndex = 0;
|
|
23456
24279
|
for (const m of line.matchAll(r.pattern)) {
|
|
@@ -26207,7 +27030,7 @@ async function parseHwp(buffer, options) {
|
|
|
26207
27030
|
async function parsePdf(buffer, options) {
|
|
26208
27031
|
let parsePdfDocument;
|
|
26209
27032
|
try {
|
|
26210
|
-
const mod = await import("./parser-
|
|
27033
|
+
const mod = await import("./parser-46JGSTPR.js");
|
|
26211
27034
|
parsePdfDocument = mod.parsePdfDocument;
|
|
26212
27035
|
} catch {
|
|
26213
27036
|
return {
|
|
@@ -26298,6 +27121,9 @@ async function fillForm(input, values, outputFormat = "markdown") {
|
|
|
26298
27121
|
}
|
|
26299
27122
|
|
|
26300
27123
|
export {
|
|
27124
|
+
PRESET_ALIAS,
|
|
27125
|
+
normalizeGongmunPreset,
|
|
27126
|
+
incompatibleGongmunWarnings,
|
|
26301
27127
|
extractHwpxMetadataOnly,
|
|
26302
27128
|
extractHwp5MetadataOnly,
|
|
26303
27129
|
formatFillValue,
|
|
@@ -26309,8 +27135,6 @@ export {
|
|
|
26309
27135
|
extractFormSchema,
|
|
26310
27136
|
fillFormFields,
|
|
26311
27137
|
fillHwpx,
|
|
26312
|
-
PRESET_ALIAS,
|
|
26313
|
-
normalizeGongmunPreset,
|
|
26314
27138
|
markdownToHwpx,
|
|
26315
27139
|
compare,
|
|
26316
27140
|
diffBlocks,
|
|
@@ -26339,4 +27163,4 @@ export {
|
|
|
26339
27163
|
parseHwpml,
|
|
26340
27164
|
fillForm
|
|
26341
27165
|
};
|
|
26342
|
-
//# sourceMappingURL=chunk-
|
|
27166
|
+
//# sourceMappingURL=chunk-MLNHZN32.js.map
|