@cj-tech-master/excelts 9.6.1 → 10.0.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/README.md +18 -3
- package/README_zh.md +18 -3
- package/dist/browser/modules/excel/cell.d.ts +4 -0
- package/dist/browser/modules/excel/note.js +5 -1
- package/dist/browser/modules/excel/row.js +35 -2
- package/dist/browser/modules/excel/stream/workbook-writer.browser.d.ts +8 -1
- package/dist/browser/modules/excel/stream/workbook-writer.browser.js +22 -2
- package/dist/browser/modules/excel/types.d.ts +81 -0
- package/dist/browser/modules/excel/utils/drawing-utils.d.ts +8 -0
- package/dist/browser/modules/excel/utils/drawing-utils.js +19 -2
- package/dist/browser/modules/excel/workbook.browser.d.ts +16 -0
- package/dist/browser/modules/excel/workbook.browser.js +32 -2
- package/dist/browser/modules/excel/worksheet.d.ts +31 -1
- package/dist/browser/modules/excel/worksheet.js +83 -0
- package/dist/browser/modules/excel/xlsx/xform/comment/vml-shape-xform.d.ts +7 -0
- package/dist/browser/modules/excel/xlsx/xform/comment/vml-shape-xform.js +42 -8
- package/dist/browser/modules/excel/xlsx/xform/core/content-types-xform.js +3 -1
- package/dist/browser/modules/excel/xlsx/xform/drawing/absolute-anchor-xform.js +5 -0
- package/dist/browser/modules/excel/xlsx/xform/drawing/base-cell-anchor-xform.js +18 -1
- package/dist/browser/modules/excel/xlsx/xform/drawing/blip-xform.d.ts +6 -0
- package/dist/browser/modules/excel/xlsx/xform/drawing/blip-xform.js +38 -11
- package/dist/browser/modules/excel/xlsx/xform/drawing/one-cell-anchor-xform.d.ts +1 -0
- package/dist/browser/modules/excel/xlsx/xform/drawing/one-cell-anchor-xform.js +5 -0
- package/dist/browser/modules/excel/xlsx/xform/drawing/pic-xform.d.ts +2 -0
- package/dist/browser/modules/excel/xlsx/xform/drawing/pic-xform.js +2 -1
- package/dist/browser/modules/excel/xlsx/xform/drawing/shape-xform.d.ts +47 -0
- package/dist/browser/modules/excel/xlsx/xform/drawing/shape-xform.js +109 -0
- package/dist/browser/modules/excel/xlsx/xform/drawing/two-cell-anchor-xform.js +10 -1
- package/dist/browser/modules/excel/xlsx/xform/sheet/worksheet-xform.js +64 -1
- package/dist/browser/modules/pdf/builder/document-builder.js +22 -49
- package/dist/browser/modules/pdf/builder/pdf-editor.js +1 -1
- package/dist/browser/modules/pdf/core/pdf-stream.d.ts +28 -1
- package/dist/browser/modules/pdf/core/pdf-stream.js +38 -2
- package/dist/browser/modules/pdf/font/font-manager.d.ts +26 -0
- package/dist/browser/modules/pdf/font/font-manager.js +35 -18
- package/dist/browser/modules/pdf/render/page-renderer.d.ts +51 -3
- package/dist/browser/modules/pdf/render/page-renderer.js +111 -18
- package/dist/browser/modules/word/advanced/field-engine.js +45 -20
- package/dist/browser/modules/word/advanced/glossary.d.ts +10 -36
- package/dist/browser/modules/word/advanced/glossary.js +8 -9
- package/dist/browser/modules/word/advanced/math-convert.js +94 -12
- package/dist/browser/modules/word/advanced/ole-objects.d.ts +28 -0
- package/dist/browser/modules/word/advanced/ole-objects.js +122 -19
- package/dist/browser/modules/word/advanced/style-map.js +31 -10
- package/dist/browser/modules/word/builder/run-builders.d.ts +7 -1
- package/dist/browser/modules/word/builder/run-builders.js +7 -1
- package/dist/browser/modules/word/constants.d.ts +4 -0
- package/dist/browser/modules/word/constants.js +5 -1
- package/dist/browser/modules/word/convert/docx-to-semantic.d.ts +2 -1
- package/dist/browser/modules/word/convert/docx-to-semantic.js +135 -1
- package/dist/browser/modules/word/convert/html/html-import.d.ts +32 -1
- package/dist/browser/modules/word/convert/html/html-import.js +167 -14
- package/dist/browser/modules/word/convert/html/html.d.ts +2 -2
- package/dist/browser/modules/word/convert/html/html.js +1 -1
- package/dist/browser/modules/word/convert/markdown/markdown-import.d.ts +48 -18
- package/dist/browser/modules/word/convert/markdown/markdown-import.js +279 -69
- package/dist/browser/modules/word/convert/markdown/markdown.d.ts +1 -1
- package/dist/browser/modules/word/convert/odt/odt.js +407 -56
- package/dist/browser/modules/word/html.d.ts +2 -2
- package/dist/browser/modules/word/html.js +1 -1
- package/dist/browser/modules/word/index.base.d.ts +3 -3
- package/dist/browser/modules/word/index.base.js +1 -1
- package/dist/browser/modules/word/layout/layout-full.js +326 -19
- package/dist/browser/modules/word/layout/render-page.js +35 -8
- package/dist/browser/modules/word/markdown.d.ts +1 -1
- package/dist/browser/modules/word/query/compat.d.ts +10 -2
- package/dist/browser/modules/word/query/compat.js +29 -21
- package/dist/browser/modules/word/reader/docx-reader.js +105 -2
- package/dist/browser/modules/word/reader/math-parser.js +8 -2
- package/dist/browser/modules/word/security/cfb-reader.js +5 -5
- package/dist/browser/modules/word/types.d.ts +96 -1
- package/dist/browser/modules/word/writer/docx-packager.js +108 -2
- package/dist/browser/modules/word/writer/glossary-writer.d.ts +28 -0
- package/dist/browser/modules/word/writer/glossary-writer.js +121 -0
- package/dist/browser/modules/word/writer/header-footer-writer.js +105 -20
- package/dist/browser/modules/word/writer/math-writer.js +7 -2
- package/dist/browser/utils/font-metrics.d.ts +8 -0
- package/dist/browser/utils/font-metrics.js +43 -0
- package/dist/browser/utils/theme-colors.js +4 -1
- package/dist/cjs/modules/excel/note.js +5 -1
- package/dist/cjs/modules/excel/row.js +35 -2
- package/dist/cjs/modules/excel/stream/workbook-writer.browser.js +22 -2
- package/dist/cjs/modules/excel/utils/drawing-utils.js +19 -2
- package/dist/cjs/modules/excel/workbook.browser.js +31 -1
- package/dist/cjs/modules/excel/worksheet.js +83 -0
- package/dist/cjs/modules/excel/xlsx/xform/comment/vml-shape-xform.js +42 -8
- package/dist/cjs/modules/excel/xlsx/xform/core/content-types-xform.js +3 -1
- package/dist/cjs/modules/excel/xlsx/xform/drawing/absolute-anchor-xform.js +5 -0
- package/dist/cjs/modules/excel/xlsx/xform/drawing/base-cell-anchor-xform.js +18 -1
- package/dist/cjs/modules/excel/xlsx/xform/drawing/blip-xform.js +38 -11
- package/dist/cjs/modules/excel/xlsx/xform/drawing/one-cell-anchor-xform.js +5 -0
- package/dist/cjs/modules/excel/xlsx/xform/drawing/pic-xform.js +2 -1
- package/dist/cjs/modules/excel/xlsx/xform/drawing/shape-xform.js +112 -0
- package/dist/cjs/modules/excel/xlsx/xform/drawing/two-cell-anchor-xform.js +10 -1
- package/dist/cjs/modules/excel/xlsx/xform/sheet/worksheet-xform.js +64 -1
- package/dist/cjs/modules/pdf/builder/document-builder.js +21 -48
- package/dist/cjs/modules/pdf/builder/pdf-editor.js +1 -1
- package/dist/cjs/modules/pdf/core/pdf-stream.js +38 -2
- package/dist/cjs/modules/pdf/font/font-manager.js +35 -18
- package/dist/cjs/modules/pdf/render/page-renderer.js +112 -18
- package/dist/cjs/modules/word/advanced/field-engine.js +45 -20
- package/dist/cjs/modules/word/advanced/glossary.js +8 -9
- package/dist/cjs/modules/word/advanced/math-convert.js +94 -12
- package/dist/cjs/modules/word/advanced/ole-objects.js +123 -19
- package/dist/cjs/modules/word/advanced/style-map.js +31 -10
- package/dist/cjs/modules/word/builder/run-builders.js +7 -1
- package/dist/cjs/modules/word/constants.js +5 -1
- package/dist/cjs/modules/word/convert/docx-to-semantic.js +135 -1
- package/dist/cjs/modules/word/convert/html/html-import.js +168 -14
- package/dist/cjs/modules/word/convert/html/html.js +2 -1
- package/dist/cjs/modules/word/convert/markdown/markdown-import.js +279 -69
- package/dist/cjs/modules/word/convert/odt/odt.js +407 -56
- package/dist/cjs/modules/word/html.js +2 -1
- package/dist/cjs/modules/word/index.base.js +4 -3
- package/dist/cjs/modules/word/layout/layout-full.js +325 -18
- package/dist/cjs/modules/word/layout/render-page.js +35 -8
- package/dist/cjs/modules/word/query/compat.js +29 -21
- package/dist/cjs/modules/word/reader/docx-reader.js +104 -1
- package/dist/cjs/modules/word/reader/math-parser.js +8 -2
- package/dist/cjs/modules/word/security/cfb-reader.js +5 -5
- package/dist/cjs/modules/word/writer/docx-packager.js +108 -2
- package/dist/cjs/modules/word/writer/glossary-writer.js +124 -0
- package/dist/cjs/modules/word/writer/header-footer-writer.js +105 -20
- package/dist/cjs/modules/word/writer/math-writer.js +7 -2
- package/dist/cjs/utils/font-metrics.js +44 -0
- package/dist/cjs/utils/theme-colors.js +4 -1
- package/dist/esm/modules/excel/note.js +5 -1
- package/dist/esm/modules/excel/row.js +35 -2
- package/dist/esm/modules/excel/stream/workbook-writer.browser.js +22 -2
- package/dist/esm/modules/excel/utils/drawing-utils.js +19 -2
- package/dist/esm/modules/excel/workbook.browser.js +32 -2
- package/dist/esm/modules/excel/worksheet.js +83 -0
- package/dist/esm/modules/excel/xlsx/xform/comment/vml-shape-xform.js +42 -8
- package/dist/esm/modules/excel/xlsx/xform/core/content-types-xform.js +3 -1
- package/dist/esm/modules/excel/xlsx/xform/drawing/absolute-anchor-xform.js +5 -0
- package/dist/esm/modules/excel/xlsx/xform/drawing/base-cell-anchor-xform.js +18 -1
- package/dist/esm/modules/excel/xlsx/xform/drawing/blip-xform.js +38 -11
- package/dist/esm/modules/excel/xlsx/xform/drawing/one-cell-anchor-xform.js +5 -0
- package/dist/esm/modules/excel/xlsx/xform/drawing/pic-xform.js +2 -1
- package/dist/esm/modules/excel/xlsx/xform/drawing/shape-xform.js +109 -0
- package/dist/esm/modules/excel/xlsx/xform/drawing/two-cell-anchor-xform.js +10 -1
- package/dist/esm/modules/excel/xlsx/xform/sheet/worksheet-xform.js +64 -1
- package/dist/esm/modules/pdf/builder/document-builder.js +22 -49
- package/dist/esm/modules/pdf/builder/pdf-editor.js +1 -1
- package/dist/esm/modules/pdf/core/pdf-stream.js +38 -2
- package/dist/esm/modules/pdf/font/font-manager.js +35 -18
- package/dist/esm/modules/pdf/render/page-renderer.js +111 -18
- package/dist/esm/modules/word/advanced/field-engine.js +45 -20
- package/dist/esm/modules/word/advanced/glossary.js +8 -9
- package/dist/esm/modules/word/advanced/math-convert.js +94 -12
- package/dist/esm/modules/word/advanced/ole-objects.js +122 -19
- package/dist/esm/modules/word/advanced/style-map.js +31 -10
- package/dist/esm/modules/word/builder/run-builders.js +7 -1
- package/dist/esm/modules/word/constants.js +5 -1
- package/dist/esm/modules/word/convert/docx-to-semantic.js +135 -1
- package/dist/esm/modules/word/convert/html/html-import.js +167 -14
- package/dist/esm/modules/word/convert/html/html.js +1 -1
- package/dist/esm/modules/word/convert/markdown/markdown-import.js +279 -69
- package/dist/esm/modules/word/convert/odt/odt.js +407 -56
- package/dist/esm/modules/word/html.js +1 -1
- package/dist/esm/modules/word/index.base.js +1 -1
- package/dist/esm/modules/word/layout/layout-full.js +326 -19
- package/dist/esm/modules/word/layout/render-page.js +35 -8
- package/dist/esm/modules/word/query/compat.js +29 -21
- package/dist/esm/modules/word/reader/docx-reader.js +105 -2
- package/dist/esm/modules/word/reader/math-parser.js +8 -2
- package/dist/esm/modules/word/security/cfb-reader.js +5 -5
- package/dist/esm/modules/word/writer/docx-packager.js +108 -2
- package/dist/esm/modules/word/writer/glossary-writer.js +121 -0
- package/dist/esm/modules/word/writer/header-footer-writer.js +105 -20
- package/dist/esm/modules/word/writer/math-writer.js +7 -2
- package/dist/esm/utils/font-metrics.js +43 -0
- package/dist/esm/utils/theme-colors.js +4 -1
- package/dist/iife/excelts.iife.js +496 -59
- package/dist/iife/excelts.iife.js.map +1 -1
- package/dist/iife/excelts.iife.min.js +39 -39
- package/dist/types/modules/excel/cell.d.ts +4 -0
- package/dist/types/modules/excel/stream/workbook-writer.browser.d.ts +8 -1
- package/dist/types/modules/excel/types.d.ts +81 -0
- package/dist/types/modules/excel/utils/drawing-utils.d.ts +8 -0
- package/dist/types/modules/excel/workbook.browser.d.ts +16 -0
- package/dist/types/modules/excel/worksheet.d.ts +31 -1
- package/dist/types/modules/excel/xlsx/xform/comment/vml-shape-xform.d.ts +7 -0
- package/dist/types/modules/excel/xlsx/xform/drawing/blip-xform.d.ts +6 -0
- package/dist/types/modules/excel/xlsx/xform/drawing/one-cell-anchor-xform.d.ts +1 -0
- package/dist/types/modules/excel/xlsx/xform/drawing/pic-xform.d.ts +2 -0
- package/dist/types/modules/excel/xlsx/xform/drawing/shape-xform.d.ts +47 -0
- package/dist/types/modules/pdf/core/pdf-stream.d.ts +28 -1
- package/dist/types/modules/pdf/font/font-manager.d.ts +26 -0
- package/dist/types/modules/pdf/render/page-renderer.d.ts +51 -3
- package/dist/types/modules/word/advanced/glossary.d.ts +10 -36
- package/dist/types/modules/word/advanced/ole-objects.d.ts +28 -0
- package/dist/types/modules/word/builder/run-builders.d.ts +7 -1
- package/dist/types/modules/word/constants.d.ts +4 -0
- package/dist/types/modules/word/convert/docx-to-semantic.d.ts +2 -1
- package/dist/types/modules/word/convert/html/html-import.d.ts +32 -1
- package/dist/types/modules/word/convert/html/html.d.ts +2 -2
- package/dist/types/modules/word/convert/markdown/markdown-import.d.ts +48 -18
- package/dist/types/modules/word/convert/markdown/markdown.d.ts +1 -1
- package/dist/types/modules/word/html.d.ts +2 -2
- package/dist/types/modules/word/index.base.d.ts +3 -3
- package/dist/types/modules/word/markdown.d.ts +1 -1
- package/dist/types/modules/word/query/compat.d.ts +10 -2
- package/dist/types/modules/word/types.d.ts +96 -1
- package/dist/types/modules/word/writer/glossary-writer.d.ts +28 -0
- package/dist/types/utils/font-metrics.d.ts +8 -0
- package/package.json +3 -1
|
@@ -422,7 +422,7 @@ function parseParagraph(el, styles, listContext) {
|
|
|
422
422
|
if (listContext) {
|
|
423
423
|
paraProps = {
|
|
424
424
|
...paraProps,
|
|
425
|
-
numbering: { numId:
|
|
425
|
+
numbering: { numId: listContext.numId, level: listContext.level }
|
|
426
426
|
};
|
|
427
427
|
}
|
|
428
428
|
const children = [];
|
|
@@ -551,8 +551,12 @@ function parseDrawFrame(el) {
|
|
|
551
551
|
};
|
|
552
552
|
}
|
|
553
553
|
/** Parse a text:list element. */
|
|
554
|
-
function parseList(el, styles, parentLevel) {
|
|
554
|
+
function parseList(el, styles, parentLevel, registry, parentNumId) {
|
|
555
555
|
const listStyleName = nsAttr(el, "text", "style-name");
|
|
556
|
+
// The outermost list determines the numId; nested lists inherit it so a
|
|
557
|
+
// single multi-level list resolves to one numbering definition. Only the
|
|
558
|
+
// top-level list (no parent numId) consults its own style name.
|
|
559
|
+
const numId = parentNumId ?? registry.numIdFor(listStyleName);
|
|
556
560
|
const paragraphs = [];
|
|
557
561
|
const level = parentLevel;
|
|
558
562
|
const items = findNsChildren(el, "text", "list-item");
|
|
@@ -563,18 +567,19 @@ function parseList(el, styles, parentLevel) {
|
|
|
563
567
|
}
|
|
564
568
|
const local = getLocalName(child.name);
|
|
565
569
|
if (local === "p" || local === "h") {
|
|
566
|
-
|
|
570
|
+
registry.noteLevel(numId, level);
|
|
571
|
+
paragraphs.push(parseParagraph(child, styles, { listStyleName, level, numId }));
|
|
567
572
|
}
|
|
568
573
|
else if (local === "list") {
|
|
569
|
-
// Nested list
|
|
570
|
-
paragraphs.push(...parseList(child, styles, level + 1));
|
|
574
|
+
// Nested list — inherit the enclosing list's numId.
|
|
575
|
+
paragraphs.push(...parseList(child, styles, level + 1, registry, numId));
|
|
571
576
|
}
|
|
572
577
|
}
|
|
573
578
|
}
|
|
574
579
|
return paragraphs;
|
|
575
580
|
}
|
|
576
581
|
/** Parse a table:table element into a Table. */
|
|
577
|
-
function parseTable(el, styles) {
|
|
582
|
+
function parseTable(el, styles, registry) {
|
|
578
583
|
const tableStyleName = nsAttr(el, "table", "style-name");
|
|
579
584
|
const tableStyle = tableStyleName ? styles.get(tableStyleName) : undefined;
|
|
580
585
|
// Parse column definitions for widths
|
|
@@ -625,10 +630,10 @@ function parseTable(el, styles) {
|
|
|
625
630
|
cellContent.push(parseParagraph(cellChild, styles));
|
|
626
631
|
}
|
|
627
632
|
else if (cellChildLocal === "table") {
|
|
628
|
-
cellContent.push(parseTable(cellChild, styles));
|
|
633
|
+
cellContent.push(parseTable(cellChild, styles, registry));
|
|
629
634
|
}
|
|
630
635
|
else if (cellChildLocal === "list") {
|
|
631
|
-
cellContent.push(...parseList(cellChild, styles, 0));
|
|
636
|
+
cellContent.push(...parseList(cellChild, styles, 0, registry));
|
|
632
637
|
}
|
|
633
638
|
}
|
|
634
639
|
// Ensure at least one paragraph per cell
|
|
@@ -659,7 +664,7 @@ function parseTable(el, styles) {
|
|
|
659
664
|
};
|
|
660
665
|
}
|
|
661
666
|
/** Parse document body content from the office:body/office:text element. */
|
|
662
|
-
function parseDocumentBody(bodyEl, styles) {
|
|
667
|
+
function parseDocumentBody(bodyEl, styles, registry) {
|
|
663
668
|
const content = [];
|
|
664
669
|
for (const child of bodyEl.children) {
|
|
665
670
|
if (child.type !== "element") {
|
|
@@ -670,14 +675,14 @@ function parseDocumentBody(bodyEl, styles) {
|
|
|
670
675
|
content.push(parseParagraph(child, styles));
|
|
671
676
|
}
|
|
672
677
|
else if (local === "table") {
|
|
673
|
-
content.push(parseTable(child, styles));
|
|
678
|
+
content.push(parseTable(child, styles, registry));
|
|
674
679
|
}
|
|
675
680
|
else if (local === "list") {
|
|
676
|
-
content.push(...parseList(child, styles, 0));
|
|
681
|
+
content.push(...parseList(child, styles, 0, registry));
|
|
677
682
|
}
|
|
678
683
|
else if (local === "section") {
|
|
679
684
|
// Sections in ODF are logical containers; flatten their content
|
|
680
|
-
content.push(...parseDocumentBody(child, styles));
|
|
685
|
+
content.push(...parseDocumentBody(child, styles, registry));
|
|
681
686
|
}
|
|
682
687
|
}
|
|
683
688
|
return content;
|
|
@@ -815,59 +820,248 @@ function convertStylesToStyleDefs(styles) {
|
|
|
815
820
|
return defs;
|
|
816
821
|
}
|
|
817
822
|
// =============================================================================
|
|
818
|
-
// ODT list numbering
|
|
823
|
+
// ODT list numbering definitions (bullet & numbered, multi-level)
|
|
819
824
|
// =============================================================================
|
|
825
|
+
/** Bullet glyphs per nesting level, cycling every three levels. */
|
|
826
|
+
const ODT_BULLET_CHARS = ["•", "◦", "▪", "•", "◦", "▪", "•", "◦", "▪"];
|
|
827
|
+
/** Default ODF `style:num-format` value emitted for each docx NumberFormat. */
|
|
828
|
+
function numberFormatToOdf(format) {
|
|
829
|
+
switch (format) {
|
|
830
|
+
case "decimal":
|
|
831
|
+
case "decimalZero":
|
|
832
|
+
return "1";
|
|
833
|
+
case "lowerLetter":
|
|
834
|
+
return "a";
|
|
835
|
+
case "upperLetter":
|
|
836
|
+
return "A";
|
|
837
|
+
case "lowerRoman":
|
|
838
|
+
return "i";
|
|
839
|
+
case "upperRoman":
|
|
840
|
+
return "I";
|
|
841
|
+
default:
|
|
842
|
+
// ODF has no equivalent for many Word-specific formats; fall back to
|
|
843
|
+
// decimal so the list still renders as an ordered list (its ordered
|
|
844
|
+
// nature is preserved even when the exact glyph set is not).
|
|
845
|
+
return "1";
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
/** Map an ODF `style:num-format` value to the closest docx NumberFormat. */
|
|
849
|
+
function odfNumFormatToDocx(numFormat) {
|
|
850
|
+
switch (numFormat) {
|
|
851
|
+
case "1":
|
|
852
|
+
return "decimal";
|
|
853
|
+
case "a":
|
|
854
|
+
return "lowerLetter";
|
|
855
|
+
case "A":
|
|
856
|
+
return "upperLetter";
|
|
857
|
+
case "i":
|
|
858
|
+
return "lowerRoman";
|
|
859
|
+
case "I":
|
|
860
|
+
return "upperRoman";
|
|
861
|
+
default:
|
|
862
|
+
return "decimal";
|
|
863
|
+
}
|
|
864
|
+
}
|
|
820
865
|
/**
|
|
821
|
-
*
|
|
822
|
-
* synthesize the corresponding `abstractNumberings` and `numberingInstances`
|
|
823
|
-
* so Word renders bullet/number markers. Without this the list paragraphs
|
|
824
|
-
* carry a `numId` that resolves to nothing in the produced document and the
|
|
825
|
-
* markers are invisible.
|
|
866
|
+
* Parse a single `text:list-style` element into its per-level formats.
|
|
826
867
|
*
|
|
827
|
-
*
|
|
828
|
-
* `
|
|
829
|
-
*
|
|
830
|
-
*
|
|
868
|
+
* Each `text:list-level-style-bullet` becomes a bullet level; each
|
|
869
|
+
* `text:list-level-style-number` becomes a numbered level whose docx format
|
|
870
|
+
* is derived from `style:num-format`. Levels are indexed by `text:level`
|
|
871
|
+
* (1-based in ODF) and stored 0-based.
|
|
831
872
|
*/
|
|
832
|
-
function
|
|
833
|
-
|
|
834
|
-
|
|
873
|
+
function parseOdfListStyle(el) {
|
|
874
|
+
const levels = new Map();
|
|
875
|
+
for (const child of el.children) {
|
|
876
|
+
if (child.type !== "element") {
|
|
877
|
+
continue;
|
|
878
|
+
}
|
|
879
|
+
const local = getLocalName(child.name);
|
|
880
|
+
const levelAttr = parseInt(nsAttr(child, "text", "level") ?? "1", 10);
|
|
881
|
+
const level = Math.max(0, levelAttr - 1);
|
|
882
|
+
if (local === "list-level-style-bullet") {
|
|
883
|
+
levels.set(level, {
|
|
884
|
+
format: "bullet",
|
|
885
|
+
bulletChar: nsAttr(child, "text", "bullet-char") ?? ODT_BULLET_CHARS[level] ?? "•"
|
|
886
|
+
});
|
|
887
|
+
}
|
|
888
|
+
else if (local === "list-level-style-number") {
|
|
889
|
+
levels.set(level, {
|
|
890
|
+
format: odfNumFormatToDocx(nsAttr(child, "style", "num-format"))
|
|
891
|
+
});
|
|
892
|
+
}
|
|
835
893
|
}
|
|
894
|
+
return levels;
|
|
895
|
+
}
|
|
896
|
+
/**
|
|
897
|
+
* Index every `text:list-style` found in the ODF automatic/named styles by
|
|
898
|
+
* name. Used by the reader to recover the original bullet-vs-number format of
|
|
899
|
+
* each list so the produced DOCX renders the correct markers.
|
|
900
|
+
*/
|
|
901
|
+
function collectOdfListStyles(roots) {
|
|
902
|
+
const out = new Map();
|
|
903
|
+
const visit = (el) => {
|
|
904
|
+
for (const child of el.children) {
|
|
905
|
+
if (child.type !== "element") {
|
|
906
|
+
continue;
|
|
907
|
+
}
|
|
908
|
+
if (getLocalName(child.name) === "list-style") {
|
|
909
|
+
const name = nsAttr(child, "style", "name");
|
|
910
|
+
if (name) {
|
|
911
|
+
out.set(name, parseOdfListStyle(child));
|
|
912
|
+
}
|
|
913
|
+
}
|
|
914
|
+
else {
|
|
915
|
+
visit(child);
|
|
916
|
+
}
|
|
917
|
+
}
|
|
918
|
+
};
|
|
919
|
+
for (const root of roots) {
|
|
920
|
+
visit(root);
|
|
921
|
+
}
|
|
922
|
+
return out;
|
|
923
|
+
}
|
|
924
|
+
/**
|
|
925
|
+
* Registry that assigns a stable `numId` to each distinct ODF list-style name
|
|
926
|
+
* encountered while parsing the body, and produces the matching docx
|
|
927
|
+
* `abstractNumberings` / `numberingInstances` on demand.
|
|
928
|
+
*
|
|
929
|
+
* A registry instance is threaded through `parseList` → `parseParagraph` so
|
|
930
|
+
* list paragraphs reference the same `numId` that ultimately resolves to the
|
|
931
|
+
* synthesized numbering definition. Lists without an explicit style name (or
|
|
932
|
+
* referencing an unknown style) fall back to a default bulleted definition.
|
|
933
|
+
*/
|
|
934
|
+
class OdtNumberingRegistry {
|
|
935
|
+
constructor(listStyles) {
|
|
936
|
+
this.numIdByStyle = new Map();
|
|
937
|
+
// The format chosen at each level for a given numId (level 0..8).
|
|
938
|
+
this.levelsByNumId = new Map();
|
|
939
|
+
this.nextNumId = 1;
|
|
940
|
+
this.listStyles = listStyles;
|
|
941
|
+
}
|
|
942
|
+
/** Resolve (and lazily register) the numId for a given list-style name. */
|
|
943
|
+
numIdFor(styleName) {
|
|
944
|
+
const key = styleName ?? "";
|
|
945
|
+
const existing = this.numIdByStyle.get(key);
|
|
946
|
+
if (existing !== undefined) {
|
|
947
|
+
return existing;
|
|
948
|
+
}
|
|
949
|
+
const numId = this.nextNumId++;
|
|
950
|
+
this.numIdByStyle.set(key, numId);
|
|
951
|
+
this.levelsByNumId.set(numId, this.listStyles.get(key) ?? new Map());
|
|
952
|
+
return numId;
|
|
953
|
+
}
|
|
954
|
+
/**
|
|
955
|
+
* Record the level a paragraph used for this numId so the synthesized
|
|
956
|
+
* abstract numbering covers at least the deepest level actually referenced
|
|
957
|
+
* even when the ODF list-style omitted some levels.
|
|
958
|
+
*/
|
|
959
|
+
noteLevel(numId, level) {
|
|
960
|
+
let levels = this.levelsByNumId.get(numId);
|
|
961
|
+
if (!levels) {
|
|
962
|
+
levels = new Map();
|
|
963
|
+
this.levelsByNumId.set(numId, levels);
|
|
964
|
+
}
|
|
965
|
+
if (!levels.has(level)) {
|
|
966
|
+
// Unknown level: default to bullet so the marker is still visible.
|
|
967
|
+
levels.set(level, { format: "bullet", bulletChar: ODT_BULLET_CHARS[level] ?? "•" });
|
|
968
|
+
}
|
|
969
|
+
}
|
|
970
|
+
/** Whether any list paragraph was registered. */
|
|
971
|
+
get isEmpty() {
|
|
972
|
+
return this.numIdByStyle.size === 0;
|
|
973
|
+
}
|
|
974
|
+
/** Build the docx numbering definitions for every registered list. */
|
|
975
|
+
build() {
|
|
976
|
+
if (this.isEmpty) {
|
|
977
|
+
return {};
|
|
978
|
+
}
|
|
979
|
+
const abstractNumberings = [];
|
|
980
|
+
const numberingInstances = [];
|
|
981
|
+
for (const numId of this.levelsByNumId.keys()) {
|
|
982
|
+
const odfLevels = this.levelsByNumId.get(numId) ?? new Map();
|
|
983
|
+
const levels = [];
|
|
984
|
+
for (let level = 0; level < 9; level++) {
|
|
985
|
+
const info = odfLevels.get(level) ??
|
|
986
|
+
{ format: "bullet", bulletChar: ODT_BULLET_CHARS[level] ?? "•" };
|
|
987
|
+
levels.push(makeNumberingLevel(level, info));
|
|
988
|
+
}
|
|
989
|
+
// abstractNumId mirrors numId for a clean 1:1 mapping.
|
|
990
|
+
abstractNumberings.push({ abstractNumId: numId, levels });
|
|
991
|
+
numberingInstances.push({ numId, abstractNumId: numId });
|
|
992
|
+
}
|
|
993
|
+
return { abstractNumberings, numberingInstances };
|
|
994
|
+
}
|
|
995
|
+
}
|
|
996
|
+
/** Build a single docx NumberingLevel from a parsed ODF level. */
|
|
997
|
+
function makeNumberingLevel(level, info) {
|
|
836
998
|
const tabSuffix = "tab";
|
|
837
|
-
const
|
|
838
|
-
|
|
999
|
+
const indent = { left: 720 * (level + 1), hanging: 360 };
|
|
1000
|
+
if (info.format === "bullet") {
|
|
1001
|
+
return {
|
|
1002
|
+
level,
|
|
1003
|
+
format: "bullet",
|
|
1004
|
+
text: info.bulletChar ?? ODT_BULLET_CHARS[level] ?? "•",
|
|
1005
|
+
start: 1,
|
|
1006
|
+
paragraphProperties: { indent },
|
|
1007
|
+
suffix: tabSuffix
|
|
1008
|
+
};
|
|
1009
|
+
}
|
|
1010
|
+
return {
|
|
839
1011
|
level,
|
|
840
|
-
format:
|
|
841
|
-
text
|
|
1012
|
+
format: info.format,
|
|
1013
|
+
// Word level text uses %N placeholders; "%<level+1>." renders "1." etc.
|
|
1014
|
+
text: `%${level + 1}.`,
|
|
842
1015
|
start: 1,
|
|
843
|
-
paragraphProperties: {
|
|
844
|
-
indent: { left: 720 * (level + 1), hanging: 360 }
|
|
845
|
-
},
|
|
1016
|
+
paragraphProperties: { indent },
|
|
846
1017
|
suffix: tabSuffix
|
|
847
|
-
}));
|
|
848
|
-
return {
|
|
849
|
-
abstractNumberings: [{ abstractNumId: 1, levels }],
|
|
850
|
-
numberingInstances: [{ numId: 1, abstractNumId: 1 }]
|
|
851
1018
|
};
|
|
852
1019
|
}
|
|
853
|
-
|
|
1020
|
+
// =============================================================================
|
|
1021
|
+
// Writer-side list numbering helpers
|
|
1022
|
+
// =============================================================================
|
|
1023
|
+
/** Deterministic ODF `text:list-style` name for a given docx numId. */
|
|
1024
|
+
function listStyleNameForNumId(numId) {
|
|
1025
|
+
return `L${numId}`;
|
|
1026
|
+
}
|
|
1027
|
+
/** Collect every distinct numId referenced by list paragraphs in the body. */
|
|
1028
|
+
function collectListNumIds(blocks, out) {
|
|
854
1029
|
for (const block of blocks) {
|
|
855
1030
|
if (block.type === "paragraph") {
|
|
856
|
-
|
|
857
|
-
|
|
1031
|
+
const numId = block.properties?.numbering?.numId;
|
|
1032
|
+
if (typeof numId === "number") {
|
|
1033
|
+
out.add(numId);
|
|
858
1034
|
}
|
|
859
1035
|
}
|
|
860
1036
|
else if (block.type === "table") {
|
|
861
1037
|
for (const row of block.rows) {
|
|
862
1038
|
for (const cell of row.cells) {
|
|
863
|
-
|
|
864
|
-
return true;
|
|
865
|
-
}
|
|
1039
|
+
collectListNumIds(cell.content, out);
|
|
866
1040
|
}
|
|
867
1041
|
}
|
|
868
1042
|
}
|
|
869
1043
|
}
|
|
870
|
-
|
|
1044
|
+
}
|
|
1045
|
+
/**
|
|
1046
|
+
* Resolve the per-level NumberFormat for a numId from the document's numbering
|
|
1047
|
+
* definitions. Follows numId → numberingInstance → abstractNumbering → levels.
|
|
1048
|
+
* Levels missing a definition default to bullet so a marker is still emitted.
|
|
1049
|
+
*/
|
|
1050
|
+
function resolveNumIdLevelFormats(doc, numId) {
|
|
1051
|
+
const formats = new Array(9).fill("bullet");
|
|
1052
|
+
const inst = doc.numberingInstances?.find(n => n.numId === numId);
|
|
1053
|
+
const abstractId = inst?.abstractNumId;
|
|
1054
|
+
const abstract = abstractId !== undefined
|
|
1055
|
+
? doc.abstractNumberings?.find(a => a.abstractNumId === abstractId)
|
|
1056
|
+
: undefined;
|
|
1057
|
+
if (abstract) {
|
|
1058
|
+
for (const lvl of abstract.levels) {
|
|
1059
|
+
if (lvl.level >= 0 && lvl.level < 9) {
|
|
1060
|
+
formats[lvl.level] = lvl.format;
|
|
1061
|
+
}
|
|
1062
|
+
}
|
|
1063
|
+
}
|
|
1064
|
+
return formats;
|
|
871
1065
|
}
|
|
872
1066
|
// =============================================================================
|
|
873
1067
|
// readOdt — Main Entry Point
|
|
@@ -916,10 +1110,12 @@ export async function readOdt(buffer) {
|
|
|
916
1110
|
// Collect all styles (from both content.xml and styles.xml)
|
|
917
1111
|
const allStyles = new Map();
|
|
918
1112
|
let pageLayoutProps;
|
|
1113
|
+
let stylesRootEl;
|
|
919
1114
|
// Parse styles from styles.xml
|
|
920
1115
|
if (stylesXml) {
|
|
921
1116
|
const stylesDoc = parseXml(stylesXml);
|
|
922
1117
|
const stylesRoot = stylesDoc.root;
|
|
1118
|
+
stylesRootEl = stylesRoot;
|
|
923
1119
|
// Named styles (office:styles)
|
|
924
1120
|
const officeStylesEl = findNsChild(stylesRoot, "office", "styles");
|
|
925
1121
|
if (officeStylesEl) {
|
|
@@ -999,7 +1195,16 @@ export async function readOdt(buffer) {
|
|
|
999
1195
|
if (!textEl) {
|
|
1000
1196
|
throw new DocxParseError("Invalid ODT: missing office:text element");
|
|
1001
1197
|
}
|
|
1002
|
-
|
|
1198
|
+
// Index every `text:list-style` (from styles.xml and content.xml) so the
|
|
1199
|
+
// body parser can recover each list's bullet-vs-number format. The registry
|
|
1200
|
+
// assigns a stable numId per distinct list style and produces the matching
|
|
1201
|
+
// numbering definitions below.
|
|
1202
|
+
const listStyleRoots = [contentDoc.root];
|
|
1203
|
+
if (stylesRootEl) {
|
|
1204
|
+
listStyleRoots.push(stylesRootEl);
|
|
1205
|
+
}
|
|
1206
|
+
const registry = new OdtNumberingRegistry(collectOdfListStyles(listStyleRoots));
|
|
1207
|
+
const body = parseDocumentBody(textEl, allStyles, registry);
|
|
1003
1208
|
// Convert styles to StyleDef[]
|
|
1004
1209
|
const styleDefs = convertStylesToStyleDefs(allStyles);
|
|
1005
1210
|
// Parse section properties from page layout
|
|
@@ -1055,7 +1260,7 @@ export async function readOdt(buffer) {
|
|
|
1055
1260
|
...(styleDefs.length > 0 ? { styles: styleDefs } : {}),
|
|
1056
1261
|
...(coreProperties ? { coreProperties } : {}),
|
|
1057
1262
|
...(images.length > 0 ? { images } : {}),
|
|
1058
|
-
...
|
|
1263
|
+
...registry.build()
|
|
1059
1264
|
};
|
|
1060
1265
|
return doc;
|
|
1061
1266
|
}
|
|
@@ -1284,9 +1489,7 @@ function generateContentXml(doc, imagePaths, imageMap) {
|
|
|
1284
1489
|
const bodyWriter = new XmlWriter();
|
|
1285
1490
|
bodyWriter.openNode("office:body");
|
|
1286
1491
|
bodyWriter.openNode("office:text");
|
|
1287
|
-
|
|
1288
|
-
writeBodyContent(bodyWriter, block, doc, imagePaths, ctx);
|
|
1289
|
-
}
|
|
1492
|
+
writeBlocks(bodyWriter, doc.body, doc, imagePaths, ctx);
|
|
1290
1493
|
bodyWriter.closeNode(); // office:text
|
|
1291
1494
|
bodyWriter.closeNode(); // office:body
|
|
1292
1495
|
const bodyXml = bodyWriter.xml;
|
|
@@ -1333,6 +1536,54 @@ function writeAutoStyles(w, doc) {
|
|
|
1333
1536
|
writeStyleDef(w, styleDef);
|
|
1334
1537
|
}
|
|
1335
1538
|
}
|
|
1539
|
+
// Emit one `text:list-style` per distinct numId referenced by the body so
|
|
1540
|
+
// bullet vs numbered (and multi-level mixes) round-trip with the correct
|
|
1541
|
+
// markers. Numbers are sorted for deterministic output.
|
|
1542
|
+
const numIds = new Set();
|
|
1543
|
+
collectListNumIds(doc.body, numIds);
|
|
1544
|
+
for (const numId of [...numIds].sort((a, b) => a - b)) {
|
|
1545
|
+
writeListStyleDef(w, numId, resolveNumIdLevelFormats(doc, numId));
|
|
1546
|
+
}
|
|
1547
|
+
}
|
|
1548
|
+
/**
|
|
1549
|
+
* Write the automatic `text:list-style` for a numId.
|
|
1550
|
+
*
|
|
1551
|
+
* Each of the nine ODF list levels is emitted as either a
|
|
1552
|
+
* `text:list-level-style-bullet` or `text:list-level-style-number` based on
|
|
1553
|
+
* the document's per-level NumberFormat, giving compliant readers (Word,
|
|
1554
|
+
* LibreOffice) the right markers and letting the reader recover the format on
|
|
1555
|
+
* the way back in.
|
|
1556
|
+
*/
|
|
1557
|
+
function writeListStyleDef(w, numId, formats) {
|
|
1558
|
+
w.openNode("text:list-style", { "style:name": listStyleNameForNumId(numId) });
|
|
1559
|
+
for (let idx = 0; idx < 9; idx++) {
|
|
1560
|
+
const level = idx + 1; // ODF list levels are 1-based.
|
|
1561
|
+
const format = formats[idx] ?? "bullet";
|
|
1562
|
+
if (format === "bullet") {
|
|
1563
|
+
w.openNode("text:list-level-style-bullet", {
|
|
1564
|
+
"text:level": String(level),
|
|
1565
|
+
"text:bullet-char": ODT_BULLET_CHARS[idx] ?? "•"
|
|
1566
|
+
});
|
|
1567
|
+
}
|
|
1568
|
+
else {
|
|
1569
|
+
w.openNode("text:list-level-style-number", {
|
|
1570
|
+
"text:level": String(level),
|
|
1571
|
+
"style:num-format": numberFormatToOdf(format),
|
|
1572
|
+
"style:num-suffix": "."
|
|
1573
|
+
});
|
|
1574
|
+
}
|
|
1575
|
+
w.openNode("style:list-level-properties", {
|
|
1576
|
+
"text:list-level-position-and-space-mode": "label-alignment"
|
|
1577
|
+
});
|
|
1578
|
+
w.leafNode("style:list-level-label-alignment", {
|
|
1579
|
+
"text:label-followed-by": "listtab",
|
|
1580
|
+
"fo:text-indent": "-0.25in",
|
|
1581
|
+
"fo:margin-left": `${0.25 * level}in`
|
|
1582
|
+
});
|
|
1583
|
+
w.closeNode(); // style:list-level-properties
|
|
1584
|
+
w.closeNode(); // text:list-level-style-{bullet,number}
|
|
1585
|
+
}
|
|
1586
|
+
w.closeNode(); // text:list-style
|
|
1336
1587
|
}
|
|
1337
1588
|
/** Write a single StyleDef as an ODF style. */
|
|
1338
1589
|
function writeStyleDef(w, def) {
|
|
@@ -1472,6 +1723,113 @@ function alignmentToOdf(alignment) {
|
|
|
1472
1723
|
return "start";
|
|
1473
1724
|
}
|
|
1474
1725
|
}
|
|
1726
|
+
/**
|
|
1727
|
+
* Determine whether a body block is a list paragraph (carries numbering).
|
|
1728
|
+
*
|
|
1729
|
+
* The ODT reader maps list-item paragraphs back to `numbering.numId`, so the
|
|
1730
|
+
* writer must round-trip them as `text:list` structures rather than bare
|
|
1731
|
+
* `text:p` (which would silently drop the list semantics).
|
|
1732
|
+
*/
|
|
1733
|
+
function isListParagraph(block) {
|
|
1734
|
+
return block.type === "paragraph" && block.properties?.numbering !== undefined;
|
|
1735
|
+
}
|
|
1736
|
+
/** numId of a list paragraph (falls back to 1 if somehow absent). */
|
|
1737
|
+
function paragraphNumId(block) {
|
|
1738
|
+
return block.type === "paragraph" ? (block.properties?.numbering?.numId ?? 1) : 1;
|
|
1739
|
+
}
|
|
1740
|
+
/**
|
|
1741
|
+
* Write a sequence of block-level content elements, grouping runs of
|
|
1742
|
+
* consecutive list paragraphs that share a numId into nested `text:list`
|
|
1743
|
+
* structures.
|
|
1744
|
+
*
|
|
1745
|
+
* The ODF list model nests one `text:list` per indentation level, so a
|
|
1746
|
+
* paragraph at `numbering.level === N` is wrapped in `N + 1` nested lists.
|
|
1747
|
+
* Tracking an explicit level stack lets a single run contain mixed levels
|
|
1748
|
+
* (e.g. a sub-bullet between two top-level bullets) and still produce valid,
|
|
1749
|
+
* round-trippable markup that matches what `parseList` expects. Breaking on
|
|
1750
|
+
* numId changes keeps each `text:list` pointed at a single list-style.
|
|
1751
|
+
*/
|
|
1752
|
+
function writeBlocks(w, blocks, doc, imagePaths, ctx) {
|
|
1753
|
+
let i = 0;
|
|
1754
|
+
while (i < blocks.length) {
|
|
1755
|
+
const block = blocks[i];
|
|
1756
|
+
if (isListParagraph(block)) {
|
|
1757
|
+
// Consume the maximal run of consecutive list paragraphs sharing numId.
|
|
1758
|
+
const numId = paragraphNumId(block);
|
|
1759
|
+
let j = i;
|
|
1760
|
+
while (j < blocks.length &&
|
|
1761
|
+
isListParagraph(blocks[j]) &&
|
|
1762
|
+
paragraphNumId(blocks[j]) === numId) {
|
|
1763
|
+
j++;
|
|
1764
|
+
}
|
|
1765
|
+
writeListGroup(w, blocks.slice(i, j), numId, doc, imagePaths, ctx);
|
|
1766
|
+
i = j;
|
|
1767
|
+
}
|
|
1768
|
+
else {
|
|
1769
|
+
writeBodyContent(w, block, doc, imagePaths, ctx);
|
|
1770
|
+
i++;
|
|
1771
|
+
}
|
|
1772
|
+
}
|
|
1773
|
+
}
|
|
1774
|
+
/**
|
|
1775
|
+
* Emit a run of list paragraphs (all sharing `numId`) as nested `text:list`
|
|
1776
|
+
* elements.
|
|
1777
|
+
*
|
|
1778
|
+
* ODF nests one `text:list` per indentation level, and every nested list lives
|
|
1779
|
+
* inside a `text:list-item` of its parent. We therefore track, for each open
|
|
1780
|
+
* list level, whether a `text:list-item` is currently open at that level
|
|
1781
|
+
* (`itemOpen[d]`). Raising the level opens wrapper items plus nested lists;
|
|
1782
|
+
* lowering it closes them in the correct order so the markup stays balanced
|
|
1783
|
+
* and matches what `parseList` expects on the way back in.
|
|
1784
|
+
*/
|
|
1785
|
+
function writeListGroup(w, paras, numId, doc, imagePaths, ctx) {
|
|
1786
|
+
const styleName = listStyleNameForNumId(numId);
|
|
1787
|
+
// `itemOpen[d]` is true when a `text:list-item` is open inside the list at
|
|
1788
|
+
// depth `d` (0-based). Its length equals the number of open `text:list`s.
|
|
1789
|
+
const itemOpen = [];
|
|
1790
|
+
const openList = () => {
|
|
1791
|
+
w.openNode("text:list", { "text:style-name": styleName });
|
|
1792
|
+
itemOpen.push(false);
|
|
1793
|
+
};
|
|
1794
|
+
const closeList = () => {
|
|
1795
|
+
const depth = itemOpen.length - 1;
|
|
1796
|
+
if (itemOpen[depth]) {
|
|
1797
|
+
w.closeNode(); // text:list-item
|
|
1798
|
+
}
|
|
1799
|
+
itemOpen.pop();
|
|
1800
|
+
w.closeNode(); // text:list
|
|
1801
|
+
};
|
|
1802
|
+
for (const para of paras) {
|
|
1803
|
+
const level = Math.max(0, para.properties?.numbering?.level ?? 0);
|
|
1804
|
+
const wantLists = level + 1;
|
|
1805
|
+
// Close lists until we are at or below the desired depth.
|
|
1806
|
+
while (itemOpen.length > wantLists) {
|
|
1807
|
+
closeList();
|
|
1808
|
+
}
|
|
1809
|
+
// Open nested lists until we reach the desired depth. Each nested list
|
|
1810
|
+
// must sit inside an open `text:list-item` of its parent list.
|
|
1811
|
+
while (itemOpen.length < wantLists) {
|
|
1812
|
+
const depth = itemOpen.length - 1;
|
|
1813
|
+
if (depth >= 0 && !itemOpen[depth]) {
|
|
1814
|
+
w.openNode("text:list-item");
|
|
1815
|
+
itemOpen[depth] = true;
|
|
1816
|
+
}
|
|
1817
|
+
openList();
|
|
1818
|
+
}
|
|
1819
|
+
// At the target depth start a fresh list-item for this paragraph.
|
|
1820
|
+
const depth = itemOpen.length - 1;
|
|
1821
|
+
if (itemOpen[depth]) {
|
|
1822
|
+
w.closeNode(); // previous text:list-item at this depth
|
|
1823
|
+
}
|
|
1824
|
+
w.openNode("text:list-item");
|
|
1825
|
+
itemOpen[depth] = true;
|
|
1826
|
+
writeParagraph(w, para, doc, imagePaths, ctx);
|
|
1827
|
+
}
|
|
1828
|
+
// Unwind any lists still open.
|
|
1829
|
+
while (itemOpen.length > 0) {
|
|
1830
|
+
closeList();
|
|
1831
|
+
}
|
|
1832
|
+
}
|
|
1475
1833
|
/** Write a block-level content element to the XML writer. */
|
|
1476
1834
|
function writeBodyContent(w, block, doc, imagePaths, ctx) {
|
|
1477
1835
|
switch (block.type) {
|
|
@@ -1738,14 +2096,7 @@ function writeTableCell(w, cell, doc, imagePaths, ctx) {
|
|
|
1738
2096
|
return;
|
|
1739
2097
|
}
|
|
1740
2098
|
w.openNode("table:table-cell", attrs);
|
|
1741
|
-
|
|
1742
|
-
if (content.type === "paragraph") {
|
|
1743
|
-
writeParagraph(w, content, doc, imagePaths, ctx);
|
|
1744
|
-
}
|
|
1745
|
-
else if (content.type === "table") {
|
|
1746
|
-
writeTable(w, content, doc, imagePaths, ctx);
|
|
1747
|
-
}
|
|
1748
|
-
}
|
|
2099
|
+
writeBlocks(w, cell.content, doc, imagePaths, ctx);
|
|
1749
2100
|
w.closeNode();
|
|
1750
2101
|
}
|
|
1751
2102
|
// =============================================================================
|
|
@@ -98,7 +98,7 @@ export { validateDocument } from "./advanced/validation.js";
|
|
|
98
98
|
export { embedFont, embedFontFamily, addEmbeddedFonts, subsetFont } from "./font/font-embed.js";
|
|
99
99
|
// --- Experimental API ---
|
|
100
100
|
/** @stability experimental */
|
|
101
|
-
export { extractOleObjects, hasOleObjects, getOleObjectData, createOleEmbedding } from "./advanced/ole-objects.js";
|
|
101
|
+
export { extractOleObjects, hasOleObjects, getOleObjectData, createOleEmbedding, addOleObject } from "./advanced/ole-objects.js";
|
|
102
102
|
/** @stability experimental */
|
|
103
103
|
export { createBuildingBlock, createGlossaryDocument, findBuildingBlock, listBuildingBlocks, getAutoTextEntries, getQuickParts } from "./advanced/glossary.js";
|
|
104
104
|
// Document protection
|