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.mjs
CHANGED
|
@@ -43,6 +43,25 @@ var A4 = {
|
|
|
43
43
|
mr: 70.87,
|
|
44
44
|
orient: "portrait"
|
|
45
45
|
};
|
|
46
|
+
var A4_LANDSCAPE = {
|
|
47
|
+
wPt: 841.89,
|
|
48
|
+
hPt: 595.28,
|
|
49
|
+
mt: 56.69,
|
|
50
|
+
mb: 56.69,
|
|
51
|
+
ml: 70.87,
|
|
52
|
+
mr: 70.87,
|
|
53
|
+
orient: "landscape"
|
|
54
|
+
};
|
|
55
|
+
function normalizeDims(dims) {
|
|
56
|
+
const orient = dims.orient ?? "portrait";
|
|
57
|
+
if (orient === "landscape" && dims.wPt < dims.hPt) {
|
|
58
|
+
return { ...dims, wPt: dims.hPt, hPt: dims.wPt };
|
|
59
|
+
}
|
|
60
|
+
if (orient === "portrait" && dims.wPt > dims.hPt) {
|
|
61
|
+
return { ...dims, wPt: dims.hPt, hPt: dims.wPt };
|
|
62
|
+
}
|
|
63
|
+
return dims;
|
|
64
|
+
}
|
|
46
65
|
var DEFAULT_STROKE = { kind: "solid", pt: 0.5, color: "000000" };
|
|
47
66
|
|
|
48
67
|
// src/model/builders.ts
|
|
@@ -58,6 +77,12 @@ function buildSheet(kids = [], dims = A4, opts) {
|
|
|
58
77
|
function buildPageNum(format) {
|
|
59
78
|
return { tag: "pagenum", format };
|
|
60
79
|
}
|
|
80
|
+
function buildBr() {
|
|
81
|
+
return { tag: "br" };
|
|
82
|
+
}
|
|
83
|
+
function buildPb() {
|
|
84
|
+
return { tag: "pb" };
|
|
85
|
+
}
|
|
61
86
|
function buildPara(kids = [], props = {}) {
|
|
62
87
|
return { tag: "para", props, kids };
|
|
63
88
|
}
|
|
@@ -65,14 +90,19 @@ function buildSpan(content, props = {}) {
|
|
|
65
90
|
const txt = { tag: "txt", content };
|
|
66
91
|
return { tag: "span", props, kids: [txt] };
|
|
67
92
|
}
|
|
68
|
-
function buildImg(b64, mime, w, h, alt) {
|
|
69
|
-
|
|
93
|
+
function buildImg(b64, mime, w, h, alt, layout) {
|
|
94
|
+
const node = { tag: "img", b64, mime, w, h };
|
|
95
|
+
if (alt) node.alt = alt;
|
|
96
|
+
if (layout) node.layout = layout;
|
|
97
|
+
return node;
|
|
70
98
|
}
|
|
71
99
|
function buildGrid(kids, props = {}) {
|
|
72
100
|
return { tag: "grid", props, kids };
|
|
73
101
|
}
|
|
74
|
-
function buildRow(kids) {
|
|
75
|
-
|
|
102
|
+
function buildRow(kids, heightPt) {
|
|
103
|
+
const node = { tag: "row", kids };
|
|
104
|
+
if (heightPt != null) node.heightPt = heightPt;
|
|
105
|
+
return node;
|
|
76
106
|
}
|
|
77
107
|
function buildCell(kids, opts = {}) {
|
|
78
108
|
return { tag: "cell", cs: opts.cs ?? 1, rs: opts.rs ?? 1, props: opts.props ?? {}, kids };
|
|
@@ -202,7 +232,7 @@ var ALIGN_MAP = {
|
|
|
202
232
|
end: "right"
|
|
203
233
|
};
|
|
204
234
|
function safeAlign(raw) {
|
|
205
|
-
return ALIGN_MAP[raw ?? ""] ?? "
|
|
235
|
+
return ALIGN_MAP[raw ?? ""] ?? "left";
|
|
206
236
|
}
|
|
207
237
|
var HWPX_STROKE = {
|
|
208
238
|
SOLID: "solid",
|
|
@@ -683,7 +713,36 @@ function extractParaPrs(headObj) {
|
|
|
683
713
|
if (id < 0) continue;
|
|
684
714
|
const alignNode = pp?.["hh:align"]?.[0]?._attr ?? pp?.["hh:ALIGN"]?.[0]?._attr;
|
|
685
715
|
const align = alignNode?.horizontal ?? alignNode?.Horizontal;
|
|
686
|
-
|
|
716
|
+
let marginEl = pp?.["hh:margin"]?.[0] ?? null;
|
|
717
|
+
let lineSpEl = pp?.["hh:lineSpacing"]?.[0] ?? null;
|
|
718
|
+
if (!marginEl) {
|
|
719
|
+
const sw = pp?.["hp:switch"]?.[0];
|
|
720
|
+
const container = sw?.["hp:default"]?.[0] ?? sw?.["hp:case"]?.[0];
|
|
721
|
+
marginEl = container?.["hh:margin"]?.[0] ?? null;
|
|
722
|
+
lineSpEl = lineSpEl ?? container?.["hh:lineSpacing"]?.[0] ?? null;
|
|
723
|
+
}
|
|
724
|
+
let indentPt;
|
|
725
|
+
let spaceBefore;
|
|
726
|
+
let spaceAfter;
|
|
727
|
+
let lineHeight;
|
|
728
|
+
if (marginEl) {
|
|
729
|
+
const intentEl = marginEl?.["hc:intent"]?.[0] ?? marginEl?.["hc:indent"]?.[0];
|
|
730
|
+
const prevEl = marginEl?.["hc:prev"]?.[0];
|
|
731
|
+
const nextEl = marginEl?.["hc:next"]?.[0];
|
|
732
|
+
const intentVal = Number(intentEl?._attr?.value ?? 0);
|
|
733
|
+
const prevVal = Number(prevEl?._attr?.value ?? 0);
|
|
734
|
+
const nextVal = Number(nextEl?._attr?.value ?? 0);
|
|
735
|
+
if (intentVal !== 0) indentPt = Metric.hwpToPt(intentVal);
|
|
736
|
+
if (prevVal > 0) spaceBefore = Metric.hwpToPt(prevVal);
|
|
737
|
+
if (nextVal > 0) spaceAfter = Metric.hwpToPt(nextVal);
|
|
738
|
+
}
|
|
739
|
+
if (lineSpEl) {
|
|
740
|
+
const lsAttr = lineSpEl._attr ?? {};
|
|
741
|
+
const lsType = lsAttr.type ?? "PERCENT";
|
|
742
|
+
const lsVal = Number(lsAttr.value ?? 160);
|
|
743
|
+
if (lsType === "PERCENT" && lsVal > 0) lineHeight = lsVal / 100;
|
|
744
|
+
}
|
|
745
|
+
map.set(id, { align, indentPt, spaceBefore, spaceAfter, lineHeight });
|
|
687
746
|
}
|
|
688
747
|
} catch {
|
|
689
748
|
}
|
|
@@ -806,6 +865,12 @@ function decodePara(p, ctx) {
|
|
|
806
865
|
}
|
|
807
866
|
const inlineAttr = inlineParaPr?._attr ?? {};
|
|
808
867
|
const props = { align: safeAlign(align) };
|
|
868
|
+
if (paraPrDef) {
|
|
869
|
+
if (paraPrDef.indentPt !== void 0) props.indentPt = paraPrDef.indentPt;
|
|
870
|
+
if (paraPrDef.spaceBefore !== void 0) props.spaceBefore = paraPrDef.spaceBefore;
|
|
871
|
+
if (paraPrDef.spaceAfter !== void 0) props.spaceAfter = paraPrDef.spaceAfter;
|
|
872
|
+
if (paraPrDef.lineHeight !== void 0) props.lineHeight = paraPrDef.lineHeight;
|
|
873
|
+
}
|
|
809
874
|
if (inlineAttr.listType) {
|
|
810
875
|
props.listOrd = inlineAttr.listType === "DIGIT" || inlineAttr.listType === "DECIMAL";
|
|
811
876
|
props.listLv = Number(inlineAttr.listLevel ?? 0);
|
|
@@ -833,6 +898,9 @@ function decodePara(p, ctx) {
|
|
|
833
898
|
const spanProps = resolveCharPr(run, ctx);
|
|
834
899
|
kids.push(buildSpan(content, spanProps));
|
|
835
900
|
}
|
|
901
|
+
if (pAttr.pageBreak === "1") {
|
|
902
|
+
kids.unshift({ tag: "span", props: {}, kids: [buildPb()] });
|
|
903
|
+
}
|
|
836
904
|
return buildPara(kids.filter(Boolean), props);
|
|
837
905
|
}
|
|
838
906
|
function resolveCharPr(run, ctx) {
|
|
@@ -887,11 +955,55 @@ function decodePic(pic, ctx) {
|
|
|
887
955
|
gif: "image/gif",
|
|
888
956
|
bmp: "image/bmp"
|
|
889
957
|
};
|
|
890
|
-
|
|
958
|
+
const posAttr = pic?.["hp:pos"]?.[0]?._attr ?? pic?.pos?.[0]?._attr ?? {};
|
|
959
|
+
const layout = extractHwpxLayout(posAttr, pic);
|
|
960
|
+
return buildImg(TextKit.base64Encode(imgData), mimeMap[ext] ?? "image/png", w, h, void 0, layout);
|
|
891
961
|
} catch {
|
|
892
962
|
return null;
|
|
893
963
|
}
|
|
894
964
|
}
|
|
965
|
+
function extractHwpxLayout(posAttr, pic) {
|
|
966
|
+
const treatAsChar = posAttr.treatAsChar === "1" || posAttr.treatAsChar === "true";
|
|
967
|
+
if (treatAsChar) return { wrap: "inline" };
|
|
968
|
+
const textWrap = pic?._attr?.textWrap ?? pic?.["hp:pic"]?.[0]?._attr?.textWrap ?? "TOP_AND_BOTTOM";
|
|
969
|
+
const wrapMap = {
|
|
970
|
+
TOP_AND_BOTTOM: "square",
|
|
971
|
+
SQUARE: "square",
|
|
972
|
+
BOTH_SIDES: "tight",
|
|
973
|
+
LEFT: "tight",
|
|
974
|
+
RIGHT: "tight",
|
|
975
|
+
LARGER_ONLY: "tight",
|
|
976
|
+
SMALLER_ONLY: "tight",
|
|
977
|
+
LARGEST_ONLY: "tight",
|
|
978
|
+
BEHIND_TEXT: "behind",
|
|
979
|
+
FRONT_TEXT: "none"
|
|
980
|
+
};
|
|
981
|
+
const wrap = wrapMap[textWrap] ?? "square";
|
|
982
|
+
const horzRelToMap = {
|
|
983
|
+
PARA: "para",
|
|
984
|
+
MARGIN: "margin",
|
|
985
|
+
PAGE: "page",
|
|
986
|
+
COLUMN: "column"
|
|
987
|
+
};
|
|
988
|
+
const vertRelToMap = {
|
|
989
|
+
PARA: "para",
|
|
990
|
+
MARGIN: "margin",
|
|
991
|
+
PAGE: "page",
|
|
992
|
+
PAPER: "page",
|
|
993
|
+
LINE: "line"
|
|
994
|
+
};
|
|
995
|
+
const horzRelTo = horzRelToMap[posAttr.horzRelTo ?? ""] ?? "para";
|
|
996
|
+
const vertRelTo = vertRelToMap[posAttr.vertRelTo ?? ""] ?? "para";
|
|
997
|
+
const horzAlignMap = { LEFT: "left", CENTER: "center", RIGHT: "right" };
|
|
998
|
+
const vertAlignMap = { TOP: "top", CENTER: "center", BOTTOM: "bottom" };
|
|
999
|
+
const horzAlign = horzAlignMap[posAttr.horzAlign ?? ""];
|
|
1000
|
+
const vertAlign = vertAlignMap[posAttr.vertAlign ?? ""];
|
|
1001
|
+
const horzOffset = Number(posAttr.horzOffset ?? 0);
|
|
1002
|
+
const vertOffset = Number(posAttr.vertOffset ?? 0);
|
|
1003
|
+
const xPt = horzOffset !== 0 ? Metric.hwpToPt(horzOffset) : void 0;
|
|
1004
|
+
const yPt = vertOffset !== 0 ? Metric.hwpToPt(vertOffset) : void 0;
|
|
1005
|
+
return { wrap, horzAlign, vertAlign, horzRelTo, vertRelTo, xPt, yPt };
|
|
1006
|
+
}
|
|
895
1007
|
function decodeGrid(tbl, ctx) {
|
|
896
1008
|
const tblAttr = tbl?._attr ?? {};
|
|
897
1009
|
const borderFillId = Number(tblAttr.borderFillIDRef ?? 0);
|
|
@@ -900,6 +1012,26 @@ function decodeGrid(tbl, ctx) {
|
|
|
900
1012
|
const gridProps = { headerRow: headerRow || void 0 };
|
|
901
1013
|
if (borderFill?.stroke) gridProps.defaultStroke = borderFill.stroke;
|
|
902
1014
|
const rowArr = getTag(tbl, "hp:tr", "hp:ROW");
|
|
1015
|
+
for (const row of rowArr) {
|
|
1016
|
+
const cells = getTag(row, "hp:tc", "hp:CELL");
|
|
1017
|
+
const rowWidths = [];
|
|
1018
|
+
let allSingle = true;
|
|
1019
|
+
for (const cell of cells) {
|
|
1020
|
+
const cellSpanAttr = cell?.["hp:cellSpan"]?.[0]?._attr ?? {};
|
|
1021
|
+
const cs = Number(cellSpanAttr.colSpan ?? cell?._attr?.ColSpan ?? 1);
|
|
1022
|
+
if (cs > 1) {
|
|
1023
|
+
allSingle = false;
|
|
1024
|
+
break;
|
|
1025
|
+
}
|
|
1026
|
+
const szAttr = cell?.["hp:cellSz"]?.[0]?._attr ?? {};
|
|
1027
|
+
const w = Number(szAttr.width ?? 0);
|
|
1028
|
+
rowWidths.push(Metric.hwpToPt(w));
|
|
1029
|
+
}
|
|
1030
|
+
if (allSingle && rowWidths.length > 0 && rowWidths.some((w) => w > 0)) {
|
|
1031
|
+
gridProps.colWidths = rowWidths;
|
|
1032
|
+
break;
|
|
1033
|
+
}
|
|
1034
|
+
}
|
|
903
1035
|
const rowNodes = rowArr.map((row) => {
|
|
904
1036
|
const cellArr = getTag(row, "hp:tc", "hp:CELL");
|
|
905
1037
|
const cellNodes = cellArr.map((cell) => {
|
|
@@ -940,7 +1072,14 @@ function decodeGrid(tbl, ctx) {
|
|
|
940
1072
|
{ cs, rs, props: cellProps }
|
|
941
1073
|
);
|
|
942
1074
|
});
|
|
943
|
-
|
|
1075
|
+
let rowHeightPt;
|
|
1076
|
+
const firstCellForH = cellArr[0];
|
|
1077
|
+
if (firstCellForH) {
|
|
1078
|
+
const hSz = firstCellForH?.["hp:cellSz"]?.[0]?._attr ?? {};
|
|
1079
|
+
const hVal = Number(hSz.height ?? 0);
|
|
1080
|
+
if (hVal > 0) rowHeightPt = Metric.hwpToPt(hVal);
|
|
1081
|
+
}
|
|
1082
|
+
return buildRow(cellNodes, rowHeightPt);
|
|
944
1083
|
});
|
|
945
1084
|
return buildGrid(rowNodes, gridProps);
|
|
946
1085
|
}
|
|
@@ -1165,9 +1304,13 @@ function parseRecords(data) {
|
|
|
1165
1304
|
}
|
|
1166
1305
|
function tryInflate(data) {
|
|
1167
1306
|
try {
|
|
1168
|
-
return pako2.
|
|
1307
|
+
return pako2.inflate(data);
|
|
1169
1308
|
} catch {
|
|
1170
|
-
|
|
1309
|
+
try {
|
|
1310
|
+
return pako2.inflateRaw(data);
|
|
1311
|
+
} catch {
|
|
1312
|
+
return data;
|
|
1313
|
+
}
|
|
1171
1314
|
}
|
|
1172
1315
|
}
|
|
1173
1316
|
function parseFileHeader(buf) {
|
|
@@ -1221,7 +1364,7 @@ function parseParaShape(d) {
|
|
|
1221
1364
|
if (d.length < 4) return { align: "left", spaceBefore: 0, spaceAfter: 0, lineSpacing: 160, indent: 0 };
|
|
1222
1365
|
const attr = BinaryKit.readU32LE(d, 0);
|
|
1223
1366
|
return {
|
|
1224
|
-
align: ALIGN_TBL[attr & 7] ?? "left",
|
|
1367
|
+
align: ALIGN_TBL[attr >> 2 & 7] ?? "left",
|
|
1225
1368
|
indent: d.length >= 16 ? i32(d, 12) : 0,
|
|
1226
1369
|
spaceBefore: d.length >= 20 ? i32(d, 16) : 0,
|
|
1227
1370
|
spaceAfter: d.length >= 24 ? i32(d, 20) : 0,
|
|
@@ -1229,16 +1372,17 @@ function parseParaShape(d) {
|
|
|
1229
1372
|
};
|
|
1230
1373
|
}
|
|
1231
1374
|
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];
|
|
1232
|
-
var BORDER_KIND = { 0: "
|
|
1375
|
+
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" };
|
|
1233
1376
|
function parseBorderFill(d) {
|
|
1234
1377
|
const borders = [];
|
|
1378
|
+
const BASE_TYPE = 2;
|
|
1379
|
+
const BASE_WIDTH = 6;
|
|
1380
|
+
const BASE_COLOR = 10;
|
|
1235
1381
|
for (let i = 0; i < 4; i++) {
|
|
1236
|
-
const
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
borders.push({ type: 0, widthPt: 0.5, color: "000000" });
|
|
1241
|
-
}
|
|
1382
|
+
const type = BASE_TYPE + i < d.length ? d[BASE_TYPE + i] : 0;
|
|
1383
|
+
const widthPt = BASE_WIDTH + i < d.length ? BORDER_W_PT[d[BASE_WIDTH + i]] ?? 0.5 : 0.5;
|
|
1384
|
+
const color = BASE_COLOR + i * 4 + 4 <= d.length ? colorRef(d, BASE_COLOR + i * 4) : "000000";
|
|
1385
|
+
borders.push({ type, widthPt, color });
|
|
1242
1386
|
}
|
|
1243
1387
|
let bgColor;
|
|
1244
1388
|
const fOff = 32;
|
|
@@ -1252,10 +1396,15 @@ function parseBody(raw, compressed, di, shield) {
|
|
|
1252
1396
|
const recs = parseRecords(compressed ? tryInflate(raw) : raw);
|
|
1253
1397
|
const content = [];
|
|
1254
1398
|
let pageDims;
|
|
1399
|
+
for (const r of recs) {
|
|
1400
|
+
if (r.tag === TAG_PAGE_DEF) {
|
|
1401
|
+
pageDims = shield.guard(() => parsePageDef(r.data), A4, "hwp:pageDef");
|
|
1402
|
+
break;
|
|
1403
|
+
}
|
|
1404
|
+
}
|
|
1255
1405
|
let i = 0;
|
|
1256
1406
|
while (i < recs.length) {
|
|
1257
1407
|
if (recs[i].tag === TAG_PAGE_DEF) {
|
|
1258
|
-
pageDims = shield.guard(() => parsePageDef(recs[i].data), A4, "hwp:pageDef");
|
|
1259
1408
|
i++;
|
|
1260
1409
|
} else if (recs[i].tag === TAG_PARA_HEADER) {
|
|
1261
1410
|
const r = shield.guard(
|
|
@@ -1279,6 +1428,7 @@ function parseParagraphGroup(recs, start, di, shield) {
|
|
|
1279
1428
|
let text = null;
|
|
1280
1429
|
let csPairs = [];
|
|
1281
1430
|
const grids = [];
|
|
1431
|
+
const ctrlHeaders = [];
|
|
1282
1432
|
let i = start + 1;
|
|
1283
1433
|
while (i < recs.length && recs[i].level > lv) {
|
|
1284
1434
|
const r = recs[i];
|
|
@@ -1289,14 +1439,21 @@ function parseParagraphGroup(recs, start, di, shield) {
|
|
|
1289
1439
|
csPairs = parseCharShapePairs(r.data);
|
|
1290
1440
|
i++;
|
|
1291
1441
|
} else if (r.tag === TAG_CTRL_HEADER && r.level === lv + 1) {
|
|
1292
|
-
if (r.data.length >= 4
|
|
1293
|
-
const
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1442
|
+
if (r.data.length >= 4) {
|
|
1443
|
+
const ctrlId = BinaryKit.readU32LE(r.data, 0);
|
|
1444
|
+
const objId = r.data.length >= 6 ? BinaryKit.readU16LE(r.data, 4) : 0;
|
|
1445
|
+
ctrlHeaders.push({ ctrlId, objId });
|
|
1446
|
+
if (ctrlId === CTRL_TABLE) {
|
|
1447
|
+
const tr = shield.guard(
|
|
1448
|
+
() => parseTableCtrl(recs, i, di, shield),
|
|
1449
|
+
{ grid: null, next: skipKids(recs, i) },
|
|
1450
|
+
`hwp:tbl@${i}`
|
|
1451
|
+
);
|
|
1452
|
+
if (tr.grid) grids.push(tr.grid);
|
|
1453
|
+
i = tr.next;
|
|
1454
|
+
} else {
|
|
1455
|
+
i = skipKids(recs, i);
|
|
1456
|
+
}
|
|
1300
1457
|
} else {
|
|
1301
1458
|
i = skipKids(recs, i);
|
|
1302
1459
|
}
|
|
@@ -1304,12 +1461,28 @@ function parseParagraphGroup(recs, start, di, shield) {
|
|
|
1304
1461
|
i++;
|
|
1305
1462
|
}
|
|
1306
1463
|
}
|
|
1464
|
+
if (text && ctrlHeaders.length > 0) {
|
|
1465
|
+
for (let ci = 0; ci < text.controls.length; ci++) {
|
|
1466
|
+
if (ci < ctrlHeaders.length) {
|
|
1467
|
+
text.controls[ci].ctrlId = ctrlHeaders[ci].ctrlId;
|
|
1468
|
+
text.controls[ci].matched = true;
|
|
1469
|
+
}
|
|
1470
|
+
}
|
|
1471
|
+
}
|
|
1307
1472
|
const nodes = [];
|
|
1308
|
-
if (text && text.chars.length > 0) {
|
|
1309
|
-
const
|
|
1310
|
-
if (
|
|
1473
|
+
if (text && (text.chars.length > 0 || text.controls.length > 0)) {
|
|
1474
|
+
const paraContent = [];
|
|
1475
|
+
if (text.chars.length > 0) {
|
|
1311
1476
|
const spans = resolveCharShapes(text.chars, csPairs, di);
|
|
1312
|
-
|
|
1477
|
+
paraContent.push(...spans);
|
|
1478
|
+
}
|
|
1479
|
+
if (text.controls.length > 0) {
|
|
1480
|
+
for (let ci = 0; ci < text.controls.length; ci++) {
|
|
1481
|
+
paraContent.push(buildSpan(`__EXT_${ci}__`));
|
|
1482
|
+
}
|
|
1483
|
+
}
|
|
1484
|
+
if (paraContent.length > 0) {
|
|
1485
|
+
nodes.push(buildPara(paraContent, buildParaProps(ps)));
|
|
1313
1486
|
}
|
|
1314
1487
|
}
|
|
1315
1488
|
nodes.push(...grids);
|
|
@@ -1325,7 +1498,7 @@ var EXT_CTRL = /* @__PURE__ */ new Set([2, 3, 11, 12, 14, 15]);
|
|
|
1325
1498
|
var INL_CTRL = /* @__PURE__ */ new Set([4, 5, 6, 7, 8]);
|
|
1326
1499
|
function decodeParaText(d) {
|
|
1327
1500
|
const chars = [];
|
|
1328
|
-
const
|
|
1501
|
+
const controls = [];
|
|
1329
1502
|
let i = 0, pos = 0;
|
|
1330
1503
|
while (i + 1 < d.length) {
|
|
1331
1504
|
const c = d[i] | d[i + 1] << 8;
|
|
@@ -1344,7 +1517,11 @@ function decodeParaText(d) {
|
|
|
1344
1517
|
continue;
|
|
1345
1518
|
}
|
|
1346
1519
|
if (EXT_CTRL.has(c)) {
|
|
1347
|
-
|
|
1520
|
+
let objId = 0;
|
|
1521
|
+
if (i + 16 <= d.length) {
|
|
1522
|
+
objId = BinaryKit.readU16LE(d, i + 8);
|
|
1523
|
+
}
|
|
1524
|
+
controls.push({ pos, ctrlId: 0, objId, matched: false });
|
|
1348
1525
|
i += 16;
|
|
1349
1526
|
pos += 8;
|
|
1350
1527
|
continue;
|
|
@@ -1369,7 +1546,7 @@ function decodeParaText(d) {
|
|
|
1369
1546
|
i += 2;
|
|
1370
1547
|
pos++;
|
|
1371
1548
|
}
|
|
1372
|
-
return { chars,
|
|
1549
|
+
return { chars, controls };
|
|
1373
1550
|
}
|
|
1374
1551
|
function parseCharShapePairs(d) {
|
|
1375
1552
|
const out = [];
|
|
@@ -1544,8 +1721,8 @@ function parseCellRec(d, tag, recs, cStart, cEnd, di, shield, seqIdx, colCnt) {
|
|
|
1544
1721
|
if (tag === TAG_LIST_HEADER && d.length >= 22) {
|
|
1545
1722
|
col = BinaryKit.readU16LE(d, 8);
|
|
1546
1723
|
row = BinaryKit.readU16LE(d, 10);
|
|
1547
|
-
|
|
1548
|
-
|
|
1724
|
+
cs = Math.max(1, BinaryKit.readU16LE(d, 12));
|
|
1725
|
+
rs = Math.max(1, BinaryKit.readU16LE(d, 14));
|
|
1549
1726
|
widthHwp = BinaryKit.readU32LE(d, 16);
|
|
1550
1727
|
const bfId = d.length >= 34 ? BinaryKit.readU16LE(d, 32) : 0;
|
|
1551
1728
|
if (bfId > 0 && bfId <= di.borderFills.length) {
|
|
@@ -1680,6 +1857,45 @@ var HwpScanner = class {
|
|
|
1680
1857
|
if (diRaw) {
|
|
1681
1858
|
di = shield.guard(() => parseDocInfo(diRaw, compressed), di, "hwp:docInfo");
|
|
1682
1859
|
}
|
|
1860
|
+
const imageStreams = [];
|
|
1861
|
+
for (const [path, data2] of streams) {
|
|
1862
|
+
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")) {
|
|
1863
|
+
imageStreams.push({ path, data: data2 });
|
|
1864
|
+
console.log(`[HwpScanner] Image stream found: ${path} (${data2.length} bytes)`);
|
|
1865
|
+
}
|
|
1866
|
+
}
|
|
1867
|
+
const objectMap = /* @__PURE__ */ new Map();
|
|
1868
|
+
const seenHashes = /* @__PURE__ */ new Set();
|
|
1869
|
+
let imgIdx = 0;
|
|
1870
|
+
for (const { path, data: data2 } of imageStreams) {
|
|
1871
|
+
let mimeType = "image/jpeg";
|
|
1872
|
+
const lowerPath = path.toLowerCase();
|
|
1873
|
+
if (lowerPath.includes(".png")) mimeType = "image/png";
|
|
1874
|
+
else if (lowerPath.includes(".gif")) mimeType = "image/gif";
|
|
1875
|
+
else if (lowerPath.includes(".bmp")) mimeType = "image/bmp";
|
|
1876
|
+
if (data2[0] === 137 && data2[1] === 80 && data2[2] === 78 && data2[3] === 71) mimeType = "image/png";
|
|
1877
|
+
else if (data2[0] === 71 && data2[1] === 73 && data2[2] === 70 && data2[3] === 13624) mimeType = "image/gif";
|
|
1878
|
+
else if (data2[0] === 66 && data2[1] === 77) mimeType = "image/bmp";
|
|
1879
|
+
const imgData = Buffer.from(data2);
|
|
1880
|
+
const base64 = imgData.toString("base64");
|
|
1881
|
+
const hash = base64.slice(0, 20);
|
|
1882
|
+
if (!seenHashes.has(hash)) {
|
|
1883
|
+
seenHashes.add(hash);
|
|
1884
|
+
objectMap.set(imgIdx++, buildImg(
|
|
1885
|
+
base64,
|
|
1886
|
+
mimeType,
|
|
1887
|
+
0,
|
|
1888
|
+
// w
|
|
1889
|
+
0,
|
|
1890
|
+
// h
|
|
1891
|
+
`Image from ${path}`
|
|
1892
|
+
));
|
|
1893
|
+
console.log(`[HwpScanner] Added unique image: ${hash}... (${data2.length} bytes)`);
|
|
1894
|
+
} else {
|
|
1895
|
+
console.log(`[HwpScanner] Duplicate image skipped: ${hash}...`);
|
|
1896
|
+
}
|
|
1897
|
+
}
|
|
1898
|
+
console.log(`[HwpScanner] Found ${imageStreams.length} image streams, ${objectMap.size} unique images`);
|
|
1683
1899
|
const allContent = [];
|
|
1684
1900
|
let pageDims = A4;
|
|
1685
1901
|
for (let s = 0; s < 100; s++) {
|
|
@@ -1703,6 +1919,26 @@ var HwpScanner = class {
|
|
|
1703
1919
|
allContent.push(...r.content);
|
|
1704
1920
|
if (r.pageDims) pageDims = r.pageDims;
|
|
1705
1921
|
}
|
|
1922
|
+
console.log(`[HwpScanner] Before injection: ${allContent.length} nodes, ${objectMap.size} images available`);
|
|
1923
|
+
if (objectMap.size > 0) {
|
|
1924
|
+
injectImagesIntoContent(allContent, objectMap);
|
|
1925
|
+
console.log(`[HwpScanner] After injection: ${allContent.length} nodes`);
|
|
1926
|
+
}
|
|
1927
|
+
const countImages = (nodes) => {
|
|
1928
|
+
let count = 0;
|
|
1929
|
+
for (const node of nodes) {
|
|
1930
|
+
if (node.tag === "img") count++;
|
|
1931
|
+
if (node.tag === "para" && node.kids) count += countImages(node.kids);
|
|
1932
|
+
if (node.tag === "grid" && node.kids) {
|
|
1933
|
+
for (const row of node.kids) {
|
|
1934
|
+
if (row.kids) count += countImages(row.kids);
|
|
1935
|
+
}
|
|
1936
|
+
}
|
|
1937
|
+
}
|
|
1938
|
+
return count;
|
|
1939
|
+
};
|
|
1940
|
+
const imgCount = countImages(allContent);
|
|
1941
|
+
console.log(`[HwpScanner] Images in content: ${imgCount}`);
|
|
1706
1942
|
warns.push(...shield.flush());
|
|
1707
1943
|
const content = allContent.length > 0 ? allContent : [buildPara([buildSpan("")])];
|
|
1708
1944
|
return succeed(buildRoot({}, [buildSheet(content, pageDims)]), warns);
|
|
@@ -1717,6 +1953,33 @@ function findBodySection(streams) {
|
|
|
1717
1953
|
if (k.includes("Section") && !k.includes("Header") && !k.includes("Info")) return v;
|
|
1718
1954
|
return void 0;
|
|
1719
1955
|
}
|
|
1956
|
+
function injectImagesIntoContent(content, objectMap) {
|
|
1957
|
+
const imageArray = Array.from(objectMap.values());
|
|
1958
|
+
if (imageArray.length === 0) return;
|
|
1959
|
+
const uniqueImages = Array.from(new Set(imageArray.map((img) => img.b64))).map((b64) => {
|
|
1960
|
+
return imageArray.find((img) => img.b64 === b64);
|
|
1961
|
+
});
|
|
1962
|
+
if (uniqueImages.length === 0) return;
|
|
1963
|
+
let imgIdx = 0;
|
|
1964
|
+
for (const node of content) {
|
|
1965
|
+
if (node.tag === "para" && node.kids) {
|
|
1966
|
+
for (let i = 0; i < node.kids.length; i++) {
|
|
1967
|
+
const kid = node.kids[i];
|
|
1968
|
+
if (kid.tag === "span" && kid.kids && kid.kids[0]?.tag === "txt") {
|
|
1969
|
+
const text = kid.kids[0].content;
|
|
1970
|
+
const match = text.match?.(/^__(?:IMG|EXT)_(\d+)__$/);
|
|
1971
|
+
if (match) {
|
|
1972
|
+
const imgNode = uniqueImages[imgIdx % uniqueImages.length];
|
|
1973
|
+
if (imgNode) {
|
|
1974
|
+
node.kids[i] = imgNode;
|
|
1975
|
+
imgIdx++;
|
|
1976
|
+
}
|
|
1977
|
+
}
|
|
1978
|
+
}
|
|
1979
|
+
}
|
|
1980
|
+
}
|
|
1981
|
+
}
|
|
1982
|
+
}
|
|
1720
1983
|
registry.registerDecoder(new HwpScanner());
|
|
1721
1984
|
|
|
1722
1985
|
// src/decoders/docx/DocxDecoder.ts
|
|
@@ -1749,26 +2012,59 @@ var DocxDecoder = class {
|
|
|
1749
2012
|
} catch {
|
|
1750
2013
|
}
|
|
1751
2014
|
}
|
|
2015
|
+
let stylesMap = /* @__PURE__ */ new Map();
|
|
2016
|
+
const stylesXml2 = files.get("word/styles.xml");
|
|
2017
|
+
if (stylesXml2) {
|
|
2018
|
+
try {
|
|
2019
|
+
stylesMap = await parseStylesMap(TextKit.decode(stylesXml2));
|
|
2020
|
+
} catch {
|
|
2021
|
+
}
|
|
2022
|
+
}
|
|
1752
2023
|
const docStr = TextKit.decode(docXml);
|
|
1753
2024
|
const docObj = await XmlKit.parseStrict(docStr);
|
|
1754
2025
|
const body = getBody(docObj);
|
|
1755
2026
|
const dims = extractDims2(body) ?? { ...A4 };
|
|
1756
2027
|
const elements = getBodyElements(body);
|
|
1757
|
-
const decCtx = { relsMap, files, shield, numMap, warns };
|
|
1758
|
-
const kids =
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
2028
|
+
const decCtx = { relsMap, files, shield, numMap, warns, stylesMap };
|
|
2029
|
+
const kids = [];
|
|
2030
|
+
for (const el of elements) {
|
|
2031
|
+
const node = shield.guard(
|
|
2032
|
+
() => decodeElement(el, decCtx),
|
|
2033
|
+
buildPara([buildSpan("[\uC694\uC18C \uD30C\uC2F1 \uC2E4\uD328]")]),
|
|
2034
|
+
"docx:bodyElement"
|
|
2035
|
+
);
|
|
2036
|
+
kids.push(node);
|
|
2037
|
+
if (el.type === "para") {
|
|
2038
|
+
const pPr = el.node?.["w:pPr"]?.[0] ?? el.node?.pPr?.[0] ?? {};
|
|
2039
|
+
const inlineSectPr = pPr?.["w:sectPr"]?.[0] ?? pPr?.sectPr?.[0];
|
|
2040
|
+
if (inlineSectPr) {
|
|
2041
|
+
const typeAttr = inlineSectPr?.["w:type"]?.[0]?._attr;
|
|
2042
|
+
const sectType = typeAttr?.["w:val"] ?? typeAttr?.val ?? "nextPage";
|
|
2043
|
+
if (sectType !== "continuous") {
|
|
2044
|
+
kids.push(buildPara([{ tag: "span", props: {}, kids: [buildPb()] }]));
|
|
2045
|
+
}
|
|
2046
|
+
}
|
|
2047
|
+
}
|
|
2048
|
+
}
|
|
2049
|
+
const headerParas = await decodeHeaderFooter2(
|
|
2050
|
+
"header",
|
|
2051
|
+
body,
|
|
2052
|
+
relsMap,
|
|
2053
|
+
files,
|
|
2054
|
+
decCtx
|
|
1763
2055
|
);
|
|
1764
|
-
const
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
{ header: headerParas, footer: footerParas }
|
|
2056
|
+
const footerParas = await decodeHeaderFooter2(
|
|
2057
|
+
"footer",
|
|
2058
|
+
body,
|
|
2059
|
+
relsMap,
|
|
2060
|
+
files,
|
|
2061
|
+
decCtx
|
|
1771
2062
|
);
|
|
2063
|
+
warns.push(...shield.flush());
|
|
2064
|
+
const sheet = buildSheet(kids.filter(Boolean), dims, {
|
|
2065
|
+
header: headerParas,
|
|
2066
|
+
footer: footerParas
|
|
2067
|
+
});
|
|
1772
2068
|
return succeed(buildRoot(meta, [sheet]), warns);
|
|
1773
2069
|
} catch (e) {
|
|
1774
2070
|
warns.push(...shield.flush());
|
|
@@ -1779,6 +2075,19 @@ var DocxDecoder = class {
|
|
|
1779
2075
|
function toArr2(v) {
|
|
1780
2076
|
return v == null ? [] : Array.isArray(v) ? v : [v];
|
|
1781
2077
|
}
|
|
2078
|
+
function resolveDocxPath(baseDir, target) {
|
|
2079
|
+
if (target.startsWith("/")) return target.slice(1);
|
|
2080
|
+
const parts = (baseDir + "/" + target).split("/");
|
|
2081
|
+
const stack = [];
|
|
2082
|
+
for (const p of parts) {
|
|
2083
|
+
if (p === "..") {
|
|
2084
|
+
stack.pop();
|
|
2085
|
+
} else if (p !== ".") {
|
|
2086
|
+
stack.push(p);
|
|
2087
|
+
}
|
|
2088
|
+
}
|
|
2089
|
+
return stack.join("/");
|
|
2090
|
+
}
|
|
1782
2091
|
async function parseRels(xml) {
|
|
1783
2092
|
const map = /* @__PURE__ */ new Map();
|
|
1784
2093
|
try {
|
|
@@ -1813,7 +2122,9 @@ async function parseNumbering(xml) {
|
|
|
1813
2122
|
const root = obj?.["w:numbering"]?.[0] ?? obj?.numbering?.[0] ?? obj;
|
|
1814
2123
|
const absMap = /* @__PURE__ */ new Map();
|
|
1815
2124
|
for (const abs of toArr2(root?.["w:abstractNum"] ?? root?.abstractNum)) {
|
|
1816
|
-
const absId = Number(
|
|
2125
|
+
const absId = Number(
|
|
2126
|
+
abs?._attr?.["w:abstractNumId"] ?? abs?._attr?.abstractNumId ?? 0
|
|
2127
|
+
);
|
|
1817
2128
|
const levels = /* @__PURE__ */ new Map();
|
|
1818
2129
|
for (const lvl of toArr2(abs?.["w:lvl"] ?? abs?.lvl)) {
|
|
1819
2130
|
const ilvl = Number(lvl?._attr?.["w:ilvl"] ?? lvl?._attr?.ilvl ?? 0);
|
|
@@ -1860,8 +2171,10 @@ function extractDims2(body) {
|
|
|
1860
2171
|
function getBodyElements(body) {
|
|
1861
2172
|
const paras = toArr2(body?.["w:p"] ?? body?.p);
|
|
1862
2173
|
const tables = toArr2(body?.["w:tbl"] ?? body?.tbl);
|
|
1863
|
-
if (tables.length === 0)
|
|
1864
|
-
|
|
2174
|
+
if (tables.length === 0)
|
|
2175
|
+
return paras.map((n) => ({ type: "para", node: n }));
|
|
2176
|
+
if (paras.length === 0)
|
|
2177
|
+
return tables.map((n) => ({ type: "table", node: n }));
|
|
1865
2178
|
const childOrder = body?.["_childOrder"];
|
|
1866
2179
|
if (Array.isArray(childOrder)) {
|
|
1867
2180
|
const items = [];
|
|
@@ -1874,7 +2187,8 @@ function getBodyElements(body) {
|
|
|
1874
2187
|
}
|
|
1875
2188
|
}
|
|
1876
2189
|
while (pi < paras.length) items.push({ type: "para", node: paras[pi++] });
|
|
1877
|
-
while (ti < tables.length)
|
|
2190
|
+
while (ti < tables.length)
|
|
2191
|
+
items.push({ type: "table", node: tables[ti++] });
|
|
1878
2192
|
return items;
|
|
1879
2193
|
}
|
|
1880
2194
|
return [
|
|
@@ -1893,7 +2207,7 @@ async function decodeHeaderFooter2(kind, body, relsMap, files, ctx) {
|
|
|
1893
2207
|
if (!rId) return void 0;
|
|
1894
2208
|
const target = relsMap.get(rId);
|
|
1895
2209
|
if (!target) return void 0;
|
|
1896
|
-
const filePath =
|
|
2210
|
+
const filePath = resolveDocxPath("word", target);
|
|
1897
2211
|
const fileData = files.get(filePath);
|
|
1898
2212
|
if (!fileData) return void 0;
|
|
1899
2213
|
const xmlStr = TextKit.decode(fileData);
|
|
@@ -1907,6 +2221,14 @@ async function decodeHeaderFooter2(kind, body, relsMap, files, ctx) {
|
|
|
1907
2221
|
return void 0;
|
|
1908
2222
|
}
|
|
1909
2223
|
}
|
|
2224
|
+
function hasDrawingDeep(node) {
|
|
2225
|
+
if (!node || typeof node !== "object") return false;
|
|
2226
|
+
if (node["w:drawing"] || node["w:pict"]) return true;
|
|
2227
|
+
return Object.values(node).some((v) => {
|
|
2228
|
+
if (Array.isArray(v)) return v.some(hasDrawingDeep);
|
|
2229
|
+
return hasDrawingDeep(v);
|
|
2230
|
+
});
|
|
2231
|
+
}
|
|
1910
2232
|
function decodeElement(el, ctx) {
|
|
1911
2233
|
if (el.type === "table") {
|
|
1912
2234
|
const { value } = ctx.shield.guardGrid(
|
|
@@ -1929,6 +2251,19 @@ function decodePara2(p, ctx) {
|
|
|
1929
2251
|
align: safeAlign(alignVal),
|
|
1930
2252
|
heading: parseHeading(headStyle)
|
|
1931
2253
|
};
|
|
2254
|
+
const spacingAttr = pPr?.["w:spacing"]?.[0]?._attr ?? pPr?.spacing?.[0]?._attr ?? {};
|
|
2255
|
+
const beforeVal = Number(
|
|
2256
|
+
spacingAttr?.["w:before"] ?? spacingAttr?.before ?? 0
|
|
2257
|
+
);
|
|
2258
|
+
const afterVal = Number(spacingAttr?.["w:after"] ?? spacingAttr?.after ?? 0);
|
|
2259
|
+
const lineVal = Number(spacingAttr?.["w:line"] ?? spacingAttr?.line ?? 0);
|
|
2260
|
+
const lineRule = spacingAttr?.["w:lineRule"] ?? spacingAttr?.lineRule ?? "auto";
|
|
2261
|
+
if (beforeVal > 0) props.spaceBefore = Metric.dxaToPt(beforeVal);
|
|
2262
|
+
if (afterVal > 0) props.spaceAfter = Metric.dxaToPt(afterVal);
|
|
2263
|
+
if (lineVal > 0 && lineRule === "auto") props.lineHeight = lineVal / 240;
|
|
2264
|
+
const indAttr = pPr?.["w:ind"]?.[0]?._attr ?? pPr?.ind?.[0]?._attr ?? {};
|
|
2265
|
+
const leftVal = Number(indAttr?.["w:left"] ?? indAttr?.left ?? 0);
|
|
2266
|
+
if (leftVal > 0) props.indentPt = Metric.dxaToPt(leftVal);
|
|
1932
2267
|
const numPr = pPr?.["w:numPr"]?.[0] ?? pPr?.numPr?.[0];
|
|
1933
2268
|
if (numPr) {
|
|
1934
2269
|
const ilvlNode = numPr?.["w:ilvl"]?.[0]?._attr ?? numPr?.ilvl?.[0]?._attr ?? {};
|
|
@@ -1944,17 +2279,40 @@ function decodePara2(p, ctx) {
|
|
|
1944
2279
|
props.listOrd = numId >= 2;
|
|
1945
2280
|
}
|
|
1946
2281
|
}
|
|
2282
|
+
const pbBeforeNode = pPr?.["w:pageBreakBefore"]?.[0] ?? pPr?.pageBreakBefore?.[0];
|
|
2283
|
+
const hasPageBreakBefore = pbBeforeNode != null && (pbBeforeNode?._attr?.["w:val"] ?? pbBeforeNode?._attr?.val ?? "1") !== "0";
|
|
1947
2284
|
const runs = toArr2(p?.["w:r"] ?? p?.r);
|
|
1948
2285
|
const kids = ctx.shield.guardAll(
|
|
1949
2286
|
runs,
|
|
1950
|
-
(run) => decodeRunOrImage(run, ctx),
|
|
2287
|
+
(run) => hasDrawingDeep(run) ? decodeRunOrImage(run, ctx) : decodeRun(run, ctx),
|
|
1951
2288
|
() => buildSpan(""),
|
|
1952
2289
|
"docx:run"
|
|
1953
2290
|
);
|
|
1954
|
-
|
|
2291
|
+
const filteredKids = kids.filter(Boolean);
|
|
2292
|
+
if (hasPageBreakBefore) {
|
|
2293
|
+
filteredKids.unshift({ tag: "span", props: {}, kids: [buildPb()] });
|
|
2294
|
+
}
|
|
2295
|
+
return buildPara(filteredKids, props);
|
|
1955
2296
|
}
|
|
1956
2297
|
function decodeRunOrImage(run, ctx) {
|
|
1957
|
-
|
|
2298
|
+
function findFirstDrawing(node) {
|
|
2299
|
+
if (!node || typeof node !== "object") return null;
|
|
2300
|
+
if (node["w:drawing"]) return node["w:drawing"][0];
|
|
2301
|
+
if (node["w:pict"]) return node["w:pict"][0];
|
|
2302
|
+
for (const value of Object.values(node)) {
|
|
2303
|
+
if (Array.isArray(value)) {
|
|
2304
|
+
for (const v of value) {
|
|
2305
|
+
const found = findFirstDrawing(v);
|
|
2306
|
+
if (found) return found;
|
|
2307
|
+
}
|
|
2308
|
+
} else {
|
|
2309
|
+
const found = findFirstDrawing(value);
|
|
2310
|
+
if (found) return found;
|
|
2311
|
+
}
|
|
2312
|
+
}
|
|
2313
|
+
return null;
|
|
2314
|
+
}
|
|
2315
|
+
const drawing = findFirstDrawing(run);
|
|
1958
2316
|
if (drawing) {
|
|
1959
2317
|
const img = decodeDrawing(drawing, ctx);
|
|
1960
2318
|
if (img) return img;
|
|
@@ -1983,9 +2341,14 @@ function decodeDrawing(drawing, ctx) {
|
|
|
1983
2341
|
if (!rId) return null;
|
|
1984
2342
|
const target = ctx.relsMap.get(rId);
|
|
1985
2343
|
if (!target) return null;
|
|
1986
|
-
const filePath =
|
|
2344
|
+
const filePath = resolveDocxPath("word", target);
|
|
1987
2345
|
const fileData = ctx.files.get(filePath);
|
|
1988
|
-
if (!fileData)
|
|
2346
|
+
if (!fileData) {
|
|
2347
|
+
console.warn(
|
|
2348
|
+
`[DocxDecoder] image not found in ZIP: "${filePath}" (rId=${rId}, target=${target})`
|
|
2349
|
+
);
|
|
2350
|
+
return null;
|
|
2351
|
+
}
|
|
1989
2352
|
const ext = target.split(".").pop()?.toLowerCase() ?? "png";
|
|
1990
2353
|
const mimeMap = {
|
|
1991
2354
|
png: "image/png",
|
|
@@ -1995,7 +2358,11 @@ function decodeDrawing(drawing, ctx) {
|
|
|
1995
2358
|
bmp: "image/bmp"
|
|
1996
2359
|
};
|
|
1997
2360
|
const mime = mimeMap[ext] ?? "image/png";
|
|
1998
|
-
|
|
2361
|
+
console.log(
|
|
2362
|
+
`[DocxDecoder] image loaded: ${filePath} (${mime}, ${fileData.length} bytes)`
|
|
2363
|
+
);
|
|
2364
|
+
const layout = inline ? { wrap: "inline" } : extractAnchorLayout(anchor);
|
|
2365
|
+
return buildImg(TextKit.base64Encode(fileData), mime, wPt, hPt, alt || void 0, layout);
|
|
1999
2366
|
} catch {
|
|
2000
2367
|
return null;
|
|
2001
2368
|
}
|
|
@@ -2032,6 +2399,13 @@ function decodeRun(run, ctx) {
|
|
|
2032
2399
|
};
|
|
2033
2400
|
const fldChar = run?.["w:fldChar"]?.[0]?._attr ?? run?.fldChar?.[0]?._attr;
|
|
2034
2401
|
const instrText = run?.["w:instrText"]?.[0];
|
|
2402
|
+
const brNodes = toArr2(run?.["w:br"] ?? run?.br ?? []);
|
|
2403
|
+
for (const br of brNodes) {
|
|
2404
|
+
const brType = br?._attr?.["w:type"] ?? br?._attr?.type;
|
|
2405
|
+
if (brType === "page") {
|
|
2406
|
+
return { tag: "span", props, kids: [buildPb()] };
|
|
2407
|
+
}
|
|
2408
|
+
}
|
|
2035
2409
|
const textNodes = toArr2(run?.["w:t"] ?? run?.t);
|
|
2036
2410
|
const content = textNodes.map((t) => typeof t === "string" ? t : t?._ ?? t?._text ?? "").join("");
|
|
2037
2411
|
if (instrText) {
|
|
@@ -2043,6 +2417,73 @@ function decodeRun(run, ctx) {
|
|
|
2043
2417
|
}
|
|
2044
2418
|
return buildSpan(content, props);
|
|
2045
2419
|
}
|
|
2420
|
+
function parseBorderDef(bdrNode) {
|
|
2421
|
+
const sides = [
|
|
2422
|
+
["top", "top"],
|
|
2423
|
+
["bottom", "bottom"],
|
|
2424
|
+
["left", "left"],
|
|
2425
|
+
["right", "right"],
|
|
2426
|
+
["insideH", "insideH"],
|
|
2427
|
+
["insideV", "insideV"]
|
|
2428
|
+
];
|
|
2429
|
+
const result = {};
|
|
2430
|
+
for (const [xml, prop] of sides) {
|
|
2431
|
+
const bdr = bdrNode?.["w:" + xml]?.[0]?._attr ?? bdrNode?.[xml]?.[0]?._attr;
|
|
2432
|
+
if (!bdr) continue;
|
|
2433
|
+
const val = bdr?.["w:val"] ?? bdr?.val;
|
|
2434
|
+
if (val === "none" || val === "nil") continue;
|
|
2435
|
+
result[prop] = safeStrokeDocx(
|
|
2436
|
+
val,
|
|
2437
|
+
Number(bdr?.["w:sz"] ?? bdr?.sz ?? 4),
|
|
2438
|
+
bdr?.["w:color"] ?? bdr?.color
|
|
2439
|
+
);
|
|
2440
|
+
}
|
|
2441
|
+
return result;
|
|
2442
|
+
}
|
|
2443
|
+
async function parseStylesMap(xml) {
|
|
2444
|
+
const map = /* @__PURE__ */ new Map();
|
|
2445
|
+
try {
|
|
2446
|
+
const obj = await XmlKit.parseStrict(xml);
|
|
2447
|
+
const stylesRoot = obj?.["w:styles"]?.[0] ?? obj?.styles?.[0] ?? obj;
|
|
2448
|
+
const styleArr = toArr2(stylesRoot?.["w:style"] ?? stylesRoot?.style);
|
|
2449
|
+
for (const style of styleArr) {
|
|
2450
|
+
const attr = style?._attr ?? {};
|
|
2451
|
+
const type = attr?.["w:type"] ?? attr?.type;
|
|
2452
|
+
if (type !== "table") continue;
|
|
2453
|
+
const id = attr?.["w:styleId"] ?? attr?.styleId;
|
|
2454
|
+
if (!id) continue;
|
|
2455
|
+
const tblPr = style?.["w:tblPr"]?.[0] ?? style?.tblPr?.[0];
|
|
2456
|
+
const tblBdrNode = tblPr?.["w:tblBorders"]?.[0] ?? tblPr?.tblBorders?.[0];
|
|
2457
|
+
const tblBorders = tblBdrNode ? parseBorderDef(tblBdrNode) : void 0;
|
|
2458
|
+
const tcStyle = style?.["w:tcStyle"]?.[0] ?? style?.tcStyle?.[0];
|
|
2459
|
+
const tcBdrNode = tcStyle?.["w:tcBdr"]?.[0] ?? tcStyle?.tcBdr?.[0];
|
|
2460
|
+
if (tcBdrNode) {
|
|
2461
|
+
const cellDef = parseBorderDef(tcBdrNode);
|
|
2462
|
+
if (!tblBorders) {
|
|
2463
|
+
map.set(id, { tblBorders: cellDef });
|
|
2464
|
+
} else {
|
|
2465
|
+
map.set(id, { tblBorders: { ...cellDef, ...tblBorders } });
|
|
2466
|
+
}
|
|
2467
|
+
} else if (tblBorders) {
|
|
2468
|
+
map.set(id, { tblBorders });
|
|
2469
|
+
}
|
|
2470
|
+
}
|
|
2471
|
+
} catch {
|
|
2472
|
+
}
|
|
2473
|
+
return map;
|
|
2474
|
+
}
|
|
2475
|
+
function resolveCellBorders(cp, ri, ci, rs, cs, rowCount, colCount, tblBdr) {
|
|
2476
|
+
const isTopEdge = ri === 0;
|
|
2477
|
+
const isBottomEdge = ri + rs >= rowCount;
|
|
2478
|
+
const isLeftEdge = ci === 0;
|
|
2479
|
+
const isRightEdge = ci + cs >= colCount;
|
|
2480
|
+
const resolved = { ...cp };
|
|
2481
|
+
if (!resolved.top) resolved.top = isTopEdge ? tblBdr.top : tblBdr.insideH;
|
|
2482
|
+
if (!resolved.bot) resolved.bot = isBottomEdge ? tblBdr.bottom : tblBdr.insideH;
|
|
2483
|
+
if (!resolved.left) resolved.left = isLeftEdge ? tblBdr.left : tblBdr.insideV;
|
|
2484
|
+
if (!resolved.right) resolved.right = isRightEdge ? tblBdr.right : tblBdr.insideV;
|
|
2485
|
+
return resolved;
|
|
2486
|
+
}
|
|
2046
2487
|
function decodeGrid2(tbl, ctx) {
|
|
2047
2488
|
const tblPr = tbl?.["w:tblPr"]?.[0] ?? tbl?.tblPr?.[0] ?? {};
|
|
2048
2489
|
const tblLookAttr = tblPr?.["w:tblLook"]?.[0]?._attr ?? tblPr?.tblLook?.[0]?._attr ?? {};
|
|
@@ -2054,19 +2495,24 @@ function decodeGrid2(tbl, ctx) {
|
|
|
2054
2495
|
bandedRows: tblLookAttr?.["w:noHBand"] === "0" || void 0,
|
|
2055
2496
|
bandedCols: tblLookAttr?.["w:noVBand"] === "0" || void 0
|
|
2056
2497
|
};
|
|
2057
|
-
const
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
);
|
|
2067
|
-
}
|
|
2068
|
-
}
|
|
2498
|
+
const tblStyleId = (tblPr?.["w:tblStyle"]?.[0]?._attr ?? tblPr?.tblStyle?.[0]?._attr)?.["w:val"];
|
|
2499
|
+
const styleDef = tblStyleId ? ctx.stylesMap.get(tblStyleId) : void 0;
|
|
2500
|
+
let tblBdr = styleDef?.tblBorders ?? {};
|
|
2501
|
+
const tblBordersNode = tblPr?.["w:tblBorders"]?.[0] ?? tblPr?.tblBorders?.[0];
|
|
2502
|
+
if (tblBordersNode) {
|
|
2503
|
+
const parsed = parseBorderDef(tblBordersNode);
|
|
2504
|
+
tblBdr = { ...tblBdr, ...parsed };
|
|
2505
|
+
}
|
|
2506
|
+
const defaultStroke = tblBdr.insideH ?? tblBdr.top;
|
|
2069
2507
|
const gridProps = { look, defaultStroke };
|
|
2508
|
+
const tblGrid = tbl?.["w:tblGrid"]?.[0] ?? tbl?.tblGrid?.[0];
|
|
2509
|
+
if (tblGrid) {
|
|
2510
|
+
const gridCols = toArr2(tblGrid?.["w:gridCol"] ?? tblGrid?.gridCol ?? []);
|
|
2511
|
+
const colWidthsPt = gridCols.map(
|
|
2512
|
+
(gc) => Metric.dxaToPt(Number(gc?._attr?.["w:w"] ?? gc?._attr?.w ?? 0))
|
|
2513
|
+
).filter((w) => w > 0);
|
|
2514
|
+
if (colWidthsPt.length > 0) gridProps.colWidths = colWidthsPt;
|
|
2515
|
+
}
|
|
2070
2516
|
const rowArr = toArr2(tbl?.["w:tr"] ?? tbl?.tr);
|
|
2071
2517
|
const rawGrid = rowArr.map((row) => {
|
|
2072
2518
|
const cellArr = toArr2(row?.["w:tc"] ?? row?.tc);
|
|
@@ -2110,6 +2556,12 @@ function decodeGrid2(tbl, ctx) {
|
|
|
2110
2556
|
const trPr = row?.["w:trPr"]?.[0] ?? row?.trPr?.[0] ?? {};
|
|
2111
2557
|
const isHeaderRow = trPr?.["w:tblHeader"]?.[0] != null || trPr?.tblHeader?.[0] != null;
|
|
2112
2558
|
if (ri === 0 && isHeaderRow) gridProps.headerRow = true;
|
|
2559
|
+
let rowHeightPt;
|
|
2560
|
+
const trHAttr = trPr?.["w:trHeight"]?.[0]?._attr ?? trPr?.trHeight?.[0]?._attr;
|
|
2561
|
+
if (trHAttr) {
|
|
2562
|
+
const hDxa = Number(trHAttr?.["w:val"] ?? trHAttr?.val ?? 0);
|
|
2563
|
+
if (hDxa > 0) rowHeightPt = Metric.dxaToPt(hDxa);
|
|
2564
|
+
}
|
|
2113
2565
|
const cellNodes = [];
|
|
2114
2566
|
for (let ci = 0; ci < rawRow.length; ci++) {
|
|
2115
2567
|
const rc = rawRow[ci];
|
|
@@ -2118,9 +2570,9 @@ function decodeGrid2(tbl, ctx) {
|
|
|
2118
2570
|
const tcPr = cell?.["w:tcPr"]?.[0] ?? {};
|
|
2119
2571
|
const bgAttr = tcPr?.["w:shd"]?.[0]?._attr ?? {};
|
|
2120
2572
|
const bg = safeHex(bgAttr?.["w:fill"] ?? bgAttr?.fill);
|
|
2121
|
-
const
|
|
2573
|
+
const tcBordersNode = tcPr?.["w:tcBorders"]?.[0] ?? tcPr?.tcBorders?.[0];
|
|
2122
2574
|
const cp = { bg, isHeader: isHeaderRow || void 0 };
|
|
2123
|
-
if (
|
|
2575
|
+
if (tcBordersNode) {
|
|
2124
2576
|
const dirs = [
|
|
2125
2577
|
["top", "top"],
|
|
2126
2578
|
["bottom", "bot"],
|
|
@@ -2128,10 +2580,13 @@ function decodeGrid2(tbl, ctx) {
|
|
|
2128
2580
|
["right", "right"]
|
|
2129
2581
|
];
|
|
2130
2582
|
for (const [xmlTag, propKey] of dirs) {
|
|
2131
|
-
const bdr =
|
|
2132
|
-
if (bdr)
|
|
2583
|
+
const bdr = tcBordersNode?.["w:" + xmlTag]?.[0]?._attr ?? tcBordersNode?.[xmlTag]?.[0]?._attr;
|
|
2584
|
+
if (!bdr) continue;
|
|
2585
|
+
const val = bdr?.["w:val"] ?? bdr?.val;
|
|
2586
|
+
if (val === "none" || val === "nil") {
|
|
2587
|
+
} else {
|
|
2133
2588
|
cp[propKey] = safeStrokeDocx(
|
|
2134
|
-
|
|
2589
|
+
val,
|
|
2135
2590
|
Number(bdr?.["w:sz"] ?? bdr?.sz ?? 4),
|
|
2136
2591
|
bdr?.["w:color"] ?? bdr?.color
|
|
2137
2592
|
);
|
|
@@ -2141,17 +2596,32 @@ function decodeGrid2(tbl, ctx) {
|
|
|
2141
2596
|
const vaAttr = tcPr?.["w:vAlign"]?.[0]?._attr ?? tcPr?.vAlign?.[0]?._attr ?? {};
|
|
2142
2597
|
const vaVal = vaAttr?.["w:val"] ?? vaAttr?.val;
|
|
2143
2598
|
if (vaVal) {
|
|
2144
|
-
const vaMap = {
|
|
2599
|
+
const vaMap = {
|
|
2600
|
+
top: "top",
|
|
2601
|
+
center: "mid",
|
|
2602
|
+
bottom: "bot"
|
|
2603
|
+
};
|
|
2145
2604
|
cp.va = vaMap[vaVal];
|
|
2146
2605
|
}
|
|
2147
2606
|
const rs = rsMap.get(`${ri},${ci}`) ?? 1;
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
));
|
|
2607
|
+
let gridColIdx = 0;
|
|
2608
|
+
for (let prevCi = 0; prevCi < ci; prevCi++) {
|
|
2609
|
+
if (!rawRow[prevCi].vMergeContinue) gridColIdx += rawRow[prevCi].gridSpan;
|
|
2610
|
+
}
|
|
2611
|
+
const colCount = gridProps.colWidths?.length ?? rawGrid[0]?.reduce((s, c) => s + c.gridSpan, 0) ?? 1;
|
|
2612
|
+
const resolvedCp = resolveCellBorders(cp, ri, gridColIdx, rs, rc.gridSpan, rawGrid.length, colCount, tblBdr);
|
|
2613
|
+
const paras = toArr2(cell?.["w:p"] ?? cell?.p).map(
|
|
2614
|
+
(p) => decodePara2(p, ctx)
|
|
2615
|
+
);
|
|
2616
|
+
cellNodes.push(
|
|
2617
|
+
buildCell(paras.length > 0 ? paras : [buildPara([buildSpan("")])], {
|
|
2618
|
+
cs: rc.gridSpan,
|
|
2619
|
+
rs,
|
|
2620
|
+
props: resolvedCp
|
|
2621
|
+
})
|
|
2622
|
+
);
|
|
2153
2623
|
}
|
|
2154
|
-
return buildRow(cellNodes);
|
|
2624
|
+
return buildRow(cellNodes, rowHeightPt);
|
|
2155
2625
|
});
|
|
2156
2626
|
return buildGrid(rowNodes, gridProps);
|
|
2157
2627
|
}
|
|
@@ -2159,12 +2629,16 @@ function decodeGridSimple2(tbl) {
|
|
|
2159
2629
|
const rowArr = toArr2(tbl?.["w:tr"] ?? tbl?.tr);
|
|
2160
2630
|
const rowNodes = rowArr.map((row) => {
|
|
2161
2631
|
const cellArr = toArr2(row?.["w:tc"] ?? row?.tc);
|
|
2162
|
-
return buildRow(
|
|
2632
|
+
return buildRow(
|
|
2633
|
+
cellArr.map((c) => buildCell([buildPara([buildSpan(cellText2(c))])]))
|
|
2634
|
+
);
|
|
2163
2635
|
});
|
|
2164
2636
|
return buildGrid(rowNodes);
|
|
2165
2637
|
}
|
|
2166
2638
|
function decodeGridFlat2(tbl) {
|
|
2167
|
-
return buildGrid([
|
|
2639
|
+
return buildGrid([
|
|
2640
|
+
buildRow([buildCell([buildPara([buildSpan(tableText2(tbl))])])])
|
|
2641
|
+
]);
|
|
2168
2642
|
}
|
|
2169
2643
|
function decodeGridText2(tbl) {
|
|
2170
2644
|
return buildPara([buildSpan(tableText2(tbl))]);
|
|
@@ -2191,6 +2665,82 @@ function parseHeading(style) {
|
|
|
2191
2665
|
return void 0;
|
|
2192
2666
|
}
|
|
2193
2667
|
registry.registerDecoder(new DocxDecoder());
|
|
2668
|
+
function extractAnchorLayout(anchor) {
|
|
2669
|
+
const attr = anchor?._attr ?? {};
|
|
2670
|
+
const behindDoc = attr.behindDoc === "1";
|
|
2671
|
+
let wrap = "square";
|
|
2672
|
+
if (anchor?.["wp:wrapNone"]?.[0] != null) wrap = behindDoc ? "behind" : "none";
|
|
2673
|
+
else if (anchor?.["wp:wrapTight"]?.[0] != null) wrap = "tight";
|
|
2674
|
+
else if (anchor?.["wp:wrapThrough"]?.[0] != null) wrap = "through";
|
|
2675
|
+
else if (anchor?.["wp:wrapSquare"]?.[0] != null) wrap = "square";
|
|
2676
|
+
else if (anchor?.["wp:wrapTopAndBottom"]?.[0] != null) wrap = "square";
|
|
2677
|
+
else if (anchor?.["wp:wrapBehind"]?.[0] != null || behindDoc) wrap = "behind";
|
|
2678
|
+
const posH = anchor?.["wp:positionH"]?.[0];
|
|
2679
|
+
const horzRelTo = parseHorzRelTo(posH?._attr?.relativeFrom);
|
|
2680
|
+
const horzAlignTxt = posH?.["wp:align"]?.[0]?._text;
|
|
2681
|
+
const horzOffsetTxt = posH?.["wp:posOffset"]?.[0]?._text;
|
|
2682
|
+
const horzAlign = horzAlignTxt ? parseHorzAlign(horzAlignTxt) : void 0;
|
|
2683
|
+
const xPt = horzOffsetTxt && !horzAlignTxt ? Metric.emuToPt(Number(horzOffsetTxt)) : void 0;
|
|
2684
|
+
const posV = anchor?.["wp:positionV"]?.[0];
|
|
2685
|
+
const vertRelTo = parseVertRelTo(posV?._attr?.relativeFrom);
|
|
2686
|
+
const vertAlignTxt = posV?.["wp:align"]?.[0]?._text;
|
|
2687
|
+
const vertOffsetTxt = posV?.["wp:posOffset"]?.[0]?._text;
|
|
2688
|
+
const vertAlign = vertAlignTxt ? parseVertAlign(vertAlignTxt) : void 0;
|
|
2689
|
+
const yPt = vertOffsetTxt && !vertAlignTxt ? Metric.emuToPt(Number(vertOffsetTxt)) : void 0;
|
|
2690
|
+
const distT = attr.distT ? Metric.emuToPt(Number(attr.distT)) : void 0;
|
|
2691
|
+
const distB = attr.distB ? Metric.emuToPt(Number(attr.distB)) : void 0;
|
|
2692
|
+
const distL = attr.distL ? Metric.emuToPt(Number(attr.distL)) : void 0;
|
|
2693
|
+
const distR = attr.distR ? Metric.emuToPt(Number(attr.distR)) : void 0;
|
|
2694
|
+
const zOrder = attr.relativeHeight ? Number(attr.relativeHeight) : void 0;
|
|
2695
|
+
return { wrap, horzAlign, vertAlign, horzRelTo, vertRelTo, xPt, yPt, distT, distB, distL, distR, behindDoc, zOrder };
|
|
2696
|
+
}
|
|
2697
|
+
var HORZ_RELTO_MAP = {
|
|
2698
|
+
margin: "margin",
|
|
2699
|
+
leftMargin: "margin",
|
|
2700
|
+
rightMargin: "margin",
|
|
2701
|
+
insideMargin: "margin",
|
|
2702
|
+
outsideMargin: "margin",
|
|
2703
|
+
column: "column",
|
|
2704
|
+
page: "page",
|
|
2705
|
+
character: "para",
|
|
2706
|
+
paragraph: "para"
|
|
2707
|
+
};
|
|
2708
|
+
var VERT_RELTO_MAP = {
|
|
2709
|
+
margin: "margin",
|
|
2710
|
+
topMargin: "margin",
|
|
2711
|
+
bottomMargin: "margin",
|
|
2712
|
+
insideMargin: "margin",
|
|
2713
|
+
outsideMargin: "margin",
|
|
2714
|
+
line: "line",
|
|
2715
|
+
page: "page",
|
|
2716
|
+
paragraph: "para"
|
|
2717
|
+
};
|
|
2718
|
+
var HORZ_ALIGN_MAP = {
|
|
2719
|
+
left: "left",
|
|
2720
|
+
center: "center",
|
|
2721
|
+
right: "right",
|
|
2722
|
+
inside: "left",
|
|
2723
|
+
outside: "right"
|
|
2724
|
+
};
|
|
2725
|
+
var VERT_ALIGN_MAP = {
|
|
2726
|
+
top: "top",
|
|
2727
|
+
center: "center",
|
|
2728
|
+
bottom: "bottom",
|
|
2729
|
+
inside: "top",
|
|
2730
|
+
outside: "bottom"
|
|
2731
|
+
};
|
|
2732
|
+
function parseHorzRelTo(v) {
|
|
2733
|
+
return HORZ_RELTO_MAP[v ?? ""] ?? "column";
|
|
2734
|
+
}
|
|
2735
|
+
function parseVertRelTo(v) {
|
|
2736
|
+
return VERT_RELTO_MAP[v ?? ""] ?? "para";
|
|
2737
|
+
}
|
|
2738
|
+
function parseHorzAlign(v) {
|
|
2739
|
+
return HORZ_ALIGN_MAP[v ?? ""];
|
|
2740
|
+
}
|
|
2741
|
+
function parseVertAlign(v) {
|
|
2742
|
+
return VERT_ALIGN_MAP[v ?? ""];
|
|
2743
|
+
}
|
|
2194
2744
|
|
|
2195
2745
|
// src/decoders/md/MdDecoder.ts
|
|
2196
2746
|
var MdDecoder = class {
|
|
@@ -2391,6 +2941,7 @@ var NS = [
|
|
|
2391
2941
|
'xmlns:dc="http://purl.org/dc/elements/1.1/"',
|
|
2392
2942
|
'xmlns:opf="http://www.idpf.org/2007/opf/"',
|
|
2393
2943
|
'xmlns:ooxmlchart="http://www.hancom.co.kr/hwpml/2016/ooxmlchart"',
|
|
2944
|
+
'xmlns:hwpunitchar="http://www.hancom.co.kr/hwpml/2016/HwpUnitChar"',
|
|
2394
2945
|
'xmlns:epub="http://www.idpf.org/2007/ops"',
|
|
2395
2946
|
'xmlns:config="urn:oasis:names:tc:opendocument:xmlns:config:1.0"'
|
|
2396
2947
|
].join(" ");
|
|
@@ -2398,7 +2949,7 @@ function charPrKey(p) {
|
|
|
2398
2949
|
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 ?? ""}`;
|
|
2399
2950
|
}
|
|
2400
2951
|
function paraPrKey(p) {
|
|
2401
|
-
return `${p.align ?? "left"}|${p.listOrd ?? ""}|${p.listLv ?? 0}`;
|
|
2952
|
+
return `${p.align ?? "left"}|${p.listOrd ?? ""}|${p.listLv ?? 0}|${p.indentPt ?? 0}|${p.spaceBefore ?? 0}|${p.spaceAfter ?? 0}|${p.lineHeight ?? 0}`;
|
|
2402
2953
|
}
|
|
2403
2954
|
function registerFont(name, ctx) {
|
|
2404
2955
|
const n = name || "\uAD74\uB9BC\uCCB4";
|
|
@@ -2439,7 +2990,11 @@ function registerParaPr(props, ctx) {
|
|
|
2439
2990
|
const id = ctx.paraPrs.length;
|
|
2440
2991
|
const def = {
|
|
2441
2992
|
id,
|
|
2442
|
-
align: (props.align ?? "left").toUpperCase()
|
|
2993
|
+
align: (props.align ?? "left").toUpperCase(),
|
|
2994
|
+
intentHwp: props.indentPt ? Metric.ptToHwp(props.indentPt) : 0,
|
|
2995
|
+
prevHwp: props.spaceBefore ? Metric.ptToHwp(props.spaceBefore) : 0,
|
|
2996
|
+
nextHwp: props.spaceAfter ? Metric.ptToHwp(props.spaceAfter) : 0,
|
|
2997
|
+
lineSpacing: props.lineHeight ? Math.round(props.lineHeight * 100) : 160
|
|
2443
2998
|
};
|
|
2444
2999
|
if (props.listOrd !== void 0) {
|
|
2445
3000
|
def.listType = props.listOrd ? "DIGIT" : "BULLET";
|
|
@@ -2464,8 +3019,7 @@ function scanPara(para, ctx) {
|
|
|
2464
3019
|
}
|
|
2465
3020
|
function scanGrid(grid, ctx) {
|
|
2466
3021
|
for (const row of grid.kids)
|
|
2467
|
-
for (const cell of row.kids)
|
|
2468
|
-
for (const p of cell.kids) scanPara(p, ctx);
|
|
3022
|
+
for (const cell of row.kids) for (const p of cell.kids) scanPara(p, ctx);
|
|
2469
3023
|
}
|
|
2470
3024
|
function scanParas(paras, ctx) {
|
|
2471
3025
|
for (const p of paras) scanPara(p, ctx);
|
|
@@ -2477,19 +3031,25 @@ function mimeToExt(mime) {
|
|
|
2477
3031
|
return "png";
|
|
2478
3032
|
}
|
|
2479
3033
|
function registerImage(img, ctx) {
|
|
2480
|
-
if (img
|
|
3034
|
+
if (ctx.imgMap.has(img)) return;
|
|
2481
3035
|
const ext = mimeToExt(img.mime);
|
|
2482
3036
|
const id = `BIN${String(ctx.nextBinNum).padStart(4, "0")}`;
|
|
2483
3037
|
const name = `${id}.${ext}`;
|
|
2484
3038
|
ctx.nextBinNum++;
|
|
2485
3039
|
const data = TextKit.base64Decode(img.b64);
|
|
2486
3040
|
ctx.bins.push({ id, name, data });
|
|
2487
|
-
img
|
|
3041
|
+
ctx.imgMap.set(img, id);
|
|
2488
3042
|
}
|
|
2489
3043
|
function addBorderFill(ctx, stroke, bgColor) {
|
|
2490
3044
|
const id = ctx.borderFills.length + 1;
|
|
2491
3045
|
const s = stroke ?? DEFAULT_STROKE;
|
|
2492
|
-
const kindMap = {
|
|
3046
|
+
const kindMap = {
|
|
3047
|
+
solid: "SOLID",
|
|
3048
|
+
dash: "DASH",
|
|
3049
|
+
dot: "DOT",
|
|
3050
|
+
double: "DOUBLE",
|
|
3051
|
+
none: "NONE"
|
|
3052
|
+
};
|
|
2493
3053
|
const type = kindMap[s.kind] ?? "SOLID";
|
|
2494
3054
|
const w = `${(s.pt * 0.3528).toFixed(2)} mm`;
|
|
2495
3055
|
const c = s.color.startsWith("#") ? s.color : `#${s.color}`;
|
|
@@ -2498,7 +3058,31 @@ function addBorderFill(ctx, stroke, bgColor) {
|
|
|
2498
3058
|
const bc = bgColor.startsWith("#") ? bgColor : `#${bgColor}`;
|
|
2499
3059
|
fill = `<hc:fillBrush><hc:winBrush faceColor="${bc}" hatchColor="none" alpha="0"/></hc:fillBrush>`;
|
|
2500
3060
|
}
|
|
2501
|
-
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="
|
|
3061
|
+
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>`;
|
|
3062
|
+
ctx.borderFills.push({ id, xml });
|
|
3063
|
+
return id;
|
|
3064
|
+
}
|
|
3065
|
+
function addBorderFillPerSide(ctx, top, right, bottom, left, bgColor) {
|
|
3066
|
+
const id = ctx.borderFills.length + 1;
|
|
3067
|
+
const kindMap = {
|
|
3068
|
+
solid: "SOLID",
|
|
3069
|
+
dash: "DASH",
|
|
3070
|
+
dot: "DOT",
|
|
3071
|
+
double: "DOUBLE",
|
|
3072
|
+
none: "NONE"
|
|
3073
|
+
};
|
|
3074
|
+
function sideXml(tag, s) {
|
|
3075
|
+
const type = s ? kindMap[s.kind] ?? "SOLID" : "NONE";
|
|
3076
|
+
const w = s ? `${(s.pt * 0.3528).toFixed(2)} mm` : "0.12 mm";
|
|
3077
|
+
const c = s ? s.color.startsWith("#") ? s.color : `#${s.color}` : "#000000";
|
|
3078
|
+
return `<hh:${tag} type="${type}" width="${w}" color="${c}"/>`;
|
|
3079
|
+
}
|
|
3080
|
+
let fill = "";
|
|
3081
|
+
if (bgColor) {
|
|
3082
|
+
const bc = bgColor.startsWith("#") ? bgColor : `#${bgColor}`;
|
|
3083
|
+
fill = `<hc:fillBrush><hc:winBrush faceColor="${bc}" hatchColor="none" alpha="0"/></hc:fillBrush>`;
|
|
3084
|
+
}
|
|
3085
|
+
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>`;
|
|
2502
3086
|
ctx.borderFills.push({ id, xml });
|
|
2503
3087
|
return id;
|
|
2504
3088
|
}
|
|
@@ -2509,7 +3093,7 @@ var HwpxEncoder = class {
|
|
|
2509
3093
|
async encode(doc) {
|
|
2510
3094
|
try {
|
|
2511
3095
|
const sheet = doc.kids[0];
|
|
2512
|
-
const dims = sheet?.dims ?? A4;
|
|
3096
|
+
const dims = normalizeDims(sheet?.dims ?? A4);
|
|
2513
3097
|
const availableWidth = Metric.ptToHwp(dims.wPt) - Metric.ptToHwp(dims.ml) - Metric.ptToHwp(dims.mr);
|
|
2514
3098
|
const ctx = {
|
|
2515
3099
|
charPrs: [],
|
|
@@ -2522,7 +3106,9 @@ var HwpxEncoder = class {
|
|
|
2522
3106
|
nextElementId: 1e4,
|
|
2523
3107
|
availableWidth,
|
|
2524
3108
|
fonts: [],
|
|
2525
|
-
fontMap: /* @__PURE__ */ new Map()
|
|
3109
|
+
fontMap: /* @__PURE__ */ new Map(),
|
|
3110
|
+
imgMap: /* @__PURE__ */ new WeakMap(),
|
|
3111
|
+
nextZOrder: 0
|
|
2526
3112
|
};
|
|
2527
3113
|
addBorderFill(ctx, { kind: "none", pt: 0.1, color: "000000" });
|
|
2528
3114
|
addBorderFill(ctx, DEFAULT_STROKE);
|
|
@@ -2543,7 +3129,10 @@ var HwpxEncoder = class {
|
|
|
2543
3129
|
{ name: "Preview/PrvText.txt", data: TextKit.encode(previewText) },
|
|
2544
3130
|
{ name: "settings.xml", data: TextKit.encode(SETTINGS_XML) },
|
|
2545
3131
|
{ name: "META-INF/container.rdf", data: TextKit.encode(CONTAINER_RDF) },
|
|
2546
|
-
{
|
|
3132
|
+
{
|
|
3133
|
+
name: "Contents/content.hpf",
|
|
3134
|
+
data: TextKit.encode(contentHpf(ctx, doc.meta))
|
|
3135
|
+
},
|
|
2547
3136
|
{ name: "META-INF/container.xml", data: TextKit.encode(CONTAINER_XML) },
|
|
2548
3137
|
{ name: "META-INF/manifest.xml", data: TextKit.encode(MANIFEST_XML) }
|
|
2549
3138
|
];
|
|
@@ -2556,7 +3145,7 @@ var HwpxEncoder = class {
|
|
|
2556
3145
|
}
|
|
2557
3146
|
}
|
|
2558
3147
|
};
|
|
2559
|
-
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="
|
|
3148
|
+
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"/>`;
|
|
2560
3149
|
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>`;
|
|
2561
3150
|
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>`;
|
|
2562
3151
|
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"/>`;
|
|
@@ -2570,11 +3159,19 @@ function contentHpf(ctx, meta) {
|
|
|
2570
3159
|
const ct = ext === "png" ? "image/png" : ext === "jpg" || ext === "jpeg" ? "image/jpeg" : ext === "gif" ? "image/gif" : "image/bmp";
|
|
2571
3160
|
items += `<opf:item id="${bin.id}" href="BinData/${bin.name}" media-type="${ct}" isEmbeded="1"/>`;
|
|
2572
3161
|
}
|
|
2573
|
-
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="
|
|
3162
|
+
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>`;
|
|
2574
3163
|
}
|
|
2575
3164
|
function headerXml(dims, meta, ctx) {
|
|
2576
3165
|
const fontCount = ctx.fonts.length || 1;
|
|
2577
|
-
const langs = [
|
|
3166
|
+
const langs = [
|
|
3167
|
+
"HANGUL",
|
|
3168
|
+
"LATIN",
|
|
3169
|
+
"HANJA",
|
|
3170
|
+
"JAPANESE",
|
|
3171
|
+
"OTHER",
|
|
3172
|
+
"SYMBOL",
|
|
3173
|
+
"USER"
|
|
3174
|
+
];
|
|
2578
3175
|
let fontFaces = `<hh:fontfaces itemCnt="${langs.length}">`;
|
|
2579
3176
|
for (const lang of langs) {
|
|
2580
3177
|
fontFaces += `<hh:fontface lang="${lang}" fontCnt="${fontCount}">`;
|
|
@@ -2596,14 +3193,15 @@ function headerXml(dims, meta, ctx) {
|
|
|
2596
3193
|
}
|
|
2597
3194
|
let paraPrXml = "";
|
|
2598
3195
|
for (const pp of ctx.paraPrs) {
|
|
2599
|
-
|
|
3196
|
+
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>`;
|
|
3197
|
+
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>`;
|
|
2600
3198
|
}
|
|
2601
3199
|
const borderFillXml = ctx.borderFills.map((bf) => bf.xml).join("");
|
|
2602
|
-
return `<?xml version="1.0" encoding="UTF-8" standalone="yes" ?><hh:head ${NS} version="1.
|
|
3200
|
+
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>`;
|
|
2603
3201
|
}
|
|
2604
3202
|
function sectionXml(sheet, dims, ctx) {
|
|
2605
3203
|
const kids = sheet?.kids ?? [];
|
|
2606
|
-
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>`;
|
|
3204
|
+
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>`;
|
|
2607
3205
|
let contentXml = "";
|
|
2608
3206
|
let isFirst = true;
|
|
2609
3207
|
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>`;
|
|
@@ -2614,17 +3212,38 @@ function sectionXml(sheet, dims, ctx) {
|
|
|
2614
3212
|
} else if (kid.tag === "grid") {
|
|
2615
3213
|
const gridXml = encodeGrid(kid, ctx);
|
|
2616
3214
|
const prefix = isFirst ? secPr : "";
|
|
2617
|
-
|
|
3215
|
+
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>`;
|
|
3216
|
+
contentXml += `<hp:p id="0" paraPrIDRef="0" styleIDRef="0" pageBreak="0" columnBreak="0" merged="0">${runsXml}${defaultLineseg}</hp:p>`;
|
|
2618
3217
|
isFirst = false;
|
|
2619
3218
|
}
|
|
2620
3219
|
}
|
|
2621
3220
|
if (contentXml === "") {
|
|
2622
|
-
contentXml = `<hp:p id="
|
|
3221
|
+
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>`;
|
|
2623
3222
|
}
|
|
2624
3223
|
return `<?xml version="1.0" encoding="UTF-8" standalone="yes" ?><hs:sec ${NS}>${contentXml}</hs:sec>`;
|
|
2625
3224
|
}
|
|
2626
|
-
function
|
|
3225
|
+
function estimateCellHeight(cell, ctx) {
|
|
3226
|
+
const topPad = 141;
|
|
3227
|
+
const botPad = 141;
|
|
3228
|
+
let contentHeight = 0;
|
|
3229
|
+
for (const para of cell.kids) {
|
|
3230
|
+
const fontSize = getFontSizeForPara(para, ctx);
|
|
3231
|
+
const paraPrId = ctx.paraPrMap.get(paraPrKey(para.props));
|
|
3232
|
+
const paraPr = paraPrId !== void 0 ? ctx.paraPrs[paraPrId] : null;
|
|
3233
|
+
const lineSpacing = paraPr ? paraPr.lineSpacing : 160;
|
|
3234
|
+
const spaceBefore = paraPr ? paraPr.prevHwp : 0;
|
|
3235
|
+
const spaceAfter = paraPr ? paraPr.nextHwp : 0;
|
|
3236
|
+
const lineHeight = Math.round(fontSize * lineSpacing / 100);
|
|
3237
|
+
contentHeight += lineHeight + spaceBefore + spaceAfter;
|
|
3238
|
+
}
|
|
3239
|
+
if (contentHeight === 0) contentHeight = Math.round(1e3 * 1.6);
|
|
3240
|
+
return contentHeight + topPad + botPad;
|
|
3241
|
+
}
|
|
3242
|
+
function encodePara(para, ctx, secPr = "", availWidth) {
|
|
2627
3243
|
const paraPrId = registerParaPr(para.props, ctx);
|
|
3244
|
+
const hasPageBreak = para.kids.some(
|
|
3245
|
+
(k) => k.tag === "span" && k.kids.some((c) => c.tag === "pb")
|
|
3246
|
+
);
|
|
2628
3247
|
let runs = "";
|
|
2629
3248
|
for (const kid of para.kids) {
|
|
2630
3249
|
if (kid.tag === "span") {
|
|
@@ -2643,12 +3262,15 @@ function encodePara(para, ctx, secPr = "") {
|
|
|
2643
3262
|
}
|
|
2644
3263
|
}
|
|
2645
3264
|
const fontSize = getFontSizeForPara(para, ctx);
|
|
3265
|
+
const paraPr = ctx.paraPrs[paraPrId];
|
|
3266
|
+
const lineSpacing = paraPr?.lineSpacing ?? 160;
|
|
2646
3267
|
const vertsize = fontSize;
|
|
2647
3268
|
const textheight = fontSize;
|
|
2648
3269
|
const baseline = Math.round(fontSize * 0.85);
|
|
2649
|
-
const spacing = Math.round(fontSize *
|
|
2650
|
-
const
|
|
2651
|
-
|
|
3270
|
+
const spacing = Math.max(0, Math.round(fontSize * (lineSpacing / 100 - 1)));
|
|
3271
|
+
const horzsize = availWidth ?? ctx.availableWidth;
|
|
3272
|
+
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>`;
|
|
3273
|
+
return `<hp:p id="0" paraPrIDRef="${paraPrId}" styleIDRef="0" pageBreak="${hasPageBreak ? "1" : "0"}" columnBreak="0" merged="0">${runs}${linesegarray}</hp:p>`;
|
|
2652
3274
|
}
|
|
2653
3275
|
function getFontSizeForPara(para, ctx) {
|
|
2654
3276
|
for (const kid of para.kids) {
|
|
@@ -2681,21 +3303,85 @@ function encodeRun(span, ctx) {
|
|
|
2681
3303
|
}
|
|
2682
3304
|
return `<hp:run charPrIDRef="${charPrId}">${parts.join("")}</hp:run>`;
|
|
2683
3305
|
}
|
|
3306
|
+
var WRAP_HWPX = {
|
|
3307
|
+
inline: "TOP_AND_BOTTOM",
|
|
3308
|
+
square: "SQUARE",
|
|
3309
|
+
tight: "BOTH_SIDES",
|
|
3310
|
+
through: "BOTH_SIDES",
|
|
3311
|
+
none: "FRONT_TEXT",
|
|
3312
|
+
behind: "BEHIND_TEXT",
|
|
3313
|
+
front: "FRONT_TEXT"
|
|
3314
|
+
};
|
|
3315
|
+
var TEXT_FLOW_HWPX = {
|
|
3316
|
+
inline: "BOTH_SIDES",
|
|
3317
|
+
square: "LARGEST_ONLY",
|
|
3318
|
+
tight: "BOTH_SIDES",
|
|
3319
|
+
through: "BOTH_SIDES",
|
|
3320
|
+
none: "BOTH_SIDES",
|
|
3321
|
+
behind: "BOTH_SIDES",
|
|
3322
|
+
front: "BOTH_SIDES"
|
|
3323
|
+
};
|
|
3324
|
+
var HORZ_RELTO_HWPX = {
|
|
3325
|
+
para: "PARA",
|
|
3326
|
+
margin: "MARGIN",
|
|
3327
|
+
page: "PAPER",
|
|
3328
|
+
column: "COLUMN"
|
|
3329
|
+
};
|
|
3330
|
+
var VERT_RELTO_HWPX = {
|
|
3331
|
+
para: "PARA",
|
|
3332
|
+
margin: "MARGIN",
|
|
3333
|
+
page: "PAPER",
|
|
3334
|
+
line: "LINE"
|
|
3335
|
+
};
|
|
2684
3336
|
function encodeImage(img, ctx) {
|
|
2685
|
-
const binId = img
|
|
3337
|
+
const binId = ctx.imgMap.get(img);
|
|
2686
3338
|
if (!binId) return "";
|
|
2687
3339
|
const charPrId = registerCharPr({}, ctx);
|
|
2688
3340
|
const w = Metric.ptToHwp(img.w);
|
|
2689
3341
|
const h = Metric.ptToHwp(img.h);
|
|
2690
|
-
|
|
3342
|
+
const cx = Math.round(w / 2);
|
|
3343
|
+
const cy = Math.round(h / 2);
|
|
3344
|
+
const layout = img.layout;
|
|
3345
|
+
const isInline = !layout || layout.wrap === "inline";
|
|
3346
|
+
const textWrap = layout ? WRAP_HWPX[layout.wrap] ?? "TOP_AND_BOTTOM" : "TOP_AND_BOTTOM";
|
|
3347
|
+
const textFlow = layout ? TEXT_FLOW_HWPX[layout.wrap] ?? "BOTH_SIDES" : "BOTH_SIDES";
|
|
3348
|
+
const treatAsChar = isInline ? "1" : "0";
|
|
3349
|
+
const flowWithText = "1";
|
|
3350
|
+
const allowOverlap = !isInline && layout?.wrap !== "behind" && layout?.wrap !== "front" ? "1" : "0";
|
|
3351
|
+
const horzRelTo = layout?.horzRelTo ? HORZ_RELTO_HWPX[layout.horzRelTo] ?? "PARA" : "PARA";
|
|
3352
|
+
const vertRelTo = layout?.vertRelTo ? VERT_RELTO_HWPX[layout.vertRelTo] ?? "PARA" : "PARA";
|
|
3353
|
+
const ALIGN_H = { left: "LEFT", center: "CENTER", right: "RIGHT" };
|
|
3354
|
+
const ALIGN_V = { top: "TOP", center: "CENTER", bottom: "BOTTOM" };
|
|
3355
|
+
const horzAlign = layout?.horzAlign ? ALIGN_H[layout.horzAlign] ?? "LEFT" : "LEFT";
|
|
3356
|
+
const vertAlign = layout?.vertAlign ? ALIGN_V[layout.vertAlign] ?? "TOP" : "TOP";
|
|
3357
|
+
const horzOffset = layout?.xPt != null ? Metric.ptToHwp(layout.xPt) : 0;
|
|
3358
|
+
const vertOffset = layout?.yPt != null ? Metric.ptToHwp(layout.yPt) : 0;
|
|
3359
|
+
const zOrder = ctx.nextZOrder++;
|
|
3360
|
+
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>`;
|
|
2691
3361
|
}
|
|
2692
3362
|
function encodeGrid(grid, ctx) {
|
|
2693
3363
|
const rowCount = grid.kids.length;
|
|
3364
|
+
const rowOccupancy = Array.from(
|
|
3365
|
+
{ length: rowCount },
|
|
3366
|
+
() => /* @__PURE__ */ new Set()
|
|
3367
|
+
);
|
|
2694
3368
|
let colCount = 0;
|
|
2695
|
-
for (
|
|
2696
|
-
|
|
2697
|
-
|
|
2698
|
-
|
|
3369
|
+
for (let ri = 0; ri < grid.kids.length; ri++) {
|
|
3370
|
+
const row = grid.kids[ri];
|
|
3371
|
+
let ci = 0;
|
|
3372
|
+
for (const cell of row.kids) {
|
|
3373
|
+
while (rowOccupancy[ri].has(ci)) ci++;
|
|
3374
|
+
if (cell.rs > 1) {
|
|
3375
|
+
for (let r = ri + 1; r < ri + cell.rs && r < rowCount; r++) {
|
|
3376
|
+
for (let c = ci; c < ci + cell.cs; c++) {
|
|
3377
|
+
rowOccupancy[r].add(c);
|
|
3378
|
+
}
|
|
3379
|
+
}
|
|
3380
|
+
}
|
|
3381
|
+
ci += cell.cs;
|
|
3382
|
+
}
|
|
3383
|
+
while (rowOccupancy[ri].has(ci)) ci++;
|
|
3384
|
+
if (ci > colCount) colCount = ci;
|
|
2699
3385
|
}
|
|
2700
3386
|
if (colCount === 0) colCount = grid.kids[0]?.kids.length ?? 1;
|
|
2701
3387
|
const totalWidth = ctx.availableWidth;
|
|
@@ -2708,7 +3394,8 @@ function encodeGrid(grid, ctx) {
|
|
|
2708
3394
|
const remaining = Math.max(0, Metric.hwpToPt(totalWidth) - knownTotal);
|
|
2709
3395
|
const zeroFill = zeroCount > 0 ? remaining / zeroCount : 0;
|
|
2710
3396
|
for (let i = 0; i < srcPt.length; i++) {
|
|
2711
|
-
if (srcPt[i] <= 0)
|
|
3397
|
+
if (srcPt[i] <= 0)
|
|
3398
|
+
srcPt[i] = zeroFill > 0 ? zeroFill : Metric.hwpToPt(defaultColW);
|
|
2712
3399
|
}
|
|
2713
3400
|
for (const wPt of srcPt) colWidths.push(Metric.ptToHwp(wPt));
|
|
2714
3401
|
} else {
|
|
@@ -2717,32 +3404,69 @@ function encodeGrid(grid, ctx) {
|
|
|
2717
3404
|
const rawTotal = colWidths.reduce((s, w) => s + w, 0);
|
|
2718
3405
|
if (rawTotal > totalWidth * 1.05) {
|
|
2719
3406
|
const scale = totalWidth / rawTotal;
|
|
2720
|
-
for (let i = 0; i < colWidths.length; i++)
|
|
3407
|
+
for (let i = 0; i < colWidths.length; i++)
|
|
3408
|
+
colWidths[i] = Math.round(colWidths[i] * scale);
|
|
2721
3409
|
}
|
|
2722
3410
|
const actualTotal = colWidths.reduce((s, w) => s + w, 0);
|
|
2723
3411
|
const tblBfId = grid.props.defaultStroke ? addBorderFill(ctx, grid.props.defaultStroke) : 2;
|
|
3412
|
+
const rowHeights = [];
|
|
3413
|
+
for (const row of grid.kids) {
|
|
3414
|
+
if (row.heightPt != null && row.heightPt > 0) {
|
|
3415
|
+
rowHeights.push(Metric.ptToHwp(row.heightPt));
|
|
3416
|
+
} else {
|
|
3417
|
+
let maxH = 0;
|
|
3418
|
+
for (const cell of row.kids) {
|
|
3419
|
+
const h = estimateCellHeight(cell, ctx);
|
|
3420
|
+
if (h > maxH) maxH = h;
|
|
3421
|
+
}
|
|
3422
|
+
rowHeights.push(maxH);
|
|
3423
|
+
}
|
|
3424
|
+
}
|
|
3425
|
+
const totalTableHeight = rowHeights.reduce((s, h) => s + h, 0);
|
|
2724
3426
|
let rowsXml = "";
|
|
2725
3427
|
for (let ri = 0; ri < grid.kids.length; ri++) {
|
|
2726
3428
|
const row = grid.kids[ri];
|
|
3429
|
+
const rowH = rowHeights[ri];
|
|
2727
3430
|
let cellsXml = "";
|
|
2728
3431
|
let colIdx = 0;
|
|
2729
3432
|
for (let ci = 0; ci < row.kids.length; ci++) {
|
|
2730
3433
|
const cell = row.kids[ci];
|
|
3434
|
+
while (rowOccupancy[ri].has(colIdx)) colIdx++;
|
|
2731
3435
|
let cellBfId = tblBfId;
|
|
2732
|
-
|
|
2733
|
-
|
|
3436
|
+
const cp = cell.props;
|
|
3437
|
+
const hasPerSideBorder = cp.top || cp.bot || cp.left || cp.right;
|
|
3438
|
+
if (hasPerSideBorder || cp.bg) {
|
|
3439
|
+
if (hasPerSideBorder) {
|
|
3440
|
+
const defStroke = grid.props.defaultStroke ?? DEFAULT_STROKE;
|
|
3441
|
+
cellBfId = addBorderFillPerSide(
|
|
3442
|
+
ctx,
|
|
3443
|
+
cp.top ?? defStroke,
|
|
3444
|
+
cp.right ?? defStroke,
|
|
3445
|
+
cp.bot ?? defStroke,
|
|
3446
|
+
cp.left ?? defStroke,
|
|
3447
|
+
cp.bg
|
|
3448
|
+
);
|
|
3449
|
+
} else {
|
|
3450
|
+
cellBfId = addBorderFill(
|
|
3451
|
+
ctx,
|
|
3452
|
+
grid.props.defaultStroke ?? DEFAULT_STROKE,
|
|
3453
|
+
cp.bg
|
|
3454
|
+
);
|
|
3455
|
+
}
|
|
2734
3456
|
}
|
|
2735
|
-
const parasXml = cell.kids.map((p) => encodePara(p, ctx)).join("");
|
|
2736
3457
|
let cellW = 0;
|
|
2737
|
-
for (let sc = colIdx; sc < colIdx + cell.cs && sc < colWidths.length; sc++)
|
|
3458
|
+
for (let sc = colIdx; sc < colIdx + cell.cs && sc < colWidths.length; sc++)
|
|
3459
|
+
cellW += colWidths[sc];
|
|
2738
3460
|
if (cellW === 0) cellW = defaultColW * cell.cs;
|
|
2739
|
-
|
|
3461
|
+
const cellInnerW = Math.max(cellW - 282, 100);
|
|
3462
|
+
const parasXml = cell.kids.map((p) => encodePara(p, ctx, "", cellInnerW)).join("");
|
|
3463
|
+
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>`;
|
|
2740
3464
|
colIdx += cell.cs;
|
|
2741
3465
|
}
|
|
2742
3466
|
rowsXml += `<hp:tr>${cellsXml}</hp:tr>`;
|
|
2743
3467
|
}
|
|
2744
3468
|
const headerRow = grid.props.headerRow ? ' repeatHeader="1"' : "";
|
|
2745
|
-
return `<hp:tbl id="${ctx.nextElementId++}" zOrder="0" numberingType="TABLE" textWrap="TOP_AND_BOTTOM" textFlow="BOTH_SIDES" lock="0" dropcapstyle="None" pageBreak="
|
|
3469
|
+
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>`;
|
|
2746
3470
|
}
|
|
2747
3471
|
function extractPreviewText(sheet) {
|
|
2748
3472
|
if (!sheet) return "";
|
|
@@ -2750,17 +3474,21 @@ function extractPreviewText(sheet) {
|
|
|
2750
3474
|
for (const kid of sheet.kids) {
|
|
2751
3475
|
if (kid.tag === "para") {
|
|
2752
3476
|
const text = kid.kids.map((k) => {
|
|
2753
|
-
if (k.tag === "span")
|
|
3477
|
+
if (k.tag === "span")
|
|
3478
|
+
return k.kids.map((c) => c.tag === "txt" ? c.content : "").join("");
|
|
2754
3479
|
return "";
|
|
2755
3480
|
}).join("");
|
|
2756
3481
|
if (text) lines.push(text);
|
|
2757
3482
|
} else if (kid.tag === "grid") {
|
|
2758
3483
|
for (const row of kid.kids) {
|
|
2759
3484
|
const cells = row.kids.map(
|
|
2760
|
-
(cell) => cell.kids.map(
|
|
2761
|
-
|
|
2762
|
-
|
|
2763
|
-
|
|
3485
|
+
(cell) => cell.kids.map(
|
|
3486
|
+
(p) => p.kids.map((k) => {
|
|
3487
|
+
if (k.tag === "span")
|
|
3488
|
+
return k.kids.map((c) => c.tag === "txt" ? c.content : "").join("");
|
|
3489
|
+
return "";
|
|
3490
|
+
}).join("")
|
|
3491
|
+
).join("")
|
|
2764
3492
|
);
|
|
2765
3493
|
lines.push(cells.join(" "));
|
|
2766
3494
|
}
|
|
@@ -2781,10 +3509,10 @@ var DocxEncoder = class {
|
|
|
2781
3509
|
async encode(doc) {
|
|
2782
3510
|
try {
|
|
2783
3511
|
const sheet = doc.kids[0];
|
|
2784
|
-
const dims = sheet?.dims ?? A4;
|
|
3512
|
+
const dims = normalizeDims(sheet?.dims ?? A4);
|
|
2785
3513
|
const kids = sheet?.kids ?? [];
|
|
2786
3514
|
const images = [];
|
|
2787
|
-
const ctx = { images, nextId: 10, nextImgNum: 1, warns: [] };
|
|
3515
|
+
const ctx = { images, nextId: 10, nextImgNum: 1, warns: [], imgMap: /* @__PURE__ */ new WeakMap() };
|
|
2788
3516
|
collectImages(kids, ctx);
|
|
2789
3517
|
let headerParas = sheet?.header;
|
|
2790
3518
|
let footerParas = sheet?.footer;
|
|
@@ -2848,13 +3576,13 @@ function collectImagesFromPara(para, ctx) {
|
|
|
2848
3576
|
}
|
|
2849
3577
|
}
|
|
2850
3578
|
function registerImage2(img, ctx) {
|
|
2851
|
-
if (img
|
|
3579
|
+
if (ctx.imgMap.has(img)) return;
|
|
2852
3580
|
const ext = mimeToExt2(img.mime);
|
|
2853
3581
|
const name = `image${ctx.nextImgNum++}.${ext}`;
|
|
2854
3582
|
const rId = `rId${ctx.nextId++}`;
|
|
2855
3583
|
const data = TextKit.base64Decode(img.b64);
|
|
2856
3584
|
ctx.images.push({ rId, name, data, ext });
|
|
2857
|
-
img
|
|
3585
|
+
ctx.imgMap.set(img, rId);
|
|
2858
3586
|
}
|
|
2859
3587
|
function collectNumbering(kids) {
|
|
2860
3588
|
let hasBullet = false;
|
|
@@ -3049,13 +3777,26 @@ function encodeParaInner(para, ctx) {
|
|
|
3049
3777
|
const ilvl = para.props.listLv ?? 0;
|
|
3050
3778
|
numPr = `<w:pStyle w:val="ListParagraph"/><w:numPr><w:ilvl w:val="${ilvl}"/><w:numId w:val="${numId}"/></w:numPr>`;
|
|
3051
3779
|
}
|
|
3780
|
+
let spacingXml = "";
|
|
3781
|
+
const { spaceBefore, spaceAfter, lineHeight } = para.props;
|
|
3782
|
+
if (spaceBefore !== void 0 || spaceAfter !== void 0 || lineHeight !== void 0) {
|
|
3783
|
+
const parts = [];
|
|
3784
|
+
if (spaceBefore !== void 0) parts.push(`w:before="${Metric.ptToDxa(spaceBefore)}"`);
|
|
3785
|
+
if (spaceAfter !== void 0) parts.push(`w:after="${Metric.ptToDxa(spaceAfter)}"`);
|
|
3786
|
+
if (lineHeight !== void 0) parts.push(`w:line="${Math.round(lineHeight * 240)}" w:lineRule="auto"`);
|
|
3787
|
+
spacingXml = `<w:spacing ${parts.join(" ")}/>`;
|
|
3788
|
+
}
|
|
3789
|
+
let indentXml = "";
|
|
3790
|
+
if (para.props.indentPt !== void 0) {
|
|
3791
|
+
indentXml = `<w:ind w:left="${Metric.ptToDxa(para.props.indentPt)}"/>`;
|
|
3792
|
+
}
|
|
3052
3793
|
const runs = para.kids.map((k) => {
|
|
3053
3794
|
if (k.tag === "span") return encodeRun2(k, ctx);
|
|
3054
3795
|
if (k.tag === "img") return encodeImage2(k, ctx);
|
|
3055
3796
|
return "";
|
|
3056
3797
|
}).join("");
|
|
3057
3798
|
return ` <w:p>
|
|
3058
|
-
<w:pPr>${headStyle}${numPr}<w:jc w:val="${align === "justify" ? "both" : align}"/></w:pPr>
|
|
3799
|
+
<w:pPr>${headStyle}${numPr}${spacingXml}${indentXml}<w:jc w:val="${align === "justify" ? "both" : align}"/></w:pPr>
|
|
3059
3800
|
${runs}
|
|
3060
3801
|
</w:p>`;
|
|
3061
3802
|
}
|
|
@@ -3086,14 +3827,77 @@ function encodeRun2(span, _ctx) {
|
|
|
3086
3827
|
}
|
|
3087
3828
|
return parts.join("");
|
|
3088
3829
|
}
|
|
3089
|
-
function encodeImage2(img,
|
|
3090
|
-
const rId = img
|
|
3830
|
+
function encodeImage2(img, ctx) {
|
|
3831
|
+
const rId = ctx.imgMap.get(img);
|
|
3091
3832
|
if (!rId) return "";
|
|
3092
3833
|
const cx = Metric.ptToEmu(img.w);
|
|
3093
3834
|
const cy = Metric.ptToEmu(img.h);
|
|
3094
3835
|
const alt = esc2(img.alt ?? "");
|
|
3095
|
-
|
|
3836
|
+
const docPrId = ctx.nextId++;
|
|
3837
|
+
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>`;
|
|
3838
|
+
const layout = img.layout;
|
|
3839
|
+
const isInline = !layout || layout.wrap === "inline";
|
|
3840
|
+
if (isInline) {
|
|
3841
|
+
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>`;
|
|
3842
|
+
}
|
|
3843
|
+
return `<w:r><w:drawing>${encodeAnchor(img, cx, cy, alt, docPrId, graphic, layout)}</w:drawing></w:r>`;
|
|
3844
|
+
}
|
|
3845
|
+
function encodeAnchor(_img, cx, cy, alt, docPrId, graphic, layout) {
|
|
3846
|
+
const distT = Metric.ptToEmu(layout.distT ?? 0);
|
|
3847
|
+
const distB = Metric.ptToEmu(layout.distB ?? 0);
|
|
3848
|
+
const distL = Metric.ptToEmu(layout.distL ?? 9144);
|
|
3849
|
+
const distR = Metric.ptToEmu(layout.distR ?? 9144);
|
|
3850
|
+
const behindDoc = layout.behindDoc || layout.wrap === "behind" ? "1" : "0";
|
|
3851
|
+
const relH = layout.zOrder ?? 251658240;
|
|
3852
|
+
const horzRelFrom = HORZ_RELTO_DOCX[layout.horzRelTo ?? "column"] ?? "column";
|
|
3853
|
+
let posH;
|
|
3854
|
+
if (layout.xPt != null) {
|
|
3855
|
+
posH = `<wp:positionH relativeFrom="${horzRelFrom}"><wp:posOffset>${Metric.ptToEmu(layout.xPt)}</wp:posOffset></wp:positionH>`;
|
|
3856
|
+
} else {
|
|
3857
|
+
const ha = HORZ_ALIGN_DOCX[layout.horzAlign ?? "left"] ?? "left";
|
|
3858
|
+
posH = `<wp:positionH relativeFrom="${horzRelFrom}"><wp:align>${ha}</wp:align></wp:positionH>`;
|
|
3859
|
+
}
|
|
3860
|
+
const vertRelFrom = VERT_RELTO_DOCX[layout.vertRelTo ?? "para"] ?? "paragraph";
|
|
3861
|
+
let posV;
|
|
3862
|
+
if (layout.yPt != null) {
|
|
3863
|
+
posV = `<wp:positionV relativeFrom="${vertRelFrom}"><wp:posOffset>${Metric.ptToEmu(layout.yPt)}</wp:posOffset></wp:positionV>`;
|
|
3864
|
+
} else {
|
|
3865
|
+
const va = VERT_ALIGN_DOCX[layout.vertAlign ?? "top"] ?? "top";
|
|
3866
|
+
posV = `<wp:positionV relativeFrom="${vertRelFrom}"><wp:align>${va}</wp:align></wp:positionV>`;
|
|
3867
|
+
}
|
|
3868
|
+
const wrapXml = WRAP_DOCX[layout.wrap] ?? '<wp:wrapSquare wrapText="bothSides"/>';
|
|
3869
|
+
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>`;
|
|
3096
3870
|
}
|
|
3871
|
+
var HORZ_RELTO_DOCX = {
|
|
3872
|
+
margin: "margin",
|
|
3873
|
+
column: "column",
|
|
3874
|
+
page: "page",
|
|
3875
|
+
para: "paragraph"
|
|
3876
|
+
};
|
|
3877
|
+
var VERT_RELTO_DOCX = {
|
|
3878
|
+
margin: "margin",
|
|
3879
|
+
line: "line",
|
|
3880
|
+
page: "page",
|
|
3881
|
+
para: "paragraph"
|
|
3882
|
+
};
|
|
3883
|
+
var HORZ_ALIGN_DOCX = {
|
|
3884
|
+
left: "left",
|
|
3885
|
+
center: "center",
|
|
3886
|
+
right: "right"
|
|
3887
|
+
};
|
|
3888
|
+
var VERT_ALIGN_DOCX = {
|
|
3889
|
+
top: "top",
|
|
3890
|
+
center: "center",
|
|
3891
|
+
bottom: "bottom"
|
|
3892
|
+
};
|
|
3893
|
+
var WRAP_DOCX = {
|
|
3894
|
+
square: '<wp:wrapSquare wrapText="bothSides"/>',
|
|
3895
|
+
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>',
|
|
3896
|
+
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>',
|
|
3897
|
+
none: "<wp:wrapNone/>",
|
|
3898
|
+
behind: "<wp:wrapNone/>",
|
|
3899
|
+
front: "<wp:wrapNone/>"
|
|
3900
|
+
};
|
|
3097
3901
|
function encodeGrid2(grid, ctx, dims) {
|
|
3098
3902
|
const gp = grid.props;
|
|
3099
3903
|
const look = gp.look;
|
|
@@ -3105,129 +3909,115 @@ function encodeGrid2(grid, ctx, dims) {
|
|
|
3105
3909
|
const noVBand = look?.bandedCols ? "0" : "1";
|
|
3106
3910
|
const d = dims ?? A4;
|
|
3107
3911
|
const availDxa = Metric.ptToDxa(d.wPt - d.ml - d.mr);
|
|
3912
|
+
const tableMap = Array.from({ length: grid.kids.length }, () => []);
|
|
3108
3913
|
let colCount = 0;
|
|
3109
|
-
for (
|
|
3110
|
-
let
|
|
3111
|
-
for (const cell of
|
|
3112
|
-
|
|
3914
|
+
for (let ri = 0; ri < grid.kids.length; ri++) {
|
|
3915
|
+
let c = 0;
|
|
3916
|
+
for (const cell of grid.kids[ri].kids) {
|
|
3917
|
+
while (tableMap[ri][c]) c++;
|
|
3918
|
+
tableMap[ri][c] = { type: "real", cell, width: cell.cs };
|
|
3919
|
+
for (let rr = 0; rr < cell.rs; rr++) {
|
|
3920
|
+
for (let cc = 0; cc < cell.cs; cc++) {
|
|
3921
|
+
if (rr === 0 && cc === 0) continue;
|
|
3922
|
+
if (!tableMap[ri + rr]) tableMap[ri + rr] = [];
|
|
3923
|
+
if (rr > 0 && cc === 0) {
|
|
3924
|
+
tableMap[ri + rr][c + cc] = { type: "continue", width: cell.cs };
|
|
3925
|
+
} else {
|
|
3926
|
+
tableMap[ri + rr][c + cc] = { type: "empty" };
|
|
3927
|
+
}
|
|
3928
|
+
}
|
|
3929
|
+
}
|
|
3930
|
+
c += cell.cs;
|
|
3931
|
+
}
|
|
3932
|
+
if (c > colCount) colCount = c;
|
|
3113
3933
|
}
|
|
3114
3934
|
if (colCount === 0) colCount = grid.kids[0]?.kids.length ?? 1;
|
|
3935
|
+
for (let ri = 0; ri < grid.kids.length; ri++) {
|
|
3936
|
+
for (let c = 0; c < colCount; c++) {
|
|
3937
|
+
if (!tableMap[ri][c]) tableMap[ri][c] = { type: "empty" };
|
|
3938
|
+
}
|
|
3939
|
+
}
|
|
3115
3940
|
const defaultColDxa = Math.round(availDxa / colCount);
|
|
3116
|
-
|
|
3117
|
-
if (grid.props.colWidths && grid.props.colWidths.length
|
|
3941
|
+
let colWidthsDxa = [];
|
|
3942
|
+
if (grid.props.colWidths && grid.props.colWidths.length > 0) {
|
|
3118
3943
|
const srcPt = [...grid.props.colWidths];
|
|
3119
|
-
|
|
3944
|
+
while (srcPt.length < colCount) {
|
|
3945
|
+
srcPt.push(0);
|
|
3946
|
+
}
|
|
3947
|
+
srcPt.length = colCount;
|
|
3948
|
+
const knownTotalPt = srcPt.filter((w) => w > 0).reduce((s, w) => s + w, 0);
|
|
3120
3949
|
const zeroCount = srcPt.filter((w) => w <= 0).length;
|
|
3121
|
-
const
|
|
3122
|
-
const
|
|
3950
|
+
const availPt = Metric.dxaToPt(availDxa);
|
|
3951
|
+
const remainingPt = Math.max(0, availPt - knownTotalPt);
|
|
3952
|
+
const zeroFillPt = zeroCount > 0 ? remainingPt / zeroCount : 0;
|
|
3123
3953
|
for (let i = 0; i < srcPt.length; i++) {
|
|
3124
|
-
if (srcPt[i] <= 0)
|
|
3954
|
+
if (srcPt[i] <= 0) {
|
|
3955
|
+
srcPt[i] = zeroFillPt > 0 ? zeroFillPt : availPt / colCount;
|
|
3956
|
+
}
|
|
3957
|
+
}
|
|
3958
|
+
colWidthsDxa = srcPt.map((w) => Math.round(Metric.ptToDxa(w)));
|
|
3959
|
+
const computedTotalDxa = colWidthsDxa.reduce((s, w) => s + w, 0);
|
|
3960
|
+
if (computedTotalDxa > availDxa) {
|
|
3961
|
+
const scale = availDxa / computedTotalDxa;
|
|
3962
|
+
colWidthsDxa = colWidthsDxa.map((w) => Math.round(w * scale));
|
|
3125
3963
|
}
|
|
3126
|
-
const srcWidths = srcPt.map((w) => Metric.ptToDxa(w));
|
|
3127
|
-
const srcTotal = srcWidths.reduce((s, w) => s + w, 0);
|
|
3128
|
-
const scale = srcTotal > availDxa ? availDxa / srcTotal : 1;
|
|
3129
|
-
for (const w of srcWidths) colWidthsDxa.push(Math.round(w * scale));
|
|
3130
3964
|
} else {
|
|
3131
3965
|
for (let c = 0; c < colCount; c++) colWidthsDxa.push(defaultColDxa);
|
|
3132
3966
|
}
|
|
3133
3967
|
const totalDxa = colWidthsDxa.reduce((s, w) => s + w, 0);
|
|
3134
|
-
const gridCols = colWidthsDxa.map((w) => `<w:gridCol w:w="${
|
|
3135
|
-
const vMergeMap = /* @__PURE__ */ new Map();
|
|
3136
|
-
for (let ri = 0; ri < grid.kids.length; ri++) {
|
|
3137
|
-
let colIdx = 0;
|
|
3138
|
-
for (const cell of grid.kids[ri].kids) {
|
|
3139
|
-
if (cell.rs > 1) {
|
|
3140
|
-
vMergeMap.set(`${ri},${colIdx}`, "restart");
|
|
3141
|
-
for (let sr = 1; sr < cell.rs; sr++) {
|
|
3142
|
-
vMergeMap.set(`${ri + sr},${colIdx}`, "continue");
|
|
3143
|
-
}
|
|
3144
|
-
}
|
|
3145
|
-
colIdx += cell.cs;
|
|
3146
|
-
}
|
|
3147
|
-
}
|
|
3968
|
+
const gridCols = colWidthsDxa.map((w) => `<w:gridCol w:w="${w}"/>`).join("");
|
|
3148
3969
|
const rows = grid.kids.map((row, ri) => {
|
|
3149
|
-
let colIdx = 0;
|
|
3150
3970
|
const cellXmls = [];
|
|
3151
|
-
let
|
|
3152
|
-
|
|
3153
|
-
|
|
3154
|
-
|
|
3155
|
-
const cp = cell.props;
|
|
3156
|
-
const tcPrParts = [];
|
|
3157
|
-
let cellW = 0;
|
|
3158
|
-
for (let sc = colIdx; sc < colIdx + cell.cs && sc < colWidthsDxa.length; sc++) cellW += colWidthsDxa[sc];
|
|
3159
|
-
if (cellW === 0) cellW = defaultColDxa * cell.cs;
|
|
3160
|
-
tcPrParts.push(`<w:tcW w:w="${Math.round(cellW)}" w:type="dxa"/>`);
|
|
3161
|
-
if (cell.cs > 1) tcPrParts.push(`<w:gridSpan w:val="${cell.cs}"/>`);
|
|
3162
|
-
if (cell.rs > 1) {
|
|
3163
|
-
tcPrParts.push(`<w:vMerge w:val="restart"/>`);
|
|
3164
|
-
}
|
|
3165
|
-
const borders = encodeCellBorders(cp);
|
|
3166
|
-
if (borders) tcPrParts.push(borders);
|
|
3167
|
-
if (cp.bg) tcPrParts.push(`<w:shd w:val="clear" w:color="auto" w:fill="${cp.bg}"/>`);
|
|
3168
|
-
if (cp.va) {
|
|
3169
|
-
const vaMap = { top: "top", mid: "center", bot: "bottom" };
|
|
3170
|
-
tcPrParts.push(`<w:vAlign w:val="${vaMap[cp.va] ?? "top"}"/>`);
|
|
3171
|
-
}
|
|
3172
|
-
const tcPr = `<w:tcPr>${tcPrParts.join("")}</w:tcPr>`;
|
|
3173
|
-
cellXmls.push(` <w:tc>${tcPr}${cell.kids.map((p) => encodeParaInner(p, ctx)).join("")}</w:tc>`);
|
|
3174
|
-
colIdx += cell.cs;
|
|
3175
|
-
srcCellIdx++;
|
|
3176
|
-
}
|
|
3177
|
-
const finalCells = [];
|
|
3178
|
-
let finalColIdx = 0;
|
|
3179
|
-
let cellIter = 0;
|
|
3180
|
-
const totalGridCols = colCount;
|
|
3181
|
-
finalColIdx = 0;
|
|
3182
|
-
cellIter = 0;
|
|
3183
|
-
for (let gc = 0; gc < totalGridCols; ) {
|
|
3184
|
-
const mergeType = vMergeMap.get(`${ri},${gc}`);
|
|
3185
|
-
if (mergeType === "continue") {
|
|
3186
|
-
let origCs = 1;
|
|
3187
|
-
for (let sr = ri - 1; sr >= 0; sr--) {
|
|
3188
|
-
const mt = vMergeMap.get(`${sr},${gc}`);
|
|
3189
|
-
if (mt === "restart") {
|
|
3190
|
-
let col = 0;
|
|
3191
|
-
for (const c of grid.kids[sr].kids) {
|
|
3192
|
-
if (col === gc) {
|
|
3193
|
-
origCs = c.cs;
|
|
3194
|
-
break;
|
|
3195
|
-
}
|
|
3196
|
-
col += c.cs;
|
|
3197
|
-
}
|
|
3198
|
-
break;
|
|
3199
|
-
}
|
|
3200
|
-
}
|
|
3971
|
+
for (let c = 0; c < colCount; c++) {
|
|
3972
|
+
const mapEntry = tableMap[ri][c];
|
|
3973
|
+
if (mapEntry.type === "empty") continue;
|
|
3974
|
+
if (mapEntry.type === "continue") {
|
|
3201
3975
|
let cw = 0;
|
|
3202
|
-
for (let sc =
|
|
3203
|
-
if (cw === 0) cw = defaultColDxa *
|
|
3976
|
+
for (let sc = c; sc < c + mapEntry.width && sc < colWidthsDxa.length; sc++) cw += colWidthsDxa[sc];
|
|
3977
|
+
if (cw === 0) cw = defaultColDxa * mapEntry.width;
|
|
3204
3978
|
let contParts = `<w:tcW w:w="${Math.round(cw)}" w:type="dxa"/>`;
|
|
3205
|
-
if (
|
|
3979
|
+
if (mapEntry.width > 1) contParts += `<w:gridSpan w:val="${mapEntry.width}"/>`;
|
|
3206
3980
|
contParts += `<w:vMerge/>`;
|
|
3207
|
-
|
|
3208
|
-
|
|
3209
|
-
|
|
3210
|
-
|
|
3211
|
-
|
|
3212
|
-
|
|
3213
|
-
|
|
3214
|
-
|
|
3215
|
-
|
|
3981
|
+
cellXmls.push(` <w:tc><w:tcPr>${contParts}</w:tcPr><w:p><w:pPr/></w:p></w:tc>`);
|
|
3982
|
+
}
|
|
3983
|
+
if (mapEntry.type === "real") {
|
|
3984
|
+
const cell = mapEntry.cell;
|
|
3985
|
+
const cp = cell.props;
|
|
3986
|
+
const tcPrParts = [];
|
|
3987
|
+
let cellW = 0;
|
|
3988
|
+
for (let sc = c; sc < c + cell.cs && sc < colWidthsDxa.length; sc++) cellW += colWidthsDxa[sc];
|
|
3989
|
+
if (cellW === 0) cellW = defaultColDxa * cell.cs;
|
|
3990
|
+
tcPrParts.push(`<w:tcW w:w="${Math.round(cellW)}" w:type="dxa"/>`);
|
|
3991
|
+
if (cell.cs > 1) tcPrParts.push(`<w:gridSpan w:val="${cell.cs}"/>`);
|
|
3992
|
+
if (cell.rs > 1) tcPrParts.push(`<w:vMerge w:val="restart"/>`);
|
|
3993
|
+
const borders = encodeCellBorders(cp);
|
|
3994
|
+
if (borders) tcPrParts.push(borders);
|
|
3995
|
+
if (cp.bg) tcPrParts.push(`<w:shd w:val="clear" w:color="auto" w:fill="${cp.bg}"/>`);
|
|
3996
|
+
if (cp.va) {
|
|
3997
|
+
const vaMap = { top: "top", mid: "center", bot: "bottom" };
|
|
3998
|
+
tcPrParts.push(`<w:vAlign w:val="${vaMap[cp.va] ?? "top"}"/>`);
|
|
3216
3999
|
}
|
|
4000
|
+
const tcPr = `<w:tcPr>${tcPrParts.join("")}</w:tcPr>`;
|
|
4001
|
+
cellXmls.push(` <w:tc>${tcPr}${cell.kids.map((p) => encodeParaInner(p, ctx)).join("")}</w:tc>`);
|
|
3217
4002
|
}
|
|
3218
4003
|
}
|
|
3219
|
-
|
|
4004
|
+
const trPrParts = [];
|
|
3220
4005
|
if (ri === 0 && (gp.headerRow || look?.firstRow)) {
|
|
3221
|
-
|
|
4006
|
+
trPrParts.push("<w:tblHeader/>");
|
|
3222
4007
|
}
|
|
4008
|
+
if (row.heightPt != null && row.heightPt > 0) {
|
|
4009
|
+
const hDxa = Math.round(Metric.ptToDxa(row.heightPt));
|
|
4010
|
+
trPrParts.push(`<w:trHeight w:val="${hDxa}" w:hRule="exact"/>`);
|
|
4011
|
+
}
|
|
4012
|
+
const trPr = trPrParts.length > 0 ? `<w:trPr>${trPrParts.join("")}</w:trPr>` : "";
|
|
3223
4013
|
return ` <w:tr>${trPr}
|
|
3224
|
-
${
|
|
4014
|
+
${cellXmls.join("\n")}
|
|
3225
4015
|
</w:tr>`;
|
|
3226
4016
|
}).join("\n");
|
|
3227
4017
|
let tblBorders = "";
|
|
3228
4018
|
if (gp.defaultStroke) {
|
|
3229
4019
|
const s = gp.defaultStroke;
|
|
3230
|
-
const strokeKindMap = { solid: "single", dash: "
|
|
4020
|
+
const strokeKindMap = { solid: "single", dash: "dash", dot: "dot", double: "double", none: "none" };
|
|
3231
4021
|
const val = strokeKindMap[s.kind] ?? "single";
|
|
3232
4022
|
const sz = Math.round(s.pt * 8);
|
|
3233
4023
|
const bdr = `w:val="${val}" w:sz="${sz}" w:space="0" w:color="${s.color}"`;
|
|
@@ -3241,7 +4031,7 @@ ${rows}
|
|
|
3241
4031
|
}
|
|
3242
4032
|
function encodeCellBorders(cp) {
|
|
3243
4033
|
if (!cp.top && !cp.bot && !cp.left && !cp.right) return "";
|
|
3244
|
-
const strokeKindMap = { solid: "single", dash: "
|
|
4034
|
+
const strokeKindMap = { solid: "single", dash: "dash", dot: "dot", double: "double", none: "none" };
|
|
3245
4035
|
const encode = (s, tag) => {
|
|
3246
4036
|
if (!s || !tag) return "";
|
|
3247
4037
|
const val = strokeKindMap[s.kind] ?? "single";
|
|
@@ -3250,6 +4040,11 @@ function encodeCellBorders(cp) {
|
|
|
3250
4040
|
return `<w:tcBorders>${encode(cp.top, "top")}${encode(cp.bot, "bottom")}${encode(cp.left, "left")}${encode(cp.right, "right")}</w:tcBorders>`;
|
|
3251
4041
|
}
|
|
3252
4042
|
function esc2(s) {
|
|
4043
|
+
if (!s) return "";
|
|
4044
|
+
s = s.replace(/__EXT_\d+__/g, "");
|
|
4045
|
+
s = s.replace(/湰灧/g, "");
|
|
4046
|
+
s = s.replace(/\uFEFF/g, "");
|
|
4047
|
+
s = s.replace(/[^\x09\x0A\x0D\x20-\uD7FF\uE000-\uFFFD]/g, "");
|
|
3253
4048
|
return TextKit.escapeXml(s);
|
|
3254
4049
|
}
|
|
3255
4050
|
registry.registerEncoder(new DocxEncoder());
|
|
@@ -3294,10 +4089,6 @@ function encodePara2(para, warns) {
|
|
|
3294
4089
|
return text;
|
|
3295
4090
|
}
|
|
3296
4091
|
function encodeSpan(span, warns) {
|
|
3297
|
-
if (span.props.font) warns.push(`[SHIELD] MD: \uAE00\uAF34(${span.props.font}) \uD45C\uD604 \uBD88\uAC00 \u2014 \uC190\uC2E4\uB428`);
|
|
3298
|
-
if (span.props.pt) warns.push(`[SHIELD] MD: \uAE00\uC790 \uD06C\uAE30(${span.props.pt}pt) \uD45C\uD604 \uBD88\uAC00 \u2014 \uC190\uC2E4\uB428`);
|
|
3299
|
-
if (span.props.color) warns.push(`[SHIELD] MD: \uAE00\uC790 \uC0C9\uC0C1(#${span.props.color}) \uD45C\uD604 \uBD88\uAC00 \u2014 \uC190\uC2E4\uB428`);
|
|
3300
|
-
if (span.props.bg) warns.push(`[SHIELD] MD: \uBC30\uACBD \uC0C9\uC0C1(#${span.props.bg}) \uD45C\uD604 \uBD88\uAC00 \u2014 \uC190\uC2E4\uB428`);
|
|
3301
4092
|
let hasPageNum = false;
|
|
3302
4093
|
const textParts = [];
|
|
3303
4094
|
for (const kid of span.kids) {
|
|
@@ -3309,6 +4100,31 @@ function encodeSpan(span, warns) {
|
|
|
3309
4100
|
}
|
|
3310
4101
|
let r = textParts.join("");
|
|
3311
4102
|
if (hasPageNum && r === "") r = "[\uD398\uC774\uC9C0 \uBC88\uD638]";
|
|
4103
|
+
const cssStyles = [];
|
|
4104
|
+
if (span.props.font) cssStyles.push(`font-family: ${span.props.font}`);
|
|
4105
|
+
if (span.props.pt) cssStyles.push(`font-size: ${span.props.pt}pt`);
|
|
4106
|
+
if (span.props.color) cssStyles.push(`color: #${span.props.color}`);
|
|
4107
|
+
if (span.props.bg) cssStyles.push(`background-color: #${span.props.bg}`);
|
|
4108
|
+
const hasHtmlStyle = cssStyles.length > 0;
|
|
4109
|
+
if (hasHtmlStyle) {
|
|
4110
|
+
if (span.props.b) cssStyles.push("font-weight: bold");
|
|
4111
|
+
if (span.props.i) cssStyles.push("font-style: italic");
|
|
4112
|
+
if (span.props.s) cssStyles.push("text-decoration: line-through");
|
|
4113
|
+
if (span.props.u) {
|
|
4114
|
+
const existing = cssStyles.find((s) => s.startsWith("text-decoration:"));
|
|
4115
|
+
if (existing) {
|
|
4116
|
+
const idx = cssStyles.indexOf(existing);
|
|
4117
|
+
cssStyles[idx] = existing.replace("line-through", "underline line-through");
|
|
4118
|
+
if (!existing.includes("line-through")) cssStyles[idx] = existing + " underline";
|
|
4119
|
+
} else {
|
|
4120
|
+
cssStyles.push("text-decoration: underline");
|
|
4121
|
+
}
|
|
4122
|
+
}
|
|
4123
|
+
const styleAttr = cssStyles.join("; ");
|
|
4124
|
+
if (span.props.sup) return `<sup style="${styleAttr}">${r}</sup>`;
|
|
4125
|
+
if (span.props.sub) return `<sub style="${styleAttr}">${r}</sub>`;
|
|
4126
|
+
return `<span style="${styleAttr}">${r}</span>`;
|
|
4127
|
+
}
|
|
3312
4128
|
if (span.props.b && span.props.i) r = `***${r}***`;
|
|
3313
4129
|
else if (span.props.b) r = `**${r}**`;
|
|
3314
4130
|
else if (span.props.i) r = `*${r}*`;
|
|
@@ -3321,17 +4137,68 @@ function encodeSpan(span, warns) {
|
|
|
3321
4137
|
function encodeImage3(img) {
|
|
3322
4138
|
return ``;
|
|
3323
4139
|
}
|
|
4140
|
+
function strokeToCss(s) {
|
|
4141
|
+
if (!s || s.kind === "none" || s.pt <= 0) return void 0;
|
|
4142
|
+
const kindMap = { solid: "solid", dash: "dashed", dot: "dotted", double: "double", none: "none" };
|
|
4143
|
+
const style = kindMap[s.kind] ?? "solid";
|
|
4144
|
+
const px = Math.max(1, Math.round(s.pt * 96 / 72));
|
|
4145
|
+
const color = s.color.startsWith("#") ? s.color : `#${s.color}`;
|
|
4146
|
+
return `${px}px ${style} ${color}`;
|
|
4147
|
+
}
|
|
3324
4148
|
function encodeGrid3(grid, warns) {
|
|
3325
4149
|
if (grid.kids.length === 0) return "";
|
|
3326
|
-
|
|
3327
|
-
const
|
|
3328
|
-
|
|
3329
|
-
)
|
|
3330
|
-
|
|
3331
|
-
|
|
3332
|
-
|
|
4150
|
+
const rowCount = grid.kids.length;
|
|
4151
|
+
const occupancy = Array.from({ length: rowCount }, () => /* @__PURE__ */ new Set());
|
|
4152
|
+
let colCount = 0;
|
|
4153
|
+
for (let ri = 0; ri < rowCount; ri++) {
|
|
4154
|
+
const row = grid.kids[ri];
|
|
4155
|
+
let ci = 0;
|
|
4156
|
+
for (const cell of row.kids) {
|
|
4157
|
+
while (occupancy[ri].has(ci)) ci++;
|
|
4158
|
+
if (cell.rs > 1) {
|
|
4159
|
+
for (let r = ri + 1; r < ri + cell.rs && r < rowCount; r++) {
|
|
4160
|
+
for (let c = ci; c < ci + cell.cs; c++) occupancy[r].add(c);
|
|
4161
|
+
}
|
|
4162
|
+
}
|
|
4163
|
+
ci += cell.cs;
|
|
4164
|
+
}
|
|
4165
|
+
while (occupancy[ri].has(ci)) ci++;
|
|
4166
|
+
if (ci > colCount) colCount = ci;
|
|
4167
|
+
}
|
|
4168
|
+
let rows = "";
|
|
4169
|
+
for (let ri = 0; ri < rowCount; ri++) {
|
|
4170
|
+
const row = grid.kids[ri];
|
|
4171
|
+
let cells = "";
|
|
4172
|
+
let colIdx = 0;
|
|
4173
|
+
for (const cell of row.kids) {
|
|
4174
|
+
while (occupancy[ri].has(colIdx)) colIdx++;
|
|
4175
|
+
const cs = cell.cs > 1 ? ` colspan="${cell.cs}"` : "";
|
|
4176
|
+
const rs = cell.rs > 1 ? ` rowspan="${cell.rs}"` : "";
|
|
4177
|
+
const styles = ["padding:4px 6px", "vertical-align:top"];
|
|
4178
|
+
const top = strokeToCss(cell.props.top);
|
|
4179
|
+
const bot = strokeToCss(cell.props.bot);
|
|
4180
|
+
const left = strokeToCss(cell.props.left);
|
|
4181
|
+
const right = strokeToCss(cell.props.right);
|
|
4182
|
+
if (top) styles.push(`border-top:${top}`);
|
|
4183
|
+
if (bot) styles.push(`border-bottom:${bot}`);
|
|
4184
|
+
if (left) styles.push(`border-left:${left}`);
|
|
4185
|
+
if (right) styles.push(`border-right:${right}`);
|
|
4186
|
+
if (cell.props.bg) styles.push(`background-color:#${cell.props.bg}`);
|
|
4187
|
+
if (cell.props.va === "mid") styles[1] = "vertical-align:middle";
|
|
4188
|
+
else if (cell.props.va === "bot") styles[1] = "vertical-align:bottom";
|
|
4189
|
+
const tag = grid.props.headerRow && ri === 0 || cell.props.isHeader ? "th" : "td";
|
|
4190
|
+
const content = cell.kids.map((p) => encodePara2(p, warns)).join("\n");
|
|
4191
|
+
cells += `<${tag}${cs}${rs} style="${styles.join(";")}">${content}</${tag}>`;
|
|
4192
|
+
colIdx += cell.cs;
|
|
4193
|
+
}
|
|
4194
|
+
rows += `<tr>${cells}</tr>
|
|
4195
|
+
`;
|
|
3333
4196
|
}
|
|
3334
|
-
return
|
|
4197
|
+
return `<table style="border-collapse:collapse;width:100%">
|
|
4198
|
+
<tbody>
|
|
4199
|
+
${rows}</tbody>
|
|
4200
|
+
</table>
|
|
4201
|
+
`;
|
|
3335
4202
|
}
|
|
3336
4203
|
registry.registerEncoder(new MdEncoder());
|
|
3337
4204
|
|
|
@@ -3378,8 +4245,15 @@ var Pipeline = class _Pipeline {
|
|
|
3378
4245
|
}
|
|
3379
4246
|
};
|
|
3380
4247
|
function detectFormat(data) {
|
|
3381
|
-
if (data[0] === 80 && data[1] === 75) return "zip";
|
|
3382
4248
|
if (data[0] === 208 && data[1] === 207 && data[2] === 17 && data[3] === 224) return "hwp";
|
|
4249
|
+
if (data[0] === 80 && data[1] === 75) {
|
|
4250
|
+
const str = new TextDecoder("utf-8", { fatal: false }).decode(data.slice(0, 4096));
|
|
4251
|
+
if (str.includes("wordprocessingml")) return "docx";
|
|
4252
|
+
if (str.includes("ha-xml")) return "hwpx";
|
|
4253
|
+
if (str.includes("hwpml/")) return "hwpx";
|
|
4254
|
+
if (str.includes("word/")) return "docx";
|
|
4255
|
+
return "hwpx";
|
|
4256
|
+
}
|
|
3383
4257
|
return "md";
|
|
3384
4258
|
}
|
|
3385
4259
|
function getExt(name) {
|
|
@@ -3387,6 +4261,1017 @@ function getExt(name) {
|
|
|
3387
4261
|
return parts.length > 1 ? parts[parts.length - 1].toLowerCase() : void 0;
|
|
3388
4262
|
}
|
|
3389
4263
|
|
|
4264
|
+
// src/encoders/html/HtmlEncoder.ts
|
|
4265
|
+
var HtmlEncoder = class {
|
|
4266
|
+
constructor() {
|
|
4267
|
+
this.format = "html";
|
|
4268
|
+
}
|
|
4269
|
+
async encode(doc) {
|
|
4270
|
+
try {
|
|
4271
|
+
const warns = [];
|
|
4272
|
+
const bodyParts = [];
|
|
4273
|
+
for (const sheet of doc.kids) {
|
|
4274
|
+
if (sheet.header && sheet.header.length > 0) {
|
|
4275
|
+
const hText = sheet.header.map((p) => encodePara3(p, warns)).join("");
|
|
4276
|
+
bodyParts.push(`<div class="hwp-header">${hText}</div>`);
|
|
4277
|
+
}
|
|
4278
|
+
for (const kid of sheet.kids) {
|
|
4279
|
+
bodyParts.push(encodeContent3(kid, warns));
|
|
4280
|
+
}
|
|
4281
|
+
if (sheet.footer && sheet.footer.length > 0) {
|
|
4282
|
+
const fText = sheet.footer.map((p) => encodePara3(p, warns)).join("");
|
|
4283
|
+
bodyParts.push(`<div class="hwp-footer">${fText}</div>`);
|
|
4284
|
+
}
|
|
4285
|
+
}
|
|
4286
|
+
const title = esc3(doc.meta?.title ?? "");
|
|
4287
|
+
const html = `<!DOCTYPE html>
|
|
4288
|
+
<html lang="ko">
|
|
4289
|
+
<head>
|
|
4290
|
+
<meta charset="UTF-8">
|
|
4291
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
4292
|
+
<title>${title}</title>
|
|
4293
|
+
<style>
|
|
4294
|
+
${BASE_CSS}
|
|
4295
|
+
</style>
|
|
4296
|
+
</head>
|
|
4297
|
+
<body>
|
|
4298
|
+
<div class="hwp-doc">
|
|
4299
|
+
${bodyParts.join("\n")}
|
|
4300
|
+
</div>
|
|
4301
|
+
</body>
|
|
4302
|
+
</html>`;
|
|
4303
|
+
return succeed(TextKit.encode(html), warns);
|
|
4304
|
+
} catch (e) {
|
|
4305
|
+
return fail(`HTML encode error: ${e?.message ?? String(e)}`);
|
|
4306
|
+
}
|
|
4307
|
+
}
|
|
4308
|
+
};
|
|
4309
|
+
var BASE_CSS = `
|
|
4310
|
+
body { margin: 0; padding: 0; background: #f0f0f0; }
|
|
4311
|
+
.hwp-doc { max-width: 800px; margin: 0 auto; background: #fff; padding: 40px 60px; box-shadow: 0 0 8px rgba(0,0,0,0.15); }
|
|
4312
|
+
.hwp-header, .hwp-footer { color: #666; font-size: 0.9em; border-bottom: 1px solid #ddd; margin-bottom: 8px; padding-bottom: 4px; }
|
|
4313
|
+
.hwp-footer { border-top: 1px solid #ddd; border-bottom: none; margin-top: 8px; padding-top: 4px; }
|
|
4314
|
+
p { margin: 0; padding: 0; line-height: 1.6; }
|
|
4315
|
+
table { border-collapse: collapse; width: 100%; margin: 8px 0; }
|
|
4316
|
+
td, th { border: 1px solid #ccc; padding: 4px 8px; vertical-align: top; }
|
|
4317
|
+
img { max-width: 100%; height: auto; }
|
|
4318
|
+
`.trim();
|
|
4319
|
+
function encodeContent3(node, warns) {
|
|
4320
|
+
return node.tag === "grid" ? encodeGrid4(node, warns) : encodePara3(node, warns);
|
|
4321
|
+
}
|
|
4322
|
+
function encodePara3(para, warns) {
|
|
4323
|
+
const kids = para.kids.map((k) => {
|
|
4324
|
+
if (k.tag === "span") return encodeSpan2(k, warns);
|
|
4325
|
+
if (k.tag === "img") return encodeImage4(k);
|
|
4326
|
+
if (k.tag === "link") {
|
|
4327
|
+
const link = k;
|
|
4328
|
+
const inner = link.kids.map((s) => encodeSpan2(s, warns)).join("");
|
|
4329
|
+
return `<a href="${esc3(link.href)}">${inner}</a>`;
|
|
4330
|
+
}
|
|
4331
|
+
return "";
|
|
4332
|
+
}).join("");
|
|
4333
|
+
if (para.props.heading) {
|
|
4334
|
+
const tag = `h${para.props.heading}`;
|
|
4335
|
+
return `<${tag}>${kids}</${tag}>
|
|
4336
|
+
`;
|
|
4337
|
+
}
|
|
4338
|
+
if (para.props.listOrd !== void 0) {
|
|
4339
|
+
const indent = (para.props.listLv ?? 0) * 20;
|
|
4340
|
+
const style = indent > 0 ? ` style="margin-left:${indent}px"` : "";
|
|
4341
|
+
const marker = para.props.listOrd ? `<span class="list-marker">1. </span>` : `<span class="list-marker">\u2022 </span>`;
|
|
4342
|
+
return `<p${style}>${marker}${kids}</p>
|
|
4343
|
+
`;
|
|
4344
|
+
}
|
|
4345
|
+
const align = para.props.align;
|
|
4346
|
+
const styleAttrs = [];
|
|
4347
|
+
if (align && align !== "left") styleAttrs.push(`text-align:${align}`);
|
|
4348
|
+
if (para.props.indentPt) styleAttrs.push(`margin-left:${para.props.indentPt.toFixed(1)}pt`);
|
|
4349
|
+
if (para.props.spaceBefore) styleAttrs.push(`margin-top:${para.props.spaceBefore.toFixed(1)}pt`);
|
|
4350
|
+
if (para.props.spaceAfter) styleAttrs.push(`margin-bottom:${para.props.spaceAfter.toFixed(1)}pt`);
|
|
4351
|
+
if (para.props.lineHeight) styleAttrs.push(`line-height:${para.props.lineHeight}`);
|
|
4352
|
+
const styleAttr = styleAttrs.length > 0 ? ` style="${styleAttrs.join(";")}"` : "";
|
|
4353
|
+
return `<p${styleAttr}>${kids || " "}</p>
|
|
4354
|
+
`;
|
|
4355
|
+
}
|
|
4356
|
+
function encodeSpan2(span, warns) {
|
|
4357
|
+
const parts = [];
|
|
4358
|
+
let hasPageNum = false;
|
|
4359
|
+
for (const kid of span.kids) {
|
|
4360
|
+
if (kid.tag === "txt") {
|
|
4361
|
+
parts.push(esc3(kid.content));
|
|
4362
|
+
} else if (kid.tag === "br") {
|
|
4363
|
+
parts.push("<br>");
|
|
4364
|
+
} else if (kid.tag === "pb") {
|
|
4365
|
+
parts.push('<div style="page-break-after:always"></div>');
|
|
4366
|
+
} else if (kid.tag === "pagenum") {
|
|
4367
|
+
hasPageNum = true;
|
|
4368
|
+
warns.push("[SHIELD] HTML: \uD398\uC774\uC9C0 \uBC88\uD638 \u2014 \uC815\uC801 \uAC12\uC73C\uB85C \uB300\uCCB4\uB428");
|
|
4369
|
+
parts.push('<span class="page-num">[\uD398\uC774\uC9C0]</span>');
|
|
4370
|
+
}
|
|
4371
|
+
}
|
|
4372
|
+
let text = parts.join("");
|
|
4373
|
+
if (hasPageNum && text.trim() === '<span class="page-num">[\uD398\uC774\uC9C0]</span>') {
|
|
4374
|
+
}
|
|
4375
|
+
const p = span.props;
|
|
4376
|
+
const css = [];
|
|
4377
|
+
if (p.font) css.push(`font-family:${esc3(p.font)}`);
|
|
4378
|
+
if (p.pt) css.push(`font-size:${p.pt}pt`);
|
|
4379
|
+
if (p.color) css.push(`color:#${p.color}`);
|
|
4380
|
+
if (p.bg) css.push(`background-color:#${p.bg}`);
|
|
4381
|
+
if (p.b) css.push("font-weight:bold");
|
|
4382
|
+
if (p.i) css.push("font-style:italic");
|
|
4383
|
+
const decorations = [];
|
|
4384
|
+
if (p.u) decorations.push("underline");
|
|
4385
|
+
if (p.s) decorations.push("line-through");
|
|
4386
|
+
if (decorations.length > 0) css.push(`text-decoration:${decorations.join(" ")}`);
|
|
4387
|
+
if (p.sup) return `<sup${css.length ? ` style="${css.join(";")}"` : ""}>${text}</sup>`;
|
|
4388
|
+
if (p.sub) return `<sub${css.length ? ` style="${css.join(";")}"` : ""}>${text}</sub>`;
|
|
4389
|
+
if (css.length > 0) return `<span style="${css.join(";")}">${text}</span>`;
|
|
4390
|
+
return text;
|
|
4391
|
+
}
|
|
4392
|
+
function encodeImage4(img) {
|
|
4393
|
+
const wStyle = img.w ? ` width="${Math.round(img.w / 72 * 96)}px"` : "";
|
|
4394
|
+
const hStyle = img.h ? ` height="${Math.round(img.h / 72 * 96)}px"` : "";
|
|
4395
|
+
const alt = esc3(img.alt ?? "");
|
|
4396
|
+
return `<img src="data:${img.mime};base64,${img.b64}" alt="${alt}"${wStyle}${hStyle}>`;
|
|
4397
|
+
}
|
|
4398
|
+
function encodeGrid4(grid, warns) {
|
|
4399
|
+
if (grid.kids.length === 0) return "";
|
|
4400
|
+
const rowCount = grid.kids.length;
|
|
4401
|
+
const occupancy = Array.from({ length: rowCount }, () => /* @__PURE__ */ new Set());
|
|
4402
|
+
let colCount = 0;
|
|
4403
|
+
for (let ri = 0; ri < rowCount; ri++) {
|
|
4404
|
+
const row = grid.kids[ri];
|
|
4405
|
+
let ci = 0;
|
|
4406
|
+
for (const cell of row.kids) {
|
|
4407
|
+
while (occupancy[ri].has(ci)) ci++;
|
|
4408
|
+
if (cell.rs > 1) {
|
|
4409
|
+
for (let r = ri + 1; r < ri + cell.rs && r < rowCount; r++) {
|
|
4410
|
+
for (let c = ci; c < ci + cell.cs; c++) occupancy[r].add(c);
|
|
4411
|
+
}
|
|
4412
|
+
}
|
|
4413
|
+
ci += cell.cs;
|
|
4414
|
+
}
|
|
4415
|
+
while (occupancy[ri].has(ci)) ci++;
|
|
4416
|
+
if (ci > colCount) colCount = ci;
|
|
4417
|
+
}
|
|
4418
|
+
let rows = "";
|
|
4419
|
+
for (let ri = 0; ri < rowCount; ri++) {
|
|
4420
|
+
const row = grid.kids[ri];
|
|
4421
|
+
let cells = "";
|
|
4422
|
+
let ci = 0;
|
|
4423
|
+
for (const cell of row.kids) {
|
|
4424
|
+
while (occupancy[ri].has(ci)) ci++;
|
|
4425
|
+
const isHeader = cell.props.isHeader || grid.props.headerRow && ri === 0;
|
|
4426
|
+
const tag = isHeader ? "th" : "td";
|
|
4427
|
+
const cs = cell.cs > 1 ? ` colspan="${cell.cs}"` : "";
|
|
4428
|
+
const rs = cell.rs > 1 ? ` rowspan="${cell.rs}"` : "";
|
|
4429
|
+
const styleAttrs = [];
|
|
4430
|
+
if (cell.props.bg) styleAttrs.push(`background-color:#${cell.props.bg}`);
|
|
4431
|
+
const va = cell.props.va;
|
|
4432
|
+
if (va === "mid") styleAttrs.push("vertical-align:middle");
|
|
4433
|
+
else if (va === "bot") styleAttrs.push("vertical-align:bottom");
|
|
4434
|
+
const styleAttr = styleAttrs.length > 0 ? ` style="${styleAttrs.join(";")}"` : "";
|
|
4435
|
+
const content = cell.kids.map((p) => encodePara3(p, warns)).join("");
|
|
4436
|
+
cells += `<${tag}${cs}${rs}${styleAttr}>${content}</${tag}>`;
|
|
4437
|
+
ci += cell.cs;
|
|
4438
|
+
}
|
|
4439
|
+
rows += `<tr>${cells}</tr>
|
|
4440
|
+
`;
|
|
4441
|
+
}
|
|
4442
|
+
return `<table>
|
|
4443
|
+
<tbody>
|
|
4444
|
+
${rows}</tbody>
|
|
4445
|
+
</table>
|
|
4446
|
+
`;
|
|
4447
|
+
}
|
|
4448
|
+
function esc3(s) {
|
|
4449
|
+
return TextKit.escapeXml(s);
|
|
4450
|
+
}
|
|
4451
|
+
registry.registerEncoder(new HtmlEncoder());
|
|
4452
|
+
|
|
4453
|
+
// src/encoders/hwp/HwpEncoder.ts
|
|
4454
|
+
import pako3 from "pako";
|
|
4455
|
+
var T = 16;
|
|
4456
|
+
var TAG_DOCUMENT_PROPERTIES = T + 0;
|
|
4457
|
+
var TAG_ID_MAPPINGS = T + 1;
|
|
4458
|
+
var TAG_FACE_NAME2 = T + 3;
|
|
4459
|
+
var TAG_BORDER_FILL2 = T + 4;
|
|
4460
|
+
var TAG_CHAR_SHAPE2 = T + 5;
|
|
4461
|
+
var TAG_PARA_SHAPE2 = T + 9;
|
|
4462
|
+
var TAG_BIN_DATA = T + 2;
|
|
4463
|
+
var TAG_PARA_HEADER2 = T + 50;
|
|
4464
|
+
var TAG_PARA_TEXT2 = T + 51;
|
|
4465
|
+
var TAG_PARA_CHAR_SHAPE2 = T + 52;
|
|
4466
|
+
var TAG_PARA_LINE_SEG = T + 53;
|
|
4467
|
+
var TAG_CTRL_HEADER2 = T + 55;
|
|
4468
|
+
var TAG_LIST_HEADER2 = T + 56;
|
|
4469
|
+
var TAG_PAGE_DEF2 = T + 57;
|
|
4470
|
+
var TAG_FOOTNOTE_SHAPE = T + 58;
|
|
4471
|
+
var TAG_TABLE = T + 61;
|
|
4472
|
+
var TAG_SHAPE_COMPONENT_PICTURE = T + 69;
|
|
4473
|
+
var CTRL_TABLE2 = 1952607264;
|
|
4474
|
+
var CTRL_SECD = 1936024420;
|
|
4475
|
+
var CTRL_PIC = 611346787;
|
|
4476
|
+
var BORDER_W_PT2 = [
|
|
4477
|
+
0.28,
|
|
4478
|
+
0.34,
|
|
4479
|
+
0.43,
|
|
4480
|
+
0.57,
|
|
4481
|
+
0.71,
|
|
4482
|
+
0.85,
|
|
4483
|
+
1.13,
|
|
4484
|
+
1.42,
|
|
4485
|
+
1.7,
|
|
4486
|
+
1.98,
|
|
4487
|
+
2.84,
|
|
4488
|
+
4.25,
|
|
4489
|
+
5.67,
|
|
4490
|
+
8.5,
|
|
4491
|
+
11.34,
|
|
4492
|
+
14.17
|
|
4493
|
+
];
|
|
4494
|
+
var BORDER_KIND_IDX = {
|
|
4495
|
+
solid: 0,
|
|
4496
|
+
dash: 2,
|
|
4497
|
+
dot: 3,
|
|
4498
|
+
double: 7,
|
|
4499
|
+
none: 0
|
|
4500
|
+
};
|
|
4501
|
+
var ALIGN_CODE = {
|
|
4502
|
+
justify: 0,
|
|
4503
|
+
left: 1,
|
|
4504
|
+
right: 2,
|
|
4505
|
+
center: 3
|
|
4506
|
+
};
|
|
4507
|
+
var BufWriter = class {
|
|
4508
|
+
constructor() {
|
|
4509
|
+
this.chunks = [];
|
|
4510
|
+
this._sz = 0;
|
|
4511
|
+
}
|
|
4512
|
+
get size() {
|
|
4513
|
+
return this._sz;
|
|
4514
|
+
}
|
|
4515
|
+
u8(v) {
|
|
4516
|
+
this.chunks.push(new Uint8Array([v & 255]));
|
|
4517
|
+
this._sz++;
|
|
4518
|
+
return this;
|
|
4519
|
+
}
|
|
4520
|
+
u16(v) {
|
|
4521
|
+
this.chunks.push(new Uint8Array([v & 255, v >> 8 & 255]));
|
|
4522
|
+
this._sz += 2;
|
|
4523
|
+
return this;
|
|
4524
|
+
}
|
|
4525
|
+
u32(v) {
|
|
4526
|
+
const b = new Uint8Array(4);
|
|
4527
|
+
b[0] = v & 255;
|
|
4528
|
+
b[1] = v >>> 8 & 255;
|
|
4529
|
+
b[2] = v >>> 16 & 255;
|
|
4530
|
+
b[3] = v >>> 24 & 255;
|
|
4531
|
+
this.chunks.push(b);
|
|
4532
|
+
this._sz += 4;
|
|
4533
|
+
return this;
|
|
4534
|
+
}
|
|
4535
|
+
i32(v) {
|
|
4536
|
+
return this.u32(v < 0 ? v + 4294967296 : v);
|
|
4537
|
+
}
|
|
4538
|
+
i16(v) {
|
|
4539
|
+
return this.u16(v < 0 ? v + 65536 : v);
|
|
4540
|
+
}
|
|
4541
|
+
bytes(d) {
|
|
4542
|
+
this.chunks.push(d);
|
|
4543
|
+
this._sz += d.length;
|
|
4544
|
+
return this;
|
|
4545
|
+
}
|
|
4546
|
+
zeros(n) {
|
|
4547
|
+
this.chunks.push(new Uint8Array(n));
|
|
4548
|
+
this._sz += n;
|
|
4549
|
+
return this;
|
|
4550
|
+
}
|
|
4551
|
+
/** Write each char as UTF-16LE UINT16 (BMP only) */
|
|
4552
|
+
utf16(s) {
|
|
4553
|
+
for (let i = 0; i < s.length; i++) this.u16(s.charCodeAt(i));
|
|
4554
|
+
return this;
|
|
4555
|
+
}
|
|
4556
|
+
/** Write 4-byte COLORREF (R, G, B, 0) from 6-hex string */
|
|
4557
|
+
colorRef(hex) {
|
|
4558
|
+
const h = (hex || "000000").replace("#", "").padStart(6, "0");
|
|
4559
|
+
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);
|
|
4560
|
+
}
|
|
4561
|
+
build() {
|
|
4562
|
+
const out = new Uint8Array(this._sz);
|
|
4563
|
+
let off = 0;
|
|
4564
|
+
for (const c of this.chunks) {
|
|
4565
|
+
out.set(c, off);
|
|
4566
|
+
off += c.length;
|
|
4567
|
+
}
|
|
4568
|
+
return out;
|
|
4569
|
+
}
|
|
4570
|
+
};
|
|
4571
|
+
function mkRec(tag, level, data) {
|
|
4572
|
+
const sz = data.length;
|
|
4573
|
+
const enc = Math.min(sz, 4095);
|
|
4574
|
+
const hdr = enc << 20 | (level & 1023) << 10 | tag & 1023;
|
|
4575
|
+
const w = new BufWriter().u32(hdr);
|
|
4576
|
+
if (enc >= 4095) w.u32(sz);
|
|
4577
|
+
w.bytes(data);
|
|
4578
|
+
return w.build();
|
|
4579
|
+
}
|
|
4580
|
+
function csKey(p) {
|
|
4581
|
+
return [
|
|
4582
|
+
p.font ?? "",
|
|
4583
|
+
p.pt ?? 10,
|
|
4584
|
+
p.b ? 1 : 0,
|
|
4585
|
+
p.i ? 1 : 0,
|
|
4586
|
+
p.u ? 1 : 0,
|
|
4587
|
+
p.s ? 1 : 0,
|
|
4588
|
+
p.sup ? 1 : 0,
|
|
4589
|
+
p.sub ? 1 : 0,
|
|
4590
|
+
p.color ?? "000000"
|
|
4591
|
+
].join("|");
|
|
4592
|
+
}
|
|
4593
|
+
function psKey(p) {
|
|
4594
|
+
return [
|
|
4595
|
+
p.align ?? "left",
|
|
4596
|
+
p.indentPt ?? 0,
|
|
4597
|
+
p.spaceBefore ?? 0,
|
|
4598
|
+
p.spaceAfter ?? 0,
|
|
4599
|
+
p.lineHeight ?? 1
|
|
4600
|
+
].join("|");
|
|
4601
|
+
}
|
|
4602
|
+
function bfKey(s, bg) {
|
|
4603
|
+
return `${s.kind}|${s.pt}|${s.color}|${bg ?? ""}`;
|
|
4604
|
+
}
|
|
4605
|
+
function bfPerSideKey(l, r, t, b, bg) {
|
|
4606
|
+
return `${bfKey(l)}/${bfKey(r)}/${bfKey(t)}/${bfKey(b)}/${bg ?? ""}`;
|
|
4607
|
+
}
|
|
4608
|
+
var StyleCollector = class {
|
|
4609
|
+
constructor() {
|
|
4610
|
+
this.DEF_STROKE = { kind: "solid", pt: 0.5, color: "000000" };
|
|
4611
|
+
this.fonts = ["Malgun Gothic"];
|
|
4612
|
+
this.fontIdx = /* @__PURE__ */ new Map([["Malgun Gothic", 0]]);
|
|
4613
|
+
this.csProps = [{}];
|
|
4614
|
+
this.csIdx = /* @__PURE__ */ new Map([[csKey({}), 0]]);
|
|
4615
|
+
this.psProps = [{}];
|
|
4616
|
+
this.psIdx = /* @__PURE__ */ new Map([[psKey({}), 0]]);
|
|
4617
|
+
this.bfData = [];
|
|
4618
|
+
this.bfIdx = /* @__PURE__ */ new Map();
|
|
4619
|
+
this.addBorderFill(this.DEF_STROKE);
|
|
4620
|
+
}
|
|
4621
|
+
font(name) {
|
|
4622
|
+
const n = name || "Malgun Gothic";
|
|
4623
|
+
if (this.fontIdx.has(n)) return this.fontIdx.get(n);
|
|
4624
|
+
const id = this.fonts.length;
|
|
4625
|
+
this.fonts.push(n);
|
|
4626
|
+
this.fontIdx.set(n, id);
|
|
4627
|
+
return id;
|
|
4628
|
+
}
|
|
4629
|
+
addCharShape(p) {
|
|
4630
|
+
const k = csKey(p);
|
|
4631
|
+
if (this.csIdx.has(k)) return this.csIdx.get(k);
|
|
4632
|
+
const id = this.csProps.length;
|
|
4633
|
+
this.csProps.push(p);
|
|
4634
|
+
this.csIdx.set(k, id);
|
|
4635
|
+
if (p.font) this.font(p.font);
|
|
4636
|
+
return id;
|
|
4637
|
+
}
|
|
4638
|
+
addParaShape(p) {
|
|
4639
|
+
const k = psKey(p);
|
|
4640
|
+
if (this.psIdx.has(k)) return this.psIdx.get(k);
|
|
4641
|
+
const id = this.psProps.length;
|
|
4642
|
+
this.psProps.push(p);
|
|
4643
|
+
this.psIdx.set(k, id);
|
|
4644
|
+
return id;
|
|
4645
|
+
}
|
|
4646
|
+
/** Returns 1-based border fill ID */
|
|
4647
|
+
addBorderFill(s, bg) {
|
|
4648
|
+
const k = bfKey(s, bg);
|
|
4649
|
+
if (this.bfIdx.has(k)) return this.bfIdx.get(k);
|
|
4650
|
+
const id = this.bfData.length + 1;
|
|
4651
|
+
this.bfData.push({ uniform: true, s, bg });
|
|
4652
|
+
this.bfIdx.set(k, id);
|
|
4653
|
+
return id;
|
|
4654
|
+
}
|
|
4655
|
+
addBorderFillPerSide(l, r, t, b, bg) {
|
|
4656
|
+
const k = bfPerSideKey(l, r, t, b, bg);
|
|
4657
|
+
if (this.bfIdx.has(k)) return this.bfIdx.get(k);
|
|
4658
|
+
const id = this.bfData.length + 1;
|
|
4659
|
+
this.bfData.push({ uniform: false, l, r, t, b, bg });
|
|
4660
|
+
this.bfIdx.set(k, id);
|
|
4661
|
+
return id;
|
|
4662
|
+
}
|
|
4663
|
+
};
|
|
4664
|
+
function collectNode(node, col) {
|
|
4665
|
+
if (node.tag === "para") {
|
|
4666
|
+
col.addParaShape(node.props);
|
|
4667
|
+
for (const kid of node.kids) {
|
|
4668
|
+
if (kid.tag === "span") col.addCharShape(kid.props);
|
|
4669
|
+
}
|
|
4670
|
+
} else if (node.tag === "grid") {
|
|
4671
|
+
if (node.props.defaultStroke) col.addBorderFill(node.props.defaultStroke);
|
|
4672
|
+
for (const row of node.kids) {
|
|
4673
|
+
for (const cell of row.kids) {
|
|
4674
|
+
const defStroke = node.props.defaultStroke ?? col.DEF_STROKE;
|
|
4675
|
+
const cp = cell.props;
|
|
4676
|
+
if (cp.top || cp.bot || cp.left || cp.right) {
|
|
4677
|
+
col.addBorderFillPerSide(
|
|
4678
|
+
cp.left ?? defStroke,
|
|
4679
|
+
cp.right ?? defStroke,
|
|
4680
|
+
cp.top ?? defStroke,
|
|
4681
|
+
cp.bot ?? defStroke,
|
|
4682
|
+
cp.bg
|
|
4683
|
+
);
|
|
4684
|
+
} else {
|
|
4685
|
+
col.addBorderFill(defStroke, cp.bg);
|
|
4686
|
+
}
|
|
4687
|
+
for (const para of cell.kids) collectNode(para, col);
|
|
4688
|
+
}
|
|
4689
|
+
}
|
|
4690
|
+
}
|
|
4691
|
+
}
|
|
4692
|
+
function mkDocumentProperties() {
|
|
4693
|
+
return new BufWriter().u16(1).u16(1).u16(1).u16(1).u16(1).u16(1).u16(1).u32(0).u32(0).u32(0).build();
|
|
4694
|
+
}
|
|
4695
|
+
function mkIdMappings(col, nBinData = 0) {
|
|
4696
|
+
const w = new BufWriter();
|
|
4697
|
+
w.u32(nBinData);
|
|
4698
|
+
for (let i = 0; i < 7; i++) w.u32(col.fonts.length);
|
|
4699
|
+
w.u32(col.bfData.length);
|
|
4700
|
+
w.u32(col.csProps.length);
|
|
4701
|
+
w.u32(0);
|
|
4702
|
+
w.u32(0);
|
|
4703
|
+
w.u32(0);
|
|
4704
|
+
w.u32(col.psProps.length);
|
|
4705
|
+
w.u32(0);
|
|
4706
|
+
w.u32(0);
|
|
4707
|
+
w.u32(0);
|
|
4708
|
+
w.u32(0);
|
|
4709
|
+
return w.build();
|
|
4710
|
+
}
|
|
4711
|
+
function mkFaceName(name) {
|
|
4712
|
+
return new BufWriter().u8(0).u16(name.length).utf16(name).u8(0).u16(0).zeros(10).u16(0).build();
|
|
4713
|
+
}
|
|
4714
|
+
function borderWidthIdx(pt) {
|
|
4715
|
+
let best = 0;
|
|
4716
|
+
for (let i = 0; i < BORDER_W_PT2.length; i++) {
|
|
4717
|
+
if (Math.abs(BORDER_W_PT2[i] - pt) < Math.abs(BORDER_W_PT2[best] - pt)) best = i;
|
|
4718
|
+
}
|
|
4719
|
+
return best;
|
|
4720
|
+
}
|
|
4721
|
+
function mkBorderFill(s, bg) {
|
|
4722
|
+
const w = new BufWriter();
|
|
4723
|
+
const t = BORDER_KIND_IDX[s.kind] ?? 0;
|
|
4724
|
+
const wi = borderWidthIdx(s.pt);
|
|
4725
|
+
const col = s.color || "000000";
|
|
4726
|
+
w.u16(0);
|
|
4727
|
+
for (let i = 0; i < 4; i++) w.u8(t);
|
|
4728
|
+
for (let i = 0; i < 4; i++) w.u8(wi);
|
|
4729
|
+
for (let i = 0; i < 4; i++) w.colorRef(col);
|
|
4730
|
+
w.u8(0).u8(0).colorRef("000000");
|
|
4731
|
+
if (bg) {
|
|
4732
|
+
w.u32(1);
|
|
4733
|
+
w.colorRef(bg);
|
|
4734
|
+
w.colorRef("FFFFFF");
|
|
4735
|
+
w.u32(0);
|
|
4736
|
+
} else {
|
|
4737
|
+
w.u32(0);
|
|
4738
|
+
}
|
|
4739
|
+
return w.build();
|
|
4740
|
+
}
|
|
4741
|
+
function mkBorderFillPerSide(left, right, top, bottom, bg) {
|
|
4742
|
+
const w = new BufWriter();
|
|
4743
|
+
w.u16(0);
|
|
4744
|
+
w.u8(BORDER_KIND_IDX[left.kind] ?? 0);
|
|
4745
|
+
w.u8(BORDER_KIND_IDX[right.kind] ?? 0);
|
|
4746
|
+
w.u8(BORDER_KIND_IDX[top.kind] ?? 0);
|
|
4747
|
+
w.u8(BORDER_KIND_IDX[bottom.kind] ?? 0);
|
|
4748
|
+
w.u8(borderWidthIdx(left.pt));
|
|
4749
|
+
w.u8(borderWidthIdx(right.pt));
|
|
4750
|
+
w.u8(borderWidthIdx(top.pt));
|
|
4751
|
+
w.u8(borderWidthIdx(bottom.pt));
|
|
4752
|
+
w.colorRef(left.color || "000000");
|
|
4753
|
+
w.colorRef(right.color || "000000");
|
|
4754
|
+
w.colorRef(top.color || "000000");
|
|
4755
|
+
w.colorRef(bottom.color || "000000");
|
|
4756
|
+
w.u8(0).u8(0).colorRef("000000");
|
|
4757
|
+
if (bg) {
|
|
4758
|
+
w.u32(1).colorRef(bg).colorRef("FFFFFF").u32(0);
|
|
4759
|
+
} else {
|
|
4760
|
+
w.u32(0);
|
|
4761
|
+
}
|
|
4762
|
+
return w.build();
|
|
4763
|
+
}
|
|
4764
|
+
function mkCharShape(p, col) {
|
|
4765
|
+
const fontId = p.font ? col.font(p.font) : 0;
|
|
4766
|
+
const height = Math.round((p.pt ?? 10) * 100);
|
|
4767
|
+
let attr = 0;
|
|
4768
|
+
if (p.i) attr |= 1 << 0;
|
|
4769
|
+
if (p.b) attr |= 1 << 1;
|
|
4770
|
+
if (p.u) attr |= 1 << 2;
|
|
4771
|
+
if (p.s) attr |= 1 << 18;
|
|
4772
|
+
if (p.sup) attr |= 1 << 16;
|
|
4773
|
+
if (p.sub) attr |= 2 << 16;
|
|
4774
|
+
const textColor = p.color ?? "000000";
|
|
4775
|
+
const w = new BufWriter();
|
|
4776
|
+
for (let i = 0; i < 7; i++) w.u16(fontId);
|
|
4777
|
+
for (let i = 0; i < 7; i++) w.u8(100);
|
|
4778
|
+
for (let i = 0; i < 7; i++) w.u8(0);
|
|
4779
|
+
for (let i = 0; i < 7; i++) w.u8(100);
|
|
4780
|
+
for (let i = 0; i < 7; i++) w.u8(0);
|
|
4781
|
+
w.i32(height);
|
|
4782
|
+
w.u32(attr);
|
|
4783
|
+
w.u8(0).u8(0);
|
|
4784
|
+
w.colorRef(textColor);
|
|
4785
|
+
w.colorRef("000000");
|
|
4786
|
+
w.colorRef("FFFFFF");
|
|
4787
|
+
w.colorRef("000000");
|
|
4788
|
+
w.u16(0);
|
|
4789
|
+
w.colorRef("000000");
|
|
4790
|
+
return w.build();
|
|
4791
|
+
}
|
|
4792
|
+
function mkParaShape(p) {
|
|
4793
|
+
const alignVal = ALIGN_CODE[p.align ?? "left"] ?? 1;
|
|
4794
|
+
const attr1 = (alignVal & 7) << 2;
|
|
4795
|
+
const lineSpacePct = p.lineHeight ? Math.round(p.lineHeight * 100) : 160;
|
|
4796
|
+
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();
|
|
4797
|
+
}
|
|
4798
|
+
function mkBinData(id, ext) {
|
|
4799
|
+
const w = new BufWriter();
|
|
4800
|
+
w.u16(2);
|
|
4801
|
+
w.u16(id);
|
|
4802
|
+
w.u16(ext.length);
|
|
4803
|
+
w.utf16(ext);
|
|
4804
|
+
return w.build();
|
|
4805
|
+
}
|
|
4806
|
+
function buildDocInfoStream(col, images = []) {
|
|
4807
|
+
const chunks = [];
|
|
4808
|
+
chunks.push(mkRec(TAG_DOCUMENT_PROPERTIES, 0, mkDocumentProperties()));
|
|
4809
|
+
chunks.push(mkRec(TAG_ID_MAPPINGS, 1, mkIdMappings(col, images.length)));
|
|
4810
|
+
for (const img of images) {
|
|
4811
|
+
chunks.push(mkRec(TAG_BIN_DATA, 1, mkBinData(img.id, img.ext)));
|
|
4812
|
+
}
|
|
4813
|
+
for (let cat = 0; cat < 7; cat++) {
|
|
4814
|
+
for (const name of col.fonts) {
|
|
4815
|
+
chunks.push(mkRec(TAG_FACE_NAME2, 1, mkFaceName(name)));
|
|
4816
|
+
}
|
|
4817
|
+
}
|
|
4818
|
+
for (const entry of col.bfData) {
|
|
4819
|
+
chunks.push(mkRec(
|
|
4820
|
+
TAG_BORDER_FILL2,
|
|
4821
|
+
1,
|
|
4822
|
+
entry.uniform ? mkBorderFill(entry.s, entry.bg) : mkBorderFillPerSide(entry.l, entry.r, entry.t, entry.b, entry.bg)
|
|
4823
|
+
));
|
|
4824
|
+
}
|
|
4825
|
+
for (const p of col.csProps) {
|
|
4826
|
+
chunks.push(mkRec(TAG_CHAR_SHAPE2, 1, mkCharShape(p, col)));
|
|
4827
|
+
}
|
|
4828
|
+
for (const p of col.psProps) {
|
|
4829
|
+
chunks.push(mkRec(TAG_PARA_SHAPE2, 1, mkParaShape(p)));
|
|
4830
|
+
}
|
|
4831
|
+
return concatU8(chunks);
|
|
4832
|
+
}
|
|
4833
|
+
function mkPageDef(dims) {
|
|
4834
|
+
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();
|
|
4835
|
+
}
|
|
4836
|
+
function mkParaHeader(nchars, ctrlMask, psId, csCount, lineAlignCount = 0, instanceId = 0) {
|
|
4837
|
+
return new BufWriter().u32(nchars).u32(ctrlMask).u16(psId).u8(0).u8(0).u16(csCount).u16(0).u16(lineAlignCount).u32(instanceId).u16(0).build();
|
|
4838
|
+
}
|
|
4839
|
+
function mkParaText(text) {
|
|
4840
|
+
const w = new BufWriter();
|
|
4841
|
+
for (let i = 0; i < text.length; i++) {
|
|
4842
|
+
const c = text.charCodeAt(i);
|
|
4843
|
+
w.u16(c < 32 ? 0 : c);
|
|
4844
|
+
}
|
|
4845
|
+
w.u16(13);
|
|
4846
|
+
return w.build();
|
|
4847
|
+
}
|
|
4848
|
+
function mkParaCharShape(pairs) {
|
|
4849
|
+
const w = new BufWriter();
|
|
4850
|
+
for (const [pos, id] of pairs) w.u32(pos).u32(id);
|
|
4851
|
+
return w.build();
|
|
4852
|
+
}
|
|
4853
|
+
function mkSecdParaText() {
|
|
4854
|
+
const lo = CTRL_SECD & 65535;
|
|
4855
|
+
const hi = CTRL_SECD >>> 16 & 65535;
|
|
4856
|
+
return new BufWriter().u16(2).u16(lo).u16(hi).u16(0).u16(0).u16(0).u16(0).u16(2).u16(13).build();
|
|
4857
|
+
}
|
|
4858
|
+
function mkTableParaText() {
|
|
4859
|
+
const lo = CTRL_TABLE2 & 65535;
|
|
4860
|
+
const hi = CTRL_TABLE2 >>> 16 & 65535;
|
|
4861
|
+
return new BufWriter().u16(11).u16(lo).u16(hi).u16(0).u16(0).u16(0).u16(0).u16(11).u16(13).build();
|
|
4862
|
+
}
|
|
4863
|
+
function mkPicParaText() {
|
|
4864
|
+
const lo = CTRL_PIC & 65535;
|
|
4865
|
+
const hi = CTRL_PIC >>> 16 & 65535;
|
|
4866
|
+
return new BufWriter().u16(11).u16(lo).u16(hi).u16(0).u16(0).u16(0).u16(0).u16(11).u16(13).build();
|
|
4867
|
+
}
|
|
4868
|
+
function mkShapeComponentPicture(binDataId, wHwp, hHwp) {
|
|
4869
|
+
const w = new BufWriter();
|
|
4870
|
+
w.u32(CTRL_PIC);
|
|
4871
|
+
w.zeros(15);
|
|
4872
|
+
w.u32(0);
|
|
4873
|
+
w.u32(0);
|
|
4874
|
+
w.u32(wHwp);
|
|
4875
|
+
w.u32(hHwp);
|
|
4876
|
+
w.u32(0);
|
|
4877
|
+
w.u32(0);
|
|
4878
|
+
w.u32(wHwp);
|
|
4879
|
+
w.u32(hHwp);
|
|
4880
|
+
w.zeros(20);
|
|
4881
|
+
w.zeros(16);
|
|
4882
|
+
w.u16(binDataId);
|
|
4883
|
+
w.u8(0);
|
|
4884
|
+
w.u8(0);
|
|
4885
|
+
w.u8(0);
|
|
4886
|
+
w.zeros(5);
|
|
4887
|
+
return w.build();
|
|
4888
|
+
}
|
|
4889
|
+
function encodePicPara(imgNode, binDataId, col, lv, idGen) {
|
|
4890
|
+
const wHwp = Metric.ptToHwp(Math.max(imgNode.w, 10));
|
|
4891
|
+
const hHwp = Metric.ptToHwp(Math.max(imgNode.h, 10));
|
|
4892
|
+
const TABLE_CTRL_MASK = 1 << 11;
|
|
4893
|
+
const instanceId = idGen();
|
|
4894
|
+
const psId = col.addParaShape({});
|
|
4895
|
+
return [
|
|
4896
|
+
mkRec(TAG_PARA_HEADER2, lv, mkParaHeader(9, TABLE_CTRL_MASK, psId, 1, 0, instanceId)),
|
|
4897
|
+
mkRec(TAG_PARA_TEXT2, lv + 1, mkPicParaText()),
|
|
4898
|
+
mkRec(TAG_PARA_CHAR_SHAPE2, lv + 1, mkParaCharShape([[0, 0]])),
|
|
4899
|
+
// $pic CTRL_HEADER (GHDR) at level lv+1
|
|
4900
|
+
mkRec(TAG_CTRL_HEADER2, lv + 1, mkPicCtrl(wHwp, hHwp, idGen())),
|
|
4901
|
+
// SHAPE_COMPONENT_PICTURE at level lv+2
|
|
4902
|
+
mkRec(TAG_SHAPE_COMPONENT_PICTURE, lv + 2, mkShapeComponentPicture(binDataId, wHwp, hHwp))
|
|
4903
|
+
];
|
|
4904
|
+
}
|
|
4905
|
+
function encodePara4(para, col, lv, instanceId) {
|
|
4906
|
+
let text = "";
|
|
4907
|
+
const csPairs = [];
|
|
4908
|
+
let pos = 0;
|
|
4909
|
+
for (const kid of para.kids) {
|
|
4910
|
+
if (kid.tag === "span") {
|
|
4911
|
+
const span = kid;
|
|
4912
|
+
const csId = col.addCharShape(span.props);
|
|
4913
|
+
if (csPairs.length === 0 || csPairs[csPairs.length - 1][1] !== csId) {
|
|
4914
|
+
csPairs.push([pos, csId]);
|
|
4915
|
+
}
|
|
4916
|
+
for (const t of span.kids) {
|
|
4917
|
+
if (t.tag === "txt") {
|
|
4918
|
+
text += t.content;
|
|
4919
|
+
pos += t.content.length;
|
|
4920
|
+
}
|
|
4921
|
+
}
|
|
4922
|
+
}
|
|
4923
|
+
}
|
|
4924
|
+
if (csPairs.length === 0) csPairs.push([0, 0]);
|
|
4925
|
+
const psId = col.addParaShape(para.props);
|
|
4926
|
+
const nchars = text.length + 1;
|
|
4927
|
+
return [
|
|
4928
|
+
mkRec(TAG_PARA_HEADER2, lv, mkParaHeader(nchars, 0, psId, csPairs.length, 0, instanceId)),
|
|
4929
|
+
mkRec(TAG_PARA_TEXT2, lv + 1, mkParaText(text)),
|
|
4930
|
+
mkRec(TAG_PARA_CHAR_SHAPE2, lv + 1, mkParaCharShape(csPairs))
|
|
4931
|
+
];
|
|
4932
|
+
}
|
|
4933
|
+
function mkTableCtrl(wHwp, hHwp, instanceId) {
|
|
4934
|
+
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();
|
|
4935
|
+
}
|
|
4936
|
+
function mkPicCtrl(wHwp, hHwp, instanceId) {
|
|
4937
|
+
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();
|
|
4938
|
+
}
|
|
4939
|
+
function mkTableRecord(rowCnt, colCnt, rowHwp, bfId) {
|
|
4940
|
+
const w = new BufWriter();
|
|
4941
|
+
w.u32(67108870);
|
|
4942
|
+
w.u16(rowCnt);
|
|
4943
|
+
w.u16(colCnt);
|
|
4944
|
+
w.u16(0);
|
|
4945
|
+
w.u16(510);
|
|
4946
|
+
w.u16(510);
|
|
4947
|
+
w.u16(141);
|
|
4948
|
+
w.u16(141);
|
|
4949
|
+
for (const h of rowHwp) w.u16(Math.max(1, h & 65535));
|
|
4950
|
+
w.u16(bfId);
|
|
4951
|
+
w.u16(0);
|
|
4952
|
+
return w.build();
|
|
4953
|
+
}
|
|
4954
|
+
function mkCellListHeader(paraCount, row, col, rs, cs, wHwp, hHwp, bfId) {
|
|
4955
|
+
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();
|
|
4956
|
+
}
|
|
4957
|
+
var DEFAULT_ROW_HEIGHT_PT = 14;
|
|
4958
|
+
function encodeGrid5(grid, col, lv, idGen) {
|
|
4959
|
+
const records = [];
|
|
4960
|
+
const rowCnt = grid.kids.length;
|
|
4961
|
+
const colCnt = Math.max(1, grid.kids[0]?.kids.length ?? 1);
|
|
4962
|
+
const cwPt = grid.props.colWidths ?? [];
|
|
4963
|
+
const totalPt = cwPt.reduce((s, w) => s + w, 0) || 453;
|
|
4964
|
+
const defColPt = totalPt / colCnt;
|
|
4965
|
+
const defStroke = grid.props.defaultStroke ?? col.DEF_STROKE;
|
|
4966
|
+
const defBfId = col.addBorderFill(defStroke);
|
|
4967
|
+
const rowHwp = grid.kids.map((row) => row.heightPt != null && row.heightPt > 0 ? Metric.ptToHwp(row.heightPt) : Metric.ptToHwp(DEFAULT_ROW_HEIGHT_PT));
|
|
4968
|
+
const totalWPt = cwPt.length > 0 ? cwPt.reduce((s, w) => s + w, 0) : totalPt;
|
|
4969
|
+
const totalHPt = grid.kids.reduce((s, row) => s + (row.heightPt != null && row.heightPt > 0 ? row.heightPt : DEFAULT_ROW_HEIGHT_PT), 0);
|
|
4970
|
+
const tblWHwp = Metric.ptToHwp(totalWPt);
|
|
4971
|
+
const tblHHwp = Metric.ptToHwp(totalHPt);
|
|
4972
|
+
const tblInstanceId = idGen();
|
|
4973
|
+
records.push(mkRec(TAG_CTRL_HEADER2, lv, mkTableCtrl(tblWHwp, tblHHwp, tblInstanceId)));
|
|
4974
|
+
records.push(mkRec(TAG_TABLE, lv + 1, mkTableRecord(rowCnt, colCnt, rowHwp, defBfId)));
|
|
4975
|
+
for (let r = 0; r < grid.kids.length; r++) {
|
|
4976
|
+
for (let c = 0; c < grid.kids[r].kids.length; c++) {
|
|
4977
|
+
const cell = grid.kids[r].kids[c];
|
|
4978
|
+
const wHwp = Metric.ptToHwp(cwPt[c] ?? defColPt);
|
|
4979
|
+
const hHwp = rowHwp[r];
|
|
4980
|
+
const cp = cell.props;
|
|
4981
|
+
const hasPerSide = cp.top || cp.bot || cp.left || cp.right;
|
|
4982
|
+
const bfId = hasPerSide ? col.addBorderFillPerSide(
|
|
4983
|
+
cp.left ?? defStroke,
|
|
4984
|
+
cp.right ?? defStroke,
|
|
4985
|
+
cp.top ?? defStroke,
|
|
4986
|
+
cp.bot ?? defStroke,
|
|
4987
|
+
cp.bg
|
|
4988
|
+
) : col.addBorderFill(defStroke, cp.bg);
|
|
4989
|
+
const paras = cell.kids.length > 0 ? cell.kids : [{ tag: "para", props: {}, kids: [] }];
|
|
4990
|
+
records.push(mkRec(
|
|
4991
|
+
TAG_LIST_HEADER2,
|
|
4992
|
+
lv + 1,
|
|
4993
|
+
mkCellListHeader(paras.length, r, c, cell.rs, cell.cs, wHwp, hHwp, bfId)
|
|
4994
|
+
));
|
|
4995
|
+
for (const para of paras) {
|
|
4996
|
+
records.push(...encodePara4(para, col, lv + 2, idGen()));
|
|
4997
|
+
}
|
|
4998
|
+
}
|
|
4999
|
+
}
|
|
5000
|
+
return records;
|
|
5001
|
+
}
|
|
5002
|
+
function mkSectionCtrl() {
|
|
5003
|
+
return new BufWriter().u32(CTRL_SECD).u32(0).u32(1134).u16(16384).u16(31).zeros(31).build();
|
|
5004
|
+
}
|
|
5005
|
+
function buildSectionParagraph(dims, instanceId) {
|
|
5006
|
+
const SECD_CTRL_MASK = 1 << 2;
|
|
5007
|
+
const nchars = 9;
|
|
5008
|
+
return [
|
|
5009
|
+
mkRec(TAG_PARA_HEADER2, 0, mkParaHeader(nchars, SECD_CTRL_MASK, 0, 1, 1, instanceId)),
|
|
5010
|
+
mkRec(TAG_PARA_TEXT2, 1, mkSecdParaText()),
|
|
5011
|
+
mkRec(TAG_PARA_CHAR_SHAPE2, 1, mkParaCharShape([[0, 0]])),
|
|
5012
|
+
mkRec(TAG_PARA_LINE_SEG, 1, new Uint8Array(36)),
|
|
5013
|
+
// 1 line × 36 bytes = 36 bytes
|
|
5014
|
+
mkRec(TAG_CTRL_HEADER2, 1, mkSectionCtrl()),
|
|
5015
|
+
// 'secd' at level 1
|
|
5016
|
+
mkRec(TAG_PAGE_DEF2, 2, mkPageDef(dims)),
|
|
5017
|
+
// page def at level 2
|
|
5018
|
+
mkRec(TAG_FOOTNOTE_SHAPE, 2, new Uint8Array(28)),
|
|
5019
|
+
// footnote shape (defaults)
|
|
5020
|
+
mkRec(TAG_FOOTNOTE_SHAPE, 2, new Uint8Array(28))
|
|
5021
|
+
// endnote shape (defaults)
|
|
5022
|
+
];
|
|
5023
|
+
}
|
|
5024
|
+
function buildBodyTextStream(doc, col, images) {
|
|
5025
|
+
const chunks = [];
|
|
5026
|
+
const dims = doc.kids[0]?.dims ?? A4;
|
|
5027
|
+
let instanceIdCounter = 1;
|
|
5028
|
+
const idGen = () => instanceIdCounter++;
|
|
5029
|
+
for (const r of buildSectionParagraph(dims, idGen())) chunks.push(r);
|
|
5030
|
+
const TABLE_CTRL_MASK = 1 << 11;
|
|
5031
|
+
for (const sheet of doc.kids) {
|
|
5032
|
+
for (const node of sheet.kids) {
|
|
5033
|
+
if (node.tag === "para") {
|
|
5034
|
+
const para = node;
|
|
5035
|
+
const hasImg = para.kids.some((k) => k.tag === "img");
|
|
5036
|
+
if (hasImg) {
|
|
5037
|
+
for (const kid of para.kids) {
|
|
5038
|
+
if (kid.tag === "img") {
|
|
5039
|
+
const img = kid;
|
|
5040
|
+
const binImg = images.find((b) => b64Matches(b, img.b64));
|
|
5041
|
+
if (binImg) {
|
|
5042
|
+
for (const r of encodePicPara(img, binImg.id, col, 0, idGen)) chunks.push(r);
|
|
5043
|
+
}
|
|
5044
|
+
}
|
|
5045
|
+
}
|
|
5046
|
+
const textKids = para.kids.filter((k) => k.tag !== "img");
|
|
5047
|
+
if (textKids.length > 0) {
|
|
5048
|
+
const textPara = { tag: "para", props: para.props, kids: textKids };
|
|
5049
|
+
for (const r of encodePara4(textPara, col, 0, idGen())) chunks.push(r);
|
|
5050
|
+
}
|
|
5051
|
+
} else {
|
|
5052
|
+
for (const r of encodePara4(para, col, 0, idGen())) chunks.push(r);
|
|
5053
|
+
}
|
|
5054
|
+
} else if (node.tag === "grid") {
|
|
5055
|
+
chunks.push(mkRec(TAG_PARA_HEADER2, 0, mkParaHeader(9, TABLE_CTRL_MASK, 0, 1, 0, idGen())));
|
|
5056
|
+
chunks.push(mkRec(TAG_PARA_TEXT2, 1, mkTableParaText()));
|
|
5057
|
+
chunks.push(mkRec(TAG_PARA_CHAR_SHAPE2, 1, mkParaCharShape([[0, 0]])));
|
|
5058
|
+
for (const r of encodeGrid5(node, col, 1, idGen)) chunks.push(r);
|
|
5059
|
+
}
|
|
5060
|
+
}
|
|
5061
|
+
}
|
|
5062
|
+
return concatU8(chunks);
|
|
5063
|
+
}
|
|
5064
|
+
function b64Matches(binImg, b64) {
|
|
5065
|
+
const a = TextKit.base64Encode(binImg.data).replace(/\s/g, "");
|
|
5066
|
+
const b = b64.replace(/\s/g, "");
|
|
5067
|
+
return a === b;
|
|
5068
|
+
}
|
|
5069
|
+
function buildHwpFileHeader() {
|
|
5070
|
+
const buf = new Uint8Array(256);
|
|
5071
|
+
const sig = "HWP Document File";
|
|
5072
|
+
for (let i = 0; i < sig.length; i++) buf[i] = sig.charCodeAt(i);
|
|
5073
|
+
const dv = new DataView(buf.buffer);
|
|
5074
|
+
dv.setUint32(32, 83886848, true);
|
|
5075
|
+
dv.setUint32(36, 1, true);
|
|
5076
|
+
return buf;
|
|
5077
|
+
}
|
|
5078
|
+
function buildHwpOle2(fileHeaderData, docInfoData, section0Data, binImages = []) {
|
|
5079
|
+
const SS = 512;
|
|
5080
|
+
const ENDOFCHAIN = 4294967294;
|
|
5081
|
+
const FREESECT = 4294967295;
|
|
5082
|
+
const FATSECT = 4294967293;
|
|
5083
|
+
function padSector(d) {
|
|
5084
|
+
const n = Math.ceil(Math.max(d.length, 1) / SS) * SS;
|
|
5085
|
+
if (d.length === n) return d;
|
|
5086
|
+
const out2 = new Uint8Array(n);
|
|
5087
|
+
out2.set(d);
|
|
5088
|
+
return out2;
|
|
5089
|
+
}
|
|
5090
|
+
const fhPad = padSector(fileHeaderData);
|
|
5091
|
+
const diPad = padSector(docInfoData);
|
|
5092
|
+
const s0Pad = padSector(section0Data);
|
|
5093
|
+
const imgPads = binImages.map((img) => padSector(img.data));
|
|
5094
|
+
const fhN = fhPad.length / SS;
|
|
5095
|
+
const diN = diPad.length / SS;
|
|
5096
|
+
const s0N = s0Pad.length / SS;
|
|
5097
|
+
const imgNs = imgPads.map((p) => p.length / SS);
|
|
5098
|
+
const totalImgN = imgNs.reduce((s, n) => s + n, 0);
|
|
5099
|
+
const numDirEntries = 5 + (binImages.length > 0 ? 1 + binImages.length : 0);
|
|
5100
|
+
const dirN = Math.max(2, Math.ceil(numDirEntries / 4));
|
|
5101
|
+
let fatN = 1;
|
|
5102
|
+
for (let iter = 0; iter < 10; iter++) {
|
|
5103
|
+
const total = fatN + dirN + fhN + diN + s0N + totalImgN;
|
|
5104
|
+
const needed = Math.ceil(total / 128);
|
|
5105
|
+
if (needed <= fatN) break;
|
|
5106
|
+
fatN = needed;
|
|
5107
|
+
}
|
|
5108
|
+
const dir1Sec = fatN;
|
|
5109
|
+
const fhSec = fatN + dirN;
|
|
5110
|
+
const diSec = fhSec + fhN;
|
|
5111
|
+
const s0Sec = diSec + diN;
|
|
5112
|
+
const imgSecs = [];
|
|
5113
|
+
let curSec = s0Sec + s0N;
|
|
5114
|
+
for (const n of imgNs) {
|
|
5115
|
+
imgSecs.push(curSec);
|
|
5116
|
+
curSec += n;
|
|
5117
|
+
}
|
|
5118
|
+
const totalSec = curSec;
|
|
5119
|
+
const fatBuf = new Uint8Array(fatN * SS).fill(255);
|
|
5120
|
+
const setFat = (i, v) => {
|
|
5121
|
+
fatBuf[i * 4] = v & 255;
|
|
5122
|
+
fatBuf[i * 4 + 1] = v >>> 8 & 255;
|
|
5123
|
+
fatBuf[i * 4 + 2] = v >>> 16 & 255;
|
|
5124
|
+
fatBuf[i * 4 + 3] = v >>> 24 & 255;
|
|
5125
|
+
};
|
|
5126
|
+
for (let i = 0; i < fatN; i++) setFat(i, FATSECT);
|
|
5127
|
+
for (let i = 0; i < dirN; i++) setFat(dir1Sec + i, i + 1 < dirN ? dir1Sec + i + 1 : ENDOFCHAIN);
|
|
5128
|
+
for (let i = 0; i < fhN; i++) setFat(fhSec + i, i + 1 < fhN ? fhSec + i + 1 : ENDOFCHAIN);
|
|
5129
|
+
for (let i = 0; i < diN; i++) setFat(diSec + i, i + 1 < diN ? diSec + i + 1 : ENDOFCHAIN);
|
|
5130
|
+
for (let i = 0; i < s0N; i++) setFat(s0Sec + i, i + 1 < s0N ? s0Sec + i + 1 : ENDOFCHAIN);
|
|
5131
|
+
for (let ii = 0; ii < imgNs.length; ii++) {
|
|
5132
|
+
const start = imgSecs[ii];
|
|
5133
|
+
const n = imgNs[ii];
|
|
5134
|
+
for (let i = 0; i < n; i++) setFat(start + i, i + 1 < n ? start + i + 1 : ENDOFCHAIN);
|
|
5135
|
+
}
|
|
5136
|
+
const dirBuf = new Uint8Array(dirN * SS);
|
|
5137
|
+
const dv = new DataView(dirBuf.buffer);
|
|
5138
|
+
function writeDirEntry(idx, name, type, left, right, child, startSec, size) {
|
|
5139
|
+
const base = idx * 128;
|
|
5140
|
+
const nl = Math.min(name.length, 31);
|
|
5141
|
+
for (let i = 0; i < nl; i++) dv.setUint16(base + i * 2, name.charCodeAt(i), true);
|
|
5142
|
+
dv.setUint16(base + 64, (nl + 1) * 2, true);
|
|
5143
|
+
dirBuf[base + 66] = type;
|
|
5144
|
+
dirBuf[base + 67] = 1;
|
|
5145
|
+
dv.setInt32(base + 68, left, true);
|
|
5146
|
+
dv.setInt32(base + 72, right, true);
|
|
5147
|
+
dv.setInt32(base + 76, child, true);
|
|
5148
|
+
dv.setUint32(base + 116, startSec >>> 0, true);
|
|
5149
|
+
dv.setUint32(base + 120, size >>> 0, true);
|
|
5150
|
+
}
|
|
5151
|
+
if (binImages.length > 0) {
|
|
5152
|
+
writeDirEntry(0, "Root Entry", 5, -1, -1, 1, ENDOFCHAIN, 0);
|
|
5153
|
+
writeDirEntry(1, "FileHeader", 2, -1, 2, -1, fhSec, fileHeaderData.length);
|
|
5154
|
+
writeDirEntry(2, "DocInfo", 2, -1, 3, -1, diSec, docInfoData.length);
|
|
5155
|
+
writeDirEntry(3, "BodyText", 1, -1, 5, 4, ENDOFCHAIN, 0);
|
|
5156
|
+
writeDirEntry(4, "Section0", 2, -1, -1, -1, s0Sec, section0Data.length);
|
|
5157
|
+
writeDirEntry(5, "BinData", 1, -1, -1, 6, ENDOFCHAIN, 0);
|
|
5158
|
+
for (let ii = 0; ii < binImages.length; ii++) {
|
|
5159
|
+
const img = binImages[ii];
|
|
5160
|
+
const streamName = `BIN${String(img.id).padStart(4, "0")}.${img.ext}`;
|
|
5161
|
+
const sibling = ii + 1 < binImages.length ? 7 + ii : -1;
|
|
5162
|
+
writeDirEntry(6 + ii, streamName, 2, -1, sibling, -1, imgSecs[ii], img.data.length);
|
|
5163
|
+
}
|
|
5164
|
+
} else {
|
|
5165
|
+
writeDirEntry(0, "Root Entry", 5, -1, -1, 1, ENDOFCHAIN, 0);
|
|
5166
|
+
writeDirEntry(1, "FileHeader", 2, -1, 2, -1, fhSec, fileHeaderData.length);
|
|
5167
|
+
writeDirEntry(2, "DocInfo", 2, -1, 3, -1, diSec, docInfoData.length);
|
|
5168
|
+
writeDirEntry(3, "BodyText", 1, -1, -1, 4, ENDOFCHAIN, 0);
|
|
5169
|
+
writeDirEntry(4, "Section0", 2, -1, -1, -1, s0Sec, section0Data.length);
|
|
5170
|
+
}
|
|
5171
|
+
const hdr = new Uint8Array(SS);
|
|
5172
|
+
const hdv = new DataView(hdr.buffer);
|
|
5173
|
+
const MAGIC = [208, 207, 17, 224, 161, 177, 26, 225];
|
|
5174
|
+
MAGIC.forEach((b, i) => {
|
|
5175
|
+
hdr[i] = b;
|
|
5176
|
+
});
|
|
5177
|
+
hdv.setUint16(24, 62, true);
|
|
5178
|
+
hdv.setUint16(26, 3, true);
|
|
5179
|
+
hdv.setUint16(28, 65534, true);
|
|
5180
|
+
hdv.setUint16(30, 9, true);
|
|
5181
|
+
hdv.setUint16(32, 6, true);
|
|
5182
|
+
hdv.setUint32(40, 0, true);
|
|
5183
|
+
hdv.setUint32(44, fatN, true);
|
|
5184
|
+
hdv.setUint32(48, dir1Sec, true);
|
|
5185
|
+
hdv.setUint32(52, 0, true);
|
|
5186
|
+
hdv.setUint32(56, 4096, true);
|
|
5187
|
+
hdv.setUint32(60, ENDOFCHAIN, true);
|
|
5188
|
+
hdv.setUint32(64, 0, true);
|
|
5189
|
+
hdv.setUint32(68, ENDOFCHAIN, true);
|
|
5190
|
+
hdv.setUint32(72, 0, true);
|
|
5191
|
+
for (let i = 0; i < 109; i++) {
|
|
5192
|
+
hdv.setUint32(76 + i * 4, i < fatN ? i : FREESECT, true);
|
|
5193
|
+
}
|
|
5194
|
+
const out = new Uint8Array(SS + totalSec * SS);
|
|
5195
|
+
out.set(hdr, 0);
|
|
5196
|
+
for (let i = 0; i < fatN; i++) {
|
|
5197
|
+
out.set(fatBuf.subarray(i * SS, (i + 1) * SS), SS + i * SS);
|
|
5198
|
+
}
|
|
5199
|
+
for (let i = 0; i < dirN; i++) {
|
|
5200
|
+
out.set(dirBuf.subarray(i * SS, (i + 1) * SS), SS + (dir1Sec + i) * SS);
|
|
5201
|
+
}
|
|
5202
|
+
out.set(fhPad, SS + fhSec * SS);
|
|
5203
|
+
out.set(diPad, SS + diSec * SS);
|
|
5204
|
+
out.set(s0Pad, SS + s0Sec * SS);
|
|
5205
|
+
for (let ii = 0; ii < imgPads.length; ii++) {
|
|
5206
|
+
out.set(imgPads[ii], SS + imgSecs[ii] * SS);
|
|
5207
|
+
}
|
|
5208
|
+
return out;
|
|
5209
|
+
}
|
|
5210
|
+
function concatU8(arrays) {
|
|
5211
|
+
const total = arrays.reduce((s, a) => s + a.length, 0);
|
|
5212
|
+
const out = new Uint8Array(total);
|
|
5213
|
+
let off = 0;
|
|
5214
|
+
for (const a of arrays) {
|
|
5215
|
+
out.set(a, off);
|
|
5216
|
+
off += a.length;
|
|
5217
|
+
}
|
|
5218
|
+
return out;
|
|
5219
|
+
}
|
|
5220
|
+
var HwpEncoder = class {
|
|
5221
|
+
constructor() {
|
|
5222
|
+
this.format = "hwp";
|
|
5223
|
+
}
|
|
5224
|
+
async encode(doc) {
|
|
5225
|
+
try {
|
|
5226
|
+
let collectImages3 = function(node) {
|
|
5227
|
+
if (node.tag === "para") {
|
|
5228
|
+
for (const kid of node.kids) {
|
|
5229
|
+
if (kid.tag === "img") {
|
|
5230
|
+
const key = kid.b64.substring(0, 50);
|
|
5231
|
+
if (!seenB64.has(key)) {
|
|
5232
|
+
seenB64.add(key);
|
|
5233
|
+
const raw = TextKit.base64Decode(kid.b64);
|
|
5234
|
+
let ext = "jpg";
|
|
5235
|
+
if (kid.mime === "image/png") ext = "png";
|
|
5236
|
+
else if (kid.mime === "image/gif") ext = "gif";
|
|
5237
|
+
else if (kid.mime === "image/bmp") ext = "bmp";
|
|
5238
|
+
images.push({ id: binIdCounter++, ext, data: new Uint8Array(raw) });
|
|
5239
|
+
}
|
|
5240
|
+
}
|
|
5241
|
+
}
|
|
5242
|
+
} else if (node.tag === "grid") {
|
|
5243
|
+
for (const row of node.kids) {
|
|
5244
|
+
for (const cell of row.kids) {
|
|
5245
|
+
for (const para of cell.kids) collectImages3(para);
|
|
5246
|
+
}
|
|
5247
|
+
}
|
|
5248
|
+
}
|
|
5249
|
+
};
|
|
5250
|
+
var collectImages2 = collectImages3;
|
|
5251
|
+
const col = new StyleCollector();
|
|
5252
|
+
for (const sheet of doc.kids) {
|
|
5253
|
+
for (const node of sheet.kids) collectNode(node, col);
|
|
5254
|
+
}
|
|
5255
|
+
const images = [];
|
|
5256
|
+
const seenB64 = /* @__PURE__ */ new Set();
|
|
5257
|
+
let binIdCounter = 1;
|
|
5258
|
+
for (const sheet of doc.kids) {
|
|
5259
|
+
for (const node of sheet.kids) collectImages3(node);
|
|
5260
|
+
}
|
|
5261
|
+
const docInfoRaw = buildDocInfoStream(col, images);
|
|
5262
|
+
const bodyRaw = buildBodyTextStream(doc, col, images);
|
|
5263
|
+
const docInfoCmp = pako3.deflate(docInfoRaw);
|
|
5264
|
+
const bodyCmp = pako3.deflate(bodyRaw);
|
|
5265
|
+
const fileHdr = buildHwpFileHeader();
|
|
5266
|
+
const hwp = buildHwpOle2(fileHdr, docInfoCmp, bodyCmp, images);
|
|
5267
|
+
return succeed(hwp);
|
|
5268
|
+
} catch (e) {
|
|
5269
|
+
return fail(`HwpEncoder: ${e instanceof Error ? e.message : String(e)}`);
|
|
5270
|
+
}
|
|
5271
|
+
}
|
|
5272
|
+
};
|
|
5273
|
+
registry.registerEncoder(new HwpEncoder());
|
|
5274
|
+
|
|
3390
5275
|
// src/walk/TreeWalker.ts
|
|
3391
5276
|
function walkNode(node, cb, parent = null, depth = 0) {
|
|
3392
5277
|
const result = cb(node, parent, depth);
|
|
@@ -3445,6 +5330,7 @@ function validateRoot(root) {
|
|
|
3445
5330
|
}
|
|
3446
5331
|
export {
|
|
3447
5332
|
A4,
|
|
5333
|
+
A4_LANDSCAPE,
|
|
3448
5334
|
ArchiveKit,
|
|
3449
5335
|
BinaryKit,
|
|
3450
5336
|
DEFAULT_STROKE,
|
|
@@ -3454,17 +5340,20 @@ export {
|
|
|
3454
5340
|
TextKit,
|
|
3455
5341
|
TreeWalker,
|
|
3456
5342
|
XmlKit,
|
|
5343
|
+
buildBr,
|
|
3457
5344
|
buildCell,
|
|
3458
5345
|
buildGrid,
|
|
3459
5346
|
buildImg,
|
|
3460
5347
|
buildPageNum,
|
|
3461
5348
|
buildPara,
|
|
5349
|
+
buildPb,
|
|
3462
5350
|
buildRoot,
|
|
3463
5351
|
buildRow,
|
|
3464
5352
|
buildSheet,
|
|
3465
5353
|
buildSpan,
|
|
3466
5354
|
countNodes,
|
|
3467
5355
|
fail,
|
|
5356
|
+
normalizeDims,
|
|
3468
5357
|
registry,
|
|
3469
5358
|
safeAlign,
|
|
3470
5359
|
safeFont,
|