kordoc 4.14.2 → 4.14.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +51 -6
- package/dist/{-AQKXLFIM.js → -Z4TJ3QGK.js} +13 -11
- package/dist/{chunk-QOMNEWCQ.cjs → chunk-5LAZQCUJ.cjs} +305 -31
- package/dist/chunk-5LAZQCUJ.cjs.map +1 -0
- package/dist/{chunk-7IAQ4HOK.js → chunk-6BWU6SQX.js} +1 -1
- package/dist/chunk-6BWU6SQX.js.map +1 -0
- package/dist/{chunk-FOJZNMMK.js → chunk-6CUIXJOC.js} +1 -1
- package/dist/chunk-6CUIXJOC.js.map +1 -0
- package/dist/{chunk-ZWD734EY.js → chunk-7SE4EZ45.js} +2 -2
- package/dist/{chunk-TSFS5VKD.js → chunk-D4U2TXDC.js} +332 -141
- package/dist/chunk-D4U2TXDC.js.map +1 -0
- package/dist/chunk-DPWWXUOG.js +2222 -0
- package/dist/chunk-DPWWXUOG.js.map +1 -0
- package/dist/{chunk-GDW4YP7K.js → chunk-GLSBEEZJ.js} +2 -2
- package/dist/{chunk-JQEFP45L.js → chunk-GUJ4Q73I.js} +2 -2
- package/dist/{chunk-ZWOFQRIB.js → chunk-HIJUKZHM.js} +3 -3
- package/dist/chunk-IMAAFSAX.cjs +1352 -0
- package/dist/chunk-IMAAFSAX.cjs.map +1 -0
- package/dist/{chunk-TNCKOL2O.js → chunk-LCW7SEPG.js} +2 -2
- package/dist/chunk-LFSQAMSM.js +1352 -0
- package/dist/chunk-LFSQAMSM.js.map +1 -0
- package/dist/chunk-MG6DJEX4.js +1299 -0
- package/dist/chunk-MG6DJEX4.js.map +1 -0
- package/dist/{chunk-QC5T6D4J.js → chunk-NLU6LBLY.js} +299 -25
- package/dist/chunk-NLU6LBLY.js.map +1 -0
- package/dist/{chunk-EQZI77PP.cjs → chunk-QEJUY6SK.cjs} +3 -3
- package/dist/{chunk-EQZI77PP.cjs.map → chunk-QEJUY6SK.cjs.map} +1 -1
- package/dist/{chunk-VQ5W4MYO.js → chunk-QIF2VXBQ.js} +298 -24
- package/dist/chunk-QIF2VXBQ.js.map +1 -0
- package/dist/{chunk-UET462HB.js → chunk-TITDJWVZ.js} +2 -2
- package/dist/{chunk-247ANM72.cjs → chunk-USHQCF5T.cjs} +870 -147
- package/dist/chunk-USHQCF5T.cjs.map +1 -0
- package/dist/{chunk-C7FQBSON.js → chunk-V3DX4DUF.js} +2 -2
- package/dist/{chunk-DOGPOY4B.js → chunk-VDN7LMPL.js} +2 -2
- package/dist/{chunk-EDNJLUSE.js → chunk-VJIL6PIL.js} +2 -2
- package/dist/{chunk-L7DMSMAG.cjs → chunk-VWUYVEFN.cjs} +1 -1
- package/dist/chunk-VWUYVEFN.cjs.map +1 -0
- package/dist/{chunk-HR2XNERB.cjs → chunk-WJHAU73B.cjs} +2 -2
- package/dist/{chunk-HR2XNERB.cjs.map → chunk-WJHAU73B.cjs.map} +1 -1
- package/dist/{chunk-NS55SM7A.js → chunk-WJJGW5PL.js} +2 -2
- package/dist/{chunk-EISO3POT.js → chunk-XVXHXQIO.js} +788 -194
- package/dist/chunk-XVXHXQIO.js.map +1 -0
- package/dist/{chunk-ILU76UF3.js → chunk-YAKC46HZ.js} +2 -2
- package/dist/chunk-YAKC46HZ.js.map +1 -0
- package/dist/{chunk-3DUADAY2.js → chunk-YAVHVFRY.js} +851 -128
- package/dist/chunk-YAVHVFRY.js.map +1 -0
- package/dist/{chunk-A7QC2ZVP.js → chunk-YT3GC62E.js} +851 -128
- package/dist/chunk-YT3GC62E.js.map +1 -0
- package/dist/chunks-CCMRL4MJ.js +10 -0
- package/dist/cli.js +228 -97
- package/dist/cli.js.map +1 -1
- package/dist/{formula-PJJ6S3Z2.cjs → formula-5LOVHAKE.cjs} +9 -9
- package/dist/{formula-PJJ6S3Z2.cjs.map → formula-5LOVHAKE.cjs.map} +1 -1
- package/dist/{formula-JGFTXWYT.js → formula-7RANCW5L.js} +3 -3
- package/dist/{formula-5S7WWXGH.js → formula-7UYJOVTB.js} +4 -4
- package/dist/{image-ocr-WCAYXLOA.js → image-ocr-I6JOCATS.js} +34 -16
- package/dist/image-ocr-I6JOCATS.js.map +1 -0
- package/dist/{image-ocr-ZTYAGV3W.cjs → image-ocr-NYBJEOH2.cjs} +36 -18
- package/dist/image-ocr-NYBJEOH2.cjs.map +1 -0
- package/dist/{image-ocr-FLIL7MGN.js → image-ocr-ZV6PDBUQ.js} +32 -14
- package/dist/image-ocr-ZV6PDBUQ.js.map +1 -0
- package/dist/index.cjs +4048 -1216
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +66 -28
- package/dist/index.d.ts +66 -28
- package/dist/index.js +3487 -655
- package/dist/index.js.map +1 -1
- package/dist/mcp.js +74 -53
- package/dist/mcp.js.map +1 -1
- package/dist/{model-bundle-FSPZZ2EM.js → model-bundle-2C5IYDLE.js} +5 -5
- package/dist/{models-CJPAQK5R.js → models-SEQZHZFT.js} +5 -5
- package/dist/{parser-SCUTZAIJ.js → parser-3FNXEP6T.js} +708 -250
- package/dist/parser-3FNXEP6T.js.map +1 -0
- package/dist/{parser-A4TMPJAM.js → parser-FJBJXRJK.js} +708 -250
- package/dist/parser-FJBJXRJK.js.map +1 -0
- package/dist/{parser-VCXE4I5A.cjs → parser-HVEKF73B.cjs} +733 -275
- package/dist/parser-HVEKF73B.cjs.map +1 -0
- package/dist/pdf-ocr-JRDBJ6TA.cjs +14 -0
- package/dist/{pdf-ocr-MBZZ57LD.cjs.map → pdf-ocr-JRDBJ6TA.cjs.map} +1 -1
- package/dist/pdf-ocr-UUBWBEHL.js +16 -0
- package/dist/pdf-ocr-VJSF2SQC.js +13 -0
- package/dist/{profile-io-LBM263K7.js → profile-io-RUFZOZO6.js} +3 -3
- package/dist/{rasterize-46RG4ZF6.js → rasterize-IOYXR5SI.js} +3 -3
- package/dist/redact-NMIKVHMP.js +18 -0
- package/dist/redact-doc-QBHNJ2BU.js +1284 -0
- package/dist/redact-doc-QBHNJ2BU.js.map +1 -0
- package/dist/{render-NGP5T24S.js → render-YZ2TEC3N.js} +7 -7
- package/dist/seal-ECV44TUH.js +10 -0
- package/dist/{setup-AU4OCHNH.js → setup-MPIEP6XT.js} +3 -3
- package/dist/visual-ICMOWUCR.js +19 -0
- package/dist/{watch-MKPUCPXM.js → watch-RYO7MN5B.js} +12 -12
- package/package.json +2 -2
- package/dist/chunk-247ANM72.cjs.map +0 -1
- package/dist/chunk-3DUADAY2.js.map +0 -1
- package/dist/chunk-422427XJ.cjs +0 -571
- package/dist/chunk-422427XJ.cjs.map +0 -1
- package/dist/chunk-7IAQ4HOK.js.map +0 -1
- package/dist/chunk-A7QC2ZVP.js.map +0 -1
- package/dist/chunk-DZIXKL7E.js +0 -145
- package/dist/chunk-DZIXKL7E.js.map +0 -1
- package/dist/chunk-EISO3POT.js.map +0 -1
- package/dist/chunk-EP7KOI2V.js +0 -571
- package/dist/chunk-EP7KOI2V.js.map +0 -1
- package/dist/chunk-FOJZNMMK.js.map +0 -1
- package/dist/chunk-ILU76UF3.js.map +0 -1
- package/dist/chunk-KVWV4ULP.js +0 -516
- package/dist/chunk-KVWV4ULP.js.map +0 -1
- package/dist/chunk-L7DMSMAG.cjs.map +0 -1
- package/dist/chunk-QC5T6D4J.js.map +0 -1
- package/dist/chunk-QOMNEWCQ.cjs.map +0 -1
- package/dist/chunk-TSFS5VKD.js.map +0 -1
- package/dist/chunk-VQ5W4MYO.js.map +0 -1
- package/dist/chunks-SX77P5CV.js +0 -10
- package/dist/image-ocr-FLIL7MGN.js.map +0 -1
- package/dist/image-ocr-WCAYXLOA.js.map +0 -1
- package/dist/image-ocr-ZTYAGV3W.cjs.map +0 -1
- package/dist/parser-A4TMPJAM.js.map +0 -1
- package/dist/parser-SCUTZAIJ.js.map +0 -1
- package/dist/parser-VCXE4I5A.cjs.map +0 -1
- package/dist/pdf-ocr-76SAUXMZ.js +0 -13
- package/dist/pdf-ocr-CPU55CVF.js +0 -16
- package/dist/pdf-ocr-MBZZ57LD.cjs +0 -14
- package/dist/redact-7ILEAUTS.js +0 -12
- package/dist/seal-RGUPOLXD.js +0 -10
- package/dist/visual-NZLYIYEM.js +0 -19
- /package/dist/{-AQKXLFIM.js.map → -Z4TJ3QGK.js.map} +0 -0
- /package/dist/{chunk-ZWD734EY.js.map → chunk-7SE4EZ45.js.map} +0 -0
- /package/dist/{chunk-GDW4YP7K.js.map → chunk-GLSBEEZJ.js.map} +0 -0
- /package/dist/{chunk-JQEFP45L.js.map → chunk-GUJ4Q73I.js.map} +0 -0
- /package/dist/{chunk-ZWOFQRIB.js.map → chunk-HIJUKZHM.js.map} +0 -0
- /package/dist/{chunk-TNCKOL2O.js.map → chunk-LCW7SEPG.js.map} +0 -0
- /package/dist/{chunk-UET462HB.js.map → chunk-TITDJWVZ.js.map} +0 -0
- /package/dist/{chunk-C7FQBSON.js.map → chunk-V3DX4DUF.js.map} +0 -0
- /package/dist/{chunk-DOGPOY4B.js.map → chunk-VDN7LMPL.js.map} +0 -0
- /package/dist/{chunk-EDNJLUSE.js.map → chunk-VJIL6PIL.js.map} +0 -0
- /package/dist/{chunk-NS55SM7A.js.map → chunk-WJJGW5PL.js.map} +0 -0
- /package/dist/{chunks-SX77P5CV.js.map → chunks-CCMRL4MJ.js.map} +0 -0
- /package/dist/{formula-JGFTXWYT.js.map → formula-7RANCW5L.js.map} +0 -0
- /package/dist/{formula-5S7WWXGH.js.map → formula-7UYJOVTB.js.map} +0 -0
- /package/dist/{model-bundle-FSPZZ2EM.js.map → model-bundle-2C5IYDLE.js.map} +0 -0
- /package/dist/{models-CJPAQK5R.js.map → models-SEQZHZFT.js.map} +0 -0
- /package/dist/{pdf-ocr-76SAUXMZ.js.map → pdf-ocr-UUBWBEHL.js.map} +0 -0
- /package/dist/{pdf-ocr-CPU55CVF.js.map → pdf-ocr-VJSF2SQC.js.map} +0 -0
- /package/dist/{profile-io-LBM263K7.js.map → profile-io-RUFZOZO6.js.map} +0 -0
- /package/dist/{rasterize-46RG4ZF6.js.map → rasterize-IOYXR5SI.js.map} +0 -0
- /package/dist/{redact-7ILEAUTS.js.map → redact-NMIKVHMP.js.map} +0 -0
- /package/dist/{render-NGP5T24S.js.map → render-YZ2TEC3N.js.map} +0 -0
- /package/dist/{seal-RGUPOLXD.js.map → seal-ECV44TUH.js.map} +0 -0
- /package/dist/{setup-AU4OCHNH.js.map → setup-MPIEP6XT.js.map} +0 -0
- /package/dist/{visual-NZLYIYEM.js.map → visual-ICMOWUCR.js.map} +0 -0
- /package/dist/{watch-MKPUCPXM.js.map → watch-RYO7MN5B.js.map} +0 -0
|
@@ -12,13 +12,14 @@ import {
|
|
|
12
12
|
paraTextPureT,
|
|
13
13
|
patchZipEntries,
|
|
14
14
|
scanSectionXml
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-HIJUKZHM.js";
|
|
16
16
|
import {
|
|
17
17
|
CONVERT_MAP,
|
|
18
18
|
FLAG_COMPRESSED,
|
|
19
19
|
FLAG_DISTRIBUTION,
|
|
20
20
|
FLAG_DRM,
|
|
21
21
|
FLAG_ENCRYPTED,
|
|
22
|
+
INLINE_TABLE_MARK,
|
|
22
23
|
MIDDLE_CONVERT_MAP,
|
|
23
24
|
SPACE_EM_FIXED,
|
|
24
25
|
TAG_CHAR_SHAPE,
|
|
@@ -28,6 +29,9 @@ import {
|
|
|
28
29
|
TAG_PARA_TEXT,
|
|
29
30
|
TAG_TABLE,
|
|
30
31
|
appendParaText,
|
|
32
|
+
blocksPlainText,
|
|
33
|
+
buildAddressedTable,
|
|
34
|
+
cellTextFromBlocks,
|
|
31
35
|
charWidthEm1000,
|
|
32
36
|
circledHangul,
|
|
33
37
|
circledNumber,
|
|
@@ -37,12 +41,15 @@ import {
|
|
|
37
41
|
detectImageMime,
|
|
38
42
|
detectOle2Format,
|
|
39
43
|
detectZipFormat,
|
|
44
|
+
emitParagraphBlocks,
|
|
40
45
|
extractRenderedRegions,
|
|
41
46
|
faceClassForGen,
|
|
42
47
|
fitRatioForFewerLines,
|
|
48
|
+
formatNumber,
|
|
43
49
|
hangulAmount,
|
|
44
50
|
hangulOrdinal,
|
|
45
51
|
hmlToLatex,
|
|
52
|
+
hwpEquationToLatex,
|
|
46
53
|
isExtendedOnlyCtrlChar,
|
|
47
54
|
measureTextWidth,
|
|
48
55
|
parseFileHeader,
|
|
@@ -51,12 +58,12 @@ import {
|
|
|
51
58
|
renderDocumentToScene,
|
|
52
59
|
romanNumeral,
|
|
53
60
|
simulateWrap
|
|
54
|
-
} from "./chunk-
|
|
61
|
+
} from "./chunk-D4U2TXDC.js";
|
|
55
62
|
import {
|
|
56
63
|
HEADING_RATIO_H1,
|
|
57
64
|
HEADING_RATIO_H2,
|
|
58
65
|
HEADING_RATIO_H3
|
|
59
|
-
} from "./chunk-
|
|
66
|
+
} from "./chunk-6BWU6SQX.js";
|
|
60
67
|
import {
|
|
61
68
|
parsePageRange
|
|
62
69
|
} from "./chunk-MOL7MDBG.js";
|
|
@@ -75,16 +82,20 @@ import {
|
|
|
75
82
|
findChildByLocalName,
|
|
76
83
|
localName,
|
|
77
84
|
rawTextContent
|
|
78
|
-
} from "./chunk-
|
|
85
|
+
} from "./chunk-YAKC46HZ.js";
|
|
79
86
|
import {
|
|
80
87
|
MAX_COLS,
|
|
81
88
|
MAX_ROWS,
|
|
82
89
|
blocksToMarkdown,
|
|
83
90
|
buildTable,
|
|
84
91
|
convertTableToText,
|
|
92
|
+
escapeGfm,
|
|
93
|
+
escapeLiteralDollar,
|
|
94
|
+
flattenLayoutTables,
|
|
85
95
|
hasStructuredCellContent,
|
|
86
|
-
mapPuaText
|
|
87
|
-
|
|
96
|
+
mapPuaText,
|
|
97
|
+
noteSuffix
|
|
98
|
+
} from "./chunk-NLU6LBLY.js";
|
|
88
99
|
import {
|
|
89
100
|
KordocError,
|
|
90
101
|
classifyError,
|
|
@@ -96,7 +107,7 @@ import {
|
|
|
96
107
|
sanitizeHref,
|
|
97
108
|
stripDtd,
|
|
98
109
|
toArrayBuffer
|
|
99
|
-
} from "./chunk-
|
|
110
|
+
} from "./chunk-TITDJWVZ.js";
|
|
100
111
|
|
|
101
112
|
// src/table/visual.ts
|
|
102
113
|
import { readFile as readFile2 } from "fs/promises";
|
|
@@ -654,7 +665,7 @@ function buildTableWithCellMeta(state, keepAnchoredEmptyCols, keepEmptyParagraph
|
|
|
654
665
|
for (const row of state.rows) {
|
|
655
666
|
for (const src of row) {
|
|
656
667
|
flatIdx++;
|
|
657
|
-
const needsBlocks = !!src.blocks && src.blocks.length > 0 && (src.hasStructure || src.blocks.some((b) => b.spans));
|
|
668
|
+
const needsBlocks = !!src.blocks && src.blocks.length > 0 && (src.hasStructure || src.blocks.some((b) => b.spans || b.footnoteText));
|
|
658
669
|
if (!needsBlocks && !src.isHeader) continue;
|
|
659
670
|
let target;
|
|
660
671
|
const trimmed = keepEmptyParagraphs ? src.text : src.text.trim();
|
|
@@ -686,7 +697,7 @@ function buildTableWithCellMeta(state, keepAnchoredEmptyCols, keepEmptyParagraph
|
|
|
686
697
|
}
|
|
687
698
|
function completeTable(newTable, tableStack, blocks, ctx) {
|
|
688
699
|
const parentTable = tableStack.length > 0 ? tableStack.pop() : null;
|
|
689
|
-
if (newTable.rows.length === 0) {
|
|
700
|
+
if (newTable.rows.every((r) => r.length === 0)) {
|
|
690
701
|
if (newTable.caption) blocks.push({ type: "paragraph", text: newTable.caption, pageNumber: ctx.page });
|
|
691
702
|
return parentTable;
|
|
692
703
|
}
|
|
@@ -836,6 +847,162 @@ function detectHwpxSectionPages(root) {
|
|
|
836
847
|
};
|
|
837
848
|
}
|
|
838
849
|
|
|
850
|
+
// src/hwpx/notes.ts
|
|
851
|
+
function codePointAttr(v) {
|
|
852
|
+
if (v == null || v === "") return void 0;
|
|
853
|
+
const n = parseInt(v, 10);
|
|
854
|
+
if (!Number.isFinite(n) || n < 0 || n > 1114111) return void 0;
|
|
855
|
+
return n === 0 ? "" : String.fromCodePoint(n);
|
|
856
|
+
}
|
|
857
|
+
function noteFormatFrom(get) {
|
|
858
|
+
return {
|
|
859
|
+
type: get("type") || "DIGIT",
|
|
860
|
+
userChar: get("userChar") ?? "",
|
|
861
|
+
prefixChar: get("prefixChar") ?? "",
|
|
862
|
+
suffixChar: get("suffixChar") ?? ""
|
|
863
|
+
};
|
|
864
|
+
}
|
|
865
|
+
function noteCore(num2, fmt, userChar) {
|
|
866
|
+
if (fmt.type === "USER_CHAR") return userChar ?? fmt.userChar;
|
|
867
|
+
return formatHeadNumber(num2, fmt.type);
|
|
868
|
+
}
|
|
869
|
+
function noteRefMark(attrs, fmt) {
|
|
870
|
+
const f = fmt ?? { type: "DIGIT", userChar: "", prefixChar: "", suffixChar: ")" };
|
|
871
|
+
const n = parseInt(attrs.number ?? "", 10);
|
|
872
|
+
const prefix = codePointAttr(attrs.prefixChar) ?? f.prefixChar;
|
|
873
|
+
const suffix = codePointAttr(attrs.suffixChar) ?? f.suffixChar;
|
|
874
|
+
return prefix + noteCore(Number.isFinite(n) ? n : 1, f, codePointAttr(attrs.userChar)) + suffix;
|
|
875
|
+
}
|
|
876
|
+
function noteAutoNumText(num2, fmt) {
|
|
877
|
+
const n = parseInt(num2 ?? "", 10);
|
|
878
|
+
return fmt.prefixChar + noteCore(Number.isFinite(n) ? n : 1, fmt) + fmt.suffixChar;
|
|
879
|
+
}
|
|
880
|
+
function isRenderedAutoNum(numType) {
|
|
881
|
+
return numType === "FOOTNOTE" || numType === "ENDNOTE" || numType === "PICTURE" || numType === "TABLE" || numType === "EQUATION";
|
|
882
|
+
}
|
|
883
|
+
function noteAttrsOf(el) {
|
|
884
|
+
return {
|
|
885
|
+
number: el.getAttribute("number"),
|
|
886
|
+
prefixChar: el.getAttribute("prefixChar"),
|
|
887
|
+
suffixChar: el.getAttribute("suffixChar"),
|
|
888
|
+
userChar: el.getAttribute("userChar")
|
|
889
|
+
};
|
|
890
|
+
}
|
|
891
|
+
function readSectionNoteFormats(root) {
|
|
892
|
+
const out = {};
|
|
893
|
+
const pick2 = (names) => {
|
|
894
|
+
for (const name of names) {
|
|
895
|
+
const els = root.getElementsByTagName(name);
|
|
896
|
+
if (els.length === 0) continue;
|
|
897
|
+
const f = findChildByLocalName(els[0], "autoNumFormat");
|
|
898
|
+
return f ? noteFormatFrom((n) => f.getAttribute(n)) : void 0;
|
|
899
|
+
}
|
|
900
|
+
return void 0;
|
|
901
|
+
};
|
|
902
|
+
out.footnote = pick2(["hp:footNotePr", "footNotePr"]);
|
|
903
|
+
out.endnote = pick2(["hp:endNotePr", "endNotePr"]);
|
|
904
|
+
return out;
|
|
905
|
+
}
|
|
906
|
+
function noteAutoNumOf(el) {
|
|
907
|
+
if (!isRenderedAutoNum(el.getAttribute("numType"))) return "";
|
|
908
|
+
const f = findChildByLocalName(el, "autoNumFormat");
|
|
909
|
+
return noteAutoNumText(el.getAttribute("num"), noteFormatFrom((n) => f?.getAttribute(n)));
|
|
910
|
+
}
|
|
911
|
+
|
|
912
|
+
// src/hwpx/run-spans.ts
|
|
913
|
+
var KORDOC_CHAR_CODE = "4";
|
|
914
|
+
var KORDOC_PARA_QUOTE = "6";
|
|
915
|
+
function spanModeOf(layout) {
|
|
916
|
+
if (layout === "default") return "kordoc";
|
|
917
|
+
if (layout === "gongmun") return "gongmun";
|
|
918
|
+
if (!layout) return "foreign";
|
|
919
|
+
return null;
|
|
920
|
+
}
|
|
921
|
+
function extractRunSpans(para2, ctx, mode, requireMixed) {
|
|
922
|
+
const styleMap = ctx.styleMap;
|
|
923
|
+
if (!styleMap) return null;
|
|
924
|
+
const spans = [];
|
|
925
|
+
let styled = false;
|
|
926
|
+
const kids = para2.childNodes;
|
|
927
|
+
if (!kids) return null;
|
|
928
|
+
for (let i = 0; i < kids.length; i++) {
|
|
929
|
+
const child = kids[i];
|
|
930
|
+
if (child.nodeType !== 1) continue;
|
|
931
|
+
const tag = (child.tagName || child.localName || "").replace(/^[^:]+:/, "");
|
|
932
|
+
if (tag === "linesegarray") continue;
|
|
933
|
+
if (tag !== "run" && tag !== "r") return null;
|
|
934
|
+
let text = "";
|
|
935
|
+
const rkids = child.childNodes;
|
|
936
|
+
for (let j = 0; j < (rkids?.length ?? 0); j++) {
|
|
937
|
+
const rc = rkids[j];
|
|
938
|
+
if (rc.nodeType !== 1) continue;
|
|
939
|
+
const rtag = (rc.tagName || rc.localName || "").replace(/^[^:]+:/, "");
|
|
940
|
+
if (rtag === "t") {
|
|
941
|
+
const tkids = rc.childNodes;
|
|
942
|
+
for (let k = 0; k < (tkids?.length ?? 0); k++) {
|
|
943
|
+
const tk = tkids[k];
|
|
944
|
+
if (tk.nodeType === 3 || tk.nodeType === 4) text += escapeLiteralDollar(tk.textContent || "");
|
|
945
|
+
else if (tk.nodeType === 1) {
|
|
946
|
+
const ttag = (tk.tagName || tk.localName || "").replace(/^[^:]+:/, "");
|
|
947
|
+
if (ttag === "tab" || ttag === "fwSpace" || ttag === "hwSpace" || ttag === "nbSpace") text += " ";
|
|
948
|
+
else return null;
|
|
949
|
+
}
|
|
950
|
+
}
|
|
951
|
+
} else if (rtag === "secPr" || rtag === "colPr") {
|
|
952
|
+
} else if (rtag === "ctrl") {
|
|
953
|
+
if (extractTextFromNode(rc)) return null;
|
|
954
|
+
} else {
|
|
955
|
+
return null;
|
|
956
|
+
}
|
|
957
|
+
}
|
|
958
|
+
if (!text) continue;
|
|
959
|
+
const prId = child.getAttribute("charPrIDRef") ?? "";
|
|
960
|
+
const cp = styleMap.charProperties.get(prId);
|
|
961
|
+
const span = { text };
|
|
962
|
+
if (mode !== "foreign" && prId === KORDOC_CHAR_CODE) span.code = true;
|
|
963
|
+
else {
|
|
964
|
+
if (cp?.bold) span.bold = true;
|
|
965
|
+
if (cp?.italic) span.italic = true;
|
|
966
|
+
if (cp?.strike) span.strike = true;
|
|
967
|
+
if (cp?.underline) span.underline = true;
|
|
968
|
+
}
|
|
969
|
+
if (span.bold || span.italic || span.strike || span.underline || span.code) styled = true;
|
|
970
|
+
spans.push(span);
|
|
971
|
+
}
|
|
972
|
+
if (!styled || spans.length === 0) return null;
|
|
973
|
+
const merged = [];
|
|
974
|
+
for (const s of spans) {
|
|
975
|
+
const last = merged[merged.length - 1];
|
|
976
|
+
if (last && !!last.bold === !!s.bold && !!last.italic === !!s.italic && !!last.strike === !!s.strike && !!last.underline === !!s.underline && !!last.code === !!s.code) {
|
|
977
|
+
last.text += s.text;
|
|
978
|
+
} else {
|
|
979
|
+
merged.push(s);
|
|
980
|
+
}
|
|
981
|
+
}
|
|
982
|
+
if (requireMixed && !merged.some((s) => s.strike || s.underline) && !merged.some((s) => !(s.bold || s.italic || s.code))) return null;
|
|
983
|
+
return merged;
|
|
984
|
+
}
|
|
985
|
+
function gongmunDepthFromIndent(para2, left, ctx) {
|
|
986
|
+
const styleMap = ctx.styleMap;
|
|
987
|
+
if (!styleMap) return null;
|
|
988
|
+
let unit = 0;
|
|
989
|
+
const kids = para2.childNodes;
|
|
990
|
+
for (let i = 0; i < (kids?.length ?? 0); i++) {
|
|
991
|
+
const child = kids[i];
|
|
992
|
+
if (child.nodeType !== 1) continue;
|
|
993
|
+
const tag = (child.tagName || child.localName || "").replace(/^[^:]+:/, "");
|
|
994
|
+
if (tag !== "run" && tag !== "r") continue;
|
|
995
|
+
const size = styleMap.charProperties.get(child.getAttribute("charPrIDRef") ?? "")?.fontSize;
|
|
996
|
+
if (size) unit = size * 100;
|
|
997
|
+
break;
|
|
998
|
+
}
|
|
999
|
+
if (!unit) return null;
|
|
1000
|
+
const r = left / unit;
|
|
1001
|
+
const nearInt = Math.round(r);
|
|
1002
|
+
const depth = Math.abs(r - nearInt) < 0.2 ? nearInt : Math.round(2 * r - 1);
|
|
1003
|
+
return depth >= 1 && depth < 8 ? depth : null;
|
|
1004
|
+
}
|
|
1005
|
+
|
|
839
1006
|
// src/hwpx/section-walker.ts
|
|
840
1007
|
function parseSectionXml(xml, styleMap, warnings, sectionNum, shared) {
|
|
841
1008
|
const parser = createXmlParser(warnings);
|
|
@@ -858,6 +1025,7 @@ function parseSectionXml(xml, styleMap, warnings, sectionNum, shared) {
|
|
|
858
1025
|
break;
|
|
859
1026
|
}
|
|
860
1027
|
}
|
|
1028
|
+
ctx.noteFormats = readSectionNoteFormats(doc.documentElement);
|
|
861
1029
|
const blocks = [];
|
|
862
1030
|
walkSection(doc.documentElement, blocks, null, [], ctx);
|
|
863
1031
|
return blocks;
|
|
@@ -924,7 +1092,7 @@ function walkSection(node, blocks, tableCtx, tableStack, ctx, depth = 0) {
|
|
|
924
1092
|
if (tableCtx) {
|
|
925
1093
|
tableCtx.currentRow = [];
|
|
926
1094
|
walkSection(el, blocks, tableCtx, tableStack, ctx, depth + 1);
|
|
927
|
-
|
|
1095
|
+
tableCtx.rows.push(tableCtx.currentRow);
|
|
928
1096
|
tableCtx.currentRow = [];
|
|
929
1097
|
}
|
|
930
1098
|
break;
|
|
@@ -960,7 +1128,7 @@ function walkSection(node, blocks, tableCtx, tableStack, ctx, depth = 0) {
|
|
|
960
1128
|
case "p": {
|
|
961
1129
|
const paraPg = ctx.paraPage?.get(el);
|
|
962
1130
|
if (paraPg !== void 0 && ctx.pageBase !== void 0) ctx.page = ctx.pageBase + paraPg + 1;
|
|
963
|
-
const { text: rawText, href, footnote, style, segments } = extractParagraphInfo(el, ctx.styleMap, ctx);
|
|
1131
|
+
const { text: rawText, href, footnote, style, segments, placeholderSpans } = extractParagraphInfo(el, ctx.styleMap, ctx);
|
|
964
1132
|
let text = rawText;
|
|
965
1133
|
let headingLevel;
|
|
966
1134
|
const ph = resolveParaHeading(el, ctx);
|
|
@@ -978,10 +1146,12 @@ function walkSection(node, blocks, tableCtx, tableStack, ctx, depth = 0) {
|
|
|
978
1146
|
}
|
|
979
1147
|
let segIdx = 0;
|
|
980
1148
|
let first = true;
|
|
1149
|
+
let lastCellBlock;
|
|
981
1150
|
const flush = () => {
|
|
982
1151
|
const s = segs[segIdx++];
|
|
983
1152
|
if (!s) return;
|
|
984
1153
|
const block = { type: "paragraph", text: s, pageNumber: ctx.page };
|
|
1154
|
+
if (cell2) lastCellBlock = block;
|
|
985
1155
|
if (first && !cell2) {
|
|
986
1156
|
first = false;
|
|
987
1157
|
if (style) block.style = style;
|
|
@@ -996,16 +1166,19 @@ function walkSection(node, blocks, tableCtx, tableStack, ctx, depth = 0) {
|
|
|
996
1166
|
};
|
|
997
1167
|
tableCtx = walkParagraphChildren(el, blocks, tableCtx, tableStack, ctx, depth + 1, flush);
|
|
998
1168
|
while (segIdx < segs.length) flush();
|
|
999
|
-
if (footnote && cell2)
|
|
1169
|
+
if (footnote && cell2) {
|
|
1170
|
+
cell2.text += (cell2.text ? "\n" : "") + `(\uC8FC: ${footnote})`;
|
|
1171
|
+
if (lastCellBlock) lastCellBlock.footnoteText = footnote;
|
|
1172
|
+
}
|
|
1000
1173
|
break;
|
|
1001
1174
|
}
|
|
1002
1175
|
if (text) {
|
|
1003
1176
|
if (tableCtx?.cell) {
|
|
1004
1177
|
const cell2 = tableCtx.cell;
|
|
1005
|
-
|
|
1006
|
-
cell2.text += (cell2.text || cell2.paraSeen ? "\n" : "") + text;
|
|
1178
|
+
cell2.text += (cell2.text || cell2.paraSeen ? "\n" : "") + (footnote ? `${text} (\uC8FC: ${footnote})` : text);
|
|
1007
1179
|
cell2.paraSeen = true;
|
|
1008
1180
|
const cellBlock = { type: "paragraph", text, pageNumber: ctx.page };
|
|
1181
|
+
if (footnote) cellBlock.footnoteText = footnote;
|
|
1009
1182
|
const cellSpanMode = spanModeOf(ctx.shared.kordocLayout);
|
|
1010
1183
|
if (cellSpanMode && !ph?.prefix) {
|
|
1011
1184
|
const spans = extractRunSpans(el, ctx, cellSpanMode, cellSpanMode !== "kordoc");
|
|
@@ -1013,7 +1186,7 @@ function walkSection(node, blocks, tableCtx, tableStack, ctx, depth = 0) {
|
|
|
1013
1186
|
cellBlock.spans = spans;
|
|
1014
1187
|
}
|
|
1015
1188
|
}
|
|
1016
|
-
;
|
|
1189
|
+
if (!cellBlock.spans && placeholderSpans) cellBlock.spans = withPrefixSpan(placeholderSpans, ph?.prefix);
|
|
1017
1190
|
(cell2.blocks ??= []).push(cellBlock);
|
|
1018
1191
|
} else if (!tableCtx) {
|
|
1019
1192
|
if (ctx.shared.kordocLayout && /^─{10,}$/.test(text)) {
|
|
@@ -1045,6 +1218,7 @@ function walkSection(node, blocks, tableCtx, tableStack, ctx, depth = 0) {
|
|
|
1045
1218
|
}
|
|
1046
1219
|
if (spanMode !== "foreign" && el.getAttribute("paraPrIDRef") === KORDOC_PARA_QUOTE) block.quote = true;
|
|
1047
1220
|
}
|
|
1221
|
+
if (!headingLevel && !block.spans && placeholderSpans) block.spans = withPrefixSpan(placeholderSpans, ph?.prefix);
|
|
1048
1222
|
blocks.push(block);
|
|
1049
1223
|
} else {
|
|
1050
1224
|
blocks.push({ type: "paragraph", text, pageNumber: ctx.page });
|
|
@@ -1124,7 +1298,7 @@ function userShapeComment(el) {
|
|
|
1124
1298
|
function mergeBlocksIntoCell(cell2, sink) {
|
|
1125
1299
|
for (const b of sink) {
|
|
1126
1300
|
if ((b.type === "paragraph" || b.type === "heading") && b.text) {
|
|
1127
|
-
cell2.text += (cell2.text ? "\n" : "") + b.text;
|
|
1301
|
+
cell2.text += (cell2.text ? "\n" : "") + b.text + (b.footnoteText ? ` (\uC8FC: ${b.footnoteText})` : "");
|
|
1128
1302
|
(cell2.blocks ??= []).push(b);
|
|
1129
1303
|
} else if (b.type === "image" || b.type === "table") {
|
|
1130
1304
|
if (b.type === "image" && b.text) {
|
|
@@ -1180,7 +1354,7 @@ function hasPageAutoNum(el, depth = 0) {
|
|
|
1180
1354
|
function collectSubListText(el, ctx, depth = 0) {
|
|
1181
1355
|
return collectSubListContent(el, ctx, depth).text;
|
|
1182
1356
|
}
|
|
1183
|
-
function collectSubListContent(el, ctx, depth = 0) {
|
|
1357
|
+
function collectSubListContent(el, ctx, depth = 0, sep = "\n") {
|
|
1184
1358
|
const out = { text: "", blocks: [], hasStructure: false };
|
|
1185
1359
|
if (depth > 10) return out;
|
|
1186
1360
|
const parts = [];
|
|
@@ -1191,7 +1365,9 @@ function collectSubListContent(el, ctx, depth = 0) {
|
|
|
1191
1365
|
if (ch.nodeType !== 1) continue;
|
|
1192
1366
|
const tag = (ch.tagName || ch.localName || "").replace(/^[^:]+:/, "");
|
|
1193
1367
|
if (tag === "p" || tag === "para") {
|
|
1194
|
-
|
|
1368
|
+
let t = extractParagraphInfo(ch, ctx.styleMap, ctx).text;
|
|
1369
|
+
const ph = resolveParaHeading(ch, ctx);
|
|
1370
|
+
if (t && ph?.prefix) t = ph.prefix + " " + t;
|
|
1195
1371
|
if (t) {
|
|
1196
1372
|
parts.push(t);
|
|
1197
1373
|
out.blocks.push({ type: "paragraph", text: t, pageNumber: ctx.page });
|
|
@@ -1206,6 +1382,10 @@ function collectSubListContent(el, ctx, depth = 0) {
|
|
|
1206
1382
|
out.hasStructure = true;
|
|
1207
1383
|
}
|
|
1208
1384
|
}
|
|
1385
|
+
for (const t2 of drawTextParaTexts(ch, ctx)) {
|
|
1386
|
+
parts.push(t2);
|
|
1387
|
+
out.blocks.push({ type: "paragraph", text: t2, pageNumber: ctx.page });
|
|
1388
|
+
}
|
|
1209
1389
|
} else if (tag === "tbl") {
|
|
1210
1390
|
const built = buildSubListTable(ch, ctx, depth);
|
|
1211
1391
|
if (built.text) parts.push(built.text);
|
|
@@ -1214,13 +1394,41 @@ function collectSubListContent(el, ctx, depth = 0) {
|
|
|
1214
1394
|
out.hasStructure = true;
|
|
1215
1395
|
}
|
|
1216
1396
|
} else {
|
|
1217
|
-
const sub = collectSubListContent(ch, ctx, depth + 1);
|
|
1397
|
+
const sub = collectSubListContent(ch, ctx, depth + 1, sep);
|
|
1218
1398
|
if (sub.text) parts.push(sub.text);
|
|
1219
1399
|
out.blocks.push(...sub.blocks);
|
|
1220
1400
|
if (sub.hasStructure) out.hasStructure = true;
|
|
1221
1401
|
}
|
|
1222
1402
|
}
|
|
1223
|
-
out.text = parts.join(
|
|
1403
|
+
out.text = parts.join(sep).trim();
|
|
1404
|
+
return out;
|
|
1405
|
+
}
|
|
1406
|
+
function drawTextParaTexts(para2, ctx) {
|
|
1407
|
+
const out = [];
|
|
1408
|
+
const visit = (node, depth, inDrawText) => {
|
|
1409
|
+
if (depth > MAX_XML_DEPTH) return;
|
|
1410
|
+
const kids = node.childNodes;
|
|
1411
|
+
if (!kids) return;
|
|
1412
|
+
for (let i = 0; i < kids.length; i++) {
|
|
1413
|
+
const ch = kids[i];
|
|
1414
|
+
if (ch.nodeType !== 1) continue;
|
|
1415
|
+
const tag = (ch.tagName || ch.localName || "").replace(/^[^:]+:/, "");
|
|
1416
|
+
if (tag === "tbl" || tag === "footNote" || tag === "endNote") continue;
|
|
1417
|
+
if (tag === "drawText") {
|
|
1418
|
+
if (!inDrawText) visit(ch, depth + 1, true);
|
|
1419
|
+
continue;
|
|
1420
|
+
}
|
|
1421
|
+
if (inDrawText && (tag === "p" || tag === "para")) {
|
|
1422
|
+
let t = extractParagraphInfo(ch, ctx.styleMap, ctx).text.trim();
|
|
1423
|
+
const ph = resolveParaHeading(ch, ctx);
|
|
1424
|
+
if (t && ph?.prefix) t = ph.prefix + " " + t;
|
|
1425
|
+
if (t) out.push(t);
|
|
1426
|
+
continue;
|
|
1427
|
+
}
|
|
1428
|
+
visit(ch, depth + 1, inDrawText);
|
|
1429
|
+
}
|
|
1430
|
+
};
|
|
1431
|
+
visit(para2, 0, false);
|
|
1224
1432
|
return out;
|
|
1225
1433
|
}
|
|
1226
1434
|
function buildSubListTable(el, ctx, depth) {
|
|
@@ -1368,6 +1576,29 @@ function extractDrawTextBlocks(drawTextNode, blocks, ctx) {
|
|
|
1368
1576
|
}
|
|
1369
1577
|
}
|
|
1370
1578
|
}
|
|
1579
|
+
var PH_OPEN = "";
|
|
1580
|
+
var PH_CLOSE = "";
|
|
1581
|
+
function clickHereGuide(fieldBegin) {
|
|
1582
|
+
if ((fieldBegin.getAttribute("type") || "").toUpperCase() !== "CLICK_HERE") return void 0;
|
|
1583
|
+
if (fieldBegin.getAttribute("dirty") === "1") return void 0;
|
|
1584
|
+
const children = findChildByLocalName(fieldBegin, "parameters")?.childNodes;
|
|
1585
|
+
if (!children) return void 0;
|
|
1586
|
+
let fromCommand;
|
|
1587
|
+
for (let i = 0; i < children.length; i++) {
|
|
1588
|
+
const ch = children[i];
|
|
1589
|
+
if (ch.nodeType !== 1) continue;
|
|
1590
|
+
const tag = (ch.tagName || ch.localName || "").replace(/^[^:]+:/, "");
|
|
1591
|
+
if (tag !== "stringParam") continue;
|
|
1592
|
+
const name = ch.getAttribute("name");
|
|
1593
|
+
if (name === "Direction") return ch.textContent || void 0;
|
|
1594
|
+
if (name === "Command") {
|
|
1595
|
+
const cmd = ch.textContent || "";
|
|
1596
|
+
const m = /Direction:wstring:(\d+):/.exec(cmd);
|
|
1597
|
+
if (m) fromCommand = cmd.slice(m.index + m[0].length, m.index + m[0].length + Number(m[1])) || void 0;
|
|
1598
|
+
}
|
|
1599
|
+
}
|
|
1600
|
+
return fromCommand;
|
|
1601
|
+
}
|
|
1371
1602
|
function extractHyperlinkHref(fieldBegin) {
|
|
1372
1603
|
if ((fieldBegin.getAttribute("type") || "").toUpperCase() !== "HYPERLINK") return void 0;
|
|
1373
1604
|
const params = findChildByLocalName(fieldBegin, "parameters");
|
|
@@ -1404,12 +1635,26 @@ function extractParagraphInfo(para2, styleMap, ctx) {
|
|
|
1404
1635
|
openFields.push({ rangeIdx: linkRanges.length - 1 });
|
|
1405
1636
|
if (!href) href = url;
|
|
1406
1637
|
} else {
|
|
1407
|
-
|
|
1638
|
+
const guide = clickHereGuide(el);
|
|
1639
|
+
openFields.push(guide ? { guide, start: text.length } : {});
|
|
1408
1640
|
}
|
|
1409
1641
|
};
|
|
1410
1642
|
const onFieldEnd = () => {
|
|
1411
1643
|
const open = openFields.pop();
|
|
1412
1644
|
if (open?.rangeIdx !== void 0) linkRanges[open.rangeIdx].end = text.length;
|
|
1645
|
+
if (open?.guide !== void 0 && open.start !== void 0) {
|
|
1646
|
+
const value = text.slice(open.start).replace(/\\\$/g, "$");
|
|
1647
|
+
if (value && (value === open.guide || value.trimEnd() === open.guide)) {
|
|
1648
|
+
text = text.slice(0, open.start) + PH_OPEN + text.slice(open.start) + PH_CLOSE;
|
|
1649
|
+
}
|
|
1650
|
+
}
|
|
1651
|
+
};
|
|
1652
|
+
const addNote = (noteEl, tag) => {
|
|
1653
|
+
if (isInDeletedRange(ctx)) return;
|
|
1654
|
+
const endnote = tag === "endNote" || tag === "en";
|
|
1655
|
+
text += noteRefMark(noteAttrsOf(noteEl), endnote ? ctx?.noteFormats?.endnote : ctx?.noteFormats?.footnote);
|
|
1656
|
+
const noteText = ctx ? collectSubListContent(noteEl, ctx, 0, " ").text : extractTextFromNode(noteEl);
|
|
1657
|
+
if (noteText) footnote = (footnote ? footnote + "; " : "") + noteText;
|
|
1413
1658
|
};
|
|
1414
1659
|
const handleCtrl = (ctrlEl) => {
|
|
1415
1660
|
const kids = ctrlEl.childNodes;
|
|
@@ -1433,13 +1678,11 @@ function extractParagraphInfo(para2, styleMap, ctx) {
|
|
|
1433
1678
|
}
|
|
1434
1679
|
break;
|
|
1435
1680
|
}
|
|
1436
|
-
// 각주/미주 — 해당 문단의 footnote로 인라인 보존
|
|
1681
|
+
// 각주/미주 — 개체 자리에 참조 부호, 본문은 해당 문단의 footnote로 인라인 보존
|
|
1437
1682
|
case "footNote":
|
|
1438
|
-
case "endNote":
|
|
1439
|
-
|
|
1440
|
-
if (noteText) footnote = (footnote ? footnote + "; " : "") + noteText;
|
|
1683
|
+
case "endNote":
|
|
1684
|
+
addNote(k, ktag);
|
|
1441
1685
|
break;
|
|
1442
|
-
}
|
|
1443
1686
|
// 하이퍼링크 — fieldBegin type=HYPERLINK의 Path 파라미터 (extent 오프셋 추적)
|
|
1444
1687
|
case "fieldBegin":
|
|
1445
1688
|
onFieldBegin(k);
|
|
@@ -1465,13 +1708,16 @@ function extractParagraphInfo(para2, styleMap, ctx) {
|
|
|
1465
1708
|
}
|
|
1466
1709
|
break;
|
|
1467
1710
|
}
|
|
1711
|
+
// 자동번호 — 주석 머리 번호("1)"·"문1)")·캡션 번호("<그림 1>")만 그린다. 쪽번호 등은 종전대로 미방출
|
|
1712
|
+
case "autoNum":
|
|
1713
|
+
if (!isInDeletedRange(ctx)) text += noteAutoNumOf(k);
|
|
1714
|
+
break;
|
|
1468
1715
|
// 콘텐츠 없는 제어 요소 — 스킵
|
|
1469
1716
|
case "bookmark":
|
|
1470
1717
|
case "pageNum":
|
|
1471
1718
|
case "pageNumCtrl":
|
|
1472
1719
|
case "pageHiding":
|
|
1473
1720
|
case "newNum":
|
|
1474
|
-
case "autoNum":
|
|
1475
1721
|
case "indexmark":
|
|
1476
1722
|
case "colPr":
|
|
1477
1723
|
break;
|
|
@@ -1501,7 +1747,7 @@ function extractParagraphInfo(para2, styleMap, ctx) {
|
|
|
1501
1747
|
ctx.warnings?.push({ page: ctx.page, message: "\uBCC0\uACBD\uCD94\uC801 \uC0AD\uC81C \uD14D\uC2A4\uD2B8 \uCD9C\uB825 \uC81C\uC678", code: "HIDDEN_TEXT_FILTERED" });
|
|
1502
1748
|
}
|
|
1503
1749
|
} else {
|
|
1504
|
-
text += t.replace(/\x1E/g, "");
|
|
1750
|
+
text += escapeLiteralDollar(t.replace(/\x1E/g, ""));
|
|
1505
1751
|
}
|
|
1506
1752
|
continue;
|
|
1507
1753
|
}
|
|
@@ -1554,9 +1800,14 @@ function extractParagraphInfo(para2, styleMap, ctx) {
|
|
|
1554
1800
|
case "footNote":
|
|
1555
1801
|
case "endNote":
|
|
1556
1802
|
case "fn":
|
|
1557
|
-
case "en":
|
|
1558
|
-
|
|
1559
|
-
|
|
1803
|
+
case "en":
|
|
1804
|
+
addNote(child, tag);
|
|
1805
|
+
break;
|
|
1806
|
+
// 대체 표현 묶음 — 한컴은 지원하는 hp:case 하나만 그린다(차트, 없으면 hp:default 의 OLE).
|
|
1807
|
+
// 둘 다 순회하면 같은 캡션이 두 번 나온다 (1790387 [그림 5] 이중 방출)
|
|
1808
|
+
case "switch": {
|
|
1809
|
+
const branch = findChildByLocalName(child, "case") ?? findChildByLocalName(child, "default");
|
|
1810
|
+
if (branch) walk(branch, depth + 1);
|
|
1560
1811
|
break;
|
|
1561
1812
|
}
|
|
1562
1813
|
// 제어 요소 — 선별 순회 (머리말/꼬리말/각주/하이퍼링크/변경추적, v3.0)
|
|
@@ -1608,7 +1859,7 @@ function extractParagraphInfo(para2, styleMap, ctx) {
|
|
|
1608
1859
|
if (raw.trim()) {
|
|
1609
1860
|
try {
|
|
1610
1861
|
const latex = hmlToLatex(raw).trim();
|
|
1611
|
-
if (latex) text += " $" + latex + "$ ";
|
|
1862
|
+
if (latex) text += " $" + latex.replace(/\$/g, "\\$") + "$ ";
|
|
1612
1863
|
} catch {
|
|
1613
1864
|
if (ctx?.warnings) {
|
|
1614
1865
|
ctx.warnings.push({
|
|
@@ -1657,10 +1908,21 @@ function extractParagraphInfo(para2, styleMap, ctx) {
|
|
|
1657
1908
|
};
|
|
1658
1909
|
let segments;
|
|
1659
1910
|
if (text.includes("")) {
|
|
1660
|
-
segments = text.split("").map(cleanParaText);
|
|
1911
|
+
segments = text.split("").map((s) => cleanParaText(stripPlaceholderMarks(s)));
|
|
1661
1912
|
text = text.replace(/\x1E/g, "");
|
|
1662
1913
|
}
|
|
1663
|
-
|
|
1914
|
+
let cleanText = cleanParaText(segments ? stripPlaceholderMarks(text) : text);
|
|
1915
|
+
let placeholderSpans;
|
|
1916
|
+
if (cleanText.includes(PH_OPEN)) {
|
|
1917
|
+
placeholderSpans = [];
|
|
1918
|
+
for (const part of cleanText.split(/(\x1C[^\x1C\x1D]*\x1D)/)) {
|
|
1919
|
+
if (!part) continue;
|
|
1920
|
+
if (part.startsWith(PH_OPEN)) placeholderSpans.push({ text: part.slice(1, -1), placeholder: true });
|
|
1921
|
+
else placeholderSpans.push({ text: stripPlaceholderMarks(part) });
|
|
1922
|
+
}
|
|
1923
|
+
cleanText = stripPlaceholderMarks(cleanText);
|
|
1924
|
+
if (!placeholderSpans.some((s) => s.placeholder && s.text)) placeholderSpans = void 0;
|
|
1925
|
+
}
|
|
1664
1926
|
let style;
|
|
1665
1927
|
if (styleMap && charPrId) {
|
|
1666
1928
|
const charProp = styleMap.charProperties.get(charPrId);
|
|
@@ -1673,99 +1935,13 @@ function extractParagraphInfo(para2, styleMap, ctx) {
|
|
|
1673
1935
|
if (!style.fontSize && !style.bold && !style.italic) style = void 0;
|
|
1674
1936
|
}
|
|
1675
1937
|
}
|
|
1676
|
-
return { text: cleanText, href, footnote, style, segments };
|
|
1938
|
+
return { text: cleanText, href, footnote, style, segments, placeholderSpans };
|
|
1677
1939
|
}
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
function spanModeOf(layout) {
|
|
1681
|
-
if (layout === "default") return "kordoc";
|
|
1682
|
-
if (layout === "gongmun") return "gongmun";
|
|
1683
|
-
if (!layout) return "foreign";
|
|
1684
|
-
return null;
|
|
1685
|
-
}
|
|
1686
|
-
function extractRunSpans(para2, ctx, mode, requireMixed) {
|
|
1687
|
-
const styleMap = ctx.styleMap;
|
|
1688
|
-
if (!styleMap) return null;
|
|
1689
|
-
const spans = [];
|
|
1690
|
-
let styled = false;
|
|
1691
|
-
const kids = para2.childNodes;
|
|
1692
|
-
if (!kids) return null;
|
|
1693
|
-
for (let i = 0; i < kids.length; i++) {
|
|
1694
|
-
const child = kids[i];
|
|
1695
|
-
if (child.nodeType !== 1) continue;
|
|
1696
|
-
const tag = (child.tagName || child.localName || "").replace(/^[^:]+:/, "");
|
|
1697
|
-
if (tag === "linesegarray") continue;
|
|
1698
|
-
if (tag !== "run" && tag !== "r") return null;
|
|
1699
|
-
let text = "";
|
|
1700
|
-
const rkids = child.childNodes;
|
|
1701
|
-
for (let j = 0; j < (rkids?.length ?? 0); j++) {
|
|
1702
|
-
const rc = rkids[j];
|
|
1703
|
-
if (rc.nodeType !== 1) continue;
|
|
1704
|
-
const rtag = (rc.tagName || rc.localName || "").replace(/^[^:]+:/, "");
|
|
1705
|
-
if (rtag === "t") {
|
|
1706
|
-
const tkids = rc.childNodes;
|
|
1707
|
-
for (let k = 0; k < (tkids?.length ?? 0); k++) {
|
|
1708
|
-
const tk = tkids[k];
|
|
1709
|
-
if (tk.nodeType === 3 || tk.nodeType === 4) text += tk.textContent || "";
|
|
1710
|
-
else if (tk.nodeType === 1) {
|
|
1711
|
-
const ttag = (tk.tagName || tk.localName || "").replace(/^[^:]+:/, "");
|
|
1712
|
-
if (ttag === "tab" || ttag === "fwSpace" || ttag === "hwSpace" || ttag === "nbSpace") text += " ";
|
|
1713
|
-
else return null;
|
|
1714
|
-
}
|
|
1715
|
-
}
|
|
1716
|
-
} else if (rtag === "secPr" || rtag === "colPr") {
|
|
1717
|
-
} else if (rtag === "ctrl") {
|
|
1718
|
-
if (extractTextFromNode(rc)) return null;
|
|
1719
|
-
} else {
|
|
1720
|
-
return null;
|
|
1721
|
-
}
|
|
1722
|
-
}
|
|
1723
|
-
if (!text) continue;
|
|
1724
|
-
const prId = child.getAttribute("charPrIDRef") ?? "";
|
|
1725
|
-
const cp = styleMap.charProperties.get(prId);
|
|
1726
|
-
const span = { text };
|
|
1727
|
-
if (mode !== "foreign" && prId === KORDOC_CHAR_CODE) span.code = true;
|
|
1728
|
-
else {
|
|
1729
|
-
if (cp?.bold) span.bold = true;
|
|
1730
|
-
if (cp?.italic) span.italic = true;
|
|
1731
|
-
if (cp?.strike) span.strike = true;
|
|
1732
|
-
if (cp?.underline) span.underline = true;
|
|
1733
|
-
}
|
|
1734
|
-
if (span.bold || span.italic || span.strike || span.underline || span.code) styled = true;
|
|
1735
|
-
spans.push(span);
|
|
1736
|
-
}
|
|
1737
|
-
if (!styled || spans.length === 0) return null;
|
|
1738
|
-
const merged = [];
|
|
1739
|
-
for (const s of spans) {
|
|
1740
|
-
const last = merged[merged.length - 1];
|
|
1741
|
-
if (last && !!last.bold === !!s.bold && !!last.italic === !!s.italic && !!last.strike === !!s.strike && !!last.underline === !!s.underline && !!last.code === !!s.code) {
|
|
1742
|
-
last.text += s.text;
|
|
1743
|
-
} else {
|
|
1744
|
-
merged.push(s);
|
|
1745
|
-
}
|
|
1746
|
-
}
|
|
1747
|
-
if (requireMixed && !merged.some((s) => s.strike || s.underline) && !merged.some((s) => !(s.bold || s.italic || s.code))) return null;
|
|
1748
|
-
return merged;
|
|
1940
|
+
function withPrefixSpan(spans, prefix) {
|
|
1941
|
+
return prefix ? [{ text: prefix + " " }, ...spans] : spans;
|
|
1749
1942
|
}
|
|
1750
|
-
function
|
|
1751
|
-
|
|
1752
|
-
if (!styleMap) return null;
|
|
1753
|
-
let unit = 0;
|
|
1754
|
-
const kids = para2.childNodes;
|
|
1755
|
-
for (let i = 0; i < (kids?.length ?? 0); i++) {
|
|
1756
|
-
const child = kids[i];
|
|
1757
|
-
if (child.nodeType !== 1) continue;
|
|
1758
|
-
const tag = (child.tagName || child.localName || "").replace(/^[^:]+:/, "");
|
|
1759
|
-
if (tag !== "run" && tag !== "r") continue;
|
|
1760
|
-
const size = styleMap.charProperties.get(child.getAttribute("charPrIDRef") ?? "")?.fontSize;
|
|
1761
|
-
if (size) unit = size * 100;
|
|
1762
|
-
break;
|
|
1763
|
-
}
|
|
1764
|
-
if (!unit) return null;
|
|
1765
|
-
const r = left / unit;
|
|
1766
|
-
const nearInt = Math.round(r);
|
|
1767
|
-
const depth = Math.abs(r - nearInt) < 0.2 ? nearInt : Math.round(2 * r - 1);
|
|
1768
|
-
return depth >= 1 && depth < 8 ? depth : null;
|
|
1943
|
+
function stripPlaceholderMarks(s) {
|
|
1944
|
+
return s.includes(PH_OPEN) || s.includes(PH_CLOSE) ? s.replace(/[\x1C\x1D]/g, "") : s;
|
|
1769
1945
|
}
|
|
1770
1946
|
|
|
1771
1947
|
// src/hwpx/images.ts
|
|
@@ -2038,21 +2214,65 @@ function parseSectionPathsFromManifest(xml) {
|
|
|
2038
2214
|
// src/hwpx/metadata.ts
|
|
2039
2215
|
async function extractHwpxMetadata(zip, metadata, decompressed) {
|
|
2040
2216
|
try {
|
|
2041
|
-
const
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
if (!file) continue;
|
|
2217
|
+
const read = async (path) => {
|
|
2218
|
+
const file = zip.file(path) || Object.values(zip.files).find((f) => f.name.toLowerCase() === path.toLowerCase()) || null;
|
|
2219
|
+
if (!file) return null;
|
|
2045
2220
|
const xml = await file.async("text");
|
|
2046
2221
|
if (decompressed) {
|
|
2047
2222
|
decompressed.total += xml.length * 2;
|
|
2048
2223
|
if (decompressed.total > MAX_DECOMPRESS_SIZE) throw new KordocError("ZIP \uC555\uCD95 \uD574\uC81C \uD06C\uAE30 \uCD08\uACFC (ZIP bomb \uC758\uC2EC)");
|
|
2049
2224
|
}
|
|
2225
|
+
return xml;
|
|
2226
|
+
};
|
|
2227
|
+
for (const hp of ["Contents/content.hpf", "content.hpf"]) {
|
|
2228
|
+
const xml = await read(hp);
|
|
2229
|
+
if (!xml) continue;
|
|
2230
|
+
parseOpfMetadata(xml, metadata);
|
|
2231
|
+
break;
|
|
2232
|
+
}
|
|
2233
|
+
if (metadata.title || metadata.author) return;
|
|
2234
|
+
const metaPaths = ["meta.xml", "META-INF/meta.xml", "docProps/core.xml"];
|
|
2235
|
+
for (const mp of metaPaths) {
|
|
2236
|
+
const xml = await read(mp);
|
|
2237
|
+
if (!xml) continue;
|
|
2050
2238
|
parseDublinCoreMetadata(xml, metadata);
|
|
2051
2239
|
if (metadata.title || metadata.author) return;
|
|
2052
2240
|
}
|
|
2053
2241
|
} catch {
|
|
2054
2242
|
}
|
|
2055
2243
|
}
|
|
2244
|
+
function parseOpfMetadata(xml, metadata) {
|
|
2245
|
+
const root = createXmlParser().parseFromString(stripDtd(xml), "text/xml").documentElement;
|
|
2246
|
+
const md = root ? findChildByLocalName(root, "metadata") : null;
|
|
2247
|
+
if (!md) return;
|
|
2248
|
+
let title;
|
|
2249
|
+
const meta = /* @__PURE__ */ new Map();
|
|
2250
|
+
for (const el of elementChildren(md)) {
|
|
2251
|
+
const text = el.textContent?.trim();
|
|
2252
|
+
if (!text) continue;
|
|
2253
|
+
const tag = localName(el);
|
|
2254
|
+
if (tag === "title") title ??= text;
|
|
2255
|
+
else if (tag === "meta") {
|
|
2256
|
+
const name = el.getAttribute("name");
|
|
2257
|
+
if (name && !meta.has(name)) meta.set(name, text);
|
|
2258
|
+
}
|
|
2259
|
+
}
|
|
2260
|
+
const put = (k, v) => {
|
|
2261
|
+
if (v !== void 0 && !metadata[k]) metadata[k] = v;
|
|
2262
|
+
};
|
|
2263
|
+
const keywords = meta.get("keyword")?.split(/[,;]/).map((k) => k.trim()).filter(Boolean);
|
|
2264
|
+
put("title", title);
|
|
2265
|
+
put("author", meta.get("creator"));
|
|
2266
|
+
put("description", meta.get("description") || meta.get("subject"));
|
|
2267
|
+
put("keywords", keywords?.length ? keywords : void 0);
|
|
2268
|
+
put("createdAt", isoDate(meta.get("CreatedDate")));
|
|
2269
|
+
put("modifiedAt", isoDate(meta.get("ModifiedDate")));
|
|
2270
|
+
}
|
|
2271
|
+
function isoDate(s) {
|
|
2272
|
+
if (!s || s.startsWith("1601-01-01")) return void 0;
|
|
2273
|
+
const m = /^(\d{4}-\d{2}-\d{2}) (\d{2}:\d{2}:\d{2})$/.exec(s);
|
|
2274
|
+
return m ? `${m[1]}T${m[2]}` : s;
|
|
2275
|
+
}
|
|
2056
2276
|
function parseDublinCoreMetadata(xml, metadata) {
|
|
2057
2277
|
const parser = createXmlParser();
|
|
2058
2278
|
const doc = parser.parseFromString(stripDtd(xml), "text/xml");
|
|
@@ -14526,6 +14746,32 @@ function decodeHwp3Extra(ch) {
|
|
|
14526
14746
|
case 12038:
|
|
14527
14747
|
return 9632;
|
|
14528
14748
|
// ■ hwp3-sample10 "제목차례" 좌우 장식 2건
|
|
14749
|
+
// SO-SUEOP ↔ 한컴 HWP3→HWPX 변환본 문맥 정렬 — 코드 개수와 변환본 글자 증가분이 전부 일치
|
|
14750
|
+
// (“ 13·” 14·‧ 4·《 3·》 3·— 1·∣ 6·↳ 18). 앞 셋은 항등, 0x3062~0x30BD 는 사적 문장부호
|
|
14751
|
+
case 8212:
|
|
14752
|
+
return 8212;
|
|
14753
|
+
// —
|
|
14754
|
+
case 8739:
|
|
14755
|
+
return 8739;
|
|
14756
|
+
// ∣
|
|
14757
|
+
case 8627:
|
|
14758
|
+
return 8627;
|
|
14759
|
+
// ↳
|
|
14760
|
+
case 12386:
|
|
14761
|
+
return 8220;
|
|
14762
|
+
// “
|
|
14763
|
+
case 12387:
|
|
14764
|
+
return 8221;
|
|
14765
|
+
// ”
|
|
14766
|
+
case 12390:
|
|
14767
|
+
return 8231;
|
|
14768
|
+
// ‧
|
|
14769
|
+
case 12475:
|
|
14770
|
+
return 12298;
|
|
14771
|
+
// 《
|
|
14772
|
+
case 12477:
|
|
14773
|
+
return 12299;
|
|
14774
|
+
// 》
|
|
14529
14775
|
// 관인·서명란 도장 기호. rhwp 는 U+F012B 로 보존하고 렌더러가 `(인)` 으로 편다 —
|
|
14530
14776
|
// kordoc 도 PUA 를 그대로 내보내면 shared/pua.ts 의 검증표가 같은 문자열로 편다.
|
|
14531
14777
|
case 11214:
|
|
@@ -14677,16 +14923,78 @@ function decryptHwp3Document(input, password) {
|
|
|
14677
14923
|
return out;
|
|
14678
14924
|
}
|
|
14679
14925
|
|
|
14926
|
+
// src/hwp3/table.ts
|
|
14927
|
+
var NEIGHBOR_SLACK = 10;
|
|
14928
|
+
var MAX_NEIGHBOR_SCAN = 2048;
|
|
14929
|
+
var uniqSorted = (values) => [...new Set(values)].sort((a, b) => a - b);
|
|
14930
|
+
function edgeAt(edges, v) {
|
|
14931
|
+
let lo = 0, hi = edges.length - 1;
|
|
14932
|
+
while (lo < hi) {
|
|
14933
|
+
const mid = lo + hi >> 1;
|
|
14934
|
+
if (edges[mid] < v) lo = mid + 1;
|
|
14935
|
+
else hi = mid;
|
|
14936
|
+
}
|
|
14937
|
+
return lo;
|
|
14938
|
+
}
|
|
14939
|
+
function nextStarts(geo, axis) {
|
|
14940
|
+
const start = axis === "x" ? (g) => g.x : (g) => g.y;
|
|
14941
|
+
const end = axis === "x" ? (g) => g.r : (g) => g.b;
|
|
14942
|
+
const overlap = axis === "x" ? (a, b) => b.y < a.b && b.b > a.y : (a, b) => b.x < a.r && b.r > a.x;
|
|
14943
|
+
const order = geo.map((_, i) => i).sort((i, j) => start(geo[i]) - start(geo[j]));
|
|
14944
|
+
const starts = order.map((i) => start(geo[i]));
|
|
14945
|
+
return geo.map((a) => {
|
|
14946
|
+
let k = edgeAt(starts, Math.max(end(a) - NEIGHBOR_SLACK, start(a) + 1));
|
|
14947
|
+
for (let steps = 0; k < order.length && steps < MAX_NEIGHBOR_SCAN; k++, steps++) {
|
|
14948
|
+
const b = geo[order[k]];
|
|
14949
|
+
if (start(b) <= start(a)) continue;
|
|
14950
|
+
if (overlap(a, b)) return start(b);
|
|
14951
|
+
}
|
|
14952
|
+
return void 0;
|
|
14953
|
+
});
|
|
14954
|
+
}
|
|
14955
|
+
function hwp3CellGrid(info, count) {
|
|
14956
|
+
const geo = [];
|
|
14957
|
+
let right = 0, bottom = 0;
|
|
14958
|
+
for (let k = 0; k < count; k++) {
|
|
14959
|
+
const o = k * 27;
|
|
14960
|
+
const x = info.readUInt16LE(o + 4), y = info.readUInt16LE(o + 6);
|
|
14961
|
+
const g = { x, y, r: x + info.readUInt16LE(o + 8), b: y + info.readUInt16LE(o + 10) };
|
|
14962
|
+
geo.push(g);
|
|
14963
|
+
if (g.r > right) right = g.r;
|
|
14964
|
+
if (g.b > bottom) bottom = g.b;
|
|
14965
|
+
}
|
|
14966
|
+
const xs = uniqSorted([...geo.map((g) => g.x), right]);
|
|
14967
|
+
const ys = uniqSorted([...geo.map((g) => g.y), bottom]);
|
|
14968
|
+
const nextX = nextStarts(geo, "x");
|
|
14969
|
+
const nextY = nextStarts(geo, "y");
|
|
14970
|
+
const cells = geo.map((g, k) => {
|
|
14971
|
+
const col = edgeAt(xs, g.x), row = edgeAt(ys, g.y);
|
|
14972
|
+
const c2 = nextX[k] !== void 0 ? edgeAt(xs, nextX[k]) : xs.length - 1;
|
|
14973
|
+
const r2 = nextY[k] !== void 0 ? edgeAt(ys, nextY[k]) : ys.length - 1;
|
|
14974
|
+
return { row, col, rowSpan: Math.min(Math.max(1, r2 - row), MAX_ROWS), colSpan: Math.min(Math.max(1, c2 - col), MAX_COLS) };
|
|
14975
|
+
});
|
|
14976
|
+
return { rows: Math.min(Math.max(1, ys.length - 1), MAX_ROWS), cols: Math.min(Math.max(1, xs.length - 1), MAX_COLS), cells };
|
|
14977
|
+
}
|
|
14978
|
+
|
|
14680
14979
|
// src/hwp3/parser.ts
|
|
14681
14980
|
var MAX_DECOMPRESS_SIZE2 = 100 * 1024 * 1024;
|
|
14682
14981
|
var PIC_TYPE_DRAWING = 3;
|
|
14982
|
+
var OBJ_EQUATION = 2;
|
|
14983
|
+
var OPT_HYPERTEXT = 16;
|
|
14984
|
+
var LEADER_TAB_MARK = "";
|
|
14985
|
+
var OUTLINE_FORMATS = [
|
|
14986
|
+
{ fmt: "romanUpper", before: "", after: "." },
|
|
14987
|
+
{ fmt: "digit", before: "", after: ")" },
|
|
14988
|
+
{ fmt: "digit", before: "(", after: ")" },
|
|
14989
|
+
{ fmt: "ganada", before: "", after: "." },
|
|
14990
|
+
{ fmt: "ganada", before: "", after: ")" },
|
|
14991
|
+
{ fmt: "ganada", before: "(", after: ")" },
|
|
14992
|
+
{ fmt: "circled", before: "", after: "" }
|
|
14993
|
+
];
|
|
14683
14994
|
var PARA_SHAPE_SIZE = 187;
|
|
14684
14995
|
var LINE_INFO_SIZE = 14;
|
|
14685
14996
|
var INLINE_CHAR_SHAPE_SIZE = 31;
|
|
14686
14997
|
var SIMPLE_CTRL = /* @__PURE__ */ new Map([
|
|
14687
|
-
[9, { extraBytes: 6, extraHchar: 3, emit: " " }],
|
|
14688
|
-
[18, { extraBytes: 6, extraHchar: 3, emit: " " }],
|
|
14689
|
-
// AutoNumber → 공백 (HWP5 패턴)
|
|
14690
14998
|
// 19/20/21 (새 번호·쪽 번호 위치·쪽 감추기) 는 비가시 마커다 — 한글도 그 자리에
|
|
14691
14999
|
// 아무 글자를 내지 않는다 (rhwp #4957). 종전엔 U+FFFC 를 본문에 심어 마크다운에
|
|
14692
15000
|
// 대체 문자가 그대로 남았다 (samples 33개 중 6문서 11건). 차례 표식(25) 과 같은 계약.
|
|
@@ -14699,8 +15007,7 @@ var SIMPLE_CTRL = /* @__PURE__ */ new Map([
|
|
|
14699
15007
|
[25, { extraBytes: 4, extraHchar: 2, emit: null }],
|
|
14700
15008
|
// 차례 표식 — 비가시 (rhwp #2765)
|
|
14701
15009
|
[26, { extraBytes: 244, extraHchar: 122, emit: "\uFFFC" }],
|
|
14702
|
-
[
|
|
14703
|
-
[30, { extraBytes: 2, extraHchar: 1, emit: "\xA0" }],
|
|
15010
|
+
[30, { extraBytes: 2, extraHchar: 1, emit: " " }],
|
|
14704
15011
|
[31, { extraBytes: 2, extraHchar: 1, emit: " " }]
|
|
14705
15012
|
]);
|
|
14706
15013
|
function parseHwp3Document(buffer, options) {
|
|
@@ -14733,18 +15040,18 @@ function parseHwp3Document(buffer, options) {
|
|
|
14733
15040
|
body = tail;
|
|
14734
15041
|
}
|
|
14735
15042
|
const bodyReader = new Reader(body);
|
|
14736
|
-
const ctx = {
|
|
15043
|
+
const ctx = { warnings, headerBlocks: [], footerBlocks: [], headerTexts: /* @__PURE__ */ new Set(), pageNumbers: 0, noteSeq: [0, 0] };
|
|
15044
|
+
const bodyBlocks = [];
|
|
14737
15045
|
try {
|
|
14738
15046
|
skipFontFacesAndStyles(bodyReader);
|
|
14739
|
-
parseParagraphList(bodyReader, ctx);
|
|
15047
|
+
parseParagraphList(bodyReader, ctx, bodyBlocks);
|
|
14740
15048
|
} catch (err) {
|
|
14741
15049
|
warnings.push({
|
|
14742
15050
|
code: "PARTIAL_PARSE",
|
|
14743
15051
|
message: `HWP3 paragraph stream \uB3C4\uC911 \uD30C\uC2F1 \uC911\uB2E8: ${err instanceof Error ? err.message : String(err)}`
|
|
14744
15052
|
});
|
|
14745
15053
|
}
|
|
14746
|
-
const
|
|
14747
|
-
const blocks = ctx.paragraphs.map((p) => ({ type: "paragraph", text: p }));
|
|
15054
|
+
const blocks = flattenLayoutTables([...ctx.headerBlocks, ...bodyBlocks, ...ctx.footerBlocks]);
|
|
14748
15055
|
const metadata = {
|
|
14749
15056
|
title: header.title || void 0,
|
|
14750
15057
|
author: header.author || void 0,
|
|
@@ -14753,7 +15060,7 @@ function parseHwp3Document(buffer, options) {
|
|
|
14753
15060
|
version: "3.0"
|
|
14754
15061
|
};
|
|
14755
15062
|
return {
|
|
14756
|
-
markdown:
|
|
15063
|
+
markdown: blocksToMarkdown(blocks),
|
|
14757
15064
|
blocks,
|
|
14758
15065
|
metadata,
|
|
14759
15066
|
warnings: warnings.length ? warnings : void 0
|
|
@@ -14768,7 +15075,7 @@ function skipFontFacesAndStyles(reader) {
|
|
|
14768
15075
|
const nStyles = reader.readU16();
|
|
14769
15076
|
reader.skip(nStyles * STYLE_RECORD_SIZE);
|
|
14770
15077
|
}
|
|
14771
|
-
function parseParagraphList(reader, ctx) {
|
|
15078
|
+
function parseParagraphList(reader, ctx, sink) {
|
|
14772
15079
|
for (; ; ) {
|
|
14773
15080
|
if (reader.eof()) return;
|
|
14774
15081
|
const followPrev = reader.readU8();
|
|
@@ -14798,40 +15105,77 @@ function parseParagraphList(reader, ctx) {
|
|
|
14798
15105
|
if (flag !== 1) reader.skip(INLINE_CHAR_SHAPE_SIZE);
|
|
14799
15106
|
}
|
|
14800
15107
|
}
|
|
15108
|
+
const para2 = { text: "", objects: [], footnotes: [] };
|
|
14801
15109
|
try {
|
|
14802
|
-
|
|
14803
|
-
ctx.paragraphs.push(text);
|
|
15110
|
+
parseCharStream(reader, charCount, ctx, para2);
|
|
14804
15111
|
} catch (err) {
|
|
15112
|
+
sink.push(...emitHwp3Paragraph(para2));
|
|
14805
15113
|
ctx.warnings.push({
|
|
14806
15114
|
code: "PARTIAL_PARSE",
|
|
14807
|
-
message: `HWP3 paragraph
|
|
15115
|
+
message: `HWP3 paragraph char stream \uD30C\uC2F1 \uC2E4\uD328: ${err instanceof Error ? err.message : String(err)}`
|
|
14808
15116
|
});
|
|
14809
15117
|
return;
|
|
14810
15118
|
}
|
|
15119
|
+
sink.push(...emitHwp3Paragraph(para2));
|
|
14811
15120
|
}
|
|
14812
15121
|
}
|
|
14813
|
-
function
|
|
14814
|
-
let
|
|
15122
|
+
function emitHwp3Paragraph(para2) {
|
|
15123
|
+
let text = para2.text;
|
|
15124
|
+
const leaderAt = text.indexOf(LEADER_TAB_MARK);
|
|
15125
|
+
if (leaderAt >= 0) text = text.slice(0, leaderAt);
|
|
15126
|
+
let headMarker = null;
|
|
15127
|
+
if (para2.outline) {
|
|
15128
|
+
const { level, numbers } = para2.outline;
|
|
15129
|
+
const f = OUTLINE_FORMATS[level];
|
|
15130
|
+
const n = numbers[level];
|
|
15131
|
+
if (f && n > 0) headMarker = f.before + formatNumber(n, f.fmt) + f.after;
|
|
15132
|
+
text = text.replace(/^ +/, "");
|
|
15133
|
+
}
|
|
15134
|
+
return emitParagraphBlocks({ text: text.trim(), headMarker, headingLevel: 0, footnotes: para2.footnotes, objects: para2.objects });
|
|
15135
|
+
}
|
|
15136
|
+
function parseCharStream(reader, charCount, ctx, para2) {
|
|
14815
15137
|
let i = 0;
|
|
14816
15138
|
while (i < charCount) {
|
|
14817
15139
|
const ch = reader.readU16();
|
|
14818
15140
|
i += 1;
|
|
14819
15141
|
if (ch === 13) {
|
|
14820
|
-
|
|
15142
|
+
para2.text += "\n";
|
|
14821
15143
|
continue;
|
|
14822
15144
|
}
|
|
14823
15145
|
if (ch === 0) {
|
|
14824
15146
|
continue;
|
|
14825
15147
|
}
|
|
14826
15148
|
if (ch >= 32) {
|
|
14827
|
-
|
|
15149
|
+
para2.text += escapeLiteralDollar(decodeJohabText(ch) ?? "");
|
|
15150
|
+
continue;
|
|
15151
|
+
}
|
|
15152
|
+
if (ch === 9) {
|
|
15153
|
+
reader.skip(2);
|
|
15154
|
+
const leader = reader.readU16();
|
|
15155
|
+
reader.skip(2);
|
|
15156
|
+
i += 3;
|
|
15157
|
+
para2.text += leader ? LEADER_TAB_MARK : " ";
|
|
15158
|
+
continue;
|
|
15159
|
+
}
|
|
15160
|
+
if (ch === 18) {
|
|
15161
|
+
const info = reader.readBytes(6);
|
|
15162
|
+
i += 3;
|
|
15163
|
+
para2.text += autoNumberText(info.readUInt16LE(0), info.readUInt16LE(2), ctx);
|
|
15164
|
+
continue;
|
|
15165
|
+
}
|
|
15166
|
+
if (ch === 28) {
|
|
15167
|
+
const info = reader.readBytes(62);
|
|
15168
|
+
i += 31;
|
|
15169
|
+
if (info.readUInt16LE(0) === 1) {
|
|
15170
|
+
para2.outline = { level: Math.min(info[3], 6), numbers: Array.from({ length: 7 }, (_, k) => info.readUInt16LE(4 + k * 2)) };
|
|
15171
|
+
}
|
|
14828
15172
|
continue;
|
|
14829
15173
|
}
|
|
14830
15174
|
const simple = SIMPLE_CTRL.get(ch);
|
|
14831
15175
|
if (simple) {
|
|
14832
15176
|
reader.skip(simple.extraBytes);
|
|
14833
15177
|
i += simple.extraHchar;
|
|
14834
|
-
if (simple.emit)
|
|
15178
|
+
if (simple.emit) para2.text += simple.emit;
|
|
14835
15179
|
continue;
|
|
14836
15180
|
}
|
|
14837
15181
|
const headerVal1 = reader.readU32();
|
|
@@ -14839,27 +15183,31 @@ function parseCharStream(reader, charCount, ctx) {
|
|
|
14839
15183
|
i += 3;
|
|
14840
15184
|
switch (ch) {
|
|
14841
15185
|
case 10:
|
|
14842
|
-
|
|
15186
|
+
parseTableLike(reader, ctx, para2);
|
|
14843
15187
|
break;
|
|
14844
15188
|
case 11:
|
|
14845
|
-
parsePicture(reader, ctx);
|
|
15189
|
+
para2.objects.push({ blocks: parsePicture(reader, ctx), table: false, inline: false });
|
|
14846
15190
|
break;
|
|
14847
15191
|
case 14:
|
|
14848
15192
|
reader.skip(84);
|
|
14849
15193
|
break;
|
|
14850
15194
|
case 15: {
|
|
14851
15195
|
reader.skip(8);
|
|
14852
|
-
|
|
15196
|
+
const hidden = [];
|
|
15197
|
+
parseParagraphList(reader, ctx, hidden);
|
|
15198
|
+
if (hidden.length > 0 && !ctx.warnings.some((w) => w.code === "HIDDEN_TEXT_FILTERED")) {
|
|
15199
|
+
ctx.warnings.push({ code: "HIDDEN_TEXT_FILTERED", message: "HWP3 \uC228\uC740 \uC124\uBA85 \uD14D\uC2A4\uD2B8 \uBCF8\uBB38 \uC81C\uC678" });
|
|
15200
|
+
}
|
|
14853
15201
|
break;
|
|
14854
15202
|
}
|
|
14855
15203
|
case 16: {
|
|
14856
|
-
reader.
|
|
14857
|
-
|
|
15204
|
+
const info = reader.readBytes(10);
|
|
15205
|
+
parseHeaderFooter(reader, ctx, info[8] === 1);
|
|
14858
15206
|
break;
|
|
14859
15207
|
}
|
|
14860
15208
|
case 17: {
|
|
14861
|
-
reader.
|
|
14862
|
-
|
|
15209
|
+
const info = reader.readBytes(14);
|
|
15210
|
+
parseNote(reader, ctx, para2, info.readUInt16LE(8), info.readUInt16LE(10) === 1 ? 1 : 0);
|
|
14863
15211
|
break;
|
|
14864
15212
|
}
|
|
14865
15213
|
case 5:
|
|
@@ -14887,9 +15235,38 @@ function parseCharStream(reader, charCount, ctx) {
|
|
|
14887
15235
|
break;
|
|
14888
15236
|
}
|
|
14889
15237
|
}
|
|
14890
|
-
|
|
15238
|
+
para2.text = para2.text.trim();
|
|
15239
|
+
}
|
|
15240
|
+
function autoNumberText(type, num2, ctx) {
|
|
15241
|
+
if (type === 0) {
|
|
15242
|
+
ctx.pageNumbers++;
|
|
15243
|
+
return "";
|
|
15244
|
+
}
|
|
15245
|
+
if (num2 <= 0) return " ";
|
|
15246
|
+
return type === 1 || type === 2 ? `${num2})` : String(num2);
|
|
15247
|
+
}
|
|
15248
|
+
function parseNote(reader, ctx, para2, number, kind) {
|
|
15249
|
+
const blocks = [];
|
|
15250
|
+
parseParagraphList(reader, ctx, blocks);
|
|
15251
|
+
const n = number > 0 ? number : ++ctx.noteSeq[kind];
|
|
15252
|
+
const marker = `${n})`;
|
|
15253
|
+
para2.text += marker;
|
|
15254
|
+
const content = blocksPlainText(blocks, " ");
|
|
15255
|
+
if (content) para2.footnotes.push(content.startsWith(marker) ? content : `${marker} ${content}`);
|
|
15256
|
+
}
|
|
15257
|
+
function parseHeaderFooter(reader, ctx, isFooter) {
|
|
15258
|
+
const pagesBefore = ctx.pageNumbers;
|
|
15259
|
+
const blocks = [];
|
|
15260
|
+
parseParagraphList(reader, ctx, blocks);
|
|
15261
|
+
const text = blocksPlainText(blocks, "\n");
|
|
15262
|
+
if (!text) return;
|
|
15263
|
+
if (ctx.pageNumbers > pagesBefore && !/\p{L}/u.test(text)) return;
|
|
15264
|
+
const key = (isFooter ? "f:" : "h:") + text;
|
|
15265
|
+
if (ctx.headerTexts.has(key)) return;
|
|
15266
|
+
ctx.headerTexts.add(key);
|
|
15267
|
+
(isFooter ? ctx.footerBlocks : ctx.headerBlocks).push({ type: "paragraph", text });
|
|
14891
15268
|
}
|
|
14892
|
-
function parseTableLike(reader, ctx) {
|
|
15269
|
+
function parseTableLike(reader, ctx, para2) {
|
|
14893
15270
|
const info = reader.readBytes(84);
|
|
14894
15271
|
const cellCount = info.readUInt16LE(80) || 1;
|
|
14895
15272
|
if (27 * cellCount > reader.remaining()) {
|
|
@@ -14899,22 +15276,63 @@ function parseTableLike(reader, ctx) {
|
|
|
14899
15276
|
});
|
|
14900
15277
|
throw new Error(`HWP3 \uBE44\uC815\uC0C1 cell_count=${cellCount}`);
|
|
14901
15278
|
}
|
|
14902
|
-
reader.
|
|
14903
|
-
|
|
14904
|
-
|
|
15279
|
+
const cellInfo = reader.readBytes(27 * cellCount);
|
|
15280
|
+
const cellBlocks = [];
|
|
15281
|
+
try {
|
|
15282
|
+
for (let i = 0; i < cellCount; i++) {
|
|
15283
|
+
const blocks = [];
|
|
15284
|
+
cellBlocks.push(blocks);
|
|
15285
|
+
parseParagraphList(reader, ctx, blocks);
|
|
15286
|
+
}
|
|
15287
|
+
} catch (err) {
|
|
15288
|
+
para2.objects.push({ blocks: cellBlocks.flat(), table: false, inline: false });
|
|
15289
|
+
throw err;
|
|
15290
|
+
}
|
|
15291
|
+
const captionBlocks = [];
|
|
15292
|
+
parseParagraphList(reader, ctx, captionBlocks);
|
|
15293
|
+
const caption = blocksPlainText(captionBlocks, " ");
|
|
15294
|
+
if (info.readUInt16LE(78) === OBJ_EQUATION) {
|
|
15295
|
+
const script = (cellBlocks[0] ?? []).map((b) => b.text ?? "").join("\n").trim();
|
|
15296
|
+
const latex = script ? hwpEquationToLatex(script) : "";
|
|
15297
|
+
if (latex) para2.text += `$${latex.replace(/\$/g, "\\$")}$`;
|
|
15298
|
+
if (caption) para2.objects.push({ blocks: [{ type: "paragraph", text: caption }], table: false, inline: false });
|
|
15299
|
+
return;
|
|
14905
15300
|
}
|
|
14906
|
-
|
|
14907
|
-
|
|
15301
|
+
if (info.readUInt16LE(14) & OPT_HYPERTEXT) {
|
|
15302
|
+
para2.text += blocksPlainText(cellBlocks.flat(), " ");
|
|
15303
|
+
if (caption) para2.objects.push({ blocks: [{ type: "paragraph", text: caption }], table: false, inline: false });
|
|
15304
|
+
return;
|
|
15305
|
+
}
|
|
15306
|
+
const grid = hwp3CellGrid(cellInfo, cellCount);
|
|
15307
|
+
const cells = cellBlocks.map((blocks, k) => {
|
|
15308
|
+
const pos = grid.cells[k];
|
|
15309
|
+
const { text, hasStructure } = cellTextFromBlocks(blocks);
|
|
15310
|
+
const cell2 = { text, colSpan: pos.colSpan, rowSpan: pos.rowSpan, colAddr: pos.col, rowAddr: pos.row };
|
|
15311
|
+
if (hasStructure && blocks.length > 0) cell2.blocks = blocks;
|
|
15312
|
+
return cell2;
|
|
15313
|
+
});
|
|
15314
|
+
cells.sort((a, b) => a.rowAddr - b.rowAddr || a.colAddr - b.colAddr);
|
|
15315
|
+
const table2 = buildAddressedTable(cells, grid.rows, grid.cols);
|
|
15316
|
+
if (!table2) {
|
|
15317
|
+
para2.objects.push({ blocks: [...cellBlocks.flat(), ...captionBlocks], table: false, inline: false });
|
|
15318
|
+
return;
|
|
15319
|
+
}
|
|
15320
|
+
if (caption) table2.caption = caption;
|
|
15321
|
+
const inline = info[8] === 0;
|
|
15322
|
+
if (inline) para2.text += INLINE_TABLE_MARK;
|
|
15323
|
+
para2.objects.push({ blocks: [{ type: "table", table: table2 }], table: true, inline });
|
|
14908
15324
|
}
|
|
14909
15325
|
function parsePicture(reader, ctx) {
|
|
14910
15326
|
const info = reader.readBytes(348);
|
|
14911
15327
|
const nExt = info.readUInt32LE(0);
|
|
14912
15328
|
let ext = null;
|
|
14913
15329
|
if (nExt > 0 && nExt < 100 * 1024 * 1024) ext = reader.readBytes(nExt);
|
|
14914
|
-
|
|
14915
|
-
|
|
15330
|
+
const blocks = [];
|
|
15331
|
+
if (ext && info[74] === PIC_TYPE_DRAWING) parseDrawingObject(ext, ctx, blocks);
|
|
15332
|
+
parseParagraphList(reader, ctx, blocks);
|
|
15333
|
+
return blocks;
|
|
14916
15334
|
}
|
|
14917
|
-
function parseDrawingObject(ext, ctx) {
|
|
15335
|
+
function parseDrawingObject(ext, ctx, sink) {
|
|
14918
15336
|
let lists;
|
|
14919
15337
|
try {
|
|
14920
15338
|
lists = collectDrawingTextBoxLists(ext);
|
|
@@ -14924,7 +15342,7 @@ function parseDrawingObject(ext, ctx) {
|
|
|
14924
15342
|
}
|
|
14925
15343
|
for (const list of lists) {
|
|
14926
15344
|
try {
|
|
14927
|
-
parseParagraphList(new Reader(list), ctx);
|
|
15345
|
+
parseParagraphList(new Reader(list), ctx, sink);
|
|
14928
15346
|
} catch (err) {
|
|
14929
15347
|
pushDrawingWarning(ctx, `HWP3 \uAE00\uC0C1\uC790 \uBB38\uB2E8 \uB9AC\uC2A4\uD2B8 \uD30C\uC2F1 \uC2E4\uD328: ${errText(err)}`);
|
|
14930
15348
|
}
|
|
@@ -18192,7 +18610,9 @@ function scanClickHereRegions(xml) {
|
|
|
18192
18610
|
placeholder: extractPlaceholder(fieldBeginXml),
|
|
18193
18611
|
start,
|
|
18194
18612
|
end,
|
|
18195
|
-
prefix
|
|
18613
|
+
prefix,
|
|
18614
|
+
beginTagStart: m.index,
|
|
18615
|
+
beginTagEnd: m.index + m[0].length
|
|
18196
18616
|
});
|
|
18197
18617
|
}
|
|
18198
18618
|
return regions;
|
|
@@ -18216,6 +18636,13 @@ function fillClickHereInXml(xml, cursor, blockedLabels) {
|
|
|
18216
18636
|
const current = xml.slice(region.start, region.end);
|
|
18217
18637
|
matchedKeys.add(key);
|
|
18218
18638
|
filled.push({ label: region.name, value, row: -1, col: -1, key, source: "clickhere" });
|
|
18639
|
+
if (value !== "") {
|
|
18640
|
+
const tag = xml.slice(region.beginTagStart, region.beginTagEnd);
|
|
18641
|
+
if (attrOf2(tag, "dirty") !== "1") {
|
|
18642
|
+
const dirtyTag = /\sdirty="[^"]*"/.test(tag) ? tag.replace(/(\sdirty=")[^"]*"/, (_, head) => head + '1"') : tag.replace(/(\/?>)$/, ' dirty="1"$1');
|
|
18643
|
+
splices.push({ start: region.beginTagStart, end: region.beginTagEnd, replacement: dirtyTag });
|
|
18644
|
+
}
|
|
18645
|
+
}
|
|
18219
18646
|
if (current === replacement) continue;
|
|
18220
18647
|
splices.push({ start: region.start, end: region.end, replacement });
|
|
18221
18648
|
}
|
|
@@ -19562,7 +19989,7 @@ function parseMarkdownToBlocks(md) {
|
|
|
19562
19989
|
}
|
|
19563
19990
|
function parseInlineMarkdown(text) {
|
|
19564
19991
|
const literals = [];
|
|
19565
|
-
text = text.replace(/\x00/g, "").replace(/\\([\\`*_{}[\]()
|
|
19992
|
+
text = text.replace(/\x00/g, "").replace(/\\([\\`*_{}[\]()#+\-.!|<>~$])/g, (_, c) => {
|
|
19566
19993
|
literals.push(c);
|
|
19567
19994
|
return `\0${literals.length - 1}\0`;
|
|
19568
19995
|
});
|
|
@@ -20773,16 +21200,6 @@ function bestSimInRange(arr, from, to, target) {
|
|
|
20773
21200
|
}
|
|
20774
21201
|
return best;
|
|
20775
21202
|
}
|
|
20776
|
-
function escapeGfm(text) {
|
|
20777
|
-
const NUL = String.fromCharCode(0);
|
|
20778
|
-
const spans = [];
|
|
20779
|
-
const masked = text.replace(/!\[[^\]]*\]\([^)\n]*\)|\]\((?:https?:|mailto:|tel:|#)[^)\n]*\)/gi, (m) => {
|
|
20780
|
-
spans.push(m);
|
|
20781
|
-
return NUL + (spans.length - 1) + NUL;
|
|
20782
|
-
});
|
|
20783
|
-
const escaped = masked.replace(/([~*_`])/g, "\\$1");
|
|
20784
|
-
return escaped.replace(new RegExp(NUL + "(\\d+)" + NUL, "g"), (_, n) => spans[Number(n)]);
|
|
20785
|
-
}
|
|
20786
21203
|
var HWP_SHAPE_ALT_TEXT_RE = /^(?:모서리가 둥근 |둥근 )?(?:사각형|직사각형|정사각형|원|타원|삼각형|이등변 삼각형|직각 삼각형|선|직선|곡선|화살표|굵은 화살표|이중 화살표|오각형|육각형|팔각형|별|[4-8]점별|십자|십자형|구름|구름형|마름모|도넛|평행사변형|사다리꼴|부채꼴|호|반원|물결|번개|하트|빗금|블록 화살표|수식|표|그림|개체|그리기\s?개체|묶음\s?개체|글상자|수식\s?개체|OLE\s?개체)\s?입니다\.?$/gm;
|
|
20787
21204
|
function sanitizeText(text) {
|
|
20788
21205
|
let result = mapPuaText(text).replace(/[\u{F0000}-\u{FFFFD}]/gu, "").replace(HWP_SHAPE_ALT_TEXT_RE, "").replace(/ +/g, " ").trim();
|
|
@@ -20796,10 +21213,10 @@ function sanitizeText(text) {
|
|
|
20796
21213
|
return result;
|
|
20797
21214
|
}
|
|
20798
21215
|
function normForMatch(text) {
|
|
20799
|
-
return sanitizeText(text).replace(/\s+/g, " ").trim();
|
|
21216
|
+
return sanitizeText(text.replace(/\\\$/g, "$")).replace(/\s+/g, " ").trim();
|
|
20800
21217
|
}
|
|
20801
21218
|
function unescapeGfm(text) {
|
|
20802
|
-
return text.replace(/\\([~*_
|
|
21219
|
+
return text.replace(/\\([~*_`|#<$\\])/g, "$1");
|
|
20803
21220
|
}
|
|
20804
21221
|
function summarize(text) {
|
|
20805
21222
|
const t = text.replace(/\s+/g, " ").trim();
|
|
@@ -20818,7 +21235,7 @@ function replicateGfmTable(table2) {
|
|
|
20818
21235
|
const cell2 = cells[r]?.[c];
|
|
20819
21236
|
if (!cell2) continue;
|
|
20820
21237
|
display[r][c] = {
|
|
20821
|
-
text: escapeGfm(sanitizeText(cell2.text)).replace(
|
|
21238
|
+
text: escapeGfm(sanitizeText(cell2.text)).replace(/(?<!\\)\|/g, "\\|").replace(/\n/g, "<br>"),
|
|
20822
21239
|
gridR: r,
|
|
20823
21240
|
gridC: c
|
|
20824
21241
|
};
|
|
@@ -20866,7 +21283,7 @@ function parseGfmTable(lines) {
|
|
|
20866
21283
|
return rows;
|
|
20867
21284
|
}
|
|
20868
21285
|
function unescapeGfmCell(text) {
|
|
20869
|
-
return text.replace(
|
|
21286
|
+
return text.replace(/(?<!\\)<br\s*\/?>/gi, "\n").replace(/\\\|/g, "|").replace(/\\([~*_`#<$\\])/g, "$1");
|
|
20870
21287
|
}
|
|
20871
21288
|
function replicateCellInnerHtml(cell2) {
|
|
20872
21289
|
if (cell2.blocks?.length) {
|
|
@@ -20877,7 +21294,7 @@ function replicateCellInnerHtml(cell2) {
|
|
|
20877
21294
|
}
|
|
20878
21295
|
if (b.type === "image" && b.text) return `<img src="${b.text}" alt="image">`;
|
|
20879
21296
|
const t = sanitizeText(b.text ?? "");
|
|
20880
|
-
return t ? t.replace(/\n/g, "<br>") : "";
|
|
21297
|
+
return t ? (t + noteSuffix(b)).replace(/\n/g, "<br>") : "";
|
|
20881
21298
|
}).filter(Boolean).join("<br>");
|
|
20882
21299
|
}
|
|
20883
21300
|
return sanitizeText(cell2.text).replace(/\n/g, "<br>");
|
|
@@ -20894,7 +21311,7 @@ function replicateTableToHtml(table2) {
|
|
|
20894
21311
|
const attrStr = attrs.length ? " " + attrs.join(" ") : "";
|
|
20895
21312
|
return `<${tag}${attrStr}>${cell2.inner}</${tag}>`;
|
|
20896
21313
|
});
|
|
20897
|
-
|
|
21314
|
+
lines.push(`<tr>${rowHtml.join("")}</tr>`);
|
|
20898
21315
|
}
|
|
20899
21316
|
lines.push("</table>");
|
|
20900
21317
|
return lines.join("\n");
|
|
@@ -20924,7 +21341,7 @@ function replicateHtmlTable(table2) {
|
|
|
20924
21341
|
gridC: c
|
|
20925
21342
|
});
|
|
20926
21343
|
}
|
|
20927
|
-
|
|
21344
|
+
result.push({ tag, cells: rowCells });
|
|
20928
21345
|
}
|
|
20929
21346
|
return result;
|
|
20930
21347
|
}
|
|
@@ -26398,6 +26815,11 @@ function resolveParagraphMappings(blocks, scans) {
|
|
|
26398
26815
|
if (b.type !== "paragraph" && b.type !== "heading" || !b.text || !footerNorms.has(normForMatch(b.text))) break;
|
|
26399
26816
|
pageText.add(i);
|
|
26400
26817
|
}
|
|
26818
|
+
const noted = scans.flatMap((scan) => {
|
|
26819
|
+
if (!/<(?:\w+:)?(?:footNote|endNote)\b/.test(scan.xml)) return [];
|
|
26820
|
+
const fmts = sectionNoteFormats(scan.xml);
|
|
26821
|
+
return scan.bodyParagraphs.map((p) => ({ key: normForMatch(p.text), marks: paraNoteMarks(scan.xml, p, fmts) })).filter((x) => x.key && x.marks.length > 0);
|
|
26822
|
+
});
|
|
26401
26823
|
const counters = /* @__PURE__ */ new Map();
|
|
26402
26824
|
const result = /* @__PURE__ */ new Map();
|
|
26403
26825
|
for (let i = 0; i < blocks.length; i++) {
|
|
@@ -26409,13 +26831,26 @@ function resolveParagraphMappings(blocks, scans) {
|
|
|
26409
26831
|
}
|
|
26410
26832
|
let key = normForMatch(b.text);
|
|
26411
26833
|
let prefixStripped = false;
|
|
26834
|
+
let noteMarks;
|
|
26412
26835
|
if (!buckets.has(key)) {
|
|
26413
26836
|
const sp = b.text.indexOf(" ");
|
|
26414
|
-
|
|
26415
|
-
|
|
26416
|
-
|
|
26417
|
-
|
|
26418
|
-
|
|
26837
|
+
const alt = sp > 0 ? normForMatch(b.text.slice(sp + 1)) : "";
|
|
26838
|
+
if (alt && buckets.has(alt)) {
|
|
26839
|
+
key = alt;
|
|
26840
|
+
prefixStripped = true;
|
|
26841
|
+
} else {
|
|
26842
|
+
for (const n of noted) {
|
|
26843
|
+
if (textWithoutMarks(key, n.key, n.marks)) {
|
|
26844
|
+
key = n.key;
|
|
26845
|
+
noteMarks = n.marks;
|
|
26846
|
+
break;
|
|
26847
|
+
}
|
|
26848
|
+
if (alt && textWithoutMarks(alt, n.key, n.marks)) {
|
|
26849
|
+
key = n.key;
|
|
26850
|
+
noteMarks = n.marks;
|
|
26851
|
+
prefixStripped = true;
|
|
26852
|
+
break;
|
|
26853
|
+
}
|
|
26419
26854
|
}
|
|
26420
26855
|
}
|
|
26421
26856
|
}
|
|
@@ -26426,10 +26861,90 @@ function resolveParagraphMappings(blocks, scans) {
|
|
|
26426
26861
|
}
|
|
26427
26862
|
const occ = counters.get(key) ?? 0;
|
|
26428
26863
|
counters.set(key, occ + 1);
|
|
26429
|
-
result.set(i, occ < list.length ? { para: list[occ], prefixStripped } : {});
|
|
26864
|
+
result.set(i, occ < list.length ? { para: list[occ], prefixStripped, noteMarks } : {});
|
|
26430
26865
|
}
|
|
26431
26866
|
return result;
|
|
26432
26867
|
}
|
|
26868
|
+
function sectionNoteFormats(xml) {
|
|
26869
|
+
const pick2 = (tag) => {
|
|
26870
|
+
const m = new RegExp(`<(?:\\w+:)?${tag}\\b[^>]*>\\s*<(?:\\w+:)?autoNumFormat\\b([^>]*?)/?>`).exec(xml);
|
|
26871
|
+
return m ? noteFormatFrom((n) => xmlAttr(m[1], n)) : void 0;
|
|
26872
|
+
};
|
|
26873
|
+
return { footnote: pick2("footNotePr"), endnote: pick2("endNotePr") };
|
|
26874
|
+
}
|
|
26875
|
+
function xmlAttr(attrs, name) {
|
|
26876
|
+
const m = new RegExp(`(?:^|\\s)${name}\\s*=\\s*"([^"]*)"`).exec(attrs);
|
|
26877
|
+
return m ? decodeXmlEntities(m[1]) : void 0;
|
|
26878
|
+
}
|
|
26879
|
+
function paraNoteMarks(xml, para2, fmts) {
|
|
26880
|
+
const end = findElementEnd(xml, para2.start);
|
|
26881
|
+
if (end < 0) return [];
|
|
26882
|
+
const seg = xml.slice(para2.start, end);
|
|
26883
|
+
if (!/<(?:\w+:)?(?:footNote|endNote)\b/.test(seg)) return [];
|
|
26884
|
+
const marks = [];
|
|
26885
|
+
let depth = 0;
|
|
26886
|
+
for (const m of seg.matchAll(/<(\/?)(?:\w+:)?(tbl|drawText|caption|footNote|endNote|header|footer|memo|hiddenComment)\b([^>]*?)(\/?)>/g)) {
|
|
26887
|
+
const [, close, tag, attrs, selfClose] = m;
|
|
26888
|
+
if (selfClose) continue;
|
|
26889
|
+
if (close) {
|
|
26890
|
+
depth = Math.max(0, depth - 1);
|
|
26891
|
+
continue;
|
|
26892
|
+
}
|
|
26893
|
+
if (depth === 0 && (tag === "footNote" || tag === "endNote")) {
|
|
26894
|
+
const get = (n) => xmlAttr(attrs, n);
|
|
26895
|
+
marks.push(noteRefMark(
|
|
26896
|
+
{ number: get("number"), prefixChar: get("prefixChar"), suffixChar: get("suffixChar"), userChar: get("userChar") },
|
|
26897
|
+
tag === "endNote" ? fmts.endnote : fmts.footnote
|
|
26898
|
+
));
|
|
26899
|
+
}
|
|
26900
|
+
depth++;
|
|
26901
|
+
}
|
|
26902
|
+
return marks;
|
|
26903
|
+
}
|
|
26904
|
+
function textWithoutMarks(irKey, scanKey, marks) {
|
|
26905
|
+
let i = 0, j = 0, k = 0;
|
|
26906
|
+
while (i < irKey.length) {
|
|
26907
|
+
if (j < scanKey.length && irKey[i] === scanKey[j]) {
|
|
26908
|
+
i++;
|
|
26909
|
+
j++;
|
|
26910
|
+
continue;
|
|
26911
|
+
}
|
|
26912
|
+
if (k < marks.length && marks[k] && irKey.startsWith(marks[k], i)) {
|
|
26913
|
+
i += marks[k].length;
|
|
26914
|
+
k++;
|
|
26915
|
+
continue;
|
|
26916
|
+
}
|
|
26917
|
+
if (irKey[i] === " " && (j === 0 || scanKey[j - 1] === " ")) {
|
|
26918
|
+
i++;
|
|
26919
|
+
continue;
|
|
26920
|
+
}
|
|
26921
|
+
return false;
|
|
26922
|
+
}
|
|
26923
|
+
return j === scanKey.length && k === marks.length;
|
|
26924
|
+
}
|
|
26925
|
+
function removeNoteMarks(text, marks) {
|
|
26926
|
+
let out = text;
|
|
26927
|
+
let cursor = 0;
|
|
26928
|
+
const missing = [];
|
|
26929
|
+
for (const mark of marks) {
|
|
26930
|
+
if (!mark) continue;
|
|
26931
|
+
let at = -1;
|
|
26932
|
+
for (let p = out.indexOf(mark, cursor); p !== -1; p = out.indexOf(mark, p + 1)) {
|
|
26933
|
+
if (p === 0 || !/\s/.test(out[p - 1])) {
|
|
26934
|
+
at = p;
|
|
26935
|
+
break;
|
|
26936
|
+
}
|
|
26937
|
+
if (at === -1) at = p;
|
|
26938
|
+
}
|
|
26939
|
+
if (at === -1) {
|
|
26940
|
+
missing.push(mark);
|
|
26941
|
+
continue;
|
|
26942
|
+
}
|
|
26943
|
+
out = out.slice(0, at) + out.slice(at + mark.length);
|
|
26944
|
+
cursor = at;
|
|
26945
|
+
}
|
|
26946
|
+
return { text: out, missing };
|
|
26947
|
+
}
|
|
26433
26948
|
function handleModifiedUnit(orig, edited, ctx) {
|
|
26434
26949
|
const block = ctx.origBlocks[orig.blockIdx];
|
|
26435
26950
|
const skip = (reason) => {
|
|
@@ -26512,6 +27027,11 @@ function patchParagraphUnit(block, orig, edited, ctx, skip) {
|
|
|
26512
27027
|
ctx.skipped.push({ reason: "\uAC01\uC8FC \uD45C\uAE30 \uC0AD\uC81C\uB294 \uBBF8\uC9C0\uC6D0 \u2014 \uAC01\uC8FC \uC720\uC9C0, \uBCF8\uBB38\uB9CC \uC801\uC6A9", before: `(\uC8FC: ${block.footnoteText})` });
|
|
26513
27028
|
}
|
|
26514
27029
|
}
|
|
27030
|
+
if (mapping.noteMarks?.length) {
|
|
27031
|
+
const r = removeNoteMarks(newPlain, mapping.noteMarks);
|
|
27032
|
+
newPlain = r.text;
|
|
27033
|
+
if (r.missing.length) ctx.skipped.push({ reason: "\uAC01\uC8FC \uCC38\uC870 \uBD80\uD638 \uC0AD\uC81C\uB294 \uBBF8\uC9C0\uC6D0 \u2014 \uAC01\uC8FC \uC720\uC9C0, \uBCF8\uBB38\uB9CC \uC801\uC6A9", before: r.missing.join(" ") });
|
|
27034
|
+
}
|
|
26515
27035
|
if (mapping.prefixStripped) {
|
|
26516
27036
|
const origPrefix = block.text.split(" ", 1)[0];
|
|
26517
27037
|
const sp = newPlain.indexOf(" ");
|
|
@@ -26611,6 +27131,11 @@ function replaceOleStream(file, path, newData) {
|
|
|
26611
27131
|
surgeon.replace(path, newData);
|
|
26612
27132
|
return surgeon.finish();
|
|
26613
27133
|
}
|
|
27134
|
+
function wipeFreeSectors(file) {
|
|
27135
|
+
const surgeon = new Surgeon(file);
|
|
27136
|
+
surgeon.wipeUnallocated();
|
|
27137
|
+
return surgeon.finish();
|
|
27138
|
+
}
|
|
26614
27139
|
var Surgeon = class {
|
|
26615
27140
|
buf;
|
|
26616
27141
|
/** 논리 파일 길이 — buf는 성장 시 기하급수 capacity로 재할당되므로 buf.length와 다를 수 있음 */
|
|
@@ -26900,6 +27425,47 @@ var Surgeon = class {
|
|
|
26900
27425
|
entry.size = newData.length;
|
|
26901
27426
|
this.writeDirEntry(entry);
|
|
26902
27427
|
}
|
|
27428
|
+
/**
|
|
27429
|
+
* wipeFreeSectors 본체 — 어느 체인에도 닿지 않는 섹터·미니섹터와 스트림 꼬리 슬랙을 0으로.
|
|
27430
|
+
* FREESECT 표시를 믿지 않고 도달성으로 판정한다(쓰는 쪽에 따라 빈 FAT 칸을 ENDOFCHAIN 으로 채우기도 한다).
|
|
27431
|
+
*/
|
|
27432
|
+
wipeUnallocated() {
|
|
27433
|
+
const used = /* @__PURE__ */ new Set([...this.fatSectors, ...this.miniFatSectors, ...this.dirSectors]);
|
|
27434
|
+
let d = this.buf.readUInt32LE(68);
|
|
27435
|
+
for (let guard = 0; d !== ENDOFCHAIN && d !== FREESECT && guard < 1e6; guard++) {
|
|
27436
|
+
used.add(d);
|
|
27437
|
+
d = this.buf.readUInt32LE(this.sectorOffset(d) + 127 * 4);
|
|
27438
|
+
}
|
|
27439
|
+
const root = this.rootEntry();
|
|
27440
|
+
const rootChain = root.start === ENDOFCHAIN || root.size === 0 ? [] : this.chain(root.start);
|
|
27441
|
+
for (const s of rootChain) used.add(s);
|
|
27442
|
+
const usedMini = /* @__PURE__ */ new Set();
|
|
27443
|
+
for (const e of this.entries) {
|
|
27444
|
+
if (e.type !== 2 || e.size === 0 || e.start === ENDOFCHAIN) continue;
|
|
27445
|
+
if (e.size < MINI_CUTOFF) {
|
|
27446
|
+
const chain = this.miniChain(e.start);
|
|
27447
|
+
for (const s of chain) usedMini.add(s);
|
|
27448
|
+
const tail = e.size - (chain.length - 1) * MINI_SECTOR;
|
|
27449
|
+
const off = this.miniOffset(chain[chain.length - 1], rootChain);
|
|
27450
|
+
if (tail > 0 && tail < MINI_SECTOR) this.buf.fill(0, off + tail, off + MINI_SECTOR);
|
|
27451
|
+
} else {
|
|
27452
|
+
const chain = this.chain(e.start);
|
|
27453
|
+
for (const s of chain) used.add(s);
|
|
27454
|
+
const tail = e.size - (chain.length - 1) * SECTOR;
|
|
27455
|
+
const off = this.sectorOffset(chain[chain.length - 1]);
|
|
27456
|
+
if (tail > 0 && tail < SECTOR) this.buf.fill(0, off + tail, off + SECTOR);
|
|
27457
|
+
}
|
|
27458
|
+
}
|
|
27459
|
+
for (let i = 0; SECTOR + (i + 1) * SECTOR <= this.len; i++) {
|
|
27460
|
+
if (!used.has(i)) this.buf.fill(0, SECTOR + i * SECTOR, SECTOR + (i + 1) * SECTOR);
|
|
27461
|
+
}
|
|
27462
|
+
const capacity = rootChain.length * (SECTOR / MINI_SECTOR);
|
|
27463
|
+
for (let i = 0; i < capacity; i++) {
|
|
27464
|
+
if (usedMini.has(i)) continue;
|
|
27465
|
+
const off = this.miniOffset(i, rootChain);
|
|
27466
|
+
this.buf.fill(0, off, off + MINI_SECTOR);
|
|
27467
|
+
}
|
|
27468
|
+
}
|
|
26903
27469
|
finish() {
|
|
26904
27470
|
this.wipeFreedSectors();
|
|
26905
27471
|
this.flushFat();
|
|
@@ -27232,6 +27798,11 @@ function resolveTableMappings(blocks, scanTables) {
|
|
|
27232
27798
|
for (let k = si; k < scanTables.length; k++) {
|
|
27233
27799
|
if (scanTables[k].rows === table2.rows && scanTables[k].cols === table2.cols) cands.push(k);
|
|
27234
27800
|
}
|
|
27801
|
+
if (cands.length === 0) {
|
|
27802
|
+
for (let k = si; k < scanTables.length; k++) {
|
|
27803
|
+
if (scanTables[k].rows === table2.rows && scanTables[k].cols > table2.cols) cands.push(k);
|
|
27804
|
+
}
|
|
27805
|
+
}
|
|
27235
27806
|
if (cands.length === 0) continue;
|
|
27236
27807
|
let pick2 = cands[0];
|
|
27237
27808
|
if (cands.length > 1) {
|
|
@@ -28122,6 +28693,11 @@ var HwpxSession = class _HwpxSession {
|
|
|
28122
28693
|
return skip("\uBB38\uB2E8 \uC18C\uC2A4\uB9F5 \uB9E4\uD551 \uC2E4\uD328 (\uBA38\uB9AC\uB9D0/\uAE00\uC0C1\uC790/\uCEA1\uC158 \uC601\uC5ED\uC774\uAC70\uB098 \uD14D\uC2A4\uD2B8 \uBD88\uC77C\uCE58)");
|
|
28123
28694
|
}
|
|
28124
28695
|
let newPlain = newTextRaw.split("\n").map((l) => l.trim()).filter(Boolean).join(" ");
|
|
28696
|
+
if (mapping.noteMarks?.length) {
|
|
28697
|
+
const r = removeNoteMarks(newPlain, mapping.noteMarks);
|
|
28698
|
+
newPlain = r.text;
|
|
28699
|
+
if (r.missing.length) skipped.push({ reason: "\uAC01\uC8FC \uCC38\uC870 \uBD80\uD638 \uC0AD\uC81C\uB294 \uBBF8\uC9C0\uC6D0 \u2014 \uAC01\uC8FC \uC720\uC9C0, \uBCF8\uBB38\uB9CC \uC801\uC6A9", before: r.missing.join(" ") });
|
|
28700
|
+
}
|
|
28125
28701
|
if (mapping.prefixStripped) {
|
|
28126
28702
|
const origPrefix = block.text.split(" ", 1)[0];
|
|
28127
28703
|
const sp = newPlain.indexOf(" ");
|
|
@@ -28202,6 +28778,10 @@ async function parse(input, options) {
|
|
|
28202
28778
|
}
|
|
28203
28779
|
const format = detectFormat(buffer);
|
|
28204
28780
|
const result = await dispatch(format, buffer, opts);
|
|
28781
|
+
if (result.success && opts?.images === false) {
|
|
28782
|
+
delete result.images;
|
|
28783
|
+
dropImageBytes(result.blocks);
|
|
28784
|
+
}
|
|
28205
28785
|
if (result.success && opts?.classifyTables) classifyTableTree(result.blocks);
|
|
28206
28786
|
if (result.success && !result.pages) {
|
|
28207
28787
|
const pages = blocksToPages(result.blocks);
|
|
@@ -28209,6 +28789,17 @@ async function parse(input, options) {
|
|
|
28209
28789
|
}
|
|
28210
28790
|
return result;
|
|
28211
28791
|
}
|
|
28792
|
+
function dropImageBytes(blocks, depth = 0) {
|
|
28793
|
+
if (!blocks || depth > 64) return;
|
|
28794
|
+
for (const b of blocks) {
|
|
28795
|
+
if (b.imageData) delete b.imageData;
|
|
28796
|
+
dropImageBytes(b.children, depth + 1);
|
|
28797
|
+
if (b.table) {
|
|
28798
|
+
for (const row of b.table.cells) for (const cell2 of row) dropImageBytes(cell2.blocks, depth + 1);
|
|
28799
|
+
dropImageBytes(b.table.captionBlocks, depth + 1);
|
|
28800
|
+
}
|
|
28801
|
+
}
|
|
28802
|
+
}
|
|
28212
28803
|
async function dispatch(format, buffer, opts) {
|
|
28213
28804
|
switch (format) {
|
|
28214
28805
|
case "hwpx": {
|
|
@@ -28239,7 +28830,7 @@ async function dispatch(format, buffer, opts) {
|
|
|
28239
28830
|
}
|
|
28240
28831
|
async function parseImage(buffer, options) {
|
|
28241
28832
|
try {
|
|
28242
|
-
const { parseImageDocument } = await import("./image-ocr-
|
|
28833
|
+
const { parseImageDocument } = await import("./image-ocr-I6JOCATS.js");
|
|
28243
28834
|
const { blocks, warnings } = await parseImageDocument(buffer, options);
|
|
28244
28835
|
return {
|
|
28245
28836
|
success: true,
|
|
@@ -28298,7 +28889,7 @@ async function parseHwp(buffer, options) {
|
|
|
28298
28889
|
async function parsePdf(buffer, options) {
|
|
28299
28890
|
let parsePdfDocument;
|
|
28300
28891
|
try {
|
|
28301
|
-
const mod = await import("./parser-
|
|
28892
|
+
const mod = await import("./parser-3FNXEP6T.js");
|
|
28302
28893
|
parsePdfDocument = mod.parsePdfDocument;
|
|
28303
28894
|
} catch {
|
|
28304
28895
|
return {
|
|
@@ -28490,7 +29081,10 @@ export {
|
|
|
28490
29081
|
lintMuncheText,
|
|
28491
29082
|
muncheLintWarnings,
|
|
28492
29083
|
patchHwpx,
|
|
29084
|
+
replaceOleStream,
|
|
29085
|
+
wipeFreeSectors,
|
|
28493
29086
|
patchHwp,
|
|
29087
|
+
compressWithTail,
|
|
28494
29088
|
validateHwpx,
|
|
28495
29089
|
HwpxSession,
|
|
28496
29090
|
openHwpxDocument,
|
|
@@ -28508,4 +29102,4 @@ export {
|
|
|
28508
29102
|
parseHwpml,
|
|
28509
29103
|
fillForm
|
|
28510
29104
|
};
|
|
28511
|
-
//# sourceMappingURL=chunk-
|
|
29105
|
+
//# sourceMappingURL=chunk-XVXHXQIO.js.map
|