kordoc 4.2.8 → 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/{-SCTT2TBZ.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-IJUAWDOS.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-JP5MNHUK.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-WP4VD5T7.js → chunk-7MFO4TKP.js} +4 -3
- package/dist/chunk-7MFO4TKP.js.map +1 -0
- package/dist/{chunk-CELJIGBQ.cjs → chunk-AL34DGS5.cjs} +6 -6
- package/dist/chunk-AL34DGS5.cjs.map +1 -0
- package/dist/{chunk-FS3XQ3ZQ.js → chunk-ARXNKC5D.js} +3 -3
- package/dist/chunk-ARXNKC5D.js.map +1 -0
- package/dist/{chunk-2BTKLDNX.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-7GEBDS6B.js → chunk-DRQUAYNT.js} +4 -3
- package/dist/chunk-DRQUAYNT.js.map +1 -0
- package/dist/{chunk-YNAPK4H6.js → chunk-DRZUJ2RK.js} +1521 -1144
- package/dist/chunk-DRZUJ2RK.js.map +1 -0
- package/dist/{chunk-GKSFNCVP.js → chunk-EFVAY5VE.js} +71 -29
- package/dist/chunk-EFVAY5VE.js.map +1 -0
- package/dist/{chunk-UKEZ46VL.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-4FTXKIGT.js → chunk-GSMPTZC7.js} +4 -4
- package/dist/chunk-GSMPTZC7.js.map +1 -0
- package/dist/{chunk-OQNFGKRY.cjs → chunk-JFV7QT5T.cjs} +4 -3
- package/dist/chunk-JFV7QT5T.cjs.map +1 -0
- package/dist/{chunk-ROKHZE76.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-55QIELGV.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-COHHXQSP.js → chunk-SPKBIFSK.js} +2 -2
- package/dist/chunk-SPKBIFSK.js.map +1 -0
- package/dist/{chunk-QZCAIFUW.js → chunk-US4WFSIQ.js} +4 -4
- package/dist/chunk-US4WFSIQ.js.map +1 -0
- package/dist/{chunk-EOVJ27WC.js → chunk-VXMLENCV.js} +15 -11
- 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-URKX77GB.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-ANSXR5KJ.js → image-ocr-G2LYTEUH.js} +8 -6
- package/dist/image-ocr-G2LYTEUH.js.map +1 -0
- package/dist/{image-ocr-ORATZW73.js → image-ocr-R44RYDBU.js} +10 -8
- package/dist/image-ocr-R44RYDBU.js.map +1 -0
- package/dist/{image-ocr-GHQ6YKJH.cjs → image-ocr-XEJFXH6M.cjs} +13 -11
- package/dist/image-ocr-XEJFXH6M.cjs.map +1 -0
- package/dist/index.cjs +2017 -1868
- 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 +2139 -1990
- 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-BPVTYM5K.js → parser-2G245MMN.js} +6 -6
- package/dist/parser-2G245MMN.js.map +1 -0
- package/dist/{parser-GZDOZCHM.js → parser-W3POOYQQ.js} +8 -8
- package/dist/parser-W3POOYQQ.js.map +1 -0
- package/dist/{parser-KTNIUF2N.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-I2TH44EO.js → profile-io-72R3B7KO.js} +3 -3
- package/dist/{rasterize-QBA43YAQ.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-7V6X7LHV.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-2BTKLDNX.cjs.map +0 -1
- package/dist/chunk-4FTXKIGT.js.map +0 -1
- package/dist/chunk-55QIELGV.js.map +0 -1
- package/dist/chunk-5RPYBC2Q.js.map +0 -1
- package/dist/chunk-7FCNOKOR.js.map +0 -1
- package/dist/chunk-7GEBDS6B.js.map +0 -1
- package/dist/chunk-CELJIGBQ.cjs.map +0 -1
- package/dist/chunk-COHHXQSP.js.map +0 -1
- package/dist/chunk-DZIXKL7E.js.map +0 -1
- package/dist/chunk-EOVJ27WC.js.map +0 -1
- package/dist/chunk-FS3XQ3ZQ.js.map +0 -1
- package/dist/chunk-GEPINOL6.js.map +0 -1
- package/dist/chunk-GKSFNCVP.js.map +0 -1
- package/dist/chunk-H7OWEYH2.cjs.map +0 -1
- package/dist/chunk-IJUAWDOS.js.map +0 -1
- package/dist/chunk-J2RZ444H.js.map +0 -1
- package/dist/chunk-JP5MNHUK.js.map +0 -1
- package/dist/chunk-LM7C6HI2.js.map +0 -1
- package/dist/chunk-NNDVFNMJ.js.map +0 -1
- package/dist/chunk-OQNFGKRY.cjs.map +0 -1
- package/dist/chunk-QZCAIFUW.js.map +0 -1
- package/dist/chunk-ROKHZE76.cjs.map +0 -1
- package/dist/chunk-TCJRGFYY.js +0 -280
- package/dist/chunk-TCJRGFYY.js.map +0 -1
- package/dist/chunk-UKEZ46VL.js.map +0 -1
- package/dist/chunk-URKX77GB.js.map +0 -1
- package/dist/chunk-WP4VD5T7.js.map +0 -1
- package/dist/chunk-YNAPK4H6.js.map +0 -1
- package/dist/chunks-KKYJDJT7.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-ANSXR5KJ.js.map +0 -1
- package/dist/image-ocr-GHQ6YKJH.cjs.map +0 -1
- package/dist/image-ocr-ORATZW73.js.map +0 -1
- package/dist/parser-BPVTYM5K.js.map +0 -1
- package/dist/parser-GZDOZCHM.js.map +0 -1
- package/dist/parser-KTNIUF2N.cjs.map +0 -1
- package/dist/pdf-ocr-6XKLXZTF.cjs +0 -13
- package/dist/pdf-ocr-6XKLXZTF.cjs.map +0 -1
- package/dist/pdf-ocr-HWPNMKKC.js +0 -12
- package/dist/pdf-ocr-W2PA2K3U.js +0 -15
- package/dist/rasterize-QBA43YAQ.js.map +0 -1
- package/dist/render-MDJDKKNH.js +0 -10
- package/dist/seal-Z52JR2GX.js +0 -10
- package/dist/seal-Z52JR2GX.js.map +0 -1
- package/dist/setup-GUQT5ELJ.js.map +0 -1
- package/dist/watch-7V6X7LHV.js.map +0 -1
- /package/dist/{-SCTT2TBZ.js.map → -DYHT3HWH.js.map} +0 -0
- /package/dist/{chunks-KKYJDJT7.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-HWPNMKKC.js.map → pdf-ocr-U7MEGNBZ.js.map} +0 -0
- /package/dist/{pdf-ocr-W2PA2K3U.js.map → profile-io-72R3B7KO.js.map} +0 -0
- /package/dist/{profile-io-I2TH44EO.js.map → redact-W5K3ILQH.js.map} +0 -0
- /package/dist/{redact-7ILEAUTS.js.map → render-6OW6U4L4.js.map} +0 -0
- /package/dist/{render-MDJDKKNH.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
|
|
@@ -17066,9 +16827,8 @@ function decodeJohab(ch) {
|
|
|
17066
16827
|
const jongIdx = ch & 31;
|
|
17067
16828
|
const cho = CHO_MAP[choIdx];
|
|
17068
16829
|
const jung = JUNG_MAP[jungIdx];
|
|
17069
|
-
|
|
17070
|
-
if (cho !== -1 && jung !== -1) {
|
|
17071
|
-
if (jong === -1) jong = 0;
|
|
16830
|
+
const jong = JONG_MAP[jongIdx];
|
|
16831
|
+
if (cho !== -1 && jung !== -1 && jong !== -1) {
|
|
17072
16832
|
return 44032 + cho * 588 + jung * 28 + jong;
|
|
17073
16833
|
}
|
|
17074
16834
|
const hit = lookupSymbol(ch);
|
|
@@ -17237,8 +16997,6 @@ var LINE_INFO_SIZE = 14;
|
|
|
17237
16997
|
var INLINE_CHAR_SHAPE_SIZE = 31;
|
|
17238
16998
|
var SIMPLE_CTRL = /* @__PURE__ */ new Map([
|
|
17239
16999
|
[9, { extraBytes: 6, extraHchar: 3, emit: " " }],
|
|
17240
|
-
[7, { extraBytes: 6, extraHchar: 3, emit: "\uFFFC" }],
|
|
17241
|
-
[8, { extraBytes: 6, extraHchar: 3, emit: "\uFFFC" }],
|
|
17242
17000
|
[18, { extraBytes: 6, extraHchar: 3, emit: " " }],
|
|
17243
17001
|
// AutoNumber → 공백 (HWP5 패턴)
|
|
17244
17002
|
[19, { extraBytes: 6, extraHchar: 3, emit: "\uFFFC" }],
|
|
@@ -17247,7 +17005,8 @@ var SIMPLE_CTRL = /* @__PURE__ */ new Map([
|
|
|
17247
17005
|
[22, { extraBytes: 22, extraHchar: 11, emit: "\uFFFC" }],
|
|
17248
17006
|
[23, { extraBytes: 8, extraHchar: 4, emit: "\uFFFC" }],
|
|
17249
17007
|
[24, { extraBytes: 4, extraHchar: 2, emit: "-" }],
|
|
17250
|
-
[25, { extraBytes: 4, extraHchar: 2, emit:
|
|
17008
|
+
[25, { extraBytes: 4, extraHchar: 2, emit: null }],
|
|
17009
|
+
// 차례 표식 — 비가시 (rhwp #2765)
|
|
17251
17010
|
[26, { extraBytes: 244, extraHchar: 122, emit: "\uFFFC" }],
|
|
17252
17011
|
[28, { extraBytes: 62, extraHchar: 31, emit: "\uFFFC" }],
|
|
17253
17012
|
[30, { extraBytes: 2, extraHchar: 1, emit: "\xA0" }],
|
|
@@ -17257,9 +17016,7 @@ function parseHwp3Document(buffer, _options) {
|
|
|
17257
17016
|
const headReader = new Reader(Buffer.from(buffer));
|
|
17258
17017
|
const header = readHeader(headReader);
|
|
17259
17018
|
if (header.encrypted !== 0) {
|
|
17260
|
-
|
|
17261
|
-
e.code = "ENCRYPTED";
|
|
17262
|
-
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.");
|
|
17263
17020
|
}
|
|
17264
17021
|
headReader.skip(header.infoBlockLength);
|
|
17265
17022
|
const tail = headReader.readToEnd();
|
|
@@ -17270,10 +17027,10 @@ function parseHwp3Document(buffer, _options) {
|
|
|
17270
17027
|
body = inflateRawSync3(tail, { maxOutputLength: MAX_DECOMPRESS_SIZE3 });
|
|
17271
17028
|
} catch (err) {
|
|
17272
17029
|
if (err?.code === "ERR_BUFFER_TOO_LARGE") {
|
|
17273
|
-
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`);
|
|
17274
17031
|
}
|
|
17275
17032
|
const msg2 = err instanceof Error ? err.message : String(err);
|
|
17276
|
-
throw new
|
|
17033
|
+
throw new KordocError(`HWP3 \uC555\uCD95 \uD574\uC81C \uC2E4\uD328: ${msg2}`);
|
|
17277
17034
|
}
|
|
17278
17035
|
} else {
|
|
17279
17036
|
body = tail;
|
|
@@ -17391,9 +17148,6 @@ function parseCharStream(reader, charCount, ctx) {
|
|
|
17391
17148
|
case 11:
|
|
17392
17149
|
parsePicture(reader, ctx);
|
|
17393
17150
|
break;
|
|
17394
|
-
case 12:
|
|
17395
|
-
reader.skip(84);
|
|
17396
|
-
break;
|
|
17397
17151
|
case 14:
|
|
17398
17152
|
reader.skip(84);
|
|
17399
17153
|
break;
|
|
@@ -17418,6 +17172,12 @@ function parseCharStream(reader, charCount, ctx) {
|
|
|
17418
17172
|
case 6:
|
|
17419
17173
|
reader.skip(34);
|
|
17420
17174
|
break;
|
|
17175
|
+
case 7:
|
|
17176
|
+
reader.skip(76);
|
|
17177
|
+
break;
|
|
17178
|
+
case 8:
|
|
17179
|
+
reader.skip(88);
|
|
17180
|
+
break;
|
|
17421
17181
|
case 29:
|
|
17422
17182
|
if (headerVal1 < 1e6) reader.skip(headerVal1);
|
|
17423
17183
|
break;
|
|
@@ -17471,7 +17231,7 @@ function isDistributionSentinel(markdown) {
|
|
|
17471
17231
|
}
|
|
17472
17232
|
|
|
17473
17233
|
// src/xlsx/parser.ts
|
|
17474
|
-
import
|
|
17234
|
+
import JSZip4 from "jszip";
|
|
17475
17235
|
import { DOMParser } from "@xmldom/xmldom";
|
|
17476
17236
|
var MAX_SHEETS = 100;
|
|
17477
17237
|
var MAX_DECOMPRESS_SIZE4 = 100 * 1024 * 1024;
|
|
@@ -17756,7 +17516,7 @@ function sheetToBlocks(sheetName, grid, merges, maxRow, maxCol, sheetIndex, keep
|
|
|
17756
17516
|
}
|
|
17757
17517
|
async function parseXlsxDocument(buffer, options) {
|
|
17758
17518
|
precheckZipSize(buffer, MAX_DECOMPRESS_SIZE4);
|
|
17759
|
-
const zip = await
|
|
17519
|
+
const zip = await JSZip4.loadAsync(buffer);
|
|
17760
17520
|
const warnings = [];
|
|
17761
17521
|
const workbookFile = zip.file("xl/workbook.xml");
|
|
17762
17522
|
if (!workbookFile) {
|
|
@@ -17777,6 +17537,10 @@ async function parseXlsxDocument(buffer, options) {
|
|
|
17777
17537
|
try {
|
|
17778
17538
|
dateXfs = parseStyleDateXfs(await stylesFile.async("text"));
|
|
17779
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
|
+
});
|
|
17780
17544
|
}
|
|
17781
17545
|
}
|
|
17782
17546
|
let relsMap = /* @__PURE__ */ new Map();
|
|
@@ -18473,17 +18237,7 @@ async function parseXlsDocument(buffer, options) {
|
|
|
18473
18237
|
const globals = processGlobals(records);
|
|
18474
18238
|
const warnings = [];
|
|
18475
18239
|
if (globals.encrypted) {
|
|
18476
|
-
|
|
18477
|
-
markdown: "",
|
|
18478
|
-
blocks: [],
|
|
18479
|
-
metadata: { pageCount: globals.sheets.length },
|
|
18480
|
-
warnings: [
|
|
18481
|
-
{
|
|
18482
|
-
message: "XLS \uD30C\uC77C\uC774 \uC554\uD638\uD654\uB418\uC5B4 \uC788\uC5B4 \uD30C\uC2F1\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4",
|
|
18483
|
-
code: "PARTIAL_PARSE"
|
|
18484
|
-
}
|
|
18485
|
-
]
|
|
18486
|
-
};
|
|
18240
|
+
throw new KordocError("XLS \uD30C\uC77C\uC774 \uC554\uD638\uD654\uB418\uC5B4 \uC788\uC5B4 \uD30C\uC2F1\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4");
|
|
18487
18241
|
}
|
|
18488
18242
|
const convertNum = globals.dateXfs.size > 0 ? (n, ixfe) => {
|
|
18489
18243
|
const kind = globals.dateXfs.get(ixfe);
|
|
@@ -18542,41 +18296,13 @@ async function parseXlsDocument(buffer, options) {
|
|
|
18542
18296
|
}
|
|
18543
18297
|
|
|
18544
18298
|
// src/docx/parser.ts
|
|
18545
|
-
import
|
|
18299
|
+
import JSZip5 from "jszip";
|
|
18546
18300
|
import { DOMParser as DOMParser2 } from "@xmldom/xmldom";
|
|
18547
18301
|
|
|
18548
18302
|
// src/docx/equation.ts
|
|
18549
|
-
function lname(el) {
|
|
18550
|
-
return el.localName || el.tagName?.replace(/^[^:]+:/, "") || "";
|
|
18551
|
-
}
|
|
18552
|
-
function kids(parent, name) {
|
|
18553
|
-
const out = [];
|
|
18554
|
-
const nodes = parent.childNodes;
|
|
18555
|
-
if (!nodes) return out;
|
|
18556
|
-
for (let i = 0; i < nodes.length; i++) {
|
|
18557
|
-
const n = nodes[i];
|
|
18558
|
-
if (n.nodeType !== 1) continue;
|
|
18559
|
-
const el = n;
|
|
18560
|
-
if (lname(el) === name) out.push(el);
|
|
18561
|
-
}
|
|
18562
|
-
return out;
|
|
18563
|
-
}
|
|
18564
|
-
function firstKid(parent, name) {
|
|
18565
|
-
const list = kids(parent, name);
|
|
18566
|
-
return list[0] ?? null;
|
|
18567
|
-
}
|
|
18568
|
-
function eachChild(parent) {
|
|
18569
|
-
const out = [];
|
|
18570
|
-
const nodes = parent.childNodes;
|
|
18571
|
-
if (!nodes) return out;
|
|
18572
|
-
for (let i = 0; i < nodes.length; i++) {
|
|
18573
|
-
if (nodes[i].nodeType === 1) out.push(nodes[i]);
|
|
18574
|
-
}
|
|
18575
|
-
return out;
|
|
18576
|
-
}
|
|
18577
18303
|
function runToLatex(r) {
|
|
18578
18304
|
let out = "";
|
|
18579
|
-
for (const t of
|
|
18305
|
+
for (const t of childrenByLocalName(r, "t")) out += t.textContent ?? "";
|
|
18580
18306
|
return out;
|
|
18581
18307
|
}
|
|
18582
18308
|
var FUNC_NAMES = /* @__PURE__ */ new Set([
|
|
@@ -18697,13 +18423,13 @@ function grp(body) {
|
|
|
18697
18423
|
}
|
|
18698
18424
|
function childrenToLatex(parent) {
|
|
18699
18425
|
let out = "";
|
|
18700
|
-
for (const ch of
|
|
18426
|
+
for (const ch of elementChildren(parent)) {
|
|
18701
18427
|
out += nodeToLatex(ch);
|
|
18702
18428
|
}
|
|
18703
18429
|
return out;
|
|
18704
18430
|
}
|
|
18705
18431
|
function nodeToLatex(el) {
|
|
18706
|
-
const tag =
|
|
18432
|
+
const tag = localName(el);
|
|
18707
18433
|
switch (tag) {
|
|
18708
18434
|
case "r":
|
|
18709
18435
|
return runToLatex(el);
|
|
@@ -18719,33 +18445,33 @@ function nodeToLatex(el) {
|
|
|
18719
18445
|
return childrenToLatex(el);
|
|
18720
18446
|
// 분수
|
|
18721
18447
|
case "f": {
|
|
18722
|
-
const n =
|
|
18723
|
-
const d =
|
|
18448
|
+
const n = findChildByLocalName(el, "num");
|
|
18449
|
+
const d = findChildByLocalName(el, "den");
|
|
18724
18450
|
const num2 = n ? childrenToLatex(n) : "";
|
|
18725
18451
|
const den = d ? childrenToLatex(d) : "";
|
|
18726
18452
|
return "\\frac" + grp(num2) + grp(den);
|
|
18727
18453
|
}
|
|
18728
18454
|
// 첨자
|
|
18729
18455
|
case "sSup": {
|
|
18730
|
-
const e =
|
|
18731
|
-
const sup =
|
|
18456
|
+
const e = findChildByLocalName(el, "e");
|
|
18457
|
+
const sup = findChildByLocalName(el, "sup");
|
|
18732
18458
|
return grp(e ? childrenToLatex(e) : "") + "^" + grp(sup ? childrenToLatex(sup) : "");
|
|
18733
18459
|
}
|
|
18734
18460
|
case "sSub": {
|
|
18735
|
-
const e =
|
|
18736
|
-
const sub =
|
|
18461
|
+
const e = findChildByLocalName(el, "e");
|
|
18462
|
+
const sub = findChildByLocalName(el, "sub");
|
|
18737
18463
|
return grp(e ? childrenToLatex(e) : "") + "_" + grp(sub ? childrenToLatex(sub) : "");
|
|
18738
18464
|
}
|
|
18739
18465
|
case "sSubSup": {
|
|
18740
|
-
const e =
|
|
18741
|
-
const sub =
|
|
18742
|
-
const sup =
|
|
18466
|
+
const e = findChildByLocalName(el, "e");
|
|
18467
|
+
const sub = findChildByLocalName(el, "sub");
|
|
18468
|
+
const sup = findChildByLocalName(el, "sup");
|
|
18743
18469
|
return grp(e ? childrenToLatex(e) : "") + "_" + grp(sub ? childrenToLatex(sub) : "") + "^" + grp(sup ? childrenToLatex(sup) : "");
|
|
18744
18470
|
}
|
|
18745
18471
|
case "sPre": {
|
|
18746
|
-
const sub =
|
|
18747
|
-
const sup =
|
|
18748
|
-
const e =
|
|
18472
|
+
const sub = findChildByLocalName(el, "sub");
|
|
18473
|
+
const sup = findChildByLocalName(el, "sup");
|
|
18474
|
+
const e = findChildByLocalName(el, "e");
|
|
18749
18475
|
const preSub = sub ? grp(childrenToLatex(sub)) : "{}";
|
|
18750
18476
|
const preSup = sup ? grp(childrenToLatex(sup)) : "{}";
|
|
18751
18477
|
const body = e ? childrenToLatex(e) : "";
|
|
@@ -18753,13 +18479,13 @@ function nodeToLatex(el) {
|
|
|
18753
18479
|
}
|
|
18754
18480
|
// 근호
|
|
18755
18481
|
case "rad": {
|
|
18756
|
-
const deg =
|
|
18757
|
-
const e =
|
|
18482
|
+
const deg = findChildByLocalName(el, "deg");
|
|
18483
|
+
const e = findChildByLocalName(el, "e");
|
|
18758
18484
|
const body = e ? childrenToLatex(e) : "";
|
|
18759
|
-
const radPr =
|
|
18485
|
+
const radPr = findChildByLocalName(el, "radPr");
|
|
18760
18486
|
let hide = false;
|
|
18761
18487
|
if (radPr) {
|
|
18762
|
-
const degHide =
|
|
18488
|
+
const degHide = findChildByLocalName(radPr, "degHide");
|
|
18763
18489
|
if (degHide) {
|
|
18764
18490
|
const val = degHide.getAttribute("m:val") ?? degHide.getAttribute("val");
|
|
18765
18491
|
hide = val === "1" || val === "on" || val === "true";
|
|
@@ -18770,13 +18496,13 @@ function nodeToLatex(el) {
|
|
|
18770
18496
|
}
|
|
18771
18497
|
// n-ary 연산자 (sum, prod, int, …)
|
|
18772
18498
|
case "nary": {
|
|
18773
|
-
const naryPr =
|
|
18499
|
+
const naryPr = findChildByLocalName(el, "naryPr");
|
|
18774
18500
|
let op = "\\int";
|
|
18775
18501
|
let subHide = false;
|
|
18776
18502
|
let supHide = false;
|
|
18777
18503
|
let limLoc = "";
|
|
18778
18504
|
if (naryPr) {
|
|
18779
|
-
const chr =
|
|
18505
|
+
const chr = findChildByLocalName(naryPr, "chr");
|
|
18780
18506
|
if (chr) {
|
|
18781
18507
|
const v = chr.getAttribute("m:val") ?? chr.getAttribute("val") ?? "";
|
|
18782
18508
|
if (v && NARY_MAP[v]) op = NARY_MAP[v];
|
|
@@ -18784,16 +18510,16 @@ function nodeToLatex(el) {
|
|
|
18784
18510
|
} else {
|
|
18785
18511
|
op = "\\int";
|
|
18786
18512
|
}
|
|
18787
|
-
const sh =
|
|
18788
|
-
const ph =
|
|
18513
|
+
const sh = findChildByLocalName(naryPr, "subHide");
|
|
18514
|
+
const ph = findChildByLocalName(naryPr, "supHide");
|
|
18789
18515
|
if (sh) subHide = onOffVal(sh.getAttribute("m:val") ?? sh.getAttribute("val"));
|
|
18790
18516
|
if (ph) supHide = onOffVal(ph.getAttribute("m:val") ?? ph.getAttribute("val"));
|
|
18791
|
-
const ll =
|
|
18517
|
+
const ll = findChildByLocalName(naryPr, "limLoc");
|
|
18792
18518
|
if (ll) limLoc = ll.getAttribute("m:val") ?? ll.getAttribute("val") ?? "";
|
|
18793
18519
|
}
|
|
18794
|
-
const sub =
|
|
18795
|
-
const sup =
|
|
18796
|
-
const e =
|
|
18520
|
+
const sub = findChildByLocalName(el, "sub");
|
|
18521
|
+
const sup = findChildByLocalName(el, "sup");
|
|
18522
|
+
const e = findChildByLocalName(el, "e");
|
|
18797
18523
|
const subStr = !subHide && sub ? childrenToLatex(sub) : "";
|
|
18798
18524
|
const supStr = !supHide && sup ? childrenToLatex(sup) : "";
|
|
18799
18525
|
const body = e ? childrenToLatex(e) : "";
|
|
@@ -18809,27 +18535,27 @@ function nodeToLatex(el) {
|
|
|
18809
18535
|
}
|
|
18810
18536
|
// 괄호 (delimiter)
|
|
18811
18537
|
case "d": {
|
|
18812
|
-
const dPr =
|
|
18538
|
+
const dPr = findChildByLocalName(el, "dPr");
|
|
18813
18539
|
let beg = "(";
|
|
18814
18540
|
let end = ")";
|
|
18815
18541
|
let sep = ",";
|
|
18816
18542
|
if (dPr) {
|
|
18817
|
-
const begChr =
|
|
18818
|
-
const endChr =
|
|
18819
|
-
const sepChr =
|
|
18543
|
+
const begChr = findChildByLocalName(dPr, "begChr");
|
|
18544
|
+
const endChr = findChildByLocalName(dPr, "endChr");
|
|
18545
|
+
const sepChr = findChildByLocalName(dPr, "sepChr");
|
|
18820
18546
|
if (begChr) beg = begChr.getAttribute("m:val") ?? begChr.getAttribute("val") ?? beg;
|
|
18821
18547
|
if (endChr) end = endChr.getAttribute("m:val") ?? endChr.getAttribute("val") ?? end;
|
|
18822
18548
|
if (sepChr) sep = sepChr.getAttribute("m:val") ?? sepChr.getAttribute("val") ?? sep;
|
|
18823
18549
|
}
|
|
18824
|
-
const items =
|
|
18550
|
+
const items = childrenByLocalName(el, "e").map(childrenToLatex);
|
|
18825
18551
|
const body = items.join(sep);
|
|
18826
18552
|
return "\\left" + mapDelim(beg, true) + body + "\\right" + mapDelim(end, false);
|
|
18827
18553
|
}
|
|
18828
18554
|
// 행렬
|
|
18829
18555
|
case "m": {
|
|
18830
18556
|
const rows = [];
|
|
18831
|
-
for (const mr of
|
|
18832
|
-
const cells =
|
|
18557
|
+
for (const mr of childrenByLocalName(el, "mr")) {
|
|
18558
|
+
const cells = childrenByLocalName(mr, "e").map(childrenToLatex);
|
|
18833
18559
|
rows.push(cells.join(" & "));
|
|
18834
18560
|
}
|
|
18835
18561
|
return "\\begin{matrix}" + rows.join(" \\\\ ") + "\\end{matrix}";
|
|
@@ -18839,8 +18565,8 @@ function nodeToLatex(el) {
|
|
|
18839
18565
|
return childrenToLatex(el);
|
|
18840
18566
|
// 함수 적용 (sin, cos, log …)
|
|
18841
18567
|
case "func": {
|
|
18842
|
-
const fn =
|
|
18843
|
-
const e =
|
|
18568
|
+
const fn = findChildByLocalName(el, "fName");
|
|
18569
|
+
const e = findChildByLocalName(el, "e");
|
|
18844
18570
|
const fnStr = fn ? childrenToLatex(fn).trim() : "";
|
|
18845
18571
|
const body = e ? childrenToLatex(e) : "";
|
|
18846
18572
|
const fnLatex = FUNC_NAMES.has(fnStr) ? "\\" + fnStr : fnStr;
|
|
@@ -18848,42 +18574,42 @@ function nodeToLatex(el) {
|
|
|
18848
18574
|
}
|
|
18849
18575
|
// 악센트 (hat/bar/vec/…)
|
|
18850
18576
|
case "acc": {
|
|
18851
|
-
const accPr =
|
|
18577
|
+
const accPr = findChildByLocalName(el, "accPr");
|
|
18852
18578
|
let chr = "";
|
|
18853
18579
|
if (accPr) {
|
|
18854
|
-
const chrEl =
|
|
18580
|
+
const chrEl = findChildByLocalName(accPr, "chr");
|
|
18855
18581
|
if (chrEl) chr = chrEl.getAttribute("m:val") ?? chrEl.getAttribute("val") ?? "";
|
|
18856
18582
|
}
|
|
18857
18583
|
if (!chr) chr = "\u0302";
|
|
18858
|
-
const e =
|
|
18584
|
+
const e = findChildByLocalName(el, "e");
|
|
18859
18585
|
const body = e ? childrenToLatex(e) : "";
|
|
18860
18586
|
const cmd = ACCENT_MAP[chr] ?? "\\hat";
|
|
18861
18587
|
return cmd + grp(body);
|
|
18862
18588
|
}
|
|
18863
18589
|
// bar (위/아래 줄)
|
|
18864
18590
|
case "bar": {
|
|
18865
|
-
const barPr =
|
|
18591
|
+
const barPr = findChildByLocalName(el, "barPr");
|
|
18866
18592
|
let pos = "top";
|
|
18867
18593
|
if (barPr) {
|
|
18868
|
-
const posEl =
|
|
18594
|
+
const posEl = findChildByLocalName(barPr, "pos");
|
|
18869
18595
|
if (posEl) pos = posEl.getAttribute("m:val") ?? posEl.getAttribute("val") ?? pos;
|
|
18870
18596
|
}
|
|
18871
|
-
const e =
|
|
18597
|
+
const e = findChildByLocalName(el, "e");
|
|
18872
18598
|
const body = e ? childrenToLatex(e) : "";
|
|
18873
18599
|
return (pos === "bot" ? "\\underline" : "\\overline") + grp(body);
|
|
18874
18600
|
}
|
|
18875
18601
|
// lim 위/아래
|
|
18876
18602
|
case "limLow": {
|
|
18877
|
-
const e =
|
|
18878
|
-
const lim =
|
|
18603
|
+
const e = findChildByLocalName(el, "e");
|
|
18604
|
+
const lim = findChildByLocalName(el, "lim");
|
|
18879
18605
|
const base = e ? childrenToLatex(e).trim() : "";
|
|
18880
18606
|
const below = lim ? childrenToLatex(lim) : "";
|
|
18881
18607
|
if (FUNC_NAMES.has(base)) return "\\" + base + "_" + grp(below);
|
|
18882
18608
|
return base + "_" + grp(below);
|
|
18883
18609
|
}
|
|
18884
18610
|
case "limUpp": {
|
|
18885
|
-
const e =
|
|
18886
|
-
const lim =
|
|
18611
|
+
const e = findChildByLocalName(el, "e");
|
|
18612
|
+
const lim = findChildByLocalName(el, "lim");
|
|
18887
18613
|
const base = e ? childrenToLatex(e).trim() : "";
|
|
18888
18614
|
const above = lim ? childrenToLatex(lim) : "";
|
|
18889
18615
|
if (FUNC_NAMES.has(base)) return "\\" + base + "^" + grp(above);
|
|
@@ -18891,7 +18617,7 @@ function nodeToLatex(el) {
|
|
|
18891
18617
|
}
|
|
18892
18618
|
// group character (over/underset 비슷)
|
|
18893
18619
|
case "groupChr":
|
|
18894
|
-
return childrenToLatex(
|
|
18620
|
+
return childrenToLatex(findChildByLocalName(el, "e") ?? el);
|
|
18895
18621
|
// box/borderBox/phantom/eqArr/… 는 자식 본문만 유지
|
|
18896
18622
|
case "borderBox":
|
|
18897
18623
|
case "phant":
|
|
@@ -18921,7 +18647,7 @@ function nodeToLatex(el) {
|
|
|
18921
18647
|
}
|
|
18922
18648
|
}
|
|
18923
18649
|
function isOmmlRoot(el) {
|
|
18924
|
-
const t =
|
|
18650
|
+
const t = localName(el);
|
|
18925
18651
|
return t === "oMath" || t === "oMathPara";
|
|
18926
18652
|
}
|
|
18927
18653
|
function ommlElementToLatex(el) {
|
|
@@ -18930,7 +18656,7 @@ function ommlElementToLatex(el) {
|
|
|
18930
18656
|
return raw.replace(/\s+/g, " ").trim();
|
|
18931
18657
|
}
|
|
18932
18658
|
function isDisplayMath(el) {
|
|
18933
|
-
return
|
|
18659
|
+
return localName(el) === "oMathPara";
|
|
18934
18660
|
}
|
|
18935
18661
|
|
|
18936
18662
|
// src/docx/parser.ts
|
|
@@ -19445,7 +19171,7 @@ function emitParagraphImages(p, imageMap, linked, out) {
|
|
|
19445
19171
|
}
|
|
19446
19172
|
async function parseDocxDocument(buffer, options) {
|
|
19447
19173
|
precheckZipSize(buffer, MAX_DECOMPRESS_SIZE5);
|
|
19448
|
-
const zip = await
|
|
19174
|
+
const zip = await JSZip5.loadAsync(buffer);
|
|
19449
19175
|
const warnings = [];
|
|
19450
19176
|
const docFile = zip.file("word/document.xml");
|
|
19451
19177
|
if (!docFile) {
|
|
@@ -19560,13 +19286,12 @@ async function parseDocxDocument(buffer, options) {
|
|
|
19560
19286
|
|
|
19561
19287
|
// src/hwpml/parser.ts
|
|
19562
19288
|
import { DOMParser as DOMParser3 } from "@xmldom/xmldom";
|
|
19563
|
-
var MAX_XML_DEPTH2 = 200;
|
|
19564
19289
|
var MAX_TABLE_ROWS = 5e3;
|
|
19565
19290
|
var MAX_TABLE_COLS = 500;
|
|
19566
19291
|
var MAX_HWPML_BYTES = 50 * 1024 * 1024;
|
|
19567
19292
|
function parseHwpmlDocument(buffer, options) {
|
|
19568
19293
|
if (buffer.byteLength > MAX_HWPML_BYTES) {
|
|
19569
|
-
throw new
|
|
19294
|
+
throw new KordocError(`HWPML \uD30C\uC77C \uD06C\uAE30 \uCD08\uACFC (${(buffer.byteLength / 1024 / 1024).toFixed(1)}MB > 50MB)`);
|
|
19570
19295
|
}
|
|
19571
19296
|
const text = new TextDecoder("utf-8").decode(buffer).replace(/^\uFEFF/, "");
|
|
19572
19297
|
const normalized = text.replace(/ /g, " ");
|
|
@@ -19583,17 +19308,17 @@ function parseHwpmlDocument(buffer, options) {
|
|
|
19583
19308
|
}
|
|
19584
19309
|
const root = doc.documentElement;
|
|
19585
19310
|
const metadata = {};
|
|
19586
|
-
const docSummary =
|
|
19311
|
+
const docSummary = findChildByLocalName(root, "DOCSUMMARY");
|
|
19587
19312
|
if (docSummary) {
|
|
19588
|
-
const title =
|
|
19589
|
-
const author =
|
|
19590
|
-
const date =
|
|
19591
|
-
if (title) metadata.title =
|
|
19592
|
-
if (author) metadata.author =
|
|
19593
|
-
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;
|
|
19594
19319
|
}
|
|
19595
19320
|
const paraShapeMap = buildParaShapeMap(root);
|
|
19596
|
-
const body =
|
|
19321
|
+
const body = findChildByLocalName(root, "BODY");
|
|
19597
19322
|
if (!body) {
|
|
19598
19323
|
return { markdown: "", blocks: [], metadata, warnings };
|
|
19599
19324
|
}
|
|
@@ -19621,11 +19346,11 @@ function parseHwpmlDocument(buffer, options) {
|
|
|
19621
19346
|
}
|
|
19622
19347
|
function buildParaShapeMap(root) {
|
|
19623
19348
|
const map = /* @__PURE__ */ new Map();
|
|
19624
|
-
const head =
|
|
19349
|
+
const head = findChildByLocalName(root, "HEAD");
|
|
19625
19350
|
if (!head) return map;
|
|
19626
|
-
const mappingTable =
|
|
19351
|
+
const mappingTable = findChildByLocalName(head, "MAPPINGTABLE");
|
|
19627
19352
|
if (!mappingTable) return map;
|
|
19628
|
-
const paraShapeList =
|
|
19353
|
+
const paraShapeList = findChildByLocalName(mappingTable, "PARASHAPELIST");
|
|
19629
19354
|
if (!paraShapeList) return map;
|
|
19630
19355
|
const children = paraShapeList.childNodes;
|
|
19631
19356
|
for (let i = 0; i < children.length; i++) {
|
|
@@ -19647,7 +19372,7 @@ function parseSection2(section, blocks, paraShapeMap, sectionNum, warnings, keep
|
|
|
19647
19372
|
walkContent(section, blocks, paraShapeMap, sectionNum, warnings, false, keep);
|
|
19648
19373
|
}
|
|
19649
19374
|
function walkContent(node, blocks, paraShapeMap, sectionNum, warnings, inHeaderFooter, keep, depth = 0) {
|
|
19650
|
-
if (depth >
|
|
19375
|
+
if (depth > MAX_XML_DEPTH) return;
|
|
19651
19376
|
const children = node.childNodes;
|
|
19652
19377
|
for (let i = 0; i < children.length; i++) {
|
|
19653
19378
|
const el = children[i];
|
|
@@ -19677,7 +19402,7 @@ function walkContent(node, blocks, paraShapeMap, sectionNum, warnings, inHeaderF
|
|
|
19677
19402
|
}
|
|
19678
19403
|
}
|
|
19679
19404
|
function walkTablesInP(node, blocks, paraShapeMap, sectionNum, warnings, keep, depth = 0) {
|
|
19680
|
-
if (depth >
|
|
19405
|
+
if (depth > MAX_XML_DEPTH) return;
|
|
19681
19406
|
const children = node.childNodes;
|
|
19682
19407
|
for (let i = 0; i < children.length; i++) {
|
|
19683
19408
|
const el = children[i];
|
|
@@ -19708,14 +19433,14 @@ function extractParagraphText(p) {
|
|
|
19708
19433
|
return parts.join("").trim();
|
|
19709
19434
|
}
|
|
19710
19435
|
function collectCharText(node, parts, depth = 0) {
|
|
19711
|
-
if (depth >
|
|
19436
|
+
if (depth > MAX_XML_DEPTH) return;
|
|
19712
19437
|
const children = node.childNodes;
|
|
19713
19438
|
for (let i = 0; i < children.length; i++) {
|
|
19714
19439
|
const el = children[i];
|
|
19715
19440
|
if (el.nodeType !== 1) continue;
|
|
19716
19441
|
const tag = localName(el);
|
|
19717
19442
|
if (tag === "CHAR") {
|
|
19718
|
-
const t =
|
|
19443
|
+
const t = rawTextContent(el);
|
|
19719
19444
|
if (t) parts.push(t);
|
|
19720
19445
|
} else if (tag === "TABLE" || tag === "PICTURE" || tag === "SHAPEOBJECT") {
|
|
19721
19446
|
} else if (tag === "AUTONUM") {
|
|
@@ -19724,14 +19449,28 @@ function collectCharText(node, parts, depth = 0) {
|
|
|
19724
19449
|
}
|
|
19725
19450
|
}
|
|
19726
19451
|
}
|
|
19727
|
-
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) {
|
|
19728
19466
|
const cells = [];
|
|
19467
|
+
const structured = [];
|
|
19729
19468
|
const rowCount = parseInt(el.getAttribute("RowCount") ?? "0", 10);
|
|
19730
19469
|
const colCount = parseInt(el.getAttribute("ColCount") ?? "0", 10);
|
|
19731
|
-
if (isNaN(rowCount) || isNaN(colCount) || rowCount === 0 || colCount === 0) return;
|
|
19470
|
+
if (isNaN(rowCount) || isNaN(colCount) || rowCount === 0 || colCount === 0) return null;
|
|
19732
19471
|
if (rowCount > MAX_TABLE_ROWS || colCount > MAX_TABLE_COLS) {
|
|
19733
19472
|
warnings.push({ message: `\uD14C\uC774\uBE14 \uD06C\uAE30 \uCD08\uACFC (${rowCount}x${colCount}) \u2014 \uC2A4\uD0B5`, code: "TRUNCATED_TABLE" });
|
|
19734
|
-
return;
|
|
19473
|
+
return null;
|
|
19735
19474
|
}
|
|
19736
19475
|
const children = el.childNodes;
|
|
19737
19476
|
for (let i = 0; i < children.length; i++) {
|
|
@@ -19746,28 +19485,106 @@ function parseTable2(el, blocks, paraShapeMap, sectionNum, warnings, keep) {
|
|
|
19746
19485
|
const colSpan = Math.min(Math.max(1, parseInt(cellEl.getAttribute("ColSpan") ?? "1", 10) || 1), MAX_TABLE_COLS);
|
|
19747
19486
|
const rowSpan = Math.min(Math.max(1, parseInt(cellEl.getAttribute("RowSpan") ?? "1", 10) || 1), MAX_TABLE_ROWS);
|
|
19748
19487
|
const cellText = extractCellText(cellEl);
|
|
19749
|
-
|
|
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
|
+
}
|
|
19750
19495
|
}
|
|
19751
19496
|
}
|
|
19752
|
-
if (cells.length === 0) return;
|
|
19497
|
+
if (cells.length === 0) return null;
|
|
19753
19498
|
const cellRows = Array.from({ length: rowCount }, () => []);
|
|
19754
19499
|
for (const cell2 of cells) {
|
|
19755
19500
|
const r = Math.min(Math.max(cell2.rowAddr ?? 0, 0), rowCount - 1);
|
|
19756
19501
|
cellRows[r].push(cell2);
|
|
19757
19502
|
}
|
|
19758
19503
|
const table2 = buildTable(cellRows, { keepAnchoredEmptyCols: keep });
|
|
19759
|
-
|
|
19760
|
-
|
|
19761
|
-
|
|
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
|
+
}
|
|
19762
19523
|
}
|
|
19763
|
-
|
|
19764
|
-
|
|
19765
|
-
|
|
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;
|
|
19766
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 });
|
|
19767
19584
|
}
|
|
19768
19585
|
function extractShapeCaption(tableEl) {
|
|
19769
|
-
const shape =
|
|
19770
|
-
const caption = shape &&
|
|
19586
|
+
const shape = findChildByLocalName(tableEl, "SHAPEOBJECT");
|
|
19587
|
+
const caption = shape && findChildByLocalName(shape, "CAPTION");
|
|
19771
19588
|
if (!caption) return { text: "", before: false };
|
|
19772
19589
|
const parts = [];
|
|
19773
19590
|
collectCellText2(caption, parts, 0);
|
|
@@ -19812,30 +19629,6 @@ function collectNestedTableText(node, parts, depth) {
|
|
|
19812
19629
|
collectNestedTableText(el, parts, depth + 1);
|
|
19813
19630
|
}
|
|
19814
19631
|
}
|
|
19815
|
-
function localName(el) {
|
|
19816
|
-
return (el.tagName || el.localName || "").replace(/^[^:]+:/, "");
|
|
19817
|
-
}
|
|
19818
|
-
function findChild(parent, tag) {
|
|
19819
|
-
const children = parent.childNodes;
|
|
19820
|
-
for (let i = 0; i < children.length; i++) {
|
|
19821
|
-
const el = children[i];
|
|
19822
|
-
if (el.nodeType === 1 && localName(el) === tag) return el;
|
|
19823
|
-
}
|
|
19824
|
-
return null;
|
|
19825
|
-
}
|
|
19826
|
-
function textContent(el) {
|
|
19827
|
-
const children = el.childNodes;
|
|
19828
|
-
const parts = [];
|
|
19829
|
-
for (let i = 0; i < children.length; i++) {
|
|
19830
|
-
const node = children[i];
|
|
19831
|
-
if (node.nodeType === 3) {
|
|
19832
|
-
parts.push(node.nodeValue || "");
|
|
19833
|
-
} else if (node.nodeType === 1) {
|
|
19834
|
-
parts.push(textContent(node));
|
|
19835
|
-
}
|
|
19836
|
-
}
|
|
19837
|
-
return parts.join("");
|
|
19838
|
-
}
|
|
19839
19632
|
function countSections(body) {
|
|
19840
19633
|
let count = 0;
|
|
19841
19634
|
const children = body.childNodes;
|
|
@@ -20585,10 +20378,136 @@ function fillInlineFields(text, values, filled, matchedLabels, blockedLabels) {
|
|
|
20585
20378
|
}
|
|
20586
20379
|
|
|
20587
20380
|
// src/form/filler-hwpx.ts
|
|
20588
|
-
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
|
|
20589
20508
|
async function fillHwpx(hwpxBuffer, values, blockedLabels) {
|
|
20590
20509
|
const u8 = new Uint8Array(hwpxBuffer);
|
|
20591
|
-
const zip = await
|
|
20510
|
+
const zip = await JSZip7.loadAsync(hwpxBuffer);
|
|
20592
20511
|
const sectionPaths = Object.keys(zip.files).filter((name) => /[Ss]ection\d+\.xml$/i.test(name)).sort();
|
|
20593
20512
|
if (sectionPaths.length === 0) {
|
|
20594
20513
|
throw new KordocError("HWPX\uC5D0\uC11C \uC139\uC158 \uD30C\uC77C\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4");
|
|
@@ -20602,8 +20521,23 @@ async function fillHwpx(hwpxBuffer, values, blockedLabels) {
|
|
|
20602
20521
|
const succeededKeys = /* @__PURE__ */ new Set();
|
|
20603
20522
|
const replacements = /* @__PURE__ */ new Map();
|
|
20604
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
|
+
}
|
|
20605
20539
|
for (let si = 0; si < sectionPaths.length; si++) {
|
|
20606
|
-
const xml =
|
|
20540
|
+
const xml = sectionXmls.get(sectionPaths[si]);
|
|
20607
20541
|
const scan = scanSectionXml(xml, si);
|
|
20608
20542
|
const ledger = /* @__PURE__ */ new Map();
|
|
20609
20543
|
const led = (p) => {
|
|
@@ -20785,94 +20719,460 @@ async function fillHwpx(hwpxBuffer, values, blockedLabels) {
|
|
|
20785
20719
|
}
|
|
20786
20720
|
}
|
|
20787
20721
|
}
|
|
20788
|
-
for (const para2 of [...scan.bodyParagraphs, ...scan.excludedParagraphs]) {
|
|
20789
|
-
const existing = ledger.get(para2);
|
|
20790
|
-
if (existing?.fullText !== void 0) continue;
|
|
20791
|
-
const text = matchText(para2);
|
|
20792
|
-
const segments = scanInlineSegments(text);
|
|
20793
|
-
const matches = segments.map((seg) => matchInlineSegment(seg, cursor, blockedLabels));
|
|
20794
|
-
for (let i = 0; i < segments.length; i++) {
|
|
20795
|
-
const seg = segments[i];
|
|
20796
|
-
const matched = matches[i];
|
|
20797
|
-
if (matched === void 0) continue;
|
|
20798
|
-
const matchKey = matched.key;
|
|
20799
|
-
const newValue = cursor.consume(matchKey);
|
|
20800
|
-
if (newValue === void 0) continue;
|
|
20801
|
-
const ve = clampSegmentEnd(text, seg, segments[i + 1], matches[i + 1]?.viaExt ?? false);
|
|
20802
|
-
const replacement = seg.valueStart === ve ? padInsertion(text, seg.valueStart, newValue) : newValue;
|
|
20803
|
-
const l = led(para2);
|
|
20804
|
-
l.ranges.push({ start: seg.valueStart, end: ve, replacement });
|
|
20805
|
-
matchedLabels.add(matchKey);
|
|
20806
|
-
l.filledIdx.push(filled.length);
|
|
20807
|
-
l.matchKeys.push(matchKey);
|
|
20808
|
-
filled.push({ label: matched.label.trim(), value: newValue, row: -1, col: -1, key: matchKey });
|
|
20809
|
-
}
|
|
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");
|
|
20810
21023
|
}
|
|
20811
|
-
|
|
20812
|
-
|
|
20813
|
-
let paraSplices = null;
|
|
20814
|
-
if (l.fullText !== void 0) {
|
|
20815
|
-
paraSplices = buildParagraphSplices(para2, l.fullText, xml);
|
|
20816
|
-
} else if (l.ranges.length > 0) {
|
|
20817
|
-
const sorted = [...l.ranges].sort((a, b) => a.start - b.start || a.end - b.end);
|
|
20818
|
-
const merged = [];
|
|
20819
|
-
for (const r of sorted) {
|
|
20820
|
-
const prev = merged[merged.length - 1];
|
|
20821
|
-
if (prev && r.start < prev.end) continue;
|
|
20822
|
-
merged.push(r);
|
|
20823
|
-
}
|
|
20824
|
-
if (paraTText(para2, xml) !== null) {
|
|
20825
|
-
const precise = [];
|
|
20826
|
-
let ok = true;
|
|
20827
|
-
for (const r of merged) {
|
|
20828
|
-
const sp = buildRangeSplices(para2, xml, r.start, r.end, r.replacement);
|
|
20829
|
-
if (!sp) {
|
|
20830
|
-
ok = false;
|
|
20831
|
-
break;
|
|
20832
|
-
}
|
|
20833
|
-
precise.push(...sp);
|
|
20834
|
-
}
|
|
20835
|
-
paraSplices = ok ? precise : null;
|
|
20836
|
-
} else if (paraTextPureT(para2, xml)) {
|
|
20837
|
-
let text = para2.text;
|
|
20838
|
-
for (let k = merged.length - 1; k >= 0; k--) {
|
|
20839
|
-
const r = merged[k];
|
|
20840
|
-
text = text.slice(0, r.start) + r.replacement + text.slice(r.end);
|
|
20841
|
-
}
|
|
20842
|
-
paraSplices = buildParagraphSplices(para2, text, xml);
|
|
20843
|
-
} else {
|
|
20844
|
-
paraSplices = null;
|
|
20845
|
-
}
|
|
20846
|
-
}
|
|
20847
|
-
if (paraSplices === null) {
|
|
20848
|
-
for (const idx of l.filledIdx) filled[idx] = null;
|
|
20849
|
-
for (const k of l.matchKeys) failedKeys.add(k);
|
|
20850
|
-
continue;
|
|
20851
|
-
}
|
|
20852
|
-
for (const k of l.matchKeys) succeededKeys.add(k);
|
|
20853
|
-
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");
|
|
20854
21026
|
}
|
|
20855
|
-
|
|
20856
|
-
|
|
20857
|
-
|
|
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);
|
|
20858
21031
|
}
|
|
20859
21032
|
}
|
|
20860
|
-
|
|
20861
|
-
|
|
21033
|
+
if (opts.approval && opts.approval.length > 6) {
|
|
21034
|
+
throw new KordocError("approval must contain at most 6 labels");
|
|
20862
21035
|
}
|
|
20863
|
-
|
|
20864
|
-
|
|
20865
|
-
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";
|
|
20866
21065
|
return {
|
|
20867
|
-
|
|
20868
|
-
|
|
20869
|
-
|
|
20870
|
-
|
|
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
|
|
20871
21102
|
};
|
|
20872
21103
|
}
|
|
20873
|
-
|
|
20874
|
-
|
|
20875
|
-
|
|
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
|
+
};
|
|
20876
21176
|
|
|
20877
21177
|
// src/hwpx/gen-ids.ts
|
|
20878
21178
|
var NS_SECTION = "http://www.hancom.co.kr/hwpml/2011/section";
|
|
@@ -24239,7 +24539,7 @@ async function markdownToHwpx(markdown, options) {
|
|
|
24239
24539
|
}
|
|
24240
24540
|
}
|
|
24241
24541
|
const extraBorderFills = [...remap?.borderFillXmls ?? [], ...bfReg?.emit() ?? []];
|
|
24242
|
-
const zip = new
|
|
24542
|
+
const zip = new JSZip8();
|
|
24243
24543
|
zip.file("mimetype", "application/hwp+zip", { compression: "STORE" });
|
|
24244
24544
|
zip.file("META-INF/container.xml", generateContainerXml());
|
|
24245
24545
|
zip.file("Contents/content.hpf", generateManifest(chartParts, images.manifestItems(), gongmun ? "gongmun" : "default"));
|
|
@@ -24401,8 +24701,55 @@ function diffTableCells(a, b) {
|
|
|
24401
24701
|
return result;
|
|
24402
24702
|
}
|
|
24403
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
|
+
|
|
24404
24751
|
// src/hwpx/extract-profile.ts
|
|
24405
|
-
import
|
|
24752
|
+
import JSZip9 from "jszip";
|
|
24406
24753
|
function elemsByLocal(root, name) {
|
|
24407
24754
|
const all = root.getElementsByTagName("*");
|
|
24408
24755
|
const out = [];
|
|
@@ -24605,7 +24952,7 @@ function pick(map, keys) {
|
|
|
24605
24952
|
}
|
|
24606
24953
|
async function hwpxToProfile(input) {
|
|
24607
24954
|
const buf = input instanceof ArrayBuffer ? input : toArrayBuffer(input);
|
|
24608
|
-
const zip = await
|
|
24955
|
+
const zip = await JSZip9.loadAsync(buf);
|
|
24609
24956
|
const parser = createXmlParser();
|
|
24610
24957
|
const headerFile = zip.file("Contents/header.xml") ?? zip.file(/[Hh]eader\.xml$/)?.[0];
|
|
24611
24958
|
let headerXml = "<root/>";
|
|
@@ -24920,7 +25267,7 @@ function gongmunLintWarnings(text, limit = 10) {
|
|
|
24920
25267
|
}
|
|
24921
25268
|
|
|
24922
25269
|
// src/roundtrip/patcher.ts
|
|
24923
|
-
import
|
|
25270
|
+
import JSZip10 from "jszip";
|
|
24924
25271
|
|
|
24925
25272
|
// src/roundtrip/table-rows.ts
|
|
24926
25273
|
var ROW_OBJECT_RE = /<(?:[A-Za-z0-9_]+:)?(?:tbl|pic|equation|ole|container|shape|drawingObject|drawText|video|chart|fieldBegin|fieldEnd|ctrl)\b/;
|
|
@@ -25567,7 +25914,7 @@ async function patchHwpx(original, editedMarkdown, options) {
|
|
|
25567
25914
|
}
|
|
25568
25915
|
let zip;
|
|
25569
25916
|
try {
|
|
25570
|
-
zip = await
|
|
25917
|
+
zip = await JSZip10.loadAsync(original);
|
|
25571
25918
|
} catch {
|
|
25572
25919
|
return { success: false, applied: 0, skipped, error: "ZIP \uB85C\uB4DC \uC2E4\uD328" };
|
|
25573
25920
|
}
|
|
@@ -25927,6 +26274,8 @@ function replaceOleStream(file, path, newData) {
|
|
|
25927
26274
|
}
|
|
25928
26275
|
var Surgeon = class {
|
|
25929
26276
|
buf;
|
|
26277
|
+
/** 논리 파일 길이 — buf는 성장 시 기하급수 capacity로 재할당되므로 buf.length와 다를 수 있음 */
|
|
26278
|
+
len;
|
|
25930
26279
|
fat = [];
|
|
25931
26280
|
/** FAT 배열을 구성하는 섹터 번호들 (DIFAT 순서) */
|
|
25932
26281
|
fatSectors = [];
|
|
@@ -25946,6 +26295,7 @@ var Surgeon = class {
|
|
|
25946
26295
|
}
|
|
25947
26296
|
const padded = Math.ceil((file.length - SECTOR) / SECTOR) * SECTOR + SECTOR;
|
|
25948
26297
|
this.buf = Buffer.alloc(padded);
|
|
26298
|
+
this.len = padded;
|
|
25949
26299
|
file.copy(this.buf);
|
|
25950
26300
|
this.loadFat();
|
|
25951
26301
|
this.loadMiniFat();
|
|
@@ -26000,7 +26350,7 @@ var Surgeon = class {
|
|
|
26000
26350
|
// ── 헬퍼 ──
|
|
26001
26351
|
sectorOffset(n) {
|
|
26002
26352
|
const off = SECTOR + n * SECTOR;
|
|
26003
|
-
if (n >= 4294967290 || off + SECTOR > this.
|
|
26353
|
+
if (n >= 4294967290 || off + SECTOR > this.len) throw new OleSurgeonError(`\uC139\uD130 \uBC94\uC704 \uCD08\uACFC: ${n}`);
|
|
26004
26354
|
return off;
|
|
26005
26355
|
}
|
|
26006
26356
|
chain(start) {
|
|
@@ -26058,26 +26408,40 @@ var Surgeon = class {
|
|
|
26058
26408
|
const out = [];
|
|
26059
26409
|
for (let i = 0; i < this.fat.length && out.length < n; i++) {
|
|
26060
26410
|
if (this.fat[i] !== FREESECT) continue;
|
|
26061
|
-
if (SECTOR + (i + 1) * SECTOR > this.
|
|
26411
|
+
if (SECTOR + (i + 1) * SECTOR > this.len) continue;
|
|
26062
26412
|
this.fat[i] = ENDOFCHAIN;
|
|
26063
26413
|
const off = this.sectorOffset(i);
|
|
26064
26414
|
this.buf.fill(0, off, off + SECTOR);
|
|
26065
26415
|
out.push(i);
|
|
26066
26416
|
}
|
|
26067
26417
|
while (out.length < n) {
|
|
26068
|
-
this.ensureFatCapacity((this.
|
|
26069
|
-
const idx =
|
|
26070
|
-
this.buf = Buffer.concat([this.buf, Buffer.alloc(SECTOR)]);
|
|
26418
|
+
this.ensureFatCapacity((this.len - SECTOR) / SECTOR + 2);
|
|
26419
|
+
const idx = this.appendSector();
|
|
26071
26420
|
this.fat[idx] = ENDOFCHAIN;
|
|
26072
26421
|
out.push(idx);
|
|
26073
26422
|
}
|
|
26074
26423
|
return out;
|
|
26075
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
|
+
}
|
|
26076
26441
|
/** FAT 배열이 sectorCount개 엔트리를 담도록 확장 (FAT 섹터 추가 + DIFAT 갱신) */
|
|
26077
26442
|
ensureFatCapacity(sectorCount) {
|
|
26078
26443
|
while (this.fat.length < sectorCount) {
|
|
26079
|
-
const idx =
|
|
26080
|
-
this.buf = Buffer.concat([this.buf, Buffer.alloc(SECTOR)]);
|
|
26444
|
+
const idx = this.appendSector();
|
|
26081
26445
|
for (let i = 0; i < 128; i++) this.fat.push(FREESECT);
|
|
26082
26446
|
this.fat[idx] = FATSECT;
|
|
26083
26447
|
this.fatSectors.push(idx);
|
|
@@ -26200,7 +26564,7 @@ var Surgeon = class {
|
|
|
26200
26564
|
finish() {
|
|
26201
26565
|
this.wipeFreedSectors();
|
|
26202
26566
|
this.flushFat();
|
|
26203
|
-
return this.buf;
|
|
26567
|
+
return this.len === this.buf.length ? this.buf : this.buf.subarray(0, this.len);
|
|
26204
26568
|
}
|
|
26205
26569
|
/** 해제 후 재할당되지 않고 남은 FREESECT 섹터의 바이트를 0으로 채움 (데이터 remanence 제거) */
|
|
26206
26570
|
wipeFreedSectors() {
|
|
@@ -27018,7 +27382,7 @@ function stageParaPatch(scan, para2, newPlain, skip) {
|
|
|
27018
27382
|
}
|
|
27019
27383
|
|
|
27020
27384
|
// src/validate.ts
|
|
27021
|
-
import
|
|
27385
|
+
import JSZip11 from "jszip";
|
|
27022
27386
|
import { DOMParser as DOMParser4 } from "@xmldom/xmldom";
|
|
27023
27387
|
var REQUIRED_FILES = [
|
|
27024
27388
|
"mimetype",
|
|
@@ -27036,7 +27400,7 @@ async function validateHwpx(buffer) {
|
|
|
27036
27400
|
const issues = [];
|
|
27037
27401
|
let zip;
|
|
27038
27402
|
try {
|
|
27039
|
-
zip = await
|
|
27403
|
+
zip = await JSZip11.loadAsync(buffer);
|
|
27040
27404
|
} catch (err) {
|
|
27041
27405
|
return {
|
|
27042
27406
|
ok: false,
|
|
@@ -27104,10 +27468,10 @@ async function validateHwpx(buffer) {
|
|
|
27104
27468
|
}
|
|
27105
27469
|
|
|
27106
27470
|
// src/roundtrip/session.ts
|
|
27107
|
-
import
|
|
27471
|
+
import JSZip12 from "jszip";
|
|
27108
27472
|
async function buildState(bytes) {
|
|
27109
27473
|
const parsed = await parseHwpxDocument(u8ToArrayBuffer2(bytes));
|
|
27110
|
-
const zip = await
|
|
27474
|
+
const zip = await JSZip12.loadAsync(bytes);
|
|
27111
27475
|
const sectionPaths = await resolveSectionEntryNames(zip);
|
|
27112
27476
|
if (sectionPaths.length === 0) {
|
|
27113
27477
|
throw new Error("HWPX \uC139\uC158 \uD30C\uC77C\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4");
|
|
@@ -27449,7 +27813,7 @@ async function patchHwpxBlocks(original, edits, options) {
|
|
|
27449
27813
|
}
|
|
27450
27814
|
|
|
27451
27815
|
// src/print/renderer.ts
|
|
27452
|
-
import { existsSync } from "fs";
|
|
27816
|
+
import { existsSync as existsSync2 } from "fs";
|
|
27453
27817
|
import MarkdownIt from "markdown-it";
|
|
27454
27818
|
var PRESETS = {
|
|
27455
27819
|
default: `
|
|
@@ -27588,7 +27952,7 @@ function findChromiumPath() {
|
|
|
27588
27952
|
const linux = ["/usr/bin/google-chrome", "/usr/bin/chromium-browser", "/usr/bin/chromium"];
|
|
27589
27953
|
const candidates = process.platform === "win32" ? win : process.platform === "darwin" ? mac : linux;
|
|
27590
27954
|
for (const p of candidates) {
|
|
27591
|
-
if (p &&
|
|
27955
|
+
if (p && existsSync2(p)) return p;
|
|
27592
27956
|
}
|
|
27593
27957
|
return null;
|
|
27594
27958
|
}
|
|
@@ -27648,7 +28012,7 @@ async function parse(input, options) {
|
|
|
27648
28012
|
}
|
|
27649
28013
|
async function parseImage(buffer, options) {
|
|
27650
28014
|
try {
|
|
27651
|
-
const { parseImageDocument } = await import("./image-ocr-
|
|
28015
|
+
const { parseImageDocument } = await import("./image-ocr-R44RYDBU.js");
|
|
27652
28016
|
const { blocks, warnings } = await parseImageDocument(buffer, options);
|
|
27653
28017
|
return {
|
|
27654
28018
|
success: true,
|
|
@@ -27665,7 +28029,7 @@ async function parseImage(buffer, options) {
|
|
|
27665
28029
|
async function parseHwp3(buffer, options) {
|
|
27666
28030
|
try {
|
|
27667
28031
|
const { markdown, blocks, metadata, outline, warnings } = parseHwp3Document(buffer, options);
|
|
27668
|
-
return { success: true, fileType: "hwp3", markdown, blocks, metadata, outline, warnings };
|
|
28032
|
+
return { success: true, fileType: "hwp3", markdown, blocks, metadata, outline, warnings, pageCount: metadata?.pageCount };
|
|
27669
28033
|
} catch (err) {
|
|
27670
28034
|
return { success: false, fileType: "hwp3", error: sanitizeError(err), code: classifyError(err) };
|
|
27671
28035
|
}
|
|
@@ -27673,7 +28037,7 @@ async function parseHwp3(buffer, options) {
|
|
|
27673
28037
|
async function parseHwpx(buffer, options) {
|
|
27674
28038
|
try {
|
|
27675
28039
|
const { markdown, blocks, metadata, outline, warnings, images } = await parseHwpxDocument(buffer, options);
|
|
27676
|
-
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 };
|
|
27677
28041
|
} catch (err) {
|
|
27678
28042
|
return { success: false, fileType: "hwpx", error: sanitizeError(err), code: classifyError(err) };
|
|
27679
28043
|
}
|
|
@@ -27692,13 +28056,14 @@ async function parseHwp(buffer, options) {
|
|
|
27692
28056
|
markdown: com.markdown,
|
|
27693
28057
|
blocks: com.blocks,
|
|
27694
28058
|
metadata: com.metadata,
|
|
27695
|
-
warnings: com.warnings
|
|
28059
|
+
warnings: com.warnings,
|
|
28060
|
+
pageCount
|
|
27696
28061
|
};
|
|
27697
28062
|
}
|
|
27698
28063
|
} catch {
|
|
27699
28064
|
}
|
|
27700
28065
|
}
|
|
27701
|
-
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 };
|
|
27702
28067
|
} catch (err) {
|
|
27703
28068
|
return { success: false, fileType: "hwp", error: sanitizeError(err), code: classifyError(err) };
|
|
27704
28069
|
}
|
|
@@ -27706,7 +28071,7 @@ async function parseHwp(buffer, options) {
|
|
|
27706
28071
|
async function parsePdf(buffer, options) {
|
|
27707
28072
|
let parsePdfDocument;
|
|
27708
28073
|
try {
|
|
27709
|
-
const mod = await import("./parser-
|
|
28074
|
+
const mod = await import("./parser-W3POOYQQ.js");
|
|
27710
28075
|
parsePdfDocument = mod.parsePdfDocument;
|
|
27711
28076
|
} catch {
|
|
27712
28077
|
return {
|
|
@@ -27718,7 +28083,7 @@ async function parsePdf(buffer, options) {
|
|
|
27718
28083
|
}
|
|
27719
28084
|
try {
|
|
27720
28085
|
const { markdown, blocks, metadata, outline, warnings, isImageBased, pageQuality, qualitySummary, images } = await parsePdfDocument(buffer, options);
|
|
27721
|
-
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 };
|
|
27722
28087
|
} catch (err) {
|
|
27723
28088
|
const isImageBased = err instanceof Error && "isImageBased" in err ? true : void 0;
|
|
27724
28089
|
return { success: false, fileType: "pdf", error: sanitizeError(err), code: classifyError(err), isImageBased };
|
|
@@ -27727,7 +28092,7 @@ async function parsePdf(buffer, options) {
|
|
|
27727
28092
|
async function parseXlsx(buffer, options) {
|
|
27728
28093
|
try {
|
|
27729
28094
|
const { markdown, blocks, metadata, warnings } = await parseXlsxDocument(buffer, options);
|
|
27730
|
-
return { success: true, fileType: "xlsx", markdown, blocks, metadata, warnings };
|
|
28095
|
+
return { success: true, fileType: "xlsx", markdown, blocks, metadata, warnings, pageCount: metadata?.pageCount };
|
|
27731
28096
|
} catch (err) {
|
|
27732
28097
|
return { success: false, fileType: "xlsx", error: sanitizeError(err), code: classifyError(err) };
|
|
27733
28098
|
}
|
|
@@ -27735,7 +28100,7 @@ async function parseXlsx(buffer, options) {
|
|
|
27735
28100
|
async function parseXls(buffer, options) {
|
|
27736
28101
|
try {
|
|
27737
28102
|
const { markdown, blocks, metadata, warnings } = await parseXlsDocument(buffer, options);
|
|
27738
|
-
return { success: true, fileType: "xls", markdown, blocks, metadata, warnings };
|
|
28103
|
+
return { success: true, fileType: "xls", markdown, blocks, metadata, warnings, pageCount: metadata?.pageCount };
|
|
27739
28104
|
} catch (err) {
|
|
27740
28105
|
return { success: false, fileType: "xls", error: sanitizeError(err), code: classifyError(err) };
|
|
27741
28106
|
}
|
|
@@ -27743,7 +28108,7 @@ async function parseXls(buffer, options) {
|
|
|
27743
28108
|
async function parseDocx(buffer, options) {
|
|
27744
28109
|
try {
|
|
27745
28110
|
const { markdown, blocks, metadata, outline, warnings, images } = await parseDocxDocument(buffer, options);
|
|
27746
|
-
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 };
|
|
27747
28112
|
} catch (err) {
|
|
27748
28113
|
return { success: false, fileType: "docx", error: sanitizeError(err), code: classifyError(err) };
|
|
27749
28114
|
}
|
|
@@ -27751,7 +28116,7 @@ async function parseDocx(buffer, options) {
|
|
|
27751
28116
|
async function parseHwpml(buffer, options) {
|
|
27752
28117
|
try {
|
|
27753
28118
|
const { markdown, blocks, metadata, outline, warnings } = parseHwpmlDocument(buffer, options);
|
|
27754
|
-
return { success: true, fileType: "hwpml", markdown, blocks, metadata, outline, warnings };
|
|
28119
|
+
return { success: true, fileType: "hwpml", markdown, blocks, metadata, outline, warnings, pageCount: metadata?.pageCount };
|
|
27755
28120
|
} catch (err) {
|
|
27756
28121
|
return { success: false, fileType: "hwpml", error: sanitizeError(err), code: classifyError(err) };
|
|
27757
28122
|
}
|
|
@@ -27797,9 +28162,13 @@ async function fillForm(input, values, outputFormat = "markdown") {
|
|
|
27797
28162
|
}
|
|
27798
28163
|
|
|
27799
28164
|
export {
|
|
27800
|
-
|
|
27801
|
-
|
|
27802
|
-
|
|
28165
|
+
isZipFile,
|
|
28166
|
+
isHwpxFile,
|
|
28167
|
+
isOldHwpFile,
|
|
28168
|
+
isPdfFile,
|
|
28169
|
+
detectFormat,
|
|
28170
|
+
detectOle2Format,
|
|
28171
|
+
detectZipFormat,
|
|
27803
28172
|
extractHwpxMetadataOnly,
|
|
27804
28173
|
extractHwp5MetadataOnly,
|
|
27805
28174
|
formatFillValue,
|
|
@@ -27810,10 +28179,18 @@ export {
|
|
|
27810
28179
|
inferFieldType,
|
|
27811
28180
|
extractFormSchema,
|
|
27812
28181
|
fillFormFields,
|
|
28182
|
+
extractClickHereFields,
|
|
27813
28183
|
fillHwpx,
|
|
28184
|
+
PRESET_ALIAS,
|
|
28185
|
+
normalizeGongmunPreset,
|
|
28186
|
+
incompatibleGongmunWarnings,
|
|
27814
28187
|
markdownToHwpx,
|
|
27815
28188
|
compare,
|
|
27816
28189
|
diffBlocks,
|
|
28190
|
+
BUILTIN_TEMPLATES,
|
|
28191
|
+
resolveBuiltinTemplate,
|
|
28192
|
+
readBuiltinTemplate,
|
|
28193
|
+
readBuiltinTemplateSample,
|
|
27817
28194
|
hwpxToProfile,
|
|
27818
28195
|
isKnownFont,
|
|
27819
28196
|
unknownFontWarnings,
|
|
@@ -27840,4 +28217,4 @@ export {
|
|
|
27840
28217
|
parseHwpml,
|
|
27841
28218
|
fillForm
|
|
27842
28219
|
};
|
|
27843
|
-
//# sourceMappingURL=chunk-
|
|
28220
|
+
//# sourceMappingURL=chunk-DRZUJ2RK.js.map
|