kordoc 4.2.9 → 4.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +922 -885
- package/dist/{-ECUCTQTR.js → -DYHT3HWH.js} +28 -20
- package/dist/{chunk-J2RZ444H.js → chunk-3FFPHTXD.js} +1 -1
- package/dist/chunk-3FFPHTXD.js.map +1 -0
- package/dist/{chunk-27Z4PUZZ.js → chunk-4DBCUF3C.js} +38 -7
- package/dist/chunk-4DBCUF3C.js.map +1 -0
- package/dist/{chunk-5RPYBC2Q.js → chunk-6GBLFQMA.js} +1 -1
- package/dist/chunk-6GBLFQMA.js.map +1 -0
- package/dist/{chunk-VEHC4EJL.js → chunk-6RAYTNQT.js} +5 -5
- package/dist/chunk-6RAYTNQT.js.map +1 -0
- package/dist/{chunk-GEPINOL6.js → chunk-6XPXIG57.js} +2 -2
- package/dist/chunk-6XPXIG57.js.map +1 -0
- package/dist/{chunk-7ZARSBQ3.js → chunk-7MFO4TKP.js} +4 -3
- package/dist/chunk-7MFO4TKP.js.map +1 -0
- package/dist/{chunk-PATPZTH7.cjs → chunk-AL34DGS5.cjs} +6 -6
- package/dist/chunk-AL34DGS5.cjs.map +1 -0
- package/dist/{chunk-LXE7OR6N.js → chunk-ARXNKC5D.js} +3 -3
- package/dist/chunk-ARXNKC5D.js.map +1 -0
- package/dist/{chunk-6GM2QLM3.cjs → chunk-AUOEZKJE.cjs} +21 -21
- package/dist/chunk-AUOEZKJE.cjs.map +1 -0
- package/dist/chunk-DCZVOIEO.cjs.map +1 -1
- package/dist/{chunk-3TSTZGCJ.js → chunk-DRQUAYNT.js} +4 -3
- package/dist/chunk-DRQUAYNT.js.map +1 -0
- package/dist/{chunk-G3DMDTZI.js → chunk-DRZUJ2RK.js} +1511 -1135
- package/dist/chunk-DRZUJ2RK.js.map +1 -0
- package/dist/{chunk-NIISBGSR.js → chunk-EFVAY5VE.js} +71 -29
- package/dist/chunk-EFVAY5VE.js.map +1 -0
- package/dist/{chunk-3PLF5UIS.js → chunk-EZHW3RNQ.js} +37 -6
- package/dist/chunk-EZHW3RNQ.js.map +1 -0
- package/dist/chunk-GS7T56RP.cjs.map +1 -1
- package/dist/{chunk-MHMVFI2N.js → chunk-GSMPTZC7.js} +4 -4
- package/dist/chunk-GSMPTZC7.js.map +1 -0
- package/dist/{chunk-GZ7W64QU.cjs → chunk-JFV7QT5T.cjs} +4 -3
- package/dist/chunk-JFV7QT5T.cjs.map +1 -0
- package/dist/{chunk-4ZWKFDUC.cjs → chunk-LPQWKQQQ.cjs} +44 -13
- package/dist/chunk-LPQWKQQQ.cjs.map +1 -0
- package/dist/{chunk-H7OWEYH2.cjs → chunk-MGGU3ZRK.cjs} +1 -1
- package/dist/chunk-MGGU3ZRK.cjs.map +1 -0
- package/dist/chunk-MOL7MDBG.js +0 -0
- package/dist/{chunk-XU4VWOKL.js → chunk-NMWXBJ5B.js} +4 -3
- package/dist/chunk-NMWXBJ5B.js.map +1 -0
- package/dist/{chunk-DZIXKL7E.js → chunk-O5EK52TG.js} +1 -1
- package/dist/chunk-O5EK52TG.js.map +1 -0
- package/dist/{chunk-KTPZYXN2.js → chunk-SPKBIFSK.js} +2 -2
- package/dist/chunk-SPKBIFSK.js.map +1 -0
- package/dist/{chunk-STYN45MS.js → chunk-US4WFSIQ.js} +4 -4
- package/dist/chunk-US4WFSIQ.js.map +1 -0
- package/dist/{chunk-WTT2KJOM.js → chunk-VXMLENCV.js} +14 -10
- package/dist/chunk-VXMLENCV.js.map +1 -0
- package/dist/{chunk-7FCNOKOR.js → chunk-W34QSJFF.js} +1 -1
- package/dist/chunk-W34QSJFF.js.map +1 -0
- package/dist/{chunk-YR4PF56B.js → chunk-WFOQFJI4.js} +2 -2
- package/dist/chunk-WFOQFJI4.js.map +1 -0
- package/dist/{chunk-NNDVFNMJ.js → chunk-YNJDNP44.js} +1 -1
- package/dist/chunk-YNJDNP44.js.map +1 -0
- package/dist/{chunk-LM7C6HI2.js → chunk-ZSL6MQVE.js} +1 -1
- package/dist/chunk-ZSL6MQVE.js.map +1 -0
- package/dist/chunks-Q7T4SQGW.js +10 -0
- package/dist/cli.js +75 -39
- package/dist/cli.js.map +1 -1
- package/dist/{formula-SXC6LRRC.js → formula-7ZPC54JO.js} +2 -2
- package/dist/formula-7ZPC54JO.js.map +1 -0
- package/dist/{formula-KSNAEBZ2.js → formula-EQRHSMYW.js} +2 -2
- package/dist/formula-EQRHSMYW.js.map +1 -0
- package/dist/{formula-SB3NHDUM.cjs → formula-IWV5IJ3Y.cjs} +8 -8
- package/dist/formula-IWV5IJ3Y.cjs.map +1 -0
- package/dist/{image-ocr-PS7XCV6K.js → image-ocr-G2LYTEUH.js} +8 -6
- package/dist/image-ocr-G2LYTEUH.js.map +1 -0
- package/dist/{image-ocr-KZTNJ62U.js → image-ocr-R44RYDBU.js} +10 -8
- package/dist/image-ocr-R44RYDBU.js.map +1 -0
- package/dist/{image-ocr-J5656BEE.cjs → image-ocr-XEJFXH6M.cjs} +13 -11
- package/dist/image-ocr-XEJFXH6M.cjs.map +1 -0
- package/dist/index.cjs +1993 -1845
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +75 -4
- package/dist/index.d.ts +75 -4
- package/dist/index.js +2128 -1980
- package/dist/index.js.map +1 -1
- package/dist/mcp.js +123 -78
- package/dist/mcp.js.map +1 -1
- package/dist/{models-KA4GLQK5.js → models-6VMAVZR6.js} +3 -3
- package/dist/page-range-737B4EZW.js +0 -0
- package/dist/page-range-P7SDW6LR.cjs.map +1 -1
- package/dist/{parser-A34NAYKA.js → parser-2G245MMN.js} +6 -6
- package/dist/parser-2G245MMN.js.map +1 -0
- package/dist/{parser-WNFWMBSL.js → parser-W3POOYQQ.js} +8 -8
- package/dist/parser-W3POOYQQ.js.map +1 -0
- package/dist/{parser-CSYU3CAW.cjs → parser-W3ZGQUCQ.cjs} +25 -25
- package/dist/parser-W3ZGQUCQ.cjs.map +1 -0
- package/dist/pdf-ocr-3GDTJSHF.js +15 -0
- package/dist/pdf-ocr-N7IU23UV.cjs +13 -0
- package/dist/pdf-ocr-N7IU23UV.cjs.map +1 -0
- package/dist/pdf-ocr-U7MEGNBZ.js +12 -0
- package/dist/{profile-io-43MF52H6.js → profile-io-72R3B7KO.js} +3 -3
- package/dist/{rasterize-OG7KXBJS.js → rasterize-GNJQVURE.js} +2 -2
- package/dist/rasterize-GNJQVURE.js.map +1 -0
- package/dist/{redact-7ILEAUTS.js → redact-W5K3ILQH.js} +2 -2
- package/dist/render-6OW6U4L4.js +10 -0
- package/dist/seal-GTNLVBJX.js +10 -0
- package/dist/{setup-GUQT5ELJ.js → setup-I4I75QJU.js} +1 -1
- package/dist/setup-I4I75QJU.js.map +1 -0
- package/dist/{watch-SOHQMZN2.js → watch-2ADAJLIJ.js} +12 -14
- package/dist/watch-2ADAJLIJ.js.map +1 -0
- package/package.json +101 -98
- package/templates//352/260/204/354/235/264/352/270/260/354/225/210/353/254/270_/354/204/234/354/213/235.hwpx +0 -0
- package/templates//352/260/204/354/235/264/352/270/260/354/225/210/353/254/270_/354/230/210/354/213/234/352/260/222.json +9 -0
- package/templates//354/235/274/353/260/230/352/270/260/354/225/210/353/254/270_/354/204/234/354/213/235.hwpx +0 -0
- package/templates//354/235/274/353/260/230/352/270/260/354/225/210/353/254/270_/354/230/210/354/213/234/352/260/222.json +25 -0
- package/dist/chunk-27Z4PUZZ.js.map +0 -1
- package/dist/chunk-3PLF5UIS.js.map +0 -1
- package/dist/chunk-3TSTZGCJ.js.map +0 -1
- package/dist/chunk-4ZWKFDUC.cjs.map +0 -1
- package/dist/chunk-5RPYBC2Q.js.map +0 -1
- package/dist/chunk-6GM2QLM3.cjs.map +0 -1
- package/dist/chunk-7FCNOKOR.js.map +0 -1
- package/dist/chunk-7ZARSBQ3.js.map +0 -1
- package/dist/chunk-DZIXKL7E.js.map +0 -1
- package/dist/chunk-G3DMDTZI.js.map +0 -1
- package/dist/chunk-GEPINOL6.js.map +0 -1
- package/dist/chunk-GZ7W64QU.cjs.map +0 -1
- package/dist/chunk-H7OWEYH2.cjs.map +0 -1
- package/dist/chunk-J2RZ444H.js.map +0 -1
- package/dist/chunk-KTPZYXN2.js.map +0 -1
- package/dist/chunk-LM7C6HI2.js.map +0 -1
- package/dist/chunk-LXE7OR6N.js.map +0 -1
- package/dist/chunk-MHMVFI2N.js.map +0 -1
- package/dist/chunk-NIISBGSR.js.map +0 -1
- package/dist/chunk-NNDVFNMJ.js.map +0 -1
- package/dist/chunk-PATPZTH7.cjs.map +0 -1
- package/dist/chunk-STYN45MS.js.map +0 -1
- package/dist/chunk-TCJRGFYY.js +0 -280
- package/dist/chunk-TCJRGFYY.js.map +0 -1
- package/dist/chunk-VEHC4EJL.js.map +0 -1
- package/dist/chunk-WTT2KJOM.js.map +0 -1
- package/dist/chunk-XU4VWOKL.js.map +0 -1
- package/dist/chunk-YR4PF56B.js.map +0 -1
- package/dist/chunks-VFIMYK36.js +0 -10
- package/dist/detect-FX2ZHKZP.js +0 -26
- package/dist/formula-KSNAEBZ2.js.map +0 -1
- package/dist/formula-SB3NHDUM.cjs.map +0 -1
- package/dist/formula-SXC6LRRC.js.map +0 -1
- package/dist/image-ocr-J5656BEE.cjs.map +0 -1
- package/dist/image-ocr-KZTNJ62U.js.map +0 -1
- package/dist/image-ocr-PS7XCV6K.js.map +0 -1
- package/dist/parser-A34NAYKA.js.map +0 -1
- package/dist/parser-CSYU3CAW.cjs.map +0 -1
- package/dist/parser-WNFWMBSL.js.map +0 -1
- package/dist/pdf-ocr-KBMWPG2L.js +0 -12
- package/dist/pdf-ocr-NPZG67FN.js +0 -15
- package/dist/pdf-ocr-O44DAGTY.cjs +0 -13
- package/dist/pdf-ocr-O44DAGTY.cjs.map +0 -1
- package/dist/rasterize-OG7KXBJS.js.map +0 -1
- package/dist/render-336CJHAS.js +0 -10
- package/dist/seal-TM2XCE2R.js +0 -10
- package/dist/seal-TM2XCE2R.js.map +0 -1
- package/dist/setup-GUQT5ELJ.js.map +0 -1
- package/dist/watch-SOHQMZN2.js.map +0 -1
- /package/dist/{-ECUCTQTR.js.map → -DYHT3HWH.js.map} +0 -0
- /package/dist/{chunks-VFIMYK36.js.map → chunks-Q7T4SQGW.js.map} +0 -0
- /package/dist/{detect-FX2ZHKZP.js.map → models-6VMAVZR6.js.map} +0 -0
- /package/dist/{models-KA4GLQK5.js.map → pdf-ocr-3GDTJSHF.js.map} +0 -0
- /package/dist/{pdf-ocr-KBMWPG2L.js.map → pdf-ocr-U7MEGNBZ.js.map} +0 -0
- /package/dist/{pdf-ocr-NPZG67FN.js.map → profile-io-72R3B7KO.js.map} +0 -0
- /package/dist/{profile-io-43MF52H6.js.map → redact-W5K3ILQH.js.map} +0 -0
- /package/dist/{redact-7ILEAUTS.js.map → render-6OW6U4L4.js.map} +0 -0
- /package/dist/{render-336CJHAS.js.map → seal-GTNLVBJX.js.map} +0 -0
|
@@ -1,18 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
inlineImagesIntoMarkdown
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-W34QSJFF.js";
|
|
5
5
|
import {
|
|
6
6
|
HEADING_RATIO_H1,
|
|
7
7
|
HEADING_RATIO_H2,
|
|
8
8
|
HEADING_RATIO_H3
|
|
9
|
-
} from "./chunk-
|
|
10
|
-
import {
|
|
11
|
-
detectFormat,
|
|
12
|
-
detectOle2Format,
|
|
13
|
-
detectZipFormat,
|
|
14
|
-
parseLenientCfb
|
|
15
|
-
} from "./chunk-TCJRGFYY.js";
|
|
9
|
+
} from "./chunk-ZSL6MQVE.js";
|
|
16
10
|
import {
|
|
17
11
|
parsePageRange
|
|
18
12
|
} from "./chunk-MOL7MDBG.js";
|
|
@@ -21,13 +15,14 @@ import {
|
|
|
21
15
|
applySplices,
|
|
22
16
|
buildParagraphSplices,
|
|
23
17
|
buildRangeSplices,
|
|
18
|
+
decodeXmlEntities,
|
|
24
19
|
escapeXmlText,
|
|
25
20
|
findElementEnd,
|
|
26
21
|
paraTText,
|
|
27
22
|
paraTextPureT,
|
|
28
23
|
patchZipEntries,
|
|
29
24
|
scanSectionXml
|
|
30
|
-
} from "./chunk-
|
|
25
|
+
} from "./chunk-NMWXBJ5B.js";
|
|
31
26
|
import {
|
|
32
27
|
MAX_COLS,
|
|
33
28
|
MAX_ROWS,
|
|
@@ -37,26 +32,30 @@ import {
|
|
|
37
32
|
dedupeRunningHeaders,
|
|
38
33
|
flattenLayoutTables,
|
|
39
34
|
mapPuaText
|
|
40
|
-
} from "./chunk-
|
|
35
|
+
} from "./chunk-GSMPTZC7.js";
|
|
41
36
|
import {
|
|
42
37
|
SPACE_EM_FIXED,
|
|
43
38
|
charWidthEm1000,
|
|
44
39
|
fitRatioForFewerLines,
|
|
45
40
|
measureTextWidth,
|
|
46
41
|
simulateWrap
|
|
47
|
-
} from "./chunk-
|
|
42
|
+
} from "./chunk-VXMLENCV.js";
|
|
48
43
|
import {
|
|
49
44
|
MAX_DECOMPRESS_SIZE,
|
|
50
45
|
MAX_XML_DEPTH,
|
|
51
46
|
MAX_ZIP_ENTRIES,
|
|
52
47
|
ZipBombError,
|
|
53
48
|
applyPageText,
|
|
49
|
+
childrenByLocalName,
|
|
54
50
|
clampSpan,
|
|
55
51
|
createSectionShared,
|
|
56
52
|
createXmlParser,
|
|
53
|
+
elementChildren,
|
|
57
54
|
extractTextFromNode,
|
|
58
|
-
findChildByLocalName
|
|
59
|
-
|
|
55
|
+
findChildByLocalName,
|
|
56
|
+
localName,
|
|
57
|
+
rawTextContent
|
|
58
|
+
} from "./chunk-EFVAY5VE.js";
|
|
60
59
|
import {
|
|
61
60
|
KordocError,
|
|
62
61
|
classifyError,
|
|
@@ -68,13 +67,277 @@ import {
|
|
|
68
67
|
sanitizeHref,
|
|
69
68
|
stripDtd,
|
|
70
69
|
toArrayBuffer
|
|
71
|
-
} from "./chunk-
|
|
70
|
+
} from "./chunk-DRQUAYNT.js";
|
|
72
71
|
|
|
73
72
|
// src/index.ts
|
|
74
73
|
import { readFile } from "fs/promises";
|
|
75
74
|
|
|
75
|
+
// src/detect.ts
|
|
76
|
+
import JSZip from "jszip";
|
|
77
|
+
|
|
78
|
+
// src/hwp5/cfb-lenient.ts
|
|
79
|
+
var CFB_MAGIC = Buffer.from([208, 207, 17, 224, 161, 177, 26, 225]);
|
|
80
|
+
var END_OF_CHAIN = 4294967294;
|
|
81
|
+
var FREE_SECT = 4294967295;
|
|
82
|
+
var MAX_CHAIN_LENGTH = 1e6;
|
|
83
|
+
var MAX_DIR_ENTRIES = 1e5;
|
|
84
|
+
var MAX_STREAM_SIZE = 100 * 1024 * 1024;
|
|
85
|
+
function parseLenientCfb(data) {
|
|
86
|
+
if (data.length < 512) throw new Error("CFB \uD30C\uC77C\uC774 \uB108\uBB34 \uC9E7\uC2B5\uB2C8\uB2E4 (\uCD5C\uC18C 512\uBC14\uC774\uD2B8)");
|
|
87
|
+
if (!data.subarray(0, 8).equals(CFB_MAGIC)) throw new Error("CFB \uB9E4\uC9C1 \uBC14\uC774\uD2B8 \uBD88\uC77C\uCE58");
|
|
88
|
+
const sectorSizeShift = data.readUInt16LE(30);
|
|
89
|
+
if (sectorSizeShift < 7 || sectorSizeShift > 16) throw new Error("\uC720\uD6A8\uD558\uC9C0 \uC54A\uC740 \uC139\uD130 \uD06C\uAE30 \uC2DC\uD504\uD2B8: " + sectorSizeShift);
|
|
90
|
+
const sectorSize = 1 << sectorSizeShift;
|
|
91
|
+
const miniSectorSizeShift = data.readUInt16LE(32);
|
|
92
|
+
if (miniSectorSizeShift > 16) throw new Error("\uC720\uD6A8\uD558\uC9C0 \uC54A\uC740 \uBBF8\uB2C8 \uC139\uD130 \uD06C\uAE30 \uC2DC\uD504\uD2B8: " + miniSectorSizeShift);
|
|
93
|
+
const miniSectorSize = 1 << miniSectorSizeShift;
|
|
94
|
+
const headerFatSectorCount = data.readUInt32LE(44);
|
|
95
|
+
if (headerFatSectorCount > 1e4) throw new Error("FAT \uC139\uD130 \uC218\uAC00 \uB108\uBB34 \uB9CE\uC2B5\uB2C8\uB2E4: " + headerFatSectorCount);
|
|
96
|
+
const maxRealSectors = Math.ceil(Math.max(0, data.length - 512) / sectorSize);
|
|
97
|
+
const maxFatSectors = Math.max(1, Math.ceil(maxRealSectors / (sectorSize / 4)));
|
|
98
|
+
const fatSectorCount = Math.min(headerFatSectorCount, maxFatSectors);
|
|
99
|
+
const firstDirSector = data.readUInt32LE(48);
|
|
100
|
+
const miniStreamCutoff = data.readUInt32LE(56);
|
|
101
|
+
const firstMiniFatSector = data.readUInt32LE(60);
|
|
102
|
+
const miniFatSectorCount = data.readUInt32LE(64);
|
|
103
|
+
const firstDifatSector = data.readUInt32LE(68);
|
|
104
|
+
const difatSectorCount = data.readUInt32LE(72);
|
|
105
|
+
function sectorOffset(id) {
|
|
106
|
+
return 512 + id * sectorSize;
|
|
107
|
+
}
|
|
108
|
+
function readSectorData(id) {
|
|
109
|
+
const off = sectorOffset(id);
|
|
110
|
+
if (off + sectorSize > data.length) return Buffer.alloc(0);
|
|
111
|
+
return data.subarray(off, off + sectorSize);
|
|
112
|
+
}
|
|
113
|
+
const fatSectors = [];
|
|
114
|
+
for (let i = 0; i < 109 && fatSectors.length < fatSectorCount; i++) {
|
|
115
|
+
const sid = data.readUInt32LE(76 + i * 4);
|
|
116
|
+
if (sid === FREE_SECT || sid === END_OF_CHAIN) break;
|
|
117
|
+
fatSectors.push(sid);
|
|
118
|
+
}
|
|
119
|
+
let difatSector = firstDifatSector;
|
|
120
|
+
const visitedDifat = /* @__PURE__ */ new Set();
|
|
121
|
+
for (let d = 0; d < difatSectorCount && difatSector !== END_OF_CHAIN && difatSector !== FREE_SECT; d++) {
|
|
122
|
+
if (visitedDifat.has(difatSector)) break;
|
|
123
|
+
visitedDifat.add(difatSector);
|
|
124
|
+
const buf = readSectorData(difatSector);
|
|
125
|
+
if (buf.length < sectorSize) break;
|
|
126
|
+
const entriesPerSector = sectorSize / 4 - 1;
|
|
127
|
+
for (let i = 0; i < entriesPerSector && fatSectors.length < fatSectorCount; i++) {
|
|
128
|
+
const sid = buf.readUInt32LE(i * 4);
|
|
129
|
+
if (sid === FREE_SECT || sid === END_OF_CHAIN) continue;
|
|
130
|
+
fatSectors.push(sid);
|
|
131
|
+
}
|
|
132
|
+
difatSector = buf.readUInt32LE(entriesPerSector * 4);
|
|
133
|
+
}
|
|
134
|
+
const entriesPerFatSector = sectorSize / 4;
|
|
135
|
+
const fatTable = new Uint32Array(fatSectors.length * entriesPerFatSector);
|
|
136
|
+
for (let fi = 0; fi < fatSectors.length; fi++) {
|
|
137
|
+
const buf = readSectorData(fatSectors[fi]);
|
|
138
|
+
for (let i = 0; i < entriesPerFatSector; i++) {
|
|
139
|
+
fatTable[fi * entriesPerFatSector + i] = i * 4 + 3 < buf.length ? buf.readUInt32LE(i * 4) : FREE_SECT;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
function readChain(startSector, maxBytes) {
|
|
143
|
+
if (startSector === END_OF_CHAIN || startSector === FREE_SECT) return Buffer.alloc(0);
|
|
144
|
+
if (maxBytes > MAX_STREAM_SIZE) throw new Error("\uC2A4\uD2B8\uB9BC\uC774 \uB108\uBB34 \uD07D\uB2C8\uB2E4");
|
|
145
|
+
const chunks = [];
|
|
146
|
+
let current = startSector;
|
|
147
|
+
let totalRead = 0;
|
|
148
|
+
const visited = /* @__PURE__ */ new Set();
|
|
149
|
+
while (current !== END_OF_CHAIN && current !== FREE_SECT && totalRead < maxBytes) {
|
|
150
|
+
if (visited.has(current)) break;
|
|
151
|
+
if (visited.size > MAX_CHAIN_LENGTH) break;
|
|
152
|
+
visited.add(current);
|
|
153
|
+
const buf = readSectorData(current);
|
|
154
|
+
const remaining = maxBytes - totalRead;
|
|
155
|
+
chunks.push(remaining < sectorSize ? buf.subarray(0, remaining) : buf);
|
|
156
|
+
totalRead += Math.min(buf.length, remaining);
|
|
157
|
+
current = current < fatTable.length ? fatTable[current] : END_OF_CHAIN;
|
|
158
|
+
}
|
|
159
|
+
return Buffer.concat(chunks);
|
|
160
|
+
}
|
|
161
|
+
let miniFatTable = null;
|
|
162
|
+
function getMiniFatTable() {
|
|
163
|
+
if (miniFatTable) return miniFatTable;
|
|
164
|
+
if (miniFatSectorCount === 0 || firstMiniFatSector === END_OF_CHAIN) {
|
|
165
|
+
miniFatTable = new Uint32Array(0);
|
|
166
|
+
return miniFatTable;
|
|
167
|
+
}
|
|
168
|
+
const miniFatData = readChain(firstMiniFatSector, miniFatSectorCount * sectorSize);
|
|
169
|
+
const entries = miniFatData.length / 4;
|
|
170
|
+
miniFatTable = new Uint32Array(entries);
|
|
171
|
+
for (let i = 0; i < entries; i++) {
|
|
172
|
+
miniFatTable[i] = miniFatData.readUInt32LE(i * 4);
|
|
173
|
+
}
|
|
174
|
+
return miniFatTable;
|
|
175
|
+
}
|
|
176
|
+
const dirData = readChain(firstDirSector, MAX_DIR_ENTRIES * 128);
|
|
177
|
+
const dirEntries = [];
|
|
178
|
+
for (let offset = 0; offset + 128 <= dirData.length && dirEntries.length < MAX_DIR_ENTRIES; offset += 128) {
|
|
179
|
+
const nameLen = dirData.readUInt16LE(offset + 64);
|
|
180
|
+
if (nameLen <= 0 || nameLen > 64) {
|
|
181
|
+
dirEntries.push({ name: "", type: 0, startSector: 0, size: 0 });
|
|
182
|
+
continue;
|
|
183
|
+
}
|
|
184
|
+
const nameBytes = nameLen - 2;
|
|
185
|
+
const name = nameBytes > 0 ? dirData.subarray(offset, offset + nameBytes).toString("utf16le") : "";
|
|
186
|
+
const type = dirData[offset + 66];
|
|
187
|
+
const startSector = dirData.readUInt32LE(offset + 116);
|
|
188
|
+
const size = dirData.readUInt32LE(offset + 120);
|
|
189
|
+
dirEntries.push({ name, type, startSector, size });
|
|
190
|
+
}
|
|
191
|
+
let miniStreamData = null;
|
|
192
|
+
function getMiniStream() {
|
|
193
|
+
if (miniStreamData) return miniStreamData;
|
|
194
|
+
const root = dirEntries[0];
|
|
195
|
+
if (!root || root.type !== 5) {
|
|
196
|
+
miniStreamData = Buffer.alloc(0);
|
|
197
|
+
return miniStreamData;
|
|
198
|
+
}
|
|
199
|
+
miniStreamData = readChain(root.startSector, root.size || MAX_STREAM_SIZE);
|
|
200
|
+
return miniStreamData;
|
|
201
|
+
}
|
|
202
|
+
function readMiniStream(startSector, size) {
|
|
203
|
+
const mft = getMiniFatTable();
|
|
204
|
+
const ms = getMiniStream();
|
|
205
|
+
if (mft.length === 0 || ms.length === 0) return Buffer.alloc(0);
|
|
206
|
+
const chunks = [];
|
|
207
|
+
let current = startSector;
|
|
208
|
+
let totalRead = 0;
|
|
209
|
+
const visited = /* @__PURE__ */ new Set();
|
|
210
|
+
while (current !== END_OF_CHAIN && current !== FREE_SECT && totalRead < size) {
|
|
211
|
+
if (visited.has(current)) break;
|
|
212
|
+
if (visited.size > MAX_CHAIN_LENGTH) break;
|
|
213
|
+
visited.add(current);
|
|
214
|
+
const off = current * miniSectorSize;
|
|
215
|
+
const remaining = size - totalRead;
|
|
216
|
+
const chunkSize = Math.min(miniSectorSize, remaining);
|
|
217
|
+
if (off + chunkSize <= ms.length) {
|
|
218
|
+
chunks.push(ms.subarray(off, off + chunkSize));
|
|
219
|
+
}
|
|
220
|
+
totalRead += chunkSize;
|
|
221
|
+
current = current < mft.length ? mft[current] : END_OF_CHAIN;
|
|
222
|
+
}
|
|
223
|
+
return Buffer.concat(chunks);
|
|
224
|
+
}
|
|
225
|
+
function readStreamData(entry) {
|
|
226
|
+
if (entry.size === 0) return Buffer.alloc(0);
|
|
227
|
+
if (entry.size < miniStreamCutoff) {
|
|
228
|
+
const miniResult = readMiniStream(entry.startSector, entry.size);
|
|
229
|
+
if (miniResult.length > 0) return miniResult;
|
|
230
|
+
}
|
|
231
|
+
return readChain(entry.startSector, entry.size);
|
|
232
|
+
}
|
|
233
|
+
function findEntryByPath(path) {
|
|
234
|
+
const parts = path.replace(/^\//, "").split("/");
|
|
235
|
+
if (parts.length === 1) {
|
|
236
|
+
return dirEntries.find((e) => e.name === parts[0] && e.type === 2) ?? null;
|
|
237
|
+
}
|
|
238
|
+
const storageName = parts[0];
|
|
239
|
+
const streamName = parts.slice(1).join("/");
|
|
240
|
+
for (const e of dirEntries) {
|
|
241
|
+
if (e.type === 2 && e.name === streamName) {
|
|
242
|
+
return e;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
const lastPart = parts[parts.length - 1];
|
|
246
|
+
return dirEntries.find((e) => e.type === 2 && e.name === lastPart) ?? null;
|
|
247
|
+
}
|
|
248
|
+
return {
|
|
249
|
+
findStream(path) {
|
|
250
|
+
const normalized = path.replace(/^\//, "");
|
|
251
|
+
const entry = findEntryByPath(normalized);
|
|
252
|
+
if (!entry || entry.type !== 2) return null;
|
|
253
|
+
const stream = readStreamData(entry);
|
|
254
|
+
return stream.length > 0 ? stream : null;
|
|
255
|
+
},
|
|
256
|
+
entries() {
|
|
257
|
+
return dirEntries.filter((e) => e.type === 2);
|
|
258
|
+
}
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// src/detect.ts
|
|
263
|
+
function magicBytes(buffer) {
|
|
264
|
+
return new Uint8Array(buffer, 0, Math.min(4, buffer.byteLength));
|
|
265
|
+
}
|
|
266
|
+
function isZipFile(buffer) {
|
|
267
|
+
const b = magicBytes(buffer);
|
|
268
|
+
return b[0] === 80 && b[1] === 75 && b[2] === 3 && b[3] === 4;
|
|
269
|
+
}
|
|
270
|
+
function isHwpxFile(buffer) {
|
|
271
|
+
return isZipFile(buffer);
|
|
272
|
+
}
|
|
273
|
+
function isOldHwpFile(buffer) {
|
|
274
|
+
const b = magicBytes(buffer);
|
|
275
|
+
return b[0] === 208 && b[1] === 207 && b[2] === 17 && b[3] === 224;
|
|
276
|
+
}
|
|
277
|
+
var HWP3_PREFIX = new TextEncoder().encode("HWP Document File V3.00");
|
|
278
|
+
function isHwp3File(buffer) {
|
|
279
|
+
if (buffer.byteLength < HWP3_PREFIX.length) return false;
|
|
280
|
+
const head = new Uint8Array(buffer, 0, HWP3_PREFIX.length);
|
|
281
|
+
for (let i = 0; i < HWP3_PREFIX.length; i++) {
|
|
282
|
+
if (head[i] !== HWP3_PREFIX[i]) return false;
|
|
283
|
+
}
|
|
284
|
+
return true;
|
|
285
|
+
}
|
|
286
|
+
function isPdfFile(buffer) {
|
|
287
|
+
const b = magicBytes(buffer);
|
|
288
|
+
return b[0] === 37 && b[1] === 80 && b[2] === 68 && b[3] === 70;
|
|
289
|
+
}
|
|
290
|
+
function isImageFile(buffer) {
|
|
291
|
+
if (buffer.byteLength < 12) return false;
|
|
292
|
+
const b = new Uint8Array(buffer, 0, 12);
|
|
293
|
+
if (b[0] === 137 && b[1] === 80 && b[2] === 78 && b[3] === 71) return true;
|
|
294
|
+
if (b[0] === 255 && b[1] === 216 && b[2] === 255) return true;
|
|
295
|
+
if (b[0] === 82 && b[1] === 73 && b[2] === 70 && b[3] === 70 && b[8] === 87 && b[9] === 69 && b[10] === 66 && b[11] === 80) return true;
|
|
296
|
+
return false;
|
|
297
|
+
}
|
|
298
|
+
function isHwpmlFile(buffer) {
|
|
299
|
+
const bytes = new Uint8Array(buffer, 0, Math.min(512, buffer.byteLength));
|
|
300
|
+
const head = new TextDecoder("utf-8", { fatal: false }).decode(bytes).replace(/^\uFEFF/, "");
|
|
301
|
+
return head.trimStart().startsWith("<?xml") && head.includes("<HWPML");
|
|
302
|
+
}
|
|
303
|
+
function detectFormat(buffer) {
|
|
304
|
+
if (buffer.byteLength < 4) return "unknown";
|
|
305
|
+
if (isHwp3File(buffer)) return "hwp3";
|
|
306
|
+
if (isZipFile(buffer)) return "hwpx";
|
|
307
|
+
if (isOldHwpFile(buffer)) return "hwp";
|
|
308
|
+
if (isPdfFile(buffer)) return "pdf";
|
|
309
|
+
if (isHwpmlFile(buffer)) return "hwpml";
|
|
310
|
+
if (isImageFile(buffer)) return "image";
|
|
311
|
+
return "unknown";
|
|
312
|
+
}
|
|
313
|
+
function detectOle2Format(buffer) {
|
|
314
|
+
try {
|
|
315
|
+
const cfb = parseLenientCfb(Buffer.from(buffer));
|
|
316
|
+
const names = cfb.entries().map((e) => e.name);
|
|
317
|
+
if (names.includes("Workbook") || names.includes("Book")) return "xls";
|
|
318
|
+
if (names.includes("FileHeader")) return "hwp";
|
|
319
|
+
if (names.some((n) => n === "DocInfo" || n.startsWith("Section"))) return "hwp";
|
|
320
|
+
return "unknown";
|
|
321
|
+
} catch {
|
|
322
|
+
return "unknown";
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
async function detectZipFormat(buffer) {
|
|
326
|
+
try {
|
|
327
|
+
const zip = await JSZip.loadAsync(buffer);
|
|
328
|
+
if (zip.file("xl/workbook.xml")) return "xlsx";
|
|
329
|
+
if (zip.file("word/document.xml")) return "docx";
|
|
330
|
+
if (zip.file("Contents/content.hpf") || zip.file("mimetype")) return "hwpx";
|
|
331
|
+
const hasSection = Object.keys(zip.files).some((f) => f.startsWith("Contents/"));
|
|
332
|
+
if (hasSection) return "hwpx";
|
|
333
|
+
return "unknown";
|
|
334
|
+
} catch {
|
|
335
|
+
return "unknown";
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|
|
76
339
|
// src/hwpx/parser.ts
|
|
77
|
-
import
|
|
340
|
+
import JSZip3 from "jszip";
|
|
78
341
|
|
|
79
342
|
// src/hwpx/com-fallback.ts
|
|
80
343
|
import { execFileSync } from "child_process";
|
|
@@ -261,9 +524,9 @@ function parseCharProperties(doc, map) {
|
|
|
261
524
|
if (bold === "true" || bold === "1") prop.bold = true;
|
|
262
525
|
const italic = el.getAttribute("italic");
|
|
263
526
|
if (italic === "true" || italic === "1") prop.italic = true;
|
|
264
|
-
const
|
|
265
|
-
for (let j = 0; j < (
|
|
266
|
-
const k =
|
|
527
|
+
const kids = el.childNodes;
|
|
528
|
+
for (let j = 0; j < (kids?.length ?? 0); j++) {
|
|
529
|
+
const k = kids[j];
|
|
267
530
|
if (k.nodeType !== 1) continue;
|
|
268
531
|
const localTag = (k.tagName || "").replace(/^[^:]+:/, "");
|
|
269
532
|
if (localTag === "bold") prop.bold = true;
|
|
@@ -895,615 +1158,239 @@ function hmlToLatex(hmlEqStr) {
|
|
|
895
1158
|
return out;
|
|
896
1159
|
}
|
|
897
1160
|
|
|
898
|
-
// src/
|
|
899
|
-
var
|
|
900
|
-
var
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
var COVER_MEASURED_W = 48180;
|
|
907
|
-
var DATA_TABLE_ID_BASE = 1e3;
|
|
908
|
-
var CHART_TABLE_ID_BASE = 91e5;
|
|
909
|
-
var GJ_TABLE_ID_BASE = 92e5;
|
|
910
|
-
var EXTRA_TABLE_ID_BASE = 93e5;
|
|
911
|
-
var PIC_ID_BASE = 94e5;
|
|
912
|
-
|
|
913
|
-
// src/hwpx/gaejosik.ts
|
|
914
|
-
var GAEJOSIK_BULLETS = ["\u25A1", "\u25CB", "-", "\u318D"];
|
|
915
|
-
function gaejosikMarker(depth, bullet2 = "\u25CB") {
|
|
916
|
-
if (depth === 1) return bullet2;
|
|
917
|
-
return GAEJOSIK_BULLETS[Math.min(depth, GAEJOSIK_BULLETS.length - 1)];
|
|
918
|
-
}
|
|
919
|
-
function gaejosikSizes(bodyHeight, ov = {}) {
|
|
920
|
-
const s = bodyHeight / 1500;
|
|
921
|
-
const r = (v, pt) => pt ? Math.round(pt * 100) : Math.round(v * s);
|
|
922
|
-
return {
|
|
923
|
-
/** □ 대항목 — HY헤드라인M 16pt */
|
|
924
|
-
dae: r(1600, ov.dae),
|
|
925
|
-
/** ※ 참고 — 한양중고딕 13pt */
|
|
926
|
-
cham: r(1300, ov.cham),
|
|
927
|
-
/** 장 헤더(로마숫자·제목) 17pt */
|
|
928
|
-
chapter: r(1700, ov.chapter),
|
|
929
|
-
/** 표지 제목 30pt */
|
|
930
|
-
coverTitle: r(3e3, ov.coverTitle),
|
|
931
|
-
/** 표지 날짜·기관명 25pt */
|
|
932
|
-
coverSub: r(2500, ov.coverSub),
|
|
933
|
-
/** 목차 라벨 "목 차" 28pt */
|
|
934
|
-
tocLabel: r(2800, ov.tocLabel),
|
|
935
|
-
/** 목차 로마숫자 21pt(한양신명조) */
|
|
936
|
-
tocRoman: r(2100, ov.tocRoman),
|
|
937
|
-
/** 목차 항목 18pt */
|
|
938
|
-
tocItem: r(1800, ov.tocItem),
|
|
939
|
-
/** 표 셀 — 맑은 고딕 12pt (실측: 정부 양식 표) */
|
|
940
|
-
table: r(1200, ov.table),
|
|
941
|
-
/** 표지 장식 바 셀 빈 문단 — 6pt (실측: 바 높이 818 안에 줄높이 수납) */
|
|
942
|
-
bar: r(600),
|
|
943
|
-
/** 본문 첫 페이지 제목 박스 — HY헤드라인M 22pt (실측: GT3 표④) */
|
|
944
|
-
bodyTitle: r(2200, ov.bodyTitle)
|
|
945
|
-
};
|
|
946
|
-
}
|
|
947
|
-
function gaejosikSpaceBefore(depth, bodyHeight) {
|
|
948
|
-
const s = bodyHeight / 1500;
|
|
949
|
-
const ui = [3e3, 2e3, 1200, 600][Math.min(depth, 3)];
|
|
950
|
-
return Math.round(ui * s);
|
|
951
|
-
}
|
|
952
|
-
function gaejosikLevelIndent(depth, bodyHeight, sizes = {}, bullet2 = "\u25CB") {
|
|
953
|
-
const lefts = [0, 1, 1.5, 2];
|
|
954
|
-
const left = depth <= 3 ? Math.round(lefts[depth] * bodyHeight) : Math.round((2 + (depth - 3) * 0.5) * bodyHeight);
|
|
955
|
-
const h = depth === 0 ? gaejosikSizes(bodyHeight, sizes).dae : bodyHeight;
|
|
956
|
-
return { left, indent: -markerWidth(gaejosikMarker(depth, bullet2), h) };
|
|
1161
|
+
// src/shared/numbering.ts
|
|
1162
|
+
var HANGUL_INITIALS = [0, 2, 3, 5, 6, 7, 9, 11, 12, 14, 15, 16, 17, 18];
|
|
1163
|
+
var HANGUL_MEDIALS = [0, 4, 8, 13, 18, 20];
|
|
1164
|
+
function hangulOrdinal(n) {
|
|
1165
|
+
const cols = HANGUL_INITIALS.length;
|
|
1166
|
+
const vowel = HANGUL_MEDIALS[Math.min(Math.floor(n / cols), HANGUL_MEDIALS.length - 1)];
|
|
1167
|
+
const init = HANGUL_INITIALS[n % cols];
|
|
1168
|
+
return String.fromCodePoint(44032 + init * 588 + vowel * 28);
|
|
957
1169
|
}
|
|
958
|
-
function
|
|
959
|
-
|
|
960
|
-
|
|
1170
|
+
function circledNumber(n) {
|
|
1171
|
+
if (n < 20) return String.fromCodePoint(9312 + n);
|
|
1172
|
+
if (n < 35) return String.fromCodePoint(12881 + (n - 20));
|
|
1173
|
+
if (n < 50) return String.fromCodePoint(12977 + (n - 35));
|
|
1174
|
+
return `(${n + 1})`;
|
|
961
1175
|
}
|
|
962
|
-
function
|
|
963
|
-
|
|
964
|
-
const hang = markerWidth("\u2167.", sz.tocRoman);
|
|
965
|
-
return { left: 2e3 + hang, indent: -hang };
|
|
1176
|
+
function circledHangul(n) {
|
|
1177
|
+
return n < 14 ? String.fromCodePoint(12910 + n) : hangulOrdinal(n);
|
|
966
1178
|
}
|
|
967
|
-
function
|
|
968
|
-
|
|
1179
|
+
function romanNumeral(n, upper) {
|
|
1180
|
+
if (n <= 0 || n > 3999) return String(n);
|
|
1181
|
+
const values = [1e3, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1];
|
|
1182
|
+
const symbols = upper ? ["M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"] : ["m", "cm", "d", "cd", "c", "xc", "l", "xl", "x", "ix", "v", "iv", "i"];
|
|
1183
|
+
let result = "";
|
|
1184
|
+
let num2 = n;
|
|
1185
|
+
for (let i = 0; i < values.length; i++) {
|
|
1186
|
+
while (num2 >= values[i]) {
|
|
1187
|
+
result += symbols[i];
|
|
1188
|
+
num2 -= values[i];
|
|
1189
|
+
}
|
|
1190
|
+
}
|
|
1191
|
+
return result;
|
|
969
1192
|
}
|
|
970
|
-
|
|
971
|
-
|
|
1193
|
+
|
|
1194
|
+
// src/hwpx/para-heading.ts
|
|
1195
|
+
var HANGUL_JAMO_SEQ = "\u3131\u3134\u3137\u3139\u3141\u3142\u3145\u3147\u3148\u314A\u314B\u314C\u314D\u314E";
|
|
1196
|
+
function formatHeadNumber(n, numFormat) {
|
|
1197
|
+
if (n === 0 && numFormat === "DIGIT") return "0";
|
|
1198
|
+
if (n <= 0) n = 1;
|
|
1199
|
+
switch (numFormat) {
|
|
1200
|
+
case "DIGIT":
|
|
1201
|
+
return String(n);
|
|
1202
|
+
// 서수 시퀀스는 생성기(gongmun.ts)와 단일 소스 (v4.0.5) — 종전 mod-14 순환은
|
|
1203
|
+
// 15번째 형제를 다시 '가'로 만들어 한글 실렌더(가→하→거→너 단모음 연속)와 어긋났다
|
|
1204
|
+
case "CIRCLED_DIGIT":
|
|
1205
|
+
return n <= 50 ? circledNumber(n - 1) : `(${n})`;
|
|
1206
|
+
case "HANGUL_SYLLABLE":
|
|
1207
|
+
return hangulOrdinal(n - 1);
|
|
1208
|
+
case "CIRCLED_HANGUL_SYLLABLE":
|
|
1209
|
+
return n <= 14 ? circledHangul(n - 1) : hangulOrdinal(n - 1);
|
|
1210
|
+
case "HANGUL_JAMO":
|
|
1211
|
+
return HANGUL_JAMO_SEQ[(n - 1) % HANGUL_JAMO_SEQ.length];
|
|
1212
|
+
case "CIRCLED_HANGUL_JAMO":
|
|
1213
|
+
return n <= 14 ? String.fromCodePoint(12896 + n - 1) : HANGUL_JAMO_SEQ[(n - 1) % 14];
|
|
1214
|
+
case "LATIN_CAPITAL":
|
|
1215
|
+
return String.fromCharCode(65 + (n - 1) % 26);
|
|
1216
|
+
case "LATIN_SMALL":
|
|
1217
|
+
return String.fromCharCode(97 + (n - 1) % 26);
|
|
1218
|
+
case "CIRCLED_LATIN_CAPITAL":
|
|
1219
|
+
return n <= 26 ? String.fromCodePoint(9398 + n - 1) : String.fromCharCode(65 + (n - 1) % 26);
|
|
1220
|
+
case "CIRCLED_LATIN_SMALL":
|
|
1221
|
+
return n <= 26 ? String.fromCodePoint(9424 + n - 1) : String.fromCharCode(97 + (n - 1) % 26);
|
|
1222
|
+
case "ROMAN_CAPITAL":
|
|
1223
|
+
return romanNumeral(n, true);
|
|
1224
|
+
case "ROMAN_SMALL":
|
|
1225
|
+
return romanNumeral(n, false);
|
|
1226
|
+
default:
|
|
1227
|
+
return String(n);
|
|
1228
|
+
}
|
|
972
1229
|
}
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
}
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
const
|
|
1005
|
-
const
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
const w = (v) => Math.round(v * bodyWidth / GAEJOSIK_BASE_WIDTH);
|
|
1013
|
-
const totalW = w(COVER_GEOM.totalW);
|
|
1014
|
-
return {
|
|
1015
|
-
...COVER_GEOM,
|
|
1016
|
-
totalW,
|
|
1017
|
-
topDarkW: w(COVER_GEOM.topDarkW),
|
|
1018
|
-
topLightW: totalW - w(COVER_GEOM.topDarkW),
|
|
1019
|
-
botDarkW: w(COVER_GEOM.botDarkW),
|
|
1020
|
-
botLightW: totalW - w(COVER_GEOM.botDarkW),
|
|
1021
|
-
titleH: Math.round(COVER_GEOM.titleH * s)
|
|
1022
|
-
};
|
|
1023
|
-
}
|
|
1024
|
-
var TOC_BANNER_GEOM = {
|
|
1025
|
-
colWs: [565, 565, 1414, 13191, 1414, 565, 565],
|
|
1026
|
-
h: 3931
|
|
1027
|
-
};
|
|
1028
|
-
function tocBannerGeom(bodyHeight, ov = {}) {
|
|
1029
|
-
const s = gaejosikSizes(bodyHeight, ov).tocLabel / 2800;
|
|
1030
|
-
return { colWs: TOC_BANNER_GEOM.colWs, h: Math.round(TOC_BANNER_GEOM.h * s) };
|
|
1031
|
-
}
|
|
1032
|
-
var BODY_TITLE_GEOM = { barH: 600, titleH: 3566 };
|
|
1033
|
-
function bodyTitleGeom(bodyHeight, ov = {}) {
|
|
1034
|
-
const s = gaejosikSizes(bodyHeight, ov).bodyTitle / 2200;
|
|
1035
|
-
return { barH: BODY_TITLE_GEOM.barH, titleH: Math.round(BODY_TITLE_GEOM.titleH * s) };
|
|
1230
|
+
function resolveParaHeading(paraEl, ctx) {
|
|
1231
|
+
const sm = ctx.styleMap;
|
|
1232
|
+
if (!sm) return null;
|
|
1233
|
+
const styleId = paraEl.getAttribute("styleIDRef");
|
|
1234
|
+
let styleHeading;
|
|
1235
|
+
if (styleId && styleId !== "0") {
|
|
1236
|
+
const st = sm.styles.get(styleId);
|
|
1237
|
+
const m = st && /^개요\s*([1-6])$/.exec(st.name);
|
|
1238
|
+
if (m) styleHeading = Math.min(parseInt(m[1], 10), 6);
|
|
1239
|
+
}
|
|
1240
|
+
const prId = paraEl.getAttribute("paraPrIDRef");
|
|
1241
|
+
if (!prId) return styleHeading ? { headingLevel: styleHeading } : null;
|
|
1242
|
+
const ref = sm.paraHeadings.get(prId);
|
|
1243
|
+
if (!ref) return styleHeading ? { headingLevel: styleHeading } : null;
|
|
1244
|
+
if (ref.type === "BULLET") {
|
|
1245
|
+
const char = sm.bullets.get(ref.idRef);
|
|
1246
|
+
return char ? { prefix: char } : null;
|
|
1247
|
+
}
|
|
1248
|
+
const numId = ref.type === "OUTLINE" ? ctx.outlineNumId || "1" : ref.idRef;
|
|
1249
|
+
const level = Math.min(ref.level + 1, 10);
|
|
1250
|
+
const headingLevel = ref.type === "OUTLINE" ? Math.min(ref.level + 1, 6) : void 0;
|
|
1251
|
+
const numDef = sm.numberings.get(numId);
|
|
1252
|
+
if (!numDef) return headingLevel ? { headingLevel } : null;
|
|
1253
|
+
let counters = ctx.shared.numState.get(numId);
|
|
1254
|
+
if (!counters) {
|
|
1255
|
+
counters = new Array(11).fill(-1);
|
|
1256
|
+
ctx.shared.numState.set(numId, counters);
|
|
1257
|
+
}
|
|
1258
|
+
const head = numDef.heads.get(level);
|
|
1259
|
+
counters[level] = counters[level] < 0 ? head?.start ?? 1 : counters[level] + 1;
|
|
1260
|
+
for (let l = level + 1; l <= 10; l++) counters[l] = -1;
|
|
1261
|
+
const fmtText = head ? head.text.trim() : `^${level}.`;
|
|
1262
|
+
const prefix = fmtText.replace(/\^(10|[1-9])/g, (_, d) => {
|
|
1263
|
+
const lv = parseInt(d, 10);
|
|
1264
|
+
const refHead = numDef.heads.get(lv);
|
|
1265
|
+
const n = counters[lv] >= 0 ? counters[lv] : refHead?.start ?? 1;
|
|
1266
|
+
return formatHeadNumber(n, refHead?.numFormat || "DIGIT");
|
|
1267
|
+
});
|
|
1268
|
+
return { prefix: prefix || void 0, headingLevel };
|
|
1036
1269
|
}
|
|
1037
1270
|
|
|
1038
|
-
// src/hwpx/
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
}
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1271
|
+
// src/hwpx/table-build.ts
|
|
1272
|
+
function buildTableWithCellMeta(state, keepAnchoredEmptyCols, keepEmptyParagraphs) {
|
|
1273
|
+
const table2 = buildTable(state.rows, { keepAnchoredEmptyCols, keepEmptyParagraphs });
|
|
1274
|
+
if (state.caption) table2.caption = state.caption;
|
|
1275
|
+
if (state.captionBlocks?.length) table2.captionBlocks = state.captionBlocks;
|
|
1276
|
+
const anchors = [];
|
|
1277
|
+
{
|
|
1278
|
+
const covered = /* @__PURE__ */ new Set();
|
|
1279
|
+
for (let r = 0; r < table2.rows; r++) {
|
|
1280
|
+
for (let c = 0; c < table2.cols; c++) {
|
|
1281
|
+
if (covered.has(`${r},${c}`)) continue;
|
|
1282
|
+
const cell2 = table2.cells[r]?.[c];
|
|
1283
|
+
if (!cell2) continue;
|
|
1284
|
+
for (let dr = 0; dr < cell2.rowSpan; dr++) {
|
|
1285
|
+
for (let dc = 0; dc < cell2.colSpan; dc++) {
|
|
1286
|
+
if (dr === 0 && dc === 0) continue;
|
|
1287
|
+
if (r + dr < table2.rows && c + dc < table2.cols) covered.add(`${r + dr},${c + dc}`);
|
|
1288
|
+
}
|
|
1289
|
+
}
|
|
1290
|
+
anchors.push(cell2);
|
|
1291
|
+
c += cell2.colSpan - 1;
|
|
1292
|
+
}
|
|
1293
|
+
}
|
|
1294
|
+
}
|
|
1295
|
+
const srcCount = state.rows.reduce((s, r) => s + r.length, 0);
|
|
1296
|
+
const ordinalReliable = anchors.length === srcCount;
|
|
1297
|
+
const claimed = /* @__PURE__ */ new Set();
|
|
1298
|
+
let flatIdx = -1;
|
|
1299
|
+
for (const row of state.rows) {
|
|
1300
|
+
for (const src of row) {
|
|
1301
|
+
flatIdx++;
|
|
1302
|
+
const needsBlocks = !!src.blocks && src.blocks.length > 0 && (src.hasStructure || src.blocks.some((b) => b.spans));
|
|
1303
|
+
if (!needsBlocks && !src.isHeader) continue;
|
|
1304
|
+
let target;
|
|
1305
|
+
const trimmed = keepEmptyParagraphs ? src.text : src.text.trim();
|
|
1306
|
+
if (src.rowAddr !== void 0 && src.colAddr !== void 0) {
|
|
1307
|
+
const cand = table2.cells[src.rowAddr]?.[src.colAddr];
|
|
1308
|
+
if (cand && cand.text === trimmed && !claimed.has(cand)) target = cand;
|
|
1309
|
+
}
|
|
1310
|
+
if (!target) {
|
|
1311
|
+
outer: for (const irRow of table2.cells) {
|
|
1312
|
+
for (const cand of irRow) {
|
|
1313
|
+
if (!claimed.has(cand) && cand.text === trimmed && cand.colSpan === src.colSpan && cand.rowSpan === src.rowSpan) {
|
|
1314
|
+
target = cand;
|
|
1315
|
+
break outer;
|
|
1316
|
+
}
|
|
1317
|
+
}
|
|
1318
|
+
}
|
|
1319
|
+
}
|
|
1320
|
+
if (!target && ordinalReliable) {
|
|
1321
|
+
const cand = anchors[flatIdx];
|
|
1322
|
+
if (cand && !claimed.has(cand)) target = cand;
|
|
1323
|
+
}
|
|
1324
|
+
if (!target) continue;
|
|
1325
|
+
claimed.add(target);
|
|
1326
|
+
if (needsBlocks) target.blocks = src.blocks;
|
|
1327
|
+
if (src.isHeader) target.isHeader = true;
|
|
1328
|
+
}
|
|
1095
1329
|
}
|
|
1330
|
+
return table2;
|
|
1096
1331
|
}
|
|
1097
|
-
function
|
|
1098
|
-
|
|
1099
|
-
if (
|
|
1100
|
-
|
|
1101
|
-
|
|
1332
|
+
function completeTable(newTable, tableStack, blocks, ctx) {
|
|
1333
|
+
const parentTable = tableStack.length > 0 ? tableStack.pop() : null;
|
|
1334
|
+
if (newTable.rows.length === 0) {
|
|
1335
|
+
if (newTable.caption) blocks.push({ type: "paragraph", text: newTable.caption, pageNumber: ctx.sectionNum });
|
|
1336
|
+
return parentTable;
|
|
1102
1337
|
}
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
if (
|
|
1112
|
-
|
|
1338
|
+
const ir = buildTableWithCellMeta(newTable, ctx.shared.keepTrailingEmptyCols, ctx.shared.keepEmptyParagraphs);
|
|
1339
|
+
const block = { type: "table", table: ir, pageNumber: ctx.sectionNum };
|
|
1340
|
+
if (parentTable?.cell) {
|
|
1341
|
+
const cell2 = parentTable.cell;
|
|
1342
|
+
(cell2.blocks ??= []).push(block);
|
|
1343
|
+
cell2.hasStructure = true;
|
|
1344
|
+
let flat = convertTableToText(newTable.rows);
|
|
1345
|
+
if (newTable.caption) flat = newTable.caption + (flat ? "\n" + flat : "");
|
|
1346
|
+
if (flat) {
|
|
1347
|
+
const inlineJoin = newTable.inline === true && cell2.lineOpen === true;
|
|
1348
|
+
cell2.text += (cell2.text ? inlineJoin ? " " : "\n" : "") + flat;
|
|
1349
|
+
cell2.lineOpen = newTable.inline === true;
|
|
1113
1350
|
}
|
|
1351
|
+
} else {
|
|
1352
|
+
blocks.push(block);
|
|
1114
1353
|
}
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1354
|
+
return parentTable;
|
|
1355
|
+
}
|
|
1356
|
+
|
|
1357
|
+
// src/hwpx/section-walker.ts
|
|
1358
|
+
function parseSectionXml(xml, styleMap, warnings, sectionNum, shared) {
|
|
1359
|
+
const parser = createXmlParser(warnings);
|
|
1360
|
+
const doc = parser.parseFromString(stripDtd(xml), "text/xml");
|
|
1361
|
+
if (!doc.documentElement) return [];
|
|
1362
|
+
const ctx = { styleMap, warnings, sectionNum, shared: shared ?? createSectionShared() };
|
|
1363
|
+
ctx.shared.track.deleteDepth = 0;
|
|
1364
|
+
for (const tagName of ["hp:secPr", "secPr"]) {
|
|
1365
|
+
const els = doc.getElementsByTagName(tagName);
|
|
1366
|
+
if (els.length > 0) {
|
|
1367
|
+
const v = els[0].getAttribute("outlineShapeIDRef");
|
|
1368
|
+
if (v) ctx.outlineNumId = v;
|
|
1369
|
+
break;
|
|
1118
1370
|
}
|
|
1119
1371
|
}
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1372
|
+
const blocks = [];
|
|
1373
|
+
walkSection(doc.documentElement, blocks, null, [], ctx);
|
|
1374
|
+
return blocks;
|
|
1123
1375
|
}
|
|
1124
|
-
function
|
|
1125
|
-
|
|
1126
|
-
const
|
|
1127
|
-
if (
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
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`);
|
|
1376
|
+
function extractImageRef(el, depth = 0) {
|
|
1377
|
+
if (depth > MAX_XML_DEPTH) return null;
|
|
1378
|
+
const children = el.childNodes;
|
|
1379
|
+
if (!children) return null;
|
|
1380
|
+
for (let i = 0; i < children.length; i++) {
|
|
1381
|
+
const child = children[i];
|
|
1382
|
+
if (child.nodeType !== 1) continue;
|
|
1383
|
+
const tag = (child.tagName || child.localName || "").replace(/^[^:]+:/, "");
|
|
1384
|
+
if (tag === "imgRect" || tag === "img" || tag === "imgClip") {
|
|
1385
|
+
const ref = child.getAttribute("binaryItemIDRef") || child.getAttribute("href") || "";
|
|
1386
|
+
if (ref) return ref;
|
|
1387
|
+
}
|
|
1388
|
+
const nested = extractImageRef(child, depth + 1);
|
|
1389
|
+
if (nested) return nested;
|
|
1139
1390
|
}
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
validateGongmunOptions(opts);
|
|
1144
|
-
const preset = normalizeGongmunPreset(opts.preset);
|
|
1145
|
-
const d = PRESET_DEFAULTS[preset];
|
|
1146
|
-
const bodyPt = opts.bodyPt ?? d.bodyPt;
|
|
1147
|
-
const autoFitMinRatio = opts.autoFit === false ? null : typeof opts.autoFit === "object" ? Math.min(Math.max(opts.autoFit.minRatio ?? 90, 50), 99) : 90;
|
|
1148
|
-
const coverOn = opts.cover !== void 0 ? opts.cover !== false : preset === "gaejosik";
|
|
1149
|
-
const coverOpts = typeof opts.cover === "object" ? opts.cover : {};
|
|
1150
|
-
const gaejosik = preset === "gaejosik";
|
|
1151
|
-
const reportFamily = gaejosik || preset === "report" || preset === "plan" || preset === "notice" || preset === "press";
|
|
1152
|
-
return {
|
|
1153
|
-
preset,
|
|
1154
|
-
bodyFont: opts.bodyFont ?? "myeongjo",
|
|
1155
|
-
bodyHeight: Math.round(bodyPt * 100),
|
|
1156
|
-
lineSpacing: opts.lineSpacing ?? d.lineSpacing,
|
|
1157
|
-
numbering: opts.numbering ?? d.numbering,
|
|
1158
|
-
margins: opts.margins ?? (reportFamily ? GAEJOSIK_MARGINS : OFFICIAL_MARGINS),
|
|
1159
|
-
centerTitle: opts.centerTitle ?? true,
|
|
1160
|
-
autoFitMinRatio,
|
|
1161
|
-
// 보도자료는 머리박스가 1페이지 최상단을 차지하는 서식이라 표지·목차와 양립 불가 —
|
|
1162
|
-
// 켜면 머리박스가 표지에 얹히고 25pt 제목·부제가 유실된다 (docHead 프리셋 게이팅과 동일 관례)
|
|
1163
|
-
cover: coverOn && preset !== "press" ? { date: coverOpts.date ?? null, org: coverOpts.org ?? "" } : null,
|
|
1164
|
-
toc: preset !== "press" && (opts.toc ?? gaejosik),
|
|
1165
|
-
fonts: opts.fonts ?? {},
|
|
1166
|
-
sizes: opts.sizes ?? {},
|
|
1167
|
-
// 쪽번호 — 보고서 계열 관행(실측: 2_보고서 양식·추진계획·공고문 전부 하단 중앙)
|
|
1168
|
-
pageNumbers: opts.pageNumbers ?? (gaejosik || preset === "report" || preset === "plan"),
|
|
1169
|
-
// 머리말·꼬리말 — 실측: 보고서 계열 15mm(GT3·t2·춘천·브라더), 공고·보도 10mm,
|
|
1170
|
-
// 기안문 0(실결재 41/60건 h0/f0)
|
|
1171
|
-
headerFooter: usesReportFonts(preset) ? GAEJOSIK_HEADER_FOOTER : preset === "notice" || preset === "press" ? 2835 : 0,
|
|
1172
|
-
// "끝." — 기안문 규정(본문 끝 2타+"끝."). 그 외는 opt-in
|
|
1173
|
-
endMark: opts.endMark ?? preset === "official",
|
|
1174
|
-
approval: opts.approval && opts.approval.length > 0 ? opts.approval : null,
|
|
1175
|
-
// 본문 제목박스 — 실측(GT3·GT12): 목차 뒤 본문 시작에 제목 반복. 표지 켜진 개조식 기본
|
|
1176
|
-
bodyTitleBox: opts.bodyTitleBox ?? (gaejosik && coverOn),
|
|
1177
|
-
// h2 말머리 — 실측: 보고서 양식 □ 대항목(QA-2), 공고문 아라비아("1. 사업개요", 바이오헬스 실측)
|
|
1178
|
-
h2Marker: opts.h2Marker ?? (preset === "report" || preset === "plan" ? "box" : preset === "notice" ? "number" : "none"),
|
|
1179
|
-
// 2단계 부호 — 실결재 기안문·공고문 ㅇ 지배(60건 중 ㅇ134:○5), 보고서 양식 계열 ○
|
|
1180
|
-
bullet2: opts.bullet2 ?? (preset === "plan" || preset === "notice" || preset === "press" ? "\u3147" : "\u25CB"),
|
|
1181
|
-
// 단일 형제 부호 생략 — 규정이지만 부호 없는 계단이 실무 눈에 어색 (실무자 QA)
|
|
1182
|
-
suppressSingle: opts.suppressSingle ?? false,
|
|
1183
|
-
docHead: preset === "official" && opts.docHead ? opts.docHead : null,
|
|
1184
|
-
docFoot: preset === "official" && opts.docFoot ? opts.docFoot : null,
|
|
1185
|
-
reportInfo: opts.reportInfo?.trim() || null,
|
|
1186
|
-
noticeHead: preset === "notice" && opts.noticeHead ? opts.noticeHead : null,
|
|
1187
|
-
// 보도자료 머리박스는 프리셋 자체가 요구 — 옵션 미지정이어도 빈 객체
|
|
1188
|
-
press: preset === "press" ? opts.press ?? {} : null
|
|
1189
|
-
};
|
|
1190
|
-
}
|
|
1191
|
-
var HANGUL_INITIALS = [0, 2, 3, 5, 6, 7, 9, 11, 12, 14, 15, 16, 17, 18];
|
|
1192
|
-
var HANGUL_MEDIALS = [0, 4, 8, 13, 18, 20];
|
|
1193
|
-
function hangulOrdinal(n) {
|
|
1194
|
-
const cols = HANGUL_INITIALS.length;
|
|
1195
|
-
const vowel = HANGUL_MEDIALS[Math.min(Math.floor(n / cols), HANGUL_MEDIALS.length - 1)];
|
|
1196
|
-
const init = HANGUL_INITIALS[n % cols];
|
|
1197
|
-
return String.fromCodePoint(44032 + init * 588 + vowel * 28);
|
|
1198
|
-
}
|
|
1199
|
-
function circledNumber(n) {
|
|
1200
|
-
if (n < 20) return String.fromCodePoint(9312 + n);
|
|
1201
|
-
if (n < 35) return String.fromCodePoint(12881 + (n - 20));
|
|
1202
|
-
if (n < 50) return String.fromCodePoint(12977 + (n - 35));
|
|
1203
|
-
return `(${n + 1})`;
|
|
1204
|
-
}
|
|
1205
|
-
function circledHangul(n) {
|
|
1206
|
-
return n < 14 ? String.fromCodePoint(12910 + n) : hangulOrdinal(n);
|
|
1207
|
-
}
|
|
1208
|
-
var REPORT_BULLETS = ["\u25A1", "\u25CB", "-", "\u318D"];
|
|
1209
|
-
var ASTERISK_BULLETS = ["\u25A1", "\u25CB", "*", "\u318D"];
|
|
1210
|
-
function standardMarker(depth, n) {
|
|
1211
|
-
switch (depth) {
|
|
1212
|
-
case 0:
|
|
1213
|
-
return `${n + 1}.`;
|
|
1214
|
-
case 1:
|
|
1215
|
-
return `${hangulOrdinal(n)}.`;
|
|
1216
|
-
case 2:
|
|
1217
|
-
return `${n + 1})`;
|
|
1218
|
-
case 3:
|
|
1219
|
-
return `${hangulOrdinal(n)})`;
|
|
1220
|
-
case 4:
|
|
1221
|
-
return `(${n + 1})`;
|
|
1222
|
-
case 5:
|
|
1223
|
-
return `(${hangulOrdinal(n)})`;
|
|
1224
|
-
case 6:
|
|
1225
|
-
return circledNumber(n);
|
|
1226
|
-
case 7:
|
|
1227
|
-
return circledHangul(n);
|
|
1228
|
-
default:
|
|
1229
|
-
return circledHangul(n);
|
|
1230
|
-
}
|
|
1231
|
-
}
|
|
1232
|
-
function reportMarker(depth, bullet2 = "\u25CB", asteriskThird = false) {
|
|
1233
|
-
const bullets = asteriskThird ? ASTERISK_BULLETS : REPORT_BULLETS;
|
|
1234
|
-
const m = bullets[Math.min(depth, bullets.length - 1)];
|
|
1235
|
-
return depth === 1 ? bullet2 : m;
|
|
1236
|
-
}
|
|
1237
|
-
function markerWidth(marker, bodyHeight) {
|
|
1238
|
-
let em = SPACE_EM_FIXED;
|
|
1239
|
-
for (const c of marker) em += charWidthEm1000(c.codePointAt(0));
|
|
1240
|
-
return Math.round(em / 1e3 * bodyHeight);
|
|
1241
|
-
}
|
|
1242
|
-
function levelIndent(depth, bodyHeight, numbering, sizes = {}, bullet2 = "\u25CB", asteriskThird = false) {
|
|
1243
|
-
if (numbering === "gaejosik") return gaejosikLevelIndent(depth, bodyHeight, sizes, bullet2);
|
|
1244
|
-
const marker = numbering === "report" ? reportMarker(depth, bullet2, asteriskThird) : standardMarker(depth, 0);
|
|
1245
|
-
return { left: Math.round(depth * bodyHeight), indent: -markerWidth(marker, bodyHeight) };
|
|
1246
|
-
}
|
|
1247
|
-
function computeSuppression(depths) {
|
|
1248
|
-
const counts = /* @__PURE__ */ new Map();
|
|
1249
|
-
const keys = [];
|
|
1250
|
-
const path = [];
|
|
1251
|
-
for (const depth of depths) {
|
|
1252
|
-
path.length = depth + 1;
|
|
1253
|
-
path[depth] = (path[depth] ?? 0) + 1;
|
|
1254
|
-
const parentKey = path.slice(0, depth).join(".") + "|" + depth;
|
|
1255
|
-
keys.push(parentKey);
|
|
1256
|
-
counts.set(parentKey, (counts.get(parentKey) ?? 0) + 1);
|
|
1257
|
-
}
|
|
1258
|
-
return keys.map((k) => (counts.get(k) ?? 0) <= 1);
|
|
1259
|
-
}
|
|
1260
|
-
var GongmunNumberer = class {
|
|
1261
|
-
constructor(numbering, bullet2 = "\u25CB", asteriskThird = false) {
|
|
1262
|
-
this.numbering = numbering;
|
|
1263
|
-
this.bullet2 = bullet2;
|
|
1264
|
-
this.asteriskThird = asteriskThird;
|
|
1265
|
-
}
|
|
1266
|
-
counts = [];
|
|
1267
|
-
/** depth 항목 하나에 대한 마커. suppress=true면 빈 문자열(부호 없음) */
|
|
1268
|
-
next(depth, suppress) {
|
|
1269
|
-
this.counts.length = depth + 1;
|
|
1270
|
-
const n = this.counts[depth] ?? 0;
|
|
1271
|
-
this.counts[depth] = n + 1;
|
|
1272
|
-
if (suppress) return "";
|
|
1273
|
-
if (this.numbering === "gaejosik") return gaejosikMarker(depth, this.bullet2);
|
|
1274
|
-
return this.numbering === "report" ? reportMarker(depth, this.bullet2, this.asteriskThird) : standardMarker(depth, n);
|
|
1275
|
-
}
|
|
1276
|
-
reset() {
|
|
1277
|
-
this.counts = [];
|
|
1278
|
-
}
|
|
1279
|
-
};
|
|
1280
|
-
|
|
1281
|
-
// src/hwpx/para-heading.ts
|
|
1282
|
-
var HANGUL_JAMO_SEQ = "\u3131\u3134\u3137\u3139\u3141\u3142\u3145\u3147\u3148\u314A\u314B\u314C\u314D\u314E";
|
|
1283
|
-
function toRoman(n) {
|
|
1284
|
-
if (n <= 0 || n >= 4e3) return String(n);
|
|
1285
|
-
const table2 = [
|
|
1286
|
-
[1e3, "M"],
|
|
1287
|
-
[900, "CM"],
|
|
1288
|
-
[500, "D"],
|
|
1289
|
-
[400, "CD"],
|
|
1290
|
-
[100, "C"],
|
|
1291
|
-
[90, "XC"],
|
|
1292
|
-
[50, "L"],
|
|
1293
|
-
[40, "XL"],
|
|
1294
|
-
[10, "X"],
|
|
1295
|
-
[9, "IX"],
|
|
1296
|
-
[5, "V"],
|
|
1297
|
-
[4, "IV"],
|
|
1298
|
-
[1, "I"]
|
|
1299
|
-
];
|
|
1300
|
-
let out = "";
|
|
1301
|
-
for (const [v, s] of table2) {
|
|
1302
|
-
while (n >= v) {
|
|
1303
|
-
out += s;
|
|
1304
|
-
n -= v;
|
|
1305
|
-
}
|
|
1306
|
-
}
|
|
1307
|
-
return out;
|
|
1308
|
-
}
|
|
1309
|
-
function formatHeadNumber(n, numFormat) {
|
|
1310
|
-
if (n === 0 && numFormat === "DIGIT") return "0";
|
|
1311
|
-
if (n <= 0) n = 1;
|
|
1312
|
-
switch (numFormat) {
|
|
1313
|
-
case "DIGIT":
|
|
1314
|
-
return String(n);
|
|
1315
|
-
// 서수 시퀀스는 생성기(gongmun.ts)와 단일 소스 (v4.0.5) — 종전 mod-14 순환은
|
|
1316
|
-
// 15번째 형제를 다시 '가'로 만들어 한글 실렌더(가→하→거→너 단모음 연속)와 어긋났다
|
|
1317
|
-
case "CIRCLED_DIGIT":
|
|
1318
|
-
return n <= 50 ? circledNumber(n - 1) : `(${n})`;
|
|
1319
|
-
case "HANGUL_SYLLABLE":
|
|
1320
|
-
return hangulOrdinal(n - 1);
|
|
1321
|
-
case "CIRCLED_HANGUL_SYLLABLE":
|
|
1322
|
-
return n <= 14 ? circledHangul(n - 1) : hangulOrdinal(n - 1);
|
|
1323
|
-
case "HANGUL_JAMO":
|
|
1324
|
-
return HANGUL_JAMO_SEQ[(n - 1) % HANGUL_JAMO_SEQ.length];
|
|
1325
|
-
case "CIRCLED_HANGUL_JAMO":
|
|
1326
|
-
return n <= 14 ? String.fromCodePoint(12896 + n - 1) : HANGUL_JAMO_SEQ[(n - 1) % 14];
|
|
1327
|
-
case "LATIN_CAPITAL":
|
|
1328
|
-
return String.fromCharCode(65 + (n - 1) % 26);
|
|
1329
|
-
case "LATIN_SMALL":
|
|
1330
|
-
return String.fromCharCode(97 + (n - 1) % 26);
|
|
1331
|
-
case "CIRCLED_LATIN_CAPITAL":
|
|
1332
|
-
return n <= 26 ? String.fromCodePoint(9398 + n - 1) : String.fromCharCode(65 + (n - 1) % 26);
|
|
1333
|
-
case "CIRCLED_LATIN_SMALL":
|
|
1334
|
-
return n <= 26 ? String.fromCodePoint(9424 + n - 1) : String.fromCharCode(97 + (n - 1) % 26);
|
|
1335
|
-
case "ROMAN_CAPITAL":
|
|
1336
|
-
return toRoman(n);
|
|
1337
|
-
case "ROMAN_SMALL":
|
|
1338
|
-
return toRoman(n).toLowerCase();
|
|
1339
|
-
default:
|
|
1340
|
-
return String(n);
|
|
1341
|
-
}
|
|
1342
|
-
}
|
|
1343
|
-
function resolveParaHeading(paraEl, ctx) {
|
|
1344
|
-
const sm = ctx.styleMap;
|
|
1345
|
-
if (!sm) return null;
|
|
1346
|
-
const styleId = paraEl.getAttribute("styleIDRef");
|
|
1347
|
-
let styleHeading;
|
|
1348
|
-
if (styleId && styleId !== "0") {
|
|
1349
|
-
const st = sm.styles.get(styleId);
|
|
1350
|
-
const m = st && /^개요\s*([1-6])$/.exec(st.name);
|
|
1351
|
-
if (m) styleHeading = Math.min(parseInt(m[1], 10), 6);
|
|
1352
|
-
}
|
|
1353
|
-
const prId = paraEl.getAttribute("paraPrIDRef");
|
|
1354
|
-
if (!prId) return styleHeading ? { headingLevel: styleHeading } : null;
|
|
1355
|
-
const ref = sm.paraHeadings.get(prId);
|
|
1356
|
-
if (!ref) return styleHeading ? { headingLevel: styleHeading } : null;
|
|
1357
|
-
if (ref.type === "BULLET") {
|
|
1358
|
-
const char = sm.bullets.get(ref.idRef);
|
|
1359
|
-
return char ? { prefix: char } : null;
|
|
1360
|
-
}
|
|
1361
|
-
const numId = ref.type === "OUTLINE" ? ctx.outlineNumId || "1" : ref.idRef;
|
|
1362
|
-
const level = Math.min(ref.level + 1, 10);
|
|
1363
|
-
const headingLevel = ref.type === "OUTLINE" ? Math.min(ref.level + 1, 6) : void 0;
|
|
1364
|
-
const numDef = sm.numberings.get(numId);
|
|
1365
|
-
if (!numDef) return headingLevel ? { headingLevel } : null;
|
|
1366
|
-
let counters = ctx.shared.numState.get(numId);
|
|
1367
|
-
if (!counters) {
|
|
1368
|
-
counters = new Array(11).fill(-1);
|
|
1369
|
-
ctx.shared.numState.set(numId, counters);
|
|
1370
|
-
}
|
|
1371
|
-
const head = numDef.heads.get(level);
|
|
1372
|
-
counters[level] = counters[level] < 0 ? head?.start ?? 1 : counters[level] + 1;
|
|
1373
|
-
for (let l = level + 1; l <= 10; l++) counters[l] = -1;
|
|
1374
|
-
const fmtText = head ? head.text.trim() : `^${level}.`;
|
|
1375
|
-
const prefix = fmtText.replace(/\^(10|[1-9])/g, (_, d) => {
|
|
1376
|
-
const lv = parseInt(d, 10);
|
|
1377
|
-
const refHead = numDef.heads.get(lv);
|
|
1378
|
-
const n = counters[lv] >= 0 ? counters[lv] : refHead?.start ?? 1;
|
|
1379
|
-
return formatHeadNumber(n, refHead?.numFormat || "DIGIT");
|
|
1380
|
-
});
|
|
1381
|
-
return { prefix: prefix || void 0, headingLevel };
|
|
1382
|
-
}
|
|
1383
|
-
|
|
1384
|
-
// src/hwpx/table-build.ts
|
|
1385
|
-
function buildTableWithCellMeta(state, keepAnchoredEmptyCols) {
|
|
1386
|
-
const table2 = buildTable(state.rows, { keepAnchoredEmptyCols });
|
|
1387
|
-
if (state.caption) table2.caption = state.caption;
|
|
1388
|
-
if (state.captionBlocks?.length) table2.captionBlocks = state.captionBlocks;
|
|
1389
|
-
const anchors = [];
|
|
1390
|
-
{
|
|
1391
|
-
const covered = /* @__PURE__ */ new Set();
|
|
1392
|
-
for (let r = 0; r < table2.rows; r++) {
|
|
1393
|
-
for (let c = 0; c < table2.cols; c++) {
|
|
1394
|
-
if (covered.has(`${r},${c}`)) continue;
|
|
1395
|
-
const cell2 = table2.cells[r]?.[c];
|
|
1396
|
-
if (!cell2) continue;
|
|
1397
|
-
for (let dr = 0; dr < cell2.rowSpan; dr++) {
|
|
1398
|
-
for (let dc = 0; dc < cell2.colSpan; dc++) {
|
|
1399
|
-
if (dr === 0 && dc === 0) continue;
|
|
1400
|
-
if (r + dr < table2.rows && c + dc < table2.cols) covered.add(`${r + dr},${c + dc}`);
|
|
1401
|
-
}
|
|
1402
|
-
}
|
|
1403
|
-
anchors.push(cell2);
|
|
1404
|
-
c += cell2.colSpan - 1;
|
|
1405
|
-
}
|
|
1406
|
-
}
|
|
1407
|
-
}
|
|
1408
|
-
const srcCount = state.rows.reduce((s, r) => s + r.length, 0);
|
|
1409
|
-
const ordinalReliable = anchors.length === srcCount;
|
|
1410
|
-
const claimed = /* @__PURE__ */ new Set();
|
|
1411
|
-
let flatIdx = -1;
|
|
1412
|
-
for (const row of state.rows) {
|
|
1413
|
-
for (const src of row) {
|
|
1414
|
-
flatIdx++;
|
|
1415
|
-
const needsBlocks = !!src.blocks && src.blocks.length > 0 && (src.hasStructure || src.blocks.some((b) => b.spans));
|
|
1416
|
-
if (!needsBlocks && !src.isHeader) continue;
|
|
1417
|
-
let target;
|
|
1418
|
-
const trimmed = src.text.trim();
|
|
1419
|
-
if (src.rowAddr !== void 0 && src.colAddr !== void 0) {
|
|
1420
|
-
const cand = table2.cells[src.rowAddr]?.[src.colAddr];
|
|
1421
|
-
if (cand && cand.text === trimmed && !claimed.has(cand)) target = cand;
|
|
1422
|
-
}
|
|
1423
|
-
if (!target) {
|
|
1424
|
-
outer: for (const irRow of table2.cells) {
|
|
1425
|
-
for (const cand of irRow) {
|
|
1426
|
-
if (!claimed.has(cand) && cand.text === trimmed && cand.colSpan === src.colSpan && cand.rowSpan === src.rowSpan) {
|
|
1427
|
-
target = cand;
|
|
1428
|
-
break outer;
|
|
1429
|
-
}
|
|
1430
|
-
}
|
|
1431
|
-
}
|
|
1432
|
-
}
|
|
1433
|
-
if (!target && ordinalReliable) {
|
|
1434
|
-
const cand = anchors[flatIdx];
|
|
1435
|
-
if (cand && !claimed.has(cand)) target = cand;
|
|
1436
|
-
}
|
|
1437
|
-
if (!target) continue;
|
|
1438
|
-
claimed.add(target);
|
|
1439
|
-
if (needsBlocks) target.blocks = src.blocks;
|
|
1440
|
-
if (src.isHeader) target.isHeader = true;
|
|
1441
|
-
}
|
|
1442
|
-
}
|
|
1443
|
-
return table2;
|
|
1444
|
-
}
|
|
1445
|
-
function completeTable(newTable, tableStack, blocks, ctx) {
|
|
1446
|
-
const parentTable = tableStack.length > 0 ? tableStack.pop() : null;
|
|
1447
|
-
if (newTable.rows.length === 0) {
|
|
1448
|
-
if (newTable.caption) blocks.push({ type: "paragraph", text: newTable.caption, pageNumber: ctx.sectionNum });
|
|
1449
|
-
return parentTable;
|
|
1450
|
-
}
|
|
1451
|
-
const ir = buildTableWithCellMeta(newTable, ctx.shared.keepTrailingEmptyCols);
|
|
1452
|
-
const block = { type: "table", table: ir, pageNumber: ctx.sectionNum };
|
|
1453
|
-
if (parentTable?.cell) {
|
|
1454
|
-
const cell2 = parentTable.cell;
|
|
1455
|
-
(cell2.blocks ??= []).push(block);
|
|
1456
|
-
cell2.hasStructure = true;
|
|
1457
|
-
let flat = convertTableToText(newTable.rows);
|
|
1458
|
-
if (newTable.caption) flat = newTable.caption + (flat ? "\n" + flat : "");
|
|
1459
|
-
if (flat) {
|
|
1460
|
-
const inlineJoin = newTable.inline === true && cell2.lineOpen === true;
|
|
1461
|
-
cell2.text += (cell2.text ? inlineJoin ? " " : "\n" : "") + flat;
|
|
1462
|
-
cell2.lineOpen = newTable.inline === true;
|
|
1463
|
-
}
|
|
1464
|
-
} else {
|
|
1465
|
-
blocks.push(block);
|
|
1466
|
-
}
|
|
1467
|
-
return parentTable;
|
|
1468
|
-
}
|
|
1469
|
-
|
|
1470
|
-
// src/hwpx/section-walker.ts
|
|
1471
|
-
function parseSectionXml(xml, styleMap, warnings, sectionNum, shared) {
|
|
1472
|
-
const parser = createXmlParser(warnings);
|
|
1473
|
-
const doc = parser.parseFromString(stripDtd(xml), "text/xml");
|
|
1474
|
-
if (!doc.documentElement) return [];
|
|
1475
|
-
const ctx = { styleMap, warnings, sectionNum, shared: shared ?? createSectionShared() };
|
|
1476
|
-
ctx.shared.track.deleteDepth = 0;
|
|
1477
|
-
for (const tagName of ["hp:secPr", "secPr"]) {
|
|
1478
|
-
const els = doc.getElementsByTagName(tagName);
|
|
1479
|
-
if (els.length > 0) {
|
|
1480
|
-
const v = els[0].getAttribute("outlineShapeIDRef");
|
|
1481
|
-
if (v) ctx.outlineNumId = v;
|
|
1482
|
-
break;
|
|
1483
|
-
}
|
|
1484
|
-
}
|
|
1485
|
-
const blocks = [];
|
|
1486
|
-
walkSection(doc.documentElement, blocks, null, [], ctx);
|
|
1487
|
-
return blocks;
|
|
1488
|
-
}
|
|
1489
|
-
function extractImageRef(el, depth = 0) {
|
|
1490
|
-
if (depth > MAX_XML_DEPTH) return null;
|
|
1491
|
-
const children = el.childNodes;
|
|
1492
|
-
if (!children) return null;
|
|
1493
|
-
for (let i = 0; i < children.length; i++) {
|
|
1494
|
-
const child = children[i];
|
|
1495
|
-
if (child.nodeType !== 1) continue;
|
|
1496
|
-
const tag = (child.tagName || child.localName || "").replace(/^[^:]+:/, "");
|
|
1497
|
-
if (tag === "imgRect" || tag === "img" || tag === "imgClip") {
|
|
1498
|
-
const ref = child.getAttribute("binaryItemIDRef") || child.getAttribute("href") || "";
|
|
1499
|
-
if (ref) return ref;
|
|
1500
|
-
}
|
|
1501
|
-
const nested = extractImageRef(child, depth + 1);
|
|
1502
|
-
if (nested) return nested;
|
|
1503
|
-
}
|
|
1504
|
-
const directRef = el.getAttribute("binaryItemIDRef") || "";
|
|
1505
|
-
if (directRef) return directRef;
|
|
1506
|
-
return null;
|
|
1391
|
+
const directRef = el.getAttribute("binaryItemIDRef") || "";
|
|
1392
|
+
if (directRef) return directRef;
|
|
1393
|
+
return null;
|
|
1507
1394
|
}
|
|
1508
1395
|
function walkSection(node, blocks, tableCtx, tableStack, ctx, depth = 0) {
|
|
1509
1396
|
if (depth > MAX_XML_DEPTH) return;
|
|
@@ -1625,7 +1512,8 @@ function walkSection(node, blocks, tableCtx, tableStack, ctx, depth = 0) {
|
|
|
1625
1512
|
if (tableCtx?.cell) {
|
|
1626
1513
|
const cell2 = tableCtx.cell;
|
|
1627
1514
|
if (footnote) text += ` (\uC8FC: ${footnote})`;
|
|
1628
|
-
cell2.text += (cell2.text ? "\n" : "") + text;
|
|
1515
|
+
cell2.text += (cell2.text || cell2.paraSeen ? "\n" : "") + text;
|
|
1516
|
+
cell2.paraSeen = true;
|
|
1629
1517
|
const cellBlock = { type: "paragraph", text, pageNumber: ctx.sectionNum };
|
|
1630
1518
|
const cellSpanMode = spanModeOf(ctx.shared.kordocLayout);
|
|
1631
1519
|
if (cellSpanMode && !ph?.prefix) {
|
|
@@ -1670,6 +1558,16 @@ function walkSection(node, blocks, tableCtx, tableStack, ctx, depth = 0) {
|
|
|
1670
1558
|
} else {
|
|
1671
1559
|
blocks.push({ type: "paragraph", text, pageNumber: ctx.sectionNum });
|
|
1672
1560
|
}
|
|
1561
|
+
} else if (ctx.shared.keepEmptyParagraphs && !hasObjectDescendant(el)) {
|
|
1562
|
+
if (tableCtx?.cell) {
|
|
1563
|
+
const cell2 = tableCtx.cell;
|
|
1564
|
+
if (cell2.text || cell2.paraSeen) cell2.text += "\n";
|
|
1565
|
+
cell2.paraSeen = true;
|
|
1566
|
+
cell2.lineOpen = false;
|
|
1567
|
+
(cell2.blocks ??= []).push({ type: "paragraph", text: "", pageNumber: ctx.sectionNum });
|
|
1568
|
+
} else if (!tableCtx) {
|
|
1569
|
+
blocks.push({ type: "paragraph", text: "", pageNumber: ctx.sectionNum });
|
|
1570
|
+
}
|
|
1673
1571
|
}
|
|
1674
1572
|
tableCtx = walkParagraphChildren(el, blocks, tableCtx, tableStack, ctx, depth + 1);
|
|
1675
1573
|
break;
|
|
@@ -1846,10 +1744,10 @@ function buildSubListTable(el, ctx, depth) {
|
|
|
1846
1744
|
}
|
|
1847
1745
|
function findTopLevelTbls(el, out, depth = 0) {
|
|
1848
1746
|
if (depth > MAX_XML_DEPTH) return;
|
|
1849
|
-
const
|
|
1850
|
-
if (!
|
|
1851
|
-
for (let i = 0; i <
|
|
1852
|
-
const ch =
|
|
1747
|
+
const kids = el.childNodes;
|
|
1748
|
+
if (!kids) return;
|
|
1749
|
+
for (let i = 0; i < kids.length; i++) {
|
|
1750
|
+
const ch = kids[i];
|
|
1853
1751
|
if (ch.nodeType !== 1) continue;
|
|
1854
1752
|
const tag = (ch.tagName || ch.localName || "").replace(/^[^:]+:/, "");
|
|
1855
1753
|
if (tag === "tbl") {
|
|
@@ -1865,10 +1763,10 @@ function walkParagraphChildren(node, blocks, tableCtx, tableStack, ctx, depth =
|
|
|
1865
1763
|
if (!children) return tableCtx;
|
|
1866
1764
|
const walkChildren = (parent, d, inShape = false) => {
|
|
1867
1765
|
if (d > MAX_XML_DEPTH) return;
|
|
1868
|
-
const
|
|
1869
|
-
if (!
|
|
1870
|
-
for (let i = 0; i <
|
|
1871
|
-
const el =
|
|
1766
|
+
const kids = parent.childNodes;
|
|
1767
|
+
if (!kids) return;
|
|
1768
|
+
for (let i = 0; i < kids.length; i++) {
|
|
1769
|
+
const el = kids[i];
|
|
1872
1770
|
if (el.nodeType !== 1) continue;
|
|
1873
1771
|
const tag = el.tagName || el.localName || "";
|
|
1874
1772
|
const localTag = tag.replace(/^[^:]+:/, "");
|
|
@@ -1922,6 +1820,20 @@ function walkParagraphChildren(node, blocks, tableCtx, tableStack, ctx, depth =
|
|
|
1922
1820
|
walkChildren(node, depth);
|
|
1923
1821
|
return tableCtx;
|
|
1924
1822
|
}
|
|
1823
|
+
var OBJECT_TAGS = /* @__PURE__ */ new Set(["tbl", "pic", "shape", "drawingObject", "drawText"]);
|
|
1824
|
+
function hasObjectDescendant(node, depth = 0) {
|
|
1825
|
+
if (depth > MAX_XML_DEPTH) return false;
|
|
1826
|
+
const kids = node.childNodes;
|
|
1827
|
+
if (!kids) return false;
|
|
1828
|
+
for (let i = 0; i < kids.length; i++) {
|
|
1829
|
+
const ch = kids[i];
|
|
1830
|
+
if (ch.nodeType !== 1) continue;
|
|
1831
|
+
const tag = (ch.tagName || ch.localName || "").replace(/^[^:]+:/, "");
|
|
1832
|
+
if (OBJECT_TAGS.has(tag)) return true;
|
|
1833
|
+
if (hasObjectDescendant(ch, depth + 1)) return true;
|
|
1834
|
+
}
|
|
1835
|
+
return false;
|
|
1836
|
+
}
|
|
1925
1837
|
function isInlineTbl(tbl2) {
|
|
1926
1838
|
return findChildByLocalName(tbl2, "pos")?.getAttribute("treatAsChar") === "1";
|
|
1927
1839
|
}
|
|
@@ -2009,10 +1921,10 @@ function extractParagraphInfo(para2, styleMap, ctx) {
|
|
|
2009
1921
|
if (open?.rangeIdx !== void 0) linkRanges[open.rangeIdx].end = text.length;
|
|
2010
1922
|
};
|
|
2011
1923
|
const handleCtrl = (ctrlEl) => {
|
|
2012
|
-
const
|
|
2013
|
-
if (!
|
|
2014
|
-
for (let j = 0; j <
|
|
2015
|
-
const k =
|
|
1924
|
+
const kids = ctrlEl.childNodes;
|
|
1925
|
+
if (!kids) return;
|
|
1926
|
+
for (let j = 0; j < kids.length; j++) {
|
|
1927
|
+
const k = kids[j];
|
|
2016
1928
|
if (k.nodeType !== 1) continue;
|
|
2017
1929
|
const ktag = (k.tagName || k.localName || "").replace(/^[^:]+:/, "");
|
|
2018
1930
|
switch (ktag) {
|
|
@@ -2205,6 +2117,13 @@ function extractParagraphInfo(para2, styleMap, ctx) {
|
|
|
2205
2117
|
const latex = hmlToLatex(raw).trim();
|
|
2206
2118
|
if (latex) text += " $" + latex + "$ ";
|
|
2207
2119
|
} catch {
|
|
2120
|
+
if (ctx?.warnings) {
|
|
2121
|
+
ctx.warnings.push({
|
|
2122
|
+
page: ctx.sectionNum,
|
|
2123
|
+
message: `\uC218\uC2DD LaTeX \uBCC0\uD658 \uC2E4\uD328 \u2014 \uC218\uC2DD \uD14D\uC2A4\uD2B8 \uC81C\uC678: ${raw.trim().slice(0, 40)}`,
|
|
2124
|
+
code: "PARTIAL_PARSE"
|
|
2125
|
+
});
|
|
2126
|
+
}
|
|
2208
2127
|
}
|
|
2209
2128
|
}
|
|
2210
2129
|
break;
|
|
@@ -2276,10 +2195,10 @@ function extractRunSpans(para2, ctx, mode, requireMixed) {
|
|
|
2276
2195
|
if (!styleMap) return null;
|
|
2277
2196
|
const spans = [];
|
|
2278
2197
|
let styled = false;
|
|
2279
|
-
const
|
|
2280
|
-
if (!
|
|
2281
|
-
for (let i = 0; i <
|
|
2282
|
-
const child =
|
|
2198
|
+
const kids = para2.childNodes;
|
|
2199
|
+
if (!kids) return null;
|
|
2200
|
+
for (let i = 0; i < kids.length; i++) {
|
|
2201
|
+
const child = kids[i];
|
|
2283
2202
|
if (child.nodeType !== 1) continue;
|
|
2284
2203
|
const tag = (child.tagName || child.localName || "").replace(/^[^:]+:/, "");
|
|
2285
2204
|
if (tag === "linesegarray") continue;
|
|
@@ -2334,9 +2253,9 @@ function gongmunDepthFromIndent(para2, left, ctx) {
|
|
|
2334
2253
|
const styleMap = ctx.styleMap;
|
|
2335
2254
|
if (!styleMap) return null;
|
|
2336
2255
|
let unit = 0;
|
|
2337
|
-
const
|
|
2338
|
-
for (let i = 0; i < (
|
|
2339
|
-
const child =
|
|
2256
|
+
const kids = para2.childNodes;
|
|
2257
|
+
for (let i = 0; i < (kids?.length ?? 0); i++) {
|
|
2258
|
+
const child = kids[i];
|
|
2340
2259
|
if (child.nodeType !== 1) continue;
|
|
2341
2260
|
const tag = (child.tagName || child.localName || "").replace(/^[^:]+:/, "");
|
|
2342
2261
|
if (tag !== "run" && tag !== "r") continue;
|
|
@@ -2650,17 +2569,19 @@ function normalizeBinPayload(data) {
|
|
|
2650
2569
|
return data;
|
|
2651
2570
|
}
|
|
2652
2571
|
var BIN_ENTRY_RE = /(?:^|\/)BIN([0-9A-Fa-f]{4,8})(?:\.[^./\\]*)?$/;
|
|
2653
|
-
|
|
2572
|
+
var MAX_BLOCK_DEPTH = 200;
|
|
2573
|
+
function collectImageBlocks(blocks, out, depth = 0) {
|
|
2574
|
+
if (depth > MAX_BLOCK_DEPTH) return;
|
|
2654
2575
|
for (const b of blocks) {
|
|
2655
2576
|
if (b.type === "image") out.push(b);
|
|
2656
2577
|
if (b.table) {
|
|
2657
2578
|
for (const row of b.table.cells) {
|
|
2658
2579
|
for (const cell2 of row) {
|
|
2659
|
-
if (cell2.blocks) collectImageBlocks(cell2.blocks, out);
|
|
2580
|
+
if (cell2.blocks) collectImageBlocks(cell2.blocks, out, depth + 1);
|
|
2660
2581
|
}
|
|
2661
2582
|
}
|
|
2662
2583
|
}
|
|
2663
|
-
if (b.children) collectImageBlocks(b.children, out);
|
|
2584
|
+
if (b.children) collectImageBlocks(b.children, out, depth + 1);
|
|
2664
2585
|
}
|
|
2665
2586
|
}
|
|
2666
2587
|
function forEachTableCell(blocks, fn) {
|
|
@@ -2828,6 +2749,7 @@ function collectImageBlocks2(blocks, out, ownerCell, depth = 0) {
|
|
|
2828
2749
|
}
|
|
2829
2750
|
}
|
|
2830
2751
|
}
|
|
2752
|
+
if (block.children?.length) collectImageBlocks2(block.children, out, ownerCell, depth + 1);
|
|
2831
2753
|
}
|
|
2832
2754
|
}
|
|
2833
2755
|
async function extractImagesFromZip(zip, blocks, decompressed, warnings, sweepUnreferenced) {
|
|
@@ -2925,7 +2847,7 @@ async function extractImagesFromZip(zip, blocks, decompressed, warnings, sweepUn
|
|
|
2925
2847
|
}
|
|
2926
2848
|
|
|
2927
2849
|
// src/hwpx/metadata.ts
|
|
2928
|
-
import
|
|
2850
|
+
import JSZip2 from "jszip";
|
|
2929
2851
|
|
|
2930
2852
|
// src/hwpx/zip-sections.ts
|
|
2931
2853
|
import { inflateRawSync as inflateRawSync2 } from "zlib";
|
|
@@ -3089,7 +3011,7 @@ function parseDublinCoreMetadata(xml, metadata) {
|
|
|
3089
3011
|
async function extractHwpxMetadataOnly(buffer) {
|
|
3090
3012
|
let zip;
|
|
3091
3013
|
try {
|
|
3092
|
-
zip = await
|
|
3014
|
+
zip = await JSZip2.loadAsync(buffer);
|
|
3093
3015
|
} catch {
|
|
3094
3016
|
throw new KordocError("HWPX ZIP\uC744 \uC5F4 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4");
|
|
3095
3017
|
}
|
|
@@ -3105,7 +3027,7 @@ async function parseHwpxDocument(buffer, options) {
|
|
|
3105
3027
|
precheckZipSize(buffer, MAX_DECOMPRESS_SIZE, MAX_ZIP_ENTRIES);
|
|
3106
3028
|
let zip;
|
|
3107
3029
|
try {
|
|
3108
|
-
zip = await
|
|
3030
|
+
zip = await JSZip3.loadAsync(buffer);
|
|
3109
3031
|
} catch {
|
|
3110
3032
|
return extractFromBrokenZip(buffer);
|
|
3111
3033
|
}
|
|
@@ -3140,6 +3062,7 @@ async function parseHwpxDocument(buffer, options) {
|
|
|
3140
3062
|
const shared = createSectionShared();
|
|
3141
3063
|
shared.kordocLayout = await readKordocLayout(zip);
|
|
3142
3064
|
shared.keepTrailingEmptyCols = options?.keepTrailingEmptyCols;
|
|
3065
|
+
shared.keepEmptyParagraphs = options?.keepEmptyParagraphs;
|
|
3143
3066
|
let parsedSections = 0;
|
|
3144
3067
|
for (let si = 0; si < sectionPaths.length; si++) {
|
|
3145
3068
|
if (pageFilter && !pageFilter.has(si + 1)) continue;
|
|
@@ -3240,28 +3163,11 @@ function shapeFormatToNumFmt(code) {
|
|
|
3240
3163
|
return "digit";
|
|
3241
3164
|
}
|
|
3242
3165
|
}
|
|
3243
|
-
var CIRCLED_DIGITS = "\u2460\u2461\u2462\u2463\u2464\u2465\u2466\u2467\u2468\u2469\u246A\u246B\u246C\u246D\u246E\u246F\u2470\u2471\u2472\u2473";
|
|
3244
|
-
var GANADA = "\uAC00\uB098\uB2E4\uB77C\uB9C8\uBC14\uC0AC\uC544\uC790\uCC28\uCE74\uD0C0\uD30C\uD558";
|
|
3245
|
-
var CIRCLED_GANADA = "\u326E\u326F\u3270\u3271\u3272\u3273\u3274\u3275\u3276\u3277\u3278\u3279\u327A\u327B";
|
|
3246
3166
|
var JAMO = "\u3131\u3134\u3137\u3139\u3141\u3142\u3145\u3147\u3148\u314A\u314B\u314C\u314D\u314E";
|
|
3247
3167
|
var CIRCLED_JAMO = "\u3260\u3261\u3262\u3263\u3264\u3265\u3266\u3267\u3268\u3269\u326A\u326B\u326C\u326D";
|
|
3248
3168
|
function fromTable(n, table2) {
|
|
3249
3169
|
return n >= 1 && n <= table2.length ? table2[n - 1] : String(n);
|
|
3250
3170
|
}
|
|
3251
|
-
function formatRoman(n, upper) {
|
|
3252
|
-
if (n <= 0 || n > 3999) return String(n);
|
|
3253
|
-
const values = [1e3, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1];
|
|
3254
|
-
const symbols = upper ? ["M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"] : ["m", "cm", "d", "cd", "c", "xc", "l", "xl", "x", "ix", "v", "iv", "i"];
|
|
3255
|
-
let result = "";
|
|
3256
|
-
let num2 = n;
|
|
3257
|
-
for (let i = 0; i < values.length; i++) {
|
|
3258
|
-
while (num2 >= values[i]) {
|
|
3259
|
-
result += symbols[i];
|
|
3260
|
-
num2 -= values[i];
|
|
3261
|
-
}
|
|
3262
|
-
}
|
|
3263
|
-
return result;
|
|
3264
|
-
}
|
|
3265
3171
|
function formatLatin(n, upper) {
|
|
3266
3172
|
if (n <= 0) return "";
|
|
3267
3173
|
let result = "";
|
|
@@ -3297,19 +3203,19 @@ var HANJA_UNITS = ["", "\u5341", "\u767E", "\u5343", "\u842C"];
|
|
|
3297
3203
|
function formatNumber(n, fmt) {
|
|
3298
3204
|
switch (fmt) {
|
|
3299
3205
|
case "circled":
|
|
3300
|
-
return
|
|
3206
|
+
return n >= 1 ? circledNumber(n - 1) : String(n);
|
|
3301
3207
|
case "romanUpper":
|
|
3302
|
-
return
|
|
3208
|
+
return romanNumeral(n, true);
|
|
3303
3209
|
case "romanLower":
|
|
3304
|
-
return
|
|
3210
|
+
return romanNumeral(n, false);
|
|
3305
3211
|
case "latinUpper":
|
|
3306
3212
|
return formatLatin(n, true) || String(n);
|
|
3307
3213
|
case "latinLower":
|
|
3308
3214
|
return formatLatin(n, false) || String(n);
|
|
3309
3215
|
case "ganada":
|
|
3310
|
-
return
|
|
3216
|
+
return n >= 1 ? hangulOrdinal(n - 1) : String(n);
|
|
3311
3217
|
case "circledGanada":
|
|
3312
|
-
return
|
|
3218
|
+
return n >= 1 ? circledHangul(n - 1) : String(n);
|
|
3313
3219
|
case "jamo":
|
|
3314
3220
|
return fromTable(n, JAMO);
|
|
3315
3221
|
case "circledJamo":
|
|
@@ -4047,220 +3953,75 @@ function decryptViewText(viewTextRaw, compressed) {
|
|
|
4047
3953
|
}
|
|
4048
3954
|
|
|
4049
3955
|
// src/hwp5/equation.ts
|
|
4050
|
-
var
|
|
4051
|
-
|
|
4052
|
-
|
|
4053
|
-
|
|
4054
|
-
|
|
4055
|
-
|
|
4056
|
-
|
|
4057
|
-
|
|
4058
|
-
|
|
4059
|
-
|
|
4060
|
-
|
|
4061
|
-
|
|
4062
|
-
|
|
4063
|
-
|
|
4064
|
-
|
|
4065
|
-
|
|
4066
|
-
|
|
4067
|
-
|
|
4068
|
-
|
|
4069
|
-
|
|
4070
|
-
|
|
4071
|
-
|
|
4072
|
-
|
|
4073
|
-
|
|
4074
|
-
|
|
4075
|
-
|
|
4076
|
-
|
|
4077
|
-
|
|
4078
|
-
|
|
4079
|
-
|
|
4080
|
-
|
|
4081
|
-
|
|
4082
|
-
|
|
4083
|
-
|
|
4084
|
-
|
|
4085
|
-
|
|
4086
|
-
|
|
4087
|
-
|
|
4088
|
-
|
|
4089
|
-
|
|
4090
|
-
|
|
4091
|
-
|
|
4092
|
-
|
|
4093
|
-
|
|
4094
|
-
|
|
4095
|
-
|
|
4096
|
-
["le", "\\leq"],
|
|
4097
|
-
["ge", "\\geq"],
|
|
4098
|
-
["geq", "\\geq"],
|
|
4099
|
-
["deg", "^\\circ"],
|
|
4100
|
-
["rarrow", "\\rightarrow"],
|
|
4101
|
-
["larrow", "\\leftarrow"],
|
|
4102
|
-
["lrarrow", "\\leftrightarrow"],
|
|
4103
|
-
["in", "\\in"],
|
|
4104
|
-
["notin", "\\notin"],
|
|
4105
|
-
["emptyset", "\\emptyset"],
|
|
4106
|
-
["subset", "\\subset"],
|
|
4107
|
-
["nsubset", "\\nsubseteq"],
|
|
4108
|
-
["cup", "\\cup"],
|
|
4109
|
-
["cap", "\\cap"],
|
|
4110
|
-
["smallinter", "\\cap"],
|
|
4111
|
-
["sim", "\\sim"],
|
|
4112
|
-
["circ", "\\circ"],
|
|
4113
|
-
["bot", "\\perp"],
|
|
4114
|
-
["dyad", "\\overleftrightarrow"],
|
|
4115
|
-
["arch", "\\overset{\\frown}"]
|
|
4116
|
-
]);
|
|
4117
|
-
function hwpEquationToLatex(equation) {
|
|
4118
|
-
return convertEquation(equation.replace(/\0/g, "").trim(), 0);
|
|
4119
|
-
}
|
|
4120
|
-
function convertEquation(equation, depth) {
|
|
4121
|
-
if (!equation || depth > 12) return equation;
|
|
4122
|
-
let result = equation.replace(/\s+/g, " ").replace(/`+/g, "\\,").replace(/~+/g, "\\,").trim();
|
|
4123
|
-
result = convertMatrixLike(result);
|
|
4124
|
-
result = convertRoots(result, depth);
|
|
4125
|
-
result = convertOver(result, depth);
|
|
4126
|
-
result = convertSqrt(result, depth);
|
|
4127
|
-
result = convertScripts(result);
|
|
4128
|
-
result = convertOperators(result);
|
|
4129
|
-
result = removeFontDirectives(result);
|
|
4130
|
-
result = convertWords(result);
|
|
4131
|
-
result = cleanupLatexSpacing(result);
|
|
4132
|
-
return result;
|
|
4133
|
-
}
|
|
4134
|
-
function convertMatrixLike(input) {
|
|
4135
|
-
return input.replace(
|
|
4136
|
-
/\bmatrix\s*\{([^{}]*)\}/gi,
|
|
4137
|
-
(_match, body) => `\\begin{matrix} ${body.split("#").map((part) => part.trim()).join(" & ")} \\end{matrix}`
|
|
4138
|
-
).replace(
|
|
4139
|
-
/\bcases\s*\{([^{}]*)\}/gi,
|
|
4140
|
-
(_match, body) => `\\begin{cases} ${body.split("#").map((part) => part.trim()).join(" \\\\ ")} \\end{cases}`
|
|
4141
|
-
);
|
|
4142
|
-
}
|
|
4143
|
-
function convertRoots(input, depth) {
|
|
4144
|
-
return input.replace(/(?<!\\)\broot\s+({[^{}]*}|\S+)\s+of\s+({[^{}]*}|\S+)/gi, (_match, degree, radicand) => {
|
|
4145
|
-
return `\\sqrt[${convertEquation(unwrapGroup(degree), depth + 1)}]{${convertEquation(unwrapGroup(radicand), depth + 1)}}`;
|
|
4146
|
-
});
|
|
4147
|
-
}
|
|
4148
|
-
function convertSqrt(input, depth) {
|
|
4149
|
-
return input.replace(/(?<!\\)\bsqrt\s*({[^{}]*}|\S+)/gi, (_match, radicand) => {
|
|
4150
|
-
return `\\sqrt{${convertEquation(unwrapGroup(radicand), depth + 1)}}`;
|
|
4151
|
-
});
|
|
4152
|
-
}
|
|
4153
|
-
function convertOver(input, depth) {
|
|
4154
|
-
let result = input;
|
|
4155
|
-
for (let guard = 0; guard < 50; guard++) {
|
|
4156
|
-
const over = findTopLevelWord(result, "over");
|
|
4157
|
-
if (over < 0) break;
|
|
4158
|
-
const left = readLeftAtom(result, over);
|
|
4159
|
-
const right = readRightAtom(result, over + "over".length);
|
|
4160
|
-
if (!left || !right) break;
|
|
4161
|
-
const numerator = convertEquation(unwrapGroup(left.atom), depth + 1);
|
|
4162
|
-
const denominator = convertEquation(unwrapGroup(right.atom), depth + 1);
|
|
4163
|
-
result = result.slice(0, left.start) + `\\frac{${numerator}}{${denominator}}` + result.slice(right.end);
|
|
4164
|
-
}
|
|
4165
|
-
return result;
|
|
4166
|
-
}
|
|
4167
|
-
function convertScripts(input) {
|
|
4168
|
-
return input.replace(/\s*\^\s*/g, "^").replace(/\s*_\s*/g, "_").replace(/\^(?!\{)([^\s{}_^]+)/g, "^{$1}").replace(/_(?!\{)([^\s{}_^]+)/g, "_{$1}");
|
|
4169
|
-
}
|
|
4170
|
-
function convertOperators(input) {
|
|
4171
|
-
return input.replace(/\+-/g, "\\pm").replace(/-\+/g, "\\mp").replace(/\/\//g, "\\parallel").replace(/△/g, "\\triangle ").replace(/□/g, "\\square ").replace(/‧/g, "\\cdot ").replace(/!=/g, "\\neq").replace(/<=/g, "\\leq").replace(/>=/g, "\\geq").replace(/==/g, "\\equiv");
|
|
4172
|
-
}
|
|
4173
|
-
function removeFontDirectives(input) {
|
|
4174
|
-
return input.replace(/(?<!\\)\b(?:rm|it)\b\s*/gi, "");
|
|
4175
|
-
}
|
|
4176
|
-
function convertWords(input) {
|
|
3956
|
+
var WORD_ALIASES = {
|
|
3957
|
+
// 삼각/로그 함수 — 정본 맵 부재
|
|
3958
|
+
sin: "\\sin",
|
|
3959
|
+
cos: "\\cos",
|
|
3960
|
+
tan: "\\tan",
|
|
3961
|
+
sec: "\\sec",
|
|
3962
|
+
csc: "\\csc",
|
|
3963
|
+
cot: "\\cot",
|
|
3964
|
+
log: "\\log",
|
|
3965
|
+
ln: "\\ln",
|
|
3966
|
+
// 연산자·관계 기호 (정본은 대문자 키만 보유)
|
|
3967
|
+
divide: "\\div",
|
|
3968
|
+
div: "\\div",
|
|
3969
|
+
le: "\\leq",
|
|
3970
|
+
ge: "\\geq",
|
|
3971
|
+
geq: "\\geq",
|
|
3972
|
+
deg: "^\\circ",
|
|
3973
|
+
rarrow: "\\rightarrow",
|
|
3974
|
+
lrarrow: "\\leftrightarrow",
|
|
3975
|
+
rightarrow: "\\rightarrow",
|
|
3976
|
+
leftarrow: "\\leftarrow",
|
|
3977
|
+
in: "\\in",
|
|
3978
|
+
notin: "\\notin",
|
|
3979
|
+
emptyset: "\\emptyset",
|
|
3980
|
+
subset: "\\subset",
|
|
3981
|
+
nsubset: "\\nsubseteq",
|
|
3982
|
+
cup: "\\cup",
|
|
3983
|
+
cap: "\\cap",
|
|
3984
|
+
smallinter: "\\cap",
|
|
3985
|
+
smallsum: "\\sum",
|
|
3986
|
+
sim: "\\sim",
|
|
3987
|
+
circ: "\\circ",
|
|
3988
|
+
bot: "\\perp",
|
|
3989
|
+
partial: "\\partial",
|
|
3990
|
+
nabla: "\\nabla",
|
|
3991
|
+
angle: "\\angle",
|
|
3992
|
+
triangle: "\\triangle",
|
|
3993
|
+
inf: "\\infty",
|
|
3994
|
+
left: "\\left",
|
|
3995
|
+
right: "\\right",
|
|
3996
|
+
// 글꼴 지시자 — 제거
|
|
3997
|
+
rm: "",
|
|
3998
|
+
it: ""
|
|
3999
|
+
};
|
|
4000
|
+
var STRUCTURAL_FOLD = /* @__PURE__ */ new Set(["over", "root", "of"]);
|
|
4001
|
+
function normalizeWords(input) {
|
|
4177
4002
|
return input.replace(/(?<![\\A-Za-z0-9])([A-Za-z][A-Za-z0-9]*)(?![A-Za-z0-9])/g, (word) => {
|
|
4178
|
-
|
|
4179
|
-
if (exact) return exact;
|
|
4003
|
+
if (word in CONVERT_MAP || word in MIDDLE_CONVERT_MAP) return word;
|
|
4180
4004
|
const lower = word.toLowerCase();
|
|
4181
|
-
|
|
4005
|
+
if (STRUCTURAL_FOLD.has(lower)) return lower;
|
|
4006
|
+
if (lower in WORD_ALIASES) return WORD_ALIASES[lower];
|
|
4007
|
+
if (lower !== word && (lower in CONVERT_MAP || lower in MIDDLE_CONVERT_MAP)) return lower;
|
|
4008
|
+
return word;
|
|
4182
4009
|
});
|
|
4183
4010
|
}
|
|
4184
|
-
function
|
|
4185
|
-
return input.replace(
|
|
4186
|
-
}
|
|
4187
|
-
function findTopLevelWord(input, word) {
|
|
4188
|
-
let curly = 0;
|
|
4189
|
-
let paren = 0;
|
|
4190
|
-
for (let i = 0; i <= input.length - word.length; i++) {
|
|
4191
|
-
const ch = input[i];
|
|
4192
|
-
if (ch === "{") curly++;
|
|
4193
|
-
else if (ch === "}") curly = Math.max(0, curly - 1);
|
|
4194
|
-
else if (ch === "(") paren++;
|
|
4195
|
-
else if (ch === ")") paren = Math.max(0, paren - 1);
|
|
4196
|
-
if (curly !== 0 || paren !== 0) continue;
|
|
4197
|
-
if (input.slice(i, i + word.length).toLowerCase() !== word) continue;
|
|
4198
|
-
if (isWordChar(input[i - 1]) || isWordChar(input[i + word.length])) continue;
|
|
4199
|
-
return i;
|
|
4200
|
-
}
|
|
4201
|
-
return -1;
|
|
4202
|
-
}
|
|
4203
|
-
function readLeftAtom(input, end) {
|
|
4204
|
-
let pos = end - 1;
|
|
4205
|
-
while (pos >= 0 && /\s/.test(input[pos])) pos--;
|
|
4206
|
-
if (pos < 0) return null;
|
|
4207
|
-
if (input[pos] === "}") {
|
|
4208
|
-
const start2 = findMatchingLeft(input, pos, "{", "}");
|
|
4209
|
-
if (start2 >= 0) return { start: start2, atom: input.slice(start2, pos + 1) };
|
|
4210
|
-
}
|
|
4211
|
-
if (input[pos] === ")") {
|
|
4212
|
-
const start2 = findMatchingLeft(input, pos, "(", ")");
|
|
4213
|
-
if (start2 >= 0) return { start: start2, atom: input.slice(start2, pos + 1) };
|
|
4214
|
-
}
|
|
4215
|
-
let start = pos;
|
|
4216
|
-
while (start >= 0 && !/\s/.test(input[start]) && !/[+\-=<>]/.test(input[start])) start--;
|
|
4217
|
-
return { start: start + 1, atom: input.slice(start + 1, pos + 1) };
|
|
4218
|
-
}
|
|
4219
|
-
function readRightAtom(input, start) {
|
|
4220
|
-
let pos = start;
|
|
4221
|
-
while (pos < input.length && /\s/.test(input[pos])) pos++;
|
|
4222
|
-
if (pos >= input.length) return null;
|
|
4223
|
-
if (input[pos] === "{") {
|
|
4224
|
-
const end2 = findMatchingRight(input, pos, "{", "}");
|
|
4225
|
-
if (end2 >= 0) return { end: end2 + 1, atom: input.slice(pos, end2 + 1) };
|
|
4226
|
-
}
|
|
4227
|
-
if (input[pos] === "(") {
|
|
4228
|
-
const end2 = findMatchingRight(input, pos, "(", ")");
|
|
4229
|
-
if (end2 >= 0) return { end: end2 + 1, atom: input.slice(pos, end2 + 1) };
|
|
4230
|
-
}
|
|
4231
|
-
let end = pos;
|
|
4232
|
-
while (end < input.length && !/\s/.test(input[end]) && !/[+\-=<>]/.test(input[end])) end++;
|
|
4233
|
-
return { end, atom: input.slice(pos, end) };
|
|
4234
|
-
}
|
|
4235
|
-
function findMatchingLeft(input, closeIndex, open, close) {
|
|
4236
|
-
let depth = 0;
|
|
4237
|
-
for (let i = closeIndex; i >= 0; i--) {
|
|
4238
|
-
if (input[i] === close) depth++;
|
|
4239
|
-
else if (input[i] === open) {
|
|
4240
|
-
depth--;
|
|
4241
|
-
if (depth === 0) return i;
|
|
4242
|
-
}
|
|
4243
|
-
}
|
|
4244
|
-
return -1;
|
|
4245
|
-
}
|
|
4246
|
-
function findMatchingRight(input, openIndex, open, close) {
|
|
4247
|
-
let depth = 0;
|
|
4248
|
-
for (let i = openIndex; i < input.length; i++) {
|
|
4249
|
-
if (input[i] === open) depth++;
|
|
4250
|
-
else if (input[i] === close) {
|
|
4251
|
-
depth--;
|
|
4252
|
-
if (depth === 0) return i;
|
|
4253
|
-
}
|
|
4254
|
-
}
|
|
4255
|
-
return -1;
|
|
4011
|
+
function normalizeScripts(input) {
|
|
4012
|
+
return input.replace(/\s*\^\s*/g, "^").replace(/\s*_\s*/g, "_").replace(/\^(?!\{)([^\s{}_^]+)/g, "^{$1}").replace(/_(?!\{)([^\s{}_^]+)/g, "_{$1}").replace(/([_^])/g, " $1 ");
|
|
4256
4013
|
}
|
|
4257
|
-
function
|
|
4258
|
-
|
|
4259
|
-
if (trimmed.startsWith("{") && trimmed.endsWith("}")) return trimmed.slice(1, -1);
|
|
4260
|
-
return trimmed;
|
|
4014
|
+
function normalizeOperators(input) {
|
|
4015
|
+
return input.replace(/\+-/g, " \\pm ").replace(/-\+/g, " \\mp ").replace(/\/\//g, " \\parallel ").replace(/!=/g, " \\neq ").replace(/<=/g, " \\leq ").replace(/>=/g, " \\geq ").replace(/==/g, " \\equiv ").replace(/△/g, " \\triangle ").replace(/□/g, " \\square ").replace(/‧/g, " \\cdot ");
|
|
4261
4016
|
}
|
|
4262
|
-
function
|
|
4263
|
-
|
|
4017
|
+
function hwpEquationToLatex(equation) {
|
|
4018
|
+
let s = equation.replace(/\0/g, "").replace(/\s+/g, " ").trim();
|
|
4019
|
+
if (!s) return "";
|
|
4020
|
+
s = normalizeScripts(s);
|
|
4021
|
+
s = normalizeOperators(s);
|
|
4022
|
+
s = normalizeWords(s);
|
|
4023
|
+
s = s.replace(/\broot\s+(?!\{)(\S+)\s+of(?![A-Za-z0-9])/gi, "root { $1 } of");
|
|
4024
|
+
return hmlToLatex(s).replace(/\s+/g, " ").trim();
|
|
4264
4025
|
}
|
|
4265
4026
|
|
|
4266
4027
|
// src/hwp5/parser.ts
|
|
@@ -17255,9 +17016,7 @@ function parseHwp3Document(buffer, _options) {
|
|
|
17255
17016
|
const headReader = new Reader(Buffer.from(buffer));
|
|
17256
17017
|
const header = readHeader(headReader);
|
|
17257
17018
|
if (header.encrypted !== 0) {
|
|
17258
|
-
|
|
17259
|
-
e.code = "ENCRYPTED";
|
|
17260
|
-
throw e;
|
|
17019
|
+
throw new KordocError("\uC554\uD638\uD654\uB41C HWP3 \uBB38\uC11C \u2014 \uBCF8\uBB38\uC774 \uC554\uD638\uB85C \uBCF4\uD638\uB418\uC5B4 \uC788\uC5B4 \uCD94\uCD9C\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.");
|
|
17261
17020
|
}
|
|
17262
17021
|
headReader.skip(header.infoBlockLength);
|
|
17263
17022
|
const tail = headReader.readToEnd();
|
|
@@ -17268,10 +17027,10 @@ function parseHwp3Document(buffer, _options) {
|
|
|
17268
17027
|
body = inflateRawSync3(tail, { maxOutputLength: MAX_DECOMPRESS_SIZE3 });
|
|
17269
17028
|
} catch (err) {
|
|
17270
17029
|
if (err?.code === "ERR_BUFFER_TOO_LARGE") {
|
|
17271
|
-
throw new
|
|
17030
|
+
throw new KordocError(`HWP3 \uC555\uCD95 \uD574\uC81C \uACB0\uACFC\uAC00 \uCD5C\uB300 \uD5C8\uC6A9 \uD06C\uAE30(${MAX_DECOMPRESS_SIZE3 / 1024 / 1024}MB)\uB97C \uCD08\uACFC\uD588\uC2B5\uB2C8\uB2E4`);
|
|
17272
17031
|
}
|
|
17273
17032
|
const msg2 = err instanceof Error ? err.message : String(err);
|
|
17274
|
-
throw new
|
|
17033
|
+
throw new KordocError(`HWP3 \uC555\uCD95 \uD574\uC81C \uC2E4\uD328: ${msg2}`);
|
|
17275
17034
|
}
|
|
17276
17035
|
} else {
|
|
17277
17036
|
body = tail;
|
|
@@ -17472,7 +17231,7 @@ function isDistributionSentinel(markdown) {
|
|
|
17472
17231
|
}
|
|
17473
17232
|
|
|
17474
17233
|
// src/xlsx/parser.ts
|
|
17475
|
-
import
|
|
17234
|
+
import JSZip4 from "jszip";
|
|
17476
17235
|
import { DOMParser } from "@xmldom/xmldom";
|
|
17477
17236
|
var MAX_SHEETS = 100;
|
|
17478
17237
|
var MAX_DECOMPRESS_SIZE4 = 100 * 1024 * 1024;
|
|
@@ -17757,7 +17516,7 @@ function sheetToBlocks(sheetName, grid, merges, maxRow, maxCol, sheetIndex, keep
|
|
|
17757
17516
|
}
|
|
17758
17517
|
async function parseXlsxDocument(buffer, options) {
|
|
17759
17518
|
precheckZipSize(buffer, MAX_DECOMPRESS_SIZE4);
|
|
17760
|
-
const zip = await
|
|
17519
|
+
const zip = await JSZip4.loadAsync(buffer);
|
|
17761
17520
|
const warnings = [];
|
|
17762
17521
|
const workbookFile = zip.file("xl/workbook.xml");
|
|
17763
17522
|
if (!workbookFile) {
|
|
@@ -17778,6 +17537,10 @@ async function parseXlsxDocument(buffer, options) {
|
|
|
17778
17537
|
try {
|
|
17779
17538
|
dateXfs = parseStyleDateXfs(await stylesFile.async("text"));
|
|
17780
17539
|
} catch {
|
|
17540
|
+
warnings.push({
|
|
17541
|
+
code: "PARTIAL_PARSE",
|
|
17542
|
+
message: "xl/styles.xml \uB0A0\uC9DC \uC11C\uC2DD \uD310\uC815 \uC2E4\uD328 \u2014 \uB0A0\uC9DC \uC140\uC774 \uC2DC\uB9AC\uC5BC \uC22B\uC790\uB85C \uCD9C\uB825\uB420 \uC218 \uC788\uC2B5\uB2C8\uB2E4"
|
|
17543
|
+
});
|
|
17781
17544
|
}
|
|
17782
17545
|
}
|
|
17783
17546
|
let relsMap = /* @__PURE__ */ new Map();
|
|
@@ -18474,17 +18237,7 @@ async function parseXlsDocument(buffer, options) {
|
|
|
18474
18237
|
const globals = processGlobals(records);
|
|
18475
18238
|
const warnings = [];
|
|
18476
18239
|
if (globals.encrypted) {
|
|
18477
|
-
|
|
18478
|
-
markdown: "",
|
|
18479
|
-
blocks: [],
|
|
18480
|
-
metadata: { pageCount: globals.sheets.length },
|
|
18481
|
-
warnings: [
|
|
18482
|
-
{
|
|
18483
|
-
message: "XLS \uD30C\uC77C\uC774 \uC554\uD638\uD654\uB418\uC5B4 \uC788\uC5B4 \uD30C\uC2F1\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4",
|
|
18484
|
-
code: "PARTIAL_PARSE"
|
|
18485
|
-
}
|
|
18486
|
-
]
|
|
18487
|
-
};
|
|
18240
|
+
throw new KordocError("XLS \uD30C\uC77C\uC774 \uC554\uD638\uD654\uB418\uC5B4 \uC788\uC5B4 \uD30C\uC2F1\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4");
|
|
18488
18241
|
}
|
|
18489
18242
|
const convertNum = globals.dateXfs.size > 0 ? (n, ixfe) => {
|
|
18490
18243
|
const kind = globals.dateXfs.get(ixfe);
|
|
@@ -18543,41 +18296,13 @@ async function parseXlsDocument(buffer, options) {
|
|
|
18543
18296
|
}
|
|
18544
18297
|
|
|
18545
18298
|
// src/docx/parser.ts
|
|
18546
|
-
import
|
|
18299
|
+
import JSZip5 from "jszip";
|
|
18547
18300
|
import { DOMParser as DOMParser2 } from "@xmldom/xmldom";
|
|
18548
18301
|
|
|
18549
18302
|
// src/docx/equation.ts
|
|
18550
|
-
function lname(el) {
|
|
18551
|
-
return el.localName || el.tagName?.replace(/^[^:]+:/, "") || "";
|
|
18552
|
-
}
|
|
18553
|
-
function kids(parent, name) {
|
|
18554
|
-
const out = [];
|
|
18555
|
-
const nodes = parent.childNodes;
|
|
18556
|
-
if (!nodes) return out;
|
|
18557
|
-
for (let i = 0; i < nodes.length; i++) {
|
|
18558
|
-
const n = nodes[i];
|
|
18559
|
-
if (n.nodeType !== 1) continue;
|
|
18560
|
-
const el = n;
|
|
18561
|
-
if (lname(el) === name) out.push(el);
|
|
18562
|
-
}
|
|
18563
|
-
return out;
|
|
18564
|
-
}
|
|
18565
|
-
function firstKid(parent, name) {
|
|
18566
|
-
const list = kids(parent, name);
|
|
18567
|
-
return list[0] ?? null;
|
|
18568
|
-
}
|
|
18569
|
-
function eachChild(parent) {
|
|
18570
|
-
const out = [];
|
|
18571
|
-
const nodes = parent.childNodes;
|
|
18572
|
-
if (!nodes) return out;
|
|
18573
|
-
for (let i = 0; i < nodes.length; i++) {
|
|
18574
|
-
if (nodes[i].nodeType === 1) out.push(nodes[i]);
|
|
18575
|
-
}
|
|
18576
|
-
return out;
|
|
18577
|
-
}
|
|
18578
18303
|
function runToLatex(r) {
|
|
18579
18304
|
let out = "";
|
|
18580
|
-
for (const t of
|
|
18305
|
+
for (const t of childrenByLocalName(r, "t")) out += t.textContent ?? "";
|
|
18581
18306
|
return out;
|
|
18582
18307
|
}
|
|
18583
18308
|
var FUNC_NAMES = /* @__PURE__ */ new Set([
|
|
@@ -18698,13 +18423,13 @@ function grp(body) {
|
|
|
18698
18423
|
}
|
|
18699
18424
|
function childrenToLatex(parent) {
|
|
18700
18425
|
let out = "";
|
|
18701
|
-
for (const ch of
|
|
18426
|
+
for (const ch of elementChildren(parent)) {
|
|
18702
18427
|
out += nodeToLatex(ch);
|
|
18703
18428
|
}
|
|
18704
18429
|
return out;
|
|
18705
18430
|
}
|
|
18706
18431
|
function nodeToLatex(el) {
|
|
18707
|
-
const tag =
|
|
18432
|
+
const tag = localName(el);
|
|
18708
18433
|
switch (tag) {
|
|
18709
18434
|
case "r":
|
|
18710
18435
|
return runToLatex(el);
|
|
@@ -18720,33 +18445,33 @@ function nodeToLatex(el) {
|
|
|
18720
18445
|
return childrenToLatex(el);
|
|
18721
18446
|
// 분수
|
|
18722
18447
|
case "f": {
|
|
18723
|
-
const n =
|
|
18724
|
-
const d =
|
|
18448
|
+
const n = findChildByLocalName(el, "num");
|
|
18449
|
+
const d = findChildByLocalName(el, "den");
|
|
18725
18450
|
const num2 = n ? childrenToLatex(n) : "";
|
|
18726
18451
|
const den = d ? childrenToLatex(d) : "";
|
|
18727
18452
|
return "\\frac" + grp(num2) + grp(den);
|
|
18728
18453
|
}
|
|
18729
18454
|
// 첨자
|
|
18730
18455
|
case "sSup": {
|
|
18731
|
-
const e =
|
|
18732
|
-
const sup =
|
|
18456
|
+
const e = findChildByLocalName(el, "e");
|
|
18457
|
+
const sup = findChildByLocalName(el, "sup");
|
|
18733
18458
|
return grp(e ? childrenToLatex(e) : "") + "^" + grp(sup ? childrenToLatex(sup) : "");
|
|
18734
18459
|
}
|
|
18735
18460
|
case "sSub": {
|
|
18736
|
-
const e =
|
|
18737
|
-
const sub =
|
|
18461
|
+
const e = findChildByLocalName(el, "e");
|
|
18462
|
+
const sub = findChildByLocalName(el, "sub");
|
|
18738
18463
|
return grp(e ? childrenToLatex(e) : "") + "_" + grp(sub ? childrenToLatex(sub) : "");
|
|
18739
18464
|
}
|
|
18740
18465
|
case "sSubSup": {
|
|
18741
|
-
const e =
|
|
18742
|
-
const sub =
|
|
18743
|
-
const sup =
|
|
18466
|
+
const e = findChildByLocalName(el, "e");
|
|
18467
|
+
const sub = findChildByLocalName(el, "sub");
|
|
18468
|
+
const sup = findChildByLocalName(el, "sup");
|
|
18744
18469
|
return grp(e ? childrenToLatex(e) : "") + "_" + grp(sub ? childrenToLatex(sub) : "") + "^" + grp(sup ? childrenToLatex(sup) : "");
|
|
18745
18470
|
}
|
|
18746
18471
|
case "sPre": {
|
|
18747
|
-
const sub =
|
|
18748
|
-
const sup =
|
|
18749
|
-
const e =
|
|
18472
|
+
const sub = findChildByLocalName(el, "sub");
|
|
18473
|
+
const sup = findChildByLocalName(el, "sup");
|
|
18474
|
+
const e = findChildByLocalName(el, "e");
|
|
18750
18475
|
const preSub = sub ? grp(childrenToLatex(sub)) : "{}";
|
|
18751
18476
|
const preSup = sup ? grp(childrenToLatex(sup)) : "{}";
|
|
18752
18477
|
const body = e ? childrenToLatex(e) : "";
|
|
@@ -18754,13 +18479,13 @@ function nodeToLatex(el) {
|
|
|
18754
18479
|
}
|
|
18755
18480
|
// 근호
|
|
18756
18481
|
case "rad": {
|
|
18757
|
-
const deg =
|
|
18758
|
-
const e =
|
|
18482
|
+
const deg = findChildByLocalName(el, "deg");
|
|
18483
|
+
const e = findChildByLocalName(el, "e");
|
|
18759
18484
|
const body = e ? childrenToLatex(e) : "";
|
|
18760
|
-
const radPr =
|
|
18485
|
+
const radPr = findChildByLocalName(el, "radPr");
|
|
18761
18486
|
let hide = false;
|
|
18762
18487
|
if (radPr) {
|
|
18763
|
-
const degHide =
|
|
18488
|
+
const degHide = findChildByLocalName(radPr, "degHide");
|
|
18764
18489
|
if (degHide) {
|
|
18765
18490
|
const val = degHide.getAttribute("m:val") ?? degHide.getAttribute("val");
|
|
18766
18491
|
hide = val === "1" || val === "on" || val === "true";
|
|
@@ -18771,13 +18496,13 @@ function nodeToLatex(el) {
|
|
|
18771
18496
|
}
|
|
18772
18497
|
// n-ary 연산자 (sum, prod, int, …)
|
|
18773
18498
|
case "nary": {
|
|
18774
|
-
const naryPr =
|
|
18499
|
+
const naryPr = findChildByLocalName(el, "naryPr");
|
|
18775
18500
|
let op = "\\int";
|
|
18776
18501
|
let subHide = false;
|
|
18777
18502
|
let supHide = false;
|
|
18778
18503
|
let limLoc = "";
|
|
18779
18504
|
if (naryPr) {
|
|
18780
|
-
const chr =
|
|
18505
|
+
const chr = findChildByLocalName(naryPr, "chr");
|
|
18781
18506
|
if (chr) {
|
|
18782
18507
|
const v = chr.getAttribute("m:val") ?? chr.getAttribute("val") ?? "";
|
|
18783
18508
|
if (v && NARY_MAP[v]) op = NARY_MAP[v];
|
|
@@ -18785,16 +18510,16 @@ function nodeToLatex(el) {
|
|
|
18785
18510
|
} else {
|
|
18786
18511
|
op = "\\int";
|
|
18787
18512
|
}
|
|
18788
|
-
const sh =
|
|
18789
|
-
const ph =
|
|
18513
|
+
const sh = findChildByLocalName(naryPr, "subHide");
|
|
18514
|
+
const ph = findChildByLocalName(naryPr, "supHide");
|
|
18790
18515
|
if (sh) subHide = onOffVal(sh.getAttribute("m:val") ?? sh.getAttribute("val"));
|
|
18791
18516
|
if (ph) supHide = onOffVal(ph.getAttribute("m:val") ?? ph.getAttribute("val"));
|
|
18792
|
-
const ll =
|
|
18517
|
+
const ll = findChildByLocalName(naryPr, "limLoc");
|
|
18793
18518
|
if (ll) limLoc = ll.getAttribute("m:val") ?? ll.getAttribute("val") ?? "";
|
|
18794
18519
|
}
|
|
18795
|
-
const sub =
|
|
18796
|
-
const sup =
|
|
18797
|
-
const e =
|
|
18520
|
+
const sub = findChildByLocalName(el, "sub");
|
|
18521
|
+
const sup = findChildByLocalName(el, "sup");
|
|
18522
|
+
const e = findChildByLocalName(el, "e");
|
|
18798
18523
|
const subStr = !subHide && sub ? childrenToLatex(sub) : "";
|
|
18799
18524
|
const supStr = !supHide && sup ? childrenToLatex(sup) : "";
|
|
18800
18525
|
const body = e ? childrenToLatex(e) : "";
|
|
@@ -18810,27 +18535,27 @@ function nodeToLatex(el) {
|
|
|
18810
18535
|
}
|
|
18811
18536
|
// 괄호 (delimiter)
|
|
18812
18537
|
case "d": {
|
|
18813
|
-
const dPr =
|
|
18538
|
+
const dPr = findChildByLocalName(el, "dPr");
|
|
18814
18539
|
let beg = "(";
|
|
18815
18540
|
let end = ")";
|
|
18816
18541
|
let sep = ",";
|
|
18817
18542
|
if (dPr) {
|
|
18818
|
-
const begChr =
|
|
18819
|
-
const endChr =
|
|
18820
|
-
const sepChr =
|
|
18543
|
+
const begChr = findChildByLocalName(dPr, "begChr");
|
|
18544
|
+
const endChr = findChildByLocalName(dPr, "endChr");
|
|
18545
|
+
const sepChr = findChildByLocalName(dPr, "sepChr");
|
|
18821
18546
|
if (begChr) beg = begChr.getAttribute("m:val") ?? begChr.getAttribute("val") ?? beg;
|
|
18822
18547
|
if (endChr) end = endChr.getAttribute("m:val") ?? endChr.getAttribute("val") ?? end;
|
|
18823
18548
|
if (sepChr) sep = sepChr.getAttribute("m:val") ?? sepChr.getAttribute("val") ?? sep;
|
|
18824
18549
|
}
|
|
18825
|
-
const items =
|
|
18550
|
+
const items = childrenByLocalName(el, "e").map(childrenToLatex);
|
|
18826
18551
|
const body = items.join(sep);
|
|
18827
18552
|
return "\\left" + mapDelim(beg, true) + body + "\\right" + mapDelim(end, false);
|
|
18828
18553
|
}
|
|
18829
18554
|
// 행렬
|
|
18830
18555
|
case "m": {
|
|
18831
18556
|
const rows = [];
|
|
18832
|
-
for (const mr of
|
|
18833
|
-
const cells =
|
|
18557
|
+
for (const mr of childrenByLocalName(el, "mr")) {
|
|
18558
|
+
const cells = childrenByLocalName(mr, "e").map(childrenToLatex);
|
|
18834
18559
|
rows.push(cells.join(" & "));
|
|
18835
18560
|
}
|
|
18836
18561
|
return "\\begin{matrix}" + rows.join(" \\\\ ") + "\\end{matrix}";
|
|
@@ -18840,8 +18565,8 @@ function nodeToLatex(el) {
|
|
|
18840
18565
|
return childrenToLatex(el);
|
|
18841
18566
|
// 함수 적용 (sin, cos, log …)
|
|
18842
18567
|
case "func": {
|
|
18843
|
-
const fn =
|
|
18844
|
-
const e =
|
|
18568
|
+
const fn = findChildByLocalName(el, "fName");
|
|
18569
|
+
const e = findChildByLocalName(el, "e");
|
|
18845
18570
|
const fnStr = fn ? childrenToLatex(fn).trim() : "";
|
|
18846
18571
|
const body = e ? childrenToLatex(e) : "";
|
|
18847
18572
|
const fnLatex = FUNC_NAMES.has(fnStr) ? "\\" + fnStr : fnStr;
|
|
@@ -18849,42 +18574,42 @@ function nodeToLatex(el) {
|
|
|
18849
18574
|
}
|
|
18850
18575
|
// 악센트 (hat/bar/vec/…)
|
|
18851
18576
|
case "acc": {
|
|
18852
|
-
const accPr =
|
|
18577
|
+
const accPr = findChildByLocalName(el, "accPr");
|
|
18853
18578
|
let chr = "";
|
|
18854
18579
|
if (accPr) {
|
|
18855
|
-
const chrEl =
|
|
18580
|
+
const chrEl = findChildByLocalName(accPr, "chr");
|
|
18856
18581
|
if (chrEl) chr = chrEl.getAttribute("m:val") ?? chrEl.getAttribute("val") ?? "";
|
|
18857
18582
|
}
|
|
18858
18583
|
if (!chr) chr = "\u0302";
|
|
18859
|
-
const e =
|
|
18584
|
+
const e = findChildByLocalName(el, "e");
|
|
18860
18585
|
const body = e ? childrenToLatex(e) : "";
|
|
18861
18586
|
const cmd = ACCENT_MAP[chr] ?? "\\hat";
|
|
18862
18587
|
return cmd + grp(body);
|
|
18863
18588
|
}
|
|
18864
18589
|
// bar (위/아래 줄)
|
|
18865
18590
|
case "bar": {
|
|
18866
|
-
const barPr =
|
|
18591
|
+
const barPr = findChildByLocalName(el, "barPr");
|
|
18867
18592
|
let pos = "top";
|
|
18868
18593
|
if (barPr) {
|
|
18869
|
-
const posEl =
|
|
18594
|
+
const posEl = findChildByLocalName(barPr, "pos");
|
|
18870
18595
|
if (posEl) pos = posEl.getAttribute("m:val") ?? posEl.getAttribute("val") ?? pos;
|
|
18871
18596
|
}
|
|
18872
|
-
const e =
|
|
18597
|
+
const e = findChildByLocalName(el, "e");
|
|
18873
18598
|
const body = e ? childrenToLatex(e) : "";
|
|
18874
18599
|
return (pos === "bot" ? "\\underline" : "\\overline") + grp(body);
|
|
18875
18600
|
}
|
|
18876
18601
|
// lim 위/아래
|
|
18877
18602
|
case "limLow": {
|
|
18878
|
-
const e =
|
|
18879
|
-
const lim =
|
|
18603
|
+
const e = findChildByLocalName(el, "e");
|
|
18604
|
+
const lim = findChildByLocalName(el, "lim");
|
|
18880
18605
|
const base = e ? childrenToLatex(e).trim() : "";
|
|
18881
18606
|
const below = lim ? childrenToLatex(lim) : "";
|
|
18882
18607
|
if (FUNC_NAMES.has(base)) return "\\" + base + "_" + grp(below);
|
|
18883
18608
|
return base + "_" + grp(below);
|
|
18884
18609
|
}
|
|
18885
18610
|
case "limUpp": {
|
|
18886
|
-
const e =
|
|
18887
|
-
const lim =
|
|
18611
|
+
const e = findChildByLocalName(el, "e");
|
|
18612
|
+
const lim = findChildByLocalName(el, "lim");
|
|
18888
18613
|
const base = e ? childrenToLatex(e).trim() : "";
|
|
18889
18614
|
const above = lim ? childrenToLatex(lim) : "";
|
|
18890
18615
|
if (FUNC_NAMES.has(base)) return "\\" + base + "^" + grp(above);
|
|
@@ -18892,7 +18617,7 @@ function nodeToLatex(el) {
|
|
|
18892
18617
|
}
|
|
18893
18618
|
// group character (over/underset 비슷)
|
|
18894
18619
|
case "groupChr":
|
|
18895
|
-
return childrenToLatex(
|
|
18620
|
+
return childrenToLatex(findChildByLocalName(el, "e") ?? el);
|
|
18896
18621
|
// box/borderBox/phantom/eqArr/… 는 자식 본문만 유지
|
|
18897
18622
|
case "borderBox":
|
|
18898
18623
|
case "phant":
|
|
@@ -18922,7 +18647,7 @@ function nodeToLatex(el) {
|
|
|
18922
18647
|
}
|
|
18923
18648
|
}
|
|
18924
18649
|
function isOmmlRoot(el) {
|
|
18925
|
-
const t =
|
|
18650
|
+
const t = localName(el);
|
|
18926
18651
|
return t === "oMath" || t === "oMathPara";
|
|
18927
18652
|
}
|
|
18928
18653
|
function ommlElementToLatex(el) {
|
|
@@ -18931,7 +18656,7 @@ function ommlElementToLatex(el) {
|
|
|
18931
18656
|
return raw.replace(/\s+/g, " ").trim();
|
|
18932
18657
|
}
|
|
18933
18658
|
function isDisplayMath(el) {
|
|
18934
|
-
return
|
|
18659
|
+
return localName(el) === "oMathPara";
|
|
18935
18660
|
}
|
|
18936
18661
|
|
|
18937
18662
|
// src/docx/parser.ts
|
|
@@ -19446,7 +19171,7 @@ function emitParagraphImages(p, imageMap, linked, out) {
|
|
|
19446
19171
|
}
|
|
19447
19172
|
async function parseDocxDocument(buffer, options) {
|
|
19448
19173
|
precheckZipSize(buffer, MAX_DECOMPRESS_SIZE5);
|
|
19449
|
-
const zip = await
|
|
19174
|
+
const zip = await JSZip5.loadAsync(buffer);
|
|
19450
19175
|
const warnings = [];
|
|
19451
19176
|
const docFile = zip.file("word/document.xml");
|
|
19452
19177
|
if (!docFile) {
|
|
@@ -19561,13 +19286,12 @@ async function parseDocxDocument(buffer, options) {
|
|
|
19561
19286
|
|
|
19562
19287
|
// src/hwpml/parser.ts
|
|
19563
19288
|
import { DOMParser as DOMParser3 } from "@xmldom/xmldom";
|
|
19564
|
-
var MAX_XML_DEPTH2 = 200;
|
|
19565
19289
|
var MAX_TABLE_ROWS = 5e3;
|
|
19566
19290
|
var MAX_TABLE_COLS = 500;
|
|
19567
19291
|
var MAX_HWPML_BYTES = 50 * 1024 * 1024;
|
|
19568
19292
|
function parseHwpmlDocument(buffer, options) {
|
|
19569
19293
|
if (buffer.byteLength > MAX_HWPML_BYTES) {
|
|
19570
|
-
throw new
|
|
19294
|
+
throw new KordocError(`HWPML \uD30C\uC77C \uD06C\uAE30 \uCD08\uACFC (${(buffer.byteLength / 1024 / 1024).toFixed(1)}MB > 50MB)`);
|
|
19571
19295
|
}
|
|
19572
19296
|
const text = new TextDecoder("utf-8").decode(buffer).replace(/^\uFEFF/, "");
|
|
19573
19297
|
const normalized = text.replace(/ /g, " ");
|
|
@@ -19584,17 +19308,17 @@ function parseHwpmlDocument(buffer, options) {
|
|
|
19584
19308
|
}
|
|
19585
19309
|
const root = doc.documentElement;
|
|
19586
19310
|
const metadata = {};
|
|
19587
|
-
const docSummary =
|
|
19311
|
+
const docSummary = findChildByLocalName(root, "DOCSUMMARY");
|
|
19588
19312
|
if (docSummary) {
|
|
19589
|
-
const title =
|
|
19590
|
-
const author =
|
|
19591
|
-
const date =
|
|
19592
|
-
if (title) metadata.title =
|
|
19593
|
-
if (author) metadata.author =
|
|
19594
|
-
if (date) metadata.createdAt =
|
|
19313
|
+
const title = findChildByLocalName(docSummary, "TITLE");
|
|
19314
|
+
const author = findChildByLocalName(docSummary, "AUTHOR");
|
|
19315
|
+
const date = findChildByLocalName(docSummary, "DATE");
|
|
19316
|
+
if (title) metadata.title = rawTextContent(title).trim();
|
|
19317
|
+
if (author) metadata.author = rawTextContent(author).trim();
|
|
19318
|
+
if (date) metadata.createdAt = rawTextContent(date).trim() || void 0;
|
|
19595
19319
|
}
|
|
19596
19320
|
const paraShapeMap = buildParaShapeMap(root);
|
|
19597
|
-
const body =
|
|
19321
|
+
const body = findChildByLocalName(root, "BODY");
|
|
19598
19322
|
if (!body) {
|
|
19599
19323
|
return { markdown: "", blocks: [], metadata, warnings };
|
|
19600
19324
|
}
|
|
@@ -19622,11 +19346,11 @@ function parseHwpmlDocument(buffer, options) {
|
|
|
19622
19346
|
}
|
|
19623
19347
|
function buildParaShapeMap(root) {
|
|
19624
19348
|
const map = /* @__PURE__ */ new Map();
|
|
19625
|
-
const head =
|
|
19349
|
+
const head = findChildByLocalName(root, "HEAD");
|
|
19626
19350
|
if (!head) return map;
|
|
19627
|
-
const mappingTable =
|
|
19351
|
+
const mappingTable = findChildByLocalName(head, "MAPPINGTABLE");
|
|
19628
19352
|
if (!mappingTable) return map;
|
|
19629
|
-
const paraShapeList =
|
|
19353
|
+
const paraShapeList = findChildByLocalName(mappingTable, "PARASHAPELIST");
|
|
19630
19354
|
if (!paraShapeList) return map;
|
|
19631
19355
|
const children = paraShapeList.childNodes;
|
|
19632
19356
|
for (let i = 0; i < children.length; i++) {
|
|
@@ -19648,7 +19372,7 @@ function parseSection2(section, blocks, paraShapeMap, sectionNum, warnings, keep
|
|
|
19648
19372
|
walkContent(section, blocks, paraShapeMap, sectionNum, warnings, false, keep);
|
|
19649
19373
|
}
|
|
19650
19374
|
function walkContent(node, blocks, paraShapeMap, sectionNum, warnings, inHeaderFooter, keep, depth = 0) {
|
|
19651
|
-
if (depth >
|
|
19375
|
+
if (depth > MAX_XML_DEPTH) return;
|
|
19652
19376
|
const children = node.childNodes;
|
|
19653
19377
|
for (let i = 0; i < children.length; i++) {
|
|
19654
19378
|
const el = children[i];
|
|
@@ -19678,7 +19402,7 @@ function walkContent(node, blocks, paraShapeMap, sectionNum, warnings, inHeaderF
|
|
|
19678
19402
|
}
|
|
19679
19403
|
}
|
|
19680
19404
|
function walkTablesInP(node, blocks, paraShapeMap, sectionNum, warnings, keep, depth = 0) {
|
|
19681
|
-
if (depth >
|
|
19405
|
+
if (depth > MAX_XML_DEPTH) return;
|
|
19682
19406
|
const children = node.childNodes;
|
|
19683
19407
|
for (let i = 0; i < children.length; i++) {
|
|
19684
19408
|
const el = children[i];
|
|
@@ -19709,14 +19433,14 @@ function extractParagraphText(p) {
|
|
|
19709
19433
|
return parts.join("").trim();
|
|
19710
19434
|
}
|
|
19711
19435
|
function collectCharText(node, parts, depth = 0) {
|
|
19712
|
-
if (depth >
|
|
19436
|
+
if (depth > MAX_XML_DEPTH) return;
|
|
19713
19437
|
const children = node.childNodes;
|
|
19714
19438
|
for (let i = 0; i < children.length; i++) {
|
|
19715
19439
|
const el = children[i];
|
|
19716
19440
|
if (el.nodeType !== 1) continue;
|
|
19717
19441
|
const tag = localName(el);
|
|
19718
19442
|
if (tag === "CHAR") {
|
|
19719
|
-
const t =
|
|
19443
|
+
const t = rawTextContent(el);
|
|
19720
19444
|
if (t) parts.push(t);
|
|
19721
19445
|
} else if (tag === "TABLE" || tag === "PICTURE" || tag === "SHAPEOBJECT") {
|
|
19722
19446
|
} else if (tag === "AUTONUM") {
|
|
@@ -19725,14 +19449,28 @@ function collectCharText(node, parts, depth = 0) {
|
|
|
19725
19449
|
}
|
|
19726
19450
|
}
|
|
19727
19451
|
}
|
|
19728
|
-
function parseTable2(el, blocks,
|
|
19452
|
+
function parseTable2(el, blocks, _paraShapeMap, sectionNum, warnings, keep) {
|
|
19453
|
+
const table2 = buildHmlTable(el, warnings, keep, 0);
|
|
19454
|
+
if (!table2) return;
|
|
19455
|
+
const caption = extractShapeCaption(el);
|
|
19456
|
+
if (caption.text && caption.before) {
|
|
19457
|
+
blocks.push({ type: "paragraph", text: caption.text, pageNumber: sectionNum });
|
|
19458
|
+
}
|
|
19459
|
+
blocks.push({ type: "table", table: table2, pageNumber: sectionNum });
|
|
19460
|
+
if (caption.text && !caption.before) {
|
|
19461
|
+
blocks.push({ type: "paragraph", text: caption.text, pageNumber: sectionNum });
|
|
19462
|
+
}
|
|
19463
|
+
}
|
|
19464
|
+
var MAX_TABLE_NEST = 8;
|
|
19465
|
+
function buildHmlTable(el, warnings, keep, tableDepth) {
|
|
19729
19466
|
const cells = [];
|
|
19467
|
+
const structured = [];
|
|
19730
19468
|
const rowCount = parseInt(el.getAttribute("RowCount") ?? "0", 10);
|
|
19731
19469
|
const colCount = parseInt(el.getAttribute("ColCount") ?? "0", 10);
|
|
19732
|
-
if (isNaN(rowCount) || isNaN(colCount) || rowCount === 0 || colCount === 0) return;
|
|
19470
|
+
if (isNaN(rowCount) || isNaN(colCount) || rowCount === 0 || colCount === 0) return null;
|
|
19733
19471
|
if (rowCount > MAX_TABLE_ROWS || colCount > MAX_TABLE_COLS) {
|
|
19734
19472
|
warnings.push({ message: `\uD14C\uC774\uBE14 \uD06C\uAE30 \uCD08\uACFC (${rowCount}x${colCount}) \u2014 \uC2A4\uD0B5`, code: "TRUNCATED_TABLE" });
|
|
19735
|
-
return;
|
|
19473
|
+
return null;
|
|
19736
19474
|
}
|
|
19737
19475
|
const children = el.childNodes;
|
|
19738
19476
|
for (let i = 0; i < children.length; i++) {
|
|
@@ -19747,28 +19485,106 @@ function parseTable2(el, blocks, paraShapeMap, sectionNum, warnings, keep) {
|
|
|
19747
19485
|
const colSpan = Math.min(Math.max(1, parseInt(cellEl.getAttribute("ColSpan") ?? "1", 10) || 1), MAX_TABLE_COLS);
|
|
19748
19486
|
const rowSpan = Math.min(Math.max(1, parseInt(cellEl.getAttribute("RowSpan") ?? "1", 10) || 1), MAX_TABLE_ROWS);
|
|
19749
19487
|
const cellText = extractCellText(cellEl);
|
|
19750
|
-
|
|
19488
|
+
const ctx = { text: cellText, colSpan, rowSpan, colAddr, rowAddr };
|
|
19489
|
+
cells.push(ctx);
|
|
19490
|
+
if (tableDepth < MAX_TABLE_NEST && cellHasNestedTable(cellEl, 0)) {
|
|
19491
|
+
const cellBlocks = [];
|
|
19492
|
+
collectCellBlocks(cellEl, cellBlocks, warnings, keep, 0, tableDepth);
|
|
19493
|
+
if (cellBlocks.some((b) => b.type === "table")) structured.push({ ctx, cellBlocks });
|
|
19494
|
+
}
|
|
19751
19495
|
}
|
|
19752
19496
|
}
|
|
19753
|
-
if (cells.length === 0) return;
|
|
19497
|
+
if (cells.length === 0) return null;
|
|
19754
19498
|
const cellRows = Array.from({ length: rowCount }, () => []);
|
|
19755
19499
|
for (const cell2 of cells) {
|
|
19756
19500
|
const r = Math.min(Math.max(cell2.rowAddr ?? 0, 0), rowCount - 1);
|
|
19757
19501
|
cellRows[r].push(cell2);
|
|
19758
19502
|
}
|
|
19759
19503
|
const table2 = buildTable(cellRows, { keepAnchoredEmptyCols: keep });
|
|
19760
|
-
|
|
19761
|
-
|
|
19762
|
-
|
|
19504
|
+
if (structured.length > 0) {
|
|
19505
|
+
const claimed = /* @__PURE__ */ new Set();
|
|
19506
|
+
for (const { ctx, cellBlocks } of structured) {
|
|
19507
|
+
let target = table2.cells[ctx.rowAddr ?? -1]?.[ctx.colAddr ?? -1];
|
|
19508
|
+
if (!target || claimed.has(target) || target.text !== ctx.text) {
|
|
19509
|
+
target = void 0;
|
|
19510
|
+
outer: for (const irRow of table2.cells) {
|
|
19511
|
+
for (const cand of irRow) {
|
|
19512
|
+
if (!claimed.has(cand) && cand.text === ctx.text && cand.colSpan === ctx.colSpan && cand.rowSpan === ctx.rowSpan) {
|
|
19513
|
+
target = cand;
|
|
19514
|
+
break outer;
|
|
19515
|
+
}
|
|
19516
|
+
}
|
|
19517
|
+
}
|
|
19518
|
+
}
|
|
19519
|
+
if (!target) continue;
|
|
19520
|
+
claimed.add(target);
|
|
19521
|
+
target.blocks = cellBlocks;
|
|
19522
|
+
}
|
|
19763
19523
|
}
|
|
19764
|
-
|
|
19765
|
-
|
|
19766
|
-
|
|
19524
|
+
return table2;
|
|
19525
|
+
}
|
|
19526
|
+
function cellHasNestedTable(node, depth) {
|
|
19527
|
+
if (depth > 20) return false;
|
|
19528
|
+
const children = node.childNodes;
|
|
19529
|
+
for (let i = 0; i < children.length; i++) {
|
|
19530
|
+
const el = children[i];
|
|
19531
|
+
if (el.nodeType !== 1) continue;
|
|
19532
|
+
const tag = localName(el);
|
|
19533
|
+
if (tag === "TABLE") return true;
|
|
19534
|
+
if (tag === "FOOTNOTE" || tag === "ENDNOTE" || tag === "HEADER" || tag === "FOOTER") continue;
|
|
19535
|
+
if (cellHasNestedTable(el, depth + 1)) return true;
|
|
19536
|
+
}
|
|
19537
|
+
return false;
|
|
19538
|
+
}
|
|
19539
|
+
function collectCellBlocks(node, out, warnings, keep, depth, tableDepth) {
|
|
19540
|
+
if (depth > 20) return;
|
|
19541
|
+
const children = node.childNodes;
|
|
19542
|
+
for (let i = 0; i < children.length; i++) {
|
|
19543
|
+
const el = children[i];
|
|
19544
|
+
if (el.nodeType !== 1) continue;
|
|
19545
|
+
const tag = localName(el);
|
|
19546
|
+
if (tag === "P") {
|
|
19547
|
+
const t = extractParagraphText(el);
|
|
19548
|
+
if (t) out.push({ type: "paragraph", text: t });
|
|
19549
|
+
collectNestedTableBlocks(el, out, warnings, keep, depth + 1, tableDepth);
|
|
19550
|
+
} else if (tag === "TABLE") {
|
|
19551
|
+
pushNestedTableBlock(el, out, warnings, keep, tableDepth);
|
|
19552
|
+
} else {
|
|
19553
|
+
collectCellBlocks(el, out, warnings, keep, depth + 1, tableDepth);
|
|
19554
|
+
}
|
|
19555
|
+
}
|
|
19556
|
+
}
|
|
19557
|
+
function collectNestedTableBlocks(node, out, warnings, keep, depth, tableDepth) {
|
|
19558
|
+
if (depth > 20) return;
|
|
19559
|
+
const children = node.childNodes;
|
|
19560
|
+
for (let i = 0; i < children.length; i++) {
|
|
19561
|
+
const el = children[i];
|
|
19562
|
+
if (el.nodeType !== 1) continue;
|
|
19563
|
+
const tag = localName(el);
|
|
19564
|
+
if (tag === "TABLE") {
|
|
19565
|
+
pushNestedTableBlock(el, out, warnings, keep, tableDepth);
|
|
19566
|
+
continue;
|
|
19567
|
+
}
|
|
19568
|
+
if (tag === "FOOTNOTE" || tag === "ENDNOTE" || tag === "HEADER" || tag === "FOOTER") continue;
|
|
19569
|
+
collectNestedTableBlocks(el, out, warnings, keep, depth + 1, tableDepth);
|
|
19570
|
+
}
|
|
19571
|
+
}
|
|
19572
|
+
function pushNestedTableBlock(el, out, warnings, keep, tableDepth) {
|
|
19573
|
+
const nested = buildHmlTable(el, warnings, keep, tableDepth + 1);
|
|
19574
|
+
if (nested) {
|
|
19575
|
+
const cap = extractShapeCaption(el);
|
|
19576
|
+
if (cap.text) nested.caption = cap.text;
|
|
19577
|
+
out.push({ type: "table", table: nested });
|
|
19578
|
+
return;
|
|
19767
19579
|
}
|
|
19580
|
+
const parts = [];
|
|
19581
|
+
collectCellText2(el, parts, 0);
|
|
19582
|
+
const flat = parts.filter(Boolean).join("\n").trim();
|
|
19583
|
+
if (flat) out.push({ type: "paragraph", text: flat });
|
|
19768
19584
|
}
|
|
19769
19585
|
function extractShapeCaption(tableEl) {
|
|
19770
|
-
const shape =
|
|
19771
|
-
const caption = shape &&
|
|
19586
|
+
const shape = findChildByLocalName(tableEl, "SHAPEOBJECT");
|
|
19587
|
+
const caption = shape && findChildByLocalName(shape, "CAPTION");
|
|
19772
19588
|
if (!caption) return { text: "", before: false };
|
|
19773
19589
|
const parts = [];
|
|
19774
19590
|
collectCellText2(caption, parts, 0);
|
|
@@ -19813,30 +19629,6 @@ function collectNestedTableText(node, parts, depth) {
|
|
|
19813
19629
|
collectNestedTableText(el, parts, depth + 1);
|
|
19814
19630
|
}
|
|
19815
19631
|
}
|
|
19816
|
-
function localName(el) {
|
|
19817
|
-
return (el.tagName || el.localName || "").replace(/^[^:]+:/, "");
|
|
19818
|
-
}
|
|
19819
|
-
function findChild(parent, tag) {
|
|
19820
|
-
const children = parent.childNodes;
|
|
19821
|
-
for (let i = 0; i < children.length; i++) {
|
|
19822
|
-
const el = children[i];
|
|
19823
|
-
if (el.nodeType === 1 && localName(el) === tag) return el;
|
|
19824
|
-
}
|
|
19825
|
-
return null;
|
|
19826
|
-
}
|
|
19827
|
-
function textContent(el) {
|
|
19828
|
-
const children = el.childNodes;
|
|
19829
|
-
const parts = [];
|
|
19830
|
-
for (let i = 0; i < children.length; i++) {
|
|
19831
|
-
const node = children[i];
|
|
19832
|
-
if (node.nodeType === 3) {
|
|
19833
|
-
parts.push(node.nodeValue || "");
|
|
19834
|
-
} else if (node.nodeType === 1) {
|
|
19835
|
-
parts.push(textContent(node));
|
|
19836
|
-
}
|
|
19837
|
-
}
|
|
19838
|
-
return parts.join("");
|
|
19839
|
-
}
|
|
19840
19632
|
function countSections(body) {
|
|
19841
19633
|
let count = 0;
|
|
19842
19634
|
const children = body.childNodes;
|
|
@@ -20586,10 +20378,136 @@ function fillInlineFields(text, values, filled, matchedLabels, blockedLabels) {
|
|
|
20586
20378
|
}
|
|
20587
20379
|
|
|
20588
20380
|
// src/form/filler-hwpx.ts
|
|
20589
|
-
import
|
|
20381
|
+
import JSZip7 from "jszip";
|
|
20382
|
+
|
|
20383
|
+
// src/form/click-here.ts
|
|
20384
|
+
import JSZip6 from "jszip";
|
|
20385
|
+
var FIELD_BEGIN_RE = /<([A-Za-z0-9_]+:)?fieldBegin\b([^>]*?)(\/?)>/g;
|
|
20386
|
+
function attrOf(attrs, name) {
|
|
20387
|
+
const m = attrs.match(new RegExp(`(?:^|\\s)${name}="([^"]*)"`));
|
|
20388
|
+
return m ? m[1] : void 0;
|
|
20389
|
+
}
|
|
20390
|
+
function extractPlaceholder(fieldBeginXml) {
|
|
20391
|
+
const m = fieldBeginXml.match(/Direction:wstring:(\d+):/);
|
|
20392
|
+
if (!m) return void 0;
|
|
20393
|
+
const start = (m.index ?? 0) + m[0].length;
|
|
20394
|
+
const n = parseInt(m[1], 10);
|
|
20395
|
+
if (!Number.isFinite(n) || n < 0 || n > 1e3) return void 0;
|
|
20396
|
+
return decodeXmlEntities(fieldBeginXml.slice(start, start + n));
|
|
20397
|
+
}
|
|
20398
|
+
function regionText(inner) {
|
|
20399
|
+
return decodeXmlEntities(
|
|
20400
|
+
inner.replace(/<(?:[A-Za-z0-9_]+:)?(?:lineBreak|br)\b[^>]*\/?>/g, "\n").replace(/<(?:[A-Za-z0-9_]+:)?tab\b[^>]*\/?>/g, " ").replace(/<[^>]+>/g, "")
|
|
20401
|
+
);
|
|
20402
|
+
}
|
|
20403
|
+
function scanClickHereRegions(xml) {
|
|
20404
|
+
const regions = [];
|
|
20405
|
+
FIELD_BEGIN_RE.lastIndex = 0;
|
|
20406
|
+
let m;
|
|
20407
|
+
let lastEnd = -1;
|
|
20408
|
+
while ((m = FIELD_BEGIN_RE.exec(xml)) !== null) {
|
|
20409
|
+
const prefix = m[1] ?? "";
|
|
20410
|
+
const attrs = m[2];
|
|
20411
|
+
const selfClosed = m[3] === "/";
|
|
20412
|
+
if ((attrOf(attrs, "type") ?? "").toUpperCase() !== "CLICK_HERE") continue;
|
|
20413
|
+
const name = attrOf(attrs, "name");
|
|
20414
|
+
if (name === void 0) continue;
|
|
20415
|
+
const id = attrOf(attrs, "id");
|
|
20416
|
+
let beginElemEnd;
|
|
20417
|
+
if (selfClosed) {
|
|
20418
|
+
beginElemEnd = FIELD_BEGIN_RE.lastIndex;
|
|
20419
|
+
} else {
|
|
20420
|
+
const closeTag = `</${prefix}fieldBegin>`;
|
|
20421
|
+
const ci = xml.indexOf(closeTag, FIELD_BEGIN_RE.lastIndex);
|
|
20422
|
+
if (ci < 0) continue;
|
|
20423
|
+
beginElemEnd = ci + closeTag.length;
|
|
20424
|
+
}
|
|
20425
|
+
const fieldBeginXml = xml.slice(m.index, beginElemEnd);
|
|
20426
|
+
const ctrlClose = `</${prefix}ctrl>`;
|
|
20427
|
+
let start = beginElemEnd;
|
|
20428
|
+
let wrapped = false;
|
|
20429
|
+
if (xml.startsWith(ctrlClose, beginElemEnd)) {
|
|
20430
|
+
start = beginElemEnd + ctrlClose.length;
|
|
20431
|
+
wrapped = true;
|
|
20432
|
+
}
|
|
20433
|
+
const feRe = new RegExp(`<${prefix}fieldEnd\\b[^>]*>`, "g");
|
|
20434
|
+
feRe.lastIndex = start;
|
|
20435
|
+
let end = -1;
|
|
20436
|
+
let fe;
|
|
20437
|
+
while ((fe = feRe.exec(xml)) !== null) {
|
|
20438
|
+
const ref = attrOf(fe[0], "beginIDRef");
|
|
20439
|
+
if (id !== void 0 && ref !== void 0 && ref !== id) continue;
|
|
20440
|
+
end = fe.index;
|
|
20441
|
+
break;
|
|
20442
|
+
}
|
|
20443
|
+
if (end < 0) continue;
|
|
20444
|
+
const ctrlOpen = `<${prefix}ctrl>`;
|
|
20445
|
+
if (wrapped && xml.slice(end - ctrlOpen.length, end) === ctrlOpen) {
|
|
20446
|
+
end -= ctrlOpen.length;
|
|
20447
|
+
}
|
|
20448
|
+
if (start > end) continue;
|
|
20449
|
+
if (start < lastEnd) continue;
|
|
20450
|
+
lastEnd = end;
|
|
20451
|
+
regions.push({
|
|
20452
|
+
name: decodeXmlEntities(name),
|
|
20453
|
+
placeholder: extractPlaceholder(fieldBeginXml),
|
|
20454
|
+
start,
|
|
20455
|
+
end,
|
|
20456
|
+
prefix
|
|
20457
|
+
});
|
|
20458
|
+
}
|
|
20459
|
+
return regions;
|
|
20460
|
+
}
|
|
20461
|
+
function valueXml(value, prefix) {
|
|
20462
|
+
if (value === "") return "";
|
|
20463
|
+
const lines = value.split("\n").map(escapeXmlText);
|
|
20464
|
+
return `<${prefix}t>${lines.join(`<${prefix}lineBreak/>`)}</${prefix}t>`;
|
|
20465
|
+
}
|
|
20466
|
+
function fillClickHereInXml(xml, cursor, blockedLabels) {
|
|
20467
|
+
const filled = [];
|
|
20468
|
+
const matchedKeys = /* @__PURE__ */ new Set();
|
|
20469
|
+
const splices = [];
|
|
20470
|
+
for (const region of scanClickHereRegions(xml)) {
|
|
20471
|
+
const key = normalizeLabel(region.name);
|
|
20472
|
+
if (!key || blockedLabels?.has(key)) continue;
|
|
20473
|
+
if (!cursor.has(key)) continue;
|
|
20474
|
+
const value = cursor.consume(key);
|
|
20475
|
+
if (value === void 0) continue;
|
|
20476
|
+
const replacement = valueXml(value, region.prefix);
|
|
20477
|
+
const current = xml.slice(region.start, region.end);
|
|
20478
|
+
matchedKeys.add(key);
|
|
20479
|
+
filled.push({ label: region.name, value, row: -1, col: -1, key });
|
|
20480
|
+
if (current === replacement) continue;
|
|
20481
|
+
splices.push({ start: region.start, end: region.end, replacement });
|
|
20482
|
+
}
|
|
20483
|
+
return {
|
|
20484
|
+
xml: splices.length > 0 ? applySplices(xml, splices) : null,
|
|
20485
|
+
filled,
|
|
20486
|
+
matchedKeys
|
|
20487
|
+
};
|
|
20488
|
+
}
|
|
20489
|
+
async function extractClickHereFields(hwpxBuffer) {
|
|
20490
|
+
const zip = await JSZip6.loadAsync(hwpxBuffer);
|
|
20491
|
+
const sectionPaths = Object.keys(zip.files).filter((name) => /[Ss]ection\d+\.xml$/i.test(name)).sort();
|
|
20492
|
+
const fields = [];
|
|
20493
|
+
for (let si = 0; si < sectionPaths.length; si++) {
|
|
20494
|
+
const xml = await zip.file(sectionPaths[si]).async("text");
|
|
20495
|
+
for (const r of scanClickHereRegions(xml)) {
|
|
20496
|
+
fields.push({
|
|
20497
|
+
name: r.name,
|
|
20498
|
+
...r.placeholder !== void 0 ? { placeholder: r.placeholder } : {},
|
|
20499
|
+
value: regionText(xml.slice(r.start, r.end)),
|
|
20500
|
+
section: si
|
|
20501
|
+
});
|
|
20502
|
+
}
|
|
20503
|
+
}
|
|
20504
|
+
return fields;
|
|
20505
|
+
}
|
|
20506
|
+
|
|
20507
|
+
// src/form/filler-hwpx.ts
|
|
20590
20508
|
async function fillHwpx(hwpxBuffer, values, blockedLabels) {
|
|
20591
20509
|
const u8 = new Uint8Array(hwpxBuffer);
|
|
20592
|
-
const zip = await
|
|
20510
|
+
const zip = await JSZip7.loadAsync(hwpxBuffer);
|
|
20593
20511
|
const sectionPaths = Object.keys(zip.files).filter((name) => /[Ss]ection\d+\.xml$/i.test(name)).sort();
|
|
20594
20512
|
if (sectionPaths.length === 0) {
|
|
20595
20513
|
throw new KordocError("HWPX\uC5D0\uC11C \uC139\uC158 \uD30C\uC77C\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4");
|
|
@@ -20603,8 +20521,23 @@ async function fillHwpx(hwpxBuffer, values, blockedLabels) {
|
|
|
20603
20521
|
const succeededKeys = /* @__PURE__ */ new Set();
|
|
20604
20522
|
const replacements = /* @__PURE__ */ new Map();
|
|
20605
20523
|
const encoder = new TextEncoder();
|
|
20524
|
+
const sectionXmls = /* @__PURE__ */ new Map();
|
|
20525
|
+
const fieldModified = /* @__PURE__ */ new Set();
|
|
20526
|
+
const fieldMatchedKeys = /* @__PURE__ */ new Set();
|
|
20527
|
+
for (const p of sectionPaths) {
|
|
20528
|
+
const xml = await zip.file(p).async("text");
|
|
20529
|
+
const outcome = fillClickHereInXml(xml, cursor, blockedLabels);
|
|
20530
|
+
sectionXmls.set(p, outcome.xml ?? xml);
|
|
20531
|
+
if (outcome.xml !== null) fieldModified.add(p);
|
|
20532
|
+
for (const f of outcome.filled) filled.push(f);
|
|
20533
|
+
for (const k of outcome.matchedKeys) fieldMatchedKeys.add(k);
|
|
20534
|
+
}
|
|
20535
|
+
for (const k of fieldMatchedKeys) {
|
|
20536
|
+
matchedLabels.add(k);
|
|
20537
|
+
normalizedValues.delete(k);
|
|
20538
|
+
}
|
|
20606
20539
|
for (let si = 0; si < sectionPaths.length; si++) {
|
|
20607
|
-
const xml =
|
|
20540
|
+
const xml = sectionXmls.get(sectionPaths[si]);
|
|
20608
20541
|
const scan = scanSectionXml(xml, si);
|
|
20609
20542
|
const ledger = /* @__PURE__ */ new Map();
|
|
20610
20543
|
const led = (p) => {
|
|
@@ -20786,94 +20719,460 @@ async function fillHwpx(hwpxBuffer, values, blockedLabels) {
|
|
|
20786
20719
|
}
|
|
20787
20720
|
}
|
|
20788
20721
|
}
|
|
20789
|
-
for (const para2 of [...scan.bodyParagraphs, ...scan.excludedParagraphs]) {
|
|
20790
|
-
const existing = ledger.get(para2);
|
|
20791
|
-
if (existing?.fullText !== void 0) continue;
|
|
20792
|
-
const text = matchText(para2);
|
|
20793
|
-
const segments = scanInlineSegments(text);
|
|
20794
|
-
const matches = segments.map((seg) => matchInlineSegment(seg, cursor, blockedLabels));
|
|
20795
|
-
for (let i = 0; i < segments.length; i++) {
|
|
20796
|
-
const seg = segments[i];
|
|
20797
|
-
const matched = matches[i];
|
|
20798
|
-
if (matched === void 0) continue;
|
|
20799
|
-
const matchKey = matched.key;
|
|
20800
|
-
const newValue = cursor.consume(matchKey);
|
|
20801
|
-
if (newValue === void 0) continue;
|
|
20802
|
-
const ve = clampSegmentEnd(text, seg, segments[i + 1], matches[i + 1]?.viaExt ?? false);
|
|
20803
|
-
const replacement = seg.valueStart === ve ? padInsertion(text, seg.valueStart, newValue) : newValue;
|
|
20804
|
-
const l = led(para2);
|
|
20805
|
-
l.ranges.push({ start: seg.valueStart, end: ve, replacement });
|
|
20806
|
-
matchedLabels.add(matchKey);
|
|
20807
|
-
l.filledIdx.push(filled.length);
|
|
20808
|
-
l.matchKeys.push(matchKey);
|
|
20809
|
-
filled.push({ label: matched.label.trim(), value: newValue, row: -1, col: -1, key: matchKey });
|
|
20810
|
-
}
|
|
20722
|
+
for (const para2 of [...scan.bodyParagraphs, ...scan.excludedParagraphs]) {
|
|
20723
|
+
const existing = ledger.get(para2);
|
|
20724
|
+
if (existing?.fullText !== void 0) continue;
|
|
20725
|
+
const text = matchText(para2);
|
|
20726
|
+
const segments = scanInlineSegments(text);
|
|
20727
|
+
const matches = segments.map((seg) => matchInlineSegment(seg, cursor, blockedLabels));
|
|
20728
|
+
for (let i = 0; i < segments.length; i++) {
|
|
20729
|
+
const seg = segments[i];
|
|
20730
|
+
const matched = matches[i];
|
|
20731
|
+
if (matched === void 0) continue;
|
|
20732
|
+
const matchKey = matched.key;
|
|
20733
|
+
const newValue = cursor.consume(matchKey);
|
|
20734
|
+
if (newValue === void 0) continue;
|
|
20735
|
+
const ve = clampSegmentEnd(text, seg, segments[i + 1], matches[i + 1]?.viaExt ?? false);
|
|
20736
|
+
const replacement = seg.valueStart === ve ? padInsertion(text, seg.valueStart, newValue) : newValue;
|
|
20737
|
+
const l = led(para2);
|
|
20738
|
+
l.ranges.push({ start: seg.valueStart, end: ve, replacement });
|
|
20739
|
+
matchedLabels.add(matchKey);
|
|
20740
|
+
l.filledIdx.push(filled.length);
|
|
20741
|
+
l.matchKeys.push(matchKey);
|
|
20742
|
+
filled.push({ label: matched.label.trim(), value: newValue, row: -1, col: -1, key: matchKey });
|
|
20743
|
+
}
|
|
20744
|
+
}
|
|
20745
|
+
const splices = [];
|
|
20746
|
+
for (const [para2, l] of ledger) {
|
|
20747
|
+
let paraSplices = null;
|
|
20748
|
+
if (l.fullText !== void 0) {
|
|
20749
|
+
paraSplices = buildParagraphSplices(para2, l.fullText, xml);
|
|
20750
|
+
} else if (l.ranges.length > 0) {
|
|
20751
|
+
const sorted = [...l.ranges].sort((a, b) => a.start - b.start || a.end - b.end);
|
|
20752
|
+
const merged = [];
|
|
20753
|
+
for (const r of sorted) {
|
|
20754
|
+
const prev = merged[merged.length - 1];
|
|
20755
|
+
if (prev && r.start < prev.end) continue;
|
|
20756
|
+
merged.push(r);
|
|
20757
|
+
}
|
|
20758
|
+
if (paraTText(para2, xml) !== null) {
|
|
20759
|
+
const precise = [];
|
|
20760
|
+
let ok = true;
|
|
20761
|
+
for (const r of merged) {
|
|
20762
|
+
const sp = buildRangeSplices(para2, xml, r.start, r.end, r.replacement);
|
|
20763
|
+
if (!sp) {
|
|
20764
|
+
ok = false;
|
|
20765
|
+
break;
|
|
20766
|
+
}
|
|
20767
|
+
precise.push(...sp);
|
|
20768
|
+
}
|
|
20769
|
+
paraSplices = ok ? precise : null;
|
|
20770
|
+
} else if (paraTextPureT(para2, xml)) {
|
|
20771
|
+
let text = para2.text;
|
|
20772
|
+
for (let k = merged.length - 1; k >= 0; k--) {
|
|
20773
|
+
const r = merged[k];
|
|
20774
|
+
text = text.slice(0, r.start) + r.replacement + text.slice(r.end);
|
|
20775
|
+
}
|
|
20776
|
+
paraSplices = buildParagraphSplices(para2, text, xml);
|
|
20777
|
+
} else {
|
|
20778
|
+
paraSplices = null;
|
|
20779
|
+
}
|
|
20780
|
+
}
|
|
20781
|
+
if (paraSplices === null) {
|
|
20782
|
+
for (const idx of l.filledIdx) filled[idx] = null;
|
|
20783
|
+
for (const k of l.matchKeys) failedKeys.add(k);
|
|
20784
|
+
continue;
|
|
20785
|
+
}
|
|
20786
|
+
for (const k of l.matchKeys) succeededKeys.add(k);
|
|
20787
|
+
splices.push(...paraSplices);
|
|
20788
|
+
}
|
|
20789
|
+
if (splices.length > 0 || fieldModified.has(sectionPaths[si])) {
|
|
20790
|
+
splices.push(...allLinesegRemovalSplices(xml));
|
|
20791
|
+
replacements.set(sectionPaths[si], encoder.encode(applySplices(xml, splices)));
|
|
20792
|
+
}
|
|
20793
|
+
}
|
|
20794
|
+
for (const k of failedKeys) {
|
|
20795
|
+
if (!succeededKeys.has(k) || cursor.isArray(k)) matchedLabels.delete(k);
|
|
20796
|
+
}
|
|
20797
|
+
const cleanFilled = filled.filter((f) => f !== null);
|
|
20798
|
+
const unmatched = resolveUnmatched(normalizedValues, matchedLabels, values);
|
|
20799
|
+
const out = replacements.size > 0 ? patchZipEntries(u8, replacements) : new Uint8Array(u8);
|
|
20800
|
+
return {
|
|
20801
|
+
buffer: out.buffer.slice(out.byteOffset, out.byteOffset + out.byteLength),
|
|
20802
|
+
filled: cleanFilled,
|
|
20803
|
+
unmatched,
|
|
20804
|
+
...warnings.length > 0 ? { warnings } : {}
|
|
20805
|
+
};
|
|
20806
|
+
}
|
|
20807
|
+
|
|
20808
|
+
// src/hwpx/generator.ts
|
|
20809
|
+
import JSZip8 from "jszip";
|
|
20810
|
+
|
|
20811
|
+
// src/hwpx/geometry.ts
|
|
20812
|
+
var A4_W_HU = 59528;
|
|
20813
|
+
var A4_H_HU = 84188;
|
|
20814
|
+
var HU_PER_MM = 7200 / 25.4;
|
|
20815
|
+
function mmToHwpunit(mm) {
|
|
20816
|
+
return Math.round(mm * 7200 / 25.4);
|
|
20817
|
+
}
|
|
20818
|
+
var BODY_WIDTH_20MM = mmToHwpunit(170);
|
|
20819
|
+
var COVER_MEASURED_W = 48180;
|
|
20820
|
+
var DATA_TABLE_ID_BASE = 1e3;
|
|
20821
|
+
var CHART_TABLE_ID_BASE = 91e5;
|
|
20822
|
+
var GJ_TABLE_ID_BASE = 92e5;
|
|
20823
|
+
var EXTRA_TABLE_ID_BASE = 93e5;
|
|
20824
|
+
var PIC_ID_BASE = 94e5;
|
|
20825
|
+
|
|
20826
|
+
// src/hwpx/gaejosik.ts
|
|
20827
|
+
var GAEJOSIK_BULLETS = ["\u25A1", "\u25CB", "-", "\u318D"];
|
|
20828
|
+
function gaejosikMarker(depth, bullet2 = "\u25CB") {
|
|
20829
|
+
if (depth === 1) return bullet2;
|
|
20830
|
+
return GAEJOSIK_BULLETS[Math.min(depth, GAEJOSIK_BULLETS.length - 1)];
|
|
20831
|
+
}
|
|
20832
|
+
function gaejosikSizes(bodyHeight, ov = {}) {
|
|
20833
|
+
const s = bodyHeight / 1500;
|
|
20834
|
+
const r = (v, pt) => pt ? Math.round(pt * 100) : Math.round(v * s);
|
|
20835
|
+
return {
|
|
20836
|
+
/** □ 대항목 — HY헤드라인M 16pt */
|
|
20837
|
+
dae: r(1600, ov.dae),
|
|
20838
|
+
/** ※ 참고 — 한양중고딕 13pt */
|
|
20839
|
+
cham: r(1300, ov.cham),
|
|
20840
|
+
/** 장 헤더(로마숫자·제목) 17pt */
|
|
20841
|
+
chapter: r(1700, ov.chapter),
|
|
20842
|
+
/** 표지 제목 30pt */
|
|
20843
|
+
coverTitle: r(3e3, ov.coverTitle),
|
|
20844
|
+
/** 표지 날짜·기관명 25pt */
|
|
20845
|
+
coverSub: r(2500, ov.coverSub),
|
|
20846
|
+
/** 목차 라벨 "목 차" 28pt */
|
|
20847
|
+
tocLabel: r(2800, ov.tocLabel),
|
|
20848
|
+
/** 목차 로마숫자 21pt(한양신명조) */
|
|
20849
|
+
tocRoman: r(2100, ov.tocRoman),
|
|
20850
|
+
/** 목차 항목 18pt */
|
|
20851
|
+
tocItem: r(1800, ov.tocItem),
|
|
20852
|
+
/** 표 셀 — 맑은 고딕 12pt (실측: 정부 양식 표) */
|
|
20853
|
+
table: r(1200, ov.table),
|
|
20854
|
+
/** 표지 장식 바 셀 빈 문단 — 6pt (실측: 바 높이 818 안에 줄높이 수납) */
|
|
20855
|
+
bar: r(600),
|
|
20856
|
+
/** 본문 첫 페이지 제목 박스 — HY헤드라인M 22pt (실측: GT3 표④) */
|
|
20857
|
+
bodyTitle: r(2200, ov.bodyTitle)
|
|
20858
|
+
};
|
|
20859
|
+
}
|
|
20860
|
+
function gaejosikSpaceBefore(depth, bodyHeight) {
|
|
20861
|
+
const s = bodyHeight / 1500;
|
|
20862
|
+
const ui = [3e3, 2e3, 1200, 600][Math.min(depth, 3)];
|
|
20863
|
+
return Math.round(ui * s);
|
|
20864
|
+
}
|
|
20865
|
+
function gaejosikLevelIndent(depth, bodyHeight, sizes = {}, bullet2 = "\u25CB") {
|
|
20866
|
+
const lefts = [0, 1, 1.5, 2];
|
|
20867
|
+
const left = depth <= 3 ? Math.round(lefts[depth] * bodyHeight) : Math.round((2 + (depth - 3) * 0.5) * bodyHeight);
|
|
20868
|
+
const h = depth === 0 ? gaejosikSizes(bodyHeight, sizes).dae : bodyHeight;
|
|
20869
|
+
return { left, indent: -markerWidth(gaejosikMarker(depth, bullet2), h) };
|
|
20870
|
+
}
|
|
20871
|
+
function gaejosikChamIndent(bodyHeight, sizes = {}) {
|
|
20872
|
+
const cham = gaejosikSizes(bodyHeight, sizes).cham;
|
|
20873
|
+
return { left: Math.round(2.5 * cham), indent: -markerWidth("\u203B", cham) };
|
|
20874
|
+
}
|
|
20875
|
+
function gaejosikTocItemIndent(bodyHeight, sizes = {}) {
|
|
20876
|
+
const sz = gaejosikSizes(bodyHeight, sizes);
|
|
20877
|
+
const hang = markerWidth("\u2167.", sz.tocRoman);
|
|
20878
|
+
return { left: 2e3 + hang, indent: -hang };
|
|
20879
|
+
}
|
|
20880
|
+
function formatGaejosikDate(d) {
|
|
20881
|
+
return `${d.getFullYear()}. ${d.getMonth() + 1}. ${d.getDate()}.`;
|
|
20882
|
+
}
|
|
20883
|
+
function stripChapterNumber(title) {
|
|
20884
|
+
return title.replace(/^\s*[□○ㅇㆍ-]\s+/u, "").replace(/^\s*(?:\d+(?:\.\d+)*|[Ⅰ-Ⅻ]+)\s*[.)]\s*/u, "").trim();
|
|
20885
|
+
}
|
|
20886
|
+
var GAEJOSIK_COLORS = {
|
|
20887
|
+
/** 장헤더 로마숫자 셀·표지 진한 바 음영 */
|
|
20888
|
+
primary: "#193AAA",
|
|
20889
|
+
/** 장헤더 셀 테두리 */
|
|
20890
|
+
border: "#006699",
|
|
20891
|
+
/** 표지 연한 바 */
|
|
20892
|
+
accent: "#A0B4E6",
|
|
20893
|
+
/** 장헤더 제목 셀 음영 */
|
|
20894
|
+
titleFill: "#F2F2F2",
|
|
20895
|
+
/** 장헤더 제목 셀 상하선 */
|
|
20896
|
+
titleLine: "#A6A6A6",
|
|
20897
|
+
/** 목차 박스 테두리 */
|
|
20898
|
+
tocBorder: "#514BAC",
|
|
20899
|
+
/** 목차 배너 라벤더 스트라이프 (실측: GT3 표②) */
|
|
20900
|
+
tocStripe: "#E0E5FA"
|
|
20901
|
+
};
|
|
20902
|
+
var GAEJOSIK_BASE_WIDTH = BODY_WIDTH_20MM;
|
|
20903
|
+
var CHAPTER_GEOM = { numW: 3327, gapW: 848, titleW: 43513, rowH: 2832 };
|
|
20904
|
+
var COVER_GEOM = {
|
|
20905
|
+
totalW: COVER_MEASURED_W,
|
|
20906
|
+
topDarkW: 38219,
|
|
20907
|
+
topLightW: 9961,
|
|
20908
|
+
barH: 818,
|
|
20909
|
+
titleH: 11060,
|
|
20910
|
+
botDarkW: 10466,
|
|
20911
|
+
botLightW: 37714,
|
|
20912
|
+
botBarH: 812
|
|
20913
|
+
};
|
|
20914
|
+
var TOC_GEOM = { boxW: 46492, boxH: 57840 };
|
|
20915
|
+
function chapterGeom(bodyHeight, ov = {}, bodyWidth = GAEJOSIK_BASE_WIDTH) {
|
|
20916
|
+
const s = gaejosikSizes(bodyHeight, ov).chapter / 1700;
|
|
20917
|
+
const ws = bodyWidth / GAEJOSIK_BASE_WIDTH;
|
|
20918
|
+
const numW = Math.round(CHAPTER_GEOM.numW * ws);
|
|
20919
|
+
const gapW = Math.round(CHAPTER_GEOM.gapW * ws);
|
|
20920
|
+
const titleW = Math.round((CHAPTER_GEOM.numW + CHAPTER_GEOM.gapW + CHAPTER_GEOM.titleW) * ws) - numW - gapW;
|
|
20921
|
+
return { numW, gapW, titleW, rowH: Math.round(CHAPTER_GEOM.rowH * s) };
|
|
20922
|
+
}
|
|
20923
|
+
function coverGeom(bodyHeight, ov = {}, bodyWidth = GAEJOSIK_BASE_WIDTH) {
|
|
20924
|
+
const s = gaejosikSizes(bodyHeight, ov).coverTitle / 3e3;
|
|
20925
|
+
const w = (v) => Math.round(v * bodyWidth / GAEJOSIK_BASE_WIDTH);
|
|
20926
|
+
const totalW = w(COVER_GEOM.totalW);
|
|
20927
|
+
return {
|
|
20928
|
+
...COVER_GEOM,
|
|
20929
|
+
totalW,
|
|
20930
|
+
topDarkW: w(COVER_GEOM.topDarkW),
|
|
20931
|
+
topLightW: totalW - w(COVER_GEOM.topDarkW),
|
|
20932
|
+
botDarkW: w(COVER_GEOM.botDarkW),
|
|
20933
|
+
botLightW: totalW - w(COVER_GEOM.botDarkW),
|
|
20934
|
+
titleH: Math.round(COVER_GEOM.titleH * s)
|
|
20935
|
+
};
|
|
20936
|
+
}
|
|
20937
|
+
var TOC_BANNER_GEOM = {
|
|
20938
|
+
colWs: [565, 565, 1414, 13191, 1414, 565, 565],
|
|
20939
|
+
h: 3931
|
|
20940
|
+
};
|
|
20941
|
+
function tocBannerGeom(bodyHeight, ov = {}) {
|
|
20942
|
+
const s = gaejosikSizes(bodyHeight, ov).tocLabel / 2800;
|
|
20943
|
+
return { colWs: TOC_BANNER_GEOM.colWs, h: Math.round(TOC_BANNER_GEOM.h * s) };
|
|
20944
|
+
}
|
|
20945
|
+
var BODY_TITLE_GEOM = { barH: 600, titleH: 3566 };
|
|
20946
|
+
function bodyTitleGeom(bodyHeight, ov = {}) {
|
|
20947
|
+
const s = gaejosikSizes(bodyHeight, ov).bodyTitle / 2200;
|
|
20948
|
+
return { barH: BODY_TITLE_GEOM.barH, titleH: Math.round(BODY_TITLE_GEOM.titleH * s) };
|
|
20949
|
+
}
|
|
20950
|
+
|
|
20951
|
+
// src/hwpx/gongmun.ts
|
|
20952
|
+
var OFFICIAL_MARGINS = { top: 20, bottom: 15, left: 20, right: 15 };
|
|
20953
|
+
var GAEJOSIK_MARGINS = { top: 15, bottom: 15, left: 20, right: 20 };
|
|
20954
|
+
var GAEJOSIK_HEADER_FOOTER = 4251;
|
|
20955
|
+
var PRESET_DEFAULTS = {
|
|
20956
|
+
// 서울시 실결재 104건 중 12pt 64건(13pt 30, 15pt 5) — 지배값을 기본으로 사용.
|
|
20957
|
+
official: { bodyPt: 12, lineSpacing: 160, numbering: "standard" },
|
|
20958
|
+
report: { bodyPt: 15, lineSpacing: 160, numbering: "report" },
|
|
20959
|
+
// 실측 추진계획안: □ → ㅇ → * 계층.
|
|
20960
|
+
plan: { bodyPt: 15, lineSpacing: 160, numbering: "report" },
|
|
20961
|
+
notice: { bodyPt: 15, lineSpacing: 160, numbering: "standard" },
|
|
20962
|
+
minutes: { bodyPt: 14, lineSpacing: 130, numbering: "standard" },
|
|
20963
|
+
gaejosik: { bodyPt: 15, lineSpacing: 160, numbering: "gaejosik" },
|
|
20964
|
+
// 보도자료 — 실측(국토부 실물): 본문 바탕 14pt 160%, □→ㅇ→*(각주) 부호
|
|
20965
|
+
press: { bodyPt: 14, lineSpacing: 160, numbering: "report" }
|
|
20966
|
+
};
|
|
20967
|
+
var PRESET_ALIAS = {
|
|
20968
|
+
official: "official",
|
|
20969
|
+
\uAE30\uC548\uBB38: "official",
|
|
20970
|
+
\uC2DC\uD589\uBB38: "official",
|
|
20971
|
+
\uACF5\uBB38: "official",
|
|
20972
|
+
\uACF5\uBB38\uC11C: "official",
|
|
20973
|
+
report: "report",
|
|
20974
|
+
\uBCF4\uACE0\uC11C: "report",
|
|
20975
|
+
plan: "plan",
|
|
20976
|
+
\uACC4\uD68D\uC11C: "plan",
|
|
20977
|
+
\uACC4\uD68D: "plan",
|
|
20978
|
+
notice: "notice",
|
|
20979
|
+
\uD1B5\uC9C0: "notice",
|
|
20980
|
+
\uC54C\uB9BC: "notice",
|
|
20981
|
+
\uC548\uB0B4: "notice",
|
|
20982
|
+
minutes: "minutes",
|
|
20983
|
+
\uD68C\uC758\uB85D: "minutes",
|
|
20984
|
+
gaejosik: "gaejosik",
|
|
20985
|
+
\uAC1C\uC870\uC2DD: "gaejosik",
|
|
20986
|
+
\uAC1C\uC870\uC2DD\uBCF4\uACE0\uC11C: "gaejosik",
|
|
20987
|
+
\uC815\uBD80\uBCF4\uACE0\uC11C: "gaejosik",
|
|
20988
|
+
\uC815\uBD80\uD45C\uC900\uAC1C\uC870\uC2DD\uBCF4\uACE0\uC11C: "gaejosik",
|
|
20989
|
+
press: "press",
|
|
20990
|
+
\uBCF4\uB3C4\uC790\uB8CC: "press"
|
|
20991
|
+
};
|
|
20992
|
+
function normalizeGongmunPreset(preset) {
|
|
20993
|
+
if (!preset) return "official";
|
|
20994
|
+
return PRESET_ALIAS[preset.trim()] ?? "official";
|
|
20995
|
+
}
|
|
20996
|
+
function usesReportFonts(preset) {
|
|
20997
|
+
return preset === "gaejosik" || preset === "report" || preset === "plan";
|
|
20998
|
+
}
|
|
20999
|
+
function usesAsteriskThird(preset) {
|
|
21000
|
+
return preset === "plan" || preset === "press";
|
|
21001
|
+
}
|
|
21002
|
+
function needsGaejosikAssets(gongmun) {
|
|
21003
|
+
return usesReportFonts(gongmun.preset) || gongmun.cover !== null || gongmun.toc;
|
|
21004
|
+
}
|
|
21005
|
+
function assertFiniteRange(name, value, min, max) {
|
|
21006
|
+
if (!Number.isFinite(value) || value < min || value > max) {
|
|
21007
|
+
throw new KordocError(`${name} must be a finite number between ${min} and ${max}`);
|
|
21008
|
+
}
|
|
21009
|
+
}
|
|
21010
|
+
function validateGongmunOptions(opts) {
|
|
21011
|
+
if (opts.bodyPt !== void 0) assertFiniteRange("bodyPt", opts.bodyPt, 6, 40);
|
|
21012
|
+
if (opts.lineSpacing !== void 0) assertFiniteRange("lineSpacing", opts.lineSpacing, 50, 300);
|
|
21013
|
+
if (typeof opts.autoFit === "object" && opts.autoFit.minRatio !== void 0) {
|
|
21014
|
+
assertFiniteRange("autoFit.minRatio", opts.autoFit.minRatio, 50, 99);
|
|
21015
|
+
}
|
|
21016
|
+
if (opts.margins) {
|
|
21017
|
+
for (const side of ["top", "bottom", "left", "right"]) {
|
|
21018
|
+
const value = opts.margins[side];
|
|
21019
|
+
assertFiniteRange(`margins.${side}`, value, 0, 200);
|
|
21020
|
+
}
|
|
21021
|
+
if (opts.margins.left + opts.margins.right >= 210) {
|
|
21022
|
+
throw new KordocError("margins.left + margins.right must be less than 210");
|
|
20811
21023
|
}
|
|
20812
|
-
|
|
20813
|
-
|
|
20814
|
-
let paraSplices = null;
|
|
20815
|
-
if (l.fullText !== void 0) {
|
|
20816
|
-
paraSplices = buildParagraphSplices(para2, l.fullText, xml);
|
|
20817
|
-
} else if (l.ranges.length > 0) {
|
|
20818
|
-
const sorted = [...l.ranges].sort((a, b) => a.start - b.start || a.end - b.end);
|
|
20819
|
-
const merged = [];
|
|
20820
|
-
for (const r of sorted) {
|
|
20821
|
-
const prev = merged[merged.length - 1];
|
|
20822
|
-
if (prev && r.start < prev.end) continue;
|
|
20823
|
-
merged.push(r);
|
|
20824
|
-
}
|
|
20825
|
-
if (paraTText(para2, xml) !== null) {
|
|
20826
|
-
const precise = [];
|
|
20827
|
-
let ok = true;
|
|
20828
|
-
for (const r of merged) {
|
|
20829
|
-
const sp = buildRangeSplices(para2, xml, r.start, r.end, r.replacement);
|
|
20830
|
-
if (!sp) {
|
|
20831
|
-
ok = false;
|
|
20832
|
-
break;
|
|
20833
|
-
}
|
|
20834
|
-
precise.push(...sp);
|
|
20835
|
-
}
|
|
20836
|
-
paraSplices = ok ? precise : null;
|
|
20837
|
-
} else if (paraTextPureT(para2, xml)) {
|
|
20838
|
-
let text = para2.text;
|
|
20839
|
-
for (let k = merged.length - 1; k >= 0; k--) {
|
|
20840
|
-
const r = merged[k];
|
|
20841
|
-
text = text.slice(0, r.start) + r.replacement + text.slice(r.end);
|
|
20842
|
-
}
|
|
20843
|
-
paraSplices = buildParagraphSplices(para2, text, xml);
|
|
20844
|
-
} else {
|
|
20845
|
-
paraSplices = null;
|
|
20846
|
-
}
|
|
20847
|
-
}
|
|
20848
|
-
if (paraSplices === null) {
|
|
20849
|
-
for (const idx of l.filledIdx) filled[idx] = null;
|
|
20850
|
-
for (const k of l.matchKeys) failedKeys.add(k);
|
|
20851
|
-
continue;
|
|
20852
|
-
}
|
|
20853
|
-
for (const k of l.matchKeys) succeededKeys.add(k);
|
|
20854
|
-
splices.push(...paraSplices);
|
|
21024
|
+
if (opts.margins.top + opts.margins.bottom >= 297) {
|
|
21025
|
+
throw new KordocError("margins.top + margins.bottom must be less than 297");
|
|
20855
21026
|
}
|
|
20856
|
-
|
|
20857
|
-
|
|
20858
|
-
|
|
21027
|
+
}
|
|
21028
|
+
if (opts.sizes) {
|
|
21029
|
+
for (const [key, value] of Object.entries(opts.sizes)) {
|
|
21030
|
+
if (value !== void 0) assertFiniteRange(`sizes.${key}`, value, 6, 60);
|
|
20859
21031
|
}
|
|
20860
21032
|
}
|
|
20861
|
-
|
|
20862
|
-
|
|
21033
|
+
if (opts.approval && opts.approval.length > 6) {
|
|
21034
|
+
throw new KordocError("approval must contain at most 6 labels");
|
|
20863
21035
|
}
|
|
20864
|
-
|
|
20865
|
-
|
|
20866
|
-
const
|
|
21036
|
+
}
|
|
21037
|
+
function incompatibleGongmunWarnings(opts) {
|
|
21038
|
+
const warns = [];
|
|
21039
|
+
const preset = normalizeGongmunPreset(opts.preset);
|
|
21040
|
+
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`);
|
|
21041
|
+
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`);
|
|
21042
|
+
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`);
|
|
21043
|
+
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`);
|
|
21044
|
+
if (preset === "press" && (opts.cover === true || typeof opts.cover === "object" || opts.toc === true)) {
|
|
21045
|
+
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");
|
|
21046
|
+
}
|
|
21047
|
+
if (opts.sizes && Object.keys(opts.sizes).length > 0 && !usesReportFonts(preset)) {
|
|
21048
|
+
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`);
|
|
21049
|
+
}
|
|
21050
|
+
if (opts.suppressSingle && (opts.numbering ?? PRESET_DEFAULTS[preset].numbering) !== "standard") {
|
|
21051
|
+
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`);
|
|
21052
|
+
}
|
|
21053
|
+
return warns;
|
|
21054
|
+
}
|
|
21055
|
+
function resolveGongmun(opts) {
|
|
21056
|
+
validateGongmunOptions(opts);
|
|
21057
|
+
const preset = normalizeGongmunPreset(opts.preset);
|
|
21058
|
+
const d = PRESET_DEFAULTS[preset];
|
|
21059
|
+
const bodyPt = opts.bodyPt ?? d.bodyPt;
|
|
21060
|
+
const autoFitMinRatio = opts.autoFit === false ? null : typeof opts.autoFit === "object" ? Math.min(Math.max(opts.autoFit.minRatio ?? 90, 50), 99) : 90;
|
|
21061
|
+
const coverOn = opts.cover !== void 0 ? opts.cover !== false : preset === "gaejosik";
|
|
21062
|
+
const coverOpts = typeof opts.cover === "object" ? opts.cover : {};
|
|
21063
|
+
const gaejosik = preset === "gaejosik";
|
|
21064
|
+
const reportFamily = gaejosik || preset === "report" || preset === "plan" || preset === "notice" || preset === "press";
|
|
20867
21065
|
return {
|
|
20868
|
-
|
|
20869
|
-
|
|
20870
|
-
|
|
20871
|
-
|
|
21066
|
+
preset,
|
|
21067
|
+
bodyFont: opts.bodyFont ?? "myeongjo",
|
|
21068
|
+
bodyHeight: Math.round(bodyPt * 100),
|
|
21069
|
+
lineSpacing: opts.lineSpacing ?? d.lineSpacing,
|
|
21070
|
+
numbering: opts.numbering ?? d.numbering,
|
|
21071
|
+
margins: opts.margins ?? (reportFamily ? GAEJOSIK_MARGINS : OFFICIAL_MARGINS),
|
|
21072
|
+
centerTitle: opts.centerTitle ?? true,
|
|
21073
|
+
autoFitMinRatio,
|
|
21074
|
+
// 보도자료는 머리박스가 1페이지 최상단을 차지하는 서식이라 표지·목차와 양립 불가 —
|
|
21075
|
+
// 켜면 머리박스가 표지에 얹히고 25pt 제목·부제가 유실된다 (docHead 프리셋 게이팅과 동일 관례)
|
|
21076
|
+
cover: coverOn && preset !== "press" ? { date: coverOpts.date ?? null, org: coverOpts.org ?? "" } : null,
|
|
21077
|
+
toc: preset !== "press" && (opts.toc ?? gaejosik),
|
|
21078
|
+
fonts: opts.fonts ?? {},
|
|
21079
|
+
sizes: opts.sizes ?? {},
|
|
21080
|
+
// 쪽번호 — 보고서 계열 관행(실측: 2_보고서 양식·추진계획·공고문 전부 하단 중앙)
|
|
21081
|
+
pageNumbers: opts.pageNumbers ?? (gaejosik || preset === "report" || preset === "plan"),
|
|
21082
|
+
// 머리말·꼬리말 — 실측: 보고서 계열 15mm(GT3·t2·춘천·브라더), 공고·보도 10mm,
|
|
21083
|
+
// 기안문 0(실결재 41/60건 h0/f0)
|
|
21084
|
+
headerFooter: usesReportFonts(preset) ? GAEJOSIK_HEADER_FOOTER : preset === "notice" || preset === "press" ? 2835 : 0,
|
|
21085
|
+
// "끝." — 기안문 규정(본문 끝 2타+"끝."). 그 외는 opt-in
|
|
21086
|
+
endMark: opts.endMark ?? preset === "official",
|
|
21087
|
+
approval: opts.approval && opts.approval.length > 0 ? opts.approval : null,
|
|
21088
|
+
// 본문 제목박스 — 실측(GT3·GT12): 목차 뒤 본문 시작에 제목 반복. 표지 켜진 개조식 기본
|
|
21089
|
+
bodyTitleBox: opts.bodyTitleBox ?? (gaejosik && coverOn),
|
|
21090
|
+
// h2 말머리 — 실측: 보고서 양식 □ 대항목(QA-2), 공고문 아라비아("1. 사업개요", 바이오헬스 실측)
|
|
21091
|
+
h2Marker: opts.h2Marker ?? (preset === "report" || preset === "plan" ? "box" : preset === "notice" ? "number" : "none"),
|
|
21092
|
+
// 2단계 부호 — 실결재 기안문·공고문 ㅇ 지배(60건 중 ㅇ134:○5), 보고서 양식 계열 ○
|
|
21093
|
+
bullet2: opts.bullet2 ?? (preset === "plan" || preset === "notice" || preset === "press" ? "\u3147" : "\u25CB"),
|
|
21094
|
+
// 단일 형제 부호 생략 — 규정이지만 부호 없는 계단이 실무 눈에 어색 (실무자 QA)
|
|
21095
|
+
suppressSingle: opts.suppressSingle ?? false,
|
|
21096
|
+
docHead: preset === "official" && opts.docHead ? opts.docHead : null,
|
|
21097
|
+
docFoot: preset === "official" && opts.docFoot ? opts.docFoot : null,
|
|
21098
|
+
reportInfo: opts.reportInfo?.trim() || null,
|
|
21099
|
+
noticeHead: preset === "notice" && opts.noticeHead ? opts.noticeHead : null,
|
|
21100
|
+
// 보도자료 머리박스는 프리셋 자체가 요구 — 옵션 미지정이어도 빈 객체
|
|
21101
|
+
press: preset === "press" ? opts.press ?? {} : null
|
|
20872
21102
|
};
|
|
20873
21103
|
}
|
|
20874
|
-
|
|
20875
|
-
|
|
20876
|
-
|
|
21104
|
+
var REPORT_BULLETS = ["\u25A1", "\u25CB", "-", "\u318D"];
|
|
21105
|
+
var ASTERISK_BULLETS = ["\u25A1", "\u25CB", "*", "\u318D"];
|
|
21106
|
+
function standardMarker(depth, n) {
|
|
21107
|
+
switch (depth) {
|
|
21108
|
+
case 0:
|
|
21109
|
+
return `${n + 1}.`;
|
|
21110
|
+
case 1:
|
|
21111
|
+
return `${hangulOrdinal(n)}.`;
|
|
21112
|
+
case 2:
|
|
21113
|
+
return `${n + 1})`;
|
|
21114
|
+
case 3:
|
|
21115
|
+
return `${hangulOrdinal(n)})`;
|
|
21116
|
+
case 4:
|
|
21117
|
+
return `(${n + 1})`;
|
|
21118
|
+
case 5:
|
|
21119
|
+
return `(${hangulOrdinal(n)})`;
|
|
21120
|
+
case 6:
|
|
21121
|
+
return circledNumber(n);
|
|
21122
|
+
case 7:
|
|
21123
|
+
return circledHangul(n);
|
|
21124
|
+
default:
|
|
21125
|
+
return circledHangul(n);
|
|
21126
|
+
}
|
|
21127
|
+
}
|
|
21128
|
+
function reportMarker(depth, bullet2 = "\u25CB", asteriskThird = false) {
|
|
21129
|
+
const bullets = asteriskThird ? ASTERISK_BULLETS : REPORT_BULLETS;
|
|
21130
|
+
const m = bullets[Math.min(depth, bullets.length - 1)];
|
|
21131
|
+
return depth === 1 ? bullet2 : m;
|
|
21132
|
+
}
|
|
21133
|
+
function markerWidth(marker, bodyHeight) {
|
|
21134
|
+
let em = SPACE_EM_FIXED;
|
|
21135
|
+
for (const c of marker) em += charWidthEm1000(c.codePointAt(0));
|
|
21136
|
+
return Math.round(em / 1e3 * bodyHeight);
|
|
21137
|
+
}
|
|
21138
|
+
function levelIndent(depth, bodyHeight, numbering, sizes = {}, bullet2 = "\u25CB", asteriskThird = false) {
|
|
21139
|
+
if (numbering === "gaejosik") return gaejosikLevelIndent(depth, bodyHeight, sizes, bullet2);
|
|
21140
|
+
const marker = numbering === "report" ? reportMarker(depth, bullet2, asteriskThird) : standardMarker(depth, 0);
|
|
21141
|
+
return { left: Math.round(depth * bodyHeight), indent: -markerWidth(marker, bodyHeight) };
|
|
21142
|
+
}
|
|
21143
|
+
function computeSuppression(depths) {
|
|
21144
|
+
const counts = /* @__PURE__ */ new Map();
|
|
21145
|
+
const keys = [];
|
|
21146
|
+
const path = [];
|
|
21147
|
+
for (const depth of depths) {
|
|
21148
|
+
path.length = depth + 1;
|
|
21149
|
+
path[depth] = (path[depth] ?? 0) + 1;
|
|
21150
|
+
const parentKey = path.slice(0, depth).join(".") + "|" + depth;
|
|
21151
|
+
keys.push(parentKey);
|
|
21152
|
+
counts.set(parentKey, (counts.get(parentKey) ?? 0) + 1);
|
|
21153
|
+
}
|
|
21154
|
+
return keys.map((k) => (counts.get(k) ?? 0) <= 1);
|
|
21155
|
+
}
|
|
21156
|
+
var GongmunNumberer = class {
|
|
21157
|
+
constructor(numbering, bullet2 = "\u25CB", asteriskThird = false) {
|
|
21158
|
+
this.numbering = numbering;
|
|
21159
|
+
this.bullet2 = bullet2;
|
|
21160
|
+
this.asteriskThird = asteriskThird;
|
|
21161
|
+
}
|
|
21162
|
+
counts = [];
|
|
21163
|
+
/** depth 항목 하나에 대한 마커. suppress=true면 빈 문자열(부호 없음) */
|
|
21164
|
+
next(depth, suppress) {
|
|
21165
|
+
this.counts.length = depth + 1;
|
|
21166
|
+
const n = this.counts[depth] ?? 0;
|
|
21167
|
+
this.counts[depth] = n + 1;
|
|
21168
|
+
if (suppress) return "";
|
|
21169
|
+
if (this.numbering === "gaejosik") return gaejosikMarker(depth, this.bullet2);
|
|
21170
|
+
return this.numbering === "report" ? reportMarker(depth, this.bullet2, this.asteriskThird) : standardMarker(depth, n);
|
|
21171
|
+
}
|
|
21172
|
+
reset() {
|
|
21173
|
+
this.counts = [];
|
|
21174
|
+
}
|
|
21175
|
+
};
|
|
20877
21176
|
|
|
20878
21177
|
// src/hwpx/gen-ids.ts
|
|
20879
21178
|
var NS_SECTION = "http://www.hancom.co.kr/hwpml/2011/section";
|
|
@@ -24240,7 +24539,7 @@ async function markdownToHwpx(markdown, options) {
|
|
|
24240
24539
|
}
|
|
24241
24540
|
}
|
|
24242
24541
|
const extraBorderFills = [...remap?.borderFillXmls ?? [], ...bfReg?.emit() ?? []];
|
|
24243
|
-
const zip = new
|
|
24542
|
+
const zip = new JSZip8();
|
|
24244
24543
|
zip.file("mimetype", "application/hwp+zip", { compression: "STORE" });
|
|
24245
24544
|
zip.file("META-INF/container.xml", generateContainerXml());
|
|
24246
24545
|
zip.file("Contents/content.hpf", generateManifest(chartParts, images.manifestItems(), gongmun ? "gongmun" : "default"));
|
|
@@ -24402,8 +24701,55 @@ function diffTableCells(a, b) {
|
|
|
24402
24701
|
return result;
|
|
24403
24702
|
}
|
|
24404
24703
|
|
|
24704
|
+
// src/form/templates.ts
|
|
24705
|
+
import { existsSync, readFileSync } from "fs";
|
|
24706
|
+
import { fileURLToPath } from "url";
|
|
24707
|
+
import { dirname, join } from "path";
|
|
24708
|
+
var BUILTIN_TEMPLATES = [
|
|
24709
|
+
{
|
|
24710
|
+
id: "gian",
|
|
24711
|
+
aliases: ["\uC77C\uBC18\uAE30\uC548\uBB38", "\uAE30\uC548\uBB38", "\uC77C\uBC18\uAE30\uC548\uBB38_\uC11C\uC2DD"],
|
|
24712
|
+
file: "\uC77C\uBC18\uAE30\uC548\uBB38_\uC11C\uC2DD.hwpx",
|
|
24713
|
+
sampleFile: "\uC77C\uBC18\uAE30\uC548\uBB38_\uC608\uC2DC\uAC12.json",
|
|
24714
|
+
title: "\uC77C\uBC18\uAE30\uC548\uBB38 \u2014 \uBCC4\uC9C0 \uC81C1\uD638\uC11C\uC2DD (\uB300\uC678 \uC2DC\uD589\uBB38\xB7\uD611\uC870\uBB38, \uB204\uB984\uD2C0 23\uACF3)"
|
|
24715
|
+
},
|
|
24716
|
+
{
|
|
24717
|
+
id: "gian-simple",
|
|
24718
|
+
aliases: ["\uAC04\uC774\uAE30\uC548\uBB38", "\uAC04\uC774\uAE30\uC548\uBB38_\uC11C\uC2DD"],
|
|
24719
|
+
file: "\uAC04\uC774\uAE30\uC548\uBB38_\uC11C\uC2DD.hwpx",
|
|
24720
|
+
sampleFile: "\uAC04\uC774\uAE30\uC548\uBB38_\uC608\uC2DC\uAC12.json",
|
|
24721
|
+
title: "\uAC04\uC774\uAE30\uC548\uBB38 \u2014 \uBCC4\uC9C0 \uC81C2\uD638\uC11C\uC2DD (\uB0B4\uBD80\uACB0\uC7AC \uBCF4\uACE0\uC11C\xB7\uACC4\uD68D\uC11C, \uACB0\uC7AC\uB780 \uD45C, \uB204\uB984\uD2C0 13\uACF3)"
|
|
24722
|
+
}
|
|
24723
|
+
];
|
|
24724
|
+
function resolveBuiltinTemplate(name) {
|
|
24725
|
+
const n = name.trim().replace(/^templates?:/i, "").trim();
|
|
24726
|
+
if (!n) return void 0;
|
|
24727
|
+
const low = n.toLowerCase();
|
|
24728
|
+
return BUILTIN_TEMPLATES.find((t) => t.id === low || t.aliases.includes(n));
|
|
24729
|
+
}
|
|
24730
|
+
function builtinTemplateDir() {
|
|
24731
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
24732
|
+
const candidates = [
|
|
24733
|
+
join(here, "..", "..", "templates"),
|
|
24734
|
+
// src/form/templates.ts → 패키지 루트
|
|
24735
|
+
join(here, "..", "templates")
|
|
24736
|
+
// dist/*.js (tsup 번들) → 패키지 루트
|
|
24737
|
+
];
|
|
24738
|
+
for (const c of candidates) {
|
|
24739
|
+
if (existsSync(join(c, BUILTIN_TEMPLATES[0].file))) return c;
|
|
24740
|
+
}
|
|
24741
|
+
throw new KordocError("\uB0B4\uC7A5 templates \uB514\uB809\uD1A0\uB9AC\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4 (\uD328\uD0A4\uC9C0 \uC124\uCE58\uAC00 \uC190\uC0C1\uB418\uC5C8\uC744 \uC218 \uC788\uC2B5\uB2C8\uB2E4)");
|
|
24742
|
+
}
|
|
24743
|
+
function readBuiltinTemplate(t) {
|
|
24744
|
+
const buf = readFileSync(join(builtinTemplateDir(), t.file));
|
|
24745
|
+
return buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength);
|
|
24746
|
+
}
|
|
24747
|
+
function readBuiltinTemplateSample(t) {
|
|
24748
|
+
return JSON.parse(readFileSync(join(builtinTemplateDir(), t.sampleFile), "utf-8"));
|
|
24749
|
+
}
|
|
24750
|
+
|
|
24405
24751
|
// src/hwpx/extract-profile.ts
|
|
24406
|
-
import
|
|
24752
|
+
import JSZip9 from "jszip";
|
|
24407
24753
|
function elemsByLocal(root, name) {
|
|
24408
24754
|
const all = root.getElementsByTagName("*");
|
|
24409
24755
|
const out = [];
|
|
@@ -24606,7 +24952,7 @@ function pick(map, keys) {
|
|
|
24606
24952
|
}
|
|
24607
24953
|
async function hwpxToProfile(input) {
|
|
24608
24954
|
const buf = input instanceof ArrayBuffer ? input : toArrayBuffer(input);
|
|
24609
|
-
const zip = await
|
|
24955
|
+
const zip = await JSZip9.loadAsync(buf);
|
|
24610
24956
|
const parser = createXmlParser();
|
|
24611
24957
|
const headerFile = zip.file("Contents/header.xml") ?? zip.file(/[Hh]eader\.xml$/)?.[0];
|
|
24612
24958
|
let headerXml = "<root/>";
|
|
@@ -24921,7 +25267,7 @@ function gongmunLintWarnings(text, limit = 10) {
|
|
|
24921
25267
|
}
|
|
24922
25268
|
|
|
24923
25269
|
// src/roundtrip/patcher.ts
|
|
24924
|
-
import
|
|
25270
|
+
import JSZip10 from "jszip";
|
|
24925
25271
|
|
|
24926
25272
|
// src/roundtrip/table-rows.ts
|
|
24927
25273
|
var ROW_OBJECT_RE = /<(?:[A-Za-z0-9_]+:)?(?:tbl|pic|equation|ole|container|shape|drawingObject|drawText|video|chart|fieldBegin|fieldEnd|ctrl)\b/;
|
|
@@ -25568,7 +25914,7 @@ async function patchHwpx(original, editedMarkdown, options) {
|
|
|
25568
25914
|
}
|
|
25569
25915
|
let zip;
|
|
25570
25916
|
try {
|
|
25571
|
-
zip = await
|
|
25917
|
+
zip = await JSZip10.loadAsync(original);
|
|
25572
25918
|
} catch {
|
|
25573
25919
|
return { success: false, applied: 0, skipped, error: "ZIP \uB85C\uB4DC \uC2E4\uD328" };
|
|
25574
25920
|
}
|
|
@@ -25928,6 +26274,8 @@ function replaceOleStream(file, path, newData) {
|
|
|
25928
26274
|
}
|
|
25929
26275
|
var Surgeon = class {
|
|
25930
26276
|
buf;
|
|
26277
|
+
/** 논리 파일 길이 — buf는 성장 시 기하급수 capacity로 재할당되므로 buf.length와 다를 수 있음 */
|
|
26278
|
+
len;
|
|
25931
26279
|
fat = [];
|
|
25932
26280
|
/** FAT 배열을 구성하는 섹터 번호들 (DIFAT 순서) */
|
|
25933
26281
|
fatSectors = [];
|
|
@@ -25947,6 +26295,7 @@ var Surgeon = class {
|
|
|
25947
26295
|
}
|
|
25948
26296
|
const padded = Math.ceil((file.length - SECTOR) / SECTOR) * SECTOR + SECTOR;
|
|
25949
26297
|
this.buf = Buffer.alloc(padded);
|
|
26298
|
+
this.len = padded;
|
|
25950
26299
|
file.copy(this.buf);
|
|
25951
26300
|
this.loadFat();
|
|
25952
26301
|
this.loadMiniFat();
|
|
@@ -26001,7 +26350,7 @@ var Surgeon = class {
|
|
|
26001
26350
|
// ── 헬퍼 ──
|
|
26002
26351
|
sectorOffset(n) {
|
|
26003
26352
|
const off = SECTOR + n * SECTOR;
|
|
26004
|
-
if (n >= 4294967290 || off + SECTOR > this.
|
|
26353
|
+
if (n >= 4294967290 || off + SECTOR > this.len) throw new OleSurgeonError(`\uC139\uD130 \uBC94\uC704 \uCD08\uACFC: ${n}`);
|
|
26005
26354
|
return off;
|
|
26006
26355
|
}
|
|
26007
26356
|
chain(start) {
|
|
@@ -26059,26 +26408,40 @@ var Surgeon = class {
|
|
|
26059
26408
|
const out = [];
|
|
26060
26409
|
for (let i = 0; i < this.fat.length && out.length < n; i++) {
|
|
26061
26410
|
if (this.fat[i] !== FREESECT) continue;
|
|
26062
|
-
if (SECTOR + (i + 1) * SECTOR > this.
|
|
26411
|
+
if (SECTOR + (i + 1) * SECTOR > this.len) continue;
|
|
26063
26412
|
this.fat[i] = ENDOFCHAIN;
|
|
26064
26413
|
const off = this.sectorOffset(i);
|
|
26065
26414
|
this.buf.fill(0, off, off + SECTOR);
|
|
26066
26415
|
out.push(i);
|
|
26067
26416
|
}
|
|
26068
26417
|
while (out.length < n) {
|
|
26069
|
-
this.ensureFatCapacity((this.
|
|
26070
|
-
const idx =
|
|
26071
|
-
this.buf = Buffer.concat([this.buf, Buffer.alloc(SECTOR)]);
|
|
26418
|
+
this.ensureFatCapacity((this.len - SECTOR) / SECTOR + 2);
|
|
26419
|
+
const idx = this.appendSector();
|
|
26072
26420
|
this.fat[idx] = ENDOFCHAIN;
|
|
26073
26421
|
out.push(idx);
|
|
26074
26422
|
}
|
|
26075
26423
|
return out;
|
|
26076
26424
|
}
|
|
26425
|
+
/**
|
|
26426
|
+
* 파일 끝에 빈 섹터 1개 추가 — 새 섹터 번호 반환.
|
|
26427
|
+
* 섹터마다 Buffer.concat(전체 복사)하면 대형 스트림 삽입이 O(n²)이라,
|
|
26428
|
+
* capacity를 기하급수(×2)로 재할당하고 논리 길이(len)만 늘린다 (amortized O(n)).
|
|
26429
|
+
* Buffer.alloc은 0으로 채워지고 len 너머엔 아무도 쓰지 않으므로 새 섹터는 항상 0.
|
|
26430
|
+
*/
|
|
26431
|
+
appendSector() {
|
|
26432
|
+
const idx = (this.len - SECTOR) / SECTOR;
|
|
26433
|
+
if (this.len + SECTOR > this.buf.length) {
|
|
26434
|
+
const grown = Buffer.alloc(Math.max(this.buf.length * 2, this.len + SECTOR));
|
|
26435
|
+
this.buf.copy(grown, 0, 0, this.len);
|
|
26436
|
+
this.buf = grown;
|
|
26437
|
+
}
|
|
26438
|
+
this.len += SECTOR;
|
|
26439
|
+
return idx;
|
|
26440
|
+
}
|
|
26077
26441
|
/** FAT 배열이 sectorCount개 엔트리를 담도록 확장 (FAT 섹터 추가 + DIFAT 갱신) */
|
|
26078
26442
|
ensureFatCapacity(sectorCount) {
|
|
26079
26443
|
while (this.fat.length < sectorCount) {
|
|
26080
|
-
const idx =
|
|
26081
|
-
this.buf = Buffer.concat([this.buf, Buffer.alloc(SECTOR)]);
|
|
26444
|
+
const idx = this.appendSector();
|
|
26082
26445
|
for (let i = 0; i < 128; i++) this.fat.push(FREESECT);
|
|
26083
26446
|
this.fat[idx] = FATSECT;
|
|
26084
26447
|
this.fatSectors.push(idx);
|
|
@@ -26201,7 +26564,7 @@ var Surgeon = class {
|
|
|
26201
26564
|
finish() {
|
|
26202
26565
|
this.wipeFreedSectors();
|
|
26203
26566
|
this.flushFat();
|
|
26204
|
-
return this.buf;
|
|
26567
|
+
return this.len === this.buf.length ? this.buf : this.buf.subarray(0, this.len);
|
|
26205
26568
|
}
|
|
26206
26569
|
/** 해제 후 재할당되지 않고 남은 FREESECT 섹터의 바이트를 0으로 채움 (데이터 remanence 제거) */
|
|
26207
26570
|
wipeFreedSectors() {
|
|
@@ -27019,7 +27382,7 @@ function stageParaPatch(scan, para2, newPlain, skip) {
|
|
|
27019
27382
|
}
|
|
27020
27383
|
|
|
27021
27384
|
// src/validate.ts
|
|
27022
|
-
import
|
|
27385
|
+
import JSZip11 from "jszip";
|
|
27023
27386
|
import { DOMParser as DOMParser4 } from "@xmldom/xmldom";
|
|
27024
27387
|
var REQUIRED_FILES = [
|
|
27025
27388
|
"mimetype",
|
|
@@ -27037,7 +27400,7 @@ async function validateHwpx(buffer) {
|
|
|
27037
27400
|
const issues = [];
|
|
27038
27401
|
let zip;
|
|
27039
27402
|
try {
|
|
27040
|
-
zip = await
|
|
27403
|
+
zip = await JSZip11.loadAsync(buffer);
|
|
27041
27404
|
} catch (err) {
|
|
27042
27405
|
return {
|
|
27043
27406
|
ok: false,
|
|
@@ -27105,10 +27468,10 @@ async function validateHwpx(buffer) {
|
|
|
27105
27468
|
}
|
|
27106
27469
|
|
|
27107
27470
|
// src/roundtrip/session.ts
|
|
27108
|
-
import
|
|
27471
|
+
import JSZip12 from "jszip";
|
|
27109
27472
|
async function buildState(bytes) {
|
|
27110
27473
|
const parsed = await parseHwpxDocument(u8ToArrayBuffer2(bytes));
|
|
27111
|
-
const zip = await
|
|
27474
|
+
const zip = await JSZip12.loadAsync(bytes);
|
|
27112
27475
|
const sectionPaths = await resolveSectionEntryNames(zip);
|
|
27113
27476
|
if (sectionPaths.length === 0) {
|
|
27114
27477
|
throw new Error("HWPX \uC139\uC158 \uD30C\uC77C\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4");
|
|
@@ -27450,7 +27813,7 @@ async function patchHwpxBlocks(original, edits, options) {
|
|
|
27450
27813
|
}
|
|
27451
27814
|
|
|
27452
27815
|
// src/print/renderer.ts
|
|
27453
|
-
import { existsSync } from "fs";
|
|
27816
|
+
import { existsSync as existsSync2 } from "fs";
|
|
27454
27817
|
import MarkdownIt from "markdown-it";
|
|
27455
27818
|
var PRESETS = {
|
|
27456
27819
|
default: `
|
|
@@ -27589,7 +27952,7 @@ function findChromiumPath() {
|
|
|
27589
27952
|
const linux = ["/usr/bin/google-chrome", "/usr/bin/chromium-browser", "/usr/bin/chromium"];
|
|
27590
27953
|
const candidates = process.platform === "win32" ? win : process.platform === "darwin" ? mac : linux;
|
|
27591
27954
|
for (const p of candidates) {
|
|
27592
|
-
if (p &&
|
|
27955
|
+
if (p && existsSync2(p)) return p;
|
|
27593
27956
|
}
|
|
27594
27957
|
return null;
|
|
27595
27958
|
}
|
|
@@ -27649,7 +28012,7 @@ async function parse(input, options) {
|
|
|
27649
28012
|
}
|
|
27650
28013
|
async function parseImage(buffer, options) {
|
|
27651
28014
|
try {
|
|
27652
|
-
const { parseImageDocument } = await import("./image-ocr-
|
|
28015
|
+
const { parseImageDocument } = await import("./image-ocr-R44RYDBU.js");
|
|
27653
28016
|
const { blocks, warnings } = await parseImageDocument(buffer, options);
|
|
27654
28017
|
return {
|
|
27655
28018
|
success: true,
|
|
@@ -27666,7 +28029,7 @@ async function parseImage(buffer, options) {
|
|
|
27666
28029
|
async function parseHwp3(buffer, options) {
|
|
27667
28030
|
try {
|
|
27668
28031
|
const { markdown, blocks, metadata, outline, warnings } = parseHwp3Document(buffer, options);
|
|
27669
|
-
return { success: true, fileType: "hwp3", markdown, blocks, metadata, outline, warnings };
|
|
28032
|
+
return { success: true, fileType: "hwp3", markdown, blocks, metadata, outline, warnings, pageCount: metadata?.pageCount };
|
|
27670
28033
|
} catch (err) {
|
|
27671
28034
|
return { success: false, fileType: "hwp3", error: sanitizeError(err), code: classifyError(err) };
|
|
27672
28035
|
}
|
|
@@ -27674,7 +28037,7 @@ async function parseHwp3(buffer, options) {
|
|
|
27674
28037
|
async function parseHwpx(buffer, options) {
|
|
27675
28038
|
try {
|
|
27676
28039
|
const { markdown, blocks, metadata, outline, warnings, images } = await parseHwpxDocument(buffer, options);
|
|
27677
|
-
return { success: true, fileType: "hwpx", markdown, blocks, metadata, outline, warnings, images: images?.length ? images : void 0 };
|
|
28040
|
+
return { success: true, fileType: "hwpx", markdown, blocks, metadata, outline, warnings, images: images?.length ? images : void 0, pageCount: metadata?.pageCount };
|
|
27678
28041
|
} catch (err) {
|
|
27679
28042
|
return { success: false, fileType: "hwpx", error: sanitizeError(err), code: classifyError(err) };
|
|
27680
28043
|
}
|
|
@@ -27693,13 +28056,14 @@ async function parseHwp(buffer, options) {
|
|
|
27693
28056
|
markdown: com.markdown,
|
|
27694
28057
|
blocks: com.blocks,
|
|
27695
28058
|
metadata: com.metadata,
|
|
27696
|
-
warnings: com.warnings
|
|
28059
|
+
warnings: com.warnings,
|
|
28060
|
+
pageCount
|
|
27697
28061
|
};
|
|
27698
28062
|
}
|
|
27699
28063
|
} catch {
|
|
27700
28064
|
}
|
|
27701
28065
|
}
|
|
27702
|
-
return { success: true, fileType: "hwp", markdown, blocks, metadata, outline, warnings, images: images?.length ? images : void 0 };
|
|
28066
|
+
return { success: true, fileType: "hwp", markdown, blocks, metadata, outline, warnings, images: images?.length ? images : void 0, pageCount: metadata?.pageCount };
|
|
27703
28067
|
} catch (err) {
|
|
27704
28068
|
return { success: false, fileType: "hwp", error: sanitizeError(err), code: classifyError(err) };
|
|
27705
28069
|
}
|
|
@@ -27707,7 +28071,7 @@ async function parseHwp(buffer, options) {
|
|
|
27707
28071
|
async function parsePdf(buffer, options) {
|
|
27708
28072
|
let parsePdfDocument;
|
|
27709
28073
|
try {
|
|
27710
|
-
const mod = await import("./parser-
|
|
28074
|
+
const mod = await import("./parser-W3POOYQQ.js");
|
|
27711
28075
|
parsePdfDocument = mod.parsePdfDocument;
|
|
27712
28076
|
} catch {
|
|
27713
28077
|
return {
|
|
@@ -27719,7 +28083,7 @@ async function parsePdf(buffer, options) {
|
|
|
27719
28083
|
}
|
|
27720
28084
|
try {
|
|
27721
28085
|
const { markdown, blocks, metadata, outline, warnings, isImageBased, pageQuality, qualitySummary, images } = await parsePdfDocument(buffer, options);
|
|
27722
|
-
return { success: true, fileType: "pdf", markdown, blocks, metadata, outline, warnings, isImageBased, pageQuality, qualitySummary, images };
|
|
28086
|
+
return { success: true, fileType: "pdf", markdown, blocks, metadata, outline, warnings, isImageBased, pageQuality, qualitySummary, images, pageCount: metadata?.pageCount };
|
|
27723
28087
|
} catch (err) {
|
|
27724
28088
|
const isImageBased = err instanceof Error && "isImageBased" in err ? true : void 0;
|
|
27725
28089
|
return { success: false, fileType: "pdf", error: sanitizeError(err), code: classifyError(err), isImageBased };
|
|
@@ -27728,7 +28092,7 @@ async function parsePdf(buffer, options) {
|
|
|
27728
28092
|
async function parseXlsx(buffer, options) {
|
|
27729
28093
|
try {
|
|
27730
28094
|
const { markdown, blocks, metadata, warnings } = await parseXlsxDocument(buffer, options);
|
|
27731
|
-
return { success: true, fileType: "xlsx", markdown, blocks, metadata, warnings };
|
|
28095
|
+
return { success: true, fileType: "xlsx", markdown, blocks, metadata, warnings, pageCount: metadata?.pageCount };
|
|
27732
28096
|
} catch (err) {
|
|
27733
28097
|
return { success: false, fileType: "xlsx", error: sanitizeError(err), code: classifyError(err) };
|
|
27734
28098
|
}
|
|
@@ -27736,7 +28100,7 @@ async function parseXlsx(buffer, options) {
|
|
|
27736
28100
|
async function parseXls(buffer, options) {
|
|
27737
28101
|
try {
|
|
27738
28102
|
const { markdown, blocks, metadata, warnings } = await parseXlsDocument(buffer, options);
|
|
27739
|
-
return { success: true, fileType: "xls", markdown, blocks, metadata, warnings };
|
|
28103
|
+
return { success: true, fileType: "xls", markdown, blocks, metadata, warnings, pageCount: metadata?.pageCount };
|
|
27740
28104
|
} catch (err) {
|
|
27741
28105
|
return { success: false, fileType: "xls", error: sanitizeError(err), code: classifyError(err) };
|
|
27742
28106
|
}
|
|
@@ -27744,7 +28108,7 @@ async function parseXls(buffer, options) {
|
|
|
27744
28108
|
async function parseDocx(buffer, options) {
|
|
27745
28109
|
try {
|
|
27746
28110
|
const { markdown, blocks, metadata, outline, warnings, images } = await parseDocxDocument(buffer, options);
|
|
27747
|
-
return { success: true, fileType: "docx", markdown, blocks, metadata, outline, warnings, images: images?.length ? images : void 0 };
|
|
28111
|
+
return { success: true, fileType: "docx", markdown, blocks, metadata, outline, warnings, images: images?.length ? images : void 0, pageCount: metadata?.pageCount };
|
|
27748
28112
|
} catch (err) {
|
|
27749
28113
|
return { success: false, fileType: "docx", error: sanitizeError(err), code: classifyError(err) };
|
|
27750
28114
|
}
|
|
@@ -27752,7 +28116,7 @@ async function parseDocx(buffer, options) {
|
|
|
27752
28116
|
async function parseHwpml(buffer, options) {
|
|
27753
28117
|
try {
|
|
27754
28118
|
const { markdown, blocks, metadata, outline, warnings } = parseHwpmlDocument(buffer, options);
|
|
27755
|
-
return { success: true, fileType: "hwpml", markdown, blocks, metadata, outline, warnings };
|
|
28119
|
+
return { success: true, fileType: "hwpml", markdown, blocks, metadata, outline, warnings, pageCount: metadata?.pageCount };
|
|
27756
28120
|
} catch (err) {
|
|
27757
28121
|
return { success: false, fileType: "hwpml", error: sanitizeError(err), code: classifyError(err) };
|
|
27758
28122
|
}
|
|
@@ -27798,9 +28162,13 @@ async function fillForm(input, values, outputFormat = "markdown") {
|
|
|
27798
28162
|
}
|
|
27799
28163
|
|
|
27800
28164
|
export {
|
|
27801
|
-
|
|
27802
|
-
|
|
27803
|
-
|
|
28165
|
+
isZipFile,
|
|
28166
|
+
isHwpxFile,
|
|
28167
|
+
isOldHwpFile,
|
|
28168
|
+
isPdfFile,
|
|
28169
|
+
detectFormat,
|
|
28170
|
+
detectOle2Format,
|
|
28171
|
+
detectZipFormat,
|
|
27804
28172
|
extractHwpxMetadataOnly,
|
|
27805
28173
|
extractHwp5MetadataOnly,
|
|
27806
28174
|
formatFillValue,
|
|
@@ -27811,10 +28179,18 @@ export {
|
|
|
27811
28179
|
inferFieldType,
|
|
27812
28180
|
extractFormSchema,
|
|
27813
28181
|
fillFormFields,
|
|
28182
|
+
extractClickHereFields,
|
|
27814
28183
|
fillHwpx,
|
|
28184
|
+
PRESET_ALIAS,
|
|
28185
|
+
normalizeGongmunPreset,
|
|
28186
|
+
incompatibleGongmunWarnings,
|
|
27815
28187
|
markdownToHwpx,
|
|
27816
28188
|
compare,
|
|
27817
28189
|
diffBlocks,
|
|
28190
|
+
BUILTIN_TEMPLATES,
|
|
28191
|
+
resolveBuiltinTemplate,
|
|
28192
|
+
readBuiltinTemplate,
|
|
28193
|
+
readBuiltinTemplateSample,
|
|
27818
28194
|
hwpxToProfile,
|
|
27819
28195
|
isKnownFont,
|
|
27820
28196
|
unknownFontWarnings,
|
|
@@ -27841,4 +28217,4 @@ export {
|
|
|
27841
28217
|
parseHwpml,
|
|
27842
28218
|
fillForm
|
|
27843
28219
|
};
|
|
27844
|
-
//# sourceMappingURL=chunk-
|
|
28220
|
+
//# sourceMappingURL=chunk-DRZUJ2RK.js.map
|