kordoc 4.0.3 → 4.0.5
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 → -QVHGOOCR.js} +9 -7
- package/dist/chunk-5RPYBC2Q.js +88 -0
- package/dist/chunk-5RPYBC2Q.js.map +1 -0
- package/dist/{chunk-6JSZBSOD.js → chunk-6BABRVOJ.js} +2 -2
- package/dist/{chunk-PNQDQ3SM.js → chunk-6PED5OOG.js} +34 -4
- package/dist/chunk-6PED5OOG.js.map +1 -0
- package/dist/{chunk-OVESZY5R.cjs → chunk-6R5FY47W.cjs} +38 -8
- package/dist/chunk-6R5FY47W.cjs.map +1 -0
- package/dist/{chunk-RO2GVZ3I.js → chunk-6WDPB4SN.js} +3 -3
- package/dist/{chunk-TQYX5E32.js → chunk-FKK4K53F.js} +34 -4
- package/dist/chunk-FKK4K53F.js.map +1 -0
- package/dist/{chunk-MQLKSRIK.js → chunk-OMERK4AW.js} +76 -25
- package/dist/chunk-OMERK4AW.js.map +1 -0
- package/dist/{chunk-AUJICYJG.js → chunk-QK2G34WE.js} +4 -3
- package/dist/chunk-QK2G34WE.js.map +1 -0
- package/dist/{chunk-K2SETHJ6.js → chunk-Z6Q3YG22.js} +1632 -765
- package/dist/chunk-Z6Q3YG22.js.map +1 -0
- package/dist/chunk-ZGMMLYAL.js +92 -0
- package/dist/chunk-ZGMMLYAL.js.map +1 -0
- package/dist/cli.js +119 -72
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +21162 -20243
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +53 -1
- package/dist/index.d.ts +53 -1
- package/dist/index.js +20987 -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-2V5SPTU7.cjs} +106 -17
- package/dist/parser-2V5SPTU7.cjs.map +1 -0
- package/dist/{parser-AODAQQLQ.js → parser-3MAYZUJX.js} +95 -6
- package/dist/parser-3MAYZUJX.js.map +1 -0
- package/dist/{parser-J7YREDNS.js → parser-YSHZC5FQ.js} +94 -5
- package/dist/parser-YSHZC5FQ.js.map +1 -0
- package/dist/profile-io-3EECPDHG.js +11 -0
- package/dist/render-MJSQ4TG2.js +10 -0
- package/dist/seal-R3POFNOU.js +10 -0
- package/dist/seal-R3POFNOU.js.map +1 -0
- package/dist/{watch-P65Z24AL.js → watch-7MHSCKEQ.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 → -QVHGOOCR.js.map} +0 -0
- /package/dist/{chunk-6JSZBSOD.js.map → chunk-6BABRVOJ.js.map} +0 -0
- /package/dist/{chunk-RO2GVZ3I.js.map → chunk-6WDPB4SN.js.map} +0 -0
- /package/dist/{render-JPP7YTV3.js.map → profile-io-3EECPDHG.js.map} +0 -0
- /package/dist/{seal-MDE4DXZQ.js.map → render-MJSQ4TG2.js.map} +0 -0
- /package/dist/{watch-P65Z24AL.js.map → watch-7MHSCKEQ.js.map} +0 -0
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
dedupeRunningHeaders,
|
|
12
12
|
flattenLayoutTables,
|
|
13
13
|
mapPuaText
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-6PED5OOG.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-6WDPB4SN.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-OMERK4AW.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-QK2G34WE.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-6BABRVOJ.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,46 @@ 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
|
+
const cellSpanMode = spanModeOf(ctx.shared.kordocLayout);
|
|
1561
|
+
if (cellSpanMode && !ph?.prefix) {
|
|
1562
|
+
const spans = extractRunSpans(el, ctx, cellSpanMode, cellSpanMode !== "kordoc");
|
|
1563
|
+
if (spans && spans.map((s) => s.text).join("").replace(/[ \t]+/g, " ").trim() === text) {
|
|
1564
|
+
cellBlock.spans = spans;
|
|
1565
|
+
}
|
|
1566
|
+
}
|
|
1567
|
+
;
|
|
1568
|
+
(cell2.blocks ??= []).push(cellBlock);
|
|
1135
1569
|
} else if (!tableCtx) {
|
|
1570
|
+
if (/^─{10,}$/.test(text)) {
|
|
1571
|
+
blocks.push({ type: "separator", pageNumber: ctx.sectionNum });
|
|
1572
|
+
tableCtx = walkParagraphChildren(el, blocks, tableCtx, tableStack, ctx, depth + 1);
|
|
1573
|
+
break;
|
|
1574
|
+
}
|
|
1136
1575
|
const block = { type: headingLevel ? "heading" : "paragraph", text, pageNumber: ctx.sectionNum };
|
|
1137
1576
|
if (headingLevel) block.level = headingLevel;
|
|
1138
1577
|
if (style) block.style = style;
|
|
1139
1578
|
if (href) block.href = href;
|
|
1140
1579
|
if (footnote) block.footnoteText = footnote;
|
|
1580
|
+
const ind = ctx.styleMap?.paraIndents.get(el.getAttribute("paraPrIDRef") ?? "");
|
|
1581
|
+
if (ind) {
|
|
1582
|
+
const eff = ind.left + Math.max(0, ind.intent);
|
|
1583
|
+
if (eff > 0) block.indent = eff;
|
|
1584
|
+
}
|
|
1585
|
+
if (ctx.shared.kordocLayout === "gongmun" && block.indent && /^(?:-|\d{1,3}\)) /.test(text)) {
|
|
1586
|
+
const depth2 = gongmunDepthFromIndent(el, block.indent, ctx);
|
|
1587
|
+
if (depth2) block.listDepth = depth2;
|
|
1588
|
+
}
|
|
1589
|
+
const spanMode = spanModeOf(ctx.shared.kordocLayout);
|
|
1590
|
+
if (!headingLevel && spanMode) {
|
|
1591
|
+
if (!ph?.prefix) {
|
|
1592
|
+
const spans = extractRunSpans(el, ctx, spanMode, spanMode === "gongmun");
|
|
1593
|
+
if (spans && spans.map((s) => s.text).join("").replace(/[ \t]+/g, " ").trim() === text) {
|
|
1594
|
+
block.spans = spans;
|
|
1595
|
+
}
|
|
1596
|
+
}
|
|
1597
|
+
if (spanMode !== "foreign" && el.getAttribute("paraPrIDRef") === KORDOC_PARA_QUOTE) block.quote = true;
|
|
1598
|
+
}
|
|
1141
1599
|
blocks.push(block);
|
|
1142
1600
|
} else {
|
|
1143
1601
|
blocks.push({ type: "paragraph", text, pageNumber: ctx.sectionNum });
|
|
@@ -1219,6 +1677,34 @@ function mergeBlocksIntoCell(cell2, sink) {
|
|
|
1219
1677
|
}
|
|
1220
1678
|
}
|
|
1221
1679
|
}
|
|
1680
|
+
function kordocTableChannel(tblEl, ctx) {
|
|
1681
|
+
const found = findKordocMarkedCell(tblEl, 0);
|
|
1682
|
+
if (!found) return null;
|
|
1683
|
+
const m = found.name.match(/^__kordoc_(?:h([1-6])|(toc|skip))$/);
|
|
1684
|
+
if (!m) return null;
|
|
1685
|
+
if (m[2]) return { kind: "skip" };
|
|
1686
|
+
const text = collectSubListText(found.cell, ctx).trim();
|
|
1687
|
+
return { kind: "heading", level: Number(m[1]), text };
|
|
1688
|
+
}
|
|
1689
|
+
function findKordocMarkedCell(el, depth) {
|
|
1690
|
+
if (depth > 6) return null;
|
|
1691
|
+
const children = el.childNodes;
|
|
1692
|
+
if (!children) return null;
|
|
1693
|
+
for (let i = 0; i < children.length; i++) {
|
|
1694
|
+
const ch = children[i];
|
|
1695
|
+
if (ch.nodeType !== 1) continue;
|
|
1696
|
+
const tag = (ch.tagName || ch.localName || "").replace(/^[^:]+:/, "");
|
|
1697
|
+
if (tag === "tc") {
|
|
1698
|
+
const name = ch.getAttribute("name") || "";
|
|
1699
|
+
if (name.startsWith("__kordoc_")) return { cell: ch, name };
|
|
1700
|
+
continue;
|
|
1701
|
+
}
|
|
1702
|
+
if (tag === "tbl" && depth > 0) continue;
|
|
1703
|
+
const found = findKordocMarkedCell(ch, depth + 1);
|
|
1704
|
+
if (found) return found;
|
|
1705
|
+
}
|
|
1706
|
+
return null;
|
|
1707
|
+
}
|
|
1222
1708
|
function collectSubListText(el, ctx, depth = 0) {
|
|
1223
1709
|
if (depth > 10) return "";
|
|
1224
1710
|
const parts = [];
|
|
@@ -1254,6 +1740,15 @@ function walkParagraphChildren(node, blocks, tableCtx, tableStack, ctx, depth =
|
|
|
1254
1740
|
const tag = el.tagName || el.localName || "";
|
|
1255
1741
|
const localTag = tag.replace(/^[^:]+:/, "");
|
|
1256
1742
|
if (localTag === "tbl") {
|
|
1743
|
+
if (!tableCtx) {
|
|
1744
|
+
const chan = kordocTableChannel(el, ctx);
|
|
1745
|
+
if (chan) {
|
|
1746
|
+
if (chan.kind === "heading" && chan.text) {
|
|
1747
|
+
blocks.push({ type: "heading", level: chan.level, text: chan.text, pageNumber: ctx.sectionNum });
|
|
1748
|
+
}
|
|
1749
|
+
continue;
|
|
1750
|
+
}
|
|
1751
|
+
}
|
|
1257
1752
|
if (tableCtx) tableStack.push(tableCtx);
|
|
1258
1753
|
const newTable = { rows: [], currentRow: [], cell: null };
|
|
1259
1754
|
walkSection(el, blocks, newTable, tableStack, ctx, d + 1);
|
|
@@ -1587,6 +2082,92 @@ function extractParagraphInfo(para2, styleMap, ctx) {
|
|
|
1587
2082
|
}
|
|
1588
2083
|
return { text: cleanText, href, footnote, style };
|
|
1589
2084
|
}
|
|
2085
|
+
var KORDOC_CHAR_CODE = "4";
|
|
2086
|
+
var KORDOC_PARA_QUOTE = "6";
|
|
2087
|
+
function spanModeOf(layout) {
|
|
2088
|
+
if (layout === "default") return "kordoc";
|
|
2089
|
+
if (layout === "gongmun") return "gongmun";
|
|
2090
|
+
if (!layout) return "foreign";
|
|
2091
|
+
return null;
|
|
2092
|
+
}
|
|
2093
|
+
function extractRunSpans(para2, ctx, mode, requireMixed) {
|
|
2094
|
+
const styleMap = ctx.styleMap;
|
|
2095
|
+
if (!styleMap) return null;
|
|
2096
|
+
const spans = [];
|
|
2097
|
+
let styled = false;
|
|
2098
|
+
const kids2 = para2.childNodes;
|
|
2099
|
+
if (!kids2) return null;
|
|
2100
|
+
for (let i = 0; i < kids2.length; i++) {
|
|
2101
|
+
const child = kids2[i];
|
|
2102
|
+
if (child.nodeType !== 1) continue;
|
|
2103
|
+
const tag = (child.tagName || child.localName || "").replace(/^[^:]+:/, "");
|
|
2104
|
+
if (tag === "linesegarray") continue;
|
|
2105
|
+
if (tag !== "run" && tag !== "r") return null;
|
|
2106
|
+
let text = "";
|
|
2107
|
+
const rkids = child.childNodes;
|
|
2108
|
+
for (let j = 0; j < (rkids?.length ?? 0); j++) {
|
|
2109
|
+
const rc = rkids[j];
|
|
2110
|
+
if (rc.nodeType !== 1) continue;
|
|
2111
|
+
const rtag = (rc.tagName || rc.localName || "").replace(/^[^:]+:/, "");
|
|
2112
|
+
if (rtag === "t") {
|
|
2113
|
+
const tkids = rc.childNodes;
|
|
2114
|
+
for (let k = 0; k < (tkids?.length ?? 0); k++) {
|
|
2115
|
+
const tk = tkids[k];
|
|
2116
|
+
if (tk.nodeType === 3) text += tk.textContent || "";
|
|
2117
|
+
else if (tk.nodeType === 1) return null;
|
|
2118
|
+
}
|
|
2119
|
+
} else if (rtag === "secPr" || rtag === "colPr") {
|
|
2120
|
+
} else if (rtag === "ctrl") {
|
|
2121
|
+
if (extractTextFromNode(rc)) return null;
|
|
2122
|
+
} else {
|
|
2123
|
+
return null;
|
|
2124
|
+
}
|
|
2125
|
+
}
|
|
2126
|
+
if (!text) continue;
|
|
2127
|
+
const prId = child.getAttribute("charPrIDRef") ?? "";
|
|
2128
|
+
const cp = styleMap.charProperties.get(prId);
|
|
2129
|
+
const span = { text };
|
|
2130
|
+
if (mode !== "foreign" && prId === KORDOC_CHAR_CODE) span.code = true;
|
|
2131
|
+
else {
|
|
2132
|
+
if (cp?.bold) span.bold = true;
|
|
2133
|
+
if (cp?.italic) span.italic = true;
|
|
2134
|
+
}
|
|
2135
|
+
if (span.bold || span.italic || span.code) styled = true;
|
|
2136
|
+
spans.push(span);
|
|
2137
|
+
}
|
|
2138
|
+
if (!styled || spans.length === 0) return null;
|
|
2139
|
+
const merged = [];
|
|
2140
|
+
for (const s of spans) {
|
|
2141
|
+
const last = merged[merged.length - 1];
|
|
2142
|
+
if (last && !!last.bold === !!s.bold && !!last.italic === !!s.italic && !!last.code === !!s.code) {
|
|
2143
|
+
last.text += s.text;
|
|
2144
|
+
} else {
|
|
2145
|
+
merged.push(s);
|
|
2146
|
+
}
|
|
2147
|
+
}
|
|
2148
|
+
if (requireMixed && !merged.some((s) => !(s.bold || s.italic || s.code))) return null;
|
|
2149
|
+
return merged;
|
|
2150
|
+
}
|
|
2151
|
+
function gongmunDepthFromIndent(para2, left, ctx) {
|
|
2152
|
+
const styleMap = ctx.styleMap;
|
|
2153
|
+
if (!styleMap) return null;
|
|
2154
|
+
let unit = 0;
|
|
2155
|
+
const kids2 = para2.childNodes;
|
|
2156
|
+
for (let i = 0; i < (kids2?.length ?? 0); i++) {
|
|
2157
|
+
const child = kids2[i];
|
|
2158
|
+
if (child.nodeType !== 1) continue;
|
|
2159
|
+
const tag = (child.tagName || child.localName || "").replace(/^[^:]+:/, "");
|
|
2160
|
+
if (tag !== "run" && tag !== "r") continue;
|
|
2161
|
+
const size = styleMap.charProperties.get(child.getAttribute("charPrIDRef") ?? "")?.fontSize;
|
|
2162
|
+
if (size) unit = size * 100;
|
|
2163
|
+
break;
|
|
2164
|
+
}
|
|
2165
|
+
if (!unit) return null;
|
|
2166
|
+
const r = left / unit;
|
|
2167
|
+
const nearInt = Math.round(r);
|
|
2168
|
+
const depth = Math.abs(r - nearInt) < 0.2 ? nearInt : Math.round(2 * r - 1);
|
|
2169
|
+
return depth >= 1 && depth < 8 ? depth : null;
|
|
2170
|
+
}
|
|
1590
2171
|
|
|
1591
2172
|
// src/hwpx/images.ts
|
|
1592
2173
|
function imageExtToMime(ext) {
|
|
@@ -1776,6 +2357,17 @@ function extractFromBrokenZip(buffer) {
|
|
|
1776
2357
|
const markdown = blocksToMarkdown(blocks);
|
|
1777
2358
|
return { markdown, blocks, warnings: warnings.length > 0 ? warnings : void 0 };
|
|
1778
2359
|
}
|
|
2360
|
+
async function readKordocLayout(zip) {
|
|
2361
|
+
const file = zip.file("Contents/content.hpf") ?? zip.file("content.hpf");
|
|
2362
|
+
if (!file) return null;
|
|
2363
|
+
try {
|
|
2364
|
+
const xml = await file.async("text");
|
|
2365
|
+
if (!/<opf:meta\b[^>]*name="generator"[^>]*content="kordoc"/.test(xml)) return null;
|
|
2366
|
+
return xml.match(/<opf:meta\b[^>]*name="kordoc-layout"[^>]*content="([^"]*)"/)?.[1] ?? null;
|
|
2367
|
+
} catch {
|
|
2368
|
+
return null;
|
|
2369
|
+
}
|
|
2370
|
+
}
|
|
1779
2371
|
async function resolveSectionPaths(zip) {
|
|
1780
2372
|
const manifestPaths = ["Contents/content.hpf", "content.hpf"];
|
|
1781
2373
|
for (const mp of manifestPaths) {
|
|
@@ -1906,6 +2498,7 @@ async function parseHwpxDocument(buffer, options) {
|
|
|
1906
2498
|
const totalTarget = pageFilter ? pageFilter.size : sectionPaths.length;
|
|
1907
2499
|
const blocks = [];
|
|
1908
2500
|
const shared = createSectionShared();
|
|
2501
|
+
shared.kordocLayout = await readKordocLayout(zip);
|
|
1909
2502
|
let parsedSections = 0;
|
|
1910
2503
|
for (let si = 0; si < sectionPaths.length; si++) {
|
|
1911
2504
|
if (pageFilter && !pageFilter.has(si + 1)) continue;
|
|
@@ -19738,364 +20331,29 @@ async function fillHwpx(hwpxBuffer, values, blockedLabels) {
|
|
|
19738
20331
|
for (const k of l.matchKeys) failedKeys.add(k);
|
|
19739
20332
|
continue;
|
|
19740
20333
|
}
|
|
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);
|
|
20334
|
+
for (const k of l.matchKeys) succeededKeys.add(k);
|
|
20335
|
+
splices.push(...paraSplices);
|
|
20336
|
+
}
|
|
20337
|
+
if (splices.length > 0) {
|
|
20338
|
+
splices.push(...allLinesegRemovalSplices(xml));
|
|
20339
|
+
replacements.set(sectionPaths[si], encoder.encode(applySplices(xml, splices)));
|
|
19955
20340
|
}
|
|
19956
20341
|
}
|
|
19957
|
-
|
|
19958
|
-
|
|
20342
|
+
for (const k of failedKeys) {
|
|
20343
|
+
if (!succeededKeys.has(k)) matchedLabels.delete(k);
|
|
19959
20344
|
}
|
|
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";
|
|
20345
|
+
const cleanFilled = filled.filter((f) => f !== null);
|
|
20346
|
+
const unmatched = resolveUnmatched(normalizedValues, matchedLabels, values);
|
|
20347
|
+
const out = replacements.size > 0 ? patchZipEntries(u8, replacements) : new Uint8Array(u8);
|
|
19971
20348
|
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
|
|
20349
|
+
buffer: out.buffer.slice(out.byteOffset, out.byteOffset + out.byteLength),
|
|
20350
|
+
filled: cleanFilled,
|
|
20351
|
+
unmatched
|
|
20008
20352
|
};
|
|
20009
20353
|
}
|
|
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
|
-
}
|
|
20354
|
+
|
|
20355
|
+
// src/hwpx/generator.ts
|
|
20356
|
+
import JSZip6 from "jszip";
|
|
20099
20357
|
|
|
20100
20358
|
// src/hwpx/gen-ids.ts
|
|
20101
20359
|
var NS_SECTION = "http://www.hancom.co.kr/hwpml/2011/section";
|
|
@@ -20140,7 +20398,7 @@ function resolveTheme(theme) {
|
|
|
20140
20398
|
};
|
|
20141
20399
|
}
|
|
20142
20400
|
function escapeXml(text) {
|
|
20143
|
-
return text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
20401
|
+
return text.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F]/g, "").replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
20144
20402
|
}
|
|
20145
20403
|
function headingParaPrId(level) {
|
|
20146
20404
|
if (level === 1) return PARA_H1;
|
|
@@ -20159,7 +20417,7 @@ function charPr(id, height, bold, italic, fontId = 0, textColor = DEFAULT_TEXT_C
|
|
|
20159
20417
|
const italicAttr = italic ? ` italic="1"` : "";
|
|
20160
20418
|
const effFont = fontId;
|
|
20161
20419
|
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}>
|
|
20420
|
+
return ` <hh:charPr id="${id}" height="${height}" textColor="${escapeXml(textColor)}" shadeColor="none" useFontSpace="0" useKerning="0" symMark="NONE" borderFillIDRef="1"${boldAttr}${italicAttr}>
|
|
20163
20421
|
<hh:fontRef hangul="${effFont}" latin="${effFont}" hanja="${effFont}" japanese="${effFont}" other="${effFont}" symbol="${effFont}" user="${effFont}"/>
|
|
20164
20422
|
<hh:ratio hangul="${ratioPct}" latin="${ratioPct}" hanja="${ratioPct}" japanese="100" other="100" symbol="100" user="100"/>
|
|
20165
20423
|
<hh:spacing hangul="0" latin="0" hanja="0" japanese="0" other="0" symbol="0" user="0"/>
|
|
@@ -20205,7 +20463,7 @@ var GJ_CHAR_BAR = 24;
|
|
|
20205
20463
|
var GJ_CHAR_BODY_TITLE = 25;
|
|
20206
20464
|
var GJ_CHAR_TITLE_BAR = 26;
|
|
20207
20465
|
var GJ_CHAR_APPROVAL = 27;
|
|
20208
|
-
var GJ_CHAR_COUNT =
|
|
20466
|
+
var GJ_CHAR_COUNT = GJ_CHAR_APPROVAL - GJ_CHAR_DAE + 1;
|
|
20209
20467
|
var GJ_PARA_CHAM = 20;
|
|
20210
20468
|
var GJ_PARA_COVER = 21;
|
|
20211
20469
|
var GJ_PARA_TOC_ITEM = 22;
|
|
@@ -20213,6 +20471,7 @@ var GJ_PARA_CHAPTER = 23;
|
|
|
20213
20471
|
var GJ_PARA_BAR = 24;
|
|
20214
20472
|
var GONGMUN_LIST_PLAIN_BASE = 25;
|
|
20215
20473
|
var GONGMUN_PARA_APPROVAL = GONGMUN_LIST_PLAIN_BASE + GONGMUN_LIST_LEVELS;
|
|
20474
|
+
var GONGMUN_LIST_VARIANT_BASE = GONGMUN_PARA_APPROVAL + 1;
|
|
20216
20475
|
var GJ_BF_CHAPTER_NUM = 3;
|
|
20217
20476
|
var GJ_BF_CHAPTER_GAP = 4;
|
|
20218
20477
|
var GJ_BF_CHAPTER_TITLE = 5;
|
|
@@ -20256,10 +20515,10 @@ var GONGMUN_TBL_CHAR_BOLD = 12;
|
|
|
20256
20515
|
var GONGMUN_TBL_PT = 1200;
|
|
20257
20516
|
var GONGMUN_TITLE_BAR_CHAR = 13;
|
|
20258
20517
|
var GONGMUN_APPROVAL_CHAR = 14;
|
|
20259
|
-
var CHAR_VARIANT_BASE =
|
|
20518
|
+
var CHAR_VARIANT_BASE = CHAR_QUOTE + 1;
|
|
20260
20519
|
function charVariantBase(richAssets, isGongmun = true) {
|
|
20261
20520
|
if (!isGongmun) return CHAR_VARIANT_BASE;
|
|
20262
|
-
return richAssets ?
|
|
20521
|
+
return richAssets ? CHAR_VARIANT_BASE + GJ_CHAR_COUNT : CHAR_VARIANT_BASE + (GONGMUN_APPROVAL_CHAR - GONGMUN_TBL_CHAR + 1);
|
|
20263
20522
|
}
|
|
20264
20523
|
var GONGMUN_BODY_RATIO = 95;
|
|
20265
20524
|
|
|
@@ -20292,6 +20551,7 @@ function parseMarkdownToBlocks(md2) {
|
|
|
20292
20551
|
const lines = md2.replace(/\r\n?/g, "\n").split("\n");
|
|
20293
20552
|
const blocks = [];
|
|
20294
20553
|
let i = 0;
|
|
20554
|
+
const listStack = [];
|
|
20295
20555
|
while (i < lines.length) {
|
|
20296
20556
|
const line = lines[i];
|
|
20297
20557
|
if (!line.trim()) {
|
|
@@ -20380,7 +20640,8 @@ function parseMarkdownToBlocks(md2) {
|
|
|
20380
20640
|
const tableRows = [];
|
|
20381
20641
|
while (i < lines.length && lines[i].trimStart().startsWith("|")) {
|
|
20382
20642
|
const row = lines[i];
|
|
20383
|
-
|
|
20643
|
+
const sepCells = row.trim().replace(/^\|/, "").replace(/\|$/, "").split("|");
|
|
20644
|
+
if (sepCells.every((c) => /^\s*:?-+:?\s*$/.test(c))) {
|
|
20384
20645
|
i++;
|
|
20385
20646
|
continue;
|
|
20386
20647
|
}
|
|
@@ -20394,17 +20655,36 @@ function parseMarkdownToBlocks(md2) {
|
|
|
20394
20655
|
if (line.trimStart().startsWith("> ")) {
|
|
20395
20656
|
const quoteLines = [];
|
|
20396
20657
|
while (i < lines.length && (lines[i].trimStart().startsWith("> ") || lines[i].trimStart().startsWith(">"))) {
|
|
20397
|
-
quoteLines.push(lines[i].replace(/^>\s?/, ""));
|
|
20658
|
+
quoteLines.push(lines[i].replace(/^>\s?/, "").trim());
|
|
20398
20659
|
i++;
|
|
20399
20660
|
}
|
|
20661
|
+
let joined = [];
|
|
20400
20662
|
for (const ql of quoteLines) {
|
|
20401
|
-
|
|
20663
|
+
if (ql) {
|
|
20664
|
+
joined.push(ql);
|
|
20665
|
+
continue;
|
|
20666
|
+
}
|
|
20667
|
+
if (joined.length) blocks.push({ type: "blockquote", text: joined.join("\n") });
|
|
20668
|
+
joined = [];
|
|
20402
20669
|
}
|
|
20670
|
+
if (joined.length) blocks.push({ type: "blockquote", text: joined.join("\n") });
|
|
20403
20671
|
continue;
|
|
20404
20672
|
}
|
|
20405
|
-
const
|
|
20673
|
+
const listLine = line.replace(/^[\t ]+/, (ws) => ws.replace(/\t/g, " "));
|
|
20674
|
+
const listMatch = listLine.match(/^(\s*)([-*+]|\d+[.)]) (.+)$/);
|
|
20406
20675
|
if (listMatch) {
|
|
20407
|
-
|
|
20676
|
+
if (blocks.length === 0 || blocks[blocks.length - 1].type !== "list_item") listStack.length = 0;
|
|
20677
|
+
const phys = listMatch[1].length;
|
|
20678
|
+
let indent;
|
|
20679
|
+
if (listStack.length === 0) {
|
|
20680
|
+
indent = Math.floor(phys / 2);
|
|
20681
|
+
for (let d = 0; d < indent; d++) listStack.push(d * 2);
|
|
20682
|
+
listStack.push(phys);
|
|
20683
|
+
} else {
|
|
20684
|
+
while (listStack.length > 1 && phys < listStack[listStack.length - 1]) listStack.pop();
|
|
20685
|
+
if (phys > listStack[listStack.length - 1]) listStack.push(phys);
|
|
20686
|
+
indent = listStack.length - 1;
|
|
20687
|
+
}
|
|
20408
20688
|
const ordered = /\d/.test(listMatch[2]);
|
|
20409
20689
|
blocks.push({ type: "list_item", text: listMatch[3].trim(), ordered, indent, marker: listMatch[2] });
|
|
20410
20690
|
i++;
|
|
@@ -20425,7 +20705,7 @@ function parseInlineMarkdown(text) {
|
|
|
20425
20705
|
text = text.replace(/\[([^\]]*)\]\(([^)]*)\)/g, (_, t, u) => t || u);
|
|
20426
20706
|
text = text.replace(/~~([^~]+)~~/g, "$1");
|
|
20427
20707
|
const spans = [];
|
|
20428
|
-
const regex = /(`[^`]+`|\*{3}[^*]+\*{3}|\*{2}[^*]+\*{2}|\*[^*]+\*|_{2}[^_]+_{2}|_[^_]+_)/
|
|
20708
|
+
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
20709
|
let lastIdx = 0;
|
|
20430
20710
|
for (const match of text.matchAll(regex)) {
|
|
20431
20711
|
const idx = match.index;
|
|
@@ -20491,14 +20771,20 @@ function generateContainerXml() {
|
|
|
20491
20771
|
</ocf:rootfiles>
|
|
20492
20772
|
</ocf:container>`;
|
|
20493
20773
|
}
|
|
20494
|
-
function generateManifest(chartParts = []) {
|
|
20774
|
+
function generateManifest(chartParts = [], imageItems = [], layout = "default") {
|
|
20495
20775
|
const chartItems = chartParts.map((p, i) => `
|
|
20496
20776
|
<opf:item id="chart${i + 1}" href="${p.name}" media-type="application/xml"/>`).join("");
|
|
20777
|
+
const imgItems = imageItems.map((x) => `
|
|
20778
|
+
${x}`).join("");
|
|
20497
20779
|
return `<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
|
|
20498
20780
|
<opf:package xmlns:opf="${NS_OPF}" xmlns:hpf="${NS_HPF}" xmlns:hh="${NS_HEAD}">
|
|
20781
|
+
<opf:metadata>
|
|
20782
|
+
<opf:meta name="generator" content="kordoc"/>
|
|
20783
|
+
<opf:meta name="kordoc-layout" content="${escapeXml(layout)}"/>
|
|
20784
|
+
</opf:metadata>
|
|
20499
20785
|
<opf:manifest>
|
|
20500
20786
|
<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}
|
|
20787
|
+
<opf:item id="section0" href="Contents/section0.xml" media-type="application/xml"/>${chartItems}${imgItems}
|
|
20502
20788
|
</opf:manifest>
|
|
20503
20789
|
<opf:spine>
|
|
20504
20790
|
<opf:itemref idref="header" linear="no"/>
|
|
@@ -20506,6 +20792,17 @@ function generateManifest(chartParts = []) {
|
|
|
20506
20792
|
</opf:spine>
|
|
20507
20793
|
</opf:package>`;
|
|
20508
20794
|
}
|
|
20795
|
+
function assertSequentialIds(rows, label, startId) {
|
|
20796
|
+
for (let i = 0; i < rows.length; i++) {
|
|
20797
|
+
const m = rows[i].match(/\bid="(\d+)"/);
|
|
20798
|
+
const got = m ? Number(m[1]) : NaN;
|
|
20799
|
+
if (got !== startId + i) {
|
|
20800
|
+
throw new KordocError(
|
|
20801
|
+
`\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)`
|
|
20802
|
+
);
|
|
20803
|
+
}
|
|
20804
|
+
}
|
|
20805
|
+
}
|
|
20509
20806
|
function buildCharProperties(theme, gongmun, ratioVariants = [], extraCharPrXmls = []) {
|
|
20510
20807
|
const measured = !!gongmun && usesReportFonts(gongmun.preset);
|
|
20511
20808
|
const richAssets = !!gongmun && needsGaejosikAssets(gongmun);
|
|
@@ -20513,8 +20810,8 @@ function buildCharProperties(theme, gongmun, ratioVariants = [], extraCharPrXmls
|
|
|
20513
20810
|
if (gongmun) {
|
|
20514
20811
|
body = gongmun.bodyHeight;
|
|
20515
20812
|
code = Math.max(body - 200, 900);
|
|
20516
|
-
h1 = gongmun.preset === "report" || gongmun.preset === "plan" ? 2e3 : 1700;
|
|
20517
|
-
h2 = 1600;
|
|
20813
|
+
h1 = Math.max(gongmun.preset === "report" || gongmun.preset === "plan" ? 2e3 : 1700, body);
|
|
20814
|
+
h2 = Math.max(1600, body);
|
|
20518
20815
|
h3 = body;
|
|
20519
20816
|
h4 = Math.min(h3, Math.max(body - 100, 1300));
|
|
20520
20817
|
}
|
|
@@ -20582,6 +20879,12 @@ function buildCharProperties(theme, gongmun, ratioVariants = [], extraCharPrXmls
|
|
|
20582
20879
|
// 결재란 라벨(맑은 고딕 10pt)
|
|
20583
20880
|
);
|
|
20584
20881
|
}
|
|
20882
|
+
const expectedBase = charVariantBase(richAssets, !!gongmun);
|
|
20883
|
+
if (rows.length !== expectedBase) {
|
|
20884
|
+
throw new KordocError(
|
|
20885
|
+
`\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`
|
|
20886
|
+
);
|
|
20887
|
+
}
|
|
20585
20888
|
for (const r of ratioVariants) {
|
|
20586
20889
|
rows.push(
|
|
20587
20890
|
charPr(rows.length, body, false, false, bodyFont, theme.body, r),
|
|
@@ -20591,11 +20894,12 @@ function buildCharProperties(theme, gongmun, ratioVariants = [], extraCharPrXmls
|
|
|
20591
20894
|
);
|
|
20592
20895
|
}
|
|
20593
20896
|
rows.push(...extraCharPrXmls);
|
|
20897
|
+
assertSequentialIds(rows, "charPr", 0);
|
|
20594
20898
|
return `<hh:charProperties itemCnt="${rows.length}">
|
|
20595
20899
|
${rows.join("\n")}
|
|
20596
20900
|
</hh:charProperties>`;
|
|
20597
20901
|
}
|
|
20598
|
-
function buildParaProperties(gongmun) {
|
|
20902
|
+
function buildParaProperties(gongmun, listIndentVariants = []) {
|
|
20599
20903
|
if (!gongmun) {
|
|
20600
20904
|
const base2 = [
|
|
20601
20905
|
paraPr(0, { keepWord: true }),
|
|
@@ -20607,6 +20911,7 @@ function buildParaProperties(gongmun) {
|
|
|
20607
20911
|
paraPr(6, { align: "LEFT", lineSpacing: 150, indent: 600, keepWord: true }),
|
|
20608
20912
|
paraPr(7, { align: "LEFT", lineSpacing: 160, indent: 600, keepWord: true })
|
|
20609
20913
|
];
|
|
20914
|
+
assertSequentialIds(base2, "paraPr", 0);
|
|
20610
20915
|
return `<hh:paraProperties itemCnt="${base2.length}">
|
|
20611
20916
|
${base2.join("\n")}
|
|
20612
20917
|
</hh:paraProperties>`;
|
|
@@ -20653,6 +20958,14 @@ ${base2.join("\n")}
|
|
|
20653
20958
|
base.push(paraPr(GONGMUN_LIST_PLAIN_BASE + d, { align: "JUSTIFY", lineSpacing: ls, left, indent: 0, spaceBefore: sectionGap, keepWord: true }));
|
|
20654
20959
|
}
|
|
20655
20960
|
base.push(paraPr(GONGMUN_PARA_APPROVAL, { align: "CENTER", lineSpacing: 100, keepWord: true }));
|
|
20961
|
+
for (let vi = 0; vi < listIndentVariants.length; vi++) {
|
|
20962
|
+
const { depth, widthHu } = listIndentVariants[vi];
|
|
20963
|
+
const { left } = levelIndent(depth, gongmun.bodyHeight, gongmun.numbering, gongmun.sizes, gongmun.bullet2, usesAsteriskThird(gongmun.preset));
|
|
20964
|
+
const sectionGap = gongmun.numbering === "gaejosik" || gongmun.numbering === "report" ? gaejosikSpaceBefore(depth, gongmun.bodyHeight) : 0;
|
|
20965
|
+
const keepNext = (gongmun.numbering === "gaejosik" || gongmun.numbering === "report") && depth === 0;
|
|
20966
|
+
base.push(paraPr(GONGMUN_LIST_VARIANT_BASE + vi, { align: "JUSTIFY", lineSpacing: ls, left, indent: -widthHu, spaceBefore: sectionGap, keepWord: true, keepWithNext: keepNext }));
|
|
20967
|
+
}
|
|
20968
|
+
assertSequentialIds(base, "paraPr", 0);
|
|
20656
20969
|
return `<hh:paraProperties itemCnt="${base.length}">
|
|
20657
20970
|
${base.join("\n")}
|
|
20658
20971
|
</hh:paraProperties>`;
|
|
@@ -20673,7 +20986,10 @@ function fontEntry(id, face, weight = 6) {
|
|
|
20673
20986
|
<hh:typeInfo familyType="FCAT_GOTHIC" weight="${weight}" proportion="4" contrast="0" strokeVariation="1" armStyle="1" letterform="1" midline="1" xHeight="1"/>
|
|
20674
20987
|
</hh:font>`;
|
|
20675
20988
|
}
|
|
20676
|
-
function
|
|
20989
|
+
function staticFontNext(gongmun) {
|
|
20990
|
+
return gongmun && needsGaejosikAssets(gongmun) ? 8 : 3;
|
|
20991
|
+
}
|
|
20992
|
+
function buildFontFaces(gongmun, bodyFace, extraFonts = []) {
|
|
20677
20993
|
if (gongmun && needsGaejosikAssets(gongmun)) {
|
|
20678
20994
|
const ov = gongmun.fonts;
|
|
20679
20995
|
const bodyDefault = usesReportFonts(gongmun.preset) ? gongmun.preset !== "gaejosik" && gongmun.bodyFont === "gothic" ? "\uB9D1\uC740 \uACE0\uB515" : "\uD734\uBA3C\uBA85\uC870" : bodyFace;
|
|
@@ -20687,21 +21003,27 @@ function buildFontFaces(gongmun, bodyFace) {
|
|
|
20687
21003
|
"\uD55C\uC591\uC2E0\uBA85\uC870",
|
|
20688
21004
|
ov.table ?? "\uB9D1\uC740 \uACE0\uB515"
|
|
20689
21005
|
];
|
|
20690
|
-
|
|
21006
|
+
if (faces.length !== staticFontNext(gongmun)) {
|
|
21007
|
+
throw new Error(`fontface \uC815\uC801 \uAC1C\uC218 \uB4DC\uB9AC\uD504\uD2B8: ${faces.length} \u2260 staticFontNext ${staticFontNext(gongmun)}`);
|
|
21008
|
+
}
|
|
21009
|
+
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
21010
|
const langs = ["HANGUL", "LATIN", "HANJA", "JAPANESE", "OTHER", "SYMBOL", "USER"];
|
|
20692
21011
|
return `<hh:fontfaces itemCnt="${langs.length}">
|
|
20693
|
-
` + langs.map((l) => ` <hh:fontface lang="${l}" fontCnt="${faces.length}">
|
|
21012
|
+
` + langs.map((l) => ` <hh:fontface lang="${l}" fontCnt="${faces.length + extraFonts.length}">
|
|
20694
21013
|
${list}
|
|
20695
21014
|
</hh:fontface>`).join("\n") + `
|
|
20696
21015
|
</hh:fontfaces>`;
|
|
20697
21016
|
}
|
|
21017
|
+
const extraH = extraFonts.map((f, i) => fontEntry(3 + i, f)).join("\n");
|
|
21018
|
+
const extraBlock = extraH ? `
|
|
21019
|
+
${extraH}` : "";
|
|
20698
21020
|
return `<hh:fontfaces itemCnt="7">
|
|
20699
|
-
<hh:fontface lang="HANGUL" fontCnt="3">
|
|
21021
|
+
<hh:fontface lang="HANGUL" fontCnt="${3 + extraFonts.length}">
|
|
20700
21022
|
${fontEntry(0, bodyFace)}
|
|
20701
21023
|
${fontEntry(1, "\uD568\uCD08\uB86C\uB3CB\uC6C0")}
|
|
20702
|
-
${fontEntry(2, "HY\uACAC\uACE0\uB515", 9)}
|
|
21024
|
+
${fontEntry(2, "HY\uACAC\uACE0\uB515", 9)}${extraBlock}
|
|
20703
21025
|
</hh:fontface>
|
|
20704
|
-
<hh:fontface lang="LATIN" fontCnt="3">
|
|
21026
|
+
<hh:fontface lang="LATIN" fontCnt="${3 + extraFonts.length}">
|
|
20705
21027
|
<hh:font id="0" face="Times New Roman" type="TTF" isEmbedded="0">
|
|
20706
21028
|
<hh:typeInfo familyType="FCAT_OLDSTYLE" weight="5" proportion="4" contrast="2" strokeVariation="0" armStyle="0" letterform="0" midline="0" xHeight="4"/>
|
|
20707
21029
|
</hh:font>
|
|
@@ -20710,7 +21032,7 @@ ${fontEntry(2, "HY\uACAC\uACE0\uB515", 9)}
|
|
|
20710
21032
|
</hh:font>
|
|
20711
21033
|
<hh:font id="2" face="Arial Black" type="TTF" isEmbedded="0">
|
|
20712
21034
|
<hh:typeInfo familyType="FCAT_GOTHIC" weight="9" proportion="0" contrast="0" strokeVariation="0" armStyle="0" letterform="0" midline="0" xHeight="0"/>
|
|
20713
|
-
</hh:font
|
|
21035
|
+
</hh:font>${extraBlock}
|
|
20714
21036
|
</hh:fontface>
|
|
20715
21037
|
<hh:fontface lang="HANJA" fontCnt="1">
|
|
20716
21038
|
${fontEntry(0, "\uD568\uCD08\uB86C\uBC14\uD0D5")}
|
|
@@ -20729,7 +21051,7 @@ ${fontEntry(0, "\uAD74\uB9BC")}
|
|
|
20729
21051
|
</hh:fontface>
|
|
20730
21052
|
</hh:fontfaces>`;
|
|
20731
21053
|
}
|
|
20732
|
-
function
|
|
21054
|
+
function staticBorderFillItems(gongmun) {
|
|
20733
21055
|
const thin = ["0.12 mm", "#000000"];
|
|
20734
21056
|
const items = [
|
|
20735
21057
|
borderFillEntry(1, {}),
|
|
@@ -20760,7 +21082,14 @@ function buildBorderFills(gongmun, extra = []) {
|
|
|
20760
21082
|
if (gongmun) {
|
|
20761
21083
|
items.push(borderFillEntry(items.length + 1, { l: thin, r: thin, t: thin, b: thin }, "#E6E6E6"));
|
|
20762
21084
|
}
|
|
20763
|
-
items
|
|
21085
|
+
return items;
|
|
21086
|
+
}
|
|
21087
|
+
function staticBorderFillNext(gongmun) {
|
|
21088
|
+
return staticBorderFillItems(gongmun).length + 1;
|
|
21089
|
+
}
|
|
21090
|
+
function buildBorderFills(gongmun, extra = []) {
|
|
21091
|
+
const items = [...staticBorderFillItems(gongmun), ...extra];
|
|
21092
|
+
assertSequentialIds(items, "borderFill", 1);
|
|
20764
21093
|
return `<hh:borderFills itemCnt="${items.length}">
|
|
20765
21094
|
${items.join("\n")}
|
|
20766
21095
|
</hh:borderFills>`;
|
|
@@ -20778,15 +21107,15 @@ function buildStyles(gongmun) {
|
|
|
20778
21107
|
${items.join("\n ")}
|
|
20779
21108
|
</hh:styles>`;
|
|
20780
21109
|
}
|
|
20781
|
-
function generateHeaderXml(theme, gongmun, ratioVariants = [], extraBorderFills = [], extraCharPrXmls = []) {
|
|
21110
|
+
function generateHeaderXml(theme, gongmun, ratioVariants = [], extraBorderFills = [], extraCharPrXmls = [], listIndentVariants = [], extraFonts = []) {
|
|
20782
21111
|
const bodyFace = gongmun?.fonts.body ?? (gongmun?.bodyFont === "gothic" ? "\uB9D1\uC740 \uACE0\uB515" : "\uD568\uCD08\uB86C\uBC14\uD0D5");
|
|
20783
21112
|
const charPropsXml = buildCharProperties(theme, gongmun, ratioVariants, extraCharPrXmls);
|
|
20784
|
-
const paraPropsXml = buildParaProperties(gongmun);
|
|
21113
|
+
const paraPropsXml = buildParaProperties(gongmun, gongmun ? listIndentVariants : []);
|
|
20785
21114
|
return `<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
|
|
20786
21115
|
<hh:head xmlns:hh="${NS_HEAD}" xmlns:hp="${NS_PARA}" xmlns:hc="${NS_CORE}" version="1.4" secCnt="1">
|
|
20787
21116
|
<hh:beginNum page="1" footnote="1" endnote="1" pic="1" tbl="1" equation="1"/>
|
|
20788
21117
|
<hh:refList>
|
|
20789
|
-
${buildFontFaces(gongmun, bodyFace)}
|
|
21118
|
+
${buildFontFaces(gongmun, bodyFace, extraFonts)}
|
|
20790
21119
|
${buildBorderFills(gongmun, extraBorderFills)}
|
|
20791
21120
|
${charPropsXml}
|
|
20792
21121
|
<hh:tabProperties itemCnt="0"/>
|
|
@@ -20860,7 +21189,6 @@ function chartSpec(t) {
|
|
|
20860
21189
|
const key = CHART_ALIAS[t.toLowerCase()] ?? Number(t);
|
|
20861
21190
|
return CHART_TYPES[key] ?? CHART_TYPES[0];
|
|
20862
21191
|
}
|
|
20863
|
-
var HU_PER_MM = 7200 / 25.4;
|
|
20864
21192
|
var RESERVED_KEYS = /* @__PURE__ */ new Set(["type", "cat", "size", "colors", "point_colors", "title"]);
|
|
20865
21193
|
function parseChartFence(text) {
|
|
20866
21194
|
let type;
|
|
@@ -21019,7 +21347,7 @@ function plainRenderText(text) {
|
|
|
21019
21347
|
function computeGongmunFitPlan(blocks, gongmun, gongmunList) {
|
|
21020
21348
|
const minRatio = gongmun.autoFitMinRatio;
|
|
21021
21349
|
if (minRatio === null || minRatio >= GONGMUN_BODY_RATIO) return null;
|
|
21022
|
-
const pageW =
|
|
21350
|
+
const pageW = A4_W_HU - mmToHwpunit(gongmun.margins.left) - mmToHwpunit(gongmun.margins.right);
|
|
21023
21351
|
const ratioByBlock = /* @__PURE__ */ new Map();
|
|
21024
21352
|
const variants = [];
|
|
21025
21353
|
for (let i = 0; i < blocks.length; i++) {
|
|
@@ -21027,14 +21355,14 @@ function computeGongmunFitPlan(blocks, gongmun, gongmunList) {
|
|
|
21027
21355
|
let text;
|
|
21028
21356
|
let firstW;
|
|
21029
21357
|
let contW;
|
|
21030
|
-
if (block.type === "list_item" && gongmunList.has(i)) {
|
|
21031
|
-
const { marker, depth } = gongmunList.get(i);
|
|
21358
|
+
if (block.type === "list_item" && gongmunList.items.has(i)) {
|
|
21359
|
+
const { marker, depth, indentVariant } = gongmunList.items.get(i);
|
|
21032
21360
|
if (gongmun.numbering === "gaejosik" && (depth === 0 || (block.text || "").trimStart().startsWith("\u203B"))) continue;
|
|
21033
21361
|
const content = plainRenderText(block.text || "");
|
|
21034
21362
|
text = marker ? `${marker} ${content}` : content;
|
|
21035
21363
|
const li = levelIndent(depth, gongmun.bodyHeight, gongmun.numbering, gongmun.sizes, gongmun.bullet2, usesAsteriskThird(gongmun.preset));
|
|
21036
21364
|
const left = li.left;
|
|
21037
|
-
const indent = marker ? li.indent : 0;
|
|
21365
|
+
const indent = marker ? indentVariant !== void 0 ? -gongmunList.indentVariants[indentVariant].widthHu : li.indent : 0;
|
|
21038
21366
|
firstW = pageW - left - Math.max(indent, 0);
|
|
21039
21367
|
contW = pageW - left - Math.max(-indent, 0);
|
|
21040
21368
|
} else if (block.type === "paragraph") {
|
|
@@ -21060,9 +21388,55 @@ function variantMapper(fit, blockIdx, base = CHAR_VARIANT_BASE) {
|
|
|
21060
21388
|
const vi = fit.variants.indexOf(r);
|
|
21061
21389
|
return (id) => id >= 0 && id <= 3 ? base + vi * 4 + id : id;
|
|
21062
21390
|
}
|
|
21391
|
+
var ORDINAL_SYLLABLES = new Set(Array.from({ length: 84 }, (_, i) => hangulOrdinal(i)));
|
|
21392
|
+
function literalMarkerDepth(text) {
|
|
21393
|
+
const t = text.trimStart();
|
|
21394
|
+
let m;
|
|
21395
|
+
if ((m = /^([가-힣])\.\s+(.*)$/s.exec(t)) && ORDINAL_SYLLABLES.has(m[1])) return { depth: 1, content: m[2] };
|
|
21396
|
+
if (m = /^(\d{1,3})\)\s+(.*)$/s.exec(t)) return { depth: 2, content: m[2] };
|
|
21397
|
+
if ((m = /^([가-힣])\)\s+(.*)$/s.exec(t)) && ORDINAL_SYLLABLES.has(m[1])) return { depth: 3, content: m[2] };
|
|
21398
|
+
if (m = /^\((\d{1,3})\)\s+(.*)$/s.exec(t)) return { depth: 4, content: m[2] };
|
|
21399
|
+
if ((m = /^\(([가-힣])\)\s+(.*)$/s.exec(t)) && ORDINAL_SYLLABLES.has(m[1])) return { depth: 5, content: m[2] };
|
|
21400
|
+
if (m = /^([①-⑳㉑-㉟㊱-㊿])\s+(.*)$/s.exec(t)) return { depth: 6, content: m[2] };
|
|
21401
|
+
if (m = /^([㉮-㉻])\s+(.*)$/s.exec(t)) return { depth: 7, content: m[2] };
|
|
21402
|
+
if (m = /^□\s+(.*)$/s.exec(t)) return { depth: 0, content: m[1] };
|
|
21403
|
+
if (m = /^[○ㅇ]\s+(.*)$/s.exec(t)) return { depth: 1, content: m[1] };
|
|
21404
|
+
if (m = /^ㆍ\s+(.*)$/s.exec(t)) return { depth: 3, content: m[1] };
|
|
21405
|
+
return null;
|
|
21406
|
+
}
|
|
21407
|
+
function reclassifyLiteralItems(blocks, depthOffset) {
|
|
21408
|
+
for (const b of blocks) {
|
|
21409
|
+
if (b.type !== "paragraph") continue;
|
|
21410
|
+
const raw = (b.text || "").trimStart();
|
|
21411
|
+
if (!raw || raw.startsWith("\u203B") || /^\*\s/.test(raw) || /^<(center|right)>/i.test(raw)) continue;
|
|
21412
|
+
const lit = literalMarkerDepth(raw);
|
|
21413
|
+
if (!lit || !lit.content.trim()) continue;
|
|
21414
|
+
b.type = "list_item";
|
|
21415
|
+
b.text = lit.content;
|
|
21416
|
+
b.indent = Math.max(lit.depth - depthOffset, 0);
|
|
21417
|
+
b.ordered = lit.depth !== 0;
|
|
21418
|
+
}
|
|
21419
|
+
}
|
|
21063
21420
|
function precomputeGongmunList(blocks, gongmun) {
|
|
21064
21421
|
const result = /* @__PURE__ */ new Map();
|
|
21422
|
+
const indentVariants = [];
|
|
21423
|
+
const variantIdx = /* @__PURE__ */ new Map();
|
|
21424
|
+
const variantOf = (marker, depth) => {
|
|
21425
|
+
if (!marker || gongmun.numbering !== "standard") return void 0;
|
|
21426
|
+
const w = markerWidth(marker, gongmun.bodyHeight);
|
|
21427
|
+
const rep = -levelIndent(depth, gongmun.bodyHeight, gongmun.numbering, gongmun.sizes, gongmun.bullet2, usesAsteriskThird(gongmun.preset)).indent;
|
|
21428
|
+
if (w === rep) return void 0;
|
|
21429
|
+
const key = `${depth}:${w}`;
|
|
21430
|
+
let vi = variantIdx.get(key);
|
|
21431
|
+
if (vi === void 0) {
|
|
21432
|
+
vi = indentVariants.length;
|
|
21433
|
+
indentVariants.push({ depth, widthHu: w });
|
|
21434
|
+
variantIdx.set(key, vi);
|
|
21435
|
+
}
|
|
21436
|
+
return vi;
|
|
21437
|
+
};
|
|
21065
21438
|
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;
|
|
21439
|
+
reclassifyLiteralItems(blocks, depthOffset);
|
|
21066
21440
|
let i = 0;
|
|
21067
21441
|
while (i < blocks.length) {
|
|
21068
21442
|
if (blocks[i].type !== "list_item") {
|
|
@@ -21089,14 +21463,21 @@ function precomputeGongmunList(blocks, gongmun) {
|
|
|
21089
21463
|
break;
|
|
21090
21464
|
}
|
|
21091
21465
|
const depths = run.map((bi) => Math.min(Math.max((blocks[bi].indent || 0) + depthOffset, 0), GONGMUN_LIST_LEVELS - 1));
|
|
21092
|
-
const
|
|
21466
|
+
const measured = usesReportFonts(gongmun.preset);
|
|
21467
|
+
const isRef = (bi) => measured && ((blocks[bi].text || "").trimStart().startsWith("\u203B") || blocks[bi].marker === "*");
|
|
21468
|
+
const activeK = run.map((_, k) => k).filter((k) => !isRef(run[k]));
|
|
21469
|
+
const activeDepths = activeK.map((k) => depths[k]);
|
|
21470
|
+
const suppress = gongmun.suppressSingle && gongmun.numbering === "standard" ? computeSuppression(activeDepths) : activeDepths.map(() => false);
|
|
21093
21471
|
const numberer = new GongmunNumberer(gongmun.numbering, gongmun.bullet2, usesAsteriskThird(gongmun.preset));
|
|
21094
21472
|
run.forEach((bi, k) => {
|
|
21095
|
-
|
|
21096
|
-
|
|
21473
|
+
if (isRef(run[k])) result.set(bi, { marker: "", depth: depths[k] });
|
|
21474
|
+
});
|
|
21475
|
+
activeK.forEach((k, j) => {
|
|
21476
|
+
const marker = numberer.next(activeDepths[j], suppress[j]);
|
|
21477
|
+
result.set(run[k], { marker, depth: depths[k], indentVariant: variantOf(marker, depths[k]) });
|
|
21097
21478
|
});
|
|
21098
21479
|
}
|
|
21099
|
-
return result;
|
|
21480
|
+
return { items: result, indentVariants };
|
|
21100
21481
|
}
|
|
21101
21482
|
|
|
21102
21483
|
// src/hwpx/gen-gaejosik.ts
|
|
@@ -21114,26 +21495,29 @@ function emptyPara(paraPrId = PARA_NORMAL, pageBreak = false, charPrId = CHAR_NO
|
|
|
21114
21495
|
const brk = pageBreak ? ` pageBreak="1"` : "";
|
|
21115
21496
|
return `<hp:p paraPrIDRef="${paraPrId}" styleIDRef="0"${brk}><hp:run charPrIDRef="${charPrId}"><hp:t></hp:t></hp:run></hp:p>`;
|
|
21116
21497
|
}
|
|
21117
|
-
var gjTableId =
|
|
21498
|
+
var gjTableId = GJ_TABLE_ID_BASE;
|
|
21499
|
+
function resetGjTableIds() {
|
|
21500
|
+
gjTableId = GJ_TABLE_ID_BASE;
|
|
21501
|
+
}
|
|
21118
21502
|
function cell(opts) {
|
|
21119
21503
|
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>`;
|
|
21504
|
+
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
21505
|
}
|
|
21122
|
-
function table(rows, w, h, cols, tblBf = 1) {
|
|
21123
|
-
const outLR = w + 566 >
|
|
21506
|
+
function table(rows, w, h, cols, tblBf = 1, bodyWidth = GAEJOSIK_BASE_WIDTH) {
|
|
21507
|
+
const outLR = w + 566 > bodyWidth ? 0 : 283;
|
|
21124
21508
|
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
21509
|
}
|
|
21126
|
-
function buildGaejosikCover(title, gongmun) {
|
|
21127
|
-
const g = coverGeom(gongmun.bodyHeight, gongmun.sizes);
|
|
21510
|
+
function buildGaejosikCover(title, gongmun, bodyWidth = GAEJOSIK_BASE_WIDTH) {
|
|
21511
|
+
const g = coverGeom(gongmun.bodyHeight, gongmun.sizes, bodyWidth);
|
|
21128
21512
|
const sz = gaejosikSizes(gongmun.bodyHeight, gongmun.sizes);
|
|
21129
21513
|
const innerW = g.totalW - 282;
|
|
21130
21514
|
const titleCharPr = simulateWrap(title, innerW, innerW, sz.coverTitle, 100, "keep").lines > 2 ? GJ_CHAR_COVER_SUB : GJ_CHAR_COVER_TITLE;
|
|
21131
21515
|
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
21516
|
const barEmpty = emptyPara(GJ_PARA_BAR, false, GJ_CHAR_BAR);
|
|
21133
21517
|
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 });
|
|
21518
|
+
const titleRow = cell({ bf: 1, col: 0, colSpan: 3, row: 1, w: g.totalW, h: g.titleH, paras: titlePara, name: "__kordoc_h1" });
|
|
21135
21519
|
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);
|
|
21520
|
+
const tbl2 = table([topRow, titleRow, botRow], g.totalW, g.barH + g.titleH + g.botBarH, 3, 1, bodyWidth);
|
|
21137
21521
|
const host = `<hp:p paraPrIDRef="${PARA_NORMAL}" styleIDRef="0"><hp:run charPrIDRef="${CHAR_NORMAL}">${tbl2}</hp:run></hp:p>`;
|
|
21138
21522
|
const date = gongmun.cover?.date ?? formatGaejosikDate(/* @__PURE__ */ new Date());
|
|
21139
21523
|
const org = gongmun.cover?.org ?? "";
|
|
@@ -21147,48 +21531,51 @@ function buildGaejosikCover(title, gongmun) {
|
|
|
21147
21531
|
...org ? [...Array(4).fill(subEmpty), sub(org)] : []
|
|
21148
21532
|
];
|
|
21149
21533
|
}
|
|
21150
|
-
function buildTocBanner(gongmun) {
|
|
21534
|
+
function buildTocBanner(gongmun, bodyWidth = GAEJOSIK_BASE_WIDTH) {
|
|
21151
21535
|
const g = tocBannerGeom(gongmun.bodyHeight, gongmun.sizes);
|
|
21152
21536
|
const bfs = [GJ_BF_BAR_DARK, 1, GJ_BF_TOC_STRIPE, 1, GJ_BF_TOC_STRIPE, 1, GJ_BF_BAR_DARK];
|
|
21153
21537
|
const filler = emptyPara(GJ_PARA_BAR, false, GJ_CHAR_BAR);
|
|
21154
21538
|
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
21539
|
const row = g.colWs.map(
|
|
21156
|
-
(w, c) => cell({ bf: bfs[c], col: c, w, h: g.h, paras: c === 3 ? label : filler })
|
|
21540
|
+
(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
21541
|
).join("");
|
|
21158
|
-
const banner = table([row], g.colWs.reduce((a, b) => a + b, 0), g.h, 7, 2);
|
|
21542
|
+
const banner = table([row], g.colWs.reduce((a, b) => a + b, 0), g.h, 7, 2, bodyWidth);
|
|
21159
21543
|
return `<hp:p paraPrIDRef="${GJ_PARA_BAR}" styleIDRef="0" pageBreak="1"><hp:run charPrIDRef="${CHAR_NORMAL}">${banner}</hp:run></hp:p>`;
|
|
21160
21544
|
}
|
|
21161
|
-
function buildGaejosikToc(chapters, gongmun) {
|
|
21162
|
-
const
|
|
21545
|
+
function buildGaejosikToc(chapters, gongmun, bodyWidth = GAEJOSIK_BASE_WIDTH) {
|
|
21546
|
+
const boxW = Math.round(TOC_GEOM.boxW * bodyWidth / GAEJOSIK_BASE_WIDTH);
|
|
21547
|
+
const label = buildTocBanner(gongmun, bodyWidth);
|
|
21163
21548
|
const items = chapters.map(
|
|
21164
21549
|
(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
21550
|
).join("");
|
|
21166
21551
|
const box = table(
|
|
21167
|
-
[cell({ bf: GJ_BF_TOC_BOX, col: 0, w:
|
|
21168
|
-
|
|
21552
|
+
[cell({ bf: GJ_BF_TOC_BOX, col: 0, w: boxW, h: TOC_GEOM.boxH, paras: items, name: "__kordoc_toc" })],
|
|
21553
|
+
boxW,
|
|
21169
21554
|
TOC_GEOM.boxH,
|
|
21170
|
-
1
|
|
21555
|
+
1,
|
|
21556
|
+
1,
|
|
21557
|
+
bodyWidth
|
|
21171
21558
|
);
|
|
21172
21559
|
const host = `<hp:p paraPrIDRef="${GJ_PARA_BAR}" styleIDRef="0"><hp:run charPrIDRef="${CHAR_NORMAL}">${box}</hp:run></hp:p>`;
|
|
21173
21560
|
return [label, emptyPara(GJ_PARA_BAR, false, GJ_CHAR_BAR), host];
|
|
21174
21561
|
}
|
|
21175
|
-
function buildGaejosikBodyTitle(title, gongmun) {
|
|
21176
|
-
const g =
|
|
21562
|
+
function buildGaejosikBodyTitle(title, gongmun, bodyWidth = GAEJOSIK_BASE_WIDTH) {
|
|
21563
|
+
const g = coverGeom(gongmun.bodyHeight, gongmun.sizes, bodyWidth);
|
|
21177
21564
|
const bt = bodyTitleGeom(gongmun.bodyHeight, gongmun.sizes);
|
|
21178
21565
|
const barEmpty = emptyPara(GJ_PARA_BAR, false, GJ_CHAR_BAR);
|
|
21179
21566
|
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
21567
|
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 });
|
|
21568
|
+
const titleRow = cell({ bf: 1, col: 0, colSpan: 3, row: 1, w: g.totalW, h: bt.titleH, paras: titlePara, name: "__kordoc_skip" });
|
|
21182
21569
|
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);
|
|
21570
|
+
const box = table([topRow, titleRow, botRow], g.totalW, bt.barH * 2 + bt.titleH, 3, 1, bodyWidth);
|
|
21184
21571
|
return `<hp:p paraPrIDRef="${PARA_NORMAL}" styleIDRef="0"><hp:run charPrIDRef="${CHAR_NORMAL}">${box}</hp:run></hp:p>`;
|
|
21185
21572
|
}
|
|
21186
|
-
function buildGaejosikChapter(n, title, gongmun) {
|
|
21187
|
-
const g = gongmun ? chapterGeom(gongmun.bodyHeight, gongmun.sizes) : chapterGeom(1500);
|
|
21573
|
+
function buildGaejosikChapter(n, title, gongmun, bodyWidth = GAEJOSIK_BASE_WIDTH, srcLevel = 2) {
|
|
21574
|
+
const g = gongmun ? chapterGeom(gongmun.bodyHeight, gongmun.sizes, bodyWidth) : chapterGeom(1500, {}, bodyWidth);
|
|
21188
21575
|
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
21576
|
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);
|
|
21577
|
+
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)}` });
|
|
21578
|
+
const tbl2 = table([row], g.numW + g.gapW + g.titleW, g.rowH, 3, 1, bodyWidth);
|
|
21192
21579
|
return `<hp:p paraPrIDRef="${GJ_PARA_CHAPTER}" styleIDRef="0"><hp:run charPrIDRef="${CHAR_NORMAL}">${tbl2}</hp:run></hp:p>`;
|
|
21193
21580
|
}
|
|
21194
21581
|
|
|
@@ -21556,16 +21943,22 @@ var AUTONUM_PREFIX_RE = /^(?:[0-90-9a-zA-Z가-힣]{1,6}[.)\]:]|[([][0-90-
|
|
|
21556
21943
|
function htmlCellInnerToLines(inner) {
|
|
21557
21944
|
let hadNonText = false;
|
|
21558
21945
|
let work = inner;
|
|
21946
|
+
const imgSrcs = [];
|
|
21559
21947
|
if (/<table[\s>]/i.test(work)) {
|
|
21560
21948
|
hadNonText = true;
|
|
21561
21949
|
work = removeNestedTables(work);
|
|
21562
21950
|
}
|
|
21563
21951
|
if (/<img\s/i.test(work)) {
|
|
21564
21952
|
hadNonText = true;
|
|
21565
|
-
work = work.replace(/<img\s(?:"[^"]*"|'[^']*'|[^>"'])*?>/gi,
|
|
21953
|
+
work = work.replace(/<img\s(?:"[^"]*"|'[^']*'|[^>"'])*?>/gi, (tag) => {
|
|
21954
|
+
const m = /\bsrc\s*=\s*(?:"([^"]*)"|'([^']*)')/i.exec(tag);
|
|
21955
|
+
const src = m?.[1] ?? m?.[2];
|
|
21956
|
+
if (src) imgSrcs.push(src);
|
|
21957
|
+
return "";
|
|
21958
|
+
});
|
|
21566
21959
|
}
|
|
21567
21960
|
const lines = work.split(/<br\s*\/?>/gi).map((s) => s.trim()).filter((s) => s.length > 0);
|
|
21568
|
-
return { lines, hadNonText };
|
|
21961
|
+
return { lines, hadNonText, imgSrcs };
|
|
21569
21962
|
}
|
|
21570
21963
|
function extractTopLevelTables(html) {
|
|
21571
21964
|
const result = [];
|
|
@@ -21660,13 +22053,19 @@ function dataCellSpec(opts) {
|
|
|
21660
22053
|
function normalizeAnchor(s) {
|
|
21661
22054
|
return s.toLowerCase().replace(/[^\p{L}\p{N}]/gu, "").slice(0, 24);
|
|
21662
22055
|
}
|
|
21663
|
-
function
|
|
22056
|
+
function normalizeRowAnchor(cells) {
|
|
22057
|
+
const j = cells.map(normalizeAnchor).join("|");
|
|
22058
|
+
return /[\p{L}\p{N}]/u.test(j) ? j.slice(0, 64) : "";
|
|
22059
|
+
}
|
|
22060
|
+
function takeProfile(remap, rows, cols, anchor, seq, rowAnchor = "") {
|
|
21664
22061
|
if (!remap) return null;
|
|
21665
22062
|
for (const t of remap.tables) {
|
|
21666
22063
|
if (t.used) continue;
|
|
21667
22064
|
if (t.rows !== rows || t.cols !== cols) continue;
|
|
21668
22065
|
if (t.anchor && anchor) {
|
|
21669
22066
|
if (t.anchor !== anchor) continue;
|
|
22067
|
+
} else if (t.anchorRow && rowAnchor) {
|
|
22068
|
+
if (t.anchorRow !== rowAnchor) continue;
|
|
21670
22069
|
} else if (t.index !== seq) {
|
|
21671
22070
|
continue;
|
|
21672
22071
|
}
|
|
@@ -21681,10 +22080,10 @@ function parseHu(s) {
|
|
|
21681
22080
|
return Number.isFinite(n) ? n : void 0;
|
|
21682
22081
|
}
|
|
21683
22082
|
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"/>`;
|
|
22083
|
+
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
22084
|
}
|
|
21686
22085
|
function borderFillDefToXml(id, def) {
|
|
21687
|
-
const fill = def.fill?.faceColor ? `<hh:fillBrush><hh:winBrush faceColor="${def.fill.faceColor}" hatchColor="#000000" alpha="0"/></hh:fillBrush>` : "";
|
|
22086
|
+
const fill = def.fill?.faceColor ? `<hh:fillBrush><hh:winBrush faceColor="${escapeXml(def.fill.faceColor)}" hatchColor="#000000" alpha="0"/></hh:fillBrush>` : "";
|
|
21688
22087
|
return ` <hh:borderFill id="${id}" threeD="0" shadow="0" centerLine="NONE" breakCellSeparateLine="0">
|
|
21689
22088
|
<hh:slash type="NONE" Crooked="0" isCounter="0"/>
|
|
21690
22089
|
<hh:backSlash type="NONE" Crooked="0" isCounter="0"/>
|
|
@@ -21695,28 +22094,45 @@ function borderFillDefToXml(id, def) {
|
|
|
21695
22094
|
${fill}` : ""}
|
|
21696
22095
|
</hh:borderFill>`;
|
|
21697
22096
|
}
|
|
21698
|
-
var
|
|
21699
|
-
function profileCharPrXml(id, def) {
|
|
21700
|
-
const height = parseHu(def.height_hwpunit) ?? 1e3;
|
|
21701
|
-
const color = def.textColor ?? "#000000";
|
|
21702
|
-
|
|
21703
|
-
|
|
22097
|
+
var LEGACY_PROFILE_FONT_MAX = 2;
|
|
22098
|
+
function profileCharPrXml(id, def, namedFontId) {
|
|
22099
|
+
const height = Math.max(parseHu(def.height_hwpunit) ?? 1e3, 100);
|
|
22100
|
+
const color = escapeXml(def.textColor ?? "#000000");
|
|
22101
|
+
let font = 0;
|
|
22102
|
+
let restFont = 0;
|
|
22103
|
+
if (namedFontId != null) {
|
|
22104
|
+
font = namedFontId;
|
|
22105
|
+
} else {
|
|
22106
|
+
const rawFont = def.fontRef_hangul != null ? parseInt(def.fontRef_hangul, 10) || 0 : 0;
|
|
22107
|
+
font = rawFont >= 0 && rawFont <= LEGACY_PROFILE_FONT_MAX ? rawFont : 0;
|
|
22108
|
+
restFont = font;
|
|
22109
|
+
}
|
|
21704
22110
|
const boldAttr = def.bold ? ` bold="1"` : "";
|
|
21705
22111
|
const italicAttr = def.italic ? ` italic="1"` : "";
|
|
21706
22112
|
const underline = def.underline ? `
|
|
21707
22113
|
<hh:underline type="BOTTOM" shape="SOLID" color="${color}"/>` : "";
|
|
21708
22114
|
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="${
|
|
22115
|
+
<hh:fontRef hangul="${font}" latin="${font}" hanja="${restFont}" japanese="${restFont}" other="${restFont}" symbol="${restFont}" user="${restFont}"/>
|
|
21710
22116
|
<hh:ratio hangul="100" latin="100" hanja="100" japanese="100" other="100" symbol="100" user="100"/>
|
|
21711
22117
|
<hh:spacing hangul="0" latin="0" hanja="0" japanese="0" other="0" symbol="0" user="0"/>
|
|
21712
22118
|
<hh:relSz hangul="100" latin="100" hanja="100" japanese="100" other="100" symbol="100" user="100"/>
|
|
21713
22119
|
<hh:offset hangul="0" latin="0" hanja="0" japanese="0" other="0" symbol="0" user="0"/>${underline}
|
|
21714
22120
|
</hh:charPr>`;
|
|
21715
22121
|
}
|
|
21716
|
-
function buildProfileRemap(profile, charPrBase, borderFillBase = 3) {
|
|
21717
|
-
const remap = { borderFillXmls: [], charPrXmls: [], tables: [] };
|
|
22122
|
+
function buildProfileRemap(profile, charPrBase, borderFillBase = 3, fontBase = 3) {
|
|
22123
|
+
const remap = { borderFillXmls: [], charPrXmls: [], fontFaces: [], tables: [] };
|
|
21718
22124
|
let bfNext = borderFillBase;
|
|
21719
22125
|
let charNext = charPrBase;
|
|
22126
|
+
const fontIds = /* @__PURE__ */ new Map();
|
|
22127
|
+
const fontIdOf = (name) => {
|
|
22128
|
+
let id = fontIds.get(name);
|
|
22129
|
+
if (id == null) {
|
|
22130
|
+
id = fontBase + remap.fontFaces.length;
|
|
22131
|
+
remap.fontFaces.push(name);
|
|
22132
|
+
fontIds.set(name, id);
|
|
22133
|
+
}
|
|
22134
|
+
return id;
|
|
22135
|
+
};
|
|
21720
22136
|
for (const t of profile.tables) {
|
|
21721
22137
|
const localBf = {};
|
|
21722
22138
|
for (const [key, def] of Object.entries(t.used_border_fills ?? {})) {
|
|
@@ -21727,7 +22143,7 @@ function buildProfileRemap(profile, charPrBase, borderFillBase = 3) {
|
|
|
21727
22143
|
const localChar = {};
|
|
21728
22144
|
for (const [key, def] of Object.entries(t.used_char_prs ?? {})) {
|
|
21729
22145
|
const gid = charNext++;
|
|
21730
|
-
remap.charPrXmls.push(profileCharPrXml(gid, def));
|
|
22146
|
+
remap.charPrXmls.push(profileCharPrXml(gid, def, def.fontName_hangul ? fontIdOf(def.fontName_hangul) : void 0));
|
|
21731
22147
|
localChar[key] = gid;
|
|
21732
22148
|
}
|
|
21733
22149
|
let colWidths;
|
|
@@ -21741,6 +22157,7 @@ function buildProfileRemap(profile, charPrBase, borderFillBase = 3) {
|
|
|
21741
22157
|
cols: t.cols,
|
|
21742
22158
|
// 재정규화 — 추출기는 정규화해 담지만 손편집된 프로필 JSON도 같은 키 공간으로
|
|
21743
22159
|
anchor: t.anchor_text ? normalizeAnchor(t.anchor_text) : void 0,
|
|
22160
|
+
anchorRow: t.anchor_row ? normalizeRowAnchor(t.anchor_row.split("|")) || void 0 : void 0,
|
|
21744
22161
|
width: parseHu(t.width_hwpunit),
|
|
21745
22162
|
colWidths,
|
|
21746
22163
|
cellBf: /* @__PURE__ */ new Map(),
|
|
@@ -21758,17 +22175,208 @@ function buildProfileRemap(profile, charPrBase, borderFillBase = 3) {
|
|
|
21758
22175
|
const h = parseHu(cell2.height_hwpunit);
|
|
21759
22176
|
if (h != null) tr.cellH.set(k, h);
|
|
21760
22177
|
}
|
|
21761
|
-
remap.tables.push(tr);
|
|
22178
|
+
remap.tables.push(tr);
|
|
22179
|
+
}
|
|
22180
|
+
return remap;
|
|
22181
|
+
}
|
|
22182
|
+
|
|
22183
|
+
// src/hwpx/gen-image.ts
|
|
22184
|
+
var PLACEHOLDER_BMP = Uint8Array.from([
|
|
22185
|
+
66,
|
|
22186
|
+
77,
|
|
22187
|
+
58,
|
|
22188
|
+
0,
|
|
22189
|
+
0,
|
|
22190
|
+
0,
|
|
22191
|
+
0,
|
|
22192
|
+
0,
|
|
22193
|
+
0,
|
|
22194
|
+
0,
|
|
22195
|
+
54,
|
|
22196
|
+
0,
|
|
22197
|
+
0,
|
|
22198
|
+
0,
|
|
22199
|
+
40,
|
|
22200
|
+
0,
|
|
22201
|
+
0,
|
|
22202
|
+
0,
|
|
22203
|
+
1,
|
|
22204
|
+
0,
|
|
22205
|
+
0,
|
|
22206
|
+
0,
|
|
22207
|
+
1,
|
|
22208
|
+
0,
|
|
22209
|
+
0,
|
|
22210
|
+
0,
|
|
22211
|
+
1,
|
|
22212
|
+
0,
|
|
22213
|
+
24,
|
|
22214
|
+
0,
|
|
22215
|
+
0,
|
|
22216
|
+
0,
|
|
22217
|
+
0,
|
|
22218
|
+
0,
|
|
22219
|
+
4,
|
|
22220
|
+
0,
|
|
22221
|
+
0,
|
|
22222
|
+
0,
|
|
22223
|
+
19,
|
|
22224
|
+
11,
|
|
22225
|
+
0,
|
|
22226
|
+
0,
|
|
22227
|
+
19,
|
|
22228
|
+
11,
|
|
22229
|
+
0,
|
|
22230
|
+
0,
|
|
22231
|
+
0,
|
|
22232
|
+
0,
|
|
22233
|
+
0,
|
|
22234
|
+
0,
|
|
22235
|
+
0,
|
|
22236
|
+
0,
|
|
22237
|
+
0,
|
|
22238
|
+
0,
|
|
22239
|
+
255,
|
|
22240
|
+
255,
|
|
22241
|
+
255,
|
|
22242
|
+
0
|
|
22243
|
+
]);
|
|
22244
|
+
var PLACEHOLDER_PNG = Uint8Array.from([
|
|
22245
|
+
137,
|
|
22246
|
+
80,
|
|
22247
|
+
78,
|
|
22248
|
+
71,
|
|
22249
|
+
13,
|
|
22250
|
+
10,
|
|
22251
|
+
26,
|
|
22252
|
+
10,
|
|
22253
|
+
0,
|
|
22254
|
+
0,
|
|
22255
|
+
0,
|
|
22256
|
+
13,
|
|
22257
|
+
73,
|
|
22258
|
+
72,
|
|
22259
|
+
68,
|
|
22260
|
+
82,
|
|
22261
|
+
0,
|
|
22262
|
+
0,
|
|
22263
|
+
0,
|
|
22264
|
+
1,
|
|
22265
|
+
0,
|
|
22266
|
+
0,
|
|
22267
|
+
0,
|
|
22268
|
+
1,
|
|
22269
|
+
8,
|
|
22270
|
+
6,
|
|
22271
|
+
0,
|
|
22272
|
+
0,
|
|
22273
|
+
0,
|
|
22274
|
+
31,
|
|
22275
|
+
21,
|
|
22276
|
+
196,
|
|
22277
|
+
137,
|
|
22278
|
+
0,
|
|
22279
|
+
0,
|
|
22280
|
+
0,
|
|
22281
|
+
11,
|
|
22282
|
+
73,
|
|
22283
|
+
68,
|
|
22284
|
+
65,
|
|
22285
|
+
84,
|
|
22286
|
+
120,
|
|
22287
|
+
156,
|
|
22288
|
+
99,
|
|
22289
|
+
0,
|
|
22290
|
+
1,
|
|
22291
|
+
0,
|
|
22292
|
+
0,
|
|
22293
|
+
5,
|
|
22294
|
+
0,
|
|
22295
|
+
1,
|
|
22296
|
+
13,
|
|
22297
|
+
10,
|
|
22298
|
+
45,
|
|
22299
|
+
180,
|
|
22300
|
+
0,
|
|
22301
|
+
0,
|
|
22302
|
+
0,
|
|
22303
|
+
0,
|
|
22304
|
+
73,
|
|
22305
|
+
69,
|
|
22306
|
+
78,
|
|
22307
|
+
68,
|
|
22308
|
+
174,
|
|
22309
|
+
66,
|
|
22310
|
+
96,
|
|
22311
|
+
130
|
|
22312
|
+
]);
|
|
22313
|
+
var IMAGE_EXT_MIME = {
|
|
22314
|
+
png: "image/png",
|
|
22315
|
+
jpg: "image/jpeg",
|
|
22316
|
+
jpeg: "image/jpeg",
|
|
22317
|
+
gif: "image/gif",
|
|
22318
|
+
bmp: "image/bmp",
|
|
22319
|
+
tif: "image/tiff",
|
|
22320
|
+
tiff: "image/tiff"
|
|
22321
|
+
};
|
|
22322
|
+
var ImageRegistry = class {
|
|
22323
|
+
byUrl = /* @__PURE__ */ new Map();
|
|
22324
|
+
ids = /* @__PURE__ */ new Set();
|
|
22325
|
+
parts = [];
|
|
22326
|
+
picSeq = 0;
|
|
22327
|
+
/** url 등록(중복 시 기존 파트) — 수용 불가면 null */
|
|
22328
|
+
take(url) {
|
|
22329
|
+
const cached = this.byUrl.get(url);
|
|
22330
|
+
if (cached !== void 0) return cached;
|
|
22331
|
+
let part = null;
|
|
22332
|
+
const m = /^([A-Za-z0-9._-]+)\.([A-Za-z0-9]+)$/.exec(url);
|
|
22333
|
+
const mime = m ? IMAGE_EXT_MIME[m[2].toLowerCase()] : void 0;
|
|
22334
|
+
if (m && mime && !url.includes("..")) {
|
|
22335
|
+
let itemId = m[1].replace(/[^A-Za-z0-9_]/g, "_");
|
|
22336
|
+
let n = 1;
|
|
22337
|
+
while (this.ids.has(itemId)) itemId = `${m[1].replace(/[^A-Za-z0-9_]/g, "_")}_${n++}`;
|
|
22338
|
+
this.ids.add(itemId);
|
|
22339
|
+
part = {
|
|
22340
|
+
name: `BinData/${url}`,
|
|
22341
|
+
itemId,
|
|
22342
|
+
mime,
|
|
22343
|
+
data: m[2].toLowerCase() === "bmp" ? PLACEHOLDER_BMP : PLACEHOLDER_PNG
|
|
22344
|
+
};
|
|
22345
|
+
this.parts.push(part);
|
|
22346
|
+
}
|
|
22347
|
+
this.byUrl.set(url, part);
|
|
22348
|
+
return part;
|
|
21762
22349
|
}
|
|
21763
|
-
|
|
22350
|
+
/** manifest <opf:item> 조각들 (실측: isEmbeded="1") */
|
|
22351
|
+
manifestItems() {
|
|
22352
|
+
return this.parts.map((p) => `<opf:item id="${p.itemId}" href="${p.name}" media-type="${p.mime}" isEmbeded="1"/>`);
|
|
22353
|
+
}
|
|
22354
|
+
/** 인라인 <hp:pic> XML — 실측 저장본 미러, treatAsChar=1 (셀·문단 안 배치) */
|
|
22355
|
+
inlinePicXml(part) {
|
|
22356
|
+
const id = PIC_ID_BASE + ++this.picSeq;
|
|
22357
|
+
const s = 1130;
|
|
22358
|
+
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>`;
|
|
22359
|
+
}
|
|
22360
|
+
};
|
|
22361
|
+
var MD_IMAGE_RE = /!\[[^\]]*\]\(([^)\s]+)\)/g;
|
|
22362
|
+
function splitImageRefs(text) {
|
|
22363
|
+
const urls = [];
|
|
22364
|
+
const out = text.replace(MD_IMAGE_RE, (_, url) => {
|
|
22365
|
+
urls.push(url);
|
|
22366
|
+
return "";
|
|
22367
|
+
});
|
|
22368
|
+
return { text: out, urls };
|
|
21764
22369
|
}
|
|
21765
22370
|
|
|
21766
22371
|
// src/hwpx/gen-table.ts
|
|
21767
|
-
var TABLE_ID_BASE =
|
|
22372
|
+
var TABLE_ID_BASE = DATA_TABLE_ID_BASE;
|
|
21768
22373
|
var tableIdCounter = TABLE_ID_BASE;
|
|
21769
22374
|
function nextTableId() {
|
|
21770
22375
|
return ++tableIdCounter;
|
|
21771
22376
|
}
|
|
22377
|
+
function resetTableIds() {
|
|
22378
|
+
tableIdCounter = TABLE_ID_BASE;
|
|
22379
|
+
}
|
|
21772
22380
|
var DATA_TABLE_INSET = 1800;
|
|
21773
22381
|
function anchorOfMarkdownCell(cell2) {
|
|
21774
22382
|
return normalizeAnchor(cell2.replace(/!\[[^\]]*\]\([^)]*\)/g, ""));
|
|
@@ -21787,7 +22395,7 @@ function profileColWidths(tp, colCnt) {
|
|
|
21787
22395
|
var CELL_PAD = 1200;
|
|
21788
22396
|
function cellContentWidth(text, charHeight) {
|
|
21789
22397
|
let max = 0;
|
|
21790
|
-
for (const seg of text.replace(/\*\*|__|`/g, "").split(/<br\s*\/?>/i)) {
|
|
22398
|
+
for (const seg of text.replace(/!\[[^\]]*\]\([^)\s]+\)/g, "").replace(/\*\*|__|`/g, "").split(/<br\s*\/?>/i)) {
|
|
21791
22399
|
const w = measureTextWidth(seg.trim(), charHeight, 100);
|
|
21792
22400
|
if (w > max) max = w;
|
|
21793
22401
|
}
|
|
@@ -21795,7 +22403,7 @@ function cellContentWidth(text, charHeight) {
|
|
|
21795
22403
|
}
|
|
21796
22404
|
function cellMinWordWidth(text, charHeight) {
|
|
21797
22405
|
let max = 0;
|
|
21798
|
-
for (const seg of text.replace(/\*\*|__|`/g, "").split(/<br\s*\/?>/i)) {
|
|
22406
|
+
for (const seg of text.replace(/!\[[^\]]*\]\([^)\s]+\)/g, "").replace(/\*\*|__|`/g, "").split(/<br\s*\/?>/i)) {
|
|
21799
22407
|
for (const word of seg.trim().split(/\s+/)) {
|
|
21800
22408
|
const w = measureTextWidth(word, charHeight, 100);
|
|
21801
22409
|
if (w > max) max = w;
|
|
@@ -21825,7 +22433,7 @@ function computeColWidths(colMax, totalWidth, colMinWord = []) {
|
|
|
21825
22433
|
const sumFreeRaw = [...free].reduce((a, i) => a + raw[i], 0);
|
|
21826
22434
|
if (budget > sumFreeRaw * 1.6) {
|
|
21827
22435
|
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));
|
|
22436
|
+
for (let i = 0; i < colCnt; i++) widths[i] = Math.min(cap, Math.round(raw[i]) + Math.floor(raw[i] / sumRaw * (totalWidth - sumRaw)));
|
|
21829
22437
|
free.clear();
|
|
21830
22438
|
} else {
|
|
21831
22439
|
for (; ; ) {
|
|
@@ -21846,7 +22454,23 @@ function computeColWidths(colMax, totalWidth, colMinWord = []) {
|
|
|
21846
22454
|
}
|
|
21847
22455
|
let rem = totalWidth - widths.reduce((a, b) => a + b, 0);
|
|
21848
22456
|
const order = [...raw.keys()].sort((a, b) => raw[b] - raw[a]);
|
|
21849
|
-
for (let k = 0; rem > 0; k = (k + 1) % colCnt, rem--)
|
|
22457
|
+
for (let k = 0, skipped = 0; rem > 0; k = (k + 1) % colCnt, rem--) {
|
|
22458
|
+
while (widths[order[k]] >= cap && skipped < colCnt) {
|
|
22459
|
+
skipped++;
|
|
22460
|
+
k = (k + 1) % colCnt;
|
|
22461
|
+
}
|
|
22462
|
+
skipped = 0;
|
|
22463
|
+
widths[order[k]]++;
|
|
22464
|
+
}
|
|
22465
|
+
for (let k = 0, spins = 0; rem < 0; k = (k + 1) % colCnt, rem++) {
|
|
22466
|
+
while (widths[order[k]] <= 1 && spins < colCnt) {
|
|
22467
|
+
spins++;
|
|
22468
|
+
k = (k + 1) % colCnt;
|
|
22469
|
+
}
|
|
22470
|
+
if (widths[order[k]] <= 1) break;
|
|
22471
|
+
spins = 0;
|
|
22472
|
+
widths[order[k]]--;
|
|
22473
|
+
}
|
|
21850
22474
|
return widths;
|
|
21851
22475
|
}
|
|
21852
22476
|
function estimateRowHeight(cells, widths, charHeight) {
|
|
@@ -21861,13 +22485,14 @@ function estimateRowHeight(cells, widths, charHeight) {
|
|
|
21861
22485
|
});
|
|
21862
22486
|
return maxLines * Math.round(charHeight * 1.6) + 282;
|
|
21863
22487
|
}
|
|
21864
|
-
function generateTable(rows, theme, style = null, remap = null, seq = 0) {
|
|
22488
|
+
function generateTable(rows, theme, style = null, remap = null, seq = 0, images = null) {
|
|
21865
22489
|
const rowCnt = rows.length;
|
|
21866
22490
|
const colCnt = Math.max(...rows.map((r) => r.length), 1);
|
|
21867
22491
|
const reg = style?.bfRegistry ?? null;
|
|
21868
22492
|
const totalW = style ? reg ? style.totalWidth - DATA_TABLE_INSET : style.totalWidth : 44e3;
|
|
21869
22493
|
const measureH = style?.charHeight ?? 1e3;
|
|
21870
|
-
const
|
|
22494
|
+
const rowAnchor = normalizeRowAnchor((rows[0] ?? []).map((c) => c.replace(/!\[[^\]]*\]\([^)]*\)/g, "")));
|
|
22495
|
+
const prof = takeProfile(remap, rowCnt, colCnt, anchorOfMarkdownCell(rows[0]?.[0] ?? ""), seq, rowAnchor);
|
|
21871
22496
|
const colMax = Array(colCnt).fill(0);
|
|
21872
22497
|
const colMaxBody = Array(colCnt).fill(0);
|
|
21873
22498
|
const colMinWord = Array(colCnt).fill(0);
|
|
@@ -21899,8 +22524,23 @@ function generateTable(rows, theme, style = null, remap = null, seq = 0) {
|
|
|
21899
22524
|
const centered = isHeaderRow || colCentered[colIdx];
|
|
21900
22525
|
const paraPrId = style ? centered ? style.tblCenterParaPr ?? style.centerParaPr : reg ? style.tblLeftParaPr ?? PARA_NORMAL : PARA_NORMAL : PARA_NORMAL;
|
|
21901
22526
|
const p = cell2.split(/<br\s*\/?>/i).map((seg) => {
|
|
22527
|
+
let picRuns = "";
|
|
22528
|
+
if (images) {
|
|
22529
|
+
const { text: rest, urls } = splitImageRefs(seg);
|
|
22530
|
+
if (urls.length > 0) {
|
|
22531
|
+
const pics = urls.map((u) => {
|
|
22532
|
+
const part = images.take(u);
|
|
22533
|
+
return part ? images.inlinePicXml(part) : null;
|
|
22534
|
+
});
|
|
22535
|
+
if (pics.every(Boolean)) {
|
|
22536
|
+
seg = rest;
|
|
22537
|
+
picRuns = `<hp:run charPrIDRef="${cellCharPr}">${pics.join("")}</hp:run>`;
|
|
22538
|
+
}
|
|
22539
|
+
}
|
|
22540
|
+
}
|
|
21902
22541
|
const runs = generateRuns(seg, cellCharPr, prof?.cellChar.has(k) ? void 0 : mapId);
|
|
21903
|
-
|
|
22542
|
+
const body = (runs || (picRuns ? "" : `<hp:run charPrIDRef="${cellCharPr}"><hp:t></hp:t></hp:run>`)) + picRuns;
|
|
22543
|
+
return `<hp:p paraPrIDRef="${paraPrId}" styleIDRef="0">${body}</hp:p>`;
|
|
21904
22544
|
}).join("");
|
|
21905
22545
|
const bf = prof?.cellBf.get(k) ?? (reg ? reg.get(dataCellSpec({
|
|
21906
22546
|
row: rowIdx,
|
|
@@ -21942,23 +22582,37 @@ function layoutHtmlRows(rows) {
|
|
|
21942
22582
|
colCnt = Math.max(colCnt, c);
|
|
21943
22583
|
}
|
|
21944
22584
|
}
|
|
22585
|
+
for (let r = 0; r < rows.length; r++) {
|
|
22586
|
+
for (let c = 0; c < colCnt; c++) {
|
|
22587
|
+
if (!occupied.has(`${r},${c}`)) {
|
|
22588
|
+
placed.push({ r, c, colSpan: 1, rowSpan: 1, inner: "", isHeader: rows[r].tag === "th" });
|
|
22589
|
+
occupied.add(`${r},${c}`);
|
|
22590
|
+
}
|
|
22591
|
+
}
|
|
22592
|
+
}
|
|
22593
|
+
placed.sort((a, b) => a.r - b.r || a.c - b.c);
|
|
21945
22594
|
return { placed, rowCnt: rows.length, colCnt };
|
|
21946
22595
|
}
|
|
21947
22596
|
function unescapeHtml(s) {
|
|
21948
22597
|
return s.replace(/</g, "<").replace(/>/g, ">").replace(/"/g, '"').replace(/'/g, "'").replace(/&/g, "&");
|
|
21949
22598
|
}
|
|
21950
|
-
function generateHtmlTableXml(rawHtml, theme, totalWidth = 44e3, style = null, remap = null, seq = 0) {
|
|
22599
|
+
function generateHtmlTableXml(rawHtml, theme, totalWidth = 44e3, style = null, remap = null, seq = 0, images = null) {
|
|
21951
22600
|
const rows = parseHtmlTable(rawHtml);
|
|
21952
22601
|
if (!rows || rows.length === 0) return null;
|
|
21953
22602
|
const { placed, rowCnt, colCnt } = layoutHtmlRows(rows);
|
|
21954
22603
|
if (rowCnt === 0 || colCnt === 0) return null;
|
|
21955
22604
|
const measureH = style?.charHeight ?? 1e3;
|
|
21956
22605
|
const first = placed.find((p) => p.r === 0 && p.c === 0) ?? placed[0];
|
|
21957
|
-
const
|
|
22606
|
+
const row0 = Array.from({ length: colCnt }, () => "");
|
|
22607
|
+
for (const cell2 of placed) {
|
|
22608
|
+
if (cell2.r === 0 && cell2.c < colCnt) row0[cell2.c] = anchorOfHtmlCell(cell2.inner);
|
|
22609
|
+
}
|
|
22610
|
+
const prof = takeProfile(remap, rowCnt, colCnt, first ? anchorOfHtmlCell(first.inner) : "", seq, normalizeRowAnchor(row0));
|
|
21958
22611
|
const colMax = Array(colCnt).fill(0);
|
|
21959
22612
|
const colMaxBody = Array(colCnt).fill(0);
|
|
21960
22613
|
const colMinWord = Array(colCnt).fill(0);
|
|
21961
|
-
const
|
|
22614
|
+
const cellParsed = placed.map((cell2) => htmlCellInnerToLines(cell2.inner));
|
|
22615
|
+
const cellLines = cellParsed.map((p) => p.lines);
|
|
21962
22616
|
placed.forEach((cell2, i) => {
|
|
21963
22617
|
const w = Math.max(...cellLines[i].map((l) => measureTextWidth(unescapeHtml(l).trim(), measureH, 100)), 0) / cell2.colSpan;
|
|
21964
22618
|
const mw = cell2.colSpan === 1 ? Math.max(...cellLines[i].map((l) => cellMinWordWidth(unescapeHtml(l), measureH)), 0) : 0;
|
|
@@ -21980,7 +22634,32 @@ function generateHtmlTableXml(rawHtml, theme, totalWidth = 44e3, style = null, r
|
|
|
21980
22634
|
let htmlHeaderRows = 0;
|
|
21981
22635
|
while (htmlHeaderRows < rows.length && rows[htmlHeaderRows].tag === "th") htmlHeaderRows++;
|
|
21982
22636
|
const tableRowHeights = Array(rowCnt).fill(cellH);
|
|
21983
|
-
const
|
|
22637
|
+
const meta = placed.map((cell2, i) => {
|
|
22638
|
+
const rowSpan = Math.min(cell2.rowSpan, rowCnt - cell2.r);
|
|
22639
|
+
const lines = cellLines[i];
|
|
22640
|
+
let nestedH = 0;
|
|
22641
|
+
const nestedXmls = [];
|
|
22642
|
+
for (const nested of extractTopLevelTables(cell2.inner)) {
|
|
22643
|
+
const sw = spanW(cell2);
|
|
22644
|
+
const nestedW = Math.max(Math.min(Math.max(sw - 1020, 4e3), sw - 282), 500);
|
|
22645
|
+
const nestedXml = generateHtmlTableXml(nested, theme, nestedW, style ? { ...style, totalWidth: nestedW } : null);
|
|
22646
|
+
if (nestedXml) {
|
|
22647
|
+
nestedXmls.push(nestedXml);
|
|
22648
|
+
const szH = nestedXml.match(/<hp:sz [^>]*height="(\d+)"/)?.[1];
|
|
22649
|
+
nestedH += (szH ? Number(szH) : (nested.match(/<tr[\s>]/gi) ?? []).length * cellH) + 300;
|
|
22650
|
+
}
|
|
22651
|
+
}
|
|
22652
|
+
const usable = Math.max(spanW(cell2) - CELL_PAD, 1e3);
|
|
22653
|
+
let wrapLines = 0;
|
|
22654
|
+
for (const line of lines) wrapLines += Math.max(1, Math.ceil(measureTextWidth(unescapeHtml(line).trim(), measureH, 100) / usable));
|
|
22655
|
+
const lineH = style ? Math.round(measureH * 1.6) : 800;
|
|
22656
|
+
const contentH = Math.max(cellH * rowSpan, Math.max(wrapLines, 1) * lineH + nestedH);
|
|
22657
|
+
const cellHeight = Math.max(prof?.cellH.get(`${cell2.r},${cell2.c}`) ?? 0, contentH);
|
|
22658
|
+
const perRow = Math.ceil(cellHeight / rowSpan);
|
|
22659
|
+
for (let r = cell2.r; r < cell2.r + rowSpan; r++) tableRowHeights[r] = Math.max(tableRowHeights[r], perRow);
|
|
22660
|
+
return { cell: cell2, rowSpan, lines, nestedXmls, imgSrcs: cellParsed[i].imgSrcs };
|
|
22661
|
+
});
|
|
22662
|
+
const tcXmls = meta.map(({ cell: cell2, rowSpan, lines, nestedXmls, imgSrcs }) => {
|
|
21984
22663
|
const k = `${cell2.r},${cell2.c}`;
|
|
21985
22664
|
const isHeader = cell2.isHeader;
|
|
21986
22665
|
const baseCharPr = style ? style.charPr : CHAR_NORMAL;
|
|
@@ -21989,31 +22668,42 @@ function generateHtmlTableXml(rawHtml, theme, totalWidth = 44e3, style = null, r
|
|
|
21989
22668
|
const charPrId = prof?.cellChar.get(k) ?? defaultCharPr;
|
|
21990
22669
|
const centered = isHeader || colCentered[cell2.c];
|
|
21991
22670
|
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
|
-
|
|
22671
|
+
const picUrls = images ? [...imgSrcs] : [];
|
|
22672
|
+
const paras = [];
|
|
22673
|
+
for (const line of lines) {
|
|
22674
|
+
let text = unescapeHtml(line);
|
|
22675
|
+
if (images) {
|
|
22676
|
+
const { text: rest, urls } = splitImageRefs(text);
|
|
22677
|
+
if (urls.length > 0) {
|
|
22678
|
+
picUrls.push(...urls);
|
|
22679
|
+
text = rest;
|
|
22680
|
+
}
|
|
22681
|
+
}
|
|
22682
|
+
if (!images || text.trim() || picUrls.length === 0) {
|
|
22683
|
+
paras.push(`<hp:p paraPrIDRef="${paraPrId}" styleIDRef="0"><hp:run charPrIDRef="${charPrId}"><hp:t>${escapeXml(text)}</hp:t></hp:run></hp:p>`);
|
|
22684
|
+
}
|
|
22685
|
+
}
|
|
22686
|
+
if (images && picUrls.length > 0) {
|
|
22687
|
+
const pics = picUrls.map((u) => {
|
|
22688
|
+
const part = images.take(u);
|
|
22689
|
+
return part ? images.inlinePicXml(part) : null;
|
|
22690
|
+
}).filter(Boolean);
|
|
22691
|
+
if (pics.length > 0) {
|
|
22692
|
+
paras.push(`<hp:p paraPrIDRef="${paraPrId}" styleIDRef="0"><hp:run charPrIDRef="${charPrId}">${pics.join("")}</hp:run></hp:p>`);
|
|
22003
22693
|
}
|
|
22004
22694
|
}
|
|
22695
|
+
for (const nestedXml of nestedXmls) {
|
|
22696
|
+
paras.push(`<hp:p paraPrIDRef="0" styleIDRef="0"><hp:run charPrIDRef="0">${nestedXml}</hp:run></hp:p>`);
|
|
22697
|
+
}
|
|
22005
22698
|
if (paras.length === 0) {
|
|
22006
22699
|
paras.push(`<hp:p paraPrIDRef="${paraPrId}" styleIDRef="0"><hp:run charPrIDRef="${charPrId}"><hp:t></hp:t></hp:run></hp:p>`);
|
|
22007
22700
|
}
|
|
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
|
-
}
|
|
22701
|
+
let cellHeight = 0;
|
|
22702
|
+
for (let r = cell2.r; r < cell2.r + rowSpan; r++) cellHeight += tableRowHeights[r];
|
|
22703
|
+
const rowEnd = cell2.r + rowSpan - 1;
|
|
22014
22704
|
const bf = prof?.cellBf.get(k) ?? (reg ? reg.get(dataCellSpec({
|
|
22015
22705
|
row: cell2.r,
|
|
22016
|
-
rowEnd
|
|
22706
|
+
rowEnd,
|
|
22017
22707
|
col: cell2.c,
|
|
22018
22708
|
colEnd: cell2.c + cell2.colSpan - 1,
|
|
22019
22709
|
rowCnt,
|
|
@@ -22021,7 +22711,7 @@ function generateHtmlTableXml(rawHtml, theme, totalWidth = 44e3, style = null, r
|
|
|
22021
22711
|
headerRows: htmlHeaderRows,
|
|
22022
22712
|
fill: isHeader ? style.headerFill ?? "#E6E6E6" : void 0
|
|
22023
22713
|
})) : 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="${
|
|
22714
|
+
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
22715
|
});
|
|
22026
22716
|
const trXmls = [];
|
|
22027
22717
|
for (let r = 0; r < rowCnt; r++) {
|
|
@@ -22033,12 +22723,15 @@ function generateHtmlTableXml(rawHtml, theme, totalWidth = 44e3, style = null, r
|
|
|
22033
22723
|
}
|
|
22034
22724
|
|
|
22035
22725
|
// src/hwpx/gen-gongmun-extra.ts
|
|
22036
|
-
var extraTableId =
|
|
22726
|
+
var extraTableId = EXTRA_TABLE_ID_BASE;
|
|
22727
|
+
function resetExtraTableIds() {
|
|
22728
|
+
extraTableId = EXTRA_TABLE_ID_BASE;
|
|
22729
|
+
}
|
|
22037
22730
|
function tbl(rows, w, h, cols) {
|
|
22038
22731
|
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
22732
|
}
|
|
22040
22733
|
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>`;
|
|
22734
|
+
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
22735
|
}
|
|
22043
22736
|
function para(text, paraPrId, charPrId) {
|
|
22044
22737
|
return `<hp:p paraPrIDRef="${paraPrId}" styleIDRef="0"><hp:run charPrIDRef="${charPrId}"><hp:t>${escapeXml(text)}</hp:t></hp:run></hp:p>`;
|
|
@@ -22065,7 +22758,7 @@ function buildApprovalTable(labels, reg, approvalCharPr) {
|
|
|
22065
22758
|
return `<hp:p paraPrIDRef="${GONGMUN_RIGHT}" styleIDRef="0"><hp:run charPrIDRef="${CHAR_NORMAL}">${table2}</hp:run></hp:p>`;
|
|
22066
22759
|
}
|
|
22067
22760
|
function hasEndMark(lastText) {
|
|
22068
|
-
return
|
|
22761
|
+
return /(^|\s)끝\s*\.\s*$/.test(lastText.trim());
|
|
22069
22762
|
}
|
|
22070
22763
|
function buildEndMark() {
|
|
22071
22764
|
return para(" \uB05D.", PARA_NORMAL, CHAR_NORMAL);
|
|
@@ -22080,7 +22773,7 @@ function buildTitleBox(title, titleCharPr, barCharPr, bodyWidth, reg) {
|
|
|
22080
22773
|
const barPara = para("", GJ_PARA_BAR, barCharPr);
|
|
22081
22774
|
const rows = [
|
|
22082
22775
|
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) }),
|
|
22776
|
+
tc({ bf: mid, row: 1, col: 0, w, h: TITLE_BOX.titleH, paras: para(title, GONGMUN_TBL_CENTER, titleCharPr), name: "__kordoc_h1" }),
|
|
22084
22777
|
tc({ bf: barBottom, row: 2, col: 0, w, h: TITLE_BOX.barH, paras: barPara })
|
|
22085
22778
|
];
|
|
22086
22779
|
const table2 = tbl(rows, w, TITLE_BOX.barH * 2 + TITLE_BOX.titleH, 1);
|
|
@@ -22127,7 +22820,10 @@ function buildDocHead(g, ids) {
|
|
|
22127
22820
|
out.push(blank());
|
|
22128
22821
|
return out;
|
|
22129
22822
|
}
|
|
22130
|
-
|
|
22823
|
+
function footSep(g) {
|
|
22824
|
+
const bodyMm = 210 - g.margins.left - g.margins.right;
|
|
22825
|
+
return "\u2500".repeat(Math.max(10, Math.round(bodyMm * (46 / 175))));
|
|
22826
|
+
}
|
|
22131
22827
|
function buildDocFoot(g, ids) {
|
|
22132
22828
|
const f = g.docFoot;
|
|
22133
22829
|
const out = [blank()];
|
|
@@ -22135,7 +22831,7 @@ function buildDocFoot(g, ids) {
|
|
|
22135
22831
|
out.push(para(f.sender, GONGMUN_CENTER, ids.sender), blank());
|
|
22136
22832
|
}
|
|
22137
22833
|
const foot = (text) => para(text, GONGMUN_TBL_LEFT, ids.foot);
|
|
22138
|
-
out.push(foot(
|
|
22834
|
+
out.push(foot(footSep(g)));
|
|
22139
22835
|
if (f.drafter || f.reviewer || f.approver)
|
|
22140
22836
|
out.push(foot(`\uAE30\uC548\uC790 ${f.drafter ?? ""} \uAC80\uD1A0\uC790 ${f.reviewer ?? ""} \uACB0\uC7AC\uAD8C\uC790 ${f.approver ?? ""}`));
|
|
22141
22837
|
if (f.cooperator) out.push(foot(`\uD611\uC870\uC790 ${f.cooperator}`));
|
|
@@ -22284,6 +22980,54 @@ var COMMAND_MAP = {
|
|
|
22284
22980
|
vdots: "VDOTS",
|
|
22285
22981
|
ddots: "DDOTS"
|
|
22286
22982
|
};
|
|
22983
|
+
var EQEDIT_FUNCTIONS = /* @__PURE__ */ new Set([
|
|
22984
|
+
"sin",
|
|
22985
|
+
"cos",
|
|
22986
|
+
"tan",
|
|
22987
|
+
"cot",
|
|
22988
|
+
"sec",
|
|
22989
|
+
"csc",
|
|
22990
|
+
"arcsin",
|
|
22991
|
+
"arccos",
|
|
22992
|
+
"arctan",
|
|
22993
|
+
"sinh",
|
|
22994
|
+
"cosh",
|
|
22995
|
+
"tanh",
|
|
22996
|
+
"coth",
|
|
22997
|
+
"log",
|
|
22998
|
+
"ln",
|
|
22999
|
+
"exp",
|
|
23000
|
+
"det",
|
|
23001
|
+
"gcd",
|
|
23002
|
+
"mod",
|
|
23003
|
+
"max",
|
|
23004
|
+
"min",
|
|
23005
|
+
"arg",
|
|
23006
|
+
"deg",
|
|
23007
|
+
"hom",
|
|
23008
|
+
"ker",
|
|
23009
|
+
"Pr"
|
|
23010
|
+
]);
|
|
23011
|
+
{
|
|
23012
|
+
const reverse = /* @__PURE__ */ new Map();
|
|
23013
|
+
const prefer = (name, a, b) => {
|
|
23014
|
+
if (a === name) return a;
|
|
23015
|
+
if (b === name) return b;
|
|
23016
|
+
const aAlpha = /^[A-Za-z]+$/.test(a);
|
|
23017
|
+
const bAlpha = /^[A-Za-z]+$/.test(b);
|
|
23018
|
+
if (aAlpha !== bAlpha) return aAlpha ? a : b;
|
|
23019
|
+
return a < b ? a : b;
|
|
23020
|
+
};
|
|
23021
|
+
for (const [token, latex] of Object.entries(CONVERT_MAP)) {
|
|
23022
|
+
const m = /^\\([A-Za-z]+)$/.exec(latex);
|
|
23023
|
+
if (!m) continue;
|
|
23024
|
+
const name = m[1];
|
|
23025
|
+
if (name in COMMAND_MAP) continue;
|
|
23026
|
+
const prev = reverse.get(name);
|
|
23027
|
+
reverse.set(name, prev === void 0 ? token : prefer(name, token, prev));
|
|
23028
|
+
}
|
|
23029
|
+
for (const [name, token] of reverse) COMMAND_MAP[name] = token;
|
|
23030
|
+
}
|
|
22287
23031
|
var ACCENT_COMMANDS = {
|
|
22288
23032
|
bar: "bar",
|
|
22289
23033
|
overline: "bar",
|
|
@@ -22377,8 +23121,20 @@ function readCommand(input, idx, depth) {
|
|
|
22377
23121
|
const endIdx = input.indexOf(endTag, env.next);
|
|
22378
23122
|
if (endIdx === -1) return { value: env.value, next: env.next };
|
|
22379
23123
|
const body = convertLatexFragment(input.slice(env.next, endIdx), depth + 1);
|
|
22380
|
-
|
|
22381
|
-
|
|
23124
|
+
const ENV_TOKEN = {
|
|
23125
|
+
matrix: "matrix",
|
|
23126
|
+
pmatrix: "pmatrix",
|
|
23127
|
+
bmatrix: "bmatrix",
|
|
23128
|
+
vmatrix: "dmatrix",
|
|
23129
|
+
cases: "cases",
|
|
23130
|
+
align: "eqalign",
|
|
23131
|
+
"align*": "eqalign",
|
|
23132
|
+
aligned: "eqalign"
|
|
23133
|
+
};
|
|
23134
|
+
const tok = ENV_TOKEN[env.value];
|
|
23135
|
+
if (tok) return { value: `{${tok}{${body}}}`, next: endIdx + endTag.length };
|
|
23136
|
+
if (env.value === "Bmatrix") {
|
|
23137
|
+
return { value: `LEFT { {matrix{${body}}} RIGHT }`, next: endIdx + endTag.length };
|
|
22382
23138
|
}
|
|
22383
23139
|
return { value: body, next: endIdx + endTag.length };
|
|
22384
23140
|
}
|
|
@@ -22410,7 +23166,11 @@ function readCommand(input, idx, depth) {
|
|
|
22410
23166
|
const tok = readGroupOrToken(input, start, depth);
|
|
22411
23167
|
return { value: `"${tok.value}"`, next: tok.next };
|
|
22412
23168
|
}
|
|
22413
|
-
|
|
23169
|
+
const mapped = COMMAND_MAP[command];
|
|
23170
|
+
if (mapped !== void 0) return { value: mapped, next: name.next };
|
|
23171
|
+
if (EQEDIT_FUNCTIONS.has(command)) return { value: command, next: name.next };
|
|
23172
|
+
if (/^[A-Za-z]{2,}$/.test(command)) return { value: `"${command}"`, next: name.next };
|
|
23173
|
+
return { value: command, next: name.next };
|
|
22414
23174
|
}
|
|
22415
23175
|
function convertLatexFragment(input, depth) {
|
|
22416
23176
|
if (depth > MAX_GROUP_DEPTH) return normalizeEqEdit(input);
|
|
@@ -22490,44 +23250,47 @@ function generateSecPr(gongmun) {
|
|
|
22490
23250
|
m.header = gongmun.headerFooter;
|
|
22491
23251
|
m.footer = gongmun.headerFooter;
|
|
22492
23252
|
}
|
|
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="
|
|
23253
|
+
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
23254
|
const colPr = `<hp:ctrl><hp:colPr id="" type="NEWSPAPER" layout="LEFT" colCount="1" sameSz="1" sameGap="0"/></hp:ctrl>`;
|
|
22495
23255
|
return secPr + colPr + (gongmun?.pageNumbers ? pageNumCtrl() : "");
|
|
22496
23256
|
}
|
|
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
|
-
|
|
23257
|
+
var SectionOpener = class {
|
|
23258
|
+
constructor(gongmun) {
|
|
23259
|
+
this.gongmun = gongmun;
|
|
23260
|
+
}
|
|
23261
|
+
pending = true;
|
|
23262
|
+
/** 아직 secPr를 나를 첫 문단이 안 나왔는가 */
|
|
23263
|
+
get isFirst() {
|
|
23264
|
+
return this.pending;
|
|
23265
|
+
}
|
|
23266
|
+
/** 일반 문단 XML의 첫 run에 secPr 주입 (소비) */
|
|
23267
|
+
inject(xml) {
|
|
23268
|
+
this.pending = false;
|
|
23269
|
+
return xml.replace(
|
|
23270
|
+
/<hp:run charPrIDRef="(\d+)">/,
|
|
23271
|
+
`<hp:run charPrIDRef="$1">${generateSecPr(this.gongmun)}`
|
|
23272
|
+
);
|
|
23273
|
+
}
|
|
23274
|
+
/**
|
|
23275
|
+
* 표·차트·수식처럼 첫 run에 직접 못 싣는 블록 앞에 secPr 전용 빈 문단을 방출 (소비).
|
|
23276
|
+
* 이미 소비됐으면 아무것도 안 한다.
|
|
23277
|
+
*/
|
|
23278
|
+
emitCarrier(paraXmls) {
|
|
23279
|
+
if (!this.pending) return;
|
|
23280
|
+
this.pending = false;
|
|
23281
|
+
const secRun = `<hp:run charPrIDRef="0">${generateSecPr(this.gongmun)}<hp:t></hp:t></hp:run>`;
|
|
23282
|
+
paraXmls.push(`<hp:p paraPrIDRef="0" styleIDRef="0">${secRun}</hp:p>`);
|
|
23283
|
+
}
|
|
23284
|
+
/** 빈 문서 폴백 — secPr를 실은 빈 단락 XML */
|
|
23285
|
+
emptyDoc() {
|
|
23286
|
+
this.pending = false;
|
|
23287
|
+
return `<hp:p paraPrIDRef="0" styleIDRef="0"><hp:run charPrIDRef="0">${generateSecPr(this.gongmun)}<hp:t></hp:t></hp:run></hp:p>`;
|
|
23288
|
+
}
|
|
23289
|
+
};
|
|
23290
|
+
function buildPreamble(blocks, ctx) {
|
|
23291
|
+
const { gongmun, dfIds, bfReg, tableStyle, richAssets, gjBodyW } = ctx;
|
|
22530
23292
|
const preamble = [];
|
|
23293
|
+
let hasFrontPages = false;
|
|
22531
23294
|
if (gongmun?.reportInfo && dfIds) {
|
|
22532
23295
|
preamble.push(buildReportInfo(gongmun, dfIds));
|
|
22533
23296
|
}
|
|
@@ -22545,278 +23308,340 @@ function blocksToSectionXml(blocks, theme, gongmun, gongmunList = gongmun ? prec
|
|
|
22545
23308
|
}
|
|
22546
23309
|
if (gongmun && (gongmun.cover || gongmun.toc)) {
|
|
22547
23310
|
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
23311
|
let coverTitle = "";
|
|
22550
23312
|
const hide = (xml, hideHeader) => gongmun.pageNumbers ? xml.replace(/<hp:run charPrIDRef="(\d+)">/, `<hp:run charPrIDRef="$1">${pageHidingCtrl(hideHeader)}`) : xml;
|
|
22551
23313
|
if (gongmun.cover && h1Idx >= 0) {
|
|
22552
23314
|
coverTitle = (blocks[h1Idx].text || "").trim();
|
|
22553
|
-
const coverPart = buildGaejosikCover(coverTitle, gongmun);
|
|
23315
|
+
const coverPart = buildGaejosikCover(coverTitle, gongmun, gjBodyW);
|
|
22554
23316
|
coverPart[0] = hide(coverPart[0], true);
|
|
22555
23317
|
preamble.push(...coverPart);
|
|
22556
|
-
coverH1Idx = h1Idx;
|
|
23318
|
+
ctx.coverH1Idx = h1Idx;
|
|
22557
23319
|
hasFrontPages = true;
|
|
22558
23320
|
}
|
|
22559
|
-
|
|
22560
|
-
|
|
23321
|
+
ctx.gjChapterIdxs = blocks.map((b, i) => b.type === "heading" && (b.level ?? 1) <= 2 && i !== ctx.coverH1Idx ? i : -1).filter((i) => i >= 0);
|
|
23322
|
+
if (gongmun.toc && ctx.gjChapterIdxs.length > 0) {
|
|
23323
|
+
const chapters = ctx.gjChapterIdxs.map((i) => stripChapterNumber(blocks[i].text || ""));
|
|
23324
|
+
const tocPart = buildGaejosikToc(chapters, gongmun, gjBodyW);
|
|
22561
23325
|
tocPart[0] = hide(tocPart[0], false);
|
|
22562
23326
|
preamble.push(...tocPart);
|
|
22563
23327
|
hasFrontPages = true;
|
|
22564
23328
|
}
|
|
22565
23329
|
if (gongmun.bodyTitleBox && coverTitle && hasFrontPages) {
|
|
22566
|
-
preamble.push(buildGaejosikBodyTitle(coverTitle, gongmun).replace(/^<hp:p /, `<hp:p pageBreak="1" `));
|
|
23330
|
+
preamble.push(buildGaejosikBodyTitle(coverTitle, gongmun, gjBodyW).replace(/^<hp:p /, `<hp:p pageBreak="1" `));
|
|
22567
23331
|
}
|
|
22568
23332
|
}
|
|
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);
|
|
23333
|
+
if (gongmun && !ctx.gaejosik && ctx.coverH1Idx < 0 && bfReg && (gongmun.preset === "report" || gongmun.preset === "plan" || gongmun.preset === "notice")) {
|
|
23334
|
+
ctx.titleBoxH1Idx = blocks.findIndex((b) => b.type === "heading" && (b.level ?? 1) === 1);
|
|
23335
|
+
} else if (gongmun?.press && ctx.coverH1Idx < 0 && dfIds) {
|
|
23336
|
+
ctx.pressH1Idx = blocks.findIndex((b) => b.type === "heading" && (b.level ?? 1) === 1);
|
|
22573
23337
|
}
|
|
22574
23338
|
if (preamble.length > 0) {
|
|
22575
|
-
preamble[0] = preamble[0].replace(` pageBreak="1"`, "")
|
|
22576
|
-
|
|
22577
|
-
|
|
22578
|
-
|
|
22579
|
-
|
|
23339
|
+
preamble[0] = ctx.opener.inject(preamble[0].replace(` pageBreak="1"`, ""));
|
|
23340
|
+
ctx.pendingPageBreak = hasFrontPages && !preamble[preamble.length - 1].includes(`pageBreak="1"`);
|
|
23341
|
+
ctx.pendingNewNum = !!gongmun.pageNumbers && hasFrontPages;
|
|
23342
|
+
ctx.paraXmls.push(...preamble);
|
|
23343
|
+
}
|
|
23344
|
+
}
|
|
23345
|
+
function renderHeading(block, blockIdx, ctx) {
|
|
23346
|
+
const { gongmun, dfIds, tableStyle, bfReg, measured, richAssets, gjBodyW } = ctx;
|
|
23347
|
+
if (gongmun && blockIdx === ctx.coverH1Idx) return "";
|
|
23348
|
+
if (gongmun && blockIdx === ctx.pressH1Idx && dfIds) {
|
|
23349
|
+
const parts = [generateParagraph((block.text || "").trim(), GONGMUN_CENTER, dfIds.pressTitle)];
|
|
23350
|
+
for (const s of gongmun.press?.sub ?? []) {
|
|
23351
|
+
parts.push(generateParagraph(`- ${s} -`, GONGMUN_CENTER, dfIds.pressSub));
|
|
23352
|
+
}
|
|
23353
|
+
return parts.join("\n ");
|
|
23354
|
+
}
|
|
23355
|
+
if (gongmun && blockIdx === ctx.titleBoxH1Idx && tableStyle && bfReg) {
|
|
23356
|
+
return buildTitleBox(
|
|
23357
|
+
(block.text || "").trim(),
|
|
23358
|
+
measured ? GJ_CHAR_BODY_TITLE : CHAR_H1,
|
|
23359
|
+
richAssets ? GJ_CHAR_TITLE_BAR : GONGMUN_TITLE_BAR_CHAR,
|
|
23360
|
+
tableStyle.totalWidth,
|
|
23361
|
+
bfReg
|
|
23362
|
+
);
|
|
23363
|
+
}
|
|
23364
|
+
if (ctx.gaejosik) {
|
|
23365
|
+
const lvl = block.level || 1;
|
|
23366
|
+
if (lvl <= 2) {
|
|
23367
|
+
const no = ctx.gjChapterIdxs.indexOf(blockIdx);
|
|
23368
|
+
ctx.chapterNo = no >= 0 ? no + 1 : ctx.chapterNo + 1;
|
|
23369
|
+
return buildGaejosikChapter(ctx.chapterNo, stripChapterNumber(block.text || ""), gongmun, gjBodyW, lvl);
|
|
23370
|
+
} else if (lvl === 3) {
|
|
23371
|
+
return generateParagraph(
|
|
23372
|
+
`\u25A1 ${block.text || ""}`,
|
|
23373
|
+
GONGMUN_LIST_BASE,
|
|
23374
|
+
GJ_CHAR_DAE,
|
|
23375
|
+
(id) => id === CHAR_BOLD ? GJ_CHAR_DAE_BOLD : id
|
|
23376
|
+
);
|
|
23377
|
+
}
|
|
23378
|
+
return generateParagraph(`${gongmun.bullet2} ${block.text || ""}`, GONGMUN_LIST_BASE + 1, CHAR_NORMAL);
|
|
23379
|
+
}
|
|
23380
|
+
const pId = headingParaPrId(block.level || 1);
|
|
23381
|
+
const cId = headingCharPrId(block.level || 1);
|
|
23382
|
+
const styleId = gongmun ? Math.min(block.level || 1, 4) : 0;
|
|
23383
|
+
let hText = block.text || "";
|
|
23384
|
+
if (gongmun && (block.level || 1) === 2 && gongmun.h2Marker !== "none") {
|
|
23385
|
+
const title = stripChapterNumber(hText);
|
|
23386
|
+
hText = gongmun.h2Marker === "box" ? `\u25A1 ${title}` : `${++ctx.h2Seq}. ${title}`;
|
|
23387
|
+
}
|
|
23388
|
+
return generateParagraph(hText, pId, cId, void 0, styleId);
|
|
23389
|
+
}
|
|
23390
|
+
function renderParagraph(block, blockIdx, ctx) {
|
|
23391
|
+
const { gongmun, measured, fit, vBase } = ctx;
|
|
23392
|
+
if (ctx.images) {
|
|
23393
|
+
const { text: rest, urls } = splitImageRefs(block.text || "");
|
|
23394
|
+
if (urls.length > 0 && !rest.trim()) {
|
|
23395
|
+
const pics = urls.map((u) => {
|
|
23396
|
+
const part = ctx.images.take(u);
|
|
23397
|
+
return part ? ctx.images.inlinePicXml(part) : null;
|
|
23398
|
+
});
|
|
23399
|
+
if (pics.every(Boolean)) {
|
|
23400
|
+
return `<hp:p paraPrIDRef="${PARA_NORMAL}" styleIDRef="0"><hp:run charPrIDRef="${CHAR_NORMAL}">${pics.join("")}</hp:run></hp:p>`;
|
|
23401
|
+
}
|
|
23402
|
+
}
|
|
23403
|
+
}
|
|
23404
|
+
const pTrim = (block.text || "").trimStart();
|
|
23405
|
+
if (measured && (pTrim.startsWith("\u203B") || /^\*\s/.test(pTrim))) {
|
|
23406
|
+
return generateParagraph((block.text || "").trim(), GJ_PARA_CHAM, GJ_CHAR_CHAM, ctx.chamMap);
|
|
23407
|
+
}
|
|
23408
|
+
const ctr = gongmun && /^<center>([\s\S]*)<\/center>$/i.exec((block.text || "").trim());
|
|
23409
|
+
const rgt = gongmun && /^<right>([\s\S]*)<\/right>$/i.exec((block.text || "").trim());
|
|
23410
|
+
if (ctr) return generateParagraph(ctr[1].trim(), GONGMUN_CENTER);
|
|
23411
|
+
if (rgt) return generateParagraph(rgt[1].trim(), GONGMUN_RIGHT);
|
|
23412
|
+
return generateParagraph(block.text || "", PARA_NORMAL, CHAR_NORMAL, fit ? variantMapper(fit, blockIdx, vBase) : void 0);
|
|
23413
|
+
}
|
|
23414
|
+
function renderCodeBlock(block, blockIdx, ctx) {
|
|
23415
|
+
if (ctx.chartParts !== null && (block.lang || "").toLowerCase() === "chart") {
|
|
23416
|
+
const fence = parseChartFence(block.text || "");
|
|
23417
|
+
if (fence) {
|
|
23418
|
+
const partName = `Chart/chart${ctx.chartParts.length + 1}.xml`;
|
|
23419
|
+
ctx.chartParts.push({ name: partName, xml: buildChartSpaceXml(fence) });
|
|
23420
|
+
const chartEl = buildChartElementXml(partName, fence.widthHu, fence.heightHu, CHART_TABLE_ID_BASE + blockIdx);
|
|
23421
|
+
ctx.opener.emitCarrier(ctx.paraXmls);
|
|
23422
|
+
return `<hp:p paraPrIDRef="${PARA_NORMAL}" styleIDRef="0"><hp:run charPrIDRef="${CHAR_NORMAL}">${chartEl}</hp:run></hp:p>`;
|
|
23423
|
+
}
|
|
23424
|
+
}
|
|
23425
|
+
const codeLines = (block.text || "").split("\n");
|
|
23426
|
+
return codeLines.map((line) => generateParagraph(line || " ", PARA_CODE)).join("\n ");
|
|
23427
|
+
}
|
|
23428
|
+
function renderEquation(block, blockIdx, ctx) {
|
|
23429
|
+
ctx.opener.emitCarrier(ctx.paraXmls);
|
|
23430
|
+
return generateEquationParagraph(block.text || "", blockIdx);
|
|
23431
|
+
}
|
|
23432
|
+
function renderBlockquote(block, ctx) {
|
|
23433
|
+
if (ctx.measured) {
|
|
23434
|
+
const t = (block.text || "").replace(/\n+/g, " ").trim();
|
|
23435
|
+
return t ? generateParagraph(t.startsWith("\u203B") ? t : `\u203B ${t}`, GJ_PARA_CHAM, GJ_CHAR_CHAM, ctx.chamMap) : "";
|
|
23436
|
+
}
|
|
23437
|
+
const quoteChar = ctx.theme.hasQuoteOption ? CHAR_QUOTE : CHAR_NORMAL;
|
|
23438
|
+
return (block.text || "").split("\n").map((line) => generateParagraph(line, PARA_QUOTE, quoteChar)).join("\n ");
|
|
23439
|
+
}
|
|
23440
|
+
function renderListItem(block, blockIdx, ctx) {
|
|
23441
|
+
const { gongmun, gongmunList, measured, fit, vBase, dfIds } = ctx;
|
|
23442
|
+
if (gongmun && gongmunList) {
|
|
23443
|
+
const info = gongmunList.items.get(blockIdx);
|
|
23444
|
+
const depth = info?.depth ?? 0;
|
|
23445
|
+
const marker2 = info?.marker ?? "";
|
|
23446
|
+
const content = block.text || "";
|
|
23447
|
+
if (measured && content.trimStart().startsWith("\u203B")) {
|
|
23448
|
+
return generateParagraph(content.trim(), GJ_PARA_CHAM, GJ_CHAR_CHAM, ctx.chamMap);
|
|
23449
|
+
}
|
|
23450
|
+
if (measured && block.marker === "*") {
|
|
23451
|
+
return generateParagraph(`* ${content.trim()}`, GJ_PARA_CHAM, GJ_CHAR_CHAM, ctx.chamMap);
|
|
23452
|
+
}
|
|
23453
|
+
const text = marker2 ? `${marker2} ${content}` : content;
|
|
23454
|
+
const listParaPr = marker2 ? info?.indentVariant !== void 0 ? GONGMUN_LIST_VARIANT_BASE + info.indentVariant : GONGMUN_LIST_BASE + depth : GONGMUN_LIST_PLAIN_BASE + depth;
|
|
23455
|
+
let listCharPr = gongmun.numbering === "report" && depth === 0 ? CHAR_BOLD : CHAR_NORMAL;
|
|
23456
|
+
let mapId = fit ? variantMapper(fit, blockIdx, vBase) : void 0;
|
|
23457
|
+
if (measured && depth === 0 && gongmun.numbering !== "standard") {
|
|
23458
|
+
listCharPr = GJ_CHAR_DAE;
|
|
23459
|
+
mapId = (id) => id === CHAR_BOLD ? GJ_CHAR_DAE_BOLD : id;
|
|
23460
|
+
}
|
|
23461
|
+
if (gongmun.preset === "press") {
|
|
23462
|
+
if (depth === 0) listCharPr = CHAR_NORMAL;
|
|
23463
|
+
if (depth >= 2 && dfIds) {
|
|
23464
|
+
listCharPr = dfIds.small;
|
|
23465
|
+
mapId = void 0;
|
|
23466
|
+
}
|
|
23467
|
+
}
|
|
23468
|
+
return generateParagraph(text, listParaPr, listCharPr, mapId);
|
|
23469
|
+
}
|
|
23470
|
+
const indent = block.indent || 0;
|
|
23471
|
+
let marker;
|
|
23472
|
+
if (block.marker) {
|
|
23473
|
+
marker = `${block.marker} `;
|
|
23474
|
+
ctx.prevWasOrdered = !!block.ordered;
|
|
23475
|
+
} else if (block.ordered) {
|
|
23476
|
+
ctx.orderedCounters[indent] = (ctx.orderedCounters[indent] || 0) + 1;
|
|
23477
|
+
for (const k of Object.keys(ctx.orderedCounters)) {
|
|
23478
|
+
if (+k > indent) delete ctx.orderedCounters[+k];
|
|
23479
|
+
}
|
|
23480
|
+
marker = `${ctx.orderedCounters[indent]}. `;
|
|
23481
|
+
ctx.prevWasOrdered = true;
|
|
23482
|
+
} else {
|
|
23483
|
+
marker = "\xB7 ";
|
|
23484
|
+
if (ctx.prevWasOrdered) {
|
|
23485
|
+
for (const k of Object.keys(ctx.orderedCounters)) delete ctx.orderedCounters[+k];
|
|
23486
|
+
}
|
|
23487
|
+
ctx.prevWasOrdered = false;
|
|
23488
|
+
}
|
|
23489
|
+
const indentPrefix = " ".repeat(indent);
|
|
23490
|
+
return generateParagraph(indentPrefix + marker + (block.text || ""), PARA_LIST);
|
|
23491
|
+
}
|
|
23492
|
+
function renderHr(ctx) {
|
|
23493
|
+
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>`;
|
|
23494
|
+
}
|
|
23495
|
+
function renderTable(block, ctx) {
|
|
23496
|
+
if (!block.rows) return "";
|
|
23497
|
+
ctx.opener.emitCarrier(ctx.paraXmls);
|
|
23498
|
+
return generateTable(block.rows, ctx.theme, ctx.tableStyle, ctx.remap, ctx.tableSeq++, ctx.images);
|
|
23499
|
+
}
|
|
23500
|
+
function renderHtmlTable(block, ctx) {
|
|
23501
|
+
const { tableStyle, bfReg } = ctx;
|
|
23502
|
+
const htmlW = tableStyle ? bfReg ? tableStyle.totalWidth - DATA_TABLE_INSET : tableStyle.totalWidth : 44e3;
|
|
23503
|
+
const tbl2 = generateHtmlTableXml(block.text || "", ctx.theme, htmlW, tableStyle, ctx.remap, ctx.tableSeq++, ctx.images);
|
|
23504
|
+
if (tbl2) {
|
|
23505
|
+
ctx.opener.emitCarrier(ctx.paraXmls);
|
|
23506
|
+
return `<hp:p paraPrIDRef="${tableStyle && bfReg ? GONGMUN_RIGHT : 0}" styleIDRef="0"><hp:run charPrIDRef="0">${tbl2}</hp:run></hp:p>`;
|
|
23507
|
+
}
|
|
23508
|
+
const plain = (block.text || "").replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
|
|
23509
|
+
return plain ? generateParagraph(plain) : "";
|
|
23510
|
+
}
|
|
23511
|
+
function appendPostamble(blocks, ctx) {
|
|
23512
|
+
const { gongmun, dfIds, bfReg, tableStyle, paraXmls } = ctx;
|
|
23513
|
+
if (gongmun?.noticeHead && dfIds) {
|
|
23514
|
+
paraXmls.push(...buildNoticeFoot(gongmun));
|
|
23515
|
+
}
|
|
23516
|
+
if (gongmun?.endMark && paraXmls.length > 0) {
|
|
23517
|
+
const lastBlock = [...blocks].reverse().find((b) => (b.text || "").trim() || b.type === "table" && b.rows?.length);
|
|
23518
|
+
const lastText = lastBlock && (lastBlock.type === "paragraph" || lastBlock.type === "list_item") ? lastBlock.text || "" : "";
|
|
23519
|
+
if (!hasEndMark(lastText)) paraXmls.push(buildEndMark());
|
|
23520
|
+
}
|
|
23521
|
+
if (gongmun?.docFoot && dfIds) {
|
|
23522
|
+
paraXmls.push(...buildDocFoot(gongmun, dfIds));
|
|
23523
|
+
}
|
|
23524
|
+
if (gongmun?.press && dfIds && bfReg && tableStyle) {
|
|
23525
|
+
paraXmls.push(...buildPressContact(gongmun, dfIds, tableStyle.totalWidth, bfReg));
|
|
22580
23526
|
}
|
|
23527
|
+
}
|
|
23528
|
+
function blocksToSectionXml(blocks, theme, gongmun, gongmunList = gongmun ? precomputeGongmunList(blocks, gongmun) : null, fit = null, chartParts = null, bfReg = null, remap = null, dfIds = null, images = null) {
|
|
23529
|
+
resetTableIds();
|
|
23530
|
+
resetGjTableIds();
|
|
23531
|
+
resetExtraTableIds();
|
|
23532
|
+
const measured = !!gongmun && usesReportFonts(gongmun.preset);
|
|
23533
|
+
const richAssets = !!gongmun && needsGaejosikAssets(gongmun);
|
|
23534
|
+
const ctx = {
|
|
23535
|
+
theme,
|
|
23536
|
+
gongmun,
|
|
23537
|
+
gongmunList,
|
|
23538
|
+
fit,
|
|
23539
|
+
chartParts,
|
|
23540
|
+
bfReg,
|
|
23541
|
+
remap,
|
|
23542
|
+
dfIds,
|
|
23543
|
+
images,
|
|
23544
|
+
gaejosik: gongmun?.preset === "gaejosik",
|
|
23545
|
+
measured,
|
|
23546
|
+
richAssets,
|
|
23547
|
+
vBase: charVariantBase(richAssets, !!gongmun),
|
|
23548
|
+
// 공문서 표 스타일 — 본문 폭 맞춤 + 실측 정부 양식(헤더 음영·실측 프리셋 맑은 고딕 12pt)
|
|
23549
|
+
// bfReg가 있으면 실측 테두리 위계(외곽 0.4/내부 0.12/헤더 이중선)·셀 문단·우측 배치 적용
|
|
23550
|
+
tableStyle: gongmun ? {
|
|
23551
|
+
totalWidth: mmToHwpunit(210 - gongmun.margins.left - gongmun.margins.right),
|
|
23552
|
+
// 표 셀 크기 — 실측: 실측 프리셋 맑은 고딕 12pt(22·23), 비실측도 12pt(11·12,
|
|
23553
|
+
// 실결재 지배값 — 본문 크기 셀은 열폭 부족으로 서술 열 세로 신장, v4.0.2 QA)
|
|
23554
|
+
charPr: richAssets ? GJ_CHAR_TABLE : GONGMUN_TBL_CHAR,
|
|
23555
|
+
boldCharPr: richAssets ? GJ_CHAR_TABLE_BOLD : GONGMUN_TBL_CHAR_BOLD,
|
|
23556
|
+
charHeight: richAssets ? gaejosikSizes(gongmun.bodyHeight, gongmun.sizes).table : GONGMUN_TBL_PT,
|
|
23557
|
+
headerBf: gongmunTableHeaderBf(richAssets),
|
|
23558
|
+
centerParaPr: GONGMUN_CENTER,
|
|
23559
|
+
tblCenterParaPr: GONGMUN_TBL_CENTER,
|
|
23560
|
+
tblLeftParaPr: GONGMUN_TBL_LEFT,
|
|
23561
|
+
bfRegistry: bfReg ?? void 0,
|
|
23562
|
+
rightParaPr: GONGMUN_RIGHT
|
|
23563
|
+
} : null,
|
|
23564
|
+
// 개조식 장식표(표지·목차·장헤더·제목박스) 폭 스케일용 본문폭 — margins 오버라이드 대응
|
|
23565
|
+
gjBodyW: gongmun ? mmToHwpunit(210 - gongmun.margins.left - gongmun.margins.right) : GAEJOSIK_BASE_WIDTH,
|
|
23566
|
+
chamMap: (id) => id === CHAR_BOLD ? GJ_CHAR_CHAM_BOLD : id,
|
|
23567
|
+
opener: new SectionOpener(gongmun),
|
|
23568
|
+
paraXmls: [],
|
|
23569
|
+
// 표 방출 순번 — 생성 성공 여부와 무관하게 '시도' 기준으로 센다. 실패한 표가 이후
|
|
23570
|
+
// 표들의 순번을 밀면 앵커 없는 프로필(손편집·구버전)의 table_index 매칭이 어긋난다.
|
|
23571
|
+
tableSeq: 0,
|
|
23572
|
+
chapterNo: 0,
|
|
23573
|
+
h2Seq: 0,
|
|
23574
|
+
// h2 말머리 'number' 모드 아라비아 순번 (QA-2)
|
|
23575
|
+
coverH1Idx: -1,
|
|
23576
|
+
titleBoxH1Idx: -1,
|
|
23577
|
+
pressH1Idx: -1,
|
|
23578
|
+
gjChapterIdxs: [],
|
|
23579
|
+
pendingPageBreak: false,
|
|
23580
|
+
pendingNewNum: false,
|
|
23581
|
+
// 순서 있는 목록 카운터 — indent 레벨별 별도 유지. 다른 블록 만나면 해당 레벨 리셋.
|
|
23582
|
+
orderedCounters: {},
|
|
23583
|
+
prevWasOrdered: false
|
|
23584
|
+
};
|
|
23585
|
+
buildPreamble(blocks, ctx);
|
|
22581
23586
|
for (let blockIdx = 0; blockIdx < blocks.length; blockIdx++) {
|
|
22582
23587
|
const block = blocks[blockIdx];
|
|
22583
|
-
let xml = "";
|
|
22584
23588
|
if (block.type !== "list_item" || !block.ordered) {
|
|
22585
|
-
if (prevWasOrdered) {
|
|
22586
|
-
for (const k of Object.keys(orderedCounters)) delete orderedCounters[+k];
|
|
23589
|
+
if (ctx.prevWasOrdered) {
|
|
23590
|
+
for (const k of Object.keys(ctx.orderedCounters)) delete ctx.orderedCounters[+k];
|
|
22587
23591
|
}
|
|
22588
|
-
prevWasOrdered = false;
|
|
23592
|
+
ctx.prevWasOrdered = false;
|
|
22589
23593
|
}
|
|
23594
|
+
let xml = "";
|
|
22590
23595
|
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);
|
|
23596
|
+
case "heading":
|
|
23597
|
+
xml = renderHeading(block, blockIdx, ctx);
|
|
22637
23598
|
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
|
-
}
|
|
23599
|
+
case "paragraph":
|
|
23600
|
+
xml = renderParagraph(block, blockIdx, ctx);
|
|
22654
23601
|
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 ");
|
|
23602
|
+
case "code_block":
|
|
23603
|
+
xml = renderCodeBlock(block, blockIdx, ctx);
|
|
22674
23604
|
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);
|
|
23605
|
+
case "equation":
|
|
23606
|
+
xml = renderEquation(block, blockIdx, ctx);
|
|
22683
23607
|
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
|
-
);
|
|
23608
|
+
case "blockquote":
|
|
23609
|
+
xml = renderBlockquote(block, ctx);
|
|
22696
23610
|
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);
|
|
23611
|
+
case "list_item":
|
|
23612
|
+
xml = renderListItem(block, blockIdx, ctx);
|
|
22751
23613
|
break;
|
|
22752
|
-
}
|
|
22753
23614
|
case "hr":
|
|
22754
|
-
xml =
|
|
23615
|
+
xml = renderHr(ctx);
|
|
22755
23616
|
break;
|
|
22756
23617
|
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
|
-
}
|
|
23618
|
+
xml = renderTable(block, ctx);
|
|
22765
23619
|
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
|
-
}
|
|
23620
|
+
case "html_table":
|
|
23621
|
+
xml = renderHtmlTable(block, ctx);
|
|
22780
23622
|
break;
|
|
22781
|
-
}
|
|
22782
23623
|
}
|
|
22783
23624
|
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;
|
|
23625
|
+
if (ctx.opener.isFirst && block.type !== "table") {
|
|
23626
|
+
xml = ctx.opener.inject(xml);
|
|
22790
23627
|
}
|
|
22791
|
-
if (pendingPageBreak) {
|
|
23628
|
+
if (ctx.pendingPageBreak) {
|
|
22792
23629
|
xml = xml.replace(/^<hp:p /, `<hp:p pageBreak="1" `);
|
|
22793
|
-
pendingPageBreak = false;
|
|
23630
|
+
ctx.pendingPageBreak = false;
|
|
22794
23631
|
}
|
|
22795
|
-
if (pendingNewNum) {
|
|
23632
|
+
if (ctx.pendingNewNum) {
|
|
22796
23633
|
xml = xml.replace(/<hp:run charPrIDRef="(\d+)">/, `<hp:run charPrIDRef="$1">${newPageNumCtrl(1)}`);
|
|
22797
|
-
pendingNewNum = false;
|
|
23634
|
+
ctx.pendingNewNum = false;
|
|
22798
23635
|
}
|
|
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));
|
|
23636
|
+
ctx.paraXmls.push(xml);
|
|
22813
23637
|
}
|
|
22814
|
-
|
|
22815
|
-
|
|
23638
|
+
appendPostamble(blocks, ctx);
|
|
23639
|
+
if (ctx.paraXmls.length === 0) {
|
|
23640
|
+
ctx.paraXmls.push(ctx.opener.emptyDoc());
|
|
22816
23641
|
}
|
|
22817
23642
|
return `<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
|
|
22818
23643
|
<hs:sec xmlns:hs="${NS_SECTION}" xmlns:hp="${NS_PARA}">
|
|
22819
|
-
${paraXmls.join("\n ")}
|
|
23644
|
+
${ctx.paraXmls.join("\n ")}
|
|
22820
23645
|
</hs:sec>`;
|
|
22821
23646
|
}
|
|
22822
23647
|
|
|
@@ -22829,14 +23654,15 @@ async function markdownToHwpx(markdown, options) {
|
|
|
22829
23654
|
const blocks = parseMarkdownToBlocks(markdown);
|
|
22830
23655
|
const gongmunList = gongmun ? precomputeGongmunList(blocks, gongmun) : null;
|
|
22831
23656
|
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;
|
|
23657
|
+
const staticBfEnd = staticBorderFillNext(gongmun);
|
|
23658
|
+
const remap = options?.profile ? buildProfileRemap(options.profile, charVariantBase(richAssets, !!gongmun) + (fit?.variants?.length ?? 0) * 4, staticBfEnd, staticFontNext(gongmun)) : null;
|
|
22834
23659
|
const bfReg = gongmun ? new TableBfRegistry(staticBfEnd + (remap?.borderFillXmls.length ?? 0)) : null;
|
|
22835
23660
|
const dfActive = !!gongmun && docframeActive(gongmun);
|
|
22836
23661
|
const dfBase = charVariantBase(richAssets, !!gongmun) + (fit?.variants?.length ?? 0) * 4 + (remap?.charPrXmls.length ?? 0);
|
|
22837
23662
|
const dfIds = dfActive ? docframeIds(dfBase) : null;
|
|
22838
23663
|
const chartParts = [];
|
|
22839
|
-
const
|
|
23664
|
+
const images = new ImageRegistry();
|
|
23665
|
+
const sectionXml = blocksToSectionXml(blocks, theme, gongmun, gongmunList, fit, chartParts, bfReg, remap, dfIds, images);
|
|
22840
23666
|
if (remap && remap.tables.length > 0) {
|
|
22841
23667
|
const unused = remap.tables.filter((t) => !t.used).length;
|
|
22842
23668
|
if (unused === remap.tables.length) {
|
|
@@ -22847,13 +23673,16 @@ async function markdownToHwpx(markdown, options) {
|
|
|
22847
23673
|
const zip = new JSZip6();
|
|
22848
23674
|
zip.file("mimetype", "application/hwp+zip", { compression: "STORE" });
|
|
22849
23675
|
zip.file("META-INF/container.xml", generateContainerXml());
|
|
22850
|
-
zip.file("Contents/content.hpf", generateManifest(chartParts));
|
|
23676
|
+
zip.file("Contents/content.hpf", generateManifest(chartParts, images.manifestItems(), gongmun ? "gongmun" : "default"));
|
|
23677
|
+
for (const part of images.parts) zip.file(part.name, part.data);
|
|
22851
23678
|
zip.file("Contents/header.xml", generateHeaderXml(
|
|
22852
23679
|
theme,
|
|
22853
23680
|
gongmun,
|
|
22854
23681
|
fit?.variants ?? [],
|
|
22855
23682
|
extraBorderFills,
|
|
22856
|
-
[...remap?.charPrXmls ?? [], ...dfActive ? docframeCharPrXmls(dfBase, richAssets) : []]
|
|
23683
|
+
[...remap?.charPrXmls ?? [], ...dfActive ? docframeCharPrXmls(dfBase, richAssets) : []],
|
|
23684
|
+
gongmunList?.indentVariants ?? [],
|
|
23685
|
+
remap?.fontFaces ?? []
|
|
22857
23686
|
));
|
|
22858
23687
|
zip.file("Contents/section0.xml", sectionXml);
|
|
22859
23688
|
for (const part of chartParts) zip.file(part.name, part.xml);
|
|
@@ -23033,7 +23862,19 @@ function parseBorderFills(headerDoc) {
|
|
|
23033
23862
|
}
|
|
23034
23863
|
return map;
|
|
23035
23864
|
}
|
|
23036
|
-
function
|
|
23865
|
+
function parseHangulFonts(headerDoc) {
|
|
23866
|
+
const map = /* @__PURE__ */ new Map();
|
|
23867
|
+
for (const ff of elemsByLocal(headerDoc, "fontface")) {
|
|
23868
|
+
if (ff.getAttribute("lang") !== "HANGUL") continue;
|
|
23869
|
+
for (const font of elemsByLocal(ff, "font")) {
|
|
23870
|
+
const id = font.getAttribute("id");
|
|
23871
|
+
const face = font.getAttribute("face");
|
|
23872
|
+
if (id && face) map.set(id, face);
|
|
23873
|
+
}
|
|
23874
|
+
}
|
|
23875
|
+
return map;
|
|
23876
|
+
}
|
|
23877
|
+
function parseCharPrs(headerDoc, hangulFonts) {
|
|
23037
23878
|
const map = /* @__PURE__ */ new Map();
|
|
23038
23879
|
for (const cp of elemsByLocal(headerDoc, "charPr")) {
|
|
23039
23880
|
const id = cp.getAttribute("id");
|
|
@@ -23048,7 +23889,11 @@ function parseCharPrs(headerDoc) {
|
|
|
23048
23889
|
if (findChildByLocalName(cp, "underline")) def.underline = true;
|
|
23049
23890
|
const fontRef = findChildByLocalName(cp, "fontRef");
|
|
23050
23891
|
const hangul = fontRef?.getAttribute("hangul");
|
|
23051
|
-
if (hangul)
|
|
23892
|
+
if (hangul) {
|
|
23893
|
+
def.fontRef_hangul = hangul;
|
|
23894
|
+
const face = hangulFonts.get(hangul);
|
|
23895
|
+
if (face) def.fontName_hangul = face;
|
|
23896
|
+
}
|
|
23052
23897
|
map.set(id, def);
|
|
23053
23898
|
}
|
|
23054
23899
|
return map;
|
|
@@ -23076,6 +23921,8 @@ function parseTable3(tbl2, tableIndex, borderFills, charPrs) {
|
|
|
23076
23921
|
const usedBf = /* @__PURE__ */ new Set();
|
|
23077
23922
|
const usedCp = /* @__PURE__ */ new Set();
|
|
23078
23923
|
const colWidths = new Array(cols).fill(void 0);
|
|
23924
|
+
const spanCells = [];
|
|
23925
|
+
const row0Texts = /* @__PURE__ */ new Map();
|
|
23079
23926
|
let anchorText = "";
|
|
23080
23927
|
for (const tc2 of elemsByLocal(tbl2, "tc")) {
|
|
23081
23928
|
if (nearestTable(tc2) !== tbl2) continue;
|
|
@@ -23085,6 +23932,7 @@ function parseTable3(tbl2, tableIndex, borderFills, charPrs) {
|
|
|
23085
23932
|
const row = num(addr?.getAttribute("rowAddr")) ?? 0;
|
|
23086
23933
|
const col = num(addr?.getAttribute("colAddr")) ?? 0;
|
|
23087
23934
|
if (row === 0 && col === 0 && !anchorText) anchorText = directCellText(tc2);
|
|
23935
|
+
if (row === 0) row0Texts.set(col, directCellText(tc2));
|
|
23088
23936
|
const colSpan = num(span?.getAttribute("colSpan")) ?? 1;
|
|
23089
23937
|
const rowSpan = num(span?.getAttribute("rowSpan")) ?? 1;
|
|
23090
23938
|
const bfId = tc2.getAttribute("borderFillIDRef") || void 0;
|
|
@@ -23103,7 +23951,20 @@ function parseTable3(tbl2, tableIndex, borderFills, charPrs) {
|
|
|
23103
23951
|
usedCp.add(cpId);
|
|
23104
23952
|
}
|
|
23105
23953
|
cells.push(cell2);
|
|
23106
|
-
|
|
23954
|
+
const wNum = num(w);
|
|
23955
|
+
if (wNum != null && col < cols) {
|
|
23956
|
+
if (colSpan === 1) {
|
|
23957
|
+
if (colWidths[col] == null) colWidths[col] = wNum;
|
|
23958
|
+
} else spanCells.push({ col, colSpan, w: wNum });
|
|
23959
|
+
}
|
|
23960
|
+
}
|
|
23961
|
+
for (const s of spanCells) {
|
|
23962
|
+
const covered = Array.from({ length: s.colSpan }, (_, i) => s.col + i).filter((c) => c < cols);
|
|
23963
|
+
const unknown = covered.filter((c) => colWidths[c] == null);
|
|
23964
|
+
if (unknown.length === 0) continue;
|
|
23965
|
+
const known = covered.reduce((sum, c) => sum + (colWidths[c] ?? 0), 0);
|
|
23966
|
+
const each = Math.floor((s.w - known) / unknown.length);
|
|
23967
|
+
if (each > 0) for (const c of unknown) colWidths[c] = each;
|
|
23107
23968
|
}
|
|
23108
23969
|
const table2 = {
|
|
23109
23970
|
table_index: tableIndex,
|
|
@@ -23114,8 +23975,10 @@ function parseTable3(tbl2, tableIndex, borderFills, charPrs) {
|
|
|
23114
23975
|
};
|
|
23115
23976
|
const anchor = normalizeAnchor(anchorText);
|
|
23116
23977
|
if (anchor) table2.anchor_text = anchor;
|
|
23978
|
+
const rowAnchor = normalizeRowAnchor(Array.from({ length: cols }, (_, c) => row0Texts.get(c) ?? ""));
|
|
23979
|
+
if (rowAnchor) table2.anchor_row = rowAnchor;
|
|
23117
23980
|
if (width) table2.width_hwpunit = width;
|
|
23118
|
-
if (colWidths.every((w) => w != null)) table2.col_widths_hwpunit = colWidths;
|
|
23981
|
+
if (colWidths.every((w) => w != null)) table2.col_widths_hwpunit = colWidths.map(String);
|
|
23119
23982
|
const cp = pick(charPrs, usedCp);
|
|
23120
23983
|
if (Object.keys(cp).length) table2.used_char_prs = cp;
|
|
23121
23984
|
return table2;
|
|
@@ -23172,7 +24035,7 @@ async function hwpxToProfile(input) {
|
|
|
23172
24035
|
if (headerFile) headerXml = await headerFile.async("text");
|
|
23173
24036
|
const headerDoc = parser.parseFromString(headerXml, "text/xml");
|
|
23174
24037
|
const borderFills = parseBorderFills(headerDoc);
|
|
23175
|
-
const charPrs = parseCharPrs(headerDoc);
|
|
24038
|
+
const charPrs = parseCharPrs(headerDoc, parseHangulFonts(headerDoc));
|
|
23176
24039
|
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
24040
|
const tables = [];
|
|
23178
24041
|
let tableIndex = 0;
|
|
@@ -23183,7 +24046,7 @@ async function hwpxToProfile(input) {
|
|
|
23183
24046
|
tables.push(parseTable3(tbl2, tableIndex++, borderFills, charPrs));
|
|
23184
24047
|
}
|
|
23185
24048
|
}
|
|
23186
|
-
return { schema_version: "0.
|
|
24049
|
+
return { schema_version: "0.3.0", tables };
|
|
23187
24050
|
}
|
|
23188
24051
|
|
|
23189
24052
|
// src/hwpx/font-catalog.ts
|
|
@@ -23323,7 +24186,7 @@ var COMMON_FREE = [
|
|
|
23323
24186
|
"\uC724\uBA85\uC870"
|
|
23324
24187
|
];
|
|
23325
24188
|
function norm(name) {
|
|
23326
|
-
return name.replace(/\s+/g, "").toLowerCase();
|
|
24189
|
+
return name.normalize("NFC").replace(/\s+/g, "").toLowerCase();
|
|
23327
24190
|
}
|
|
23328
24191
|
var KNOWN = new Set([...HANCOM_BUNDLED, ...WINDOWS_DEFAULT, ...COMMON_FREE].map(norm));
|
|
23329
24192
|
function isKnownFont(name) {
|
|
@@ -23353,7 +24216,7 @@ var RULES = [
|
|
|
23353
24216
|
{
|
|
23354
24217
|
code: "DATE_ZERO_PAD",
|
|
23355
24218
|
severity: "error",
|
|
23356
|
-
pattern: /\b\d{4}\.\s*0\d
|
|
24219
|
+
pattern: /\b\d{4}\.\s*0\d\.|\b\d{4}\.\s*\d{1,2}\.\s*0\d/g,
|
|
23357
24220
|
message: "\uC6D4\xB7\uC77C \uC55E\uC758 '0'\uC740 \uD45C\uAE30\uD558\uC9C0 \uC54A\uC74C",
|
|
23358
24221
|
suggest: "\uC608) 2025. 1. 6. (2025. 01. 06. \u2715)"
|
|
23359
24222
|
},
|
|
@@ -23435,22 +24298,25 @@ var RULES = [
|
|
|
23435
24298
|
{
|
|
23436
24299
|
code: "COLON_SPACE",
|
|
23437
24300
|
severity: "warning",
|
|
23438
|
-
pattern: /\S\s
|
|
24301
|
+
pattern: /\S\s+:(?!\/\/)|\S:(?!\/\/)[^\s\d]/g,
|
|
23439
24302
|
message: "\uC30D\uC810\uC740 \uC55E\uB9D0\uC5D0 \uBD99\uC774\uACE0 \uB4A4\uB294 \uD55C \uCE78 \uB744\uC6C0",
|
|
23440
24303
|
suggest: "\uC608) \uC6D0\uC7A5: \uAE40\uAC11\uB3D9"
|
|
23441
24304
|
}
|
|
23442
24305
|
];
|
|
23443
24306
|
function lintGongmunText(text) {
|
|
23444
24307
|
const findings = [];
|
|
23445
|
-
let
|
|
24308
|
+
let fenceMarker = null;
|
|
23446
24309
|
const lines = text.split(/\r?\n/);
|
|
23447
24310
|
for (let i = 0; i < lines.length; i++) {
|
|
23448
24311
|
const line = lines[i];
|
|
23449
|
-
|
|
23450
|
-
|
|
24312
|
+
const fence = line.match(/^\s*(```+|~~~+)/);
|
|
24313
|
+
if (fence) {
|
|
24314
|
+
const kind = fence[1][0];
|
|
24315
|
+
if (fenceMarker === null) fenceMarker = kind;
|
|
24316
|
+
else if (kind === fenceMarker) fenceMarker = null;
|
|
23451
24317
|
continue;
|
|
23452
24318
|
}
|
|
23453
|
-
if (
|
|
24319
|
+
if (fenceMarker !== null) continue;
|
|
23454
24320
|
for (const r of RULES) {
|
|
23455
24321
|
r.pattern.lastIndex = 0;
|
|
23456
24322
|
for (const m of line.matchAll(r.pattern)) {
|
|
@@ -26207,7 +27073,7 @@ async function parseHwp(buffer, options) {
|
|
|
26207
27073
|
async function parsePdf(buffer, options) {
|
|
26208
27074
|
let parsePdfDocument;
|
|
26209
27075
|
try {
|
|
26210
|
-
const mod = await import("./parser-
|
|
27076
|
+
const mod = await import("./parser-3MAYZUJX.js");
|
|
26211
27077
|
parsePdfDocument = mod.parsePdfDocument;
|
|
26212
27078
|
} catch {
|
|
26213
27079
|
return {
|
|
@@ -26298,6 +27164,9 @@ async function fillForm(input, values, outputFormat = "markdown") {
|
|
|
26298
27164
|
}
|
|
26299
27165
|
|
|
26300
27166
|
export {
|
|
27167
|
+
PRESET_ALIAS,
|
|
27168
|
+
normalizeGongmunPreset,
|
|
27169
|
+
incompatibleGongmunWarnings,
|
|
26301
27170
|
extractHwpxMetadataOnly,
|
|
26302
27171
|
extractHwp5MetadataOnly,
|
|
26303
27172
|
formatFillValue,
|
|
@@ -26309,8 +27178,6 @@ export {
|
|
|
26309
27178
|
extractFormSchema,
|
|
26310
27179
|
fillFormFields,
|
|
26311
27180
|
fillHwpx,
|
|
26312
|
-
PRESET_ALIAS,
|
|
26313
|
-
normalizeGongmunPreset,
|
|
26314
27181
|
markdownToHwpx,
|
|
26315
27182
|
compare,
|
|
26316
27183
|
diffBlocks,
|
|
@@ -26339,4 +27206,4 @@ export {
|
|
|
26339
27206
|
parseHwpml,
|
|
26340
27207
|
fillForm
|
|
26341
27208
|
};
|
|
26342
|
-
//# sourceMappingURL=chunk-
|
|
27209
|
+
//# sourceMappingURL=chunk-Z6Q3YG22.js.map
|