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/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
- return { tag: "img", b64, mime, w, h, alt };
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
- return { tag: "row", kids };
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 ?? ""] ?? "justify";
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
- map.set(id, { align });
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
- return buildImg(TextKit.base64Encode(imgData), mimeMap[ext] ?? "image/png", w, h);
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
- return buildRow(cellNodes);
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.inflateRaw(data);
1307
+ return pako2.inflate(data);
1169
1308
  } catch {
1170
- return data;
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: "none", 1: "solid", 2: "dash", 3: "dot", 4: "dash", 5: "dash", 6: "dash", 7: "dot", 8: "double", 9: "double", 10: "double" };
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 b = 2 + i * 6;
1237
- if (b + 6 <= d.length) {
1238
- borders.push({ type: d[b], widthPt: BORDER_W_PT[d[b + 1]] ?? 0.5, color: colorRef(d, b + 2) });
1239
- } else {
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 && BinaryKit.readU32LE(r.data, 0) === CTRL_TABLE) {
1293
- const tr = shield.guard(
1294
- () => parseTableCtrl(recs, i, di, shield),
1295
- { grid: null, next: skipKids(recs, i) },
1296
- `hwp:tbl@${i}`
1297
- );
1298
- if (tr.grid) grids.push(tr.grid);
1299
- i = tr.next;
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 joined = text.chars.map((c) => c.ch).join("");
1310
- if (joined.trim()) {
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
- nodes.push(buildPara(spans, buildParaProps(ps)));
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 controlPositions = [];
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
- controlPositions.push(pos);
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, controlPositions };
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
- rs = Math.max(1, BinaryKit.readU16LE(d, 12));
1548
- cs = Math.max(1, BinaryKit.readU16LE(d, 14));
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 = shield.guardAll(
1759
- elements,
1760
- (el) => decodeElement(el, decCtx),
1761
- () => buildPara([buildSpan("[\uC694\uC18C \uD30C\uC2F1 \uC2E4\uD328]")]),
1762
- "docx:bodyElement"
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 headerParas = await decodeHeaderFooter2("header", body, relsMap, files, decCtx);
1765
- const footerParas = await decodeHeaderFooter2("footer", body, relsMap, files, decCtx);
1766
- warns.push(...shield.flush());
1767
- const sheet = buildSheet(
1768
- kids.filter(Boolean),
1769
- dims,
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(abs?._attr?.["w:abstractNumId"] ?? abs?._attr?.abstractNumId ?? 0);
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) return paras.map((n) => ({ type: "para", node: n }));
1864
- if (paras.length === 0) return tables.map((n) => ({ type: "table", node: n }));
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) items.push({ type: "table", node: tables[ti++] });
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 = target.startsWith("/") ? target.slice(1) : `word/${target}`;
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
- return buildPara(kids.filter(Boolean), props);
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
- const drawing = run?.["w:drawing"]?.[0] ?? run?.drawing?.[0];
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 = target.startsWith("/") ? target.slice(1) : `word/${target}`;
2344
+ const filePath = resolveDocxPath("word", target);
1987
2345
  const fileData = ctx.files.get(filePath);
1988
- if (!fileData) return null;
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
- return buildImg(TextKit.base64Encode(fileData), mime, wPt, hPt, alt || void 0);
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 tblBorders = tblPr?.["w:tblBorders"]?.[0] ?? tblPr?.tblBorders?.[0];
2058
- let defaultStroke = void 0;
2059
- if (tblBorders) {
2060
- const top = tblBorders?.["w:top"]?.[0]?._attr ?? tblBorders?.top?.[0]?._attr;
2061
- if (top) {
2062
- defaultStroke = safeStrokeDocx(
2063
- top?.["w:val"] ?? top?.val,
2064
- Number(top?.["w:sz"] ?? top?.sz ?? 4),
2065
- top?.["w:color"] ?? top?.color
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 tcBorders = tcPr?.["w:tcBorders"]?.[0] ?? tcPr?.tcBorders?.[0];
2573
+ const tcBordersNode = tcPr?.["w:tcBorders"]?.[0] ?? tcPr?.tcBorders?.[0];
2122
2574
  const cp = { bg, isHeader: isHeaderRow || void 0 };
2123
- if (tcBorders) {
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 = tcBorders?.["w:" + xmlTag]?.[0]?._attr ?? tcBorders?.[xmlTag]?.[0]?._attr;
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
- bdr?.["w:val"] ?? bdr?.val,
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 = { top: "top", center: "mid", bottom: "bot" };
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
- const paras = toArr2(cell?.["w:p"] ?? cell?.p).map((p) => decodePara2(p, ctx));
2149
- cellNodes.push(buildCell(
2150
- paras.length > 0 ? paras : [buildPara([buildSpan("")])],
2151
- { cs: rc.gridSpan, rs, props: cp }
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(cellArr.map((c) => buildCell([buildPara([buildSpan(cellText2(c))])])));
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([buildRow([buildCell([buildPara([buildSpan(tableText2(tbl))])])])]);
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._binId) return;
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._binId = id;
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 = { solid: "SOLID", dash: "DASH", dot: "DOT", double: "DOUBLE", none: "NONE" };
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="SOLID" width="0.1 mm" color="#000000"/>${fill}</hh:borderFill>`;
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
- { name: "Contents/content.hpf", data: TextKit.encode(contentHpf(ctx, doc.meta)) },
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="6" xmlVersion="1.2" application="Hancom Office Hangul" appVersion="11, 0, 0, 0"/>`;
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="no"/><opf:itemref idref="section0" linear="yes"/></opf:spine></opf:package>`;
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 = ["HANGUL", "LATIN", "HANJA", "JAPANESE", "OTHER", "SYMBOL", "USER"];
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
- 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"/><hh:margin><hc:intent value="0" unit="HWPUNIT"/><hc:left value="0" unit="HWPUNIT"/><hc:right value="0" unit="HWPUNIT"/><hc:prev value="0" unit="HWPUNIT"/><hc:next value="0" unit="HWPUNIT"/></hh:margin><hh:lineSpacing type="PERCENT" value="160" unit="HWPUNIT"/><hh:border borderFillIDRef="1" offsetLeft="0" offsetRight="0" offsetTop="0" offsetBottom="0" connect="0" ignoreMargin="0"/></hh:paraPr>`;
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.2" 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>`;
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
- contentXml += `<hp:p id="${ctx.nextElementId++}" paraPrIDRef="0" styleIDRef="0" pageBreak="0" columnBreak="0" merged="0"><hp:run charPrIDRef="0">${prefix}${gridXml}</hp:run>${defaultLineseg}</hp:p>`;
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="${ctx.nextElementId++}" paraPrIDRef="0" styleIDRef="0" pageBreak="0" columnBreak="0" merged="0"><hp:run charPrIDRef="0">${secPr}<hp:t></hp:t></hp:run>${defaultLineseg}</hp:p>`;
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 encodePara(para, ctx, secPr = "") {
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 * 0.6);
2650
- const linesegarray = `<hp:linesegarray><hp:lineseg textpos="0" vertpos="0" vertsize="${vertsize}" textheight="${textheight}" baseline="${baseline}" spacing="${spacing}" horzpos="0" horzsize="${ctx.availableWidth}" flags="393216"/></hp:linesegarray>`;
2651
- return `<hp:p id="${ctx.nextElementId++}" paraPrIDRef="${paraPrId}" styleIDRef="0" pageBreak="0" columnBreak="0" merged="0">${runs}${linesegarray}</hp:p>`;
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._binId;
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
- return `<hp:run charPrIDRef="${charPrId}"><hp:pic id="${ctx.nextElementId++}" zOrder="0" numberingType="PICTURE" textWrap="TOP_AND_BOTTOM" textFlow="BOTH_SIDES" lock="0" dropcapstyle="None" pageBreak="CELL" treatAsChar="1"><hp:sz width="${w}" widthRelTo="ABSOLUTE" height="${h}" 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="0" right="0" top="0" bottom="0"/><hp:imgRect x="0" y="0" w="${w}" h="${h}"/><hp:imgClip left="0" right="0" top="0" bottom="0"/><hp:img binaryItemIDRef="${binId}" bright="0" contrast="0" effect="REAL_PIC" alpha="0"/></hp:pic></hp:run>`;
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 (const row of grid.kids) {
2696
- let rowCols = 0;
2697
- for (const cell of row.kids) rowCols += cell.cs;
2698
- if (rowCols > colCount) colCount = rowCols;
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) srcPt[i] = zeroFill > 0 ? zeroFill : Metric.hwpToPt(defaultColW);
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++) colWidths[i] = Math.round(colWidths[i] * scale);
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
- if (cell.props.bg) {
2733
- cellBfId = addBorderFill(ctx, grid.props.defaultStroke ?? DEFAULT_STROKE, cell.props.bg);
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++) cellW += colWidths[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
- 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="1000"/><hp:cellMargin left="141" right="141" top="141" bottom="141"/></hp:tc>`;
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="CELL"${headerRow} rowCnt="${rowCount}" colCnt="${colCount}" cellSpacing="0" borderFillIDRef="${tblBfId}" noAdjust="0"><hp:sz width="${actualTotal}" widthRelTo="ABSOLUTE" height="1000" 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="0" right="0" top="0" bottom="0"/><hp:inMargin left="138" right="138" top="138" bottom="138"/>${rowsXml}</hp:tbl>`;
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") return k.kids.map((c) => c.tag === "txt" ? c.content : "").join("");
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((p) => p.kids.map((k) => {
2761
- if (k.tag === "span") return k.kids.map((c) => c.tag === "txt" ? c.content : "").join("");
2762
- return "";
2763
- }).join("")).join("")
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._rId) return;
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._rId = rId;
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, _ctx) {
3090
- const rId = img._rId;
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
- return `<w:r><w:drawing><wp:inline distT="0" distB="0" distL="0" distR="0"><wp:extent cx="${cx}" cy="${cy}"/><wp:docPr id="${_ctx.nextId++}" name="Image" descr="${alt}"/><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></wp:inline></w:drawing></w:r>`;
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 (const row of grid.kids) {
3110
- let rowCols = 0;
3111
- for (const cell of row.kids) rowCols += cell.cs;
3112
- if (rowCols > colCount) colCount = rowCols;
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
- const colWidthsDxa = [];
3117
- if (grid.props.colWidths && grid.props.colWidths.length === colCount) {
3941
+ let colWidthsDxa = [];
3942
+ if (grid.props.colWidths && grid.props.colWidths.length > 0) {
3118
3943
  const srcPt = [...grid.props.colWidths];
3119
- const knownTotal = srcPt.filter((w) => w > 0).reduce((s, w) => s + w, 0);
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 remaining = Math.max(0, Metric.dxaToPt(availDxa) - knownTotal);
3122
- const zeroFill = zeroCount > 0 ? remaining / zeroCount : 0;
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) srcPt[i] = zeroFill > 0 ? zeroFill : Metric.dxaToPt(defaultColDxa);
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="${Math.round(w)}"/>`).join("");
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 srcCellIdx = 0;
3152
- while (srcCellIdx < row.kids.length) {
3153
- const cell = row.kids[srcCellIdx];
3154
- const mergeType = vMergeMap.get(`${ri},${colIdx}`);
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 = gc; sc < gc + origCs && sc < colWidthsDxa.length; sc++) cw += colWidthsDxa[sc];
3203
- if (cw === 0) cw = defaultColDxa * origCs;
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 (origCs > 1) contParts += `<w:gridSpan w:val="${origCs}"/>`;
3979
+ if (mapEntry.width > 1) contParts += `<w:gridSpan w:val="${mapEntry.width}"/>`;
3206
3980
  contParts += `<w:vMerge/>`;
3207
- finalCells.push(` <w:tc><w:tcPr>${contParts}</w:tcPr><w:p><w:pPr/></w:p></w:tc>`);
3208
- gc += origCs;
3209
- } else {
3210
- if (cellIter < cellXmls.length) {
3211
- finalCells.push(cellXmls[cellIter]);
3212
- gc += row.kids[cellIter]?.cs ?? 1;
3213
- cellIter++;
3214
- } else {
3215
- gc++;
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
- let trPr = "";
4004
+ const trPrParts = [];
3220
4005
  if (ri === 0 && (gp.headerRow || look?.firstRow)) {
3221
- trPr = "<w:trPr><w:tblHeader/></w:trPr>";
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
- ${finalCells.join("\n")}
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: "dashed", dot: "dotted", double: "double", none: "none" };
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: "dashed", dot: "dotted", double: "double", none: "none" };
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 `![${img.alt ?? ""}](data:${img.mime};base64,${img.b64})`;
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
- if (grid.props.look) warns.push("[SHIELD] MD: \uD45C \uC2A4\uD0C0\uC77C(\uC0C9\uC0C1, \uD14C\uB450\uB9AC, \uBA38\uB9AC\uD589 \uAC15\uC870) \uD45C\uD604 \uBD88\uAC00 \u2014 \uC190\uC2E4\uB428");
3327
- const rows = grid.kids.map(
3328
- (row) => `| ${row.kids.map((cell) => cell.kids.map((p) => encodePara2(p, warns)).join(" ")).join(" | ")} |`
3329
- );
3330
- if (rows.length > 0) {
3331
- const cols = grid.kids[0].kids.length;
3332
- rows.splice(1, 0, `| ${Array(cols).fill("---").join(" | ")} |`);
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 rows.join("\n");
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 || "&nbsp;"}</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,