hwpkit-dev 0.0.1 → 0.0.2
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/ .npmignore +1 -0
- package/dist/index.d.mts +34 -3
- package/dist/index.d.ts +30 -3
- package/dist/index.js +2138 -245
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +2134 -245
- package/dist/index.mjs.map +1 -1
- package/hwp-analyze.ts +90 -0
- package/inspect-doc.ts +57 -0
- package/output_test.hwp +0 -0
- package/package.json +3 -1
- package/src/decoders/docx/DocxDecoder.ts +155 -30
- package/src/decoders/hwp/HwpScanner.ts +258 -37
- package/src/decoders/hwpx/HwpxDecoder.ts +9 -1
- package/src/encoders/docx/DocxEncoder.ts +199 -158
- package/src/encoders/html/HtmlEncoder.ts +205 -0
- package/src/encoders/hwp/HwpEncoder.ts +864 -222
- package/src/encoders/hwpx/HwpxEncoder.ts +119 -59
- package/src/encoders/md/MdEncoder.ts +98 -16
- package/src/index.ts +1 -0
- package/src/model/builders.ts +4 -2
- package/src/model/doc-tree.ts +1 -1
- package/src/pipeline/Pipeline.ts +14 -1
- package/src/safety/StyleBridge.ts +1 -1
- package/test-docx-to-hwp.ts +45 -0
package/dist/index.js
CHANGED
|
@@ -31,6 +31,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
31
31
|
var index_exports = {};
|
|
32
32
|
__export(index_exports, {
|
|
33
33
|
A4: () => A4,
|
|
34
|
+
A4_LANDSCAPE: () => A4_LANDSCAPE,
|
|
34
35
|
ArchiveKit: () => ArchiveKit,
|
|
35
36
|
BinaryKit: () => BinaryKit,
|
|
36
37
|
DEFAULT_STROKE: () => DEFAULT_STROKE,
|
|
@@ -40,17 +41,20 @@ __export(index_exports, {
|
|
|
40
41
|
TextKit: () => TextKit,
|
|
41
42
|
TreeWalker: () => TreeWalker,
|
|
42
43
|
XmlKit: () => XmlKit,
|
|
44
|
+
buildBr: () => buildBr,
|
|
43
45
|
buildCell: () => buildCell,
|
|
44
46
|
buildGrid: () => buildGrid,
|
|
45
47
|
buildImg: () => buildImg,
|
|
46
48
|
buildPageNum: () => buildPageNum,
|
|
47
49
|
buildPara: () => buildPara,
|
|
50
|
+
buildPb: () => buildPb,
|
|
48
51
|
buildRoot: () => buildRoot,
|
|
49
52
|
buildRow: () => buildRow,
|
|
50
53
|
buildSheet: () => buildSheet,
|
|
51
54
|
buildSpan: () => buildSpan,
|
|
52
55
|
countNodes: () => countNodes,
|
|
53
56
|
fail: () => fail,
|
|
57
|
+
normalizeDims: () => normalizeDims,
|
|
54
58
|
registry: () => registry,
|
|
55
59
|
safeAlign: () => safeAlign,
|
|
56
60
|
safeFont: () => safeFont,
|
|
@@ -109,6 +113,25 @@ var A4 = {
|
|
|
109
113
|
mr: 70.87,
|
|
110
114
|
orient: "portrait"
|
|
111
115
|
};
|
|
116
|
+
var A4_LANDSCAPE = {
|
|
117
|
+
wPt: 841.89,
|
|
118
|
+
hPt: 595.28,
|
|
119
|
+
mt: 56.69,
|
|
120
|
+
mb: 56.69,
|
|
121
|
+
ml: 70.87,
|
|
122
|
+
mr: 70.87,
|
|
123
|
+
orient: "landscape"
|
|
124
|
+
};
|
|
125
|
+
function normalizeDims(dims) {
|
|
126
|
+
const orient = dims.orient ?? "portrait";
|
|
127
|
+
if (orient === "landscape" && dims.wPt < dims.hPt) {
|
|
128
|
+
return { ...dims, wPt: dims.hPt, hPt: dims.wPt };
|
|
129
|
+
}
|
|
130
|
+
if (orient === "portrait" && dims.wPt > dims.hPt) {
|
|
131
|
+
return { ...dims, wPt: dims.hPt, hPt: dims.wPt };
|
|
132
|
+
}
|
|
133
|
+
return dims;
|
|
134
|
+
}
|
|
112
135
|
var DEFAULT_STROKE = { kind: "solid", pt: 0.5, color: "000000" };
|
|
113
136
|
|
|
114
137
|
// src/model/builders.ts
|
|
@@ -124,6 +147,12 @@ function buildSheet(kids = [], dims = A4, opts) {
|
|
|
124
147
|
function buildPageNum(format) {
|
|
125
148
|
return { tag: "pagenum", format };
|
|
126
149
|
}
|
|
150
|
+
function buildBr() {
|
|
151
|
+
return { tag: "br" };
|
|
152
|
+
}
|
|
153
|
+
function buildPb() {
|
|
154
|
+
return { tag: "pb" };
|
|
155
|
+
}
|
|
127
156
|
function buildPara(kids = [], props = {}) {
|
|
128
157
|
return { tag: "para", props, kids };
|
|
129
158
|
}
|
|
@@ -131,14 +160,19 @@ function buildSpan(content, props = {}) {
|
|
|
131
160
|
const txt = { tag: "txt", content };
|
|
132
161
|
return { tag: "span", props, kids: [txt] };
|
|
133
162
|
}
|
|
134
|
-
function buildImg(b64, mime, w, h, alt) {
|
|
135
|
-
|
|
163
|
+
function buildImg(b64, mime, w, h, alt, layout) {
|
|
164
|
+
const node = { tag: "img", b64, mime, w, h };
|
|
165
|
+
if (alt) node.alt = alt;
|
|
166
|
+
if (layout) node.layout = layout;
|
|
167
|
+
return node;
|
|
136
168
|
}
|
|
137
169
|
function buildGrid(kids, props = {}) {
|
|
138
170
|
return { tag: "grid", props, kids };
|
|
139
171
|
}
|
|
140
|
-
function buildRow(kids) {
|
|
141
|
-
|
|
172
|
+
function buildRow(kids, heightPt) {
|
|
173
|
+
const node = { tag: "row", kids };
|
|
174
|
+
if (heightPt != null) node.heightPt = heightPt;
|
|
175
|
+
return node;
|
|
142
176
|
}
|
|
143
177
|
function buildCell(kids, opts = {}) {
|
|
144
178
|
return { tag: "cell", cs: opts.cs ?? 1, rs: opts.rs ?? 1, props: opts.props ?? {}, kids };
|
|
@@ -268,7 +302,7 @@ var ALIGN_MAP = {
|
|
|
268
302
|
end: "right"
|
|
269
303
|
};
|
|
270
304
|
function safeAlign(raw) {
|
|
271
|
-
return ALIGN_MAP[raw ?? ""] ?? "
|
|
305
|
+
return ALIGN_MAP[raw ?? ""] ?? "left";
|
|
272
306
|
}
|
|
273
307
|
var HWPX_STROKE = {
|
|
274
308
|
SOLID: "solid",
|
|
@@ -749,7 +783,36 @@ function extractParaPrs(headObj) {
|
|
|
749
783
|
if (id < 0) continue;
|
|
750
784
|
const alignNode = pp?.["hh:align"]?.[0]?._attr ?? pp?.["hh:ALIGN"]?.[0]?._attr;
|
|
751
785
|
const align = alignNode?.horizontal ?? alignNode?.Horizontal;
|
|
752
|
-
|
|
786
|
+
let marginEl = pp?.["hh:margin"]?.[0] ?? null;
|
|
787
|
+
let lineSpEl = pp?.["hh:lineSpacing"]?.[0] ?? null;
|
|
788
|
+
if (!marginEl) {
|
|
789
|
+
const sw = pp?.["hp:switch"]?.[0];
|
|
790
|
+
const container = sw?.["hp:default"]?.[0] ?? sw?.["hp:case"]?.[0];
|
|
791
|
+
marginEl = container?.["hh:margin"]?.[0] ?? null;
|
|
792
|
+
lineSpEl = lineSpEl ?? container?.["hh:lineSpacing"]?.[0] ?? null;
|
|
793
|
+
}
|
|
794
|
+
let indentPt;
|
|
795
|
+
let spaceBefore;
|
|
796
|
+
let spaceAfter;
|
|
797
|
+
let lineHeight;
|
|
798
|
+
if (marginEl) {
|
|
799
|
+
const intentEl = marginEl?.["hc:intent"]?.[0] ?? marginEl?.["hc:indent"]?.[0];
|
|
800
|
+
const prevEl = marginEl?.["hc:prev"]?.[0];
|
|
801
|
+
const nextEl = marginEl?.["hc:next"]?.[0];
|
|
802
|
+
const intentVal = Number(intentEl?._attr?.value ?? 0);
|
|
803
|
+
const prevVal = Number(prevEl?._attr?.value ?? 0);
|
|
804
|
+
const nextVal = Number(nextEl?._attr?.value ?? 0);
|
|
805
|
+
if (intentVal !== 0) indentPt = Metric.hwpToPt(intentVal);
|
|
806
|
+
if (prevVal > 0) spaceBefore = Metric.hwpToPt(prevVal);
|
|
807
|
+
if (nextVal > 0) spaceAfter = Metric.hwpToPt(nextVal);
|
|
808
|
+
}
|
|
809
|
+
if (lineSpEl) {
|
|
810
|
+
const lsAttr = lineSpEl._attr ?? {};
|
|
811
|
+
const lsType = lsAttr.type ?? "PERCENT";
|
|
812
|
+
const lsVal = Number(lsAttr.value ?? 160);
|
|
813
|
+
if (lsType === "PERCENT" && lsVal > 0) lineHeight = lsVal / 100;
|
|
814
|
+
}
|
|
815
|
+
map.set(id, { align, indentPt, spaceBefore, spaceAfter, lineHeight });
|
|
753
816
|
}
|
|
754
817
|
} catch {
|
|
755
818
|
}
|
|
@@ -872,6 +935,12 @@ function decodePara(p, ctx) {
|
|
|
872
935
|
}
|
|
873
936
|
const inlineAttr = inlineParaPr?._attr ?? {};
|
|
874
937
|
const props = { align: safeAlign(align) };
|
|
938
|
+
if (paraPrDef) {
|
|
939
|
+
if (paraPrDef.indentPt !== void 0) props.indentPt = paraPrDef.indentPt;
|
|
940
|
+
if (paraPrDef.spaceBefore !== void 0) props.spaceBefore = paraPrDef.spaceBefore;
|
|
941
|
+
if (paraPrDef.spaceAfter !== void 0) props.spaceAfter = paraPrDef.spaceAfter;
|
|
942
|
+
if (paraPrDef.lineHeight !== void 0) props.lineHeight = paraPrDef.lineHeight;
|
|
943
|
+
}
|
|
875
944
|
if (inlineAttr.listType) {
|
|
876
945
|
props.listOrd = inlineAttr.listType === "DIGIT" || inlineAttr.listType === "DECIMAL";
|
|
877
946
|
props.listLv = Number(inlineAttr.listLevel ?? 0);
|
|
@@ -899,6 +968,9 @@ function decodePara(p, ctx) {
|
|
|
899
968
|
const spanProps = resolveCharPr(run, ctx);
|
|
900
969
|
kids.push(buildSpan(content, spanProps));
|
|
901
970
|
}
|
|
971
|
+
if (pAttr.pageBreak === "1") {
|
|
972
|
+
kids.unshift({ tag: "span", props: {}, kids: [buildPb()] });
|
|
973
|
+
}
|
|
902
974
|
return buildPara(kids.filter(Boolean), props);
|
|
903
975
|
}
|
|
904
976
|
function resolveCharPr(run, ctx) {
|
|
@@ -953,11 +1025,55 @@ function decodePic(pic, ctx) {
|
|
|
953
1025
|
gif: "image/gif",
|
|
954
1026
|
bmp: "image/bmp"
|
|
955
1027
|
};
|
|
956
|
-
|
|
1028
|
+
const posAttr = pic?.["hp:pos"]?.[0]?._attr ?? pic?.pos?.[0]?._attr ?? {};
|
|
1029
|
+
const layout = extractHwpxLayout(posAttr, pic);
|
|
1030
|
+
return buildImg(TextKit.base64Encode(imgData), mimeMap[ext] ?? "image/png", w, h, void 0, layout);
|
|
957
1031
|
} catch {
|
|
958
1032
|
return null;
|
|
959
1033
|
}
|
|
960
1034
|
}
|
|
1035
|
+
function extractHwpxLayout(posAttr, pic) {
|
|
1036
|
+
const treatAsChar = posAttr.treatAsChar === "1" || posAttr.treatAsChar === "true";
|
|
1037
|
+
if (treatAsChar) return { wrap: "inline" };
|
|
1038
|
+
const textWrap = pic?._attr?.textWrap ?? pic?.["hp:pic"]?.[0]?._attr?.textWrap ?? "TOP_AND_BOTTOM";
|
|
1039
|
+
const wrapMap = {
|
|
1040
|
+
TOP_AND_BOTTOM: "square",
|
|
1041
|
+
SQUARE: "square",
|
|
1042
|
+
BOTH_SIDES: "tight",
|
|
1043
|
+
LEFT: "tight",
|
|
1044
|
+
RIGHT: "tight",
|
|
1045
|
+
LARGER_ONLY: "tight",
|
|
1046
|
+
SMALLER_ONLY: "tight",
|
|
1047
|
+
LARGEST_ONLY: "tight",
|
|
1048
|
+
BEHIND_TEXT: "behind",
|
|
1049
|
+
FRONT_TEXT: "none"
|
|
1050
|
+
};
|
|
1051
|
+
const wrap = wrapMap[textWrap] ?? "square";
|
|
1052
|
+
const horzRelToMap = {
|
|
1053
|
+
PARA: "para",
|
|
1054
|
+
MARGIN: "margin",
|
|
1055
|
+
PAGE: "page",
|
|
1056
|
+
COLUMN: "column"
|
|
1057
|
+
};
|
|
1058
|
+
const vertRelToMap = {
|
|
1059
|
+
PARA: "para",
|
|
1060
|
+
MARGIN: "margin",
|
|
1061
|
+
PAGE: "page",
|
|
1062
|
+
PAPER: "page",
|
|
1063
|
+
LINE: "line"
|
|
1064
|
+
};
|
|
1065
|
+
const horzRelTo = horzRelToMap[posAttr.horzRelTo ?? ""] ?? "para";
|
|
1066
|
+
const vertRelTo = vertRelToMap[posAttr.vertRelTo ?? ""] ?? "para";
|
|
1067
|
+
const horzAlignMap = { LEFT: "left", CENTER: "center", RIGHT: "right" };
|
|
1068
|
+
const vertAlignMap = { TOP: "top", CENTER: "center", BOTTOM: "bottom" };
|
|
1069
|
+
const horzAlign = horzAlignMap[posAttr.horzAlign ?? ""];
|
|
1070
|
+
const vertAlign = vertAlignMap[posAttr.vertAlign ?? ""];
|
|
1071
|
+
const horzOffset = Number(posAttr.horzOffset ?? 0);
|
|
1072
|
+
const vertOffset = Number(posAttr.vertOffset ?? 0);
|
|
1073
|
+
const xPt = horzOffset !== 0 ? Metric.hwpToPt(horzOffset) : void 0;
|
|
1074
|
+
const yPt = vertOffset !== 0 ? Metric.hwpToPt(vertOffset) : void 0;
|
|
1075
|
+
return { wrap, horzAlign, vertAlign, horzRelTo, vertRelTo, xPt, yPt };
|
|
1076
|
+
}
|
|
961
1077
|
function decodeGrid(tbl, ctx) {
|
|
962
1078
|
const tblAttr = tbl?._attr ?? {};
|
|
963
1079
|
const borderFillId = Number(tblAttr.borderFillIDRef ?? 0);
|
|
@@ -966,6 +1082,26 @@ function decodeGrid(tbl, ctx) {
|
|
|
966
1082
|
const gridProps = { headerRow: headerRow || void 0 };
|
|
967
1083
|
if (borderFill?.stroke) gridProps.defaultStroke = borderFill.stroke;
|
|
968
1084
|
const rowArr = getTag(tbl, "hp:tr", "hp:ROW");
|
|
1085
|
+
for (const row of rowArr) {
|
|
1086
|
+
const cells = getTag(row, "hp:tc", "hp:CELL");
|
|
1087
|
+
const rowWidths = [];
|
|
1088
|
+
let allSingle = true;
|
|
1089
|
+
for (const cell of cells) {
|
|
1090
|
+
const cellSpanAttr = cell?.["hp:cellSpan"]?.[0]?._attr ?? {};
|
|
1091
|
+
const cs = Number(cellSpanAttr.colSpan ?? cell?._attr?.ColSpan ?? 1);
|
|
1092
|
+
if (cs > 1) {
|
|
1093
|
+
allSingle = false;
|
|
1094
|
+
break;
|
|
1095
|
+
}
|
|
1096
|
+
const szAttr = cell?.["hp:cellSz"]?.[0]?._attr ?? {};
|
|
1097
|
+
const w = Number(szAttr.width ?? 0);
|
|
1098
|
+
rowWidths.push(Metric.hwpToPt(w));
|
|
1099
|
+
}
|
|
1100
|
+
if (allSingle && rowWidths.length > 0 && rowWidths.some((w) => w > 0)) {
|
|
1101
|
+
gridProps.colWidths = rowWidths;
|
|
1102
|
+
break;
|
|
1103
|
+
}
|
|
1104
|
+
}
|
|
969
1105
|
const rowNodes = rowArr.map((row) => {
|
|
970
1106
|
const cellArr = getTag(row, "hp:tc", "hp:CELL");
|
|
971
1107
|
const cellNodes = cellArr.map((cell) => {
|
|
@@ -1006,7 +1142,14 @@ function decodeGrid(tbl, ctx) {
|
|
|
1006
1142
|
{ cs, rs, props: cellProps }
|
|
1007
1143
|
);
|
|
1008
1144
|
});
|
|
1009
|
-
|
|
1145
|
+
let rowHeightPt;
|
|
1146
|
+
const firstCellForH = cellArr[0];
|
|
1147
|
+
if (firstCellForH) {
|
|
1148
|
+
const hSz = firstCellForH?.["hp:cellSz"]?.[0]?._attr ?? {};
|
|
1149
|
+
const hVal = Number(hSz.height ?? 0);
|
|
1150
|
+
if (hVal > 0) rowHeightPt = Metric.hwpToPt(hVal);
|
|
1151
|
+
}
|
|
1152
|
+
return buildRow(cellNodes, rowHeightPt);
|
|
1010
1153
|
});
|
|
1011
1154
|
return buildGrid(rowNodes, gridProps);
|
|
1012
1155
|
}
|
|
@@ -1231,9 +1374,13 @@ function parseRecords(data) {
|
|
|
1231
1374
|
}
|
|
1232
1375
|
function tryInflate(data) {
|
|
1233
1376
|
try {
|
|
1234
|
-
return import_pako2.default.
|
|
1377
|
+
return import_pako2.default.inflate(data);
|
|
1235
1378
|
} catch {
|
|
1236
|
-
|
|
1379
|
+
try {
|
|
1380
|
+
return import_pako2.default.inflateRaw(data);
|
|
1381
|
+
} catch {
|
|
1382
|
+
return data;
|
|
1383
|
+
}
|
|
1237
1384
|
}
|
|
1238
1385
|
}
|
|
1239
1386
|
function parseFileHeader(buf) {
|
|
@@ -1287,7 +1434,7 @@ function parseParaShape(d) {
|
|
|
1287
1434
|
if (d.length < 4) return { align: "left", spaceBefore: 0, spaceAfter: 0, lineSpacing: 160, indent: 0 };
|
|
1288
1435
|
const attr = BinaryKit.readU32LE(d, 0);
|
|
1289
1436
|
return {
|
|
1290
|
-
align: ALIGN_TBL[attr & 7] ?? "left",
|
|
1437
|
+
align: ALIGN_TBL[attr >> 2 & 7] ?? "left",
|
|
1291
1438
|
indent: d.length >= 16 ? i32(d, 12) : 0,
|
|
1292
1439
|
spaceBefore: d.length >= 20 ? i32(d, 16) : 0,
|
|
1293
1440
|
spaceAfter: d.length >= 24 ? i32(d, 20) : 0,
|
|
@@ -1295,16 +1442,17 @@ function parseParaShape(d) {
|
|
|
1295
1442
|
};
|
|
1296
1443
|
}
|
|
1297
1444
|
var BORDER_W_PT = [0.28, 0.34, 0.43, 0.57, 0.71, 0.85, 1.13, 1.42, 1.7, 1.98, 2.84, 4.25, 5.67, 8.5, 11.34, 14.17];
|
|
1298
|
-
var BORDER_KIND = { 0: "
|
|
1445
|
+
var BORDER_KIND = { 0: "solid", 1: "dash", 2: "dash", 3: "dot", 4: "dash", 5: "dash", 6: "dash", 7: "double", 8: "double", 9: "double", 10: "none" };
|
|
1299
1446
|
function parseBorderFill(d) {
|
|
1300
1447
|
const borders = [];
|
|
1448
|
+
const BASE_TYPE = 2;
|
|
1449
|
+
const BASE_WIDTH = 6;
|
|
1450
|
+
const BASE_COLOR = 10;
|
|
1301
1451
|
for (let i = 0; i < 4; i++) {
|
|
1302
|
-
const
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
borders.push({ type: 0, widthPt: 0.5, color: "000000" });
|
|
1307
|
-
}
|
|
1452
|
+
const type = BASE_TYPE + i < d.length ? d[BASE_TYPE + i] : 0;
|
|
1453
|
+
const widthPt = BASE_WIDTH + i < d.length ? BORDER_W_PT[d[BASE_WIDTH + i]] ?? 0.5 : 0.5;
|
|
1454
|
+
const color = BASE_COLOR + i * 4 + 4 <= d.length ? colorRef(d, BASE_COLOR + i * 4) : "000000";
|
|
1455
|
+
borders.push({ type, widthPt, color });
|
|
1308
1456
|
}
|
|
1309
1457
|
let bgColor;
|
|
1310
1458
|
const fOff = 32;
|
|
@@ -1318,10 +1466,15 @@ function parseBody(raw, compressed, di, shield) {
|
|
|
1318
1466
|
const recs = parseRecords(compressed ? tryInflate(raw) : raw);
|
|
1319
1467
|
const content = [];
|
|
1320
1468
|
let pageDims;
|
|
1469
|
+
for (const r of recs) {
|
|
1470
|
+
if (r.tag === TAG_PAGE_DEF) {
|
|
1471
|
+
pageDims = shield.guard(() => parsePageDef(r.data), A4, "hwp:pageDef");
|
|
1472
|
+
break;
|
|
1473
|
+
}
|
|
1474
|
+
}
|
|
1321
1475
|
let i = 0;
|
|
1322
1476
|
while (i < recs.length) {
|
|
1323
1477
|
if (recs[i].tag === TAG_PAGE_DEF) {
|
|
1324
|
-
pageDims = shield.guard(() => parsePageDef(recs[i].data), A4, "hwp:pageDef");
|
|
1325
1478
|
i++;
|
|
1326
1479
|
} else if (recs[i].tag === TAG_PARA_HEADER) {
|
|
1327
1480
|
const r = shield.guard(
|
|
@@ -1345,6 +1498,7 @@ function parseParagraphGroup(recs, start, di, shield) {
|
|
|
1345
1498
|
let text = null;
|
|
1346
1499
|
let csPairs = [];
|
|
1347
1500
|
const grids = [];
|
|
1501
|
+
const ctrlHeaders = [];
|
|
1348
1502
|
let i = start + 1;
|
|
1349
1503
|
while (i < recs.length && recs[i].level > lv) {
|
|
1350
1504
|
const r = recs[i];
|
|
@@ -1355,14 +1509,21 @@ function parseParagraphGroup(recs, start, di, shield) {
|
|
|
1355
1509
|
csPairs = parseCharShapePairs(r.data);
|
|
1356
1510
|
i++;
|
|
1357
1511
|
} else if (r.tag === TAG_CTRL_HEADER && r.level === lv + 1) {
|
|
1358
|
-
if (r.data.length >= 4
|
|
1359
|
-
const
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1512
|
+
if (r.data.length >= 4) {
|
|
1513
|
+
const ctrlId = BinaryKit.readU32LE(r.data, 0);
|
|
1514
|
+
const objId = r.data.length >= 6 ? BinaryKit.readU16LE(r.data, 4) : 0;
|
|
1515
|
+
ctrlHeaders.push({ ctrlId, objId });
|
|
1516
|
+
if (ctrlId === CTRL_TABLE) {
|
|
1517
|
+
const tr = shield.guard(
|
|
1518
|
+
() => parseTableCtrl(recs, i, di, shield),
|
|
1519
|
+
{ grid: null, next: skipKids(recs, i) },
|
|
1520
|
+
`hwp:tbl@${i}`
|
|
1521
|
+
);
|
|
1522
|
+
if (tr.grid) grids.push(tr.grid);
|
|
1523
|
+
i = tr.next;
|
|
1524
|
+
} else {
|
|
1525
|
+
i = skipKids(recs, i);
|
|
1526
|
+
}
|
|
1366
1527
|
} else {
|
|
1367
1528
|
i = skipKids(recs, i);
|
|
1368
1529
|
}
|
|
@@ -1370,12 +1531,28 @@ function parseParagraphGroup(recs, start, di, shield) {
|
|
|
1370
1531
|
i++;
|
|
1371
1532
|
}
|
|
1372
1533
|
}
|
|
1534
|
+
if (text && ctrlHeaders.length > 0) {
|
|
1535
|
+
for (let ci = 0; ci < text.controls.length; ci++) {
|
|
1536
|
+
if (ci < ctrlHeaders.length) {
|
|
1537
|
+
text.controls[ci].ctrlId = ctrlHeaders[ci].ctrlId;
|
|
1538
|
+
text.controls[ci].matched = true;
|
|
1539
|
+
}
|
|
1540
|
+
}
|
|
1541
|
+
}
|
|
1373
1542
|
const nodes = [];
|
|
1374
|
-
if (text && text.chars.length > 0) {
|
|
1375
|
-
const
|
|
1376
|
-
if (
|
|
1543
|
+
if (text && (text.chars.length > 0 || text.controls.length > 0)) {
|
|
1544
|
+
const paraContent = [];
|
|
1545
|
+
if (text.chars.length > 0) {
|
|
1377
1546
|
const spans = resolveCharShapes(text.chars, csPairs, di);
|
|
1378
|
-
|
|
1547
|
+
paraContent.push(...spans);
|
|
1548
|
+
}
|
|
1549
|
+
if (text.controls.length > 0) {
|
|
1550
|
+
for (let ci = 0; ci < text.controls.length; ci++) {
|
|
1551
|
+
paraContent.push(buildSpan(`__EXT_${ci}__`));
|
|
1552
|
+
}
|
|
1553
|
+
}
|
|
1554
|
+
if (paraContent.length > 0) {
|
|
1555
|
+
nodes.push(buildPara(paraContent, buildParaProps(ps)));
|
|
1379
1556
|
}
|
|
1380
1557
|
}
|
|
1381
1558
|
nodes.push(...grids);
|
|
@@ -1391,7 +1568,7 @@ var EXT_CTRL = /* @__PURE__ */ new Set([2, 3, 11, 12, 14, 15]);
|
|
|
1391
1568
|
var INL_CTRL = /* @__PURE__ */ new Set([4, 5, 6, 7, 8]);
|
|
1392
1569
|
function decodeParaText(d) {
|
|
1393
1570
|
const chars = [];
|
|
1394
|
-
const
|
|
1571
|
+
const controls = [];
|
|
1395
1572
|
let i = 0, pos = 0;
|
|
1396
1573
|
while (i + 1 < d.length) {
|
|
1397
1574
|
const c = d[i] | d[i + 1] << 8;
|
|
@@ -1410,7 +1587,11 @@ function decodeParaText(d) {
|
|
|
1410
1587
|
continue;
|
|
1411
1588
|
}
|
|
1412
1589
|
if (EXT_CTRL.has(c)) {
|
|
1413
|
-
|
|
1590
|
+
let objId = 0;
|
|
1591
|
+
if (i + 16 <= d.length) {
|
|
1592
|
+
objId = BinaryKit.readU16LE(d, i + 8);
|
|
1593
|
+
}
|
|
1594
|
+
controls.push({ pos, ctrlId: 0, objId, matched: false });
|
|
1414
1595
|
i += 16;
|
|
1415
1596
|
pos += 8;
|
|
1416
1597
|
continue;
|
|
@@ -1435,7 +1616,7 @@ function decodeParaText(d) {
|
|
|
1435
1616
|
i += 2;
|
|
1436
1617
|
pos++;
|
|
1437
1618
|
}
|
|
1438
|
-
return { chars,
|
|
1619
|
+
return { chars, controls };
|
|
1439
1620
|
}
|
|
1440
1621
|
function parseCharShapePairs(d) {
|
|
1441
1622
|
const out = [];
|
|
@@ -1610,8 +1791,8 @@ function parseCellRec(d, tag, recs, cStart, cEnd, di, shield, seqIdx, colCnt) {
|
|
|
1610
1791
|
if (tag === TAG_LIST_HEADER && d.length >= 22) {
|
|
1611
1792
|
col = BinaryKit.readU16LE(d, 8);
|
|
1612
1793
|
row = BinaryKit.readU16LE(d, 10);
|
|
1613
|
-
|
|
1614
|
-
|
|
1794
|
+
cs = Math.max(1, BinaryKit.readU16LE(d, 12));
|
|
1795
|
+
rs = Math.max(1, BinaryKit.readU16LE(d, 14));
|
|
1615
1796
|
widthHwp = BinaryKit.readU32LE(d, 16);
|
|
1616
1797
|
const bfId = d.length >= 34 ? BinaryKit.readU16LE(d, 32) : 0;
|
|
1617
1798
|
if (bfId > 0 && bfId <= di.borderFills.length) {
|
|
@@ -1746,6 +1927,45 @@ var HwpScanner = class {
|
|
|
1746
1927
|
if (diRaw) {
|
|
1747
1928
|
di = shield.guard(() => parseDocInfo(diRaw, compressed), di, "hwp:docInfo");
|
|
1748
1929
|
}
|
|
1930
|
+
const imageStreams = [];
|
|
1931
|
+
for (const [path, data2] of streams) {
|
|
1932
|
+
if ((path.includes("BinData") || path.includes(".jpg") || path.includes(".jpeg") || path.includes(".png") || path.includes(".gif") || path.includes(".bmp")) && !path.includes("FileHeader") && !path.includes("DocInfo") && !path.includes("BodyText") && !path.includes("Section")) {
|
|
1933
|
+
imageStreams.push({ path, data: data2 });
|
|
1934
|
+
console.log(`[HwpScanner] Image stream found: ${path} (${data2.length} bytes)`);
|
|
1935
|
+
}
|
|
1936
|
+
}
|
|
1937
|
+
const objectMap = /* @__PURE__ */ new Map();
|
|
1938
|
+
const seenHashes = /* @__PURE__ */ new Set();
|
|
1939
|
+
let imgIdx = 0;
|
|
1940
|
+
for (const { path, data: data2 } of imageStreams) {
|
|
1941
|
+
let mimeType = "image/jpeg";
|
|
1942
|
+
const lowerPath = path.toLowerCase();
|
|
1943
|
+
if (lowerPath.includes(".png")) mimeType = "image/png";
|
|
1944
|
+
else if (lowerPath.includes(".gif")) mimeType = "image/gif";
|
|
1945
|
+
else if (lowerPath.includes(".bmp")) mimeType = "image/bmp";
|
|
1946
|
+
if (data2[0] === 137 && data2[1] === 80 && data2[2] === 78 && data2[3] === 71) mimeType = "image/png";
|
|
1947
|
+
else if (data2[0] === 71 && data2[1] === 73 && data2[2] === 70 && data2[3] === 13624) mimeType = "image/gif";
|
|
1948
|
+
else if (data2[0] === 66 && data2[1] === 77) mimeType = "image/bmp";
|
|
1949
|
+
const imgData = Buffer.from(data2);
|
|
1950
|
+
const base64 = imgData.toString("base64");
|
|
1951
|
+
const hash = base64.slice(0, 20);
|
|
1952
|
+
if (!seenHashes.has(hash)) {
|
|
1953
|
+
seenHashes.add(hash);
|
|
1954
|
+
objectMap.set(imgIdx++, buildImg(
|
|
1955
|
+
base64,
|
|
1956
|
+
mimeType,
|
|
1957
|
+
0,
|
|
1958
|
+
// w
|
|
1959
|
+
0,
|
|
1960
|
+
// h
|
|
1961
|
+
`Image from ${path}`
|
|
1962
|
+
));
|
|
1963
|
+
console.log(`[HwpScanner] Added unique image: ${hash}... (${data2.length} bytes)`);
|
|
1964
|
+
} else {
|
|
1965
|
+
console.log(`[HwpScanner] Duplicate image skipped: ${hash}...`);
|
|
1966
|
+
}
|
|
1967
|
+
}
|
|
1968
|
+
console.log(`[HwpScanner] Found ${imageStreams.length} image streams, ${objectMap.size} unique images`);
|
|
1749
1969
|
const allContent = [];
|
|
1750
1970
|
let pageDims = A4;
|
|
1751
1971
|
for (let s = 0; s < 100; s++) {
|
|
@@ -1769,6 +1989,26 @@ var HwpScanner = class {
|
|
|
1769
1989
|
allContent.push(...r.content);
|
|
1770
1990
|
if (r.pageDims) pageDims = r.pageDims;
|
|
1771
1991
|
}
|
|
1992
|
+
console.log(`[HwpScanner] Before injection: ${allContent.length} nodes, ${objectMap.size} images available`);
|
|
1993
|
+
if (objectMap.size > 0) {
|
|
1994
|
+
injectImagesIntoContent(allContent, objectMap);
|
|
1995
|
+
console.log(`[HwpScanner] After injection: ${allContent.length} nodes`);
|
|
1996
|
+
}
|
|
1997
|
+
const countImages = (nodes) => {
|
|
1998
|
+
let count = 0;
|
|
1999
|
+
for (const node of nodes) {
|
|
2000
|
+
if (node.tag === "img") count++;
|
|
2001
|
+
if (node.tag === "para" && node.kids) count += countImages(node.kids);
|
|
2002
|
+
if (node.tag === "grid" && node.kids) {
|
|
2003
|
+
for (const row of node.kids) {
|
|
2004
|
+
if (row.kids) count += countImages(row.kids);
|
|
2005
|
+
}
|
|
2006
|
+
}
|
|
2007
|
+
}
|
|
2008
|
+
return count;
|
|
2009
|
+
};
|
|
2010
|
+
const imgCount = countImages(allContent);
|
|
2011
|
+
console.log(`[HwpScanner] Images in content: ${imgCount}`);
|
|
1772
2012
|
warns.push(...shield.flush());
|
|
1773
2013
|
const content = allContent.length > 0 ? allContent : [buildPara([buildSpan("")])];
|
|
1774
2014
|
return succeed(buildRoot({}, [buildSheet(content, pageDims)]), warns);
|
|
@@ -1783,6 +2023,33 @@ function findBodySection(streams) {
|
|
|
1783
2023
|
if (k.includes("Section") && !k.includes("Header") && !k.includes("Info")) return v;
|
|
1784
2024
|
return void 0;
|
|
1785
2025
|
}
|
|
2026
|
+
function injectImagesIntoContent(content, objectMap) {
|
|
2027
|
+
const imageArray = Array.from(objectMap.values());
|
|
2028
|
+
if (imageArray.length === 0) return;
|
|
2029
|
+
const uniqueImages = Array.from(new Set(imageArray.map((img) => img.b64))).map((b64) => {
|
|
2030
|
+
return imageArray.find((img) => img.b64 === b64);
|
|
2031
|
+
});
|
|
2032
|
+
if (uniqueImages.length === 0) return;
|
|
2033
|
+
let imgIdx = 0;
|
|
2034
|
+
for (const node of content) {
|
|
2035
|
+
if (node.tag === "para" && node.kids) {
|
|
2036
|
+
for (let i = 0; i < node.kids.length; i++) {
|
|
2037
|
+
const kid = node.kids[i];
|
|
2038
|
+
if (kid.tag === "span" && kid.kids && kid.kids[0]?.tag === "txt") {
|
|
2039
|
+
const text = kid.kids[0].content;
|
|
2040
|
+
const match = text.match?.(/^__(?:IMG|EXT)_(\d+)__$/);
|
|
2041
|
+
if (match) {
|
|
2042
|
+
const imgNode = uniqueImages[imgIdx % uniqueImages.length];
|
|
2043
|
+
if (imgNode) {
|
|
2044
|
+
node.kids[i] = imgNode;
|
|
2045
|
+
imgIdx++;
|
|
2046
|
+
}
|
|
2047
|
+
}
|
|
2048
|
+
}
|
|
2049
|
+
}
|
|
2050
|
+
}
|
|
2051
|
+
}
|
|
2052
|
+
}
|
|
1786
2053
|
registry.registerDecoder(new HwpScanner());
|
|
1787
2054
|
|
|
1788
2055
|
// src/decoders/docx/DocxDecoder.ts
|
|
@@ -1815,26 +2082,59 @@ var DocxDecoder = class {
|
|
|
1815
2082
|
} catch {
|
|
1816
2083
|
}
|
|
1817
2084
|
}
|
|
2085
|
+
let stylesMap = /* @__PURE__ */ new Map();
|
|
2086
|
+
const stylesXml2 = files.get("word/styles.xml");
|
|
2087
|
+
if (stylesXml2) {
|
|
2088
|
+
try {
|
|
2089
|
+
stylesMap = await parseStylesMap(TextKit.decode(stylesXml2));
|
|
2090
|
+
} catch {
|
|
2091
|
+
}
|
|
2092
|
+
}
|
|
1818
2093
|
const docStr = TextKit.decode(docXml);
|
|
1819
2094
|
const docObj = await XmlKit.parseStrict(docStr);
|
|
1820
2095
|
const body = getBody(docObj);
|
|
1821
2096
|
const dims = extractDims2(body) ?? { ...A4 };
|
|
1822
2097
|
const elements = getBodyElements(body);
|
|
1823
|
-
const decCtx = { relsMap, files, shield, numMap, warns };
|
|
1824
|
-
const kids =
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
2098
|
+
const decCtx = { relsMap, files, shield, numMap, warns, stylesMap };
|
|
2099
|
+
const kids = [];
|
|
2100
|
+
for (const el of elements) {
|
|
2101
|
+
const node = shield.guard(
|
|
2102
|
+
() => decodeElement(el, decCtx),
|
|
2103
|
+
buildPara([buildSpan("[\uC694\uC18C \uD30C\uC2F1 \uC2E4\uD328]")]),
|
|
2104
|
+
"docx:bodyElement"
|
|
2105
|
+
);
|
|
2106
|
+
kids.push(node);
|
|
2107
|
+
if (el.type === "para") {
|
|
2108
|
+
const pPr = el.node?.["w:pPr"]?.[0] ?? el.node?.pPr?.[0] ?? {};
|
|
2109
|
+
const inlineSectPr = pPr?.["w:sectPr"]?.[0] ?? pPr?.sectPr?.[0];
|
|
2110
|
+
if (inlineSectPr) {
|
|
2111
|
+
const typeAttr = inlineSectPr?.["w:type"]?.[0]?._attr;
|
|
2112
|
+
const sectType = typeAttr?.["w:val"] ?? typeAttr?.val ?? "nextPage";
|
|
2113
|
+
if (sectType !== "continuous") {
|
|
2114
|
+
kids.push(buildPara([{ tag: "span", props: {}, kids: [buildPb()] }]));
|
|
2115
|
+
}
|
|
2116
|
+
}
|
|
2117
|
+
}
|
|
2118
|
+
}
|
|
2119
|
+
const headerParas = await decodeHeaderFooter2(
|
|
2120
|
+
"header",
|
|
2121
|
+
body,
|
|
2122
|
+
relsMap,
|
|
2123
|
+
files,
|
|
2124
|
+
decCtx
|
|
1829
2125
|
);
|
|
1830
|
-
const
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
{ header: headerParas, footer: footerParas }
|
|
2126
|
+
const footerParas = await decodeHeaderFooter2(
|
|
2127
|
+
"footer",
|
|
2128
|
+
body,
|
|
2129
|
+
relsMap,
|
|
2130
|
+
files,
|
|
2131
|
+
decCtx
|
|
1837
2132
|
);
|
|
2133
|
+
warns.push(...shield.flush());
|
|
2134
|
+
const sheet = buildSheet(kids.filter(Boolean), dims, {
|
|
2135
|
+
header: headerParas,
|
|
2136
|
+
footer: footerParas
|
|
2137
|
+
});
|
|
1838
2138
|
return succeed(buildRoot(meta, [sheet]), warns);
|
|
1839
2139
|
} catch (e) {
|
|
1840
2140
|
warns.push(...shield.flush());
|
|
@@ -1845,6 +2145,19 @@ var DocxDecoder = class {
|
|
|
1845
2145
|
function toArr2(v) {
|
|
1846
2146
|
return v == null ? [] : Array.isArray(v) ? v : [v];
|
|
1847
2147
|
}
|
|
2148
|
+
function resolveDocxPath(baseDir, target) {
|
|
2149
|
+
if (target.startsWith("/")) return target.slice(1);
|
|
2150
|
+
const parts = (baseDir + "/" + target).split("/");
|
|
2151
|
+
const stack = [];
|
|
2152
|
+
for (const p of parts) {
|
|
2153
|
+
if (p === "..") {
|
|
2154
|
+
stack.pop();
|
|
2155
|
+
} else if (p !== ".") {
|
|
2156
|
+
stack.push(p);
|
|
2157
|
+
}
|
|
2158
|
+
}
|
|
2159
|
+
return stack.join("/");
|
|
2160
|
+
}
|
|
1848
2161
|
async function parseRels(xml) {
|
|
1849
2162
|
const map = /* @__PURE__ */ new Map();
|
|
1850
2163
|
try {
|
|
@@ -1879,7 +2192,9 @@ async function parseNumbering(xml) {
|
|
|
1879
2192
|
const root = obj?.["w:numbering"]?.[0] ?? obj?.numbering?.[0] ?? obj;
|
|
1880
2193
|
const absMap = /* @__PURE__ */ new Map();
|
|
1881
2194
|
for (const abs of toArr2(root?.["w:abstractNum"] ?? root?.abstractNum)) {
|
|
1882
|
-
const absId = Number(
|
|
2195
|
+
const absId = Number(
|
|
2196
|
+
abs?._attr?.["w:abstractNumId"] ?? abs?._attr?.abstractNumId ?? 0
|
|
2197
|
+
);
|
|
1883
2198
|
const levels = /* @__PURE__ */ new Map();
|
|
1884
2199
|
for (const lvl of toArr2(abs?.["w:lvl"] ?? abs?.lvl)) {
|
|
1885
2200
|
const ilvl = Number(lvl?._attr?.["w:ilvl"] ?? lvl?._attr?.ilvl ?? 0);
|
|
@@ -1926,8 +2241,10 @@ function extractDims2(body) {
|
|
|
1926
2241
|
function getBodyElements(body) {
|
|
1927
2242
|
const paras = toArr2(body?.["w:p"] ?? body?.p);
|
|
1928
2243
|
const tables = toArr2(body?.["w:tbl"] ?? body?.tbl);
|
|
1929
|
-
if (tables.length === 0)
|
|
1930
|
-
|
|
2244
|
+
if (tables.length === 0)
|
|
2245
|
+
return paras.map((n) => ({ type: "para", node: n }));
|
|
2246
|
+
if (paras.length === 0)
|
|
2247
|
+
return tables.map((n) => ({ type: "table", node: n }));
|
|
1931
2248
|
const childOrder = body?.["_childOrder"];
|
|
1932
2249
|
if (Array.isArray(childOrder)) {
|
|
1933
2250
|
const items = [];
|
|
@@ -1940,7 +2257,8 @@ function getBodyElements(body) {
|
|
|
1940
2257
|
}
|
|
1941
2258
|
}
|
|
1942
2259
|
while (pi < paras.length) items.push({ type: "para", node: paras[pi++] });
|
|
1943
|
-
while (ti < tables.length)
|
|
2260
|
+
while (ti < tables.length)
|
|
2261
|
+
items.push({ type: "table", node: tables[ti++] });
|
|
1944
2262
|
return items;
|
|
1945
2263
|
}
|
|
1946
2264
|
return [
|
|
@@ -1959,7 +2277,7 @@ async function decodeHeaderFooter2(kind, body, relsMap, files, ctx) {
|
|
|
1959
2277
|
if (!rId) return void 0;
|
|
1960
2278
|
const target = relsMap.get(rId);
|
|
1961
2279
|
if (!target) return void 0;
|
|
1962
|
-
const filePath =
|
|
2280
|
+
const filePath = resolveDocxPath("word", target);
|
|
1963
2281
|
const fileData = files.get(filePath);
|
|
1964
2282
|
if (!fileData) return void 0;
|
|
1965
2283
|
const xmlStr = TextKit.decode(fileData);
|
|
@@ -1973,6 +2291,14 @@ async function decodeHeaderFooter2(kind, body, relsMap, files, ctx) {
|
|
|
1973
2291
|
return void 0;
|
|
1974
2292
|
}
|
|
1975
2293
|
}
|
|
2294
|
+
function hasDrawingDeep(node) {
|
|
2295
|
+
if (!node || typeof node !== "object") return false;
|
|
2296
|
+
if (node["w:drawing"] || node["w:pict"]) return true;
|
|
2297
|
+
return Object.values(node).some((v) => {
|
|
2298
|
+
if (Array.isArray(v)) return v.some(hasDrawingDeep);
|
|
2299
|
+
return hasDrawingDeep(v);
|
|
2300
|
+
});
|
|
2301
|
+
}
|
|
1976
2302
|
function decodeElement(el, ctx) {
|
|
1977
2303
|
if (el.type === "table") {
|
|
1978
2304
|
const { value } = ctx.shield.guardGrid(
|
|
@@ -1995,6 +2321,19 @@ function decodePara2(p, ctx) {
|
|
|
1995
2321
|
align: safeAlign(alignVal),
|
|
1996
2322
|
heading: parseHeading(headStyle)
|
|
1997
2323
|
};
|
|
2324
|
+
const spacingAttr = pPr?.["w:spacing"]?.[0]?._attr ?? pPr?.spacing?.[0]?._attr ?? {};
|
|
2325
|
+
const beforeVal = Number(
|
|
2326
|
+
spacingAttr?.["w:before"] ?? spacingAttr?.before ?? 0
|
|
2327
|
+
);
|
|
2328
|
+
const afterVal = Number(spacingAttr?.["w:after"] ?? spacingAttr?.after ?? 0);
|
|
2329
|
+
const lineVal = Number(spacingAttr?.["w:line"] ?? spacingAttr?.line ?? 0);
|
|
2330
|
+
const lineRule = spacingAttr?.["w:lineRule"] ?? spacingAttr?.lineRule ?? "auto";
|
|
2331
|
+
if (beforeVal > 0) props.spaceBefore = Metric.dxaToPt(beforeVal);
|
|
2332
|
+
if (afterVal > 0) props.spaceAfter = Metric.dxaToPt(afterVal);
|
|
2333
|
+
if (lineVal > 0 && lineRule === "auto") props.lineHeight = lineVal / 240;
|
|
2334
|
+
const indAttr = pPr?.["w:ind"]?.[0]?._attr ?? pPr?.ind?.[0]?._attr ?? {};
|
|
2335
|
+
const leftVal = Number(indAttr?.["w:left"] ?? indAttr?.left ?? 0);
|
|
2336
|
+
if (leftVal > 0) props.indentPt = Metric.dxaToPt(leftVal);
|
|
1998
2337
|
const numPr = pPr?.["w:numPr"]?.[0] ?? pPr?.numPr?.[0];
|
|
1999
2338
|
if (numPr) {
|
|
2000
2339
|
const ilvlNode = numPr?.["w:ilvl"]?.[0]?._attr ?? numPr?.ilvl?.[0]?._attr ?? {};
|
|
@@ -2010,17 +2349,40 @@ function decodePara2(p, ctx) {
|
|
|
2010
2349
|
props.listOrd = numId >= 2;
|
|
2011
2350
|
}
|
|
2012
2351
|
}
|
|
2352
|
+
const pbBeforeNode = pPr?.["w:pageBreakBefore"]?.[0] ?? pPr?.pageBreakBefore?.[0];
|
|
2353
|
+
const hasPageBreakBefore = pbBeforeNode != null && (pbBeforeNode?._attr?.["w:val"] ?? pbBeforeNode?._attr?.val ?? "1") !== "0";
|
|
2013
2354
|
const runs = toArr2(p?.["w:r"] ?? p?.r);
|
|
2014
2355
|
const kids = ctx.shield.guardAll(
|
|
2015
2356
|
runs,
|
|
2016
|
-
(run) => decodeRunOrImage(run, ctx),
|
|
2357
|
+
(run) => hasDrawingDeep(run) ? decodeRunOrImage(run, ctx) : decodeRun(run, ctx),
|
|
2017
2358
|
() => buildSpan(""),
|
|
2018
2359
|
"docx:run"
|
|
2019
2360
|
);
|
|
2020
|
-
|
|
2361
|
+
const filteredKids = kids.filter(Boolean);
|
|
2362
|
+
if (hasPageBreakBefore) {
|
|
2363
|
+
filteredKids.unshift({ tag: "span", props: {}, kids: [buildPb()] });
|
|
2364
|
+
}
|
|
2365
|
+
return buildPara(filteredKids, props);
|
|
2021
2366
|
}
|
|
2022
2367
|
function decodeRunOrImage(run, ctx) {
|
|
2023
|
-
|
|
2368
|
+
function findFirstDrawing(node) {
|
|
2369
|
+
if (!node || typeof node !== "object") return null;
|
|
2370
|
+
if (node["w:drawing"]) return node["w:drawing"][0];
|
|
2371
|
+
if (node["w:pict"]) return node["w:pict"][0];
|
|
2372
|
+
for (const value of Object.values(node)) {
|
|
2373
|
+
if (Array.isArray(value)) {
|
|
2374
|
+
for (const v of value) {
|
|
2375
|
+
const found = findFirstDrawing(v);
|
|
2376
|
+
if (found) return found;
|
|
2377
|
+
}
|
|
2378
|
+
} else {
|
|
2379
|
+
const found = findFirstDrawing(value);
|
|
2380
|
+
if (found) return found;
|
|
2381
|
+
}
|
|
2382
|
+
}
|
|
2383
|
+
return null;
|
|
2384
|
+
}
|
|
2385
|
+
const drawing = findFirstDrawing(run);
|
|
2024
2386
|
if (drawing) {
|
|
2025
2387
|
const img = decodeDrawing(drawing, ctx);
|
|
2026
2388
|
if (img) return img;
|
|
@@ -2049,9 +2411,14 @@ function decodeDrawing(drawing, ctx) {
|
|
|
2049
2411
|
if (!rId) return null;
|
|
2050
2412
|
const target = ctx.relsMap.get(rId);
|
|
2051
2413
|
if (!target) return null;
|
|
2052
|
-
const filePath =
|
|
2414
|
+
const filePath = resolveDocxPath("word", target);
|
|
2053
2415
|
const fileData = ctx.files.get(filePath);
|
|
2054
|
-
if (!fileData)
|
|
2416
|
+
if (!fileData) {
|
|
2417
|
+
console.warn(
|
|
2418
|
+
`[DocxDecoder] image not found in ZIP: "${filePath}" (rId=${rId}, target=${target})`
|
|
2419
|
+
);
|
|
2420
|
+
return null;
|
|
2421
|
+
}
|
|
2055
2422
|
const ext = target.split(".").pop()?.toLowerCase() ?? "png";
|
|
2056
2423
|
const mimeMap = {
|
|
2057
2424
|
png: "image/png",
|
|
@@ -2061,7 +2428,11 @@ function decodeDrawing(drawing, ctx) {
|
|
|
2061
2428
|
bmp: "image/bmp"
|
|
2062
2429
|
};
|
|
2063
2430
|
const mime = mimeMap[ext] ?? "image/png";
|
|
2064
|
-
|
|
2431
|
+
console.log(
|
|
2432
|
+
`[DocxDecoder] image loaded: ${filePath} (${mime}, ${fileData.length} bytes)`
|
|
2433
|
+
);
|
|
2434
|
+
const layout = inline ? { wrap: "inline" } : extractAnchorLayout(anchor);
|
|
2435
|
+
return buildImg(TextKit.base64Encode(fileData), mime, wPt, hPt, alt || void 0, layout);
|
|
2065
2436
|
} catch {
|
|
2066
2437
|
return null;
|
|
2067
2438
|
}
|
|
@@ -2098,6 +2469,13 @@ function decodeRun(run, ctx) {
|
|
|
2098
2469
|
};
|
|
2099
2470
|
const fldChar = run?.["w:fldChar"]?.[0]?._attr ?? run?.fldChar?.[0]?._attr;
|
|
2100
2471
|
const instrText = run?.["w:instrText"]?.[0];
|
|
2472
|
+
const brNodes = toArr2(run?.["w:br"] ?? run?.br ?? []);
|
|
2473
|
+
for (const br of brNodes) {
|
|
2474
|
+
const brType = br?._attr?.["w:type"] ?? br?._attr?.type;
|
|
2475
|
+
if (brType === "page") {
|
|
2476
|
+
return { tag: "span", props, kids: [buildPb()] };
|
|
2477
|
+
}
|
|
2478
|
+
}
|
|
2101
2479
|
const textNodes = toArr2(run?.["w:t"] ?? run?.t);
|
|
2102
2480
|
const content = textNodes.map((t) => typeof t === "string" ? t : t?._ ?? t?._text ?? "").join("");
|
|
2103
2481
|
if (instrText) {
|
|
@@ -2109,6 +2487,73 @@ function decodeRun(run, ctx) {
|
|
|
2109
2487
|
}
|
|
2110
2488
|
return buildSpan(content, props);
|
|
2111
2489
|
}
|
|
2490
|
+
function parseBorderDef(bdrNode) {
|
|
2491
|
+
const sides = [
|
|
2492
|
+
["top", "top"],
|
|
2493
|
+
["bottom", "bottom"],
|
|
2494
|
+
["left", "left"],
|
|
2495
|
+
["right", "right"],
|
|
2496
|
+
["insideH", "insideH"],
|
|
2497
|
+
["insideV", "insideV"]
|
|
2498
|
+
];
|
|
2499
|
+
const result = {};
|
|
2500
|
+
for (const [xml, prop] of sides) {
|
|
2501
|
+
const bdr = bdrNode?.["w:" + xml]?.[0]?._attr ?? bdrNode?.[xml]?.[0]?._attr;
|
|
2502
|
+
if (!bdr) continue;
|
|
2503
|
+
const val = bdr?.["w:val"] ?? bdr?.val;
|
|
2504
|
+
if (val === "none" || val === "nil") continue;
|
|
2505
|
+
result[prop] = safeStrokeDocx(
|
|
2506
|
+
val,
|
|
2507
|
+
Number(bdr?.["w:sz"] ?? bdr?.sz ?? 4),
|
|
2508
|
+
bdr?.["w:color"] ?? bdr?.color
|
|
2509
|
+
);
|
|
2510
|
+
}
|
|
2511
|
+
return result;
|
|
2512
|
+
}
|
|
2513
|
+
async function parseStylesMap(xml) {
|
|
2514
|
+
const map = /* @__PURE__ */ new Map();
|
|
2515
|
+
try {
|
|
2516
|
+
const obj = await XmlKit.parseStrict(xml);
|
|
2517
|
+
const stylesRoot = obj?.["w:styles"]?.[0] ?? obj?.styles?.[0] ?? obj;
|
|
2518
|
+
const styleArr = toArr2(stylesRoot?.["w:style"] ?? stylesRoot?.style);
|
|
2519
|
+
for (const style of styleArr) {
|
|
2520
|
+
const attr = style?._attr ?? {};
|
|
2521
|
+
const type = attr?.["w:type"] ?? attr?.type;
|
|
2522
|
+
if (type !== "table") continue;
|
|
2523
|
+
const id = attr?.["w:styleId"] ?? attr?.styleId;
|
|
2524
|
+
if (!id) continue;
|
|
2525
|
+
const tblPr = style?.["w:tblPr"]?.[0] ?? style?.tblPr?.[0];
|
|
2526
|
+
const tblBdrNode = tblPr?.["w:tblBorders"]?.[0] ?? tblPr?.tblBorders?.[0];
|
|
2527
|
+
const tblBorders = tblBdrNode ? parseBorderDef(tblBdrNode) : void 0;
|
|
2528
|
+
const tcStyle = style?.["w:tcStyle"]?.[0] ?? style?.tcStyle?.[0];
|
|
2529
|
+
const tcBdrNode = tcStyle?.["w:tcBdr"]?.[0] ?? tcStyle?.tcBdr?.[0];
|
|
2530
|
+
if (tcBdrNode) {
|
|
2531
|
+
const cellDef = parseBorderDef(tcBdrNode);
|
|
2532
|
+
if (!tblBorders) {
|
|
2533
|
+
map.set(id, { tblBorders: cellDef });
|
|
2534
|
+
} else {
|
|
2535
|
+
map.set(id, { tblBorders: { ...cellDef, ...tblBorders } });
|
|
2536
|
+
}
|
|
2537
|
+
} else if (tblBorders) {
|
|
2538
|
+
map.set(id, { tblBorders });
|
|
2539
|
+
}
|
|
2540
|
+
}
|
|
2541
|
+
} catch {
|
|
2542
|
+
}
|
|
2543
|
+
return map;
|
|
2544
|
+
}
|
|
2545
|
+
function resolveCellBorders(cp, ri, ci, rs, cs, rowCount, colCount, tblBdr) {
|
|
2546
|
+
const isTopEdge = ri === 0;
|
|
2547
|
+
const isBottomEdge = ri + rs >= rowCount;
|
|
2548
|
+
const isLeftEdge = ci === 0;
|
|
2549
|
+
const isRightEdge = ci + cs >= colCount;
|
|
2550
|
+
const resolved = { ...cp };
|
|
2551
|
+
if (!resolved.top) resolved.top = isTopEdge ? tblBdr.top : tblBdr.insideH;
|
|
2552
|
+
if (!resolved.bot) resolved.bot = isBottomEdge ? tblBdr.bottom : tblBdr.insideH;
|
|
2553
|
+
if (!resolved.left) resolved.left = isLeftEdge ? tblBdr.left : tblBdr.insideV;
|
|
2554
|
+
if (!resolved.right) resolved.right = isRightEdge ? tblBdr.right : tblBdr.insideV;
|
|
2555
|
+
return resolved;
|
|
2556
|
+
}
|
|
2112
2557
|
function decodeGrid2(tbl, ctx) {
|
|
2113
2558
|
const tblPr = tbl?.["w:tblPr"]?.[0] ?? tbl?.tblPr?.[0] ?? {};
|
|
2114
2559
|
const tblLookAttr = tblPr?.["w:tblLook"]?.[0]?._attr ?? tblPr?.tblLook?.[0]?._attr ?? {};
|
|
@@ -2120,19 +2565,24 @@ function decodeGrid2(tbl, ctx) {
|
|
|
2120
2565
|
bandedRows: tblLookAttr?.["w:noHBand"] === "0" || void 0,
|
|
2121
2566
|
bandedCols: tblLookAttr?.["w:noVBand"] === "0" || void 0
|
|
2122
2567
|
};
|
|
2123
|
-
const
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
);
|
|
2133
|
-
}
|
|
2134
|
-
}
|
|
2568
|
+
const tblStyleId = (tblPr?.["w:tblStyle"]?.[0]?._attr ?? tblPr?.tblStyle?.[0]?._attr)?.["w:val"];
|
|
2569
|
+
const styleDef = tblStyleId ? ctx.stylesMap.get(tblStyleId) : void 0;
|
|
2570
|
+
let tblBdr = styleDef?.tblBorders ?? {};
|
|
2571
|
+
const tblBordersNode = tblPr?.["w:tblBorders"]?.[0] ?? tblPr?.tblBorders?.[0];
|
|
2572
|
+
if (tblBordersNode) {
|
|
2573
|
+
const parsed = parseBorderDef(tblBordersNode);
|
|
2574
|
+
tblBdr = { ...tblBdr, ...parsed };
|
|
2575
|
+
}
|
|
2576
|
+
const defaultStroke = tblBdr.insideH ?? tblBdr.top;
|
|
2135
2577
|
const gridProps = { look, defaultStroke };
|
|
2578
|
+
const tblGrid = tbl?.["w:tblGrid"]?.[0] ?? tbl?.tblGrid?.[0];
|
|
2579
|
+
if (tblGrid) {
|
|
2580
|
+
const gridCols = toArr2(tblGrid?.["w:gridCol"] ?? tblGrid?.gridCol ?? []);
|
|
2581
|
+
const colWidthsPt = gridCols.map(
|
|
2582
|
+
(gc) => Metric.dxaToPt(Number(gc?._attr?.["w:w"] ?? gc?._attr?.w ?? 0))
|
|
2583
|
+
).filter((w) => w > 0);
|
|
2584
|
+
if (colWidthsPt.length > 0) gridProps.colWidths = colWidthsPt;
|
|
2585
|
+
}
|
|
2136
2586
|
const rowArr = toArr2(tbl?.["w:tr"] ?? tbl?.tr);
|
|
2137
2587
|
const rawGrid = rowArr.map((row) => {
|
|
2138
2588
|
const cellArr = toArr2(row?.["w:tc"] ?? row?.tc);
|
|
@@ -2176,6 +2626,12 @@ function decodeGrid2(tbl, ctx) {
|
|
|
2176
2626
|
const trPr = row?.["w:trPr"]?.[0] ?? row?.trPr?.[0] ?? {};
|
|
2177
2627
|
const isHeaderRow = trPr?.["w:tblHeader"]?.[0] != null || trPr?.tblHeader?.[0] != null;
|
|
2178
2628
|
if (ri === 0 && isHeaderRow) gridProps.headerRow = true;
|
|
2629
|
+
let rowHeightPt;
|
|
2630
|
+
const trHAttr = trPr?.["w:trHeight"]?.[0]?._attr ?? trPr?.trHeight?.[0]?._attr;
|
|
2631
|
+
if (trHAttr) {
|
|
2632
|
+
const hDxa = Number(trHAttr?.["w:val"] ?? trHAttr?.val ?? 0);
|
|
2633
|
+
if (hDxa > 0) rowHeightPt = Metric.dxaToPt(hDxa);
|
|
2634
|
+
}
|
|
2179
2635
|
const cellNodes = [];
|
|
2180
2636
|
for (let ci = 0; ci < rawRow.length; ci++) {
|
|
2181
2637
|
const rc = rawRow[ci];
|
|
@@ -2184,9 +2640,9 @@ function decodeGrid2(tbl, ctx) {
|
|
|
2184
2640
|
const tcPr = cell?.["w:tcPr"]?.[0] ?? {};
|
|
2185
2641
|
const bgAttr = tcPr?.["w:shd"]?.[0]?._attr ?? {};
|
|
2186
2642
|
const bg = safeHex(bgAttr?.["w:fill"] ?? bgAttr?.fill);
|
|
2187
|
-
const
|
|
2643
|
+
const tcBordersNode = tcPr?.["w:tcBorders"]?.[0] ?? tcPr?.tcBorders?.[0];
|
|
2188
2644
|
const cp = { bg, isHeader: isHeaderRow || void 0 };
|
|
2189
|
-
if (
|
|
2645
|
+
if (tcBordersNode) {
|
|
2190
2646
|
const dirs = [
|
|
2191
2647
|
["top", "top"],
|
|
2192
2648
|
["bottom", "bot"],
|
|
@@ -2194,10 +2650,13 @@ function decodeGrid2(tbl, ctx) {
|
|
|
2194
2650
|
["right", "right"]
|
|
2195
2651
|
];
|
|
2196
2652
|
for (const [xmlTag, propKey] of dirs) {
|
|
2197
|
-
const bdr =
|
|
2198
|
-
if (bdr)
|
|
2653
|
+
const bdr = tcBordersNode?.["w:" + xmlTag]?.[0]?._attr ?? tcBordersNode?.[xmlTag]?.[0]?._attr;
|
|
2654
|
+
if (!bdr) continue;
|
|
2655
|
+
const val = bdr?.["w:val"] ?? bdr?.val;
|
|
2656
|
+
if (val === "none" || val === "nil") {
|
|
2657
|
+
} else {
|
|
2199
2658
|
cp[propKey] = safeStrokeDocx(
|
|
2200
|
-
|
|
2659
|
+
val,
|
|
2201
2660
|
Number(bdr?.["w:sz"] ?? bdr?.sz ?? 4),
|
|
2202
2661
|
bdr?.["w:color"] ?? bdr?.color
|
|
2203
2662
|
);
|
|
@@ -2207,17 +2666,32 @@ function decodeGrid2(tbl, ctx) {
|
|
|
2207
2666
|
const vaAttr = tcPr?.["w:vAlign"]?.[0]?._attr ?? tcPr?.vAlign?.[0]?._attr ?? {};
|
|
2208
2667
|
const vaVal = vaAttr?.["w:val"] ?? vaAttr?.val;
|
|
2209
2668
|
if (vaVal) {
|
|
2210
|
-
const vaMap = {
|
|
2669
|
+
const vaMap = {
|
|
2670
|
+
top: "top",
|
|
2671
|
+
center: "mid",
|
|
2672
|
+
bottom: "bot"
|
|
2673
|
+
};
|
|
2211
2674
|
cp.va = vaMap[vaVal];
|
|
2212
2675
|
}
|
|
2213
2676
|
const rs = rsMap.get(`${ri},${ci}`) ?? 1;
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
));
|
|
2677
|
+
let gridColIdx = 0;
|
|
2678
|
+
for (let prevCi = 0; prevCi < ci; prevCi++) {
|
|
2679
|
+
if (!rawRow[prevCi].vMergeContinue) gridColIdx += rawRow[prevCi].gridSpan;
|
|
2680
|
+
}
|
|
2681
|
+
const colCount = gridProps.colWidths?.length ?? rawGrid[0]?.reduce((s, c) => s + c.gridSpan, 0) ?? 1;
|
|
2682
|
+
const resolvedCp = resolveCellBorders(cp, ri, gridColIdx, rs, rc.gridSpan, rawGrid.length, colCount, tblBdr);
|
|
2683
|
+
const paras = toArr2(cell?.["w:p"] ?? cell?.p).map(
|
|
2684
|
+
(p) => decodePara2(p, ctx)
|
|
2685
|
+
);
|
|
2686
|
+
cellNodes.push(
|
|
2687
|
+
buildCell(paras.length > 0 ? paras : [buildPara([buildSpan("")])], {
|
|
2688
|
+
cs: rc.gridSpan,
|
|
2689
|
+
rs,
|
|
2690
|
+
props: resolvedCp
|
|
2691
|
+
})
|
|
2692
|
+
);
|
|
2219
2693
|
}
|
|
2220
|
-
return buildRow(cellNodes);
|
|
2694
|
+
return buildRow(cellNodes, rowHeightPt);
|
|
2221
2695
|
});
|
|
2222
2696
|
return buildGrid(rowNodes, gridProps);
|
|
2223
2697
|
}
|
|
@@ -2225,12 +2699,16 @@ function decodeGridSimple2(tbl) {
|
|
|
2225
2699
|
const rowArr = toArr2(tbl?.["w:tr"] ?? tbl?.tr);
|
|
2226
2700
|
const rowNodes = rowArr.map((row) => {
|
|
2227
2701
|
const cellArr = toArr2(row?.["w:tc"] ?? row?.tc);
|
|
2228
|
-
return buildRow(
|
|
2702
|
+
return buildRow(
|
|
2703
|
+
cellArr.map((c) => buildCell([buildPara([buildSpan(cellText2(c))])]))
|
|
2704
|
+
);
|
|
2229
2705
|
});
|
|
2230
2706
|
return buildGrid(rowNodes);
|
|
2231
2707
|
}
|
|
2232
2708
|
function decodeGridFlat2(tbl) {
|
|
2233
|
-
return buildGrid([
|
|
2709
|
+
return buildGrid([
|
|
2710
|
+
buildRow([buildCell([buildPara([buildSpan(tableText2(tbl))])])])
|
|
2711
|
+
]);
|
|
2234
2712
|
}
|
|
2235
2713
|
function decodeGridText2(tbl) {
|
|
2236
2714
|
return buildPara([buildSpan(tableText2(tbl))]);
|
|
@@ -2257,6 +2735,82 @@ function parseHeading(style) {
|
|
|
2257
2735
|
return void 0;
|
|
2258
2736
|
}
|
|
2259
2737
|
registry.registerDecoder(new DocxDecoder());
|
|
2738
|
+
function extractAnchorLayout(anchor) {
|
|
2739
|
+
const attr = anchor?._attr ?? {};
|
|
2740
|
+
const behindDoc = attr.behindDoc === "1";
|
|
2741
|
+
let wrap = "square";
|
|
2742
|
+
if (anchor?.["wp:wrapNone"]?.[0] != null) wrap = behindDoc ? "behind" : "none";
|
|
2743
|
+
else if (anchor?.["wp:wrapTight"]?.[0] != null) wrap = "tight";
|
|
2744
|
+
else if (anchor?.["wp:wrapThrough"]?.[0] != null) wrap = "through";
|
|
2745
|
+
else if (anchor?.["wp:wrapSquare"]?.[0] != null) wrap = "square";
|
|
2746
|
+
else if (anchor?.["wp:wrapTopAndBottom"]?.[0] != null) wrap = "square";
|
|
2747
|
+
else if (anchor?.["wp:wrapBehind"]?.[0] != null || behindDoc) wrap = "behind";
|
|
2748
|
+
const posH = anchor?.["wp:positionH"]?.[0];
|
|
2749
|
+
const horzRelTo = parseHorzRelTo(posH?._attr?.relativeFrom);
|
|
2750
|
+
const horzAlignTxt = posH?.["wp:align"]?.[0]?._text;
|
|
2751
|
+
const horzOffsetTxt = posH?.["wp:posOffset"]?.[0]?._text;
|
|
2752
|
+
const horzAlign = horzAlignTxt ? parseHorzAlign(horzAlignTxt) : void 0;
|
|
2753
|
+
const xPt = horzOffsetTxt && !horzAlignTxt ? Metric.emuToPt(Number(horzOffsetTxt)) : void 0;
|
|
2754
|
+
const posV = anchor?.["wp:positionV"]?.[0];
|
|
2755
|
+
const vertRelTo = parseVertRelTo(posV?._attr?.relativeFrom);
|
|
2756
|
+
const vertAlignTxt = posV?.["wp:align"]?.[0]?._text;
|
|
2757
|
+
const vertOffsetTxt = posV?.["wp:posOffset"]?.[0]?._text;
|
|
2758
|
+
const vertAlign = vertAlignTxt ? parseVertAlign(vertAlignTxt) : void 0;
|
|
2759
|
+
const yPt = vertOffsetTxt && !vertAlignTxt ? Metric.emuToPt(Number(vertOffsetTxt)) : void 0;
|
|
2760
|
+
const distT = attr.distT ? Metric.emuToPt(Number(attr.distT)) : void 0;
|
|
2761
|
+
const distB = attr.distB ? Metric.emuToPt(Number(attr.distB)) : void 0;
|
|
2762
|
+
const distL = attr.distL ? Metric.emuToPt(Number(attr.distL)) : void 0;
|
|
2763
|
+
const distR = attr.distR ? Metric.emuToPt(Number(attr.distR)) : void 0;
|
|
2764
|
+
const zOrder = attr.relativeHeight ? Number(attr.relativeHeight) : void 0;
|
|
2765
|
+
return { wrap, horzAlign, vertAlign, horzRelTo, vertRelTo, xPt, yPt, distT, distB, distL, distR, behindDoc, zOrder };
|
|
2766
|
+
}
|
|
2767
|
+
var HORZ_RELTO_MAP = {
|
|
2768
|
+
margin: "margin",
|
|
2769
|
+
leftMargin: "margin",
|
|
2770
|
+
rightMargin: "margin",
|
|
2771
|
+
insideMargin: "margin",
|
|
2772
|
+
outsideMargin: "margin",
|
|
2773
|
+
column: "column",
|
|
2774
|
+
page: "page",
|
|
2775
|
+
character: "para",
|
|
2776
|
+
paragraph: "para"
|
|
2777
|
+
};
|
|
2778
|
+
var VERT_RELTO_MAP = {
|
|
2779
|
+
margin: "margin",
|
|
2780
|
+
topMargin: "margin",
|
|
2781
|
+
bottomMargin: "margin",
|
|
2782
|
+
insideMargin: "margin",
|
|
2783
|
+
outsideMargin: "margin",
|
|
2784
|
+
line: "line",
|
|
2785
|
+
page: "page",
|
|
2786
|
+
paragraph: "para"
|
|
2787
|
+
};
|
|
2788
|
+
var HORZ_ALIGN_MAP = {
|
|
2789
|
+
left: "left",
|
|
2790
|
+
center: "center",
|
|
2791
|
+
right: "right",
|
|
2792
|
+
inside: "left",
|
|
2793
|
+
outside: "right"
|
|
2794
|
+
};
|
|
2795
|
+
var VERT_ALIGN_MAP = {
|
|
2796
|
+
top: "top",
|
|
2797
|
+
center: "center",
|
|
2798
|
+
bottom: "bottom",
|
|
2799
|
+
inside: "top",
|
|
2800
|
+
outside: "bottom"
|
|
2801
|
+
};
|
|
2802
|
+
function parseHorzRelTo(v) {
|
|
2803
|
+
return HORZ_RELTO_MAP[v ?? ""] ?? "column";
|
|
2804
|
+
}
|
|
2805
|
+
function parseVertRelTo(v) {
|
|
2806
|
+
return VERT_RELTO_MAP[v ?? ""] ?? "para";
|
|
2807
|
+
}
|
|
2808
|
+
function parseHorzAlign(v) {
|
|
2809
|
+
return HORZ_ALIGN_MAP[v ?? ""];
|
|
2810
|
+
}
|
|
2811
|
+
function parseVertAlign(v) {
|
|
2812
|
+
return VERT_ALIGN_MAP[v ?? ""];
|
|
2813
|
+
}
|
|
2260
2814
|
|
|
2261
2815
|
// src/decoders/md/MdDecoder.ts
|
|
2262
2816
|
var MdDecoder = class {
|
|
@@ -2457,6 +3011,7 @@ var NS = [
|
|
|
2457
3011
|
'xmlns:dc="http://purl.org/dc/elements/1.1/"',
|
|
2458
3012
|
'xmlns:opf="http://www.idpf.org/2007/opf/"',
|
|
2459
3013
|
'xmlns:ooxmlchart="http://www.hancom.co.kr/hwpml/2016/ooxmlchart"',
|
|
3014
|
+
'xmlns:hwpunitchar="http://www.hancom.co.kr/hwpml/2016/HwpUnitChar"',
|
|
2460
3015
|
'xmlns:epub="http://www.idpf.org/2007/ops"',
|
|
2461
3016
|
'xmlns:config="urn:oasis:names:tc:opendocument:xmlns:config:1.0"'
|
|
2462
3017
|
].join(" ");
|
|
@@ -2464,7 +3019,7 @@ function charPrKey(p) {
|
|
|
2464
3019
|
return `${p.b ? 1 : 0}|${p.i ? 1 : 0}|${p.u ? 1 : 0}|${p.s ? 1 : 0}|${p.pt ?? 10}|${p.color ?? "000000"}|${p.font ?? ""}|${p.bg ?? ""}`;
|
|
2465
3020
|
}
|
|
2466
3021
|
function paraPrKey(p) {
|
|
2467
|
-
return `${p.align ?? "left"}|${p.listOrd ?? ""}|${p.listLv ?? 0}`;
|
|
3022
|
+
return `${p.align ?? "left"}|${p.listOrd ?? ""}|${p.listLv ?? 0}|${p.indentPt ?? 0}|${p.spaceBefore ?? 0}|${p.spaceAfter ?? 0}|${p.lineHeight ?? 0}`;
|
|
2468
3023
|
}
|
|
2469
3024
|
function registerFont(name, ctx) {
|
|
2470
3025
|
const n = name || "\uAD74\uB9BC\uCCB4";
|
|
@@ -2505,7 +3060,11 @@ function registerParaPr(props, ctx) {
|
|
|
2505
3060
|
const id = ctx.paraPrs.length;
|
|
2506
3061
|
const def = {
|
|
2507
3062
|
id,
|
|
2508
|
-
align: (props.align ?? "left").toUpperCase()
|
|
3063
|
+
align: (props.align ?? "left").toUpperCase(),
|
|
3064
|
+
intentHwp: props.indentPt ? Metric.ptToHwp(props.indentPt) : 0,
|
|
3065
|
+
prevHwp: props.spaceBefore ? Metric.ptToHwp(props.spaceBefore) : 0,
|
|
3066
|
+
nextHwp: props.spaceAfter ? Metric.ptToHwp(props.spaceAfter) : 0,
|
|
3067
|
+
lineSpacing: props.lineHeight ? Math.round(props.lineHeight * 100) : 160
|
|
2509
3068
|
};
|
|
2510
3069
|
if (props.listOrd !== void 0) {
|
|
2511
3070
|
def.listType = props.listOrd ? "DIGIT" : "BULLET";
|
|
@@ -2530,8 +3089,7 @@ function scanPara(para, ctx) {
|
|
|
2530
3089
|
}
|
|
2531
3090
|
function scanGrid(grid, ctx) {
|
|
2532
3091
|
for (const row of grid.kids)
|
|
2533
|
-
for (const cell of row.kids)
|
|
2534
|
-
for (const p of cell.kids) scanPara(p, ctx);
|
|
3092
|
+
for (const cell of row.kids) for (const p of cell.kids) scanPara(p, ctx);
|
|
2535
3093
|
}
|
|
2536
3094
|
function scanParas(paras, ctx) {
|
|
2537
3095
|
for (const p of paras) scanPara(p, ctx);
|
|
@@ -2543,19 +3101,25 @@ function mimeToExt(mime) {
|
|
|
2543
3101
|
return "png";
|
|
2544
3102
|
}
|
|
2545
3103
|
function registerImage(img, ctx) {
|
|
2546
|
-
if (img
|
|
3104
|
+
if (ctx.imgMap.has(img)) return;
|
|
2547
3105
|
const ext = mimeToExt(img.mime);
|
|
2548
3106
|
const id = `BIN${String(ctx.nextBinNum).padStart(4, "0")}`;
|
|
2549
3107
|
const name = `${id}.${ext}`;
|
|
2550
3108
|
ctx.nextBinNum++;
|
|
2551
3109
|
const data = TextKit.base64Decode(img.b64);
|
|
2552
3110
|
ctx.bins.push({ id, name, data });
|
|
2553
|
-
img
|
|
3111
|
+
ctx.imgMap.set(img, id);
|
|
2554
3112
|
}
|
|
2555
3113
|
function addBorderFill(ctx, stroke, bgColor) {
|
|
2556
3114
|
const id = ctx.borderFills.length + 1;
|
|
2557
3115
|
const s = stroke ?? DEFAULT_STROKE;
|
|
2558
|
-
const kindMap = {
|
|
3116
|
+
const kindMap = {
|
|
3117
|
+
solid: "SOLID",
|
|
3118
|
+
dash: "DASH",
|
|
3119
|
+
dot: "DOT",
|
|
3120
|
+
double: "DOUBLE",
|
|
3121
|
+
none: "NONE"
|
|
3122
|
+
};
|
|
2559
3123
|
const type = kindMap[s.kind] ?? "SOLID";
|
|
2560
3124
|
const w = `${(s.pt * 0.3528).toFixed(2)} mm`;
|
|
2561
3125
|
const c = s.color.startsWith("#") ? s.color : `#${s.color}`;
|
|
@@ -2564,7 +3128,31 @@ function addBorderFill(ctx, stroke, bgColor) {
|
|
|
2564
3128
|
const bc = bgColor.startsWith("#") ? bgColor : `#${bgColor}`;
|
|
2565
3129
|
fill = `<hc:fillBrush><hc:winBrush faceColor="${bc}" hatchColor="none" alpha="0"/></hc:fillBrush>`;
|
|
2566
3130
|
}
|
|
2567
|
-
const xml = `<hh:borderFill id="${id}" threeD="0" shadow="0" centerLine="NONE" breakCellSeparateLine="0"><hh:slash type="NONE" Crooked="0" isCounter="0"/><hh:backSlash type="NONE" Crooked="0" isCounter="0"/><hh:leftBorder type="${type}" width="${w}" color="${c}"/><hh:rightBorder type="${type}" width="${w}" color="${c}"/><hh:topBorder type="${type}" width="${w}" color="${c}"/><hh:bottomBorder type="${type}" width="${w}" color="${c}"/><hh:diagonal type="
|
|
3131
|
+
const xml = `<hh:borderFill id="${id}" threeD="0" shadow="0" centerLine="NONE" breakCellSeparateLine="0"><hh:slash type="NONE" Crooked="0" isCounter="0"/><hh:backSlash type="NONE" Crooked="0" isCounter="0"/><hh:leftBorder type="${type}" width="${w}" color="${c}"/><hh:rightBorder type="${type}" width="${w}" color="${c}"/><hh:topBorder type="${type}" width="${w}" color="${c}"/><hh:bottomBorder type="${type}" width="${w}" color="${c}"/><hh:diagonal type="NONE" width="0.12 mm" color="#000000"/>${fill}</hh:borderFill>`;
|
|
3132
|
+
ctx.borderFills.push({ id, xml });
|
|
3133
|
+
return id;
|
|
3134
|
+
}
|
|
3135
|
+
function addBorderFillPerSide(ctx, top, right, bottom, left, bgColor) {
|
|
3136
|
+
const id = ctx.borderFills.length + 1;
|
|
3137
|
+
const kindMap = {
|
|
3138
|
+
solid: "SOLID",
|
|
3139
|
+
dash: "DASH",
|
|
3140
|
+
dot: "DOT",
|
|
3141
|
+
double: "DOUBLE",
|
|
3142
|
+
none: "NONE"
|
|
3143
|
+
};
|
|
3144
|
+
function sideXml(tag, s) {
|
|
3145
|
+
const type = s ? kindMap[s.kind] ?? "SOLID" : "NONE";
|
|
3146
|
+
const w = s ? `${(s.pt * 0.3528).toFixed(2)} mm` : "0.12 mm";
|
|
3147
|
+
const c = s ? s.color.startsWith("#") ? s.color : `#${s.color}` : "#000000";
|
|
3148
|
+
return `<hh:${tag} type="${type}" width="${w}" color="${c}"/>`;
|
|
3149
|
+
}
|
|
3150
|
+
let fill = "";
|
|
3151
|
+
if (bgColor) {
|
|
3152
|
+
const bc = bgColor.startsWith("#") ? bgColor : `#${bgColor}`;
|
|
3153
|
+
fill = `<hc:fillBrush><hc:winBrush faceColor="${bc}" hatchColor="none" alpha="0"/></hc:fillBrush>`;
|
|
3154
|
+
}
|
|
3155
|
+
const xml = `<hh:borderFill id="${id}" threeD="0" shadow="0" centerLine="NONE" breakCellSeparateLine="0"><hh:slash type="NONE" Crooked="0" isCounter="0"/><hh:backSlash type="NONE" Crooked="0" isCounter="0"/>${sideXml("leftBorder", left)}${sideXml("rightBorder", right)}${sideXml("topBorder", top)}${sideXml("bottomBorder", bottom)}<hh:diagonal type="NONE" width="0.12 mm" color="#000000"/>${fill}</hh:borderFill>`;
|
|
2568
3156
|
ctx.borderFills.push({ id, xml });
|
|
2569
3157
|
return id;
|
|
2570
3158
|
}
|
|
@@ -2575,7 +3163,7 @@ var HwpxEncoder = class {
|
|
|
2575
3163
|
async encode(doc) {
|
|
2576
3164
|
try {
|
|
2577
3165
|
const sheet = doc.kids[0];
|
|
2578
|
-
const dims = sheet?.dims ?? A4;
|
|
3166
|
+
const dims = normalizeDims(sheet?.dims ?? A4);
|
|
2579
3167
|
const availableWidth = Metric.ptToHwp(dims.wPt) - Metric.ptToHwp(dims.ml) - Metric.ptToHwp(dims.mr);
|
|
2580
3168
|
const ctx = {
|
|
2581
3169
|
charPrs: [],
|
|
@@ -2588,7 +3176,9 @@ var HwpxEncoder = class {
|
|
|
2588
3176
|
nextElementId: 1e4,
|
|
2589
3177
|
availableWidth,
|
|
2590
3178
|
fonts: [],
|
|
2591
|
-
fontMap: /* @__PURE__ */ new Map()
|
|
3179
|
+
fontMap: /* @__PURE__ */ new Map(),
|
|
3180
|
+
imgMap: /* @__PURE__ */ new WeakMap(),
|
|
3181
|
+
nextZOrder: 0
|
|
2592
3182
|
};
|
|
2593
3183
|
addBorderFill(ctx, { kind: "none", pt: 0.1, color: "000000" });
|
|
2594
3184
|
addBorderFill(ctx, DEFAULT_STROKE);
|
|
@@ -2609,7 +3199,10 @@ var HwpxEncoder = class {
|
|
|
2609
3199
|
{ name: "Preview/PrvText.txt", data: TextKit.encode(previewText) },
|
|
2610
3200
|
{ name: "settings.xml", data: TextKit.encode(SETTINGS_XML) },
|
|
2611
3201
|
{ name: "META-INF/container.rdf", data: TextKit.encode(CONTAINER_RDF) },
|
|
2612
|
-
{
|
|
3202
|
+
{
|
|
3203
|
+
name: "Contents/content.hpf",
|
|
3204
|
+
data: TextKit.encode(contentHpf(ctx, doc.meta))
|
|
3205
|
+
},
|
|
2613
3206
|
{ name: "META-INF/container.xml", data: TextKit.encode(CONTAINER_XML) },
|
|
2614
3207
|
{ name: "META-INF/manifest.xml", data: TextKit.encode(MANIFEST_XML) }
|
|
2615
3208
|
];
|
|
@@ -2622,7 +3215,7 @@ var HwpxEncoder = class {
|
|
|
2622
3215
|
}
|
|
2623
3216
|
}
|
|
2624
3217
|
};
|
|
2625
|
-
var VERSION_XML = `<?xml version="1.0" encoding="UTF-8" standalone="yes" ?><hv:HCFVersion xmlns:hv="http://www.hancom.co.kr/hwpml/2011/version" tagetApplication="WORDPROCESSOR" major="5" minor="1" micro="0" buildNumber="1" os="
|
|
3218
|
+
var VERSION_XML = `<?xml version="1.0" encoding="UTF-8" standalone="yes" ?><hv:HCFVersion xmlns:hv="http://www.hancom.co.kr/hwpml/2011/version" tagetApplication="WORDPROCESSOR" major="5" minor="1" micro="0" buildNumber="1" os="1" xmlVersion="1.4" application="Hancom Office Hangul" appVersion="11, 0, 0, 0"/>`;
|
|
2626
3219
|
var CONTAINER_XML = `<?xml version="1.0" encoding="UTF-8" standalone="yes" ?><ocf:container xmlns:ocf="urn:oasis:names:tc:opendocument:xmlns:container" xmlns:hpf="http://www.hancom.co.kr/schema/2011/hpf"><ocf:rootfiles><ocf:rootfile full-path="Contents/content.hpf" media-type="application/hwpml-package+xml"/><ocf:rootfile full-path="Preview/PrvText.txt" media-type="text/plain"/><ocf:rootfile full-path="META-INF/container.rdf" media-type="application/rdf+xml"/></ocf:rootfiles></ocf:container>`;
|
|
2627
3220
|
var CONTAINER_RDF = `<?xml version="1.0" encoding="UTF-8" standalone="yes" ?><rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"><rdf:Description rdf:about=""><ns0:hasPart xmlns:ns0="http://www.hancom.co.kr/hwpml/2016/meta/pkg#" rdf:resource="Contents/header.xml"/></rdf:Description><rdf:Description rdf:about="Contents/header.xml"><rdf:type rdf:resource="http://www.hancom.co.kr/hwpml/2016/meta/pkg#HeaderFile"/></rdf:Description><rdf:Description rdf:about=""><ns0:hasPart xmlns:ns0="http://www.hancom.co.kr/hwpml/2016/meta/pkg#" rdf:resource="Contents/section0.xml"/></rdf:Description><rdf:Description rdf:about="Contents/section0.xml"><rdf:type rdf:resource="http://www.hancom.co.kr/hwpml/2016/meta/pkg#SectionFile"/></rdf:Description><rdf:Description rdf:about=""><rdf:type rdf:resource="http://www.hancom.co.kr/hwpml/2016/meta/pkg#Document"/></rdf:Description></rdf:RDF>`;
|
|
2628
3221
|
var MANIFEST_XML = `<?xml version="1.0" encoding="UTF-8" standalone="yes" ?><odf:manifest xmlns:odf="urn:oasis:names:tc:opendocument:xmlns:manifest:1.0"/>`;
|
|
@@ -2636,11 +3229,19 @@ function contentHpf(ctx, meta) {
|
|
|
2636
3229
|
const ct = ext === "png" ? "image/png" : ext === "jpg" || ext === "jpeg" ? "image/jpeg" : ext === "gif" ? "image/gif" : "image/bmp";
|
|
2637
3230
|
items += `<opf:item id="${bin.id}" href="BinData/${bin.name}" media-type="${ct}" isEmbeded="1"/>`;
|
|
2638
3231
|
}
|
|
2639
|
-
return `<?xml version="1.0" encoding="UTF-8" standalone="yes" ?><opf:package ${NS} version="" unique-identifier="" id=""><opf:metadata><opf:title>${title}</opf:title><opf:language>ko</opf:language><opf:meta name="creator" content="text"/><opf:meta name="subject" content="text"/><opf:meta name="description" content="text"/><opf:meta name="CreatedDate" content="text">${now}</opf:meta><opf:meta name="ModifiedDate" content="text">${now}</opf:meta><opf:meta name="keyword" content="text"/></opf:metadata><opf:manifest>${items}</opf:manifest><opf:spine><opf:itemref idref="header" linear="
|
|
3232
|
+
return `<?xml version="1.0" encoding="UTF-8" standalone="yes" ?><opf:package ${NS} version="" unique-identifier="" id=""><opf:metadata><opf:title>${title}</opf:title><opf:language>ko</opf:language><opf:meta name="creator" content="text"/><opf:meta name="subject" content="text"/><opf:meta name="description" content="text"/><opf:meta name="CreatedDate" content="text">${now}</opf:meta><opf:meta name="ModifiedDate" content="text">${now}</opf:meta><opf:meta name="keyword" content="text"/></opf:metadata><opf:manifest>${items}</opf:manifest><opf:spine><opf:itemref idref="header" linear="yes"/><opf:itemref idref="section0" linear="yes"/></opf:spine></opf:package>`;
|
|
2640
3233
|
}
|
|
2641
3234
|
function headerXml(dims, meta, ctx) {
|
|
2642
3235
|
const fontCount = ctx.fonts.length || 1;
|
|
2643
|
-
const langs = [
|
|
3236
|
+
const langs = [
|
|
3237
|
+
"HANGUL",
|
|
3238
|
+
"LATIN",
|
|
3239
|
+
"HANJA",
|
|
3240
|
+
"JAPANESE",
|
|
3241
|
+
"OTHER",
|
|
3242
|
+
"SYMBOL",
|
|
3243
|
+
"USER"
|
|
3244
|
+
];
|
|
2644
3245
|
let fontFaces = `<hh:fontfaces itemCnt="${langs.length}">`;
|
|
2645
3246
|
for (const lang of langs) {
|
|
2646
3247
|
fontFaces += `<hh:fontface lang="${lang}" fontCnt="${fontCount}">`;
|
|
@@ -2662,14 +3263,15 @@ function headerXml(dims, meta, ctx) {
|
|
|
2662
3263
|
}
|
|
2663
3264
|
let paraPrXml = "";
|
|
2664
3265
|
for (const pp of ctx.paraPrs) {
|
|
2665
|
-
|
|
3266
|
+
const marginSwitch = `<hp:switch><hp:case hp:required-namespace="http://www.hancom.co.kr/hwpml/2016/HwpUnitChar"><hh:margin><hc:intent value="${pp.intentHwp}" unit="HWPUNIT"/><hc:left value="0" unit="HWPUNIT"/><hc:right value="0" unit="HWPUNIT"/><hc:prev value="${pp.prevHwp}" unit="HWPUNIT"/><hc:next value="${pp.nextHwp}" unit="HWPUNIT"/></hh:margin><hh:lineSpacing type="PERCENT" value="${pp.lineSpacing}" unit="HWPUNIT"/></hp:case><hp:default><hh:margin><hc:intent value="${pp.intentHwp}" unit="HWPUNIT"/><hc:left value="0" unit="HWPUNIT"/><hc:right value="0" unit="HWPUNIT"/><hc:prev value="${pp.prevHwp}" unit="HWPUNIT"/><hc:next value="${pp.nextHwp}" unit="HWPUNIT"/></hh:margin><hh:lineSpacing type="PERCENT" value="${pp.lineSpacing}" unit="HWPUNIT"/></hp:default></hp:switch>`;
|
|
3267
|
+
paraPrXml += `<hh:paraPr id="${pp.id}" tabPrIDRef="0" condense="0" fontLineHeight="0" snapToGrid="0" suppressLineNumbers="0" checked="0"><hh:align horizontal="${pp.align}" vertical="BASELINE"/><hh:heading type="NONE" idRef="0" level="0"/><hh:breakSetting breakLatinWord="KEEP_WORD" breakNonLatinWord="BREAK_WORD" widowOrphan="0" keepWithNext="0" keepLines="0" pageBreakBefore="0" lineWrap="BREAK"/><hh:autoSpacing eAsianEng="0" eAsianNum="0"/>${marginSwitch}<hh:border borderFillIDRef="1" offsetLeft="0" offsetRight="0" offsetTop="0" offsetBottom="0" connect="0" ignoreMargin="0"/></hh:paraPr>`;
|
|
2666
3268
|
}
|
|
2667
3269
|
const borderFillXml = ctx.borderFills.map((bf) => bf.xml).join("");
|
|
2668
|
-
return `<?xml version="1.0" encoding="UTF-8" standalone="yes" ?><hh:head ${NS} version="1.
|
|
3270
|
+
return `<?xml version="1.0" encoding="UTF-8" standalone="yes" ?><hh:head ${NS} version="1.4" secCnt="1"><hh:beginNum page="1" footnote="1" endnote="1" pic="1" tbl="1" equation="1"/><hh:refList>${fontFaces}<hh:borderFills itemCnt="${ctx.borderFills.length}">${borderFillXml}</hh:borderFills><hh:charProperties itemCnt="${ctx.charPrs.length}">${charPrXml}</hh:charProperties><hh:paraProperties itemCnt="${ctx.paraPrs.length}">${paraPrXml}</hh:paraProperties><hh:tabProperties itemCnt="1"><hh:tabPr id="0" autoTabLeft="0" autoTabRight="0"/></hh:tabProperties><hh:styles itemCnt="1"><hh:style id="0" type="PARA" name="\uBC14\uD0D5\uAE00" engName="Normal" paraPrIDRef="0" charPrIDRef="0" nextStyleIDRef="0" langID="1042" lockForm="0"/></hh:styles></hh:refList><hh:compatibleDocument targetProgram="HWP201X"><hh:layoutCompatibility/></hh:compatibleDocument><hh:docOption><hh:linkinfo path="" pageInherit="1" footnoteInherit="0"/></hh:docOption><hh:trackchageConfig flags="56"/></hh:head>`;
|
|
2669
3271
|
}
|
|
2670
3272
|
function sectionXml(sheet, dims, ctx) {
|
|
2671
3273
|
const kids = sheet?.kids ?? [];
|
|
2672
|
-
const secPr = `<hp:secPr id="" textDirection="HORIZONTAL" spaceColumns="1134" tabStop="8000" outlineShapeIDRef="0" memoShapeIDRef="0" textVerticalWidthHead="0" masterPageCnt="0"><hp:grid lineGrid="0" charGrid="0" wonggojiFormat="0"/><hp:startNum pageStartsOn="BOTH" page="0" pic="0" tbl="0" equation="0"/><hp:visibility hideFirstHeader="0" hideFirstFooter="0" hideFirstMasterPage="0" border="SHOW_ALL" fill="SHOW_ALL" hideFirstPageNum="0" hideFirstEmptyLine="0" showLineNumber="0"/><hp:lineNumberShape restartType="0" countBy="0" distance="0" startNumber="0"/><hp:pagePr landscape="${dims.orient === "landscape" ? "NARROWLY" : "WIDELY"}" width="${Metric.ptToHwp(dims.wPt)}" height="${Metric.ptToHwp(dims.hPt)}" gutterType="LEFT_ONLY"><hp:margin header="2834" footer="2834" gutter="0" left="${Metric.ptToHwp(dims.ml)}" right="${Metric.ptToHwp(dims.mr)}" top="${Metric.ptToHwp(dims.mt)}" bottom="${Metric.ptToHwp(dims.mb)}"/></hp:pagePr><hp:footNotePr><hp:autoNumFormat type="DIGIT" userChar="" prefixChar="" suffixChar=")" supscript="0"/><hp:noteLine length="-1" type="SOLID" width="0.12 mm" color="#000000"/><hp:noteSpacing betweenNotes="284" belowLine="568" aboveLine="852"/><hp:numbering type="CONTINUOUS" newNum="1"/><hp:placement place="EACH_COLUMN" beneathText="0"/></hp:footNotePr><hp:endNotePr><hp:autoNumFormat type="DIGIT" userChar="" prefixChar="" suffixChar=")" supscript="0"/><hp:noteLine length="0" type="NONE" width="0.12 mm" color="#000000"/><hp:noteSpacing betweenNotes="0" belowLine="576" aboveLine="864"/><hp:numbering type="CONTINUOUS" newNum="1"/><hp:placement place="END_OF_DOCUMENT" beneathText="0"/></hp:endNotePr><hp:pageBorderFill type="BOTH" borderFillIDRef="1" textBorder="PAPER" headerInside="0" footerInside="0" fillArea="PAPER"><hp:offset left="1417" right="1417" top="1417" bottom="1417"/></hp:pageBorderFill><hp:pageBorderFill type="EVEN" borderFillIDRef="1" textBorder="PAPER" headerInside="0" footerInside="0" fillArea="PAPER"><hp:offset left="1417" right="1417" top="1417" bottom="1417"/></hp:pageBorderFill><hp:pageBorderFill type="ODD" borderFillIDRef="1" textBorder="PAPER" headerInside="0" footerInside="0" fillArea="PAPER"><hp:offset left="1417" right="1417" top="1417" bottom="1417"/></hp:pageBorderFill></hp:secPr><hp:ctrl><hp:colPr id="" type="NEWSPAPER" layout="LEFT" colCount="1" sameSz="1" sameGap="0"/></hp:ctrl>`;
|
|
3274
|
+
const secPr = `<hp:secPr id="" textDirection="HORIZONTAL" spaceColumns="1134" tabStop="8000" tabStopVal="4000" tabStopUnit="HWPUNIT" outlineShapeIDRef="0" memoShapeIDRef="0" textVerticalWidthHead="0" masterPageCnt="0"><hp:grid lineGrid="0" charGrid="0" wonggojiFormat="0"/><hp:startNum pageStartsOn="BOTH" page="0" pic="0" tbl="0" equation="0"/><hp:visibility hideFirstHeader="0" hideFirstFooter="0" hideFirstMasterPage="0" border="SHOW_ALL" fill="SHOW_ALL" hideFirstPageNum="0" hideFirstEmptyLine="0" showLineNumber="0"/><hp:lineNumberShape restartType="0" countBy="0" distance="0" startNumber="0"/><hp:pagePr landscape="${dims.orient === "landscape" ? "NARROWLY" : "WIDELY"}" width="${Metric.ptToHwp(dims.wPt)}" height="${Metric.ptToHwp(dims.hPt)}" gutterType="LEFT_ONLY"><hp:margin header="2834" footer="2834" gutter="0" left="${Metric.ptToHwp(dims.ml)}" right="${Metric.ptToHwp(dims.mr)}" top="${Metric.ptToHwp(dims.mt)}" bottom="${Metric.ptToHwp(dims.mb)}"/></hp:pagePr><hp:footNotePr><hp:autoNumFormat type="DIGIT" userChar="" prefixChar="" suffixChar=")" supscript="0"/><hp:noteLine length="-1" type="SOLID" width="0.12 mm" color="#000000"/><hp:noteSpacing betweenNotes="284" belowLine="568" aboveLine="852"/><hp:numbering type="CONTINUOUS" newNum="1"/><hp:placement place="EACH_COLUMN" beneathText="0"/></hp:footNotePr><hp:endNotePr><hp:autoNumFormat type="DIGIT" userChar="" prefixChar="" suffixChar=")" supscript="0"/><hp:noteLine length="0" type="NONE" width="0.12 mm" color="#000000"/><hp:noteSpacing betweenNotes="0" belowLine="576" aboveLine="864"/><hp:numbering type="CONTINUOUS" newNum="1"/><hp:placement place="END_OF_DOCUMENT" beneathText="0"/></hp:endNotePr><hp:pageBorderFill type="BOTH" borderFillIDRef="1" textBorder="PAPER" headerInside="0" footerInside="0" fillArea="PAPER"><hp:offset left="1417" right="1417" top="1417" bottom="1417"/></hp:pageBorderFill><hp:pageBorderFill type="EVEN" borderFillIDRef="1" textBorder="PAPER" headerInside="0" footerInside="0" fillArea="PAPER"><hp:offset left="1417" right="1417" top="1417" bottom="1417"/></hp:pageBorderFill><hp:pageBorderFill type="ODD" borderFillIDRef="1" textBorder="PAPER" headerInside="0" footerInside="0" fillArea="PAPER"><hp:offset left="1417" right="1417" top="1417" bottom="1417"/></hp:pageBorderFill></hp:secPr><hp:ctrl><hp:colPr id="" type="NEWSPAPER" layout="LEFT" colCount="1" sameSz="1" sameGap="0"/></hp:ctrl>`;
|
|
2673
3275
|
let contentXml = "";
|
|
2674
3276
|
let isFirst = true;
|
|
2675
3277
|
const defaultLineseg = `<hp:linesegarray><hp:lineseg textpos="0" vertpos="0" vertsize="1000" textheight="1000" baseline="850" spacing="600" horzpos="0" horzsize="${ctx.availableWidth}" flags="393216"/></hp:linesegarray>`;
|
|
@@ -2680,17 +3282,38 @@ function sectionXml(sheet, dims, ctx) {
|
|
|
2680
3282
|
} else if (kid.tag === "grid") {
|
|
2681
3283
|
const gridXml = encodeGrid(kid, ctx);
|
|
2682
3284
|
const prefix = isFirst ? secPr : "";
|
|
2683
|
-
|
|
3285
|
+
const runsXml = prefix ? `<hp:run charPrIDRef="0">${prefix}</hp:run><hp:run charPrIDRef="0">${gridXml}<hp:t></hp:t></hp:run>` : `<hp:run charPrIDRef="0">${gridXml}<hp:t></hp:t></hp:run>`;
|
|
3286
|
+
contentXml += `<hp:p id="0" paraPrIDRef="0" styleIDRef="0" pageBreak="0" columnBreak="0" merged="0">${runsXml}${defaultLineseg}</hp:p>`;
|
|
2684
3287
|
isFirst = false;
|
|
2685
3288
|
}
|
|
2686
3289
|
}
|
|
2687
3290
|
if (contentXml === "") {
|
|
2688
|
-
contentXml = `<hp:p id="
|
|
3291
|
+
contentXml = `<hp:p id="0" paraPrIDRef="0" styleIDRef="0" pageBreak="0" columnBreak="0" merged="0"><hp:run charPrIDRef="0">${secPr}<hp:t></hp:t></hp:run>${defaultLineseg}</hp:p>`;
|
|
2689
3292
|
}
|
|
2690
3293
|
return `<?xml version="1.0" encoding="UTF-8" standalone="yes" ?><hs:sec ${NS}>${contentXml}</hs:sec>`;
|
|
2691
3294
|
}
|
|
2692
|
-
function
|
|
3295
|
+
function estimateCellHeight(cell, ctx) {
|
|
3296
|
+
const topPad = 141;
|
|
3297
|
+
const botPad = 141;
|
|
3298
|
+
let contentHeight = 0;
|
|
3299
|
+
for (const para of cell.kids) {
|
|
3300
|
+
const fontSize = getFontSizeForPara(para, ctx);
|
|
3301
|
+
const paraPrId = ctx.paraPrMap.get(paraPrKey(para.props));
|
|
3302
|
+
const paraPr = paraPrId !== void 0 ? ctx.paraPrs[paraPrId] : null;
|
|
3303
|
+
const lineSpacing = paraPr ? paraPr.lineSpacing : 160;
|
|
3304
|
+
const spaceBefore = paraPr ? paraPr.prevHwp : 0;
|
|
3305
|
+
const spaceAfter = paraPr ? paraPr.nextHwp : 0;
|
|
3306
|
+
const lineHeight = Math.round(fontSize * lineSpacing / 100);
|
|
3307
|
+
contentHeight += lineHeight + spaceBefore + spaceAfter;
|
|
3308
|
+
}
|
|
3309
|
+
if (contentHeight === 0) contentHeight = Math.round(1e3 * 1.6);
|
|
3310
|
+
return contentHeight + topPad + botPad;
|
|
3311
|
+
}
|
|
3312
|
+
function encodePara(para, ctx, secPr = "", availWidth) {
|
|
2693
3313
|
const paraPrId = registerParaPr(para.props, ctx);
|
|
3314
|
+
const hasPageBreak = para.kids.some(
|
|
3315
|
+
(k) => k.tag === "span" && k.kids.some((c) => c.tag === "pb")
|
|
3316
|
+
);
|
|
2694
3317
|
let runs = "";
|
|
2695
3318
|
for (const kid of para.kids) {
|
|
2696
3319
|
if (kid.tag === "span") {
|
|
@@ -2709,12 +3332,15 @@ function encodePara(para, ctx, secPr = "") {
|
|
|
2709
3332
|
}
|
|
2710
3333
|
}
|
|
2711
3334
|
const fontSize = getFontSizeForPara(para, ctx);
|
|
3335
|
+
const paraPr = ctx.paraPrs[paraPrId];
|
|
3336
|
+
const lineSpacing = paraPr?.lineSpacing ?? 160;
|
|
2712
3337
|
const vertsize = fontSize;
|
|
2713
3338
|
const textheight = fontSize;
|
|
2714
3339
|
const baseline = Math.round(fontSize * 0.85);
|
|
2715
|
-
const spacing = Math.round(fontSize *
|
|
2716
|
-
const
|
|
2717
|
-
|
|
3340
|
+
const spacing = Math.max(0, Math.round(fontSize * (lineSpacing / 100 - 1)));
|
|
3341
|
+
const horzsize = availWidth ?? ctx.availableWidth;
|
|
3342
|
+
const linesegarray = `<hp:linesegarray><hp:lineseg textpos="0" vertpos="0" vertsize="${vertsize}" textheight="${textheight}" baseline="${baseline}" spacing="${spacing}" horzpos="0" horzsize="${horzsize}" flags="393216"/></hp:linesegarray>`;
|
|
3343
|
+
return `<hp:p id="0" paraPrIDRef="${paraPrId}" styleIDRef="0" pageBreak="${hasPageBreak ? "1" : "0"}" columnBreak="0" merged="0">${runs}${linesegarray}</hp:p>`;
|
|
2718
3344
|
}
|
|
2719
3345
|
function getFontSizeForPara(para, ctx) {
|
|
2720
3346
|
for (const kid of para.kids) {
|
|
@@ -2747,21 +3373,85 @@ function encodeRun(span, ctx) {
|
|
|
2747
3373
|
}
|
|
2748
3374
|
return `<hp:run charPrIDRef="${charPrId}">${parts.join("")}</hp:run>`;
|
|
2749
3375
|
}
|
|
3376
|
+
var WRAP_HWPX = {
|
|
3377
|
+
inline: "TOP_AND_BOTTOM",
|
|
3378
|
+
square: "SQUARE",
|
|
3379
|
+
tight: "BOTH_SIDES",
|
|
3380
|
+
through: "BOTH_SIDES",
|
|
3381
|
+
none: "FRONT_TEXT",
|
|
3382
|
+
behind: "BEHIND_TEXT",
|
|
3383
|
+
front: "FRONT_TEXT"
|
|
3384
|
+
};
|
|
3385
|
+
var TEXT_FLOW_HWPX = {
|
|
3386
|
+
inline: "BOTH_SIDES",
|
|
3387
|
+
square: "LARGEST_ONLY",
|
|
3388
|
+
tight: "BOTH_SIDES",
|
|
3389
|
+
through: "BOTH_SIDES",
|
|
3390
|
+
none: "BOTH_SIDES",
|
|
3391
|
+
behind: "BOTH_SIDES",
|
|
3392
|
+
front: "BOTH_SIDES"
|
|
3393
|
+
};
|
|
3394
|
+
var HORZ_RELTO_HWPX = {
|
|
3395
|
+
para: "PARA",
|
|
3396
|
+
margin: "MARGIN",
|
|
3397
|
+
page: "PAPER",
|
|
3398
|
+
column: "COLUMN"
|
|
3399
|
+
};
|
|
3400
|
+
var VERT_RELTO_HWPX = {
|
|
3401
|
+
para: "PARA",
|
|
3402
|
+
margin: "MARGIN",
|
|
3403
|
+
page: "PAPER",
|
|
3404
|
+
line: "LINE"
|
|
3405
|
+
};
|
|
2750
3406
|
function encodeImage(img, ctx) {
|
|
2751
|
-
const binId = img
|
|
3407
|
+
const binId = ctx.imgMap.get(img);
|
|
2752
3408
|
if (!binId) return "";
|
|
2753
3409
|
const charPrId = registerCharPr({}, ctx);
|
|
2754
3410
|
const w = Metric.ptToHwp(img.w);
|
|
2755
3411
|
const h = Metric.ptToHwp(img.h);
|
|
2756
|
-
|
|
3412
|
+
const cx = Math.round(w / 2);
|
|
3413
|
+
const cy = Math.round(h / 2);
|
|
3414
|
+
const layout = img.layout;
|
|
3415
|
+
const isInline = !layout || layout.wrap === "inline";
|
|
3416
|
+
const textWrap = layout ? WRAP_HWPX[layout.wrap] ?? "TOP_AND_BOTTOM" : "TOP_AND_BOTTOM";
|
|
3417
|
+
const textFlow = layout ? TEXT_FLOW_HWPX[layout.wrap] ?? "BOTH_SIDES" : "BOTH_SIDES";
|
|
3418
|
+
const treatAsChar = isInline ? "1" : "0";
|
|
3419
|
+
const flowWithText = "1";
|
|
3420
|
+
const allowOverlap = !isInline && layout?.wrap !== "behind" && layout?.wrap !== "front" ? "1" : "0";
|
|
3421
|
+
const horzRelTo = layout?.horzRelTo ? HORZ_RELTO_HWPX[layout.horzRelTo] ?? "PARA" : "PARA";
|
|
3422
|
+
const vertRelTo = layout?.vertRelTo ? VERT_RELTO_HWPX[layout.vertRelTo] ?? "PARA" : "PARA";
|
|
3423
|
+
const ALIGN_H = { left: "LEFT", center: "CENTER", right: "RIGHT" };
|
|
3424
|
+
const ALIGN_V = { top: "TOP", center: "CENTER", bottom: "BOTTOM" };
|
|
3425
|
+
const horzAlign = layout?.horzAlign ? ALIGN_H[layout.horzAlign] ?? "LEFT" : "LEFT";
|
|
3426
|
+
const vertAlign = layout?.vertAlign ? ALIGN_V[layout.vertAlign] ?? "TOP" : "TOP";
|
|
3427
|
+
const horzOffset = layout?.xPt != null ? Metric.ptToHwp(layout.xPt) : 0;
|
|
3428
|
+
const vertOffset = layout?.yPt != null ? Metric.ptToHwp(layout.yPt) : 0;
|
|
3429
|
+
const zOrder = ctx.nextZOrder++;
|
|
3430
|
+
return `<hp:run charPrIDRef="${charPrId}"><hp:pic id="${ctx.nextElementId++}" zOrder="${zOrder}" numberingType="PICTURE" textWrap="${textWrap}" textFlow="${textFlow}" lock="0" dropcapstyle="None" href="" groupLevel="0" instid="0" reverse="0"><hp:offset x="0" y="0"/><hp:orgSz width="${w}" height="${h}"/><hp:curSz width="${w}" height="${h}"/><hp:flip horizontal="0" vertical="0"/><hp:rotationInfo angle="0" centerX="${cx}" centerY="${cy}" rotateimage="1"/><hp:renderingInfo><hc:transMatrix e1="1" e2="0" e3="0" e4="0" e5="1" e6="0"/><hc:scaMatrix e1="1" e2="0" e3="0" e4="0" e5="1" e6="0"/><hc:rotMatrix e1="1" e2="0" e3="0" e4="0" e5="1" e6="0"/></hp:renderingInfo><hp:imgRect><hc:pt0 x="0" y="0"/><hc:pt1 x="${w}" y="0"/><hc:pt2 x="${w}" y="${h}"/><hc:pt3 x="0" y="${h}"/></hp:imgRect><hp:imgClip left="0" right="0" top="0" bottom="0"/><hp:inMargin left="0" right="0" top="0" bottom="0"/><hp:imgDim dimwidth="${w}" dimheight="${h}"/><hc:img binaryItemIDRef="${binId}" bright="0" contrast="0" effect="REAL_PIC" alpha="0"/><hp:effects/><hp:sz width="${w}" widthRelTo="ABSOLUTE" height="${h}" heightRelTo="ABSOLUTE" protect="0"/><hp:pos treatAsChar="${treatAsChar}" affectLSpacing="0" flowWithText="${flowWithText}" allowOverlap="${allowOverlap}" holdAnchorAndSO="0" vertRelTo="${vertRelTo}" horzRelTo="${horzRelTo}" vertAlign="${vertAlign}" horzAlign="${horzAlign}" vertOffset="${vertOffset}" horzOffset="${horzOffset}"/><hp:outMargin left="0" right="0" top="0" bottom="0"/></hp:pic><hp:t></hp:t></hp:run>`;
|
|
2757
3431
|
}
|
|
2758
3432
|
function encodeGrid(grid, ctx) {
|
|
2759
3433
|
const rowCount = grid.kids.length;
|
|
3434
|
+
const rowOccupancy = Array.from(
|
|
3435
|
+
{ length: rowCount },
|
|
3436
|
+
() => /* @__PURE__ */ new Set()
|
|
3437
|
+
);
|
|
2760
3438
|
let colCount = 0;
|
|
2761
|
-
for (
|
|
2762
|
-
|
|
2763
|
-
|
|
2764
|
-
|
|
3439
|
+
for (let ri = 0; ri < grid.kids.length; ri++) {
|
|
3440
|
+
const row = grid.kids[ri];
|
|
3441
|
+
let ci = 0;
|
|
3442
|
+
for (const cell of row.kids) {
|
|
3443
|
+
while (rowOccupancy[ri].has(ci)) ci++;
|
|
3444
|
+
if (cell.rs > 1) {
|
|
3445
|
+
for (let r = ri + 1; r < ri + cell.rs && r < rowCount; r++) {
|
|
3446
|
+
for (let c = ci; c < ci + cell.cs; c++) {
|
|
3447
|
+
rowOccupancy[r].add(c);
|
|
3448
|
+
}
|
|
3449
|
+
}
|
|
3450
|
+
}
|
|
3451
|
+
ci += cell.cs;
|
|
3452
|
+
}
|
|
3453
|
+
while (rowOccupancy[ri].has(ci)) ci++;
|
|
3454
|
+
if (ci > colCount) colCount = ci;
|
|
2765
3455
|
}
|
|
2766
3456
|
if (colCount === 0) colCount = grid.kids[0]?.kids.length ?? 1;
|
|
2767
3457
|
const totalWidth = ctx.availableWidth;
|
|
@@ -2774,7 +3464,8 @@ function encodeGrid(grid, ctx) {
|
|
|
2774
3464
|
const remaining = Math.max(0, Metric.hwpToPt(totalWidth) - knownTotal);
|
|
2775
3465
|
const zeroFill = zeroCount > 0 ? remaining / zeroCount : 0;
|
|
2776
3466
|
for (let i = 0; i < srcPt.length; i++) {
|
|
2777
|
-
if (srcPt[i] <= 0)
|
|
3467
|
+
if (srcPt[i] <= 0)
|
|
3468
|
+
srcPt[i] = zeroFill > 0 ? zeroFill : Metric.hwpToPt(defaultColW);
|
|
2778
3469
|
}
|
|
2779
3470
|
for (const wPt of srcPt) colWidths.push(Metric.ptToHwp(wPt));
|
|
2780
3471
|
} else {
|
|
@@ -2783,32 +3474,69 @@ function encodeGrid(grid, ctx) {
|
|
|
2783
3474
|
const rawTotal = colWidths.reduce((s, w) => s + w, 0);
|
|
2784
3475
|
if (rawTotal > totalWidth * 1.05) {
|
|
2785
3476
|
const scale = totalWidth / rawTotal;
|
|
2786
|
-
for (let i = 0; i < colWidths.length; i++)
|
|
3477
|
+
for (let i = 0; i < colWidths.length; i++)
|
|
3478
|
+
colWidths[i] = Math.round(colWidths[i] * scale);
|
|
2787
3479
|
}
|
|
2788
3480
|
const actualTotal = colWidths.reduce((s, w) => s + w, 0);
|
|
2789
3481
|
const tblBfId = grid.props.defaultStroke ? addBorderFill(ctx, grid.props.defaultStroke) : 2;
|
|
3482
|
+
const rowHeights = [];
|
|
3483
|
+
for (const row of grid.kids) {
|
|
3484
|
+
if (row.heightPt != null && row.heightPt > 0) {
|
|
3485
|
+
rowHeights.push(Metric.ptToHwp(row.heightPt));
|
|
3486
|
+
} else {
|
|
3487
|
+
let maxH = 0;
|
|
3488
|
+
for (const cell of row.kids) {
|
|
3489
|
+
const h = estimateCellHeight(cell, ctx);
|
|
3490
|
+
if (h > maxH) maxH = h;
|
|
3491
|
+
}
|
|
3492
|
+
rowHeights.push(maxH);
|
|
3493
|
+
}
|
|
3494
|
+
}
|
|
3495
|
+
const totalTableHeight = rowHeights.reduce((s, h) => s + h, 0);
|
|
2790
3496
|
let rowsXml = "";
|
|
2791
3497
|
for (let ri = 0; ri < grid.kids.length; ri++) {
|
|
2792
3498
|
const row = grid.kids[ri];
|
|
3499
|
+
const rowH = rowHeights[ri];
|
|
2793
3500
|
let cellsXml = "";
|
|
2794
3501
|
let colIdx = 0;
|
|
2795
3502
|
for (let ci = 0; ci < row.kids.length; ci++) {
|
|
2796
3503
|
const cell = row.kids[ci];
|
|
3504
|
+
while (rowOccupancy[ri].has(colIdx)) colIdx++;
|
|
2797
3505
|
let cellBfId = tblBfId;
|
|
2798
|
-
|
|
2799
|
-
|
|
3506
|
+
const cp = cell.props;
|
|
3507
|
+
const hasPerSideBorder = cp.top || cp.bot || cp.left || cp.right;
|
|
3508
|
+
if (hasPerSideBorder || cp.bg) {
|
|
3509
|
+
if (hasPerSideBorder) {
|
|
3510
|
+
const defStroke = grid.props.defaultStroke ?? DEFAULT_STROKE;
|
|
3511
|
+
cellBfId = addBorderFillPerSide(
|
|
3512
|
+
ctx,
|
|
3513
|
+
cp.top ?? defStroke,
|
|
3514
|
+
cp.right ?? defStroke,
|
|
3515
|
+
cp.bot ?? defStroke,
|
|
3516
|
+
cp.left ?? defStroke,
|
|
3517
|
+
cp.bg
|
|
3518
|
+
);
|
|
3519
|
+
} else {
|
|
3520
|
+
cellBfId = addBorderFill(
|
|
3521
|
+
ctx,
|
|
3522
|
+
grid.props.defaultStroke ?? DEFAULT_STROKE,
|
|
3523
|
+
cp.bg
|
|
3524
|
+
);
|
|
3525
|
+
}
|
|
2800
3526
|
}
|
|
2801
|
-
const parasXml = cell.kids.map((p) => encodePara(p, ctx)).join("");
|
|
2802
3527
|
let cellW = 0;
|
|
2803
|
-
for (let sc = colIdx; sc < colIdx + cell.cs && sc < colWidths.length; sc++)
|
|
3528
|
+
for (let sc = colIdx; sc < colIdx + cell.cs && sc < colWidths.length; sc++)
|
|
3529
|
+
cellW += colWidths[sc];
|
|
2804
3530
|
if (cellW === 0) cellW = defaultColW * cell.cs;
|
|
2805
|
-
|
|
3531
|
+
const cellInnerW = Math.max(cellW - 282, 100);
|
|
3532
|
+
const parasXml = cell.kids.map((p) => encodePara(p, ctx, "", cellInnerW)).join("");
|
|
3533
|
+
cellsXml += `<hp:tc name="" header="0" hasMargin="1" protect="0" editable="0" dirty="0" borderFillIDRef="${cellBfId}"><hp:subList id="" textDirection="HORIZONTAL" lineWrap="BREAK" vertAlign="${cell.props.va === "mid" ? "CENTER" : cell.props.va === "bot" ? "BOTTOM" : "TOP"}" linkListIDRef="0" linkListNextIDRef="0" textWidth="0" textHeight="0" hasTextRef="0" hasNumRef="0">${parasXml}</hp:subList><hp:cellAddr colAddr="${colIdx}" rowAddr="${ri}"/><hp:cellSpan colSpan="${cell.cs}" rowSpan="${cell.rs}"/><hp:cellSz width="${cellW}" height="${rowH}"/><hp:cellMargin left="141" right="141" top="141" bottom="141"/></hp:tc>`;
|
|
2806
3534
|
colIdx += cell.cs;
|
|
2807
3535
|
}
|
|
2808
3536
|
rowsXml += `<hp:tr>${cellsXml}</hp:tr>`;
|
|
2809
3537
|
}
|
|
2810
3538
|
const headerRow = grid.props.headerRow ? ' repeatHeader="1"' : "";
|
|
2811
|
-
return `<hp:tbl id="${ctx.nextElementId++}" zOrder="0" numberingType="TABLE" textWrap="TOP_AND_BOTTOM" textFlow="BOTH_SIDES" lock="0" dropcapstyle="None" pageBreak="
|
|
3539
|
+
return `<hp:tbl id="${ctx.nextElementId++}" zOrder="0" numberingType="TABLE" textWrap="TOP_AND_BOTTOM" textFlow="BOTH_SIDES" lock="0" dropcapstyle="None" pageBreak="NONE"${headerRow} rowCnt="${rowCount}" colCnt="${colCount}" cellSpacing="0" borderFillIDRef="${tblBfId}" noAdjust="0"><hp:sz width="${actualTotal}" widthRelTo="ABSOLUTE" height="${totalTableHeight}" heightRelTo="ABSOLUTE" protect="0"/><hp:pos treatAsChar="1" affectLSpacing="0" flowWithText="1" allowOverlap="0" holdAnchorAndSO="0" vertRelTo="PARA" horzRelTo="PARA" vertAlign="TOP" horzAlign="LEFT" vertOffset="0" horzOffset="0"/><hp:outMargin left="138" right="138" top="138" bottom="138"/><hp:inMargin left="138" right="138" top="138" bottom="138"/>${rowsXml}</hp:tbl>`;
|
|
2812
3540
|
}
|
|
2813
3541
|
function extractPreviewText(sheet) {
|
|
2814
3542
|
if (!sheet) return "";
|
|
@@ -2816,17 +3544,21 @@ function extractPreviewText(sheet) {
|
|
|
2816
3544
|
for (const kid of sheet.kids) {
|
|
2817
3545
|
if (kid.tag === "para") {
|
|
2818
3546
|
const text = kid.kids.map((k) => {
|
|
2819
|
-
if (k.tag === "span")
|
|
3547
|
+
if (k.tag === "span")
|
|
3548
|
+
return k.kids.map((c) => c.tag === "txt" ? c.content : "").join("");
|
|
2820
3549
|
return "";
|
|
2821
3550
|
}).join("");
|
|
2822
3551
|
if (text) lines.push(text);
|
|
2823
3552
|
} else if (kid.tag === "grid") {
|
|
2824
3553
|
for (const row of kid.kids) {
|
|
2825
3554
|
const cells = row.kids.map(
|
|
2826
|
-
(cell) => cell.kids.map(
|
|
2827
|
-
|
|
2828
|
-
|
|
2829
|
-
|
|
3555
|
+
(cell) => cell.kids.map(
|
|
3556
|
+
(p) => p.kids.map((k) => {
|
|
3557
|
+
if (k.tag === "span")
|
|
3558
|
+
return k.kids.map((c) => c.tag === "txt" ? c.content : "").join("");
|
|
3559
|
+
return "";
|
|
3560
|
+
}).join("")
|
|
3561
|
+
).join("")
|
|
2830
3562
|
);
|
|
2831
3563
|
lines.push(cells.join(" "));
|
|
2832
3564
|
}
|
|
@@ -2847,10 +3579,10 @@ var DocxEncoder = class {
|
|
|
2847
3579
|
async encode(doc) {
|
|
2848
3580
|
try {
|
|
2849
3581
|
const sheet = doc.kids[0];
|
|
2850
|
-
const dims = sheet?.dims ?? A4;
|
|
3582
|
+
const dims = normalizeDims(sheet?.dims ?? A4);
|
|
2851
3583
|
const kids = sheet?.kids ?? [];
|
|
2852
3584
|
const images = [];
|
|
2853
|
-
const ctx = { images, nextId: 10, nextImgNum: 1, warns: [] };
|
|
3585
|
+
const ctx = { images, nextId: 10, nextImgNum: 1, warns: [], imgMap: /* @__PURE__ */ new WeakMap() };
|
|
2854
3586
|
collectImages(kids, ctx);
|
|
2855
3587
|
let headerParas = sheet?.header;
|
|
2856
3588
|
let footerParas = sheet?.footer;
|
|
@@ -2914,13 +3646,13 @@ function collectImagesFromPara(para, ctx) {
|
|
|
2914
3646
|
}
|
|
2915
3647
|
}
|
|
2916
3648
|
function registerImage2(img, ctx) {
|
|
2917
|
-
if (img
|
|
3649
|
+
if (ctx.imgMap.has(img)) return;
|
|
2918
3650
|
const ext = mimeToExt2(img.mime);
|
|
2919
3651
|
const name = `image${ctx.nextImgNum++}.${ext}`;
|
|
2920
3652
|
const rId = `rId${ctx.nextId++}`;
|
|
2921
3653
|
const data = TextKit.base64Decode(img.b64);
|
|
2922
3654
|
ctx.images.push({ rId, name, data, ext });
|
|
2923
|
-
img
|
|
3655
|
+
ctx.imgMap.set(img, rId);
|
|
2924
3656
|
}
|
|
2925
3657
|
function collectNumbering(kids) {
|
|
2926
3658
|
let hasBullet = false;
|
|
@@ -3115,13 +3847,26 @@ function encodeParaInner(para, ctx) {
|
|
|
3115
3847
|
const ilvl = para.props.listLv ?? 0;
|
|
3116
3848
|
numPr = `<w:pStyle w:val="ListParagraph"/><w:numPr><w:ilvl w:val="${ilvl}"/><w:numId w:val="${numId}"/></w:numPr>`;
|
|
3117
3849
|
}
|
|
3850
|
+
let spacingXml = "";
|
|
3851
|
+
const { spaceBefore, spaceAfter, lineHeight } = para.props;
|
|
3852
|
+
if (spaceBefore !== void 0 || spaceAfter !== void 0 || lineHeight !== void 0) {
|
|
3853
|
+
const parts = [];
|
|
3854
|
+
if (spaceBefore !== void 0) parts.push(`w:before="${Metric.ptToDxa(spaceBefore)}"`);
|
|
3855
|
+
if (spaceAfter !== void 0) parts.push(`w:after="${Metric.ptToDxa(spaceAfter)}"`);
|
|
3856
|
+
if (lineHeight !== void 0) parts.push(`w:line="${Math.round(lineHeight * 240)}" w:lineRule="auto"`);
|
|
3857
|
+
spacingXml = `<w:spacing ${parts.join(" ")}/>`;
|
|
3858
|
+
}
|
|
3859
|
+
let indentXml = "";
|
|
3860
|
+
if (para.props.indentPt !== void 0) {
|
|
3861
|
+
indentXml = `<w:ind w:left="${Metric.ptToDxa(para.props.indentPt)}"/>`;
|
|
3862
|
+
}
|
|
3118
3863
|
const runs = para.kids.map((k) => {
|
|
3119
3864
|
if (k.tag === "span") return encodeRun2(k, ctx);
|
|
3120
3865
|
if (k.tag === "img") return encodeImage2(k, ctx);
|
|
3121
3866
|
return "";
|
|
3122
3867
|
}).join("");
|
|
3123
3868
|
return ` <w:p>
|
|
3124
|
-
<w:pPr>${headStyle}${numPr}<w:jc w:val="${align === "justify" ? "both" : align}"/></w:pPr>
|
|
3869
|
+
<w:pPr>${headStyle}${numPr}${spacingXml}${indentXml}<w:jc w:val="${align === "justify" ? "both" : align}"/></w:pPr>
|
|
3125
3870
|
${runs}
|
|
3126
3871
|
</w:p>`;
|
|
3127
3872
|
}
|
|
@@ -3152,14 +3897,77 @@ function encodeRun2(span, _ctx) {
|
|
|
3152
3897
|
}
|
|
3153
3898
|
return parts.join("");
|
|
3154
3899
|
}
|
|
3155
|
-
function encodeImage2(img,
|
|
3156
|
-
const rId = img
|
|
3900
|
+
function encodeImage2(img, ctx) {
|
|
3901
|
+
const rId = ctx.imgMap.get(img);
|
|
3157
3902
|
if (!rId) return "";
|
|
3158
3903
|
const cx = Metric.ptToEmu(img.w);
|
|
3159
3904
|
const cy = Metric.ptToEmu(img.h);
|
|
3160
3905
|
const alt = esc2(img.alt ?? "");
|
|
3161
|
-
|
|
3906
|
+
const docPrId = ctx.nextId++;
|
|
3907
|
+
const graphic = `<a:graphic><a:graphicData uri="http://schemas.openxmlformats.org/drawingml/2006/picture"><pic:pic><pic:nvPicPr><pic:cNvPr id="0" name="Image"/><pic:cNvPicPr/></pic:nvPicPr><pic:blipFill><a:blip r:embed="${rId}"/><a:stretch><a:fillRect/></a:stretch></pic:blipFill><pic:spPr><a:xfrm><a:off x="0" y="0"/><a:ext cx="${cx}" cy="${cy}"/></a:xfrm><a:prstGeom prst="rect"><a:avLst/></a:prstGeom></pic:spPr></pic:pic></a:graphicData></a:graphic>`;
|
|
3908
|
+
const layout = img.layout;
|
|
3909
|
+
const isInline = !layout || layout.wrap === "inline";
|
|
3910
|
+
if (isInline) {
|
|
3911
|
+
return `<w:r><w:drawing><wp:inline distT="0" distB="0" distL="0" distR="0"><wp:extent cx="${cx}" cy="${cy}"/><wp:docPr id="${docPrId}" name="Image" descr="${alt}"/>${graphic}</wp:inline></w:drawing></w:r>`;
|
|
3912
|
+
}
|
|
3913
|
+
return `<w:r><w:drawing>${encodeAnchor(img, cx, cy, alt, docPrId, graphic, layout)}</w:drawing></w:r>`;
|
|
3914
|
+
}
|
|
3915
|
+
function encodeAnchor(_img, cx, cy, alt, docPrId, graphic, layout) {
|
|
3916
|
+
const distT = Metric.ptToEmu(layout.distT ?? 0);
|
|
3917
|
+
const distB = Metric.ptToEmu(layout.distB ?? 0);
|
|
3918
|
+
const distL = Metric.ptToEmu(layout.distL ?? 9144);
|
|
3919
|
+
const distR = Metric.ptToEmu(layout.distR ?? 9144);
|
|
3920
|
+
const behindDoc = layout.behindDoc || layout.wrap === "behind" ? "1" : "0";
|
|
3921
|
+
const relH = layout.zOrder ?? 251658240;
|
|
3922
|
+
const horzRelFrom = HORZ_RELTO_DOCX[layout.horzRelTo ?? "column"] ?? "column";
|
|
3923
|
+
let posH;
|
|
3924
|
+
if (layout.xPt != null) {
|
|
3925
|
+
posH = `<wp:positionH relativeFrom="${horzRelFrom}"><wp:posOffset>${Metric.ptToEmu(layout.xPt)}</wp:posOffset></wp:positionH>`;
|
|
3926
|
+
} else {
|
|
3927
|
+
const ha = HORZ_ALIGN_DOCX[layout.horzAlign ?? "left"] ?? "left";
|
|
3928
|
+
posH = `<wp:positionH relativeFrom="${horzRelFrom}"><wp:align>${ha}</wp:align></wp:positionH>`;
|
|
3929
|
+
}
|
|
3930
|
+
const vertRelFrom = VERT_RELTO_DOCX[layout.vertRelTo ?? "para"] ?? "paragraph";
|
|
3931
|
+
let posV;
|
|
3932
|
+
if (layout.yPt != null) {
|
|
3933
|
+
posV = `<wp:positionV relativeFrom="${vertRelFrom}"><wp:posOffset>${Metric.ptToEmu(layout.yPt)}</wp:posOffset></wp:positionV>`;
|
|
3934
|
+
} else {
|
|
3935
|
+
const va = VERT_ALIGN_DOCX[layout.vertAlign ?? "top"] ?? "top";
|
|
3936
|
+
posV = `<wp:positionV relativeFrom="${vertRelFrom}"><wp:align>${va}</wp:align></wp:positionV>`;
|
|
3937
|
+
}
|
|
3938
|
+
const wrapXml = WRAP_DOCX[layout.wrap] ?? '<wp:wrapSquare wrapText="bothSides"/>';
|
|
3939
|
+
return `<wp:anchor distT="${distT}" distB="${distB}" distL="${distL}" distR="${distR}" simplePos="0" relativeHeight="${relH}" behindDoc="${behindDoc}" locked="0" layoutInCell="1" allowOverlap="1"><wp:simplePos x="0" y="0"/>${posH}${posV}<wp:extent cx="${cx}" cy="${cy}"/><wp:effectExtent l="0" t="0" r="0" b="0"/>${wrapXml}<wp:docPr id="${docPrId}" name="Image" descr="${alt}"/>${graphic}</wp:anchor>`;
|
|
3162
3940
|
}
|
|
3941
|
+
var HORZ_RELTO_DOCX = {
|
|
3942
|
+
margin: "margin",
|
|
3943
|
+
column: "column",
|
|
3944
|
+
page: "page",
|
|
3945
|
+
para: "paragraph"
|
|
3946
|
+
};
|
|
3947
|
+
var VERT_RELTO_DOCX = {
|
|
3948
|
+
margin: "margin",
|
|
3949
|
+
line: "line",
|
|
3950
|
+
page: "page",
|
|
3951
|
+
para: "paragraph"
|
|
3952
|
+
};
|
|
3953
|
+
var HORZ_ALIGN_DOCX = {
|
|
3954
|
+
left: "left",
|
|
3955
|
+
center: "center",
|
|
3956
|
+
right: "right"
|
|
3957
|
+
};
|
|
3958
|
+
var VERT_ALIGN_DOCX = {
|
|
3959
|
+
top: "top",
|
|
3960
|
+
center: "center",
|
|
3961
|
+
bottom: "bottom"
|
|
3962
|
+
};
|
|
3963
|
+
var WRAP_DOCX = {
|
|
3964
|
+
square: '<wp:wrapSquare wrapText="bothSides"/>',
|
|
3965
|
+
tight: '<wp:wrapTight><wp:wrapPolygon edited="0"><wp:start x="0" y="0"/><wp:lineTo x="0" y="21600"/><wp:lineTo x="21600" y="21600"/><wp:lineTo x="21600" y="0"/><wp:lineTo x="0" y="0"/></wp:wrapPolygon></wp:wrapTight>',
|
|
3966
|
+
through: '<wp:wrapThrough wrapText="bothSides"><wp:wrapPolygon edited="0"><wp:start x="0" y="0"/><wp:lineTo x="0" y="21600"/><wp:lineTo x="21600" y="21600"/><wp:lineTo x="21600" y="0"/><wp:lineTo x="0" y="0"/></wp:wrapPolygon></wp:wrapThrough>',
|
|
3967
|
+
none: "<wp:wrapNone/>",
|
|
3968
|
+
behind: "<wp:wrapNone/>",
|
|
3969
|
+
front: "<wp:wrapNone/>"
|
|
3970
|
+
};
|
|
3163
3971
|
function encodeGrid2(grid, ctx, dims) {
|
|
3164
3972
|
const gp = grid.props;
|
|
3165
3973
|
const look = gp.look;
|
|
@@ -3171,129 +3979,115 @@ function encodeGrid2(grid, ctx, dims) {
|
|
|
3171
3979
|
const noVBand = look?.bandedCols ? "0" : "1";
|
|
3172
3980
|
const d = dims ?? A4;
|
|
3173
3981
|
const availDxa = Metric.ptToDxa(d.wPt - d.ml - d.mr);
|
|
3982
|
+
const tableMap = Array.from({ length: grid.kids.length }, () => []);
|
|
3174
3983
|
let colCount = 0;
|
|
3175
|
-
for (
|
|
3176
|
-
let
|
|
3177
|
-
for (const cell of
|
|
3178
|
-
|
|
3984
|
+
for (let ri = 0; ri < grid.kids.length; ri++) {
|
|
3985
|
+
let c = 0;
|
|
3986
|
+
for (const cell of grid.kids[ri].kids) {
|
|
3987
|
+
while (tableMap[ri][c]) c++;
|
|
3988
|
+
tableMap[ri][c] = { type: "real", cell, width: cell.cs };
|
|
3989
|
+
for (let rr = 0; rr < cell.rs; rr++) {
|
|
3990
|
+
for (let cc = 0; cc < cell.cs; cc++) {
|
|
3991
|
+
if (rr === 0 && cc === 0) continue;
|
|
3992
|
+
if (!tableMap[ri + rr]) tableMap[ri + rr] = [];
|
|
3993
|
+
if (rr > 0 && cc === 0) {
|
|
3994
|
+
tableMap[ri + rr][c + cc] = { type: "continue", width: cell.cs };
|
|
3995
|
+
} else {
|
|
3996
|
+
tableMap[ri + rr][c + cc] = { type: "empty" };
|
|
3997
|
+
}
|
|
3998
|
+
}
|
|
3999
|
+
}
|
|
4000
|
+
c += cell.cs;
|
|
4001
|
+
}
|
|
4002
|
+
if (c > colCount) colCount = c;
|
|
3179
4003
|
}
|
|
3180
4004
|
if (colCount === 0) colCount = grid.kids[0]?.kids.length ?? 1;
|
|
4005
|
+
for (let ri = 0; ri < grid.kids.length; ri++) {
|
|
4006
|
+
for (let c = 0; c < colCount; c++) {
|
|
4007
|
+
if (!tableMap[ri][c]) tableMap[ri][c] = { type: "empty" };
|
|
4008
|
+
}
|
|
4009
|
+
}
|
|
3181
4010
|
const defaultColDxa = Math.round(availDxa / colCount);
|
|
3182
|
-
|
|
3183
|
-
if (grid.props.colWidths && grid.props.colWidths.length
|
|
4011
|
+
let colWidthsDxa = [];
|
|
4012
|
+
if (grid.props.colWidths && grid.props.colWidths.length > 0) {
|
|
3184
4013
|
const srcPt = [...grid.props.colWidths];
|
|
3185
|
-
|
|
4014
|
+
while (srcPt.length < colCount) {
|
|
4015
|
+
srcPt.push(0);
|
|
4016
|
+
}
|
|
4017
|
+
srcPt.length = colCount;
|
|
4018
|
+
const knownTotalPt = srcPt.filter((w) => w > 0).reduce((s, w) => s + w, 0);
|
|
3186
4019
|
const zeroCount = srcPt.filter((w) => w <= 0).length;
|
|
3187
|
-
const
|
|
3188
|
-
const
|
|
4020
|
+
const availPt = Metric.dxaToPt(availDxa);
|
|
4021
|
+
const remainingPt = Math.max(0, availPt - knownTotalPt);
|
|
4022
|
+
const zeroFillPt = zeroCount > 0 ? remainingPt / zeroCount : 0;
|
|
3189
4023
|
for (let i = 0; i < srcPt.length; i++) {
|
|
3190
|
-
if (srcPt[i] <= 0)
|
|
4024
|
+
if (srcPt[i] <= 0) {
|
|
4025
|
+
srcPt[i] = zeroFillPt > 0 ? zeroFillPt : availPt / colCount;
|
|
4026
|
+
}
|
|
4027
|
+
}
|
|
4028
|
+
colWidthsDxa = srcPt.map((w) => Math.round(Metric.ptToDxa(w)));
|
|
4029
|
+
const computedTotalDxa = colWidthsDxa.reduce((s, w) => s + w, 0);
|
|
4030
|
+
if (computedTotalDxa > availDxa) {
|
|
4031
|
+
const scale = availDxa / computedTotalDxa;
|
|
4032
|
+
colWidthsDxa = colWidthsDxa.map((w) => Math.round(w * scale));
|
|
3191
4033
|
}
|
|
3192
|
-
const srcWidths = srcPt.map((w) => Metric.ptToDxa(w));
|
|
3193
|
-
const srcTotal = srcWidths.reduce((s, w) => s + w, 0);
|
|
3194
|
-
const scale = srcTotal > availDxa ? availDxa / srcTotal : 1;
|
|
3195
|
-
for (const w of srcWidths) colWidthsDxa.push(Math.round(w * scale));
|
|
3196
4034
|
} else {
|
|
3197
4035
|
for (let c = 0; c < colCount; c++) colWidthsDxa.push(defaultColDxa);
|
|
3198
4036
|
}
|
|
3199
4037
|
const totalDxa = colWidthsDxa.reduce((s, w) => s + w, 0);
|
|
3200
|
-
const gridCols = colWidthsDxa.map((w) => `<w:gridCol w:w="${
|
|
3201
|
-
const vMergeMap = /* @__PURE__ */ new Map();
|
|
3202
|
-
for (let ri = 0; ri < grid.kids.length; ri++) {
|
|
3203
|
-
let colIdx = 0;
|
|
3204
|
-
for (const cell of grid.kids[ri].kids) {
|
|
3205
|
-
if (cell.rs > 1) {
|
|
3206
|
-
vMergeMap.set(`${ri},${colIdx}`, "restart");
|
|
3207
|
-
for (let sr = 1; sr < cell.rs; sr++) {
|
|
3208
|
-
vMergeMap.set(`${ri + sr},${colIdx}`, "continue");
|
|
3209
|
-
}
|
|
3210
|
-
}
|
|
3211
|
-
colIdx += cell.cs;
|
|
3212
|
-
}
|
|
3213
|
-
}
|
|
4038
|
+
const gridCols = colWidthsDxa.map((w) => `<w:gridCol w:w="${w}"/>`).join("");
|
|
3214
4039
|
const rows = grid.kids.map((row, ri) => {
|
|
3215
|
-
let colIdx = 0;
|
|
3216
4040
|
const cellXmls = [];
|
|
3217
|
-
let
|
|
3218
|
-
|
|
3219
|
-
|
|
3220
|
-
|
|
3221
|
-
const cp = cell.props;
|
|
3222
|
-
const tcPrParts = [];
|
|
3223
|
-
let cellW = 0;
|
|
3224
|
-
for (let sc = colIdx; sc < colIdx + cell.cs && sc < colWidthsDxa.length; sc++) cellW += colWidthsDxa[sc];
|
|
3225
|
-
if (cellW === 0) cellW = defaultColDxa * cell.cs;
|
|
3226
|
-
tcPrParts.push(`<w:tcW w:w="${Math.round(cellW)}" w:type="dxa"/>`);
|
|
3227
|
-
if (cell.cs > 1) tcPrParts.push(`<w:gridSpan w:val="${cell.cs}"/>`);
|
|
3228
|
-
if (cell.rs > 1) {
|
|
3229
|
-
tcPrParts.push(`<w:vMerge w:val="restart"/>`);
|
|
3230
|
-
}
|
|
3231
|
-
const borders = encodeCellBorders(cp);
|
|
3232
|
-
if (borders) tcPrParts.push(borders);
|
|
3233
|
-
if (cp.bg) tcPrParts.push(`<w:shd w:val="clear" w:color="auto" w:fill="${cp.bg}"/>`);
|
|
3234
|
-
if (cp.va) {
|
|
3235
|
-
const vaMap = { top: "top", mid: "center", bot: "bottom" };
|
|
3236
|
-
tcPrParts.push(`<w:vAlign w:val="${vaMap[cp.va] ?? "top"}"/>`);
|
|
3237
|
-
}
|
|
3238
|
-
const tcPr = `<w:tcPr>${tcPrParts.join("")}</w:tcPr>`;
|
|
3239
|
-
cellXmls.push(` <w:tc>${tcPr}${cell.kids.map((p) => encodeParaInner(p, ctx)).join("")}</w:tc>`);
|
|
3240
|
-
colIdx += cell.cs;
|
|
3241
|
-
srcCellIdx++;
|
|
3242
|
-
}
|
|
3243
|
-
const finalCells = [];
|
|
3244
|
-
let finalColIdx = 0;
|
|
3245
|
-
let cellIter = 0;
|
|
3246
|
-
const totalGridCols = colCount;
|
|
3247
|
-
finalColIdx = 0;
|
|
3248
|
-
cellIter = 0;
|
|
3249
|
-
for (let gc = 0; gc < totalGridCols; ) {
|
|
3250
|
-
const mergeType = vMergeMap.get(`${ri},${gc}`);
|
|
3251
|
-
if (mergeType === "continue") {
|
|
3252
|
-
let origCs = 1;
|
|
3253
|
-
for (let sr = ri - 1; sr >= 0; sr--) {
|
|
3254
|
-
const mt = vMergeMap.get(`${sr},${gc}`);
|
|
3255
|
-
if (mt === "restart") {
|
|
3256
|
-
let col = 0;
|
|
3257
|
-
for (const c of grid.kids[sr].kids) {
|
|
3258
|
-
if (col === gc) {
|
|
3259
|
-
origCs = c.cs;
|
|
3260
|
-
break;
|
|
3261
|
-
}
|
|
3262
|
-
col += c.cs;
|
|
3263
|
-
}
|
|
3264
|
-
break;
|
|
3265
|
-
}
|
|
3266
|
-
}
|
|
4041
|
+
for (let c = 0; c < colCount; c++) {
|
|
4042
|
+
const mapEntry = tableMap[ri][c];
|
|
4043
|
+
if (mapEntry.type === "empty") continue;
|
|
4044
|
+
if (mapEntry.type === "continue") {
|
|
3267
4045
|
let cw = 0;
|
|
3268
|
-
for (let sc =
|
|
3269
|
-
if (cw === 0) cw = defaultColDxa *
|
|
4046
|
+
for (let sc = c; sc < c + mapEntry.width && sc < colWidthsDxa.length; sc++) cw += colWidthsDxa[sc];
|
|
4047
|
+
if (cw === 0) cw = defaultColDxa * mapEntry.width;
|
|
3270
4048
|
let contParts = `<w:tcW w:w="${Math.round(cw)}" w:type="dxa"/>`;
|
|
3271
|
-
if (
|
|
4049
|
+
if (mapEntry.width > 1) contParts += `<w:gridSpan w:val="${mapEntry.width}"/>`;
|
|
3272
4050
|
contParts += `<w:vMerge/>`;
|
|
3273
|
-
|
|
3274
|
-
|
|
3275
|
-
|
|
3276
|
-
|
|
3277
|
-
|
|
3278
|
-
|
|
3279
|
-
|
|
3280
|
-
|
|
3281
|
-
|
|
4051
|
+
cellXmls.push(` <w:tc><w:tcPr>${contParts}</w:tcPr><w:p><w:pPr/></w:p></w:tc>`);
|
|
4052
|
+
}
|
|
4053
|
+
if (mapEntry.type === "real") {
|
|
4054
|
+
const cell = mapEntry.cell;
|
|
4055
|
+
const cp = cell.props;
|
|
4056
|
+
const tcPrParts = [];
|
|
4057
|
+
let cellW = 0;
|
|
4058
|
+
for (let sc = c; sc < c + cell.cs && sc < colWidthsDxa.length; sc++) cellW += colWidthsDxa[sc];
|
|
4059
|
+
if (cellW === 0) cellW = defaultColDxa * cell.cs;
|
|
4060
|
+
tcPrParts.push(`<w:tcW w:w="${Math.round(cellW)}" w:type="dxa"/>`);
|
|
4061
|
+
if (cell.cs > 1) tcPrParts.push(`<w:gridSpan w:val="${cell.cs}"/>`);
|
|
4062
|
+
if (cell.rs > 1) tcPrParts.push(`<w:vMerge w:val="restart"/>`);
|
|
4063
|
+
const borders = encodeCellBorders(cp);
|
|
4064
|
+
if (borders) tcPrParts.push(borders);
|
|
4065
|
+
if (cp.bg) tcPrParts.push(`<w:shd w:val="clear" w:color="auto" w:fill="${cp.bg}"/>`);
|
|
4066
|
+
if (cp.va) {
|
|
4067
|
+
const vaMap = { top: "top", mid: "center", bot: "bottom" };
|
|
4068
|
+
tcPrParts.push(`<w:vAlign w:val="${vaMap[cp.va] ?? "top"}"/>`);
|
|
3282
4069
|
}
|
|
4070
|
+
const tcPr = `<w:tcPr>${tcPrParts.join("")}</w:tcPr>`;
|
|
4071
|
+
cellXmls.push(` <w:tc>${tcPr}${cell.kids.map((p) => encodeParaInner(p, ctx)).join("")}</w:tc>`);
|
|
3283
4072
|
}
|
|
3284
4073
|
}
|
|
3285
|
-
|
|
4074
|
+
const trPrParts = [];
|
|
3286
4075
|
if (ri === 0 && (gp.headerRow || look?.firstRow)) {
|
|
3287
|
-
|
|
4076
|
+
trPrParts.push("<w:tblHeader/>");
|
|
3288
4077
|
}
|
|
4078
|
+
if (row.heightPt != null && row.heightPt > 0) {
|
|
4079
|
+
const hDxa = Math.round(Metric.ptToDxa(row.heightPt));
|
|
4080
|
+
trPrParts.push(`<w:trHeight w:val="${hDxa}" w:hRule="exact"/>`);
|
|
4081
|
+
}
|
|
4082
|
+
const trPr = trPrParts.length > 0 ? `<w:trPr>${trPrParts.join("")}</w:trPr>` : "";
|
|
3289
4083
|
return ` <w:tr>${trPr}
|
|
3290
|
-
${
|
|
4084
|
+
${cellXmls.join("\n")}
|
|
3291
4085
|
</w:tr>`;
|
|
3292
4086
|
}).join("\n");
|
|
3293
4087
|
let tblBorders = "";
|
|
3294
4088
|
if (gp.defaultStroke) {
|
|
3295
4089
|
const s = gp.defaultStroke;
|
|
3296
|
-
const strokeKindMap = { solid: "single", dash: "
|
|
4090
|
+
const strokeKindMap = { solid: "single", dash: "dash", dot: "dot", double: "double", none: "none" };
|
|
3297
4091
|
const val = strokeKindMap[s.kind] ?? "single";
|
|
3298
4092
|
const sz = Math.round(s.pt * 8);
|
|
3299
4093
|
const bdr = `w:val="${val}" w:sz="${sz}" w:space="0" w:color="${s.color}"`;
|
|
@@ -3307,7 +4101,7 @@ ${rows}
|
|
|
3307
4101
|
}
|
|
3308
4102
|
function encodeCellBorders(cp) {
|
|
3309
4103
|
if (!cp.top && !cp.bot && !cp.left && !cp.right) return "";
|
|
3310
|
-
const strokeKindMap = { solid: "single", dash: "
|
|
4104
|
+
const strokeKindMap = { solid: "single", dash: "dash", dot: "dot", double: "double", none: "none" };
|
|
3311
4105
|
const encode = (s, tag) => {
|
|
3312
4106
|
if (!s || !tag) return "";
|
|
3313
4107
|
const val = strokeKindMap[s.kind] ?? "single";
|
|
@@ -3316,6 +4110,11 @@ function encodeCellBorders(cp) {
|
|
|
3316
4110
|
return `<w:tcBorders>${encode(cp.top, "top")}${encode(cp.bot, "bottom")}${encode(cp.left, "left")}${encode(cp.right, "right")}</w:tcBorders>`;
|
|
3317
4111
|
}
|
|
3318
4112
|
function esc2(s) {
|
|
4113
|
+
if (!s) return "";
|
|
4114
|
+
s = s.replace(/__EXT_\d+__/g, "");
|
|
4115
|
+
s = s.replace(/湰灧/g, "");
|
|
4116
|
+
s = s.replace(/\uFEFF/g, "");
|
|
4117
|
+
s = s.replace(/[^\x09\x0A\x0D\x20-\uD7FF\uE000-\uFFFD]/g, "");
|
|
3319
4118
|
return TextKit.escapeXml(s);
|
|
3320
4119
|
}
|
|
3321
4120
|
registry.registerEncoder(new DocxEncoder());
|
|
@@ -3360,10 +4159,6 @@ function encodePara2(para, warns) {
|
|
|
3360
4159
|
return text;
|
|
3361
4160
|
}
|
|
3362
4161
|
function encodeSpan(span, warns) {
|
|
3363
|
-
if (span.props.font) warns.push(`[SHIELD] MD: \uAE00\uAF34(${span.props.font}) \uD45C\uD604 \uBD88\uAC00 \u2014 \uC190\uC2E4\uB428`);
|
|
3364
|
-
if (span.props.pt) warns.push(`[SHIELD] MD: \uAE00\uC790 \uD06C\uAE30(${span.props.pt}pt) \uD45C\uD604 \uBD88\uAC00 \u2014 \uC190\uC2E4\uB428`);
|
|
3365
|
-
if (span.props.color) warns.push(`[SHIELD] MD: \uAE00\uC790 \uC0C9\uC0C1(#${span.props.color}) \uD45C\uD604 \uBD88\uAC00 \u2014 \uC190\uC2E4\uB428`);
|
|
3366
|
-
if (span.props.bg) warns.push(`[SHIELD] MD: \uBC30\uACBD \uC0C9\uC0C1(#${span.props.bg}) \uD45C\uD604 \uBD88\uAC00 \u2014 \uC190\uC2E4\uB428`);
|
|
3367
4162
|
let hasPageNum = false;
|
|
3368
4163
|
const textParts = [];
|
|
3369
4164
|
for (const kid of span.kids) {
|
|
@@ -3375,6 +4170,31 @@ function encodeSpan(span, warns) {
|
|
|
3375
4170
|
}
|
|
3376
4171
|
let r = textParts.join("");
|
|
3377
4172
|
if (hasPageNum && r === "") r = "[\uD398\uC774\uC9C0 \uBC88\uD638]";
|
|
4173
|
+
const cssStyles = [];
|
|
4174
|
+
if (span.props.font) cssStyles.push(`font-family: ${span.props.font}`);
|
|
4175
|
+
if (span.props.pt) cssStyles.push(`font-size: ${span.props.pt}pt`);
|
|
4176
|
+
if (span.props.color) cssStyles.push(`color: #${span.props.color}`);
|
|
4177
|
+
if (span.props.bg) cssStyles.push(`background-color: #${span.props.bg}`);
|
|
4178
|
+
const hasHtmlStyle = cssStyles.length > 0;
|
|
4179
|
+
if (hasHtmlStyle) {
|
|
4180
|
+
if (span.props.b) cssStyles.push("font-weight: bold");
|
|
4181
|
+
if (span.props.i) cssStyles.push("font-style: italic");
|
|
4182
|
+
if (span.props.s) cssStyles.push("text-decoration: line-through");
|
|
4183
|
+
if (span.props.u) {
|
|
4184
|
+
const existing = cssStyles.find((s) => s.startsWith("text-decoration:"));
|
|
4185
|
+
if (existing) {
|
|
4186
|
+
const idx = cssStyles.indexOf(existing);
|
|
4187
|
+
cssStyles[idx] = existing.replace("line-through", "underline line-through");
|
|
4188
|
+
if (!existing.includes("line-through")) cssStyles[idx] = existing + " underline";
|
|
4189
|
+
} else {
|
|
4190
|
+
cssStyles.push("text-decoration: underline");
|
|
4191
|
+
}
|
|
4192
|
+
}
|
|
4193
|
+
const styleAttr = cssStyles.join("; ");
|
|
4194
|
+
if (span.props.sup) return `<sup style="${styleAttr}">${r}</sup>`;
|
|
4195
|
+
if (span.props.sub) return `<sub style="${styleAttr}">${r}</sub>`;
|
|
4196
|
+
return `<span style="${styleAttr}">${r}</span>`;
|
|
4197
|
+
}
|
|
3378
4198
|
if (span.props.b && span.props.i) r = `***${r}***`;
|
|
3379
4199
|
else if (span.props.b) r = `**${r}**`;
|
|
3380
4200
|
else if (span.props.i) r = `*${r}*`;
|
|
@@ -3387,17 +4207,68 @@ function encodeSpan(span, warns) {
|
|
|
3387
4207
|
function encodeImage3(img) {
|
|
3388
4208
|
return ``;
|
|
3389
4209
|
}
|
|
4210
|
+
function strokeToCss(s) {
|
|
4211
|
+
if (!s || s.kind === "none" || s.pt <= 0) return void 0;
|
|
4212
|
+
const kindMap = { solid: "solid", dash: "dashed", dot: "dotted", double: "double", none: "none" };
|
|
4213
|
+
const style = kindMap[s.kind] ?? "solid";
|
|
4214
|
+
const px = Math.max(1, Math.round(s.pt * 96 / 72));
|
|
4215
|
+
const color = s.color.startsWith("#") ? s.color : `#${s.color}`;
|
|
4216
|
+
return `${px}px ${style} ${color}`;
|
|
4217
|
+
}
|
|
3390
4218
|
function encodeGrid3(grid, warns) {
|
|
3391
4219
|
if (grid.kids.length === 0) return "";
|
|
3392
|
-
|
|
3393
|
-
const
|
|
3394
|
-
|
|
3395
|
-
)
|
|
3396
|
-
|
|
3397
|
-
|
|
3398
|
-
|
|
4220
|
+
const rowCount = grid.kids.length;
|
|
4221
|
+
const occupancy = Array.from({ length: rowCount }, () => /* @__PURE__ */ new Set());
|
|
4222
|
+
let colCount = 0;
|
|
4223
|
+
for (let ri = 0; ri < rowCount; ri++) {
|
|
4224
|
+
const row = grid.kids[ri];
|
|
4225
|
+
let ci = 0;
|
|
4226
|
+
for (const cell of row.kids) {
|
|
4227
|
+
while (occupancy[ri].has(ci)) ci++;
|
|
4228
|
+
if (cell.rs > 1) {
|
|
4229
|
+
for (let r = ri + 1; r < ri + cell.rs && r < rowCount; r++) {
|
|
4230
|
+
for (let c = ci; c < ci + cell.cs; c++) occupancy[r].add(c);
|
|
4231
|
+
}
|
|
4232
|
+
}
|
|
4233
|
+
ci += cell.cs;
|
|
4234
|
+
}
|
|
4235
|
+
while (occupancy[ri].has(ci)) ci++;
|
|
4236
|
+
if (ci > colCount) colCount = ci;
|
|
4237
|
+
}
|
|
4238
|
+
let rows = "";
|
|
4239
|
+
for (let ri = 0; ri < rowCount; ri++) {
|
|
4240
|
+
const row = grid.kids[ri];
|
|
4241
|
+
let cells = "";
|
|
4242
|
+
let colIdx = 0;
|
|
4243
|
+
for (const cell of row.kids) {
|
|
4244
|
+
while (occupancy[ri].has(colIdx)) colIdx++;
|
|
4245
|
+
const cs = cell.cs > 1 ? ` colspan="${cell.cs}"` : "";
|
|
4246
|
+
const rs = cell.rs > 1 ? ` rowspan="${cell.rs}"` : "";
|
|
4247
|
+
const styles = ["padding:4px 6px", "vertical-align:top"];
|
|
4248
|
+
const top = strokeToCss(cell.props.top);
|
|
4249
|
+
const bot = strokeToCss(cell.props.bot);
|
|
4250
|
+
const left = strokeToCss(cell.props.left);
|
|
4251
|
+
const right = strokeToCss(cell.props.right);
|
|
4252
|
+
if (top) styles.push(`border-top:${top}`);
|
|
4253
|
+
if (bot) styles.push(`border-bottom:${bot}`);
|
|
4254
|
+
if (left) styles.push(`border-left:${left}`);
|
|
4255
|
+
if (right) styles.push(`border-right:${right}`);
|
|
4256
|
+
if (cell.props.bg) styles.push(`background-color:#${cell.props.bg}`);
|
|
4257
|
+
if (cell.props.va === "mid") styles[1] = "vertical-align:middle";
|
|
4258
|
+
else if (cell.props.va === "bot") styles[1] = "vertical-align:bottom";
|
|
4259
|
+
const tag = grid.props.headerRow && ri === 0 || cell.props.isHeader ? "th" : "td";
|
|
4260
|
+
const content = cell.kids.map((p) => encodePara2(p, warns)).join("\n");
|
|
4261
|
+
cells += `<${tag}${cs}${rs} style="${styles.join(";")}">${content}</${tag}>`;
|
|
4262
|
+
colIdx += cell.cs;
|
|
4263
|
+
}
|
|
4264
|
+
rows += `<tr>${cells}</tr>
|
|
4265
|
+
`;
|
|
3399
4266
|
}
|
|
3400
|
-
return
|
|
4267
|
+
return `<table style="border-collapse:collapse;width:100%">
|
|
4268
|
+
<tbody>
|
|
4269
|
+
${rows}</tbody>
|
|
4270
|
+
</table>
|
|
4271
|
+
`;
|
|
3401
4272
|
}
|
|
3402
4273
|
registry.registerEncoder(new MdEncoder());
|
|
3403
4274
|
|
|
@@ -3444,8 +4315,15 @@ var Pipeline = class _Pipeline {
|
|
|
3444
4315
|
}
|
|
3445
4316
|
};
|
|
3446
4317
|
function detectFormat(data) {
|
|
3447
|
-
if (data[0] === 80 && data[1] === 75) return "zip";
|
|
3448
4318
|
if (data[0] === 208 && data[1] === 207 && data[2] === 17 && data[3] === 224) return "hwp";
|
|
4319
|
+
if (data[0] === 80 && data[1] === 75) {
|
|
4320
|
+
const str = new TextDecoder("utf-8", { fatal: false }).decode(data.slice(0, 4096));
|
|
4321
|
+
if (str.includes("wordprocessingml")) return "docx";
|
|
4322
|
+
if (str.includes("ha-xml")) return "hwpx";
|
|
4323
|
+
if (str.includes("hwpml/")) return "hwpx";
|
|
4324
|
+
if (str.includes("word/")) return "docx";
|
|
4325
|
+
return "hwpx";
|
|
4326
|
+
}
|
|
3449
4327
|
return "md";
|
|
3450
4328
|
}
|
|
3451
4329
|
function getExt(name) {
|
|
@@ -3453,6 +4331,1017 @@ function getExt(name) {
|
|
|
3453
4331
|
return parts.length > 1 ? parts[parts.length - 1].toLowerCase() : void 0;
|
|
3454
4332
|
}
|
|
3455
4333
|
|
|
4334
|
+
// src/encoders/html/HtmlEncoder.ts
|
|
4335
|
+
var HtmlEncoder = class {
|
|
4336
|
+
constructor() {
|
|
4337
|
+
this.format = "html";
|
|
4338
|
+
}
|
|
4339
|
+
async encode(doc) {
|
|
4340
|
+
try {
|
|
4341
|
+
const warns = [];
|
|
4342
|
+
const bodyParts = [];
|
|
4343
|
+
for (const sheet of doc.kids) {
|
|
4344
|
+
if (sheet.header && sheet.header.length > 0) {
|
|
4345
|
+
const hText = sheet.header.map((p) => encodePara3(p, warns)).join("");
|
|
4346
|
+
bodyParts.push(`<div class="hwp-header">${hText}</div>`);
|
|
4347
|
+
}
|
|
4348
|
+
for (const kid of sheet.kids) {
|
|
4349
|
+
bodyParts.push(encodeContent3(kid, warns));
|
|
4350
|
+
}
|
|
4351
|
+
if (sheet.footer && sheet.footer.length > 0) {
|
|
4352
|
+
const fText = sheet.footer.map((p) => encodePara3(p, warns)).join("");
|
|
4353
|
+
bodyParts.push(`<div class="hwp-footer">${fText}</div>`);
|
|
4354
|
+
}
|
|
4355
|
+
}
|
|
4356
|
+
const title = esc3(doc.meta?.title ?? "");
|
|
4357
|
+
const html = `<!DOCTYPE html>
|
|
4358
|
+
<html lang="ko">
|
|
4359
|
+
<head>
|
|
4360
|
+
<meta charset="UTF-8">
|
|
4361
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
4362
|
+
<title>${title}</title>
|
|
4363
|
+
<style>
|
|
4364
|
+
${BASE_CSS}
|
|
4365
|
+
</style>
|
|
4366
|
+
</head>
|
|
4367
|
+
<body>
|
|
4368
|
+
<div class="hwp-doc">
|
|
4369
|
+
${bodyParts.join("\n")}
|
|
4370
|
+
</div>
|
|
4371
|
+
</body>
|
|
4372
|
+
</html>`;
|
|
4373
|
+
return succeed(TextKit.encode(html), warns);
|
|
4374
|
+
} catch (e) {
|
|
4375
|
+
return fail(`HTML encode error: ${e?.message ?? String(e)}`);
|
|
4376
|
+
}
|
|
4377
|
+
}
|
|
4378
|
+
};
|
|
4379
|
+
var BASE_CSS = `
|
|
4380
|
+
body { margin: 0; padding: 0; background: #f0f0f0; }
|
|
4381
|
+
.hwp-doc { max-width: 800px; margin: 0 auto; background: #fff; padding: 40px 60px; box-shadow: 0 0 8px rgba(0,0,0,0.15); }
|
|
4382
|
+
.hwp-header, .hwp-footer { color: #666; font-size: 0.9em; border-bottom: 1px solid #ddd; margin-bottom: 8px; padding-bottom: 4px; }
|
|
4383
|
+
.hwp-footer { border-top: 1px solid #ddd; border-bottom: none; margin-top: 8px; padding-top: 4px; }
|
|
4384
|
+
p { margin: 0; padding: 0; line-height: 1.6; }
|
|
4385
|
+
table { border-collapse: collapse; width: 100%; margin: 8px 0; }
|
|
4386
|
+
td, th { border: 1px solid #ccc; padding: 4px 8px; vertical-align: top; }
|
|
4387
|
+
img { max-width: 100%; height: auto; }
|
|
4388
|
+
`.trim();
|
|
4389
|
+
function encodeContent3(node, warns) {
|
|
4390
|
+
return node.tag === "grid" ? encodeGrid4(node, warns) : encodePara3(node, warns);
|
|
4391
|
+
}
|
|
4392
|
+
function encodePara3(para, warns) {
|
|
4393
|
+
const kids = para.kids.map((k) => {
|
|
4394
|
+
if (k.tag === "span") return encodeSpan2(k, warns);
|
|
4395
|
+
if (k.tag === "img") return encodeImage4(k);
|
|
4396
|
+
if (k.tag === "link") {
|
|
4397
|
+
const link = k;
|
|
4398
|
+
const inner = link.kids.map((s) => encodeSpan2(s, warns)).join("");
|
|
4399
|
+
return `<a href="${esc3(link.href)}">${inner}</a>`;
|
|
4400
|
+
}
|
|
4401
|
+
return "";
|
|
4402
|
+
}).join("");
|
|
4403
|
+
if (para.props.heading) {
|
|
4404
|
+
const tag = `h${para.props.heading}`;
|
|
4405
|
+
return `<${tag}>${kids}</${tag}>
|
|
4406
|
+
`;
|
|
4407
|
+
}
|
|
4408
|
+
if (para.props.listOrd !== void 0) {
|
|
4409
|
+
const indent = (para.props.listLv ?? 0) * 20;
|
|
4410
|
+
const style = indent > 0 ? ` style="margin-left:${indent}px"` : "";
|
|
4411
|
+
const marker = para.props.listOrd ? `<span class="list-marker">1. </span>` : `<span class="list-marker">\u2022 </span>`;
|
|
4412
|
+
return `<p${style}>${marker}${kids}</p>
|
|
4413
|
+
`;
|
|
4414
|
+
}
|
|
4415
|
+
const align = para.props.align;
|
|
4416
|
+
const styleAttrs = [];
|
|
4417
|
+
if (align && align !== "left") styleAttrs.push(`text-align:${align}`);
|
|
4418
|
+
if (para.props.indentPt) styleAttrs.push(`margin-left:${para.props.indentPt.toFixed(1)}pt`);
|
|
4419
|
+
if (para.props.spaceBefore) styleAttrs.push(`margin-top:${para.props.spaceBefore.toFixed(1)}pt`);
|
|
4420
|
+
if (para.props.spaceAfter) styleAttrs.push(`margin-bottom:${para.props.spaceAfter.toFixed(1)}pt`);
|
|
4421
|
+
if (para.props.lineHeight) styleAttrs.push(`line-height:${para.props.lineHeight}`);
|
|
4422
|
+
const styleAttr = styleAttrs.length > 0 ? ` style="${styleAttrs.join(";")}"` : "";
|
|
4423
|
+
return `<p${styleAttr}>${kids || " "}</p>
|
|
4424
|
+
`;
|
|
4425
|
+
}
|
|
4426
|
+
function encodeSpan2(span, warns) {
|
|
4427
|
+
const parts = [];
|
|
4428
|
+
let hasPageNum = false;
|
|
4429
|
+
for (const kid of span.kids) {
|
|
4430
|
+
if (kid.tag === "txt") {
|
|
4431
|
+
parts.push(esc3(kid.content));
|
|
4432
|
+
} else if (kid.tag === "br") {
|
|
4433
|
+
parts.push("<br>");
|
|
4434
|
+
} else if (kid.tag === "pb") {
|
|
4435
|
+
parts.push('<div style="page-break-after:always"></div>');
|
|
4436
|
+
} else if (kid.tag === "pagenum") {
|
|
4437
|
+
hasPageNum = true;
|
|
4438
|
+
warns.push("[SHIELD] HTML: \uD398\uC774\uC9C0 \uBC88\uD638 \u2014 \uC815\uC801 \uAC12\uC73C\uB85C \uB300\uCCB4\uB428");
|
|
4439
|
+
parts.push('<span class="page-num">[\uD398\uC774\uC9C0]</span>');
|
|
4440
|
+
}
|
|
4441
|
+
}
|
|
4442
|
+
let text = parts.join("");
|
|
4443
|
+
if (hasPageNum && text.trim() === '<span class="page-num">[\uD398\uC774\uC9C0]</span>') {
|
|
4444
|
+
}
|
|
4445
|
+
const p = span.props;
|
|
4446
|
+
const css = [];
|
|
4447
|
+
if (p.font) css.push(`font-family:${esc3(p.font)}`);
|
|
4448
|
+
if (p.pt) css.push(`font-size:${p.pt}pt`);
|
|
4449
|
+
if (p.color) css.push(`color:#${p.color}`);
|
|
4450
|
+
if (p.bg) css.push(`background-color:#${p.bg}`);
|
|
4451
|
+
if (p.b) css.push("font-weight:bold");
|
|
4452
|
+
if (p.i) css.push("font-style:italic");
|
|
4453
|
+
const decorations = [];
|
|
4454
|
+
if (p.u) decorations.push("underline");
|
|
4455
|
+
if (p.s) decorations.push("line-through");
|
|
4456
|
+
if (decorations.length > 0) css.push(`text-decoration:${decorations.join(" ")}`);
|
|
4457
|
+
if (p.sup) return `<sup${css.length ? ` style="${css.join(";")}"` : ""}>${text}</sup>`;
|
|
4458
|
+
if (p.sub) return `<sub${css.length ? ` style="${css.join(";")}"` : ""}>${text}</sub>`;
|
|
4459
|
+
if (css.length > 0) return `<span style="${css.join(";")}">${text}</span>`;
|
|
4460
|
+
return text;
|
|
4461
|
+
}
|
|
4462
|
+
function encodeImage4(img) {
|
|
4463
|
+
const wStyle = img.w ? ` width="${Math.round(img.w / 72 * 96)}px"` : "";
|
|
4464
|
+
const hStyle = img.h ? ` height="${Math.round(img.h / 72 * 96)}px"` : "";
|
|
4465
|
+
const alt = esc3(img.alt ?? "");
|
|
4466
|
+
return `<img src="data:${img.mime};base64,${img.b64}" alt="${alt}"${wStyle}${hStyle}>`;
|
|
4467
|
+
}
|
|
4468
|
+
function encodeGrid4(grid, warns) {
|
|
4469
|
+
if (grid.kids.length === 0) return "";
|
|
4470
|
+
const rowCount = grid.kids.length;
|
|
4471
|
+
const occupancy = Array.from({ length: rowCount }, () => /* @__PURE__ */ new Set());
|
|
4472
|
+
let colCount = 0;
|
|
4473
|
+
for (let ri = 0; ri < rowCount; ri++) {
|
|
4474
|
+
const row = grid.kids[ri];
|
|
4475
|
+
let ci = 0;
|
|
4476
|
+
for (const cell of row.kids) {
|
|
4477
|
+
while (occupancy[ri].has(ci)) ci++;
|
|
4478
|
+
if (cell.rs > 1) {
|
|
4479
|
+
for (let r = ri + 1; r < ri + cell.rs && r < rowCount; r++) {
|
|
4480
|
+
for (let c = ci; c < ci + cell.cs; c++) occupancy[r].add(c);
|
|
4481
|
+
}
|
|
4482
|
+
}
|
|
4483
|
+
ci += cell.cs;
|
|
4484
|
+
}
|
|
4485
|
+
while (occupancy[ri].has(ci)) ci++;
|
|
4486
|
+
if (ci > colCount) colCount = ci;
|
|
4487
|
+
}
|
|
4488
|
+
let rows = "";
|
|
4489
|
+
for (let ri = 0; ri < rowCount; ri++) {
|
|
4490
|
+
const row = grid.kids[ri];
|
|
4491
|
+
let cells = "";
|
|
4492
|
+
let ci = 0;
|
|
4493
|
+
for (const cell of row.kids) {
|
|
4494
|
+
while (occupancy[ri].has(ci)) ci++;
|
|
4495
|
+
const isHeader = cell.props.isHeader || grid.props.headerRow && ri === 0;
|
|
4496
|
+
const tag = isHeader ? "th" : "td";
|
|
4497
|
+
const cs = cell.cs > 1 ? ` colspan="${cell.cs}"` : "";
|
|
4498
|
+
const rs = cell.rs > 1 ? ` rowspan="${cell.rs}"` : "";
|
|
4499
|
+
const styleAttrs = [];
|
|
4500
|
+
if (cell.props.bg) styleAttrs.push(`background-color:#${cell.props.bg}`);
|
|
4501
|
+
const va = cell.props.va;
|
|
4502
|
+
if (va === "mid") styleAttrs.push("vertical-align:middle");
|
|
4503
|
+
else if (va === "bot") styleAttrs.push("vertical-align:bottom");
|
|
4504
|
+
const styleAttr = styleAttrs.length > 0 ? ` style="${styleAttrs.join(";")}"` : "";
|
|
4505
|
+
const content = cell.kids.map((p) => encodePara3(p, warns)).join("");
|
|
4506
|
+
cells += `<${tag}${cs}${rs}${styleAttr}>${content}</${tag}>`;
|
|
4507
|
+
ci += cell.cs;
|
|
4508
|
+
}
|
|
4509
|
+
rows += `<tr>${cells}</tr>
|
|
4510
|
+
`;
|
|
4511
|
+
}
|
|
4512
|
+
return `<table>
|
|
4513
|
+
<tbody>
|
|
4514
|
+
${rows}</tbody>
|
|
4515
|
+
</table>
|
|
4516
|
+
`;
|
|
4517
|
+
}
|
|
4518
|
+
function esc3(s) {
|
|
4519
|
+
return TextKit.escapeXml(s);
|
|
4520
|
+
}
|
|
4521
|
+
registry.registerEncoder(new HtmlEncoder());
|
|
4522
|
+
|
|
4523
|
+
// src/encoders/hwp/HwpEncoder.ts
|
|
4524
|
+
var import_pako3 = __toESM(require("pako"));
|
|
4525
|
+
var T = 16;
|
|
4526
|
+
var TAG_DOCUMENT_PROPERTIES = T + 0;
|
|
4527
|
+
var TAG_ID_MAPPINGS = T + 1;
|
|
4528
|
+
var TAG_FACE_NAME2 = T + 3;
|
|
4529
|
+
var TAG_BORDER_FILL2 = T + 4;
|
|
4530
|
+
var TAG_CHAR_SHAPE2 = T + 5;
|
|
4531
|
+
var TAG_PARA_SHAPE2 = T + 9;
|
|
4532
|
+
var TAG_BIN_DATA = T + 2;
|
|
4533
|
+
var TAG_PARA_HEADER2 = T + 50;
|
|
4534
|
+
var TAG_PARA_TEXT2 = T + 51;
|
|
4535
|
+
var TAG_PARA_CHAR_SHAPE2 = T + 52;
|
|
4536
|
+
var TAG_PARA_LINE_SEG = T + 53;
|
|
4537
|
+
var TAG_CTRL_HEADER2 = T + 55;
|
|
4538
|
+
var TAG_LIST_HEADER2 = T + 56;
|
|
4539
|
+
var TAG_PAGE_DEF2 = T + 57;
|
|
4540
|
+
var TAG_FOOTNOTE_SHAPE = T + 58;
|
|
4541
|
+
var TAG_TABLE = T + 61;
|
|
4542
|
+
var TAG_SHAPE_COMPONENT_PICTURE = T + 69;
|
|
4543
|
+
var CTRL_TABLE2 = 1952607264;
|
|
4544
|
+
var CTRL_SECD = 1936024420;
|
|
4545
|
+
var CTRL_PIC = 611346787;
|
|
4546
|
+
var BORDER_W_PT2 = [
|
|
4547
|
+
0.28,
|
|
4548
|
+
0.34,
|
|
4549
|
+
0.43,
|
|
4550
|
+
0.57,
|
|
4551
|
+
0.71,
|
|
4552
|
+
0.85,
|
|
4553
|
+
1.13,
|
|
4554
|
+
1.42,
|
|
4555
|
+
1.7,
|
|
4556
|
+
1.98,
|
|
4557
|
+
2.84,
|
|
4558
|
+
4.25,
|
|
4559
|
+
5.67,
|
|
4560
|
+
8.5,
|
|
4561
|
+
11.34,
|
|
4562
|
+
14.17
|
|
4563
|
+
];
|
|
4564
|
+
var BORDER_KIND_IDX = {
|
|
4565
|
+
solid: 0,
|
|
4566
|
+
dash: 2,
|
|
4567
|
+
dot: 3,
|
|
4568
|
+
double: 7,
|
|
4569
|
+
none: 0
|
|
4570
|
+
};
|
|
4571
|
+
var ALIGN_CODE = {
|
|
4572
|
+
justify: 0,
|
|
4573
|
+
left: 1,
|
|
4574
|
+
right: 2,
|
|
4575
|
+
center: 3
|
|
4576
|
+
};
|
|
4577
|
+
var BufWriter = class {
|
|
4578
|
+
constructor() {
|
|
4579
|
+
this.chunks = [];
|
|
4580
|
+
this._sz = 0;
|
|
4581
|
+
}
|
|
4582
|
+
get size() {
|
|
4583
|
+
return this._sz;
|
|
4584
|
+
}
|
|
4585
|
+
u8(v) {
|
|
4586
|
+
this.chunks.push(new Uint8Array([v & 255]));
|
|
4587
|
+
this._sz++;
|
|
4588
|
+
return this;
|
|
4589
|
+
}
|
|
4590
|
+
u16(v) {
|
|
4591
|
+
this.chunks.push(new Uint8Array([v & 255, v >> 8 & 255]));
|
|
4592
|
+
this._sz += 2;
|
|
4593
|
+
return this;
|
|
4594
|
+
}
|
|
4595
|
+
u32(v) {
|
|
4596
|
+
const b = new Uint8Array(4);
|
|
4597
|
+
b[0] = v & 255;
|
|
4598
|
+
b[1] = v >>> 8 & 255;
|
|
4599
|
+
b[2] = v >>> 16 & 255;
|
|
4600
|
+
b[3] = v >>> 24 & 255;
|
|
4601
|
+
this.chunks.push(b);
|
|
4602
|
+
this._sz += 4;
|
|
4603
|
+
return this;
|
|
4604
|
+
}
|
|
4605
|
+
i32(v) {
|
|
4606
|
+
return this.u32(v < 0 ? v + 4294967296 : v);
|
|
4607
|
+
}
|
|
4608
|
+
i16(v) {
|
|
4609
|
+
return this.u16(v < 0 ? v + 65536 : v);
|
|
4610
|
+
}
|
|
4611
|
+
bytes(d) {
|
|
4612
|
+
this.chunks.push(d);
|
|
4613
|
+
this._sz += d.length;
|
|
4614
|
+
return this;
|
|
4615
|
+
}
|
|
4616
|
+
zeros(n) {
|
|
4617
|
+
this.chunks.push(new Uint8Array(n));
|
|
4618
|
+
this._sz += n;
|
|
4619
|
+
return this;
|
|
4620
|
+
}
|
|
4621
|
+
/** Write each char as UTF-16LE UINT16 (BMP only) */
|
|
4622
|
+
utf16(s) {
|
|
4623
|
+
for (let i = 0; i < s.length; i++) this.u16(s.charCodeAt(i));
|
|
4624
|
+
return this;
|
|
4625
|
+
}
|
|
4626
|
+
/** Write 4-byte COLORREF (R, G, B, 0) from 6-hex string */
|
|
4627
|
+
colorRef(hex) {
|
|
4628
|
+
const h = (hex || "000000").replace("#", "").padStart(6, "0");
|
|
4629
|
+
return this.u8(parseInt(h.slice(0, 2), 16)).u8(parseInt(h.slice(2, 4), 16)).u8(parseInt(h.slice(4, 6), 16)).u8(0);
|
|
4630
|
+
}
|
|
4631
|
+
build() {
|
|
4632
|
+
const out = new Uint8Array(this._sz);
|
|
4633
|
+
let off = 0;
|
|
4634
|
+
for (const c of this.chunks) {
|
|
4635
|
+
out.set(c, off);
|
|
4636
|
+
off += c.length;
|
|
4637
|
+
}
|
|
4638
|
+
return out;
|
|
4639
|
+
}
|
|
4640
|
+
};
|
|
4641
|
+
function mkRec(tag, level, data) {
|
|
4642
|
+
const sz = data.length;
|
|
4643
|
+
const enc = Math.min(sz, 4095);
|
|
4644
|
+
const hdr = enc << 20 | (level & 1023) << 10 | tag & 1023;
|
|
4645
|
+
const w = new BufWriter().u32(hdr);
|
|
4646
|
+
if (enc >= 4095) w.u32(sz);
|
|
4647
|
+
w.bytes(data);
|
|
4648
|
+
return w.build();
|
|
4649
|
+
}
|
|
4650
|
+
function csKey(p) {
|
|
4651
|
+
return [
|
|
4652
|
+
p.font ?? "",
|
|
4653
|
+
p.pt ?? 10,
|
|
4654
|
+
p.b ? 1 : 0,
|
|
4655
|
+
p.i ? 1 : 0,
|
|
4656
|
+
p.u ? 1 : 0,
|
|
4657
|
+
p.s ? 1 : 0,
|
|
4658
|
+
p.sup ? 1 : 0,
|
|
4659
|
+
p.sub ? 1 : 0,
|
|
4660
|
+
p.color ?? "000000"
|
|
4661
|
+
].join("|");
|
|
4662
|
+
}
|
|
4663
|
+
function psKey(p) {
|
|
4664
|
+
return [
|
|
4665
|
+
p.align ?? "left",
|
|
4666
|
+
p.indentPt ?? 0,
|
|
4667
|
+
p.spaceBefore ?? 0,
|
|
4668
|
+
p.spaceAfter ?? 0,
|
|
4669
|
+
p.lineHeight ?? 1
|
|
4670
|
+
].join("|");
|
|
4671
|
+
}
|
|
4672
|
+
function bfKey(s, bg) {
|
|
4673
|
+
return `${s.kind}|${s.pt}|${s.color}|${bg ?? ""}`;
|
|
4674
|
+
}
|
|
4675
|
+
function bfPerSideKey(l, r, t, b, bg) {
|
|
4676
|
+
return `${bfKey(l)}/${bfKey(r)}/${bfKey(t)}/${bfKey(b)}/${bg ?? ""}`;
|
|
4677
|
+
}
|
|
4678
|
+
var StyleCollector = class {
|
|
4679
|
+
constructor() {
|
|
4680
|
+
this.DEF_STROKE = { kind: "solid", pt: 0.5, color: "000000" };
|
|
4681
|
+
this.fonts = ["Malgun Gothic"];
|
|
4682
|
+
this.fontIdx = /* @__PURE__ */ new Map([["Malgun Gothic", 0]]);
|
|
4683
|
+
this.csProps = [{}];
|
|
4684
|
+
this.csIdx = /* @__PURE__ */ new Map([[csKey({}), 0]]);
|
|
4685
|
+
this.psProps = [{}];
|
|
4686
|
+
this.psIdx = /* @__PURE__ */ new Map([[psKey({}), 0]]);
|
|
4687
|
+
this.bfData = [];
|
|
4688
|
+
this.bfIdx = /* @__PURE__ */ new Map();
|
|
4689
|
+
this.addBorderFill(this.DEF_STROKE);
|
|
4690
|
+
}
|
|
4691
|
+
font(name) {
|
|
4692
|
+
const n = name || "Malgun Gothic";
|
|
4693
|
+
if (this.fontIdx.has(n)) return this.fontIdx.get(n);
|
|
4694
|
+
const id = this.fonts.length;
|
|
4695
|
+
this.fonts.push(n);
|
|
4696
|
+
this.fontIdx.set(n, id);
|
|
4697
|
+
return id;
|
|
4698
|
+
}
|
|
4699
|
+
addCharShape(p) {
|
|
4700
|
+
const k = csKey(p);
|
|
4701
|
+
if (this.csIdx.has(k)) return this.csIdx.get(k);
|
|
4702
|
+
const id = this.csProps.length;
|
|
4703
|
+
this.csProps.push(p);
|
|
4704
|
+
this.csIdx.set(k, id);
|
|
4705
|
+
if (p.font) this.font(p.font);
|
|
4706
|
+
return id;
|
|
4707
|
+
}
|
|
4708
|
+
addParaShape(p) {
|
|
4709
|
+
const k = psKey(p);
|
|
4710
|
+
if (this.psIdx.has(k)) return this.psIdx.get(k);
|
|
4711
|
+
const id = this.psProps.length;
|
|
4712
|
+
this.psProps.push(p);
|
|
4713
|
+
this.psIdx.set(k, id);
|
|
4714
|
+
return id;
|
|
4715
|
+
}
|
|
4716
|
+
/** Returns 1-based border fill ID */
|
|
4717
|
+
addBorderFill(s, bg) {
|
|
4718
|
+
const k = bfKey(s, bg);
|
|
4719
|
+
if (this.bfIdx.has(k)) return this.bfIdx.get(k);
|
|
4720
|
+
const id = this.bfData.length + 1;
|
|
4721
|
+
this.bfData.push({ uniform: true, s, bg });
|
|
4722
|
+
this.bfIdx.set(k, id);
|
|
4723
|
+
return id;
|
|
4724
|
+
}
|
|
4725
|
+
addBorderFillPerSide(l, r, t, b, bg) {
|
|
4726
|
+
const k = bfPerSideKey(l, r, t, b, bg);
|
|
4727
|
+
if (this.bfIdx.has(k)) return this.bfIdx.get(k);
|
|
4728
|
+
const id = this.bfData.length + 1;
|
|
4729
|
+
this.bfData.push({ uniform: false, l, r, t, b, bg });
|
|
4730
|
+
this.bfIdx.set(k, id);
|
|
4731
|
+
return id;
|
|
4732
|
+
}
|
|
4733
|
+
};
|
|
4734
|
+
function collectNode(node, col) {
|
|
4735
|
+
if (node.tag === "para") {
|
|
4736
|
+
col.addParaShape(node.props);
|
|
4737
|
+
for (const kid of node.kids) {
|
|
4738
|
+
if (kid.tag === "span") col.addCharShape(kid.props);
|
|
4739
|
+
}
|
|
4740
|
+
} else if (node.tag === "grid") {
|
|
4741
|
+
if (node.props.defaultStroke) col.addBorderFill(node.props.defaultStroke);
|
|
4742
|
+
for (const row of node.kids) {
|
|
4743
|
+
for (const cell of row.kids) {
|
|
4744
|
+
const defStroke = node.props.defaultStroke ?? col.DEF_STROKE;
|
|
4745
|
+
const cp = cell.props;
|
|
4746
|
+
if (cp.top || cp.bot || cp.left || cp.right) {
|
|
4747
|
+
col.addBorderFillPerSide(
|
|
4748
|
+
cp.left ?? defStroke,
|
|
4749
|
+
cp.right ?? defStroke,
|
|
4750
|
+
cp.top ?? defStroke,
|
|
4751
|
+
cp.bot ?? defStroke,
|
|
4752
|
+
cp.bg
|
|
4753
|
+
);
|
|
4754
|
+
} else {
|
|
4755
|
+
col.addBorderFill(defStroke, cp.bg);
|
|
4756
|
+
}
|
|
4757
|
+
for (const para of cell.kids) collectNode(para, col);
|
|
4758
|
+
}
|
|
4759
|
+
}
|
|
4760
|
+
}
|
|
4761
|
+
}
|
|
4762
|
+
function mkDocumentProperties() {
|
|
4763
|
+
return new BufWriter().u16(1).u16(1).u16(1).u16(1).u16(1).u16(1).u16(1).u32(0).u32(0).u32(0).build();
|
|
4764
|
+
}
|
|
4765
|
+
function mkIdMappings(col, nBinData = 0) {
|
|
4766
|
+
const w = new BufWriter();
|
|
4767
|
+
w.u32(nBinData);
|
|
4768
|
+
for (let i = 0; i < 7; i++) w.u32(col.fonts.length);
|
|
4769
|
+
w.u32(col.bfData.length);
|
|
4770
|
+
w.u32(col.csProps.length);
|
|
4771
|
+
w.u32(0);
|
|
4772
|
+
w.u32(0);
|
|
4773
|
+
w.u32(0);
|
|
4774
|
+
w.u32(col.psProps.length);
|
|
4775
|
+
w.u32(0);
|
|
4776
|
+
w.u32(0);
|
|
4777
|
+
w.u32(0);
|
|
4778
|
+
w.u32(0);
|
|
4779
|
+
return w.build();
|
|
4780
|
+
}
|
|
4781
|
+
function mkFaceName(name) {
|
|
4782
|
+
return new BufWriter().u8(0).u16(name.length).utf16(name).u8(0).u16(0).zeros(10).u16(0).build();
|
|
4783
|
+
}
|
|
4784
|
+
function borderWidthIdx(pt) {
|
|
4785
|
+
let best = 0;
|
|
4786
|
+
for (let i = 0; i < BORDER_W_PT2.length; i++) {
|
|
4787
|
+
if (Math.abs(BORDER_W_PT2[i] - pt) < Math.abs(BORDER_W_PT2[best] - pt)) best = i;
|
|
4788
|
+
}
|
|
4789
|
+
return best;
|
|
4790
|
+
}
|
|
4791
|
+
function mkBorderFill(s, bg) {
|
|
4792
|
+
const w = new BufWriter();
|
|
4793
|
+
const t = BORDER_KIND_IDX[s.kind] ?? 0;
|
|
4794
|
+
const wi = borderWidthIdx(s.pt);
|
|
4795
|
+
const col = s.color || "000000";
|
|
4796
|
+
w.u16(0);
|
|
4797
|
+
for (let i = 0; i < 4; i++) w.u8(t);
|
|
4798
|
+
for (let i = 0; i < 4; i++) w.u8(wi);
|
|
4799
|
+
for (let i = 0; i < 4; i++) w.colorRef(col);
|
|
4800
|
+
w.u8(0).u8(0).colorRef("000000");
|
|
4801
|
+
if (bg) {
|
|
4802
|
+
w.u32(1);
|
|
4803
|
+
w.colorRef(bg);
|
|
4804
|
+
w.colorRef("FFFFFF");
|
|
4805
|
+
w.u32(0);
|
|
4806
|
+
} else {
|
|
4807
|
+
w.u32(0);
|
|
4808
|
+
}
|
|
4809
|
+
return w.build();
|
|
4810
|
+
}
|
|
4811
|
+
function mkBorderFillPerSide(left, right, top, bottom, bg) {
|
|
4812
|
+
const w = new BufWriter();
|
|
4813
|
+
w.u16(0);
|
|
4814
|
+
w.u8(BORDER_KIND_IDX[left.kind] ?? 0);
|
|
4815
|
+
w.u8(BORDER_KIND_IDX[right.kind] ?? 0);
|
|
4816
|
+
w.u8(BORDER_KIND_IDX[top.kind] ?? 0);
|
|
4817
|
+
w.u8(BORDER_KIND_IDX[bottom.kind] ?? 0);
|
|
4818
|
+
w.u8(borderWidthIdx(left.pt));
|
|
4819
|
+
w.u8(borderWidthIdx(right.pt));
|
|
4820
|
+
w.u8(borderWidthIdx(top.pt));
|
|
4821
|
+
w.u8(borderWidthIdx(bottom.pt));
|
|
4822
|
+
w.colorRef(left.color || "000000");
|
|
4823
|
+
w.colorRef(right.color || "000000");
|
|
4824
|
+
w.colorRef(top.color || "000000");
|
|
4825
|
+
w.colorRef(bottom.color || "000000");
|
|
4826
|
+
w.u8(0).u8(0).colorRef("000000");
|
|
4827
|
+
if (bg) {
|
|
4828
|
+
w.u32(1).colorRef(bg).colorRef("FFFFFF").u32(0);
|
|
4829
|
+
} else {
|
|
4830
|
+
w.u32(0);
|
|
4831
|
+
}
|
|
4832
|
+
return w.build();
|
|
4833
|
+
}
|
|
4834
|
+
function mkCharShape(p, col) {
|
|
4835
|
+
const fontId = p.font ? col.font(p.font) : 0;
|
|
4836
|
+
const height = Math.round((p.pt ?? 10) * 100);
|
|
4837
|
+
let attr = 0;
|
|
4838
|
+
if (p.i) attr |= 1 << 0;
|
|
4839
|
+
if (p.b) attr |= 1 << 1;
|
|
4840
|
+
if (p.u) attr |= 1 << 2;
|
|
4841
|
+
if (p.s) attr |= 1 << 18;
|
|
4842
|
+
if (p.sup) attr |= 1 << 16;
|
|
4843
|
+
if (p.sub) attr |= 2 << 16;
|
|
4844
|
+
const textColor = p.color ?? "000000";
|
|
4845
|
+
const w = new BufWriter();
|
|
4846
|
+
for (let i = 0; i < 7; i++) w.u16(fontId);
|
|
4847
|
+
for (let i = 0; i < 7; i++) w.u8(100);
|
|
4848
|
+
for (let i = 0; i < 7; i++) w.u8(0);
|
|
4849
|
+
for (let i = 0; i < 7; i++) w.u8(100);
|
|
4850
|
+
for (let i = 0; i < 7; i++) w.u8(0);
|
|
4851
|
+
w.i32(height);
|
|
4852
|
+
w.u32(attr);
|
|
4853
|
+
w.u8(0).u8(0);
|
|
4854
|
+
w.colorRef(textColor);
|
|
4855
|
+
w.colorRef("000000");
|
|
4856
|
+
w.colorRef("FFFFFF");
|
|
4857
|
+
w.colorRef("000000");
|
|
4858
|
+
w.u16(0);
|
|
4859
|
+
w.colorRef("000000");
|
|
4860
|
+
return w.build();
|
|
4861
|
+
}
|
|
4862
|
+
function mkParaShape(p) {
|
|
4863
|
+
const alignVal = ALIGN_CODE[p.align ?? "left"] ?? 1;
|
|
4864
|
+
const attr1 = (alignVal & 7) << 2;
|
|
4865
|
+
const lineSpacePct = p.lineHeight ? Math.round(p.lineHeight * 100) : 160;
|
|
4866
|
+
return new BufWriter().u32(attr1).i32(Metric.ptToHwp(p.indentPt ?? 0)).i32(0).i32(0).i32(Metric.ptToHwp(p.spaceBefore ?? 0)).i32(Metric.ptToHwp(p.spaceAfter ?? 0)).i32(lineSpacePct).u16(0).u16(0).u16(0).i16(0).i16(0).i16(0).i16(0).u32(0).u32(0).u32(lineSpacePct).build();
|
|
4867
|
+
}
|
|
4868
|
+
function mkBinData(id, ext) {
|
|
4869
|
+
const w = new BufWriter();
|
|
4870
|
+
w.u16(2);
|
|
4871
|
+
w.u16(id);
|
|
4872
|
+
w.u16(ext.length);
|
|
4873
|
+
w.utf16(ext);
|
|
4874
|
+
return w.build();
|
|
4875
|
+
}
|
|
4876
|
+
function buildDocInfoStream(col, images = []) {
|
|
4877
|
+
const chunks = [];
|
|
4878
|
+
chunks.push(mkRec(TAG_DOCUMENT_PROPERTIES, 0, mkDocumentProperties()));
|
|
4879
|
+
chunks.push(mkRec(TAG_ID_MAPPINGS, 1, mkIdMappings(col, images.length)));
|
|
4880
|
+
for (const img of images) {
|
|
4881
|
+
chunks.push(mkRec(TAG_BIN_DATA, 1, mkBinData(img.id, img.ext)));
|
|
4882
|
+
}
|
|
4883
|
+
for (let cat = 0; cat < 7; cat++) {
|
|
4884
|
+
for (const name of col.fonts) {
|
|
4885
|
+
chunks.push(mkRec(TAG_FACE_NAME2, 1, mkFaceName(name)));
|
|
4886
|
+
}
|
|
4887
|
+
}
|
|
4888
|
+
for (const entry of col.bfData) {
|
|
4889
|
+
chunks.push(mkRec(
|
|
4890
|
+
TAG_BORDER_FILL2,
|
|
4891
|
+
1,
|
|
4892
|
+
entry.uniform ? mkBorderFill(entry.s, entry.bg) : mkBorderFillPerSide(entry.l, entry.r, entry.t, entry.b, entry.bg)
|
|
4893
|
+
));
|
|
4894
|
+
}
|
|
4895
|
+
for (const p of col.csProps) {
|
|
4896
|
+
chunks.push(mkRec(TAG_CHAR_SHAPE2, 1, mkCharShape(p, col)));
|
|
4897
|
+
}
|
|
4898
|
+
for (const p of col.psProps) {
|
|
4899
|
+
chunks.push(mkRec(TAG_PARA_SHAPE2, 1, mkParaShape(p)));
|
|
4900
|
+
}
|
|
4901
|
+
return concatU8(chunks);
|
|
4902
|
+
}
|
|
4903
|
+
function mkPageDef(dims) {
|
|
4904
|
+
return new BufWriter().u32(Metric.ptToHwp(dims.wPt)).u32(Metric.ptToHwp(dims.hPt)).u32(Metric.ptToHwp(dims.ml)).u32(Metric.ptToHwp(dims.mr)).u32(Metric.ptToHwp(dims.mt)).u32(Metric.ptToHwp(dims.mb)).zeros(12).u32(dims.orient === "landscape" ? 1 : 0).build();
|
|
4905
|
+
}
|
|
4906
|
+
function mkParaHeader(nchars, ctrlMask, psId, csCount, lineAlignCount = 0, instanceId = 0) {
|
|
4907
|
+
return new BufWriter().u32(nchars).u32(ctrlMask).u16(psId).u8(0).u8(0).u16(csCount).u16(0).u16(lineAlignCount).u32(instanceId).u16(0).build();
|
|
4908
|
+
}
|
|
4909
|
+
function mkParaText(text) {
|
|
4910
|
+
const w = new BufWriter();
|
|
4911
|
+
for (let i = 0; i < text.length; i++) {
|
|
4912
|
+
const c = text.charCodeAt(i);
|
|
4913
|
+
w.u16(c < 32 ? 0 : c);
|
|
4914
|
+
}
|
|
4915
|
+
w.u16(13);
|
|
4916
|
+
return w.build();
|
|
4917
|
+
}
|
|
4918
|
+
function mkParaCharShape(pairs) {
|
|
4919
|
+
const w = new BufWriter();
|
|
4920
|
+
for (const [pos, id] of pairs) w.u32(pos).u32(id);
|
|
4921
|
+
return w.build();
|
|
4922
|
+
}
|
|
4923
|
+
function mkSecdParaText() {
|
|
4924
|
+
const lo = CTRL_SECD & 65535;
|
|
4925
|
+
const hi = CTRL_SECD >>> 16 & 65535;
|
|
4926
|
+
return new BufWriter().u16(2).u16(lo).u16(hi).u16(0).u16(0).u16(0).u16(0).u16(2).u16(13).build();
|
|
4927
|
+
}
|
|
4928
|
+
function mkTableParaText() {
|
|
4929
|
+
const lo = CTRL_TABLE2 & 65535;
|
|
4930
|
+
const hi = CTRL_TABLE2 >>> 16 & 65535;
|
|
4931
|
+
return new BufWriter().u16(11).u16(lo).u16(hi).u16(0).u16(0).u16(0).u16(0).u16(11).u16(13).build();
|
|
4932
|
+
}
|
|
4933
|
+
function mkPicParaText() {
|
|
4934
|
+
const lo = CTRL_PIC & 65535;
|
|
4935
|
+
const hi = CTRL_PIC >>> 16 & 65535;
|
|
4936
|
+
return new BufWriter().u16(11).u16(lo).u16(hi).u16(0).u16(0).u16(0).u16(0).u16(11).u16(13).build();
|
|
4937
|
+
}
|
|
4938
|
+
function mkShapeComponentPicture(binDataId, wHwp, hHwp) {
|
|
4939
|
+
const w = new BufWriter();
|
|
4940
|
+
w.u32(CTRL_PIC);
|
|
4941
|
+
w.zeros(15);
|
|
4942
|
+
w.u32(0);
|
|
4943
|
+
w.u32(0);
|
|
4944
|
+
w.u32(wHwp);
|
|
4945
|
+
w.u32(hHwp);
|
|
4946
|
+
w.u32(0);
|
|
4947
|
+
w.u32(0);
|
|
4948
|
+
w.u32(wHwp);
|
|
4949
|
+
w.u32(hHwp);
|
|
4950
|
+
w.zeros(20);
|
|
4951
|
+
w.zeros(16);
|
|
4952
|
+
w.u16(binDataId);
|
|
4953
|
+
w.u8(0);
|
|
4954
|
+
w.u8(0);
|
|
4955
|
+
w.u8(0);
|
|
4956
|
+
w.zeros(5);
|
|
4957
|
+
return w.build();
|
|
4958
|
+
}
|
|
4959
|
+
function encodePicPara(imgNode, binDataId, col, lv, idGen) {
|
|
4960
|
+
const wHwp = Metric.ptToHwp(Math.max(imgNode.w, 10));
|
|
4961
|
+
const hHwp = Metric.ptToHwp(Math.max(imgNode.h, 10));
|
|
4962
|
+
const TABLE_CTRL_MASK = 1 << 11;
|
|
4963
|
+
const instanceId = idGen();
|
|
4964
|
+
const psId = col.addParaShape({});
|
|
4965
|
+
return [
|
|
4966
|
+
mkRec(TAG_PARA_HEADER2, lv, mkParaHeader(9, TABLE_CTRL_MASK, psId, 1, 0, instanceId)),
|
|
4967
|
+
mkRec(TAG_PARA_TEXT2, lv + 1, mkPicParaText()),
|
|
4968
|
+
mkRec(TAG_PARA_CHAR_SHAPE2, lv + 1, mkParaCharShape([[0, 0]])),
|
|
4969
|
+
// $pic CTRL_HEADER (GHDR) at level lv+1
|
|
4970
|
+
mkRec(TAG_CTRL_HEADER2, lv + 1, mkPicCtrl(wHwp, hHwp, idGen())),
|
|
4971
|
+
// SHAPE_COMPONENT_PICTURE at level lv+2
|
|
4972
|
+
mkRec(TAG_SHAPE_COMPONENT_PICTURE, lv + 2, mkShapeComponentPicture(binDataId, wHwp, hHwp))
|
|
4973
|
+
];
|
|
4974
|
+
}
|
|
4975
|
+
function encodePara4(para, col, lv, instanceId) {
|
|
4976
|
+
let text = "";
|
|
4977
|
+
const csPairs = [];
|
|
4978
|
+
let pos = 0;
|
|
4979
|
+
for (const kid of para.kids) {
|
|
4980
|
+
if (kid.tag === "span") {
|
|
4981
|
+
const span = kid;
|
|
4982
|
+
const csId = col.addCharShape(span.props);
|
|
4983
|
+
if (csPairs.length === 0 || csPairs[csPairs.length - 1][1] !== csId) {
|
|
4984
|
+
csPairs.push([pos, csId]);
|
|
4985
|
+
}
|
|
4986
|
+
for (const t of span.kids) {
|
|
4987
|
+
if (t.tag === "txt") {
|
|
4988
|
+
text += t.content;
|
|
4989
|
+
pos += t.content.length;
|
|
4990
|
+
}
|
|
4991
|
+
}
|
|
4992
|
+
}
|
|
4993
|
+
}
|
|
4994
|
+
if (csPairs.length === 0) csPairs.push([0, 0]);
|
|
4995
|
+
const psId = col.addParaShape(para.props);
|
|
4996
|
+
const nchars = text.length + 1;
|
|
4997
|
+
return [
|
|
4998
|
+
mkRec(TAG_PARA_HEADER2, lv, mkParaHeader(nchars, 0, psId, csPairs.length, 0, instanceId)),
|
|
4999
|
+
mkRec(TAG_PARA_TEXT2, lv + 1, mkParaText(text)),
|
|
5000
|
+
mkRec(TAG_PARA_CHAR_SHAPE2, lv + 1, mkParaCharShape(csPairs))
|
|
5001
|
+
];
|
|
5002
|
+
}
|
|
5003
|
+
function mkTableCtrl(wHwp, hHwp, instanceId) {
|
|
5004
|
+
return new BufWriter().u32(CTRL_TABLE2).u32(136978961).i32(0).i32(0).u32(wHwp).u32(hHwp).i32(7).u16(140).u16(140).u16(140).u16(140).u32(instanceId).i32(0).u16(0).build();
|
|
5005
|
+
}
|
|
5006
|
+
function mkPicCtrl(wHwp, hHwp, instanceId) {
|
|
5007
|
+
return new BufWriter().u32(CTRL_PIC).u32(136978961).i32(0).i32(0).u32(wHwp).u32(hHwp).i32(0).u16(0).u16(0).u16(0).u16(0).u32(instanceId).i32(0).u16(0).build();
|
|
5008
|
+
}
|
|
5009
|
+
function mkTableRecord(rowCnt, colCnt, rowHwp, bfId) {
|
|
5010
|
+
const w = new BufWriter();
|
|
5011
|
+
w.u32(67108870);
|
|
5012
|
+
w.u16(rowCnt);
|
|
5013
|
+
w.u16(colCnt);
|
|
5014
|
+
w.u16(0);
|
|
5015
|
+
w.u16(510);
|
|
5016
|
+
w.u16(510);
|
|
5017
|
+
w.u16(141);
|
|
5018
|
+
w.u16(141);
|
|
5019
|
+
for (const h of rowHwp) w.u16(Math.max(1, h & 65535));
|
|
5020
|
+
w.u16(bfId);
|
|
5021
|
+
w.u16(0);
|
|
5022
|
+
return w.build();
|
|
5023
|
+
}
|
|
5024
|
+
function mkCellListHeader(paraCount, row, col, rs, cs, wHwp, hHwp, bfId) {
|
|
5025
|
+
return new BufWriter().u16(paraCount).u32(0).u16(0).u16(col).u16(row).u16(rs).u16(cs).u32(wHwp).u32(hHwp).u16(510).u16(510).u16(141).u16(141).u16(bfId).zeros(13).build();
|
|
5026
|
+
}
|
|
5027
|
+
var DEFAULT_ROW_HEIGHT_PT = 14;
|
|
5028
|
+
function encodeGrid5(grid, col, lv, idGen) {
|
|
5029
|
+
const records = [];
|
|
5030
|
+
const rowCnt = grid.kids.length;
|
|
5031
|
+
const colCnt = Math.max(1, grid.kids[0]?.kids.length ?? 1);
|
|
5032
|
+
const cwPt = grid.props.colWidths ?? [];
|
|
5033
|
+
const totalPt = cwPt.reduce((s, w) => s + w, 0) || 453;
|
|
5034
|
+
const defColPt = totalPt / colCnt;
|
|
5035
|
+
const defStroke = grid.props.defaultStroke ?? col.DEF_STROKE;
|
|
5036
|
+
const defBfId = col.addBorderFill(defStroke);
|
|
5037
|
+
const rowHwp = grid.kids.map((row) => row.heightPt != null && row.heightPt > 0 ? Metric.ptToHwp(row.heightPt) : Metric.ptToHwp(DEFAULT_ROW_HEIGHT_PT));
|
|
5038
|
+
const totalWPt = cwPt.length > 0 ? cwPt.reduce((s, w) => s + w, 0) : totalPt;
|
|
5039
|
+
const totalHPt = grid.kids.reduce((s, row) => s + (row.heightPt != null && row.heightPt > 0 ? row.heightPt : DEFAULT_ROW_HEIGHT_PT), 0);
|
|
5040
|
+
const tblWHwp = Metric.ptToHwp(totalWPt);
|
|
5041
|
+
const tblHHwp = Metric.ptToHwp(totalHPt);
|
|
5042
|
+
const tblInstanceId = idGen();
|
|
5043
|
+
records.push(mkRec(TAG_CTRL_HEADER2, lv, mkTableCtrl(tblWHwp, tblHHwp, tblInstanceId)));
|
|
5044
|
+
records.push(mkRec(TAG_TABLE, lv + 1, mkTableRecord(rowCnt, colCnt, rowHwp, defBfId)));
|
|
5045
|
+
for (let r = 0; r < grid.kids.length; r++) {
|
|
5046
|
+
for (let c = 0; c < grid.kids[r].kids.length; c++) {
|
|
5047
|
+
const cell = grid.kids[r].kids[c];
|
|
5048
|
+
const wHwp = Metric.ptToHwp(cwPt[c] ?? defColPt);
|
|
5049
|
+
const hHwp = rowHwp[r];
|
|
5050
|
+
const cp = cell.props;
|
|
5051
|
+
const hasPerSide = cp.top || cp.bot || cp.left || cp.right;
|
|
5052
|
+
const bfId = hasPerSide ? col.addBorderFillPerSide(
|
|
5053
|
+
cp.left ?? defStroke,
|
|
5054
|
+
cp.right ?? defStroke,
|
|
5055
|
+
cp.top ?? defStroke,
|
|
5056
|
+
cp.bot ?? defStroke,
|
|
5057
|
+
cp.bg
|
|
5058
|
+
) : col.addBorderFill(defStroke, cp.bg);
|
|
5059
|
+
const paras = cell.kids.length > 0 ? cell.kids : [{ tag: "para", props: {}, kids: [] }];
|
|
5060
|
+
records.push(mkRec(
|
|
5061
|
+
TAG_LIST_HEADER2,
|
|
5062
|
+
lv + 1,
|
|
5063
|
+
mkCellListHeader(paras.length, r, c, cell.rs, cell.cs, wHwp, hHwp, bfId)
|
|
5064
|
+
));
|
|
5065
|
+
for (const para of paras) {
|
|
5066
|
+
records.push(...encodePara4(para, col, lv + 2, idGen()));
|
|
5067
|
+
}
|
|
5068
|
+
}
|
|
5069
|
+
}
|
|
5070
|
+
return records;
|
|
5071
|
+
}
|
|
5072
|
+
function mkSectionCtrl() {
|
|
5073
|
+
return new BufWriter().u32(CTRL_SECD).u32(0).u32(1134).u16(16384).u16(31).zeros(31).build();
|
|
5074
|
+
}
|
|
5075
|
+
function buildSectionParagraph(dims, instanceId) {
|
|
5076
|
+
const SECD_CTRL_MASK = 1 << 2;
|
|
5077
|
+
const nchars = 9;
|
|
5078
|
+
return [
|
|
5079
|
+
mkRec(TAG_PARA_HEADER2, 0, mkParaHeader(nchars, SECD_CTRL_MASK, 0, 1, 1, instanceId)),
|
|
5080
|
+
mkRec(TAG_PARA_TEXT2, 1, mkSecdParaText()),
|
|
5081
|
+
mkRec(TAG_PARA_CHAR_SHAPE2, 1, mkParaCharShape([[0, 0]])),
|
|
5082
|
+
mkRec(TAG_PARA_LINE_SEG, 1, new Uint8Array(36)),
|
|
5083
|
+
// 1 line × 36 bytes = 36 bytes
|
|
5084
|
+
mkRec(TAG_CTRL_HEADER2, 1, mkSectionCtrl()),
|
|
5085
|
+
// 'secd' at level 1
|
|
5086
|
+
mkRec(TAG_PAGE_DEF2, 2, mkPageDef(dims)),
|
|
5087
|
+
// page def at level 2
|
|
5088
|
+
mkRec(TAG_FOOTNOTE_SHAPE, 2, new Uint8Array(28)),
|
|
5089
|
+
// footnote shape (defaults)
|
|
5090
|
+
mkRec(TAG_FOOTNOTE_SHAPE, 2, new Uint8Array(28))
|
|
5091
|
+
// endnote shape (defaults)
|
|
5092
|
+
];
|
|
5093
|
+
}
|
|
5094
|
+
function buildBodyTextStream(doc, col, images) {
|
|
5095
|
+
const chunks = [];
|
|
5096
|
+
const dims = doc.kids[0]?.dims ?? A4;
|
|
5097
|
+
let instanceIdCounter = 1;
|
|
5098
|
+
const idGen = () => instanceIdCounter++;
|
|
5099
|
+
for (const r of buildSectionParagraph(dims, idGen())) chunks.push(r);
|
|
5100
|
+
const TABLE_CTRL_MASK = 1 << 11;
|
|
5101
|
+
for (const sheet of doc.kids) {
|
|
5102
|
+
for (const node of sheet.kids) {
|
|
5103
|
+
if (node.tag === "para") {
|
|
5104
|
+
const para = node;
|
|
5105
|
+
const hasImg = para.kids.some((k) => k.tag === "img");
|
|
5106
|
+
if (hasImg) {
|
|
5107
|
+
for (const kid of para.kids) {
|
|
5108
|
+
if (kid.tag === "img") {
|
|
5109
|
+
const img = kid;
|
|
5110
|
+
const binImg = images.find((b) => b64Matches(b, img.b64));
|
|
5111
|
+
if (binImg) {
|
|
5112
|
+
for (const r of encodePicPara(img, binImg.id, col, 0, idGen)) chunks.push(r);
|
|
5113
|
+
}
|
|
5114
|
+
}
|
|
5115
|
+
}
|
|
5116
|
+
const textKids = para.kids.filter((k) => k.tag !== "img");
|
|
5117
|
+
if (textKids.length > 0) {
|
|
5118
|
+
const textPara = { tag: "para", props: para.props, kids: textKids };
|
|
5119
|
+
for (const r of encodePara4(textPara, col, 0, idGen())) chunks.push(r);
|
|
5120
|
+
}
|
|
5121
|
+
} else {
|
|
5122
|
+
for (const r of encodePara4(para, col, 0, idGen())) chunks.push(r);
|
|
5123
|
+
}
|
|
5124
|
+
} else if (node.tag === "grid") {
|
|
5125
|
+
chunks.push(mkRec(TAG_PARA_HEADER2, 0, mkParaHeader(9, TABLE_CTRL_MASK, 0, 1, 0, idGen())));
|
|
5126
|
+
chunks.push(mkRec(TAG_PARA_TEXT2, 1, mkTableParaText()));
|
|
5127
|
+
chunks.push(mkRec(TAG_PARA_CHAR_SHAPE2, 1, mkParaCharShape([[0, 0]])));
|
|
5128
|
+
for (const r of encodeGrid5(node, col, 1, idGen)) chunks.push(r);
|
|
5129
|
+
}
|
|
5130
|
+
}
|
|
5131
|
+
}
|
|
5132
|
+
return concatU8(chunks);
|
|
5133
|
+
}
|
|
5134
|
+
function b64Matches(binImg, b64) {
|
|
5135
|
+
const a = TextKit.base64Encode(binImg.data).replace(/\s/g, "");
|
|
5136
|
+
const b = b64.replace(/\s/g, "");
|
|
5137
|
+
return a === b;
|
|
5138
|
+
}
|
|
5139
|
+
function buildHwpFileHeader() {
|
|
5140
|
+
const buf = new Uint8Array(256);
|
|
5141
|
+
const sig = "HWP Document File";
|
|
5142
|
+
for (let i = 0; i < sig.length; i++) buf[i] = sig.charCodeAt(i);
|
|
5143
|
+
const dv = new DataView(buf.buffer);
|
|
5144
|
+
dv.setUint32(32, 83886848, true);
|
|
5145
|
+
dv.setUint32(36, 1, true);
|
|
5146
|
+
return buf;
|
|
5147
|
+
}
|
|
5148
|
+
function buildHwpOle2(fileHeaderData, docInfoData, section0Data, binImages = []) {
|
|
5149
|
+
const SS = 512;
|
|
5150
|
+
const ENDOFCHAIN = 4294967294;
|
|
5151
|
+
const FREESECT = 4294967295;
|
|
5152
|
+
const FATSECT = 4294967293;
|
|
5153
|
+
function padSector(d) {
|
|
5154
|
+
const n = Math.ceil(Math.max(d.length, 1) / SS) * SS;
|
|
5155
|
+
if (d.length === n) return d;
|
|
5156
|
+
const out2 = new Uint8Array(n);
|
|
5157
|
+
out2.set(d);
|
|
5158
|
+
return out2;
|
|
5159
|
+
}
|
|
5160
|
+
const fhPad = padSector(fileHeaderData);
|
|
5161
|
+
const diPad = padSector(docInfoData);
|
|
5162
|
+
const s0Pad = padSector(section0Data);
|
|
5163
|
+
const imgPads = binImages.map((img) => padSector(img.data));
|
|
5164
|
+
const fhN = fhPad.length / SS;
|
|
5165
|
+
const diN = diPad.length / SS;
|
|
5166
|
+
const s0N = s0Pad.length / SS;
|
|
5167
|
+
const imgNs = imgPads.map((p) => p.length / SS);
|
|
5168
|
+
const totalImgN = imgNs.reduce((s, n) => s + n, 0);
|
|
5169
|
+
const numDirEntries = 5 + (binImages.length > 0 ? 1 + binImages.length : 0);
|
|
5170
|
+
const dirN = Math.max(2, Math.ceil(numDirEntries / 4));
|
|
5171
|
+
let fatN = 1;
|
|
5172
|
+
for (let iter = 0; iter < 10; iter++) {
|
|
5173
|
+
const total = fatN + dirN + fhN + diN + s0N + totalImgN;
|
|
5174
|
+
const needed = Math.ceil(total / 128);
|
|
5175
|
+
if (needed <= fatN) break;
|
|
5176
|
+
fatN = needed;
|
|
5177
|
+
}
|
|
5178
|
+
const dir1Sec = fatN;
|
|
5179
|
+
const fhSec = fatN + dirN;
|
|
5180
|
+
const diSec = fhSec + fhN;
|
|
5181
|
+
const s0Sec = diSec + diN;
|
|
5182
|
+
const imgSecs = [];
|
|
5183
|
+
let curSec = s0Sec + s0N;
|
|
5184
|
+
for (const n of imgNs) {
|
|
5185
|
+
imgSecs.push(curSec);
|
|
5186
|
+
curSec += n;
|
|
5187
|
+
}
|
|
5188
|
+
const totalSec = curSec;
|
|
5189
|
+
const fatBuf = new Uint8Array(fatN * SS).fill(255);
|
|
5190
|
+
const setFat = (i, v) => {
|
|
5191
|
+
fatBuf[i * 4] = v & 255;
|
|
5192
|
+
fatBuf[i * 4 + 1] = v >>> 8 & 255;
|
|
5193
|
+
fatBuf[i * 4 + 2] = v >>> 16 & 255;
|
|
5194
|
+
fatBuf[i * 4 + 3] = v >>> 24 & 255;
|
|
5195
|
+
};
|
|
5196
|
+
for (let i = 0; i < fatN; i++) setFat(i, FATSECT);
|
|
5197
|
+
for (let i = 0; i < dirN; i++) setFat(dir1Sec + i, i + 1 < dirN ? dir1Sec + i + 1 : ENDOFCHAIN);
|
|
5198
|
+
for (let i = 0; i < fhN; i++) setFat(fhSec + i, i + 1 < fhN ? fhSec + i + 1 : ENDOFCHAIN);
|
|
5199
|
+
for (let i = 0; i < diN; i++) setFat(diSec + i, i + 1 < diN ? diSec + i + 1 : ENDOFCHAIN);
|
|
5200
|
+
for (let i = 0; i < s0N; i++) setFat(s0Sec + i, i + 1 < s0N ? s0Sec + i + 1 : ENDOFCHAIN);
|
|
5201
|
+
for (let ii = 0; ii < imgNs.length; ii++) {
|
|
5202
|
+
const start = imgSecs[ii];
|
|
5203
|
+
const n = imgNs[ii];
|
|
5204
|
+
for (let i = 0; i < n; i++) setFat(start + i, i + 1 < n ? start + i + 1 : ENDOFCHAIN);
|
|
5205
|
+
}
|
|
5206
|
+
const dirBuf = new Uint8Array(dirN * SS);
|
|
5207
|
+
const dv = new DataView(dirBuf.buffer);
|
|
5208
|
+
function writeDirEntry(idx, name, type, left, right, child, startSec, size) {
|
|
5209
|
+
const base = idx * 128;
|
|
5210
|
+
const nl = Math.min(name.length, 31);
|
|
5211
|
+
for (let i = 0; i < nl; i++) dv.setUint16(base + i * 2, name.charCodeAt(i), true);
|
|
5212
|
+
dv.setUint16(base + 64, (nl + 1) * 2, true);
|
|
5213
|
+
dirBuf[base + 66] = type;
|
|
5214
|
+
dirBuf[base + 67] = 1;
|
|
5215
|
+
dv.setInt32(base + 68, left, true);
|
|
5216
|
+
dv.setInt32(base + 72, right, true);
|
|
5217
|
+
dv.setInt32(base + 76, child, true);
|
|
5218
|
+
dv.setUint32(base + 116, startSec >>> 0, true);
|
|
5219
|
+
dv.setUint32(base + 120, size >>> 0, true);
|
|
5220
|
+
}
|
|
5221
|
+
if (binImages.length > 0) {
|
|
5222
|
+
writeDirEntry(0, "Root Entry", 5, -1, -1, 1, ENDOFCHAIN, 0);
|
|
5223
|
+
writeDirEntry(1, "FileHeader", 2, -1, 2, -1, fhSec, fileHeaderData.length);
|
|
5224
|
+
writeDirEntry(2, "DocInfo", 2, -1, 3, -1, diSec, docInfoData.length);
|
|
5225
|
+
writeDirEntry(3, "BodyText", 1, -1, 5, 4, ENDOFCHAIN, 0);
|
|
5226
|
+
writeDirEntry(4, "Section0", 2, -1, -1, -1, s0Sec, section0Data.length);
|
|
5227
|
+
writeDirEntry(5, "BinData", 1, -1, -1, 6, ENDOFCHAIN, 0);
|
|
5228
|
+
for (let ii = 0; ii < binImages.length; ii++) {
|
|
5229
|
+
const img = binImages[ii];
|
|
5230
|
+
const streamName = `BIN${String(img.id).padStart(4, "0")}.${img.ext}`;
|
|
5231
|
+
const sibling = ii + 1 < binImages.length ? 7 + ii : -1;
|
|
5232
|
+
writeDirEntry(6 + ii, streamName, 2, -1, sibling, -1, imgSecs[ii], img.data.length);
|
|
5233
|
+
}
|
|
5234
|
+
} else {
|
|
5235
|
+
writeDirEntry(0, "Root Entry", 5, -1, -1, 1, ENDOFCHAIN, 0);
|
|
5236
|
+
writeDirEntry(1, "FileHeader", 2, -1, 2, -1, fhSec, fileHeaderData.length);
|
|
5237
|
+
writeDirEntry(2, "DocInfo", 2, -1, 3, -1, diSec, docInfoData.length);
|
|
5238
|
+
writeDirEntry(3, "BodyText", 1, -1, -1, 4, ENDOFCHAIN, 0);
|
|
5239
|
+
writeDirEntry(4, "Section0", 2, -1, -1, -1, s0Sec, section0Data.length);
|
|
5240
|
+
}
|
|
5241
|
+
const hdr = new Uint8Array(SS);
|
|
5242
|
+
const hdv = new DataView(hdr.buffer);
|
|
5243
|
+
const MAGIC = [208, 207, 17, 224, 161, 177, 26, 225];
|
|
5244
|
+
MAGIC.forEach((b, i) => {
|
|
5245
|
+
hdr[i] = b;
|
|
5246
|
+
});
|
|
5247
|
+
hdv.setUint16(24, 62, true);
|
|
5248
|
+
hdv.setUint16(26, 3, true);
|
|
5249
|
+
hdv.setUint16(28, 65534, true);
|
|
5250
|
+
hdv.setUint16(30, 9, true);
|
|
5251
|
+
hdv.setUint16(32, 6, true);
|
|
5252
|
+
hdv.setUint32(40, 0, true);
|
|
5253
|
+
hdv.setUint32(44, fatN, true);
|
|
5254
|
+
hdv.setUint32(48, dir1Sec, true);
|
|
5255
|
+
hdv.setUint32(52, 0, true);
|
|
5256
|
+
hdv.setUint32(56, 4096, true);
|
|
5257
|
+
hdv.setUint32(60, ENDOFCHAIN, true);
|
|
5258
|
+
hdv.setUint32(64, 0, true);
|
|
5259
|
+
hdv.setUint32(68, ENDOFCHAIN, true);
|
|
5260
|
+
hdv.setUint32(72, 0, true);
|
|
5261
|
+
for (let i = 0; i < 109; i++) {
|
|
5262
|
+
hdv.setUint32(76 + i * 4, i < fatN ? i : FREESECT, true);
|
|
5263
|
+
}
|
|
5264
|
+
const out = new Uint8Array(SS + totalSec * SS);
|
|
5265
|
+
out.set(hdr, 0);
|
|
5266
|
+
for (let i = 0; i < fatN; i++) {
|
|
5267
|
+
out.set(fatBuf.subarray(i * SS, (i + 1) * SS), SS + i * SS);
|
|
5268
|
+
}
|
|
5269
|
+
for (let i = 0; i < dirN; i++) {
|
|
5270
|
+
out.set(dirBuf.subarray(i * SS, (i + 1) * SS), SS + (dir1Sec + i) * SS);
|
|
5271
|
+
}
|
|
5272
|
+
out.set(fhPad, SS + fhSec * SS);
|
|
5273
|
+
out.set(diPad, SS + diSec * SS);
|
|
5274
|
+
out.set(s0Pad, SS + s0Sec * SS);
|
|
5275
|
+
for (let ii = 0; ii < imgPads.length; ii++) {
|
|
5276
|
+
out.set(imgPads[ii], SS + imgSecs[ii] * SS);
|
|
5277
|
+
}
|
|
5278
|
+
return out;
|
|
5279
|
+
}
|
|
5280
|
+
function concatU8(arrays) {
|
|
5281
|
+
const total = arrays.reduce((s, a) => s + a.length, 0);
|
|
5282
|
+
const out = new Uint8Array(total);
|
|
5283
|
+
let off = 0;
|
|
5284
|
+
for (const a of arrays) {
|
|
5285
|
+
out.set(a, off);
|
|
5286
|
+
off += a.length;
|
|
5287
|
+
}
|
|
5288
|
+
return out;
|
|
5289
|
+
}
|
|
5290
|
+
var HwpEncoder = class {
|
|
5291
|
+
constructor() {
|
|
5292
|
+
this.format = "hwp";
|
|
5293
|
+
}
|
|
5294
|
+
async encode(doc) {
|
|
5295
|
+
try {
|
|
5296
|
+
let collectImages3 = function(node) {
|
|
5297
|
+
if (node.tag === "para") {
|
|
5298
|
+
for (const kid of node.kids) {
|
|
5299
|
+
if (kid.tag === "img") {
|
|
5300
|
+
const key = kid.b64.substring(0, 50);
|
|
5301
|
+
if (!seenB64.has(key)) {
|
|
5302
|
+
seenB64.add(key);
|
|
5303
|
+
const raw = TextKit.base64Decode(kid.b64);
|
|
5304
|
+
let ext = "jpg";
|
|
5305
|
+
if (kid.mime === "image/png") ext = "png";
|
|
5306
|
+
else if (kid.mime === "image/gif") ext = "gif";
|
|
5307
|
+
else if (kid.mime === "image/bmp") ext = "bmp";
|
|
5308
|
+
images.push({ id: binIdCounter++, ext, data: new Uint8Array(raw) });
|
|
5309
|
+
}
|
|
5310
|
+
}
|
|
5311
|
+
}
|
|
5312
|
+
} else if (node.tag === "grid") {
|
|
5313
|
+
for (const row of node.kids) {
|
|
5314
|
+
for (const cell of row.kids) {
|
|
5315
|
+
for (const para of cell.kids) collectImages3(para);
|
|
5316
|
+
}
|
|
5317
|
+
}
|
|
5318
|
+
}
|
|
5319
|
+
};
|
|
5320
|
+
var collectImages2 = collectImages3;
|
|
5321
|
+
const col = new StyleCollector();
|
|
5322
|
+
for (const sheet of doc.kids) {
|
|
5323
|
+
for (const node of sheet.kids) collectNode(node, col);
|
|
5324
|
+
}
|
|
5325
|
+
const images = [];
|
|
5326
|
+
const seenB64 = /* @__PURE__ */ new Set();
|
|
5327
|
+
let binIdCounter = 1;
|
|
5328
|
+
for (const sheet of doc.kids) {
|
|
5329
|
+
for (const node of sheet.kids) collectImages3(node);
|
|
5330
|
+
}
|
|
5331
|
+
const docInfoRaw = buildDocInfoStream(col, images);
|
|
5332
|
+
const bodyRaw = buildBodyTextStream(doc, col, images);
|
|
5333
|
+
const docInfoCmp = import_pako3.default.deflate(docInfoRaw);
|
|
5334
|
+
const bodyCmp = import_pako3.default.deflate(bodyRaw);
|
|
5335
|
+
const fileHdr = buildHwpFileHeader();
|
|
5336
|
+
const hwp = buildHwpOle2(fileHdr, docInfoCmp, bodyCmp, images);
|
|
5337
|
+
return succeed(hwp);
|
|
5338
|
+
} catch (e) {
|
|
5339
|
+
return fail(`HwpEncoder: ${e instanceof Error ? e.message : String(e)}`);
|
|
5340
|
+
}
|
|
5341
|
+
}
|
|
5342
|
+
};
|
|
5343
|
+
registry.registerEncoder(new HwpEncoder());
|
|
5344
|
+
|
|
3456
5345
|
// src/walk/TreeWalker.ts
|
|
3457
5346
|
function walkNode(node, cb, parent = null, depth = 0) {
|
|
3458
5347
|
const result = cb(node, parent, depth);
|
|
@@ -3512,6 +5401,7 @@ function validateRoot(root) {
|
|
|
3512
5401
|
// Annotate the CommonJS export names for ESM import in node:
|
|
3513
5402
|
0 && (module.exports = {
|
|
3514
5403
|
A4,
|
|
5404
|
+
A4_LANDSCAPE,
|
|
3515
5405
|
ArchiveKit,
|
|
3516
5406
|
BinaryKit,
|
|
3517
5407
|
DEFAULT_STROKE,
|
|
@@ -3521,17 +5411,20 @@ function validateRoot(root) {
|
|
|
3521
5411
|
TextKit,
|
|
3522
5412
|
TreeWalker,
|
|
3523
5413
|
XmlKit,
|
|
5414
|
+
buildBr,
|
|
3524
5415
|
buildCell,
|
|
3525
5416
|
buildGrid,
|
|
3526
5417
|
buildImg,
|
|
3527
5418
|
buildPageNum,
|
|
3528
5419
|
buildPara,
|
|
5420
|
+
buildPb,
|
|
3529
5421
|
buildRoot,
|
|
3530
5422
|
buildRow,
|
|
3531
5423
|
buildSheet,
|
|
3532
5424
|
buildSpan,
|
|
3533
5425
|
countNodes,
|
|
3534
5426
|
fail,
|
|
5427
|
+
normalizeDims,
|
|
3535
5428
|
registry,
|
|
3536
5429
|
safeAlign,
|
|
3537
5430
|
safeFont,
|