@shadow-garden/bapbong-docx 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -385,11 +385,16 @@ function propsToMarks(p, ctx) {
385
385
  if (p.italic) marks.push(ctx.schema.marks["em"].create());
386
386
  if (p.underline) marks.push(ctx.schema.marks["underline"].create());
387
387
  if (p.strike) marks.push(ctx.schema.marks["strike"].create());
388
- if (p.color) marks.push(ctx.schema.marks["textColor"].create({ color: p.color }));
389
- if (p.sizePt !== void 0) marks.push(ctx.schema.marks["fontSize"].create({ size: p.sizePt }));
390
- if (p.fontFamily) marks.push(ctx.schema.marks["fontFamily"].create({ family: p.fontFamily }));
391
- if (p.highlight) marks.push(ctx.schema.marks["highlight"].create({ color: p.highlight }));
392
- if (p.vertAlign) marks.push(ctx.schema.marks["vertAlign"].create({ value: p.vertAlign }));
388
+ if (p.color)
389
+ marks.push(ctx.schema.marks["textColor"].create({ color: p.color }));
390
+ if (p.sizePt !== void 0)
391
+ marks.push(ctx.schema.marks["fontSize"].create({ size: p.sizePt }));
392
+ if (p.fontFamily)
393
+ marks.push(ctx.schema.marks["fontFamily"].create({ family: p.fontFamily }));
394
+ if (p.highlight)
395
+ marks.push(ctx.schema.marks["highlight"].create({ color: p.highlight }));
396
+ if (p.vertAlign)
397
+ marks.push(ctx.schema.marks["vertAlign"].create({ value: p.vertAlign }));
393
398
  return marks;
394
399
  }
