@yinyoudexing/xml2word 0.1.1 → 0.1.3

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.
@@ -7,6 +7,12 @@ function shouldPreserveSpace(text) {
7
7
  if (!text) return false;
8
8
  return /^\s/.test(text) || /\s$/.test(text) || /\s{2,}/.test(text);
9
9
  }
10
+ function shouldKeepWhitespaceOnlyRun(text) {
11
+ if (!text) return false;
12
+ if (/\r|\n/.test(text)) return false;
13
+ if (text.includes("\xA0")) return true;
14
+ return /\s{2,}/.test(text);
15
+ }
10
16
  function parseStyleAttribute(style) {
11
17
  if (!style) return {};
12
18
  const normalized = style.replace(/\r/g, "\n");
@@ -36,6 +42,17 @@ function parseCssColorToHex(value) {
36
42
  if (hex) return hex.toUpperCase();
37
43
  return parseRgbToHex(v);
38
44
  }
45
+ function extractBackgroundFillHex(css) {
46
+ const raw = (css["background-color"] ?? css.background)?.trim();
47
+ if (!raw) return void 0;
48
+ const direct = parseCssColorToHex(raw);
49
+ if (direct) return direct;
50
+ const hex = raw.match(/#([0-9a-fA-F]{6})/)?.[0];
51
+ if (hex) return parseCssColorToHex(hex);
52
+ const rgb = raw.match(/rgb\(\s*\d{1,3}\s*,\s*\d{1,3}\s*,\s*\d{1,3}\s*\)/)?.[0];
53
+ if (rgb) return parseCssColorToHex(rgb);
54
+ return void 0;
55
+ }
39
56
  function parseFontSizeToHalfPoints(value) {
40
57
  if (!value) return void 0;
41
58
  const v = value.trim().toLowerCase();
@@ -106,6 +123,7 @@ function getTextContent(node) {
106
123
  for (const c of children) out += getTextContent(c);
107
124
  return out;
108
125
  }
126
+ var IMAGE_RELATIONSHIP_ID_OFFSET = 7;
109
127
  function decodeBase64ToUint8Array(base64) {
110
128
  const BufferCtor = globalThis.Buffer;
111
129
  if (BufferCtor) {
@@ -226,7 +244,7 @@ function collectInlineRuns(node, inherited, out, result) {
226
244
  const intrinsic = parseIntrinsicImageSizePx(parsed.contentType, parsed.data);
227
245
  const { widthPx, heightPx } = computeImageSizePx(node, intrinsic);
228
246
  const id = result.images.length + 1;
229
- const relationshipId = `rId${id}`;
247
+ const relationshipId = `rId${id + IMAGE_RELATIONSHIP_ID_OFFSET}`;
230
248
  const target = `media/image${id}.${parsed.extension}`;
231
249
  result.images.push({
232
250
  relationshipId,
@@ -249,7 +267,7 @@ function collectInlineRuns(node, inherited, out, result) {
249
267
  const intrinsic = Number.isFinite(bufferW) && bufferW && Number.isFinite(bufferH) && bufferH ? { widthPx: Math.max(1, Math.round(bufferW)), heightPx: Math.max(1, Math.round(bufferH)) } : parseIntrinsicImageSizePx(parsed.contentType, parsed.data);
250
268
  const { widthPx, heightPx } = computeImageSizePx(node, intrinsic);
251
269
  const id = result.images.length + 1;
252
- const relationshipId = `rId${id}`;
270
+ const relationshipId = `rId${id + IMAGE_RELATIONSHIP_ID_OFFSET}`;
253
271
  const target = `media/image${id}.${parsed.extension}`;
254
272
  result.images.push({
255
273
  relationshipId,
@@ -350,9 +368,12 @@ function inferFirstFontSizeHalfPoints(node) {
350
368
  }
351
369
  return void 0;
352
370
  }
353
- function buildParagraphPrXml(node, baseFontHalfPoints, extraInd) {
371
+ function buildParagraphPrXml(node, baseFontHalfPoints, extraInd, pStyleId) {
354
372
  const css = parseStyleAttribute(node.attribs?.style);
355
373
  const parts = [];
374
+ if (pStyleId) parts.push(`<w:pStyle w:val="${escapeXmlText(pStyleId)}"/>`);
375
+ const shdHex = extractBackgroundFillHex(css);
376
+ if (shdHex) parts.push(`<w:shd w:val="clear" w:color="auto" w:fill="${shdHex}"/>`);
356
377
  const align = css["text-align"]?.trim().toLowerCase();
357
378
  const jcVal = align === "center" ? "center" : align === "right" ? "right" : align === "justify" ? "both" : void 0;
358
379
  if (jcVal) parts.push(`<w:jc w:val="${jcVal}"/>`);
@@ -403,15 +424,16 @@ function buildParagraphPrXml(node, baseFontHalfPoints, extraInd) {
403
424
  if (!parts.length) return "";
404
425
  return `<w:pPr>${parts.join("")}</w:pPr>`;
405
426
  }
406
- function buildParagraphXmlFromContainer(node, baseStyle, extraInd, result) {
407
- const baseFontHalfPoints = baseStyle.fontSizeHalfPoints ?? inferFirstFontSizeHalfPoints(node) ?? 28;
408
- const pPrXml = buildParagraphPrXml(node, baseFontHalfPoints, extraInd);
427
+ function buildParagraphXmlFromContainer(node, baseStyle, extraInd, pStyleId, result) {
428
+ const containerStyle = mergeTextStyle(baseStyle, styleFromElement(node));
429
+ const baseFontHalfPoints = containerStyle.fontSizeHalfPoints ?? inferFirstFontSizeHalfPoints(node) ?? 28;
430
+ const pPrXml = buildParagraphPrXml(node, baseFontHalfPoints, extraInd, pStyleId);
409
431
  const runs = [];
410
432
  const res = result ?? {
411
433
  bodyXml: "",
412
434
  images: []
413
435
  };
414
- for (const c of node.children ?? []) collectInlineRuns(c, baseStyle, runs, res);
436
+ for (const c of node.children ?? []) collectInlineRuns(c, containerStyle, runs, res);
415
437
  const rXml = [];
416
438
  for (const token of runs) {
417
439
  if (token.kind === "br") {
@@ -424,7 +446,7 @@ function buildParagraphXmlFromContainer(node, baseStyle, extraInd, result) {
424
446
  }
425
447
  const text = token.text;
426
448
  if (!text) continue;
427
- if (!text.trim()) continue;
449
+ if (!text.trim() && !shouldKeepWhitespaceOnlyRun(text)) continue;
428
450
  rXml.push(buildRunXml(token.style, text));
429
451
  }
430
452
  if (!rXml.length) return "";
@@ -445,25 +467,30 @@ function isExplicitPageBreak(node) {
445
467
  if (after?.includes("always") || before?.includes("always")) return true;
446
468
  return false;
447
469
  }
448
- function buildHeadingBaseStyle(level) {
449
- const size = level === 1 ? 44 : level === 2 ? 32 : level === 3 ? 28 : level === 4 ? 24 : 22;
450
- return { bold: true, fontSizeHalfPoints: size };
451
- }
452
- function buildListBlocks(listNode, ordered, result) {
453
- const items = [];
454
- const stack = [...listNode.children ?? []];
455
- while (stack.length) {
456
- const n = stack.shift();
457
- if (n.type === "tag" && n.name?.toLowerCase() === "li") items.push(n);
458
- }
470
+ function buildListBlocks(listNode, ordered, level, result) {
471
+ const liNodes = (listNode.children ?? []).filter(
472
+ (c) => c.type === "tag" && c.name?.toLowerCase() === "li"
473
+ );
474
+ if (!liNodes.length) return [];
459
475
  const out = [];
460
- for (let i = 0; i < items.length; i++) {
461
- const prefix = ordered ? `${i + 1}. ` : "\u2022 ";
462
- const li = items[i];
463
- const baseStyle = {};
476
+ const numId = ordered ? 2 : 1;
477
+ const ilvl = Math.max(0, Math.min(8, Math.floor(level)));
478
+ const leftTwips = 720 * (ilvl + 1);
479
+ const hangingTwips = 360;
480
+ for (const li of liNodes) {
481
+ const nestedLists = [];
482
+ const baseStyle = styleFromElement(li);
464
483
  const runs = [];
465
- runs.push({ kind: "text", text: prefix, style: baseStyle });
466
- for (const c of li.children ?? []) collectInlineRuns(c, baseStyle, runs, result);
484
+ for (const c of li.children ?? []) {
485
+ if (c.type === "tag") {
486
+ const tag = c.name?.toLowerCase();
487
+ if (tag === "ul" || tag === "ol") {
488
+ nestedLists.push(c);
489
+ continue;
490
+ }
491
+ }
492
+ collectInlineRuns(c, baseStyle, runs, result);
493
+ }
467
494
  const rXml = [];
468
495
  for (const token of runs) {
469
496
  if (token.kind === "br") {
@@ -476,18 +503,143 @@ function buildListBlocks(listNode, ordered, result) {
476
503
  }
477
504
  const text = token.text;
478
505
  if (!text) continue;
479
- if (!text.trim()) continue;
506
+ if (!text.trim() && !shouldKeepWhitespaceOnlyRun(text)) continue;
480
507
  rXml.push(buildRunXml(token.style, text));
481
508
  }
482
- if (!rXml.length) continue;
483
- const pPrXml = buildParagraphPrXml(li, inferFirstFontSizeHalfPoints(li) ?? 28, {
484
- leftTwips: 720,
485
- hangingTwips: 360
486
- });
487
- out.push(`<w:p>${pPrXml}${rXml.join("")}</w:p>`);
509
+ if (rXml.length) {
510
+ const baseFontHalfPoints = inferFirstFontSizeHalfPoints(li) ?? 28;
511
+ const pPrXml = buildParagraphPrXml(
512
+ li,
513
+ baseFontHalfPoints,
514
+ { leftTwips, hangingTwips },
515
+ void 0
516
+ );
517
+ const numPrXml = `<w:numPr><w:ilvl w:val="${ilvl}"/><w:numId w:val="${numId}"/></w:numPr>`;
518
+ const mergedPPrXml = pPrXml ? pPrXml.replace("<w:pPr>", `<w:pPr>${numPrXml}`) : `<w:pPr>${numPrXml}<w:ind w:left="${leftTwips}" w:hanging="${hangingTwips}"/></w:pPr>`;
519
+ out.push(`<w:p>${mergedPPrXml}${rXml.join("")}</w:p>`);
520
+ }
521
+ for (const nested of nestedLists) {
522
+ const nestedOrdered = nested.name?.toLowerCase() === "ol";
523
+ out.push(...buildListBlocks(nested, nestedOrdered, ilvl + 1, result));
524
+ }
488
525
  }
489
526
  return out;
490
527
  }
528
+ function parseCellWidthTwips(node) {
529
+ const css = parseStyleAttribute(node.attribs?.style);
530
+ const width = parseCssLengthToTwips(css.width, 28);
531
+ if (typeof width !== "number" || width <= 0) return void 0;
532
+ return width;
533
+ }
534
+ function estimateTextWidthTwips(text, baseFontHalfPoints) {
535
+ const basePt = baseFontHalfPoints / 2;
536
+ const cjkRegex = /[\u3400-\u4dbf\u4e00-\u9fff\u3000-\u303f\uff00-\uffef]/;
537
+ let cjk = 0;
538
+ let latin = 0;
539
+ let spaces = 0;
540
+ for (const ch of text) {
541
+ if (ch === " " || ch === " ") {
542
+ spaces++;
543
+ continue;
544
+ }
545
+ if (cjkRegex.test(ch)) {
546
+ cjk++;
547
+ continue;
548
+ }
549
+ latin++;
550
+ }
551
+ const cjkTwips = Math.round(basePt * 20);
552
+ const latinTwips = Math.round(basePt * 11);
553
+ const spaceTwips = Math.round(basePt * 6);
554
+ return cjk * cjkTwips + latin * latinTwips + spaces * spaceTwips;
555
+ }
556
+ function parseBorderShorthand(value, baseFontHalfPoints) {
557
+ if (!value) return void 0;
558
+ const raw = value.trim().toLowerCase();
559
+ if (!raw) return void 0;
560
+ if (raw === "none" || raw === "0") return { val: "nil", sz: 0 };
561
+ const tokens = raw.split(/\s+/).filter(Boolean);
562
+ if (!tokens.length) return void 0;
563
+ const css = Object.fromEntries(tokens.map((t, i) => [`${i}`, t]));
564
+ const widthToken = Object.values(css).find((t) => /^(?:\d+(?:\.\d+)?)(?:px|pt)?$/.test(t));
565
+ const styleToken = Object.values(css).find(
566
+ (t) => ["none", "solid", "dashed", "dotted", "double", "hidden"].includes(t)
567
+ );
568
+ const colorToken = Object.values(css).find((t) => t.startsWith("#") || t.startsWith("rgb("));
569
+ const widthTwips = parseCssLengthToTwips(widthToken, baseFontHalfPoints);
570
+ const sz = (() => {
571
+ if (typeof widthTwips !== "number") return 4;
572
+ if (widthTwips <= 0) return 0;
573
+ return Math.max(2, Math.round(widthTwips * 0.4));
574
+ })();
575
+ const val = (() => {
576
+ if (!styleToken) return "single";
577
+ if (styleToken === "none" || styleToken === "hidden") return "nil";
578
+ if (styleToken === "solid") return "single";
579
+ if (styleToken === "dashed") return "dashed";
580
+ if (styleToken === "dotted") return "dotted";
581
+ if (styleToken === "double") return "double";
582
+ return "single";
583
+ })();
584
+ const colorHex = parseCssColorToHex(colorToken);
585
+ return { val, sz, colorHex };
586
+ }
587
+ function buildBorderTag(tag, border, fallbackColorHex) {
588
+ const b = border ?? { val: "single", sz: 4, colorHex: fallbackColorHex };
589
+ const color = (b.colorHex ?? fallbackColorHex).toUpperCase();
590
+ return `<w:${tag} w:val="${b.val}" w:sz="${b.sz}" w:space="0" w:color="${color}"/>`;
591
+ }
592
+ function injectTableCellParagraphSpacing(pXml) {
593
+ if (!pXml.includes("<w:p")) return pXml;
594
+ if (!pXml.includes("<w:p>")) return pXml;
595
+ const spacingXml = '<w:spacing w:before="0" w:after="0" w:line="360" w:lineRule="auto"/><w:wordWrap w:val="1"/>';
596
+ if (pXml.includes("<w:pPr>")) {
597
+ if (pXml.includes("<w:spacing ")) return pXml;
598
+ return pXml.replace("<w:pPr>", `<w:pPr>${spacingXml}`);
599
+ }
600
+ return pXml.replace("<w:p>", `<w:p><w:pPr>${spacingXml}</w:pPr>`);
601
+ }
602
+ function buildTableCellBlocksXml(cell, baseStyle, result) {
603
+ const children = cell.children ?? [];
604
+ const hasBlocks = children.some((c) => {
605
+ if (c.type !== "tag") return false;
606
+ const tag = c.name?.toLowerCase();
607
+ return tag === "p" || tag === "ul" || tag === "ol" || tag === "img" || tag === "canvas" || /^h[1-6]$/.test(tag ?? "");
608
+ });
609
+ const out = [];
610
+ if (!hasBlocks) {
611
+ const p = buildParagraphXmlFromContainer(cell, baseStyle, void 0, void 0, result);
612
+ if (p) out.push(p);
613
+ return out.length ? out.map(injectTableCellParagraphSpacing).join("") : "<w:p/>";
614
+ }
615
+ for (const c of children) {
616
+ if (c.type === "tag") {
617
+ const tag = c.name?.toLowerCase();
618
+ if (tag === "p") {
619
+ const p = buildParagraphXmlFromContainer(c, baseStyle, void 0, void 0, result);
620
+ if (p) out.push(p);
621
+ continue;
622
+ }
623
+ if (tag && /^h[1-6]$/.test(tag)) {
624
+ const level = Number(tag.slice(1));
625
+ const p = buildParagraphXmlFromContainer(c, baseStyle, void 0, `Heading${level}`, result);
626
+ if (p) out.push(p);
627
+ continue;
628
+ }
629
+ if (tag === "ul" || tag === "ol") {
630
+ out.push(...buildListBlocks(c, tag === "ol", 0, result));
631
+ continue;
632
+ }
633
+ if (tag === "img" || tag === "canvas") {
634
+ const p = buildParagraphXmlFromSingleInlineNode(c, baseStyle, result);
635
+ if (p) out.push(p);
636
+ continue;
637
+ }
638
+ }
639
+ }
640
+ if (!out.length) return "<w:p/>";
641
+ return out.map(injectTableCellParagraphSpacing).join("");
642
+ }
491
643
  function buildTableXml(tableNode, result) {
492
644
  const rows = [];
493
645
  const stack = [...tableNode.children ?? []];
@@ -496,25 +648,137 @@ function buildTableXml(tableNode, result) {
496
648
  if (n.type === "tag" && n.name?.toLowerCase() === "tr") rows.push(n);
497
649
  if (n.children?.length) stack.unshift(...n.children);
498
650
  }
499
- const rowXml = [];
500
- for (const tr of rows) {
501
- const cells = (tr.children ?? []).filter(
502
- (c) => c.type === "tag" && (c.name === "td" || c.name === "th")
651
+ const rowCells = rows.map(
652
+ (tr) => (tr.children ?? []).filter((c) => c.type === "tag" && (c.name === "td" || c.name === "th"))
653
+ );
654
+ const colCount = Math.max(0, ...rowCells.map((cells) => cells.length));
655
+ const maxTableWidthTwips = 9360;
656
+ const estimatedColWidths = new Array(colCount).fill(0).map((_, i) => {
657
+ let explicit;
658
+ let estimated = 0;
659
+ for (const cells of rowCells) {
660
+ const cell = cells[i];
661
+ if (!cell) continue;
662
+ const w = parseCellWidthTwips(cell);
663
+ if (typeof w === "number") explicit = explicit ?? w;
664
+ const text = getTextContent(cell).replace(/\s+/g, " ").trim();
665
+ if (!text) continue;
666
+ const baseFontHalfPoints = inferFirstFontSizeHalfPoints(cell) ?? 28;
667
+ const wTwips = estimateTextWidthTwips(text, baseFontHalfPoints) + 240;
668
+ estimated = Math.max(estimated, wTwips);
669
+ }
670
+ const base = typeof explicit === "number" ? explicit : estimated || Math.round(maxTableWidthTwips / Math.max(1, colCount));
671
+ return Math.max(720, Math.min(6e3, Math.round(base)));
672
+ });
673
+ const normalizedColWidths = (() => {
674
+ const sum = estimatedColWidths.reduce((a, b) => a + b, 0);
675
+ if (!sum) return estimatedColWidths;
676
+ if (sum <= maxTableWidthTwips) return estimatedColWidths;
677
+ const scaled = estimatedColWidths.map(
678
+ (w) => Math.max(720, Math.floor(w * maxTableWidthTwips / sum))
503
679
  );
680
+ const scaledSum = scaled.reduce((a, b) => a + b, 0);
681
+ const diff = maxTableWidthTwips - scaledSum;
682
+ if (diff !== 0 && scaled.length) scaled[scaled.length - 1] = Math.max(720, scaled[scaled.length - 1] + diff);
683
+ return scaled;
684
+ })();
685
+ const tblGrid = `<w:tblGrid>${normalizedColWidths.map((w) => `<w:gridCol w:w="${w}"/>`).join("")}</w:tblGrid>`;
686
+ const rowXml = [];
687
+ for (let rowIdx = 0; rowIdx < rows.length; rowIdx++) {
688
+ const tr = rows[rowIdx];
689
+ const cells = rowCells[rowIdx] ?? [];
504
690
  const cellXml = [];
505
- for (const cell of cells) {
691
+ for (let i = 0; i < cells.length; i++) {
692
+ const cell = cells[i];
506
693
  const isHeader = cell.name === "th";
507
694
  const baseStyle = isHeader ? { bold: true } : {};
508
- const pXml = buildParagraphXmlFromContainer(cell, baseStyle, void 0, result);
509
- const paragraphs = pXml ? pXml : "<w:p/>";
510
- cellXml.push(
511
- `<w:tc><w:tcPr><w:tcW w:w="0" w:type="auto"/></w:tcPr>${paragraphs}</w:tc>`
512
- );
695
+ const paragraphs = buildTableCellBlocksXml(cell, baseStyle, result);
696
+ const css = parseStyleAttribute(cell.attribs?.style);
697
+ const widthTwips = parseCellWidthTwips(cell) ?? normalizedColWidths[i];
698
+ const tcW = typeof widthTwips === "number" ? `<w:tcW w:w="${widthTwips}" w:type="dxa"/>` : `<w:tcW w:w="0" w:type="auto"/>`;
699
+ const vAlign = (() => {
700
+ const v = css["vertical-align"]?.trim().toLowerCase();
701
+ if (!v) return "";
702
+ if (v === "middle" || v === "center") return '<w:vAlign w:val="center"/>';
703
+ if (v === "bottom") return '<w:vAlign w:val="bottom"/>';
704
+ if (v === "top") return '<w:vAlign w:val="top"/>';
705
+ return "";
706
+ })();
707
+ const shd = (() => {
708
+ const hex = parseCssColorToHex(css["background-color"]);
709
+ if (!hex) return "";
710
+ return `<w:shd w:val="clear" w:color="auto" w:fill="${hex}"/>`;
711
+ })();
712
+ const noWrap = (() => {
713
+ const ws = css["white-space"]?.trim().toLowerCase();
714
+ if (ws?.includes("nowrap")) return "<w:noWrap/>";
715
+ return "";
716
+ })();
717
+ const cellBorder = (() => {
718
+ const bAll = parseBorderShorthand(css.border, 28);
719
+ const bTop = parseBorderShorthand(css["border-top"] ?? css.border, 28);
720
+ const bLeft = parseBorderShorthand(css["border-left"] ?? css.border, 28);
721
+ const bBottom = parseBorderShorthand(css["border-bottom"] ?? css.border, 28);
722
+ const bRight = parseBorderShorthand(css["border-right"] ?? css.border, 28);
723
+ const any = bAll || css.border || css["border-top"] || css["border-left"] || css["border-bottom"] || css["border-right"];
724
+ if (!any) return "";
725
+ const fallback = bAll?.colorHex ?? "D9D9D9";
726
+ return `<w:tcBorders>${buildBorderTag("top", bTop, fallback)}${buildBorderTag(
727
+ "left",
728
+ bLeft,
729
+ fallback
730
+ )}${buildBorderTag("bottom", bBottom, fallback)}${buildBorderTag(
731
+ "right",
732
+ bRight,
733
+ fallback
734
+ )}</w:tcBorders>`;
735
+ })();
736
+ cellXml.push(`<w:tc><w:tcPr>${tcW}${vAlign}${shd}${noWrap}${cellBorder}</w:tcPr>${paragraphs}</w:tc>`);
513
737
  }
514
738
  if (cellXml.length) rowXml.push(`<w:tr>${cellXml.join("")}</w:tr>`);
515
739
  }
516
- const tblPr = `<w:tblPr><w:tblW w:w="0" w:type="auto"/><w:tblBorders><w:top w:val="single" w:sz="4" w:space="0" w:color="D9D9D9"/><w:left w:val="single" w:sz="4" w:space="0" w:color="D9D9D9"/><w:bottom w:val="single" w:sz="4" w:space="0" w:color="D9D9D9"/><w:right w:val="single" w:sz="4" w:space="0" w:color="D9D9D9"/><w:insideH w:val="single" w:sz="4" w:space="0" w:color="D9D9D9"/><w:insideV w:val="single" w:sz="4" w:space="0" w:color="D9D9D9"/></w:tblBorders></w:tblPr>`;
517
- const tblGrid = `<w:tblGrid/>`;
740
+ const tblCss = parseStyleAttribute(tableNode.attribs?.style);
741
+ const tblAlign = (() => {
742
+ const ml = tblCss["margin-left"]?.trim().toLowerCase();
743
+ const mr = tblCss["margin-right"]?.trim().toLowerCase();
744
+ const m = tblCss.margin?.trim().toLowerCase();
745
+ if (ml === "auto" && mr === "auto" || (m?.includes("auto") ?? false)) return '<w:tblJc w:val="center"/>';
746
+ const ta = tblCss["text-align"]?.trim().toLowerCase();
747
+ if (ta === "center") return '<w:tblJc w:val="center"/>';
748
+ if (ta === "right") return '<w:tblJc w:val="right"/>';
749
+ return "";
750
+ })();
751
+ const tblBorder = (() => {
752
+ const border = parseBorderShorthand(tblCss.border, 28);
753
+ if (tblCss.border) {
754
+ const fallback2 = border?.colorHex ?? "D9D9D9";
755
+ return `<w:tblBorders>${buildBorderTag("top", border, fallback2)}${buildBorderTag(
756
+ "left",
757
+ border,
758
+ fallback2
759
+ )}${buildBorderTag("bottom", border, fallback2)}${buildBorderTag(
760
+ "right",
761
+ border,
762
+ fallback2
763
+ )}${buildBorderTag("insideH", border, fallback2)}${buildBorderTag(
764
+ "insideV",
765
+ border,
766
+ fallback2
767
+ )}</w:tblBorders>`;
768
+ }
769
+ const fallback = "D9D9D9";
770
+ return `<w:tblBorders>${buildBorderTag("top", void 0, fallback)}${buildBorderTag(
771
+ "left",
772
+ void 0,
773
+ fallback
774
+ )}${buildBorderTag("bottom", void 0, fallback)}${buildBorderTag(
775
+ "right",
776
+ void 0,
777
+ fallback
778
+ )}${buildBorderTag("insideH", void 0, fallback)}${buildBorderTag("insideV", void 0, fallback)}</w:tblBorders>`;
779
+ })();
780
+ const tblW = `<w:tblW w:w="${normalizedColWidths.reduce((a, b) => a + b, 0)}" w:type="dxa"/>`;
781
+ const tblPr = `<w:tblPr>${tblW}<w:tblLayout w:type="fixed"/>${tblAlign}${tblBorder}</w:tblPr>`;
518
782
  return `<w:tbl>${tblPr}${tblGrid}${rowXml.join("")}</w:tbl>`;
519
783
  }
520
784
  function buildParagraphXmlFromSingleInlineNode(node, baseStyle, result) {
@@ -524,7 +788,71 @@ function buildParagraphXmlFromSingleInlineNode(node, baseStyle, result) {
524
788
  attribs: { style: "text-align: center;" },
525
789
  children: [node]
526
790
  };
527
- return buildParagraphXmlFromContainer(wrapper, baseStyle, void 0, result);
791
+ return buildParagraphXmlFromContainer(wrapper, baseStyle, void 0, void 0, result);
792
+ }
793
+ function isRecognizedBlockTag(tag) {
794
+ if (!tag) return false;
795
+ if (tag === "p") return true;
796
+ if (tag === "table") return true;
797
+ if (tag === "ul" || tag === "ol") return true;
798
+ if (tag === "img" || tag === "canvas") return true;
799
+ if (/^h[1-6]$/.test(tag)) return true;
800
+ if (tag === "pre") return true;
801
+ return false;
802
+ }
803
+ function collectDivBlocks(node, out, result) {
804
+ const parentStyle = node.attribs?.style;
805
+ const inlineBuffer = [];
806
+ const flushInline = () => {
807
+ const wrapper = {
808
+ type: "tag",
809
+ name: "p",
810
+ attribs: { style: parentStyle },
811
+ children: inlineBuffer.splice(0)
812
+ };
813
+ const pXml = buildParagraphXmlFromContainer(wrapper, {}, void 0, void 0, result);
814
+ if (pXml) out.push(pXml);
815
+ };
816
+ const children = node.children ?? [];
817
+ for (const child of children) {
818
+ if (child.type === "tag") {
819
+ const tag = child.name?.toLowerCase();
820
+ if (isExplicitPageBreak(child)) {
821
+ if (inlineBuffer.length) flushInline();
822
+ out.push(PAGE_BREAK_XML);
823
+ continue;
824
+ }
825
+ if (isRecognizedBlockTag(tag)) {
826
+ if (inlineBuffer.length) flushInline();
827
+ collectBodyBlocks(child, out, result);
828
+ continue;
829
+ }
830
+ if (tag === "div") {
831
+ const childHasRecognizedBlocks = (child.children ?? []).some((gc) => {
832
+ if (gc.type !== "tag") return false;
833
+ return isRecognizedBlockTag(gc.name?.toLowerCase());
834
+ });
835
+ if (childHasRecognizedBlocks) {
836
+ if (inlineBuffer.length) flushInline();
837
+ collectBodyBlocks(child, out, result);
838
+ continue;
839
+ }
840
+ if (inlineBuffer.length) flushInline();
841
+ const mergedStyle = [parentStyle, child.attribs?.style].filter(Boolean).join(";");
842
+ const wrapper = {
843
+ type: "tag",
844
+ name: "p",
845
+ attribs: { style: mergedStyle || void 0 },
846
+ children: child.children ?? []
847
+ };
848
+ const pXml = buildParagraphXmlFromContainer(wrapper, {}, void 0, void 0, result);
849
+ if (pXml) out.push(pXml);
850
+ continue;
851
+ }
852
+ }
853
+ inlineBuffer.push(child);
854
+ }
855
+ if (inlineBuffer.length) flushInline();
528
856
  }
529
857
  function collectBodyBlocks(node, out, result) {
530
858
  if (isSkippableSubtree(node)) return;
@@ -535,7 +863,7 @@ function collectBodyBlocks(node, out, result) {
535
863
  return;
536
864
  }
537
865
  if (tag === "p") {
538
- const pXml = buildParagraphXmlFromContainer(node, {}, void 0, result);
866
+ const pXml = buildParagraphXmlFromContainer(node, {}, void 0, void 0, result);
539
867
  if (pXml) out.push(pXml);
540
868
  return;
541
869
  }
@@ -546,7 +874,7 @@ function collectBodyBlocks(node, out, result) {
546
874
  }
547
875
  if (tag && /^h[1-6]$/.test(tag)) {
548
876
  const level = Number(tag.slice(1));
549
- const hXml = buildParagraphXmlFromContainer(node, buildHeadingBaseStyle(level), void 0, result);
877
+ const hXml = buildParagraphXmlFromContainer(node, {}, void 0, `Heading${level}`, result);
550
878
  if (hXml) out.push(hXml);
551
879
  return;
552
880
  }
@@ -556,7 +884,11 @@ function collectBodyBlocks(node, out, result) {
556
884
  return;
557
885
  }
558
886
  if (tag === "ul" || tag === "ol") {
559
- out.push(...buildListBlocks(node, tag === "ol", result));
887
+ out.push(...buildListBlocks(node, tag === "ol", 0, result));
888
+ return;
889
+ }
890
+ if (tag === "div") {
891
+ collectDivBlocks(node, out, result);
560
892
  return;
561
893
  }
562
894
  }
@@ -624,4 +956,4 @@ export {
624
956
  htmlToWordBodyXml,
625
957
  textToWordBodyXml
626
958
  };
627
- //# sourceMappingURL=htmlToWordBodyXml-AG3GTZEZ.js.map
959
+ //# sourceMappingURL=htmlToWordBodyXml-SIVUZ7K7.js.map