@yinyoudexing/xml2word 0.1.3 → 0.1.5
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/README.md +6 -3
- package/dist/{createDocxZip-BWHSZ7VQ.js → createDocxZip-5CCNW5LX.js} +2 -3
- package/dist/{createDocxZip-BWHSZ7VQ.js.map → createDocxZip-5CCNW5LX.js.map} +1 -1
- package/dist/{htmlToWordBodyXml-SIVUZ7K7.js → htmlToWordBodyXml-JSGDLGOS.js} +231 -48
- package/dist/htmlToWordBodyXml-JSGDLGOS.js.map +1 -0
- package/dist/index.cjs +231 -49
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +3 -3
- package/package.json +2 -2
- package/dist/htmlToWordBodyXml-SIVUZ7K7.js.map +0 -1
|
@@ -71,6 +71,92 @@ function normalizeFontFamily(value) {
|
|
|
71
71
|
if (!first) return void 0;
|
|
72
72
|
return first.replace(/^["']|["']$/g, "");
|
|
73
73
|
}
|
|
74
|
+
function cssSelectorsHitTarget(selectorsText, targetSelector) {
|
|
75
|
+
const target = targetSelector.trim().toLowerCase();
|
|
76
|
+
if (!target) return false;
|
|
77
|
+
const selectors = selectorsText.replace(/\/\*[\s\S]*?\*\//g, " ").split(",").map((s) => s.replace(/\s+/g, " ").trim().toLowerCase()).filter(Boolean);
|
|
78
|
+
return selectors.some((s) => {
|
|
79
|
+
if (s === target) return true;
|
|
80
|
+
if (!s.startsWith(target)) return false;
|
|
81
|
+
const rest = s.slice(target.length);
|
|
82
|
+
return rest.startsWith(".") || rest.startsWith(":") || rest.startsWith("#") || rest.startsWith("[");
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
function parseCssRuleFromHtml(html, selector) {
|
|
86
|
+
const styleTagRegex = /<style\b[^>]*>([\s\S]*?)<\/style>/gi;
|
|
87
|
+
const merged = {};
|
|
88
|
+
let m;
|
|
89
|
+
while (m = styleTagRegex.exec(html)) {
|
|
90
|
+
const cssText = m[1] ?? "";
|
|
91
|
+
const target = selector.trim().toLowerCase();
|
|
92
|
+
if (!target) continue;
|
|
93
|
+
let depth = 0;
|
|
94
|
+
let lastRuleEnd = 0;
|
|
95
|
+
let ruleStart = -1;
|
|
96
|
+
let declStart = -1;
|
|
97
|
+
for (let i = 0; i < cssText.length; i++) {
|
|
98
|
+
const ch = cssText[i];
|
|
99
|
+
if (ch === "{") {
|
|
100
|
+
if (depth === 0) {
|
|
101
|
+
ruleStart = lastRuleEnd;
|
|
102
|
+
declStart = i + 1;
|
|
103
|
+
}
|
|
104
|
+
depth++;
|
|
105
|
+
continue;
|
|
106
|
+
}
|
|
107
|
+
if (ch === "}") {
|
|
108
|
+
if (depth > 0) depth--;
|
|
109
|
+
if (depth === 0 && ruleStart >= 0 && declStart >= 0) {
|
|
110
|
+
const selectorsText = cssText.slice(ruleStart, declStart - 1).trim().toLowerCase();
|
|
111
|
+
const decl = cssText.slice(declStart, i).trim();
|
|
112
|
+
lastRuleEnd = i + 1;
|
|
113
|
+
ruleStart = -1;
|
|
114
|
+
declStart = -1;
|
|
115
|
+
const hit = cssSelectorsHitTarget(selectorsText, target);
|
|
116
|
+
if (hit) Object.assign(merged, parseStyleAttribute(decl));
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
return merged;
|
|
122
|
+
}
|
|
123
|
+
function parseCssRuleFromHtmlFirst(html, selector) {
|
|
124
|
+
const styleTagRegex = /<style\b[^>]*>([\s\S]*?)<\/style>/gi;
|
|
125
|
+
let m;
|
|
126
|
+
while (m = styleTagRegex.exec(html)) {
|
|
127
|
+
const cssText = m[1] ?? "";
|
|
128
|
+
const target = selector.trim().toLowerCase();
|
|
129
|
+
if (!target) continue;
|
|
130
|
+
let depth = 0;
|
|
131
|
+
let lastRuleEnd = 0;
|
|
132
|
+
let ruleStart = -1;
|
|
133
|
+
let declStart = -1;
|
|
134
|
+
for (let i = 0; i < cssText.length; i++) {
|
|
135
|
+
const ch = cssText[i];
|
|
136
|
+
if (ch === "{") {
|
|
137
|
+
if (depth === 0) {
|
|
138
|
+
ruleStart = lastRuleEnd;
|
|
139
|
+
declStart = i + 1;
|
|
140
|
+
}
|
|
141
|
+
depth++;
|
|
142
|
+
continue;
|
|
143
|
+
}
|
|
144
|
+
if (ch === "}") {
|
|
145
|
+
if (depth > 0) depth--;
|
|
146
|
+
if (depth === 0 && ruleStart >= 0 && declStart >= 0) {
|
|
147
|
+
const selectorsText = cssText.slice(ruleStart, declStart - 1).trim().toLowerCase();
|
|
148
|
+
const decl = cssText.slice(declStart, i).trim();
|
|
149
|
+
lastRuleEnd = i + 1;
|
|
150
|
+
ruleStart = -1;
|
|
151
|
+
declStart = -1;
|
|
152
|
+
const hit = cssSelectorsHitTarget(selectorsText, target);
|
|
153
|
+
if (hit) return parseStyleAttribute(decl);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
return {};
|
|
159
|
+
}
|
|
74
160
|
function mergeTextStyle(base, patch) {
|
|
75
161
|
return {
|
|
76
162
|
bold: patch.bold ?? base.bold,
|
|
@@ -348,10 +434,29 @@ function parseCssLengthToTwips(value, baseFontHalfPoints) {
|
|
|
348
434
|
const basePt = baseFontHalfPoints / 2;
|
|
349
435
|
return Math.round(Number(em[1]) * basePt * 20);
|
|
350
436
|
}
|
|
437
|
+
const rem = v.match(/^(-?\d+(?:\.\d+)?)rem$/);
|
|
438
|
+
if (rem) return Math.round(Number(rem[1]) * 16 * 72 * 20 / 96);
|
|
351
439
|
const num = v.match(/^(-?\d+(?:\.\d+)?)$/);
|
|
352
440
|
if (num) return Math.round(Number(num[1]));
|
|
353
441
|
return void 0;
|
|
354
442
|
}
|
|
443
|
+
function extractMarginBeforeAfterTwips(css, baseFontHalfPoints) {
|
|
444
|
+
const before = parseCssLengthToTwips(css["margin-top"], baseFontHalfPoints);
|
|
445
|
+
const after = parseCssLengthToTwips(css["margin-bottom"], baseFontHalfPoints);
|
|
446
|
+
if (typeof before === "number" || typeof after === "number") {
|
|
447
|
+
return { beforeTwips: before, afterTwips: after };
|
|
448
|
+
}
|
|
449
|
+
const m = css.margin?.trim().toLowerCase();
|
|
450
|
+
if (!m) return {};
|
|
451
|
+
const tokens = m.split(/\s+/).filter(Boolean);
|
|
452
|
+
if (!tokens.length) return {};
|
|
453
|
+
const topToken = tokens[0];
|
|
454
|
+
const bottomToken = tokens.length === 1 ? tokens[0] : tokens.length === 2 ? tokens[0] : tokens[2] ?? tokens[0];
|
|
455
|
+
return {
|
|
456
|
+
beforeTwips: parseCssLengthToTwips(topToken, baseFontHalfPoints),
|
|
457
|
+
afterTwips: parseCssLengthToTwips(bottomToken, baseFontHalfPoints)
|
|
458
|
+
};
|
|
459
|
+
}
|
|
355
460
|
function inferFirstFontSizeHalfPoints(node) {
|
|
356
461
|
const stack = [node];
|
|
357
462
|
while (stack.length) {
|
|
@@ -368,8 +473,10 @@ function inferFirstFontSizeHalfPoints(node) {
|
|
|
368
473
|
}
|
|
369
474
|
return void 0;
|
|
370
475
|
}
|
|
371
|
-
function buildParagraphPrXml(node, baseFontHalfPoints, extraInd, pStyleId) {
|
|
372
|
-
const
|
|
476
|
+
function buildParagraphPrXml(node, baseFontHalfPoints, extraInd, pStyleId, defaultCss) {
|
|
477
|
+
const tag = node.type === "tag" ? node.name?.toLowerCase() : void 0;
|
|
478
|
+
const inlineCss = parseStyleAttribute(node.attribs?.style);
|
|
479
|
+
const css = defaultCss ? { ...defaultCss, ...inlineCss } : inlineCss;
|
|
373
480
|
const parts = [];
|
|
374
481
|
if (pStyleId) parts.push(`<w:pStyle w:val="${escapeXmlText(pStyleId)}"/>`);
|
|
375
482
|
const shdHex = extractBackgroundFillHex(css);
|
|
@@ -396,10 +503,17 @@ function buildParagraphPrXml(node, baseFontHalfPoints, extraInd, pStyleId) {
|
|
|
396
503
|
if (typeof hangingTwips === "number") indAttrs.push(`w:hanging="${hangingTwips}"`);
|
|
397
504
|
if (typeof firstLine === "number") indAttrs.push(`w:firstLine="${firstLine}"`);
|
|
398
505
|
if (indAttrs.length) parts.push(`<w:ind ${indAttrs.join(" ")}/>`);
|
|
399
|
-
const
|
|
400
|
-
const
|
|
506
|
+
const hasInlineBefore = inlineCss["margin-top"] != null;
|
|
507
|
+
const hasInlineAfter = inlineCss["margin-bottom"] != null;
|
|
508
|
+
const beforeToken = inlineCss["margin-top"] ?? (pStyleId ? void 0 : defaultCss?.["margin-top"]);
|
|
509
|
+
const afterToken = inlineCss["margin-bottom"] ?? (pStyleId ? void 0 : defaultCss?.["margin-bottom"]);
|
|
510
|
+
let before = parseCssLengthToTwips(beforeToken, baseFontHalfPoints);
|
|
511
|
+
let after = parseCssLengthToTwips(afterToken, baseFontHalfPoints);
|
|
512
|
+
if (tag === "p" && !hasInlineBefore && typeof before === "number") before = Math.min(before, 160);
|
|
513
|
+
if (tag === "p" && !hasInlineAfter && typeof after === "number") after = Math.min(after, 160);
|
|
401
514
|
const lineHeight = (() => {
|
|
402
|
-
const
|
|
515
|
+
const lhToken = inlineCss["line-height"] ?? (pStyleId ? void 0 : defaultCss?.["line-height"]);
|
|
516
|
+
const lh = lhToken?.trim().toLowerCase();
|
|
403
517
|
if (!lh || lh === "normal") return void 0;
|
|
404
518
|
const unitless = lh.match(/^(\d+(?:\.\d+)?)$/);
|
|
405
519
|
if (unitless) {
|
|
@@ -414,8 +528,16 @@ function buildParagraphPrXml(node, baseFontHalfPoints, extraInd, pStyleId) {
|
|
|
414
528
|
})();
|
|
415
529
|
if (typeof before === "number" || typeof after === "number" || typeof lineHeight === "number") {
|
|
416
530
|
const attrs = [];
|
|
417
|
-
if (typeof before === "number")
|
|
418
|
-
|
|
531
|
+
if (typeof before === "number") {
|
|
532
|
+
attrs.push(`w:before="${Math.max(0, before)}"`);
|
|
533
|
+
} else if (typeof lineHeight === "number") {
|
|
534
|
+
attrs.push('w:before="0"');
|
|
535
|
+
}
|
|
536
|
+
if (typeof after === "number") {
|
|
537
|
+
attrs.push(`w:after="${Math.max(0, after)}"`);
|
|
538
|
+
} else if (typeof lineHeight === "number") {
|
|
539
|
+
attrs.push('w:after="160"');
|
|
540
|
+
}
|
|
419
541
|
if (typeof lineHeight === "number") {
|
|
420
542
|
attrs.push(`w:line="${lineHeight}"`, 'w:lineRule="exact"');
|
|
421
543
|
}
|
|
@@ -424,10 +546,13 @@ function buildParagraphPrXml(node, baseFontHalfPoints, extraInd, pStyleId) {
|
|
|
424
546
|
if (!parts.length) return "";
|
|
425
547
|
return `<w:pPr>${parts.join("")}</w:pPr>`;
|
|
426
548
|
}
|
|
427
|
-
function buildParagraphXmlFromContainer(node, baseStyle, extraInd, pStyleId, result) {
|
|
428
|
-
const
|
|
549
|
+
function buildParagraphXmlFromContainer(node, baseStyle, extraInd, pStyleId, result, ctx) {
|
|
550
|
+
const seededBaseStyle = ctx ? { fontSizeHalfPoints: ctx.defaultBaseFontHalfPoints } : {};
|
|
551
|
+
const containerStyle = mergeTextStyle(mergeTextStyle(seededBaseStyle, baseStyle), styleFromElement(node));
|
|
429
552
|
const baseFontHalfPoints = containerStyle.fontSizeHalfPoints ?? inferFirstFontSizeHalfPoints(node) ?? 28;
|
|
430
|
-
const
|
|
553
|
+
const computedBaseFontHalfPoints = containerStyle.fontSizeHalfPoints ?? inferFirstFontSizeHalfPoints(node) ?? ctx?.defaultBaseFontHalfPoints ?? 28;
|
|
554
|
+
const defaultCss = ctx ? node.type === "tag" && node.name?.toLowerCase() === "p" ? { ...ctx.defaultBodyCss, ...ctx.defaultPCss } : ctx.defaultBodyCss : void 0;
|
|
555
|
+
const pPrXml = buildParagraphPrXml(node, computedBaseFontHalfPoints, extraInd, pStyleId, defaultCss);
|
|
431
556
|
const runs = [];
|
|
432
557
|
const res = result ?? {
|
|
433
558
|
bodyXml: "",
|
|
@@ -453,6 +578,11 @@ function buildParagraphXmlFromContainer(node, baseStyle, extraInd, pStyleId, res
|
|
|
453
578
|
return `<w:p>${pPrXml}${rXml.join("")}</w:p>`;
|
|
454
579
|
}
|
|
455
580
|
var PAGE_BREAK_XML = '<w:p><w:r><w:br w:type="page"/></w:r></w:p>';
|
|
581
|
+
function buildSpacerParagraphXml(afterTwips) {
|
|
582
|
+
const after = Math.max(0, Math.round(afterTwips));
|
|
583
|
+
if (!after) return "";
|
|
584
|
+
return `<w:p><w:pPr><w:spacing w:before="0" w:after="${after}" w:line="1" w:lineRule="exact"/></w:pPr><w:r><w:rPr><w:sz w:val="1"/><w:szCs w:val="1"/></w:rPr><w:t></w:t></w:r></w:p>`;
|
|
585
|
+
}
|
|
456
586
|
function isExplicitPageBreak(node) {
|
|
457
587
|
if (node.type !== "tag") return false;
|
|
458
588
|
const tag = node.name?.toLowerCase();
|
|
@@ -461,13 +591,14 @@ function isExplicitPageBreak(node) {
|
|
|
461
591
|
const classList = cls ? cls.split(/\s+/) : [];
|
|
462
592
|
if (tag === "hr" && classList.includes("page-break")) return true;
|
|
463
593
|
if (classList.includes("page-break")) return true;
|
|
594
|
+
if (classList.includes("umo-page-break")) return true;
|
|
464
595
|
if (node.attribs?.["data-page-break"] === "true") return true;
|
|
465
596
|
const after = css["page-break-after"]?.toLowerCase() ?? css["break-after"]?.toLowerCase();
|
|
466
597
|
const before = css["page-break-before"]?.toLowerCase() ?? css["break-before"]?.toLowerCase();
|
|
467
598
|
if (after?.includes("always") || before?.includes("always")) return true;
|
|
468
599
|
return false;
|
|
469
600
|
}
|
|
470
|
-
function buildListBlocks(listNode, ordered, level, result) {
|
|
601
|
+
function buildListBlocks(listNode, ordered, level, result, ctx) {
|
|
471
602
|
const liNodes = (listNode.children ?? []).filter(
|
|
472
603
|
(c) => c.type === "tag" && c.name?.toLowerCase() === "li"
|
|
473
604
|
);
|
|
@@ -507,12 +638,13 @@ function buildListBlocks(listNode, ordered, level, result) {
|
|
|
507
638
|
rXml.push(buildRunXml(token.style, text));
|
|
508
639
|
}
|
|
509
640
|
if (rXml.length) {
|
|
510
|
-
const baseFontHalfPoints = inferFirstFontSizeHalfPoints(li) ?? 28;
|
|
641
|
+
const baseFontHalfPoints = inferFirstFontSizeHalfPoints(li) ?? ctx?.defaultBaseFontHalfPoints ?? 28;
|
|
511
642
|
const pPrXml = buildParagraphPrXml(
|
|
512
643
|
li,
|
|
513
644
|
baseFontHalfPoints,
|
|
514
645
|
{ leftTwips, hangingTwips },
|
|
515
|
-
void 0
|
|
646
|
+
void 0,
|
|
647
|
+
ctx?.defaultBodyCss
|
|
516
648
|
);
|
|
517
649
|
const numPrXml = `<w:numPr><w:ilvl w:val="${ilvl}"/><w:numId w:val="${numId}"/></w:numPr>`;
|
|
518
650
|
const mergedPPrXml = pPrXml ? pPrXml.replace("<w:pPr>", `<w:pPr>${numPrXml}`) : `<w:pPr>${numPrXml}<w:ind w:left="${leftTwips}" w:hanging="${hangingTwips}"/></w:pPr>`;
|
|
@@ -520,7 +652,7 @@ function buildListBlocks(listNode, ordered, level, result) {
|
|
|
520
652
|
}
|
|
521
653
|
for (const nested of nestedLists) {
|
|
522
654
|
const nestedOrdered = nested.name?.toLowerCase() === "ol";
|
|
523
|
-
out.push(...buildListBlocks(nested, nestedOrdered, ilvl + 1, result));
|
|
655
|
+
out.push(...buildListBlocks(nested, nestedOrdered, ilvl + 1, result, ctx));
|
|
524
656
|
}
|
|
525
657
|
}
|
|
526
658
|
return out;
|
|
@@ -599,7 +731,7 @@ function injectTableCellParagraphSpacing(pXml) {
|
|
|
599
731
|
}
|
|
600
732
|
return pXml.replace("<w:p>", `<w:p><w:pPr>${spacingXml}</w:pPr>`);
|
|
601
733
|
}
|
|
602
|
-
function buildTableCellBlocksXml(cell, baseStyle, result) {
|
|
734
|
+
function buildTableCellBlocksXml(cell, baseStyle, result, ctx) {
|
|
603
735
|
const children = cell.children ?? [];
|
|
604
736
|
const hasBlocks = children.some((c) => {
|
|
605
737
|
if (c.type !== "tag") return false;
|
|
@@ -608,7 +740,7 @@ function buildTableCellBlocksXml(cell, baseStyle, result) {
|
|
|
608
740
|
});
|
|
609
741
|
const out = [];
|
|
610
742
|
if (!hasBlocks) {
|
|
611
|
-
const p = buildParagraphXmlFromContainer(cell, baseStyle, void 0, void 0, result);
|
|
743
|
+
const p = buildParagraphXmlFromContainer(cell, baseStyle, void 0, void 0, result, ctx);
|
|
612
744
|
if (p) out.push(p);
|
|
613
745
|
return out.length ? out.map(injectTableCellParagraphSpacing).join("") : "<w:p/>";
|
|
614
746
|
}
|
|
@@ -616,18 +748,18 @@ function buildTableCellBlocksXml(cell, baseStyle, result) {
|
|
|
616
748
|
if (c.type === "tag") {
|
|
617
749
|
const tag = c.name?.toLowerCase();
|
|
618
750
|
if (tag === "p") {
|
|
619
|
-
const p = buildParagraphXmlFromContainer(c, baseStyle, void 0, void 0, result);
|
|
751
|
+
const p = buildParagraphXmlFromContainer(c, baseStyle, void 0, void 0, result, ctx);
|
|
620
752
|
if (p) out.push(p);
|
|
621
753
|
continue;
|
|
622
754
|
}
|
|
623
755
|
if (tag && /^h[1-6]$/.test(tag)) {
|
|
624
756
|
const level = Number(tag.slice(1));
|
|
625
|
-
const p = buildParagraphXmlFromContainer(c, baseStyle, void 0, `Heading${level}`, result);
|
|
757
|
+
const p = buildParagraphXmlFromContainer(c, baseStyle, void 0, `Heading${level}`, result, ctx);
|
|
626
758
|
if (p) out.push(p);
|
|
627
759
|
continue;
|
|
628
760
|
}
|
|
629
761
|
if (tag === "ul" || tag === "ol") {
|
|
630
|
-
out.push(...buildListBlocks(c, tag === "ol", 0, result));
|
|
762
|
+
out.push(...buildListBlocks(c, tag === "ol", 0, result, ctx));
|
|
631
763
|
continue;
|
|
632
764
|
}
|
|
633
765
|
if (tag === "img" || tag === "canvas") {
|
|
@@ -640,7 +772,7 @@ function buildTableCellBlocksXml(cell, baseStyle, result) {
|
|
|
640
772
|
if (!out.length) return "<w:p/>";
|
|
641
773
|
return out.map(injectTableCellParagraphSpacing).join("");
|
|
642
774
|
}
|
|
643
|
-
function buildTableXml(tableNode, result) {
|
|
775
|
+
function buildTableXml(tableNode, result, ctx) {
|
|
644
776
|
const rows = [];
|
|
645
777
|
const stack = [...tableNode.children ?? []];
|
|
646
778
|
while (stack.length) {
|
|
@@ -653,8 +785,18 @@ function buildTableXml(tableNode, result) {
|
|
|
653
785
|
);
|
|
654
786
|
const colCount = Math.max(0, ...rowCells.map((cells) => cells.length));
|
|
655
787
|
const maxTableWidthTwips = 9360;
|
|
788
|
+
const colGroup = (tableNode.children ?? []).find((c) => c.type === "tag" && c.name === "colgroup");
|
|
789
|
+
const colWidthsFromGroup = [];
|
|
790
|
+
if (colGroup) {
|
|
791
|
+
const cols = (colGroup.children ?? []).filter((c) => c.type === "tag" && c.name === "col");
|
|
792
|
+
for (const col of cols) {
|
|
793
|
+
const css = parseStyleAttribute(col.attribs?.style);
|
|
794
|
+
const w = parseCssLengthToTwips(css.width ?? css["min-width"], 28);
|
|
795
|
+
colWidthsFromGroup.push(w);
|
|
796
|
+
}
|
|
797
|
+
}
|
|
656
798
|
const estimatedColWidths = new Array(colCount).fill(0).map((_, i) => {
|
|
657
|
-
let explicit;
|
|
799
|
+
let explicit = colWidthsFromGroup[i];
|
|
658
800
|
let estimated = 0;
|
|
659
801
|
for (const cells of rowCells) {
|
|
660
802
|
const cell = cells[i];
|
|
@@ -667,7 +809,7 @@ function buildTableXml(tableNode, result) {
|
|
|
667
809
|
const wTwips = estimateTextWidthTwips(text, baseFontHalfPoints) + 240;
|
|
668
810
|
estimated = Math.max(estimated, wTwips);
|
|
669
811
|
}
|
|
670
|
-
const base = typeof explicit === "number" ? explicit : estimated || Math.round(maxTableWidthTwips / Math.max(1, colCount));
|
|
812
|
+
const base = typeof explicit === "number" ? Math.max(explicit, estimated) : estimated || Math.round(maxTableWidthTwips / Math.max(1, colCount));
|
|
671
813
|
return Math.max(720, Math.min(6e3, Math.round(base)));
|
|
672
814
|
});
|
|
673
815
|
const normalizedColWidths = (() => {
|
|
@@ -692,7 +834,7 @@ function buildTableXml(tableNode, result) {
|
|
|
692
834
|
const cell = cells[i];
|
|
693
835
|
const isHeader = cell.name === "th";
|
|
694
836
|
const baseStyle = isHeader ? { bold: true } : {};
|
|
695
|
-
const paragraphs = buildTableCellBlocksXml(cell, baseStyle, result);
|
|
837
|
+
const paragraphs = buildTableCellBlocksXml(cell, baseStyle, result, ctx);
|
|
696
838
|
const css = parseStyleAttribute(cell.attribs?.style);
|
|
697
839
|
const widthTwips = parseCellWidthTwips(cell) ?? normalizedColWidths[i];
|
|
698
840
|
const tcW = typeof widthTwips === "number" ? `<w:tcW w:w="${widthTwips}" w:type="dxa"/>` : `<w:tcW w:w="0" w:type="auto"/>`;
|
|
@@ -781,14 +923,14 @@ function buildTableXml(tableNode, result) {
|
|
|
781
923
|
const tblPr = `<w:tblPr>${tblW}<w:tblLayout w:type="fixed"/>${tblAlign}${tblBorder}</w:tblPr>`;
|
|
782
924
|
return `<w:tbl>${tblPr}${tblGrid}${rowXml.join("")}</w:tbl>`;
|
|
783
925
|
}
|
|
784
|
-
function buildParagraphXmlFromSingleInlineNode(node, baseStyle, result) {
|
|
926
|
+
function buildParagraphXmlFromSingleInlineNode(node, baseStyle, result, ctx) {
|
|
785
927
|
const wrapper = {
|
|
786
928
|
type: "tag",
|
|
787
929
|
name: "p",
|
|
788
930
|
attribs: { style: "text-align: center;" },
|
|
789
931
|
children: [node]
|
|
790
932
|
};
|
|
791
|
-
return buildParagraphXmlFromContainer(wrapper, baseStyle, void 0, void 0, result);
|
|
933
|
+
return buildParagraphXmlFromContainer(wrapper, baseStyle, void 0, void 0, result, ctx);
|
|
792
934
|
}
|
|
793
935
|
function isRecognizedBlockTag(tag) {
|
|
794
936
|
if (!tag) return false;
|
|
@@ -800,7 +942,22 @@ function isRecognizedBlockTag(tag) {
|
|
|
800
942
|
if (tag === "pre") return true;
|
|
801
943
|
return false;
|
|
802
944
|
}
|
|
803
|
-
function
|
|
945
|
+
function subtreeHasRecognizedBlocks(root) {
|
|
946
|
+
const stack = [root];
|
|
947
|
+
while (stack.length) {
|
|
948
|
+
const cur = stack.pop();
|
|
949
|
+
if (cur.type === "tag") {
|
|
950
|
+
if (isExplicitPageBreak(cur)) return true;
|
|
951
|
+
if (isRecognizedBlockTag(cur.name?.toLowerCase())) return true;
|
|
952
|
+
}
|
|
953
|
+
const children = cur.children ?? [];
|
|
954
|
+
for (let i = children.length - 1; i >= 0; i--) {
|
|
955
|
+
stack.push(children[i]);
|
|
956
|
+
}
|
|
957
|
+
}
|
|
958
|
+
return false;
|
|
959
|
+
}
|
|
960
|
+
function collectDivBlocks(node, out, result, ctx) {
|
|
804
961
|
const parentStyle = node.attribs?.style;
|
|
805
962
|
const inlineBuffer = [];
|
|
806
963
|
const flushInline = () => {
|
|
@@ -810,7 +967,7 @@ function collectDivBlocks(node, out, result) {
|
|
|
810
967
|
attribs: { style: parentStyle },
|
|
811
968
|
children: inlineBuffer.splice(0)
|
|
812
969
|
};
|
|
813
|
-
const pXml = buildParagraphXmlFromContainer(wrapper, {}, void 0, void 0, result);
|
|
970
|
+
const pXml = buildParagraphXmlFromContainer(wrapper, {}, void 0, void 0, result, ctx);
|
|
814
971
|
if (pXml) out.push(pXml);
|
|
815
972
|
};
|
|
816
973
|
const children = node.children ?? [];
|
|
@@ -824,19 +981,15 @@ function collectDivBlocks(node, out, result) {
|
|
|
824
981
|
}
|
|
825
982
|
if (isRecognizedBlockTag(tag)) {
|
|
826
983
|
if (inlineBuffer.length) flushInline();
|
|
827
|
-
collectBodyBlocks(child, out, result);
|
|
984
|
+
collectBodyBlocks(child, out, result, ctx);
|
|
985
|
+
continue;
|
|
986
|
+
}
|
|
987
|
+
if (subtreeHasRecognizedBlocks(child)) {
|
|
988
|
+
if (inlineBuffer.length) flushInline();
|
|
989
|
+
collectBodyBlocks(child, out, result, ctx);
|
|
828
990
|
continue;
|
|
829
991
|
}
|
|
830
992
|
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
993
|
if (inlineBuffer.length) flushInline();
|
|
841
994
|
const mergedStyle = [parentStyle, child.attribs?.style].filter(Boolean).join(";");
|
|
842
995
|
const wrapper = {
|
|
@@ -845,7 +998,7 @@ function collectDivBlocks(node, out, result) {
|
|
|
845
998
|
attribs: { style: mergedStyle || void 0 },
|
|
846
999
|
children: child.children ?? []
|
|
847
1000
|
};
|
|
848
|
-
const pXml = buildParagraphXmlFromContainer(wrapper, {}, void 0, void 0, result);
|
|
1001
|
+
const pXml = buildParagraphXmlFromContainer(wrapper, {}, void 0, void 0, result, ctx);
|
|
849
1002
|
if (pXml) out.push(pXml);
|
|
850
1003
|
continue;
|
|
851
1004
|
}
|
|
@@ -854,7 +1007,7 @@ function collectDivBlocks(node, out, result) {
|
|
|
854
1007
|
}
|
|
855
1008
|
if (inlineBuffer.length) flushInline();
|
|
856
1009
|
}
|
|
857
|
-
function collectBodyBlocks(node, out, result) {
|
|
1010
|
+
function collectBodyBlocks(node, out, result, ctx) {
|
|
858
1011
|
if (isSkippableSubtree(node)) return;
|
|
859
1012
|
if (node.type === "tag") {
|
|
860
1013
|
const tag = node.name?.toLowerCase();
|
|
@@ -863,36 +1016,51 @@ function collectBodyBlocks(node, out, result) {
|
|
|
863
1016
|
return;
|
|
864
1017
|
}
|
|
865
1018
|
if (tag === "p") {
|
|
866
|
-
const pXml = buildParagraphXmlFromContainer(node, {}, void 0, void 0, result);
|
|
1019
|
+
const pXml = buildParagraphXmlFromContainer(node, {}, void 0, void 0, result, ctx);
|
|
867
1020
|
if (pXml) out.push(pXml);
|
|
868
1021
|
return;
|
|
869
1022
|
}
|
|
870
1023
|
if (tag === "img" || tag === "canvas") {
|
|
871
|
-
const pXml = buildParagraphXmlFromSingleInlineNode(node, {}, result);
|
|
1024
|
+
const pXml = buildParagraphXmlFromSingleInlineNode(node, {}, result, ctx);
|
|
872
1025
|
if (pXml) out.push(pXml);
|
|
873
1026
|
return;
|
|
874
1027
|
}
|
|
875
1028
|
if (tag && /^h[1-6]$/.test(tag)) {
|
|
876
1029
|
const level = Number(tag.slice(1));
|
|
877
|
-
const hXml = buildParagraphXmlFromContainer(node, {}, void 0, `Heading${level}`, result);
|
|
1030
|
+
const hXml = buildParagraphXmlFromContainer(node, {}, void 0, `Heading${level}`, result, ctx);
|
|
878
1031
|
if (hXml) out.push(hXml);
|
|
879
1032
|
return;
|
|
880
1033
|
}
|
|
881
1034
|
if (tag === "table") {
|
|
882
|
-
const tblXml = buildTableXml(node, result);
|
|
1035
|
+
const tblXml = buildTableXml(node, result, ctx);
|
|
883
1036
|
if (tblXml) out.push(tblXml);
|
|
884
1037
|
return;
|
|
885
1038
|
}
|
|
886
1039
|
if (tag === "ul" || tag === "ol") {
|
|
887
|
-
out.push(...buildListBlocks(node, tag === "ol", 0, result));
|
|
1040
|
+
out.push(...buildListBlocks(node, tag === "ol", 0, result, ctx));
|
|
888
1041
|
return;
|
|
889
1042
|
}
|
|
890
1043
|
if (tag === "div") {
|
|
891
|
-
|
|
1044
|
+
if (hasClass(node, "tableWrapper")) {
|
|
1045
|
+
const display = ctx.tableWrapperCss.display?.trim().toLowerCase();
|
|
1046
|
+
if (display !== "contents") {
|
|
1047
|
+
const baseFontHalfPoints = inferFirstFontSizeHalfPoints(node) ?? ctx.defaultBaseFontHalfPoints;
|
|
1048
|
+
const { beforeTwips, afterTwips } = extractMarginBeforeAfterTwips(ctx.tableWrapperCss, baseFontHalfPoints);
|
|
1049
|
+
const beforeXml = typeof beforeTwips === "number" && beforeTwips > 0 ? buildSpacerParagraphXml(beforeTwips) : "";
|
|
1050
|
+
const afterXml = typeof afterTwips === "number" && afterTwips > 0 ? buildSpacerParagraphXml(afterTwips) : "";
|
|
1051
|
+
if (beforeXml) out.push(beforeXml);
|
|
1052
|
+
collectDivBlocks(node, out, result, ctx);
|
|
1053
|
+
if (afterXml) out.push(afterXml);
|
|
1054
|
+
return;
|
|
1055
|
+
}
|
|
1056
|
+
collectDivBlocks(node, out, result, ctx);
|
|
1057
|
+
return;
|
|
1058
|
+
}
|
|
1059
|
+
collectDivBlocks(node, out, result, ctx);
|
|
892
1060
|
return;
|
|
893
1061
|
}
|
|
894
1062
|
}
|
|
895
|
-
for (const c of node.children ?? []) collectBodyBlocks(c, out, result);
|
|
1063
|
+
for (const c of node.children ?? []) collectBodyBlocks(c, out, result, ctx);
|
|
896
1064
|
}
|
|
897
1065
|
function textToWordBodyXml(text) {
|
|
898
1066
|
const normalized = text.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
|
|
@@ -912,6 +1080,20 @@ function textToWordBodyXml(text) {
|
|
|
912
1080
|
}
|
|
913
1081
|
function htmlToWordBody(html) {
|
|
914
1082
|
const normalized = html.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
|
|
1083
|
+
const bodyCss = parseCssRuleFromHtmlFirst(normalized, "body");
|
|
1084
|
+
const pCss = parseCssRuleFromHtmlFirst(normalized, "p");
|
|
1085
|
+
const defaultBaseFontHalfPoints = parseFontSizeToHalfPoints(bodyCss["font-size"]) ?? 28;
|
|
1086
|
+
const defaultBodyCss = {};
|
|
1087
|
+
if (bodyCss["line-height"]) defaultBodyCss["line-height"] = bodyCss["line-height"];
|
|
1088
|
+
const defaultPCss = {};
|
|
1089
|
+
if (pCss["line-height"]) defaultPCss["line-height"] = pCss["line-height"];
|
|
1090
|
+
if (pCss["margin-top"]) defaultPCss["margin-top"] = pCss["margin-top"];
|
|
1091
|
+
if (pCss["margin-bottom"]) defaultPCss["margin-bottom"] = pCss["margin-bottom"];
|
|
1092
|
+
if (pCss["text-align"]) defaultPCss["text-align"] = pCss["text-align"];
|
|
1093
|
+
const tableWrapperCss = {
|
|
1094
|
+
...parseCssRuleFromHtml(normalized, ".tableWrapper"),
|
|
1095
|
+
...parseCssRuleFromHtml(normalized, ".tiptap .tableWrapper")
|
|
1096
|
+
};
|
|
915
1097
|
const doc = parseDocument(normalized, {
|
|
916
1098
|
lowerCaseAttributeNames: true,
|
|
917
1099
|
lowerCaseTags: true,
|
|
@@ -919,7 +1101,8 @@ function htmlToWordBody(html) {
|
|
|
919
1101
|
});
|
|
920
1102
|
const result = { bodyXml: "", images: [] };
|
|
921
1103
|
const out = [];
|
|
922
|
-
|
|
1104
|
+
const ctx = { defaultBaseFontHalfPoints, defaultBodyCss, defaultPCss, tableWrapperCss };
|
|
1105
|
+
collectBodyBlocks(doc, out, result, ctx);
|
|
923
1106
|
result.bodyXml = out.join("");
|
|
924
1107
|
return result;
|
|
925
1108
|
}
|
|
@@ -956,4 +1139,4 @@ export {
|
|
|
956
1139
|
htmlToWordBodyXml,
|
|
957
1140
|
textToWordBodyXml
|
|
958
1141
|
};
|
|
959
|
-
//# sourceMappingURL=htmlToWordBodyXml-
|
|
1142
|
+
//# sourceMappingURL=htmlToWordBodyXml-JSGDLGOS.js.map
|