395
400
  var SYMBOL_MAP = {
@@ -409,7 +414,9 @@ function symbolChar(code) {
409
414
  return Number.isNaN(n) ? "" : String.fromCodePoint(n >= 61440 ? n - 61440 + 32 : n);
410
415
  }
411
416
  function hasPageBreak(run) {
412
- return run.children.some((n) => n.name === "w:br" && attrOf(n, "w:type") === "page");
417
+ return run.children.some(
418
+ (n) => n.name === "w:br" && attrOf(n, "w:type") === "page"
419
+ );
413
420
  }
414
421
  function effectiveChildren(nodes) {
415
422
  const out = [];
@@ -458,8 +465,12 @@ function runInlineNodes(run, marks, ctx) {
458
465
  if (id && ctx.notes.bodies[kind].has(id)) {
459
466
  flush();
460
467
  const num = ctx.notes.ref(kind, id);
461
- const refMarks = [...marks, ctx.schema.marks["vertAlign"].create({ value: "super" })];
462
- if (kind === "footnote") refMarks.push(ctx.schema.marks["footnote"].create({ num }));
468
+ const refMarks = [
469
+ ...marks,
470
+ ctx.schema.marks["vertAlign"].create({ value: "super" })
471
+ ];
472
+ if (kind === "footnote")
473
+ refMarks.push(ctx.schema.marks["footnote"].create({ num }));
463
474
  out.push(ctx.schema.text(String(num), refMarks));
464
475
  }
465
476
  } else if (node.name === "w:br" && attrOf(node, "w:type") !== "page") {
@@ -483,9 +494,11 @@ function parseAnchorFloat(drawing) {
483
494
  const posH = child(anchor, "wp:positionH");
484
495
  if (posH) {
485
496
  const align = child(posH, "wp:align")?.text.trim();
486
- if (align === "left" || align === "right" || align === "center") float["hAlign"] = align;
497
+ if (align === "left" || align === "right" || align === "center")
498
+ float["hAlign"] = align;
487
499
  const off = emuToPxZero(child(posH, "wp:posOffset")?.text);
488
- if (off !== void 0 && float["hAlign"] === void 0) float["hOffset"] = off;
500
+ if (off !== void 0 && float["hAlign"] === void 0)
501
+ float["hOffset"] = off;
489
502
  const rel = attrOf(posH, "relativeFrom");
490
503
  float["hRel"] = rel === "page" ? "page" : "margin";
491
504
  }
@@ -662,7 +675,9 @@ function parseShape(run, ctx) {
662
675
  function parseTextbox(wsp, ctx) {
663
676
  const content = child(child(wsp, "wps:txbx"), "w:txbxContent");
664
677
  if (!content) return null;
665
- const paragraphs = children(content, "w:p").map((p) => parseParagraph(p, ctx).toJSON());
678
+ const paragraphs = children(content, "w:p").map(
679
+ (p) => parseParagraph(p, ctx).toJSON()
680
+ );
666
681
  if (paragraphs.length === 0) return null;
667
682
  const bodyPr = child(wsp, "wps:bodyPr");
668
683
  const ins = (name) => emuToPxZero(attrOf(bodyPr, name));
@@ -757,7 +772,10 @@ function flattenOmml(node) {
757
772
  function parseParagraph(p, ctx) {
758
773
  const pPr = child(p, "w:pPr");
759
774
  const pStyleId = attrOf(child(pPr, "w:pStyle"), "w:val");
760
- const paraBase = mergeRunProps(ctx.styles.docDefaults, ctx.styles.resolveStyle(pStyleId));
775
+ const paraBase = mergeRunProps(
776
+ ctx.styles.docDefaults,
777
+ ctx.styles.resolveStyle(pStyleId)
778
+ );
761
779
  const pPrChain = [
762
780
  ctx.styles.docDefaultsPPr,
763
781
  ...ctx.styles.resolveStylePPr(pStyleId),
@@ -790,9 +808,12 @@ function parseParagraph(p, ctx) {
790
808
  } else if (fldType === "end") {
791
809
  const kind = fieldKind(field.instr);
792
810
  if (kind) {
793
- inline.push(pageFieldNode(kind, field.resultRuns[0], paraBase, ctx));
811
+ inline.push(
812
+ pageFieldNode(kind, field.resultRuns[0], paraBase, ctx)
813
+ );
794
814
  } else {
795
- for (const r of field.resultRuns) inline.push(...runToInline(r, paraBase, ctx, null));
815
+ for (const r of field.resultRuns)
816
+ inline.push(...runToInline(r, paraBase, ctx, null));
796
817
  }
797
818
  field = null;
798
819
  } else if (field.phase === "instr") {
@@ -809,7 +830,8 @@ function parseParagraph(p, ctx) {
809
830
  if (kind) {
810
831
  inline.push(pageFieldNode(kind, resultRuns[0], paraBase, ctx));
811
832
  } else {
812
- for (const r of resultRuns) inline.push(...runToInline(r, paraBase, ctx, null));
833
+ for (const r of resultRuns)
834
+ inline.push(...runToInline(r, paraBase, ctx, null));
813
835
  }
814
836
  } else if (node.name === "w:hyperlink") {
815
837
  const rel = attrOf(node, "r:id") ? ctx.rels.get(attrOf(node, "r:id")) : void 0;
@@ -822,7 +844,9 @@ function parseParagraph(p, ctx) {
822
844
  const text = flattenOmml(node);
823
845
  if (text.length > 0) {
824
846
  const first = findDescendant(node, "m:r");
825
- inline.push(ctx.schema.text(text, runMarks(first, paraBase, ctx, null)));
847
+ inline.push(
848
+ ctx.schema.text(text, runMarks(first, paraBase, ctx, null))
849
+ );
826
850
  }
827
851
  } else if (node.name === "w:commentRangeStart") {
828
852
  const id = Number(attrOf(node, "w:id"));
@@ -839,6 +863,9 @@ function parseParagraph(p, ctx) {
839
863
  if (list) attrs.list = list;
840
864
  if (align) attrs.align = align;
841
865
  if (heading) attrs.heading = heading;
866
+ else if (pStyleId && /^(title|subtitle)$/i.test(pStyleId)) {
867
+ attrs.styleId = pStyleId.toLowerCase() === "title" ? "Title" : "Subtitle";
868
+ }
842
869
  if (indent) attrs.indent = indent;
843
870
  if (spacing) attrs.spacing = spacing;
844
871
  if (tabs) attrs.tabs = tabs;
@@ -951,7 +978,9 @@ function parseList(pPr) {
951
978
  return { numId, level: Number.isNaN(ilvl) ? 0 : ilvl };
952
979
  }
953
980
  function emptyCell(ctx) {
954
- return ctx.schema.nodes["table_cell"].create(null, [ctx.schema.nodes["paragraph"].create()]);
981
+ return ctx.schema.nodes["table_cell"].create(null, [
982
+ ctx.schema.nodes["paragraph"].create()
983
+ ]);
955
984
  }
956
985
  function parseCellMargins(tbl) {
957
986
  const mar = child(child(tbl, "w:tblPr"), "w:tblCellMar");
@@ -1004,7 +1033,14 @@ function parseBordersEl(bordersEl, sides) {
1004
1033
  }
1005
1034
  return Object.keys(out).length > 0 ? out : null;
1006
1035
  }
1007
- var TABLE_SIDES = ["top", "bottom", "left", "right", "insideH", "insideV"];
1036
+ var TABLE_SIDES = [
1037
+ "top",
1038
+ "bottom",
1039
+ "left",
1040
+ "right",
1041
+ "insideH",
1042
+ "insideV"
1043
+ ];
1008
1044
  var CELL_SIDES = ["top", "bottom", "left", "right"];
1009
1045
  function parseTableBorders(tbl, ctx) {
1010
1046
  const tblPr = child(tbl, "w:tblPr");
@@ -1031,8 +1067,18 @@ function parseTable(tbl, ctx) {
1031
1067
  const vAlign = vAlignVal === "center" || vAlignVal === "bottom" ? vAlignVal : null;
1032
1068
  const borders2 = parseBordersEl(child(tcPr, "w:tcBorders"), CELL_SIDES);
1033
1069
  const content = parseBlocks(tc, ctx);
1034
- if (content.length === 0) content.push(ctx.schema.nodes["paragraph"].create());
1035
- cells.push({ startCol: col, colspan, vMerge, colwidth: widths.length ? widths : null, background, vAlign, borders: borders2, content });
1070
+ if (content.length === 0)
1071
+ content.push(ctx.schema.nodes["paragraph"].create());
1072
+ cells.push({
1073
+ startCol: col,
1074
+ colspan,
1075
+ vMerge,
1076
+ colwidth: widths.length ? widths : null,
1077
+ background,
1078
+ vAlign,
1079
+ borders: borders2,
1080
+ content
1081
+ });
1036
1082
  col += colspan;
1037
1083
  }
1038
1084
  return cells;
@@ -1043,12 +1089,17 @@ function parseTable(tbl, ctx) {
1043
1089
  const header = hdr ? attrOf(hdr, "w:val") !== "false" && attrOf(hdr, "w:val") !== "0" : false;
1044
1090
  const trH = child(trPr, "w:trHeight");
1045
1091
  const hv = attrOf(trH, "w:val");
1046
- const height = hv !== void 0 ? { value: twipsToPx(Number(hv)), exact: attrOf(trH, "w:hRule") === "exact" } : null;
1092
+ const height = hv !== void 0 ? {
1093
+ value: twipsToPx(Number(hv)),
1094
+ exact: attrOf(trH, "w:hRule") === "exact"
1095
+ } : null;
1047
1096
  const cs = child(trPr, "w:cantSplit");
1048
1097
  const cantSplit = cs ? attrOf(cs, "w:val") !== "false" && attrOf(cs, "w:val") !== "0" : false;
1049
1098
  return { header, height, cantSplit };
1050
1099
  });
1051
- const colIndex = logicalRows.map((cells) => new Map(cells.map((c) => [c.startCol, c])));
1100
+ const colIndex = logicalRows.map(
1101
+ (cells) => new Map(cells.map((c) => [c.startCol, c]))
1102
+ );
1052
1103
  const rows = logicalRows.map((cells, r) => {
1053
1104
  const emitted = [];
1054
1105
  for (const cell of cells) {
@@ -1089,7 +1140,8 @@ function parseTable(tbl, ctx) {
1089
1140
  const attrs = {};
1090
1141
  if (cellPadding) attrs["cellPadding"] = cellPadding;
1091
1142
  if (borders) attrs["borders"] = borders;
1092
- if (jc === "center" || jc === "right" || jc === "end") attrs["align"] = jc === "end" ? "right" : jc;
1143
+ if (jc === "center" || jc === "right" || jc === "end")
1144
+ attrs["align"] = jc === "end" ? "right" : jc;
1093
1145
  return ctx.schema.nodes["table"].create(
1094
1146
  Object.keys(attrs).length > 0 ? attrs : null,
1095
1147
  rows.length > 0 ? rows : [ctx.schema.nodes["table_row"].create(null, [emptyCell(ctx)])]
@@ -1155,18 +1207,27 @@ async function readPartRels(zip, partPath) {
1155
1207
  return xml ? parseXml(xml) : void 0;
1156
1208
  }
1157
1209
  async function buildNotesRegistry(zip) {
1158
- const bodies = { footnote: /* @__PURE__ */ new Map(), endnote: /* @__PURE__ */ new Map() };
1210
+ const bodies = {
1211
+ footnote: /* @__PURE__ */ new Map(),
1212
+ endnote: /* @__PURE__ */ new Map()
1213
+ };
1159
1214
  const load = async (path, root, tag, into) => {
1160
1215
  const xml = await readPart(zip, path);
1161
1216
  if (!xml) return;
1162
1217
  for (const note of children(child(parseXml(xml), root), tag)) {
1163
1218
  const id = attrOf(note, "w:id");
1164
1219
  const type = attrOf(note, "w:type");
1165
- if (id === void 0 || Number(id) < 1 || type && type !== "normal") continue;
1220
+ if (id === void 0 || Number(id) < 1 || type && type !== "normal")
1221
+ continue;
1166
1222
  into.set(id, note);
1167
1223
  }
1168
1224
  };
1169
- await load("word/footnotes.xml", "w:footnotes", "w:footnote", bodies.footnote);
1225
+ await load(
1226
+ "word/footnotes.xml",
1227
+ "w:footnotes",
1228
+ "w:footnote",
1229
+ bodies.footnote
1230
+ );
1170
1231
  await load("word/endnotes.xml", "w:endnotes", "w:endnote", bodies.endnote);
1171
1232
  const reg = {
1172
1233
  bodies,
@@ -1201,7 +1262,10 @@ async function buildCommentsRegistry(zip) {
1201
1262
  const ext = /* @__PURE__ */ new Map();
1202
1263
  const extXml = await readPart(zip, "word/commentsExtended.xml");
1203
1264
  if (extXml) {
1204
- for (const ex of children(child(parseXml(extXml), "w15:commentsEx"), "w15:commentEx")) {
1265
+ for (const ex of children(
1266
+ child(parseXml(extXml), "w15:commentsEx"),
1267
+ "w15:commentEx"
1268
+ )) {
1205
1269
  const paraId2 = attrOf(ex, "w15:paraId");
1206
1270
  if (!paraId2) continue;
1207
1271
  const done = attrOf(ex, "w15:done");
@@ -1221,14 +1285,24 @@ function buildCommentsList(ctx) {
1221
1285
  const out = [];
1222
1286
  for (const id of ctx.comments.used) {
1223
1287
  const def = ctx.comments.defs.get(id);
1224
- if (def) out.push({ id, author: def.author, date: def.date, text: collectText(def.body).trim() });
1288
+ if (def)
1289
+ out.push({
1290
+ id,
1291
+ author: def.author,
1292
+ date: def.date,
1293
+ text: collectText(def.body).trim()
1294
+ });
1225
1295
  }
1226
1296
  return out;
1227
1297
  }
1228
1298
  function commentBodyJSON(comment) {
1229
1299
  const paras = children(comment, "w:p").map((p) => {
1230
1300
  const text = collectText(p);
1231
- return import_bapbong_model.commentSchema.node("paragraph", null, text ? [import_bapbong_model.commentSchema.text(text)] : []);
1301
+ return import_bapbong_model.commentSchema.node(
1302
+ "paragraph",
1303
+ null,
1304
+ text ? [import_bapbong_model.commentSchema.text(text)] : []
1305
+ );
1232
1306
  });
1233
1307
  return import_bapbong_model.commentSchema.node("doc", null, paras.length ? paras : [import_bapbong_model.commentSchema.node("paragraph")]).toJSON();
1234
1308
  }
@@ -1248,12 +1322,18 @@ function buildCommentNodes(ctx) {
1248
1322
  if (usedSet.has(cur)) return true;
1249
1323
  return false;
1250
1324
  };
1251
- const ids = [...used.filter((id) => defs.has(id)), ...[...defs.keys()].filter((id) => !usedSet.has(id))];
1325
+ const ids = [
1326
+ ...used.filter((id) => defs.has(id)),
1327
+ ...[...defs.keys()].filter((id) => !usedSet.has(id))
1328
+ ];
1252
1329
  const out = [];
1253
1330
  for (const id of ids) {
1254
1331
  const def = defs.get(id);
1255
1332
  if (!def || !referenced(id)) continue;
1256
- const user = { id: def.author || "unknown", name: def.author || "Unknown" };
1333
+ const user = {
1334
+ id: def.author || "unknown",
1335
+ name: def.author || "Unknown"
1336
+ };
1257
1337
  out.push({
1258
1338
  id,
1259
1339
  parentId: parentOf.get(id) ?? null,
@@ -1267,7 +1347,9 @@ function buildCommentNodes(ctx) {
1267
1347
  }
1268
1348
  function noteBlocks(note, num, ctx) {
1269
1349
  const blocks = parseBlocks(note, ctx);
1270
- const marker = ctx.schema.text(`${num}. `, [ctx.schema.marks["vertAlign"].create({ value: "super" })]);
1350
+ const marker = ctx.schema.text(`${num}. `, [
1351
+ ctx.schema.marks["vertAlign"].create({ value: "super" })
1352
+ ]);
1271
1353
  const first = blocks[0];
1272
1354
  if (first && first.type.name === "paragraph") {
1273
1355
  const kids = [marker];
@@ -1317,7 +1399,10 @@ async function extractMedia(zip) {
1317
1399
  if (!path.startsWith("word/media/")) continue;
1318
1400
  const entry = zip.file(path);
1319
1401
  if (!entry || entry.dir) continue;
1320
- media.set(path, `data:${mimeOf(path)};base64,${await entry.async("base64")}`);
1402
+ media.set(
1403
+ path,
1404
+ `data:${mimeOf(path)};base64,${await entry.async("base64")}`
1405
+ );
1321
1406
  }
1322
1407
  return media;
1323
1408
  }
@@ -1327,11 +1412,16 @@ async function importDocx(input, opts) {
1327
1412
  if (rawDocumentXml === void 0) {
1328
1413
  throw new Error("bapbong-docx: word/document.xml not found in archive");
1329
1414
  }
1330
- const stylesXml = await readPart(zip, "word/styles.xml");
1415
+ const stylesXml2 = await readPart(zip, "word/styles.xml");
1331
1416
  const numberingXml = await readPart(zip, "word/numbering.xml");
1332
1417
  const themeXml = await readPart(zip, "word/theme/theme1.xml");
1333
- const resolveTheme = buildThemeResolver(themeXml ? parseXml(themeXml) : void 0);
1334
- const styles = buildStyleRegistry(stylesXml ? parseXml(stylesXml) : void 0, resolveTheme);
1418
+ const resolveTheme = buildThemeResolver(
1419
+ themeXml ? parseXml(themeXml) : void 0
1420
+ );
1421
+ const styles = buildStyleRegistry(
1422
+ stylesXml2 ? parseXml(stylesXml2) : void 0,
1423
+ resolveTheme
1424
+ );
1335
1425
  const numberingRoot = numberingXml ? parseXml(numberingXml) : void 0;
1336
1426
  const media = await extractMedia(zip);
1337
1427
  const notes = await buildNotesRegistry(zip);
@@ -1380,7 +1470,11 @@ async function importDocx(input, opts) {
1380
1470
  if (!xml) continue;
1381
1471
  const partCtx = makeCtx(buildRels(await readPartRels(zip, partPath)));
1382
1472
  const el = child(parseXml(xml), root);
1383
- store[type] = storyDoc(partCtx, el ? parseBlocks(el, partCtx) : [], ctx.numbering.defs);
1473
+ store[type] = storyDoc(
1474
+ partCtx,
1475
+ el ? parseBlocks(el, partCtx) : [],
1476
+ ctx.numbering.defs
1477
+ );
1384
1478
  }
1385
1479
  };
1386
1480
  await collect("w:headerReference", headers, "w:hdr");
@@ -1457,7 +1551,10 @@ var pxToEmu = (px) => Math.round(px * 9525);
1457
1551
  var commentIdsOf = (node) => node.marks.find((m) => m.type.name === "comment")?.attrs["ids"] ?? [];
1458
1552
  var isInlineLeaf = (node) => node.isText || node.type.name === "image" || node.type.name === "hard_break";
1459
1553
  function esc(s) {
1460
- return s.replace(/[&<>"]/g, (c) => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;" })[c]);
1554
+ return s.replace(
1555
+ /[&<>"]/g,
1556
+ (c) => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;" })[c]
1557
+ );
1461
1558
  }
1462
1559
  var MIME_EXT = {
1463
1560
  "image/png": "png",
@@ -1480,9 +1577,15 @@ function runProps(marks) {
1480
1577
  const size = byName.get("fontSize")?.attrs["size"];
1481
1578
  if (size != null) out.push(`<w:sz w:val="${Math.round(size * 2)}"/>`);
1482
1579
  const hl = byName.get("highlight")?.attrs["color"];
1483
- if (hl) out.push(`<w:shd w:val="clear" w:color="auto" w:fill="${hl.replace(/^#/, "")}"/>`);
1580
+ if (hl)
1581
+ out.push(
1582
+ `<w:shd w:val="clear" w:color="auto" w:fill="${hl.replace(/^#/, "")}"/>`
1583
+ );
1484
1584
  const va = byName.get("vertAlign")?.attrs["value"];
1485
- if (va) out.push(`<w:vertAlign w:val="${va === "sub" ? "subscript" : "superscript"}"/>`);
1585
+ if (va)
1586
+ out.push(
1587
+ `<w:vertAlign w:val="${va === "sub" ? "subscript" : "superscript"}"/>`
1588
+ );
1486
1589
  return out.length ? `<w:rPr>${out.join("")}</w:rPr>` : "";
1487
1590
  }
1488
1591
  function anchorXml(float, cx, cy, n, graphic) {
@@ -1524,7 +1627,9 @@ function imageXml(node, ctx) {
1524
1627
  const n = ctx.nextId++;
1525
1628
  const rid = `rId${n}`;
1526
1629
  ctx.media.push({ path: `word/media/image${n}.${ext}`, base64: m[2] });
1527
- ctx.rels.push(`<Relationship Id="${rid}" Type="${R_NS}/image" Target="media/image${n}.${ext}"/>`);
1630
+ ctx.rels.push(
1631
+ `<Relationship Id="${rid}" Type="${R_NS}/image" Target="media/image${n}.${ext}"/>`
1632
+ );
1528
1633
  const cx = pxToEmu(node.attrs["width"] ?? 96);
1529
1634
  const cy = pxToEmu(node.attrs["height"] ?? 96);
1530
1635
  return `<w:r><w:drawing><wp:inline distT="0" distB="0" distL="0" distR="0"><wp:extent cx="${cx}" cy="${cy}"/><wp:docPr id="${n}" name="Picture ${n}"/><a:graphic><a:graphicData uri="${PIC_NS}"><pic:pic><pic:nvPicPr><pic:cNvPr id="${n}" name="image${n}.${ext}"/><pic:cNvPicPr/></pic:nvPicPr><pic:blipFill><a:blip r:embed="${rid}"/><a:stretch><a:fillRect/></a:stretch></pic:blipFill><pic:spPr><a:xfrm${rotAttr(node)}><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>`;
@@ -1534,7 +1639,8 @@ function inlineXml(node, ctx) {
1534
1639
  if (node.type.name === "image") return imageXml(node, ctx);
1535
1640
  if (node.isText) {
1536
1641
  const fn = node.marks.find((m) => m.type.name === "footnote");
1537
- if (fn) return `<w:r>${runProps(node.marks)}<w:footnoteReference w:id="${fn.attrs["num"]}"/></w:r>`;
1642
+ if (fn)
1643
+ return `<w:r>${runProps(node.marks)}<w:footnoteReference w:id="${fn.attrs["num"]}"/></w:r>`;
1538
1644
  return `<w:r>${runProps(node.marks)}<w:t xml:space="preserve">${esc(node.text ?? "")}</w:t></w:r>`;
1539
1645
  }
1540
1646
  return "";
@@ -1544,9 +1650,12 @@ function inlineUnit(node, ctx) {
1544
1650
  const inner = inlineXml(node, ctx);
1545
1651
  const href = linkHref(node);
1546
1652
  if (!href || !inner) return inner;
1547
- if (href.startsWith("#")) return `<w:hyperlink w:anchor="${esc(href.slice(1))}">${inner}</w:hyperlink>`;
1653
+ if (href.startsWith("#"))
1654
+ return `<w:hyperlink w:anchor="${esc(href.slice(1))}">${inner}</w:hyperlink>`;
1548
1655
  const n = ctx.nextId++;
1549
- ctx.rels.push(`<Relationship Id="rId${n}" Type="${R_NS}/hyperlink" Target="${esc(href)}" TargetMode="External"/>`);
1656
+ ctx.rels.push(
1657
+ `<Relationship Id="rId${n}" Type="${R_NS}/hyperlink" Target="${esc(href)}" TargetMode="External"/>`
1658
+ );
1550
1659
  return `<w:hyperlink r:id="rId${n}">${inner}</w:hyperlink>`;
1551
1660
  }
1552
1661
  function inlineContent(node, ctx) {
@@ -1580,10 +1689,16 @@ function paraProps(node) {
1580
1689
  const a = node.attrs;
1581
1690
  const out = [];
1582
1691
  const heading = a["heading"];
1692
+ const styleId = a["styleId"];
1583
1693
  if (heading) out.push(`<w:pStyle w:val="Heading${heading}"/>`);
1694
+ else if (styleId === "Title" || styleId === "Subtitle")
1695
+ out.push(`<w:pStyle w:val="${styleId}"/>`);
1584
1696
  if (a["pageBreakBefore"]) out.push("<w:pageBreakBefore/>");
1585
1697
  const list = a["list"];
1586
- if (list) out.push(`<w:numPr><w:ilvl w:val="${list.level}"/><w:numId w:val="${esc(list.numId)}"/></w:numPr>`);
1698
+ if (list)
1699
+ out.push(
1700
+ `<w:numPr><w:ilvl w:val="${list.level}"/><w:numId w:val="${esc(list.numId)}"/></w:numPr>`
1701
+ );
1587
1702
  const sp = a["spacing"];
1588
1703
  if (sp) {
1589
1704
  const at = [];
@@ -1591,7 +1706,9 @@ function paraProps(node) {
1591
1706
  if (sp.after != null) at.push(`w:after="${pxToTwips(sp.after)}"`);
1592
1707
  if (sp.line != null) {
1593
1708
  const auto = sp.lineRule === "auto" || sp.lineRule == null;
1594
- at.push(`w:line="${auto ? Math.round(sp.line * 240) : pxToTwips(sp.line)}"`);
1709
+ at.push(
1710
+ `w:line="${auto ? Math.round(sp.line * 240) : pxToTwips(sp.line)}"`
1711
+ );
1595
1712
  at.push(`w:lineRule="${sp.lineRule ?? "auto"}"`);
1596
1713
  }
1597
1714
  if (at.length) out.push(`<w:spacing ${at.join(" ")}/>`);
@@ -1602,11 +1719,13 @@ function paraProps(node) {
1602
1719
  if (ind.left != null) at.push(`w:left="${pxToTwips(ind.left)}"`);
1603
1720
  if (ind.right != null) at.push(`w:right="${pxToTwips(ind.right)}"`);
1604
1721
  if (ind.hanging != null) at.push(`w:hanging="${pxToTwips(ind.hanging)}"`);
1605
- else if (ind.firstLine != null) at.push(`w:firstLine="${pxToTwips(ind.firstLine)}"`);
1722
+ else if (ind.firstLine != null)
1723
+ at.push(`w:firstLine="${pxToTwips(ind.firstLine)}"`);
1606
1724
  if (at.length) out.push(`<w:ind ${at.join(" ")}/>`);
1607
1725
  }
1608
1726
  const align = a["align"];
1609
- if (align) out.push(`<w:jc w:val="${align === "justify" ? "both" : align}"/>`);
1727
+ if (align)
1728
+ out.push(`<w:jc w:val="${align === "justify" ? "both" : align}"/>`);
1610
1729
  return out.join("");
1611
1730
  }
1612
1731
  function paragraphXml(node, ctx, sectPr = "") {
@@ -1614,7 +1733,14 @@ function paragraphXml(node, ctx, sectPr = "") {
1614
1733
  const pPr = props ? `<w:pPr>${props}</w:pPr>` : "";
1615
1734
  return `<w:p>${pPr}${inlineContent(node, ctx)}</w:p>`;
1616
1735
  }
1617
- var TABLE_SIDES2 = ["top", "bottom", "left", "right", "insideH", "insideV"];
1736
+ var TABLE_SIDES2 = [
1737
+ "top",
1738
+ "bottom",
1739
+ "left",
1740
+ "right",
1741
+ "insideH",
1742
+ "insideV"
1743
+ ];
1618
1744
  var CELL_SIDES2 = ["top", "bottom", "left", "right"];
1619
1745
  var BORDER_STYLE_OUT = {
1620
1746
  solid: "single",
@@ -1636,12 +1762,19 @@ function cellXml(cell, ctx) {
1636
1762
  const a = cell.attrs;
1637
1763
  const pr = [];
1638
1764
  const colwidth = a["colwidth"];
1639
- if (colwidth?.length) pr.push(`<w:tcW w:w="${pxToTwips(colwidth.reduce((x, y) => x + y, 0))}" w:type="dxa"/>`);
1640
- if (a["colspan"] > 1) pr.push(`<w:gridSpan w:val="${a["colspan"]}"/>`);
1765
+ if (colwidth?.length)
1766
+ pr.push(
1767
+ `<w:tcW w:w="${pxToTwips(colwidth.reduce((x, y) => x + y, 0))}" w:type="dxa"/>`
1768
+ );
1769
+ if (a["colspan"] > 1)
1770
+ pr.push(`<w:gridSpan w:val="${a["colspan"]}"/>`);
1641
1771
  const borders = a["borders"];
1642
1772
  if (borders) pr.push(bordersXml("w:tcBorders", borders, CELL_SIDES2));
1643
1773
  const bg = a["background"];
1644
- if (bg) pr.push(`<w:shd w:val="clear" w:color="auto" w:fill="${bg.replace(/^#/, "")}"/>`);
1774
+ if (bg)
1775
+ pr.push(
1776
+ `<w:shd w:val="clear" w:color="auto" w:fill="${bg.replace(/^#/, "")}"/>`
1777
+ );
1645
1778
  const vAlign = a["vAlign"];
1646
1779
  if (vAlign) pr.push(`<w:vAlign w:val="${vAlign}"/>`);
1647
1780
  let content = "";
@@ -1654,7 +1787,10 @@ function rowXml(row, ctx) {
1654
1787
  if (row.attrs["header"]) pr.push("<w:tblHeader/>");
1655
1788
  if (row.attrs["cantSplit"]) pr.push("<w:cantSplit/>");
1656
1789
  const h = row.attrs["height"];
1657
- if (h) pr.push(`<w:trHeight w:val="${pxToTwips(h.value)}" w:hRule="${h.exact ? "exact" : "atLeast"}"/>`);
1790
+ if (h)
1791
+ pr.push(
1792
+ `<w:trHeight w:val="${pxToTwips(h.value)}" w:hRule="${h.exact ? "exact" : "atLeast"}"/>`
1793
+ );
1658
1794
  const trPr = pr.length ? `<w:trPr>${pr.join("")}</w:trPr>` : "";
1659
1795
  let cells = "";
1660
1796
  row.forEach((c) => cells += cellXml(c, ctx));
@@ -1673,7 +1809,9 @@ function tableXml(node, ctx) {
1673
1809
  }
1674
1810
  const firstRow = node.firstChild;
1675
1811
  const grid = [];
1676
- firstRow?.forEach((c) => c.attrs["colwidth"]?.forEach((w) => grid.push(w)));
1812
+ firstRow?.forEach(
1813
+ (c) => c.attrs["colwidth"]?.forEach((w) => grid.push(w))
1814
+ );
1677
1815
  const gridXml = grid.length ? `<w:tblGrid>${grid.map((w) => `<w:gridCol w:w="${pxToTwips(w)}"/>`).join("")}</w:tblGrid>` : "";
1678
1816
  let rows = "";
1679
1817
  node.forEach((r) => rows += rowXml(r, ctx));
@@ -1708,10 +1846,14 @@ function commentBodyXml(body, firstParaId) {
1708
1846
  doc.forEach((p, _o, i) => {
1709
1847
  let runs = "";
1710
1848
  p.forEach((inline) => {
1711
- if (inline.isText) runs += `<w:r><w:t xml:space="preserve">${esc(inline.text ?? "")}</w:t></w:r>`;
1712
- else if (inline.type.name === "mention") runs += `<w:r><w:t xml:space="preserve">@${esc(String(inline.attrs["label"] ?? ""))}</w:t></w:r>`;
1849
+ if (inline.isText)
1850
+ runs += `<w:r><w:t xml:space="preserve">${esc(inline.text ?? "")}</w:t></w:r>`;
1851
+ else if (inline.type.name === "mention")
1852
+ runs += `<w:r><w:t xml:space="preserve">@${esc(String(inline.attrs["label"] ?? ""))}</w:t></w:r>`;
1713
1853
  });
1714
- ps.push(`<w:p${i === 0 ? ` w14:paraId="${firstParaId}"` : ""}>${runs}</w:p>`);
1854
+ ps.push(
1855
+ `<w:p${i === 0 ? ` w14:paraId="${firstParaId}"` : ""}>${runs}</w:p>`
1856
+ );
1715
1857
  });
1716
1858
  return ps.join("") || `<w:p w14:paraId="${firstParaId}"/>`;
1717
1859
  }
@@ -1733,16 +1875,65 @@ function commentsExtendedXml(comments) {
1733
1875
  }
1734
1876
  var ROOT_RELS = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
1735
1877
  <Relationships xmlns="${PR_NS}"><Relationship Id="rId1" Type="${R_NS}/officeDocument" Target="word/document.xml"/></Relationships>`;
1878
+ var STYLE_DEFS = (() => {
1879
+ const heading = (level, halfPt) => `<w:style w:type="paragraph" w:styleId="Heading${level}"><w:name w:val="heading ${level}"/><w:basedOn w:val="Normal"/><w:next w:val="Normal"/><w:qFormat/><w:pPr><w:keepNext/><w:spacing w:before="240" w:after="120"/><w:outlineLvl w:val="${level - 1}"/></w:pPr><w:rPr><w:b/><w:sz w:val="${halfPt}"/><w:szCs w:val="${halfPt}"/></w:rPr></w:style>`;
1880
+ return {
1881
+ Heading1: heading(1, 48),
1882
+ Heading2: heading(2, 36),
1883
+ Heading3: heading(3, 28),
1884
+ Heading4: heading(4, 24),
1885
+ Heading5: heading(5, 22),
1886
+ Heading6: heading(6, 22),
1887
+ Title: `<w:style w:type="paragraph" w:styleId="Title"><w:name w:val="Title"/><w:basedOn w:val="Normal"/><w:next w:val="Normal"/><w:qFormat/><w:pPr><w:spacing w:after="80"/></w:pPr><w:rPr><w:sz w:val="56"/><w:szCs w:val="56"/></w:rPr></w:style>`,
1888
+ Subtitle: `<w:style w:type="paragraph" w:styleId="Subtitle"><w:name w:val="Subtitle"/><w:basedOn w:val="Normal"/><w:next w:val="Normal"/><w:qFormat/><w:rPr><w:i/><w:sz w:val="28"/><w:szCs w:val="28"/></w:rPr></w:style>`
1889
+ };
1890
+ })();
1891
+ function usedStyleIds(doc) {
1892
+ const used = /* @__PURE__ */ new Set();
1893
+ doc.descendants((n) => {
1894
+ if (n.type.name !== "paragraph") return;
1895
+ const heading = n.attrs["heading"];
1896
+ const styleId = n.attrs["styleId"];
1897
+ if (heading) used.add(`Heading${heading}`);
1898
+ else if (styleId && STYLE_DEFS[styleId]) used.add(styleId);
1899
+ });
1900
+ return used;
1901
+ }
1902
+ function stylesXml(used) {
1903
+ const defs = [...used].map((id) => STYLE_DEFS[id]).join("");
1904
+ return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
1905
+ <w:styles xmlns:w="${W_NS}"><w:docDefaults><w:rPrDefault><w:rPr><w:sz w:val="22"/><w:szCs w:val="22"/></w:rPr></w:rPrDefault><w:pPrDefault/></w:docDefaults><w:style w:type="paragraph" w:default="1" w:styleId="Normal"><w:name w:val="Normal"/><w:qFormat/></w:style>` + defs + `</w:styles>`;
1906
+ }
1907
+ function mergeStyles(xml, used) {
1908
+ const missing = [...used].filter((id) => !xml.includes(`w:styleId="${id}"`));
1909
+ if (!missing.length) return xml;
1910
+ return xml.replace(
1911
+ "</w:styles>",
1912
+ `${missing.map((id) => STYLE_DEFS[id]).join("")}</w:styles>`
1913
+ );
1914
+ }
1736
1915
  function contentTypes(exts, hasComments) {
1737
1916
  const parts = [
1738
1917
  '<Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>',
1739
1918
  '<Default Extension="xml" ContentType="application/xml"/>'
1740
1919
  ];
1741
- for (const ext of exts) parts.push(`<Default Extension="${ext}" ContentType="image/${ext === "jpg" ? "jpeg" : ext}"/>`);
1742
- parts.push('<Override PartName="/word/document.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml"/>');
1920
+ for (const ext of exts)
1921
+ parts.push(
1922
+ `<Default Extension="${ext}" ContentType="image/${ext === "jpg" ? "jpeg" : ext}"/>`
1923
+ );
1924
+ parts.push(
1925
+ '<Override PartName="/word/document.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml"/>'
1926
+ );
1927
+ parts.push(
1928
+ '<Override PartName="/word/styles.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml"/>'
1929
+ );
1743
1930
  if (hasComments) {
1744
- parts.push('<Override PartName="/word/comments.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.comments+xml"/>');
1745
- parts.push('<Override PartName="/word/commentsExtended.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.commentsExtended+xml"/>');
1931
+ parts.push(
1932
+ '<Override PartName="/word/comments.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.comments+xml"/>'
1933
+ );
1934
+ parts.push(
1935
+ '<Override PartName="/word/commentsExtended.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.commentsExtended+xml"/>'
1936
+ );
1746
1937
  }
1747
1938
  return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
1748
1939
  <Types xmlns="${CT_NS}">${parts.join("")}</Types>`;
@@ -1750,17 +1941,30 @@ function contentTypes(exts, hasComments) {
1750
1941
  function mergeContentTypes(xml, exts, hasComments) {
1751
1942
  let out = xml;
1752
1943
  const add = (frag, key) => {
1753
- if (!out.includes(`"${key}"`)) out = out.replace("</Types>", `${frag}</Types>`);
1944
+ if (!out.includes(`"${key}"`))
1945
+ out = out.replace("</Types>", `${frag}</Types>`);
1754
1946
  };
1755
- for (const ext of exts) add(`<Default Extension="${ext}" ContentType="image/${ext === "jpg" ? "jpeg" : ext}"/>`, ext);
1947
+ for (const ext of exts)
1948
+ add(
1949
+ `<Default Extension="${ext}" ContentType="image/${ext === "jpg" ? "jpeg" : ext}"/>`,
1950
+ ext
1951
+ );
1756
1952
  if (hasComments) {
1757
- add('<Override PartName="/word/comments.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.comments+xml"/>', "/word/comments.xml");
1758
- add('<Override PartName="/word/commentsExtended.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.commentsExtended+xml"/>', "/word/commentsExtended.xml");
1953
+ add(
1954
+ '<Override PartName="/word/comments.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.comments+xml"/>',
1955
+ "/word/comments.xml"
1956
+ );
1957
+ add(
1958
+ '<Override PartName="/word/commentsExtended.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.commentsExtended+xml"/>',
1959
+ "/word/commentsExtended.xml"
1960
+ );
1759
1961
  }
1760
1962
  return out;
1761
1963
  }
1762
1964
  function extractBodySectPr(xml) {
1763
- const all = xml.match(/<w:sectPr\b[^>]*>[\s\S]*?<\/w:sectPr>|<w:sectPr\b[^>]*\/>/g);
1965
+ const all = xml.match(
1966
+ /<w:sectPr\b[^>]*>[\s\S]*?<\/w:sectPr>|<w:sectPr\b[^>]*\/>/g
1967
+ );
1764
1968
  return all ? all[all.length - 1] : "";
1765
1969
  }
1766
1970
  function mergeRels(xml, newRels) {
@@ -1769,7 +1973,10 @@ function mergeRels(xml, newRels) {
1769
1973
  /<Relationship\b[^>]*Target="comments(?:Extended)?\.xml"[^>]*\/>/g,
1770
1974
  ""
1771
1975
  );
1772
- return base.replace("</Relationships>", `${newRels.join("")}</Relationships>`);
1976
+ return base.replace(
1977
+ "</Relationships>",
1978
+ `${newRels.join("")}</Relationships>`
1979
+ );
1773
1980
  }
1774
1981
  async function exportDocx(doc, opts) {
1775
1982
  const comments = doc.attrs["comments"] ?? [];
@@ -1778,7 +1985,8 @@ async function exportDocx(doc, opts) {
1778
1985
  let idx = 0;
1779
1986
  doc.descendants((n) => {
1780
1987
  if (!isInlineLeaf(n)) return;
1781
- for (const id of commentIdsOf(n)) if (knownComments.has(id)) lastRun.set(id, idx);
1988
+ for (const id of commentIdsOf(n))
1989
+ if (knownComments.has(id)) lastRun.set(id, idx);
1782
1990
  idx++;
1783
1991
  });
1784
1992
  const ctx = {
@@ -1793,13 +2001,20 @@ async function exportDocx(doc, opts) {
1793
2001
  };
1794
2002
  const boundaries = sectionBoundaries(doc);
1795
2003
  let body = "";
1796
- doc.forEach((block, _offset, i) => body += blockXml(block, ctx, boundaries.get(i)));
2004
+ doc.forEach(
2005
+ (block, _offset, i) => body += blockXml(block, ctx, boundaries.get(i))
2006
+ );
1797
2007
  const hasComments = comments.length > 0;
1798
2008
  if (hasComments) {
1799
- ctx.rels.push(`<Relationship Id="rIdComments" Type="${R_NS}/comments" Target="comments.xml"/>`);
1800
- ctx.rels.push(`<Relationship Id="rIdCommentsExt" Type="${R_NS}/commentsExtended" Target="commentsExtended.xml"/>`);
2009
+ ctx.rels.push(
2010
+ `<Relationship Id="rIdComments" Type="${R_NS}/comments" Target="comments.xml"/>`
2011
+ );
2012
+ ctx.rels.push(
2013
+ `<Relationship Id="rIdCommentsExt" Type="${R_NS}/commentsExtended" Target="commentsExtended.xml"/>`
2014
+ );
1801
2015
  }
1802
2016
  const zip = new import_jszip2.default();
2017
+ const styleIds = usedStyleIds(doc);
1803
2018
  let sectPr = "";
1804
2019
  if (opts?.carry) {
1805
2020
  const carry = opts.carry;
@@ -1807,18 +2022,29 @@ async function exportDocx(doc, opts) {
1807
2022
  if (!f.dir) zip.file(path, await f.async("uint8array"));
1808
2023
  }
1809
2024
  const ct = await carry.file("[Content_Types].xml")?.async("string");
1810
- zip.file("[Content_Types].xml", ct ? mergeContentTypes(ct, ctx.exts, hasComments) : contentTypes(ctx.exts, hasComments));
2025
+ zip.file(
2026
+ "[Content_Types].xml",
2027
+ ct ? mergeContentTypes(ct, ctx.exts, hasComments) : contentTypes(ctx.exts, hasComments)
2028
+ );
2029
+ const carriedStyles = await carry.file("word/styles.xml")?.async("string");
2030
+ if (carriedStyles)
2031
+ zip.file("word/styles.xml", mergeStyles(carriedStyles, styleIds));
1811
2032
  const rels = await carry.file("word/_rels/document.xml.rels")?.async("string");
1812
2033
  zip.file("word/_rels/document.xml.rels", mergeRels(rels, ctx.rels));
1813
2034
  const origDoc = await carry.file("word/document.xml")?.async("string");
1814
2035
  if (origDoc) sectPr = extractBodySectPr(origDoc);
1815
2036
  } else {
2037
+ ctx.rels.push(
2038
+ `<Relationship Id="rIdStyles" Type="${R_NS}/styles" Target="styles.xml"/>`
2039
+ );
2040
+ zip.file("word/styles.xml", stylesXml(styleIds));
1816
2041
  zip.file("[Content_Types].xml", contentTypes(ctx.exts, hasComments));
1817
2042
  zip.file("_rels/.rels", ROOT_RELS);
1818
- if (ctx.rels.length) {
1819
- zip.file("word/_rels/document.xml.rels", `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
1820
- <Relationships xmlns="${PR_NS}">${ctx.rels.join("")}</Relationships>`);
1821
- }
2043
+ zip.file(
2044
+ "word/_rels/document.xml.rels",
2045
+ `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
2046
+ <Relationships xmlns="${PR_NS}">${ctx.rels.join("")}</Relationships>`
2047
+ );
1822
2048
  }
1823
2049
  const documentXml = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
1824
2050
  <w:document xmlns:w="${W_NS}" xmlns:r="${R_NS}" xmlns:wp="${WP_NS}" xmlns:a="${A_NS}" xmlns:pic="${PIC_NS}" xmlns:wps="${WPS_NS}"><w:body>${body}${sectPr}</w:body></w:document>`;
@@ -1827,7 +2053,8 @@ async function exportDocx(doc, opts) {
1827
2053
  zip.file("word/comments.xml", commentsXml(comments));
1828
2054
  zip.file("word/commentsExtended.xml", commentsExtendedXml(comments));
1829
2055
  }
1830
- for (const { path, base64 } of ctx.media) zip.file(path, base64, { base64: true });
2056
+ for (const { path, base64 } of ctx.media)
2057
+ zip.file(path, base64, { base64: true });
1831
2058
  return zip.generateAsync({ type: "uint8array" });
1832
2059
  }
1833
2060
  // Annotate the CommonJS export names for ESM import in node: