@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.js CHANGED
@@ -351,11 +351,16 @@ function propsToMarks(p, ctx) {
351
351
  if (p.italic) marks.push(ctx.schema.marks["em"].create());
352
352
  if (p.underline) marks.push(ctx.schema.marks["underline"].create());
353
353
  if (p.strike) marks.push(ctx.schema.marks["strike"].create());
354
- if (p.color) marks.push(ctx.schema.marks["textColor"].create({ color: p.color }));
355
- if (p.sizePt !== void 0) marks.push(ctx.schema.marks["fontSize"].create({ size: p.sizePt }));
356
- if (p.fontFamily) marks.push(ctx.schema.marks["fontFamily"].create({ family: p.fontFamily }));
357
- if (p.highlight) marks.push(ctx.schema.marks["highlight"].create({ color: p.highlight }));
358
- if (p.vertAlign) marks.push(ctx.schema.marks["vertAlign"].create({ value: p.vertAlign }));
354
+ if (p.color)
355
+ marks.push(ctx.schema.marks["textColor"].create({ color: p.color }));
356
+ if (p.sizePt !== void 0)
357
+ marks.push(ctx.schema.marks["fontSize"].create({ size: p.sizePt }));
358
+ if (p.fontFamily)
359
+ marks.push(ctx.schema.marks["fontFamily"].create({ family: p.fontFamily }));
360
+ if (p.highlight)
361
+ marks.push(ctx.schema.marks["highlight"].create({ color: p.highlight }));
362
+ if (p.vertAlign)
363
+ marks.push(ctx.schema.marks["vertAlign"].create({ value: p.vertAlign }));
359
364
  return marks;
360
365
  }
361
366
  var SYMBOL_MAP = {
@@ -375,7 +380,9 @@ function symbolChar(code) {
375
380
  return Number.isNaN(n) ? "" : String.fromCodePoint(n >= 61440 ? n - 61440 + 32 : n);
376
381
  }
377
382
  function hasPageBreak(run) {
378
- return run.children.some((n) => n.name === "w:br" && attrOf(n, "w:type") === "page");
383
+ return run.children.some(
384
+ (n) => n.name === "w:br" && attrOf(n, "w:type") === "page"
385
+ );
379
386
  }
380
387
  function effectiveChildren(nodes) {
381
388
  const out = [];
@@ -424,8 +431,12 @@ function runInlineNodes(run, marks, ctx) {
424
431
  if (id && ctx.notes.bodies[kind].has(id)) {
425
432
  flush();
426
433
  const num = ctx.notes.ref(kind, id);
427
- const refMarks = [...marks, ctx.schema.marks["vertAlign"].create({ value: "super" })];
428
- if (kind === "footnote") refMarks.push(ctx.schema.marks["footnote"].create({ num }));
434
+ const refMarks = [
435
+ ...marks,
436
+ ctx.schema.marks["vertAlign"].create({ value: "super" })
437
+ ];
438
+ if (kind === "footnote")
439
+ refMarks.push(ctx.schema.marks["footnote"].create({ num }));
429
440
  out.push(ctx.schema.text(String(num), refMarks));
430
441
  }
431
442
  } else if (node.name === "w:br" && attrOf(node, "w:type") !== "page") {
@@ -449,9 +460,11 @@ function parseAnchorFloat(drawing) {
449
460
  const posH = child(anchor, "wp:positionH");
450
461
  if (posH) {
451
462
  const align = child(posH, "wp:align")?.text.trim();
452
- if (align === "left" || align === "right" || align === "center") float["hAlign"] = align;
463
+ if (align === "left" || align === "right" || align === "center")
464
+ float["hAlign"] = align;
453
465
  const off = emuToPxZero(child(posH, "wp:posOffset")?.text);
454
- if (off !== void 0 && float["hAlign"] === void 0) float["hOffset"] = off;
466
+ if (off !== void 0 && float["hAlign"] === void 0)
467
+ float["hOffset"] = off;
455
468
  const rel = attrOf(posH, "relativeFrom");
456
469
  float["hRel"] = rel === "page" ? "page" : "margin";
457
470
  }
@@ -628,7 +641,9 @@ function parseShape(run, ctx) {
628
641
  function parseTextbox(wsp, ctx) {
629
642
  const content = child(child(wsp, "wps:txbx"), "w:txbxContent");
630
643
  if (!content) return null;
631
- const paragraphs = children(content, "w:p").map((p) => parseParagraph(p, ctx).toJSON());
644
+ const paragraphs = children(content, "w:p").map(
645
+ (p) => parseParagraph(p, ctx).toJSON()
646
+ );
632
647
  if (paragraphs.length === 0) return null;
633
648
  const bodyPr = child(wsp, "wps:bodyPr");
634
649
  const ins = (name) => emuToPxZero(attrOf(bodyPr, name));
@@ -723,7 +738,10 @@ function flattenOmml(node) {
723
738
  function parseParagraph(p, ctx) {
724
739
  const pPr = child(p, "w:pPr");
725
740
  const pStyleId = attrOf(child(pPr, "w:pStyle"), "w:val");
726
- const paraBase = mergeRunProps(ctx.styles.docDefaults, ctx.styles.resolveStyle(pStyleId));
741
+ const paraBase = mergeRunProps(
742
+ ctx.styles.docDefaults,
743
+ ctx.styles.resolveStyle(pStyleId)
744
+ );
727
745
  const pPrChain = [
728
746
  ctx.styles.docDefaultsPPr,
729
747
  ...ctx.styles.resolveStylePPr(pStyleId),
@@ -756,9 +774,12 @@ function parseParagraph(p, ctx) {
756
774
  } else if (fldType === "end") {
757
775
  const kind = fieldKind(field.instr);
758
776
  if (kind) {
759
- inline.push(pageFieldNode(kind, field.resultRuns[0], paraBase, ctx));
777
+ inline.push(
778
+ pageFieldNode(kind, field.resultRuns[0], paraBase, ctx)
779
+ );
760
780
  } else {
761
- for (const r of field.resultRuns) inline.push(...runToInline(r, paraBase, ctx, null));
781
+ for (const r of field.resultRuns)
782
+ inline.push(...runToInline(r, paraBase, ctx, null));
762
783
  }
763
784
  field = null;
764
785
  } else if (field.phase === "instr") {
@@ -775,7 +796,8 @@ function parseParagraph(p, ctx) {
775
796
  if (kind) {
776
797
  inline.push(pageFieldNode(kind, resultRuns[0], paraBase, ctx));
777
798
  } else {
778
- for (const r of resultRuns) inline.push(...runToInline(r, paraBase, ctx, null));
799
+ for (const r of resultRuns)
800
+ inline.push(...runToInline(r, paraBase, ctx, null));
779
801
  }
780
802
  } else if (node.name === "w:hyperlink") {
781
803
  const rel = attrOf(node, "r:id") ? ctx.rels.get(attrOf(node, "r:id")) : void 0;
@@ -788,7 +810,9 @@ function parseParagraph(p, ctx) {
788
810
  const text = flattenOmml(node);
789
811
  if (text.length > 0) {
790
812
  const first = findDescendant(node, "m:r");
791
- inline.push(ctx.schema.text(text, runMarks(first, paraBase, ctx, null)));
813
+ inline.push(
814
+ ctx.schema.text(text, runMarks(first, paraBase, ctx, null))
815
+ );
792
816
  }
793
817
  } else if (node.name === "w:commentRangeStart") {
794
818
  const id = Number(attrOf(node, "w:id"));
@@ -805,6 +829,9 @@ function parseParagraph(p, ctx) {
805
829
  if (list) attrs.list = list;
806
830
  if (align) attrs.align = align;
807
831
  if (heading) attrs.heading = heading;
832
+ else if (pStyleId && /^(title|subtitle)$/i.test(pStyleId)) {
833
+ attrs.styleId = pStyleId.toLowerCase() === "title" ? "Title" : "Subtitle";
834
+ }
808
835
  if (indent) attrs.indent = indent;
809
836
  if (spacing) attrs.spacing = spacing;
810
837
  if (tabs) attrs.tabs = tabs;
@@ -917,7 +944,9 @@ function parseList(pPr) {
917
944
  return { numId, level: Number.isNaN(ilvl) ? 0 : ilvl };
918
945
  }
919
946
  function emptyCell(ctx) {
920
- return ctx.schema.nodes["table_cell"].create(null, [ctx.schema.nodes["paragraph"].create()]);
947
+ return ctx.schema.nodes["table_cell"].create(null, [
948
+ ctx.schema.nodes["paragraph"].create()
949
+ ]);
921
950
  }
922
951
  function parseCellMargins(tbl) {
923
952
  const mar = child(child(tbl, "w:tblPr"), "w:tblCellMar");
@@ -970,7 +999,14 @@ function parseBordersEl(bordersEl, sides) {
970
999
  }
971
1000
  return Object.keys(out).length > 0 ? out : null;
972
1001
  }
973
- var TABLE_SIDES = ["top", "bottom", "left", "right", "insideH", "insideV"];
1002
+ var TABLE_SIDES = [
1003
+ "top",
1004
+ "bottom",
1005
+ "left",
1006
+ "right",
1007
+ "insideH",
1008
+ "insideV"
1009
+ ];
974
1010
  var CELL_SIDES = ["top", "bottom", "left", "right"];
975
1011
  function parseTableBorders(tbl, ctx) {
976
1012
  const tblPr = child(tbl, "w:tblPr");
@@ -997,8 +1033,18 @@ function parseTable(tbl, ctx) {
997
1033
  const vAlign = vAlignVal === "center" || vAlignVal === "bottom" ? vAlignVal : null;
998
1034
  const borders2 = parseBordersEl(child(tcPr, "w:tcBorders"), CELL_SIDES);
999
1035
  const content = parseBlocks(tc, ctx);
1000
- if (content.length === 0) content.push(ctx.schema.nodes["paragraph"].create());
1001
- cells.push({ startCol: col, colspan, vMerge, colwidth: widths.length ? widths : null, background, vAlign, borders: borders2, content });
1036
+ if (content.length === 0)
1037
+ content.push(ctx.schema.nodes["paragraph"].create());
1038
+ cells.push({
1039
+ startCol: col,
1040
+ colspan,
1041
+ vMerge,
1042
+ colwidth: widths.length ? widths : null,
1043
+ background,
1044
+ vAlign,
1045
+ borders: borders2,
1046
+ content
1047
+ });
1002
1048
  col += colspan;
1003
1049
  }
1004
1050
  return cells;
@@ -1009,12 +1055,17 @@ function parseTable(tbl, ctx) {
1009
1055
  const header = hdr ? attrOf(hdr, "w:val") !== "false" && attrOf(hdr, "w:val") !== "0" : false;
1010
1056
  const trH = child(trPr, "w:trHeight");
1011
1057
  const hv = attrOf(trH, "w:val");
1012
- const height = hv !== void 0 ? { value: twipsToPx(Number(hv)), exact: attrOf(trH, "w:hRule") === "exact" } : null;
1058
+ const height = hv !== void 0 ? {
1059
+ value: twipsToPx(Number(hv)),
1060
+ exact: attrOf(trH, "w:hRule") === "exact"
1061
+ } : null;
1013
1062
  const cs = child(trPr, "w:cantSplit");
1014
1063
  const cantSplit = cs ? attrOf(cs, "w:val") !== "false" && attrOf(cs, "w:val") !== "0" : false;
1015
1064
  return { header, height, cantSplit };
1016
1065
  });
1017
- const colIndex = logicalRows.map((cells) => new Map(cells.map((c) => [c.startCol, c])));
1066
+ const colIndex = logicalRows.map(
1067
+ (cells) => new Map(cells.map((c) => [c.startCol, c]))
1068
+ );
1018
1069
  const rows = logicalRows.map((cells, r) => {
1019
1070
  const emitted = [];
1020
1071
  for (const cell of cells) {
@@ -1055,7 +1106,8 @@ function parseTable(tbl, ctx) {
1055
1106
  const attrs = {};
1056
1107
  if (cellPadding) attrs["cellPadding"] = cellPadding;
1057
1108
  if (borders) attrs["borders"] = borders;
1058
- if (jc === "center" || jc === "right" || jc === "end") attrs["align"] = jc === "end" ? "right" : jc;
1109
+ if (jc === "center" || jc === "right" || jc === "end")
1110
+ attrs["align"] = jc === "end" ? "right" : jc;
1059
1111
  return ctx.schema.nodes["table"].create(
1060
1112
  Object.keys(attrs).length > 0 ? attrs : null,
1061
1113
  rows.length > 0 ? rows : [ctx.schema.nodes["table_row"].create(null, [emptyCell(ctx)])]
@@ -1121,18 +1173,27 @@ async function readPartRels(zip, partPath) {
1121
1173
  return xml ? parseXml(xml) : void 0;
1122
1174
  }
1123
1175
  async function buildNotesRegistry(zip) {
1124
- const bodies = { footnote: /* @__PURE__ */ new Map(), endnote: /* @__PURE__ */ new Map() };
1176
+ const bodies = {
1177
+ footnote: /* @__PURE__ */ new Map(),
1178
+ endnote: /* @__PURE__ */ new Map()
1179
+ };
1125
1180
  const load = async (path, root, tag, into) => {
1126
1181
  const xml = await readPart(zip, path);
1127
1182
  if (!xml) return;
1128
1183
  for (const note of children(child(parseXml(xml), root), tag)) {
1129
1184
  const id = attrOf(note, "w:id");
1130
1185
  const type = attrOf(note, "w:type");
1131
- if (id === void 0 || Number(id) < 1 || type && type !== "normal") continue;
1186
+ if (id === void 0 || Number(id) < 1 || type && type !== "normal")
1187
+ continue;
1132
1188
  into.set(id, note);
1133
1189
  }
1134
1190
  };
1135
- await load("word/footnotes.xml", "w:footnotes", "w:footnote", bodies.footnote);
1191
+ await load(
1192
+ "word/footnotes.xml",
1193
+ "w:footnotes",
1194
+ "w:footnote",
1195
+ bodies.footnote
1196
+ );
1136
1197
  await load("word/endnotes.xml", "w:endnotes", "w:endnote", bodies.endnote);
1137
1198
  const reg = {
1138
1199
  bodies,
@@ -1167,7 +1228,10 @@ async function buildCommentsRegistry(zip) {
1167
1228
  const ext = /* @__PURE__ */ new Map();
1168
1229
  const extXml = await readPart(zip, "word/commentsExtended.xml");
1169
1230
  if (extXml) {
1170
- for (const ex of children(child(parseXml(extXml), "w15:commentsEx"), "w15:commentEx")) {
1231
+ for (const ex of children(
1232
+ child(parseXml(extXml), "w15:commentsEx"),
1233
+ "w15:commentEx"
1234
+ )) {
1171
1235
  const paraId2 = attrOf(ex, "w15:paraId");
1172
1236
  if (!paraId2) continue;
1173
1237
  const done = attrOf(ex, "w15:done");
@@ -1187,14 +1251,24 @@ function buildCommentsList(ctx) {
1187
1251
  const out = [];
1188
1252
  for (const id of ctx.comments.used) {
1189
1253
  const def = ctx.comments.defs.get(id);
1190
- if (def) out.push({ id, author: def.author, date: def.date, text: collectText(def.body).trim() });
1254
+ if (def)
1255
+ out.push({
1256
+ id,
1257
+ author: def.author,
1258
+ date: def.date,
1259
+ text: collectText(def.body).trim()
1260
+ });
1191
1261
  }
1192
1262
  return out;
1193
1263
  }
1194
1264
  function commentBodyJSON(comment) {
1195
1265
  const paras = children(comment, "w:p").map((p) => {
1196
1266
  const text = collectText(p);
1197
- return commentSchema.node("paragraph", null, text ? [commentSchema.text(text)] : []);
1267
+ return commentSchema.node(
1268
+ "paragraph",
1269
+ null,
1270
+ text ? [commentSchema.text(text)] : []
1271
+ );
1198
1272
  });
1199
1273
  return commentSchema.node("doc", null, paras.length ? paras : [commentSchema.node("paragraph")]).toJSON();
1200
1274
  }
@@ -1214,12 +1288,18 @@ function buildCommentNodes(ctx) {
1214
1288
  if (usedSet.has(cur)) return true;
1215
1289
  return false;
1216
1290
  };
1217
- const ids = [...used.filter((id) => defs.has(id)), ...[...defs.keys()].filter((id) => !usedSet.has(id))];
1291
+ const ids = [
1292
+ ...used.filter((id) => defs.has(id)),
1293
+ ...[...defs.keys()].filter((id) => !usedSet.has(id))
1294
+ ];
1218
1295
  const out = [];
1219
1296
  for (const id of ids) {
1220
1297
  const def = defs.get(id);
1221
1298
  if (!def || !referenced(id)) continue;
1222
- const user = { id: def.author || "unknown", name: def.author || "Unknown" };
1299
+ const user = {
1300
+ id: def.author || "unknown",
1301
+ name: def.author || "Unknown"
1302
+ };
1223
1303
  out.push({
1224
1304
  id,
1225
1305
  parentId: parentOf.get(id) ?? null,
@@ -1233,7 +1313,9 @@ function buildCommentNodes(ctx) {
1233
1313
  }
1234
1314
  function noteBlocks(note, num, ctx) {
1235
1315
  const blocks = parseBlocks(note, ctx);
1236
- const marker = ctx.schema.text(`${num}. `, [ctx.schema.marks["vertAlign"].create({ value: "super" })]);
1316
+ const marker = ctx.schema.text(`${num}. `, [
1317
+ ctx.schema.marks["vertAlign"].create({ value: "super" })
1318
+ ]);
1237
1319
  const first = blocks[0];
1238
1320
  if (first && first.type.name === "paragraph") {
1239
1321
  const kids = [marker];
@@ -1283,7 +1365,10 @@ async function extractMedia(zip) {
1283
1365
  if (!path.startsWith("word/media/")) continue;
1284
1366
  const entry = zip.file(path);
1285
1367
  if (!entry || entry.dir) continue;
1286
- media.set(path, `data:${mimeOf(path)};base64,${await entry.async("base64")}`);
1368
+ media.set(
1369
+ path,
1370
+ `data:${mimeOf(path)};base64,${await entry.async("base64")}`
1371
+ );
1287
1372
  }
1288
1373
  return media;
1289
1374
  }
@@ -1293,11 +1378,16 @@ async function importDocx(input, opts) {
1293
1378
  if (rawDocumentXml === void 0) {
1294
1379
  throw new Error("bapbong-docx: word/document.xml not found in archive");
1295
1380
  }
1296
- const stylesXml = await readPart(zip, "word/styles.xml");
1381
+ const stylesXml2 = await readPart(zip, "word/styles.xml");
1297
1382
  const numberingXml = await readPart(zip, "word/numbering.xml");
1298
1383
  const themeXml = await readPart(zip, "word/theme/theme1.xml");
1299
- const resolveTheme = buildThemeResolver(themeXml ? parseXml(themeXml) : void 0);
1300
- const styles = buildStyleRegistry(stylesXml ? parseXml(stylesXml) : void 0, resolveTheme);
1384
+ const resolveTheme = buildThemeResolver(
1385
+ themeXml ? parseXml(themeXml) : void 0
1386
+ );
1387
+ const styles = buildStyleRegistry(
1388
+ stylesXml2 ? parseXml(stylesXml2) : void 0,
1389
+ resolveTheme
1390
+ );
1301
1391
  const numberingRoot = numberingXml ? parseXml(numberingXml) : void 0;
1302
1392
  const media = await extractMedia(zip);
1303
1393
  const notes = await buildNotesRegistry(zip);
@@ -1346,7 +1436,11 @@ async function importDocx(input, opts) {
1346
1436
  if (!xml) continue;
1347
1437
  const partCtx = makeCtx(buildRels(await readPartRels(zip, partPath)));
1348
1438
  const el = child(parseXml(xml), root);
1349
- store[type] = storyDoc(partCtx, el ? parseBlocks(el, partCtx) : [], ctx.numbering.defs);
1439
+ store[type] = storyDoc(
1440
+ partCtx,
1441
+ el ? parseBlocks(el, partCtx) : [],
1442
+ ctx.numbering.defs
1443
+ );
1350
1444
  }
1351
1445
  };
1352
1446
  await collect("w:headerReference", headers, "w:hdr");
@@ -1423,7 +1517,10 @@ var pxToEmu = (px) => Math.round(px * 9525);
1423
1517
  var commentIdsOf = (node) => node.marks.find((m) => m.type.name === "comment")?.attrs["ids"] ?? [];
1424
1518
  var isInlineLeaf = (node) => node.isText || node.type.name === "image" || node.type.name === "hard_break";
1425
1519
  function esc(s) {
1426
- return s.replace(/[&<>"]/g, (c) => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;" })[c]);
1520
+ return s.replace(
1521
+ /[&<>"]/g,
1522
+ (c) => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;" })[c]
1523
+ );
1427
1524
  }
1428
1525
  var MIME_EXT = {
1429
1526
  "image/png": "png",
@@ -1446,9 +1543,15 @@ function runProps(marks) {
1446
1543
  const size = byName.get("fontSize")?.attrs["size"];
1447
1544
  if (size != null) out.push(`<w:sz w:val="${Math.round(size * 2)}"/>`);
1448
1545
  const hl = byName.get("highlight")?.attrs["color"];
1449
- if (hl) out.push(`<w:shd w:val="clear" w:color="auto" w:fill="${hl.replace(/^#/, "")}"/>`);
1546
+ if (hl)
1547
+ out.push(
1548
+ `<w:shd w:val="clear" w:color="auto" w:fill="${hl.replace(/^#/, "")}"/>`
1549
+ );
1450
1550
  const va = byName.get("vertAlign")?.attrs["value"];
1451
- if (va) out.push(`<w:vertAlign w:val="${va === "sub" ? "subscript" : "superscript"}"/>`);
1551
+ if (va)
1552
+ out.push(
1553
+ `<w:vertAlign w:val="${va === "sub" ? "subscript" : "superscript"}"/>`
1554
+ );
1452
1555
  return out.length ? `<w:rPr>${out.join("")}</w:rPr>` : "";
1453
1556
  }
1454
1557
  function anchorXml(float, cx, cy, n, graphic) {
@@ -1490,7 +1593,9 @@ function imageXml(node, ctx) {
1490
1593
  const n = ctx.nextId++;
1491
1594
  const rid = `rId${n}`;
1492
1595
  ctx.media.push({ path: `word/media/image${n}.${ext}`, base64: m[2] });
1493
- ctx.rels.push(`<Relationship Id="${rid}" Type="${R_NS}/image" Target="media/image${n}.${ext}"/>`);
1596
+ ctx.rels.push(
1597
+ `<Relationship Id="${rid}" Type="${R_NS}/image" Target="media/image${n}.${ext}"/>`
1598
+ );
1494
1599
  const cx = pxToEmu(node.attrs["width"] ?? 96);
1495
1600
  const cy = pxToEmu(node.attrs["height"] ?? 96);
1496
1601
  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>`;
@@ -1500,7 +1605,8 @@ function inlineXml(node, ctx) {
1500
1605
  if (node.type.name === "image") return imageXml(node, ctx);
1501
1606
  if (node.isText) {
1502
1607
  const fn = node.marks.find((m) => m.type.name === "footnote");
1503
- if (fn) return `<w:r>${runProps(node.marks)}<w:footnoteReference w:id="${fn.attrs["num"]}"/></w:r>`;
1608
+ if (fn)
1609
+ return `<w:r>${runProps(node.marks)}<w:footnoteReference w:id="${fn.attrs["num"]}"/></w:r>`;
1504
1610
  return `<w:r>${runProps(node.marks)}<w:t xml:space="preserve">${esc(node.text ?? "")}</w:t></w:r>`;
1505
1611
  }
1506
1612
  return "";
@@ -1510,9 +1616,12 @@ function inlineUnit(node, ctx) {
1510
1616
  const inner = inlineXml(node, ctx);
1511
1617
  const href = linkHref(node);
1512
1618
  if (!href || !inner) return inner;
1513
- if (href.startsWith("#")) return `<w:hyperlink w:anchor="${esc(href.slice(1))}">${inner}</w:hyperlink>`;
1619
+ if (href.startsWith("#"))
1620
+ return `<w:hyperlink w:anchor="${esc(href.slice(1))}">${inner}</w:hyperlink>`;
1514
1621
  const n = ctx.nextId++;
1515
- ctx.rels.push(`<Relationship Id="rId${n}" Type="${R_NS}/hyperlink" Target="${esc(href)}" TargetMode="External"/>`);
1622
+ ctx.rels.push(
1623
+ `<Relationship Id="rId${n}" Type="${R_NS}/hyperlink" Target="${esc(href)}" TargetMode="External"/>`
1624
+ );
1516
1625
  return `<w:hyperlink r:id="rId${n}">${inner}</w:hyperlink>`;
1517
1626
  }
1518
1627
  function inlineContent(node, ctx) {
@@ -1546,10 +1655,16 @@ function paraProps(node) {
1546
1655
  const a = node.attrs;
1547
1656
  const out = [];
1548
1657
  const heading = a["heading"];
1658
+ const styleId = a["styleId"];
1549
1659
  if (heading) out.push(`<w:pStyle w:val="Heading${heading}"/>`);
1660
+ else if (styleId === "Title" || styleId === "Subtitle")
1661
+ out.push(`<w:pStyle w:val="${styleId}"/>`);
1550
1662
  if (a["pageBreakBefore"]) out.push("<w:pageBreakBefore/>");
1551
1663
  const list = a["list"];
1552
- if (list) out.push(`<w:numPr><w:ilvl w:val="${list.level}"/><w:numId w:val="${esc(list.numId)}"/></w:numPr>`);
1664
+ if (list)
1665
+ out.push(
1666
+ `<w:numPr><w:ilvl w:val="${list.level}"/><w:numId w:val="${esc(list.numId)}"/></w:numPr>`
1667
+ );
1553
1668
  const sp = a["spacing"];
1554
1669
  if (sp) {
1555
1670
  const at = [];
@@ -1557,7 +1672,9 @@ function paraProps(node) {
1557
1672
  if (sp.after != null) at.push(`w:after="${pxToTwips(sp.after)}"`);
1558
1673
  if (sp.line != null) {
1559
1674
  const auto = sp.lineRule === "auto" || sp.lineRule == null;
1560
- at.push(`w:line="${auto ? Math.round(sp.line * 240) : pxToTwips(sp.line)}"`);
1675
+ at.push(
1676
+ `w:line="${auto ? Math.round(sp.line * 240) : pxToTwips(sp.line)}"`
1677
+ );
1561
1678
  at.push(`w:lineRule="${sp.lineRule ?? "auto"}"`);
1562
1679
  }
1563
1680
  if (at.length) out.push(`<w:spacing ${at.join(" ")}/>`);
@@ -1568,11 +1685,13 @@ function paraProps(node) {
1568
1685
  if (ind.left != null) at.push(`w:left="${pxToTwips(ind.left)}"`);
1569
1686
  if (ind.right != null) at.push(`w:right="${pxToTwips(ind.right)}"`);
1570
1687
  if (ind.hanging != null) at.push(`w:hanging="${pxToTwips(ind.hanging)}"`);
1571
- else if (ind.firstLine != null) at.push(`w:firstLine="${pxToTwips(ind.firstLine)}"`);
1688
+ else if (ind.firstLine != null)
1689
+ at.push(`w:firstLine="${pxToTwips(ind.firstLine)}"`);
1572
1690
  if (at.length) out.push(`<w:ind ${at.join(" ")}/>`);
1573
1691
  }
1574
1692
  const align = a["align"];
1575
- if (align) out.push(`<w:jc w:val="${align === "justify" ? "both" : align}"/>`);
1693
+ if (align)
1694
+ out.push(`<w:jc w:val="${align === "justify" ? "both" : align}"/>`);
1576
1695
  return out.join("");
1577
1696
  }
1578
1697
  function paragraphXml(node, ctx, sectPr = "") {
@@ -1580,7 +1699,14 @@ function paragraphXml(node, ctx, sectPr = "") {
1580
1699
  const pPr = props ? `<w:pPr>${props}</w:pPr>` : "";
1581
1700
  return `<w:p>${pPr}${inlineContent(node, ctx)}</w:p>`;
1582
1701
  }
1583
- var TABLE_SIDES2 = ["top", "bottom", "left", "right", "insideH", "insideV"];
1702
+ var TABLE_SIDES2 = [
1703
+ "top",
1704
+ "bottom",
1705
+ "left",
1706
+ "right",
1707
+ "insideH",
1708
+ "insideV"
1709
+ ];
1584
1710
  var CELL_SIDES2 = ["top", "bottom", "left", "right"];
1585
1711
  var BORDER_STYLE_OUT = {
1586
1712
  solid: "single",
@@ -1602,12 +1728,19 @@ function cellXml(cell, ctx) {
1602
1728
  const a = cell.attrs;
1603
1729
  const pr = [];
1604
1730
  const colwidth = a["colwidth"];
1605
- if (colwidth?.length) pr.push(`<w:tcW w:w="${pxToTwips(colwidth.reduce((x, y) => x + y, 0))}" w:type="dxa"/>`);
1606
- if (a["colspan"] > 1) pr.push(`<w:gridSpan w:val="${a["colspan"]}"/>`);
1731
+ if (colwidth?.length)
1732
+ pr.push(
1733
+ `<w:tcW w:w="${pxToTwips(colwidth.reduce((x, y) => x + y, 0))}" w:type="dxa"/>`
1734
+ );
1735
+ if (a["colspan"] > 1)
1736
+ pr.push(`<w:gridSpan w:val="${a["colspan"]}"/>`);
1607
1737
  const borders = a["borders"];
1608
1738
  if (borders) pr.push(bordersXml("w:tcBorders", borders, CELL_SIDES2));
1609
1739
  const bg = a["background"];
1610
- if (bg) pr.push(`<w:shd w:val="clear" w:color="auto" w:fill="${bg.replace(/^#/, "")}"/>`);
1740
+ if (bg)
1741
+ pr.push(
1742
+ `<w:shd w:val="clear" w:color="auto" w:fill="${bg.replace(/^#/, "")}"/>`
1743
+ );
1611
1744
  const vAlign = a["vAlign"];
1612
1745
  if (vAlign) pr.push(`<w:vAlign w:val="${vAlign}"/>`);
1613
1746
  let content = "";
@@ -1620,7 +1753,10 @@ function rowXml(row, ctx) {
1620
1753
  if (row.attrs["header"]) pr.push("<w:tblHeader/>");
1621
1754
  if (row.attrs["cantSplit"]) pr.push("<w:cantSplit/>");
1622
1755
  const h = row.attrs["height"];
1623
- if (h) pr.push(`<w:trHeight w:val="${pxToTwips(h.value)}" w:hRule="${h.exact ? "exact" : "atLeast"}"/>`);
1756
+ if (h)
1757
+ pr.push(
1758
+ `<w:trHeight w:val="${pxToTwips(h.value)}" w:hRule="${h.exact ? "exact" : "atLeast"}"/>`
1759
+ );
1624
1760
  const trPr = pr.length ? `<w:trPr>${pr.join("")}</w:trPr>` : "";
1625
1761
  let cells = "";
1626
1762
  row.forEach((c) => cells += cellXml(c, ctx));
@@ -1639,7 +1775,9 @@ function tableXml(node, ctx) {
1639
1775
  }
1640
1776
  const firstRow = node.firstChild;
1641
1777
  const grid = [];
1642
- firstRow?.forEach((c) => c.attrs["colwidth"]?.forEach((w) => grid.push(w)));
1778
+ firstRow?.forEach(
1779
+ (c) => c.attrs["colwidth"]?.forEach((w) => grid.push(w))
1780
+ );
1643
1781
  const gridXml = grid.length ? `<w:tblGrid>${grid.map((w) => `<w:gridCol w:w="${pxToTwips(w)}"/>`).join("")}</w:tblGrid>` : "";
1644
1782
  let rows = "";
1645
1783
  node.forEach((r) => rows += rowXml(r, ctx));
@@ -1674,10 +1812,14 @@ function commentBodyXml(body, firstParaId) {
1674
1812
  doc.forEach((p, _o, i) => {
1675
1813
  let runs = "";
1676
1814
  p.forEach((inline) => {
1677
- if (inline.isText) runs += `<w:r><w:t xml:space="preserve">${esc(inline.text ?? "")}</w:t></w:r>`;
1678
- else if (inline.type.name === "mention") runs += `<w:r><w:t xml:space="preserve">@${esc(String(inline.attrs["label"] ?? ""))}</w:t></w:r>`;
1815
+ if (inline.isText)
1816
+ runs += `<w:r><w:t xml:space="preserve">${esc(inline.text ?? "")}</w:t></w:r>`;
1817
+ else if (inline.type.name === "mention")
1818
+ runs += `<w:r><w:t xml:space="preserve">@${esc(String(inline.attrs["label"] ?? ""))}</w:t></w:r>`;
1679
1819
  });
1680
- ps.push(`<w:p${i === 0 ? ` w14:paraId="${firstParaId}"` : ""}>${runs}</w:p>`);
1820
+ ps.push(
1821
+ `<w:p${i === 0 ? ` w14:paraId="${firstParaId}"` : ""}>${runs}</w:p>`
1822
+ );
1681
1823
  });
1682
1824
  return ps.join("") || `<w:p w14:paraId="${firstParaId}"/>`;
1683
1825
  }
@@ -1699,16 +1841,65 @@ function commentsExtendedXml(comments) {
1699
1841
  }
1700
1842
  var ROOT_RELS = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
1701
1843
  <Relationships xmlns="${PR_NS}"><Relationship Id="rId1" Type="${R_NS}/officeDocument" Target="word/document.xml"/></Relationships>`;
1844
+ var STYLE_DEFS = (() => {
1845
+ 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>`;
1846
+ return {
1847
+ Heading1: heading(1, 48),
1848
+ Heading2: heading(2, 36),
1849
+ Heading3: heading(3, 28),
1850
+ Heading4: heading(4, 24),
1851
+ Heading5: heading(5, 22),
1852
+ Heading6: heading(6, 22),
1853
+ 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>`,
1854
+ 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>`
1855
+ };
1856
+ })();
1857
+ function usedStyleIds(doc) {
1858
+ const used = /* @__PURE__ */ new Set();
1859
+ doc.descendants((n) => {
1860
+ if (n.type.name !== "paragraph") return;
1861
+ const heading = n.attrs["heading"];
1862
+ const styleId = n.attrs["styleId"];
1863
+ if (heading) used.add(`Heading${heading}`);
1864
+ else if (styleId && STYLE_DEFS[styleId]) used.add(styleId);
1865
+ });
1866
+ return used;
1867
+ }
1868
+ function stylesXml(used) {
1869
+ const defs = [...used].map((id) => STYLE_DEFS[id]).join("");
1870
+ return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
1871
+ <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>`;
1872
+ }
1873
+ function mergeStyles(xml, used) {
1874
+ const missing = [...used].filter((id) => !xml.includes(`w:styleId="${id}"`));
1875
+ if (!missing.length) return xml;
1876
+ return xml.replace(
1877
+ "</w:styles>",
1878
+ `${missing.map((id) => STYLE_DEFS[id]).join("")}</w:styles>`
1879
+ );
1880
+ }
1702
1881
  function contentTypes(exts, hasComments) {
1703
1882
  const parts = [
1704
1883
  '<Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>',
1705
1884
  '<Default Extension="xml" ContentType="application/xml"/>'
1706
1885
  ];
1707
- for (const ext of exts) parts.push(`<Default Extension="${ext}" ContentType="image/${ext === "jpg" ? "jpeg" : ext}"/>`);
1708
- parts.push('<Override PartName="/word/document.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml"/>');
1886
+ for (const ext of exts)
1887
+ parts.push(
1888
+ `<Default Extension="${ext}" ContentType="image/${ext === "jpg" ? "jpeg" : ext}"/>`
1889
+ );
1890
+ parts.push(
1891
+ '<Override PartName="/word/document.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml"/>'
1892
+ );
1893
+ parts.push(
1894
+ '<Override PartName="/word/styles.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml"/>'
1895
+ );
1709
1896
  if (hasComments) {
1710
- parts.push('<Override PartName="/word/comments.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.comments+xml"/>');
1711
- parts.push('<Override PartName="/word/commentsExtended.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.commentsExtended+xml"/>');
1897
+ parts.push(
1898
+ '<Override PartName="/word/comments.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.comments+xml"/>'
1899
+ );
1900
+ parts.push(
1901
+ '<Override PartName="/word/commentsExtended.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.commentsExtended+xml"/>'
1902
+ );
1712
1903
  }
1713
1904
  return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
1714
1905
  <Types xmlns="${CT_NS}">${parts.join("")}</Types>`;
@@ -1716,17 +1907,30 @@ function contentTypes(exts, hasComments) {
1716
1907
  function mergeContentTypes(xml, exts, hasComments) {
1717
1908
  let out = xml;
1718
1909
  const add = (frag, key) => {
1719
- if (!out.includes(`"${key}"`)) out = out.replace("</Types>", `${frag}</Types>`);
1910
+ if (!out.includes(`"${key}"`))
1911
+ out = out.replace("</Types>", `${frag}</Types>`);
1720
1912
  };
1721
- for (const ext of exts) add(`<Default Extension="${ext}" ContentType="image/${ext === "jpg" ? "jpeg" : ext}"/>`, ext);
1913
+ for (const ext of exts)
1914
+ add(
1915
+ `<Default Extension="${ext}" ContentType="image/${ext === "jpg" ? "jpeg" : ext}"/>`,
1916
+ ext
1917
+ );
1722
1918
  if (hasComments) {
1723
- add('<Override PartName="/word/comments.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.comments+xml"/>', "/word/comments.xml");
1724
- add('<Override PartName="/word/commentsExtended.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.commentsExtended+xml"/>', "/word/commentsExtended.xml");
1919
+ add(
1920
+ '<Override PartName="/word/comments.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.comments+xml"/>',
1921
+ "/word/comments.xml"
1922
+ );
1923
+ add(
1924
+ '<Override PartName="/word/commentsExtended.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.commentsExtended+xml"/>',
1925
+ "/word/commentsExtended.xml"
1926
+ );
1725
1927
  }
1726
1928
  return out;
1727
1929
  }
1728
1930
  function extractBodySectPr(xml) {
1729
- const all = xml.match(/<w:sectPr\b[^>]*>[\s\S]*?<\/w:sectPr>|<w:sectPr\b[^>]*\/>/g);
1931
+ const all = xml.match(
1932
+ /<w:sectPr\b[^>]*>[\s\S]*?<\/w:sectPr>|<w:sectPr\b[^>]*\/>/g
1933
+ );
1730
1934
  return all ? all[all.length - 1] : "";
1731
1935
  }
1732
1936
  function mergeRels(xml, newRels) {
@@ -1735,7 +1939,10 @@ function mergeRels(xml, newRels) {
1735
1939
  /<Relationship\b[^>]*Target="comments(?:Extended)?\.xml"[^>]*\/>/g,
1736
1940
  ""
1737
1941
  );
1738
- return base.replace("</Relationships>", `${newRels.join("")}</Relationships>`);
1942
+ return base.replace(
1943
+ "</Relationships>",
1944
+ `${newRels.join("")}</Relationships>`
1945
+ );
1739
1946
  }
1740
1947
  async function exportDocx(doc, opts) {
1741
1948
  const comments = doc.attrs["comments"] ?? [];
@@ -1744,7 +1951,8 @@ async function exportDocx(doc, opts) {
1744
1951
  let idx = 0;
1745
1952
  doc.descendants((n) => {
1746
1953
  if (!isInlineLeaf(n)) return;
1747
- for (const id of commentIdsOf(n)) if (knownComments.has(id)) lastRun.set(id, idx);
1954
+ for (const id of commentIdsOf(n))
1955
+ if (knownComments.has(id)) lastRun.set(id, idx);
1748
1956
  idx++;
1749
1957
  });
1750
1958
  const ctx = {
@@ -1759,13 +1967,20 @@ async function exportDocx(doc, opts) {
1759
1967
  };
1760
1968
  const boundaries = sectionBoundaries(doc);
1761
1969
  let body = "";
1762
- doc.forEach((block, _offset, i) => body += blockXml(block, ctx, boundaries.get(i)));
1970
+ doc.forEach(
1971
+ (block, _offset, i) => body += blockXml(block, ctx, boundaries.get(i))
1972
+ );
1763
1973
  const hasComments = comments.length > 0;
1764
1974
  if (hasComments) {
1765
- ctx.rels.push(`<Relationship Id="rIdComments" Type="${R_NS}/comments" Target="comments.xml"/>`);
1766
- ctx.rels.push(`<Relationship Id="rIdCommentsExt" Type="${R_NS}/commentsExtended" Target="commentsExtended.xml"/>`);
1975
+ ctx.rels.push(
1976
+ `<Relationship Id="rIdComments" Type="${R_NS}/comments" Target="comments.xml"/>`
1977
+ );
1978
+ ctx.rels.push(
1979
+ `<Relationship Id="rIdCommentsExt" Type="${R_NS}/commentsExtended" Target="commentsExtended.xml"/>`
1980
+ );
1767
1981
  }
1768
1982
  const zip = new JSZip2();
1983
+ const styleIds = usedStyleIds(doc);
1769
1984
  let sectPr = "";
1770
1985
  if (opts?.carry) {
1771
1986
  const carry = opts.carry;
@@ -1773,18 +1988,29 @@ async function exportDocx(doc, opts) {
1773
1988
  if (!f.dir) zip.file(path, await f.async("uint8array"));
1774
1989
  }
1775
1990
  const ct = await carry.file("[Content_Types].xml")?.async("string");
1776
- zip.file("[Content_Types].xml", ct ? mergeContentTypes(ct, ctx.exts, hasComments) : contentTypes(ctx.exts, hasComments));
1991
+ zip.file(
1992
+ "[Content_Types].xml",
1993
+ ct ? mergeContentTypes(ct, ctx.exts, hasComments) : contentTypes(ctx.exts, hasComments)
1994
+ );
1995
+ const carriedStyles = await carry.file("word/styles.xml")?.async("string");
1996
+ if (carriedStyles)
1997
+ zip.file("word/styles.xml", mergeStyles(carriedStyles, styleIds));
1777
1998
  const rels = await carry.file("word/_rels/document.xml.rels")?.async("string");
1778
1999
  zip.file("word/_rels/document.xml.rels", mergeRels(rels, ctx.rels));
1779
2000
  const origDoc = await carry.file("word/document.xml")?.async("string");
1780
2001
  if (origDoc) sectPr = extractBodySectPr(origDoc);
1781
2002
  } else {
2003
+ ctx.rels.push(
2004
+ `<Relationship Id="rIdStyles" Type="${R_NS}/styles" Target="styles.xml"/>`
2005
+ );
2006
+ zip.file("word/styles.xml", stylesXml(styleIds));
1782
2007
  zip.file("[Content_Types].xml", contentTypes(ctx.exts, hasComments));
1783
2008
  zip.file("_rels/.rels", ROOT_RELS);
1784
- if (ctx.rels.length) {
1785
- zip.file("word/_rels/document.xml.rels", `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
1786
- <Relationships xmlns="${PR_NS}">${ctx.rels.join("")}</Relationships>`);
1787
- }
2009
+ zip.file(
2010
+ "word/_rels/document.xml.rels",
2011
+ `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
2012
+ <Relationships xmlns="${PR_NS}">${ctx.rels.join("")}</Relationships>`
2013
+ );
1788
2014
  }
1789
2015
  const documentXml = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
1790
2016
  <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>`;
@@ -1793,7 +2019,8 @@ async function exportDocx(doc, opts) {
1793
2019
  zip.file("word/comments.xml", commentsXml(comments));
1794
2020
  zip.file("word/commentsExtended.xml", commentsExtendedXml(comments));
1795
2021
  }
1796
- for (const { path, base64 } of ctx.media) zip.file(path, base64, { base64: true });
2022
+ for (const { path, base64 } of ctx.media)
2023
+ zip.file(path, base64, { base64: true });
1797
2024
  return zip.generateAsync({ type: "uint8array" });
1798
2025
  }
1799
2026
  export {