@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
|
@@ -20,6 +20,7 @@ exports.layoutDocumentFull = layoutDocumentFull;
|
|
|
20
20
|
const font_metrics_1 = require("../../../utils/font-metrics.js");
|
|
21
21
|
const math_convert_1 = require("../advanced/math-convert");
|
|
22
22
|
const text_utils_1 = require("../core/text-utils");
|
|
23
|
+
const style_resolve_1 = require("../query/style-resolve");
|
|
23
24
|
const units_1 = require("../units");
|
|
24
25
|
const layout_1 = require("./layout");
|
|
25
26
|
const layout_constants_1 = require("./layout-constants");
|
|
@@ -33,6 +34,33 @@ const layout_constants_1 = require("./layout-constants");
|
|
|
33
34
|
function layoutDocumentFull(doc, options) {
|
|
34
35
|
// First pass: get page assignments via the existing lightweight layout
|
|
35
36
|
const layoutResult = (0, layout_1.layoutDocument)(doc, options);
|
|
37
|
+
// Resolve list markers once over the whole document so ordered-list
|
|
38
|
+
// counters increment correctly across pages. Stored in a module-level
|
|
39
|
+
// context so that every `layoutParagraph` call — including those reached
|
|
40
|
+
// through tables, text boxes, SDTs, footnotes, etc. — can render markers
|
|
41
|
+
// without threading the map through every container function. Layout runs
|
|
42
|
+
// fully synchronously (no `await`), so a single shared slot is safe.
|
|
43
|
+
const listMarkers = computeListMarkers(doc);
|
|
44
|
+
activeListMarkers = listMarkers;
|
|
45
|
+
activeDoc = doc;
|
|
46
|
+
try {
|
|
47
|
+
return layoutDocumentFullInner(doc, options, layoutResult, listMarkers);
|
|
48
|
+
}
|
|
49
|
+
finally {
|
|
50
|
+
activeListMarkers = undefined;
|
|
51
|
+
activeDoc = undefined;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
/** Active list-marker map for the in-flight layout (see layoutDocumentFull). */
|
|
55
|
+
let activeListMarkers;
|
|
56
|
+
/**
|
|
57
|
+
* Active document for the in-flight layout, so `layoutParagraph` can resolve
|
|
58
|
+
* paragraph-style run properties (size/color/font) via `resolveStyle` without
|
|
59
|
+
* threading `doc` through every container function. Layout is synchronous so a
|
|
60
|
+
* single shared slot is safe.
|
|
61
|
+
*/
|
|
62
|
+
let activeDoc;
|
|
63
|
+
function layoutDocumentFullInner(doc, options, layoutResult, listMarkers) {
|
|
36
64
|
// Second pass: compute precise positions for each page. Footnote
|
|
37
65
|
// ids that don't fit on a given page are carried over to the next
|
|
38
66
|
// (a later page may still have room thanks to less body content
|
|
@@ -41,7 +69,7 @@ function layoutDocumentFull(doc, options) {
|
|
|
41
69
|
const bodyPageCount = layoutResult.pageCount;
|
|
42
70
|
let pendingFootnoteIds = [];
|
|
43
71
|
for (let pageNum = 1; pageNum <= bodyPageCount; pageNum++) {
|
|
44
|
-
const result = buildPage(doc, pageNum, layoutResult, options, pendingFootnoteIds);
|
|
72
|
+
const result = buildPage(doc, pageNum, layoutResult, options, pendingFootnoteIds, listMarkers);
|
|
45
73
|
pages.push(result.page);
|
|
46
74
|
pendingFootnoteIds = result.deferredFootnoteIds;
|
|
47
75
|
}
|
|
@@ -55,7 +83,7 @@ function layoutDocumentFull(doc, options) {
|
|
|
55
83
|
// entries for already-placed body items still point at earlier
|
|
56
84
|
// pages, so the synthetic page won't pick up extra body
|
|
57
85
|
// content; only the carried footnote queue renders.
|
|
58
|
-
layoutResult, options, pendingFootnoteIds);
|
|
86
|
+
layoutResult, options, pendingFootnoteIds, listMarkers);
|
|
59
87
|
pages.push(overflowResult.page);
|
|
60
88
|
}
|
|
61
89
|
return {
|
|
@@ -173,7 +201,7 @@ function availableSlotForLine(ctx, lineY, lineHeight) {
|
|
|
173
201
|
function twipsToPt(twips) {
|
|
174
202
|
return twips / 20;
|
|
175
203
|
}
|
|
176
|
-
function buildPage(doc, pageNumber, layout, options, pendingFootnoteIds) {
|
|
204
|
+
function buildPage(doc, pageNumber, layout, options, pendingFootnoteIds, listMarkers) {
|
|
177
205
|
const sectionProps = doc.sectionProperties;
|
|
178
206
|
const geometry = computePageGeometry(sectionProps, options?.pageGeometry);
|
|
179
207
|
const content = [];
|
|
@@ -208,7 +236,7 @@ function buildPage(doc, pageNumber, layout, options, pendingFootnoteIds) {
|
|
|
208
236
|
};
|
|
209
237
|
switch (item.type) {
|
|
210
238
|
case "paragraph": {
|
|
211
|
-
const laid = layoutParagraph(item, cursorY, geometry.contentWidth, options, pageContext, imageMap);
|
|
239
|
+
const laid = layoutParagraph(item, cursorY, geometry.contentWidth, options, pageContext, imageMap, listMarkers);
|
|
212
240
|
content.push({ ...laid, sourceIndex: i });
|
|
213
241
|
cursorY = laid.rect.y + laid.rect.height;
|
|
214
242
|
break;
|
|
@@ -701,13 +729,219 @@ function computeSectionBreaks(layout) {
|
|
|
701
729
|
}
|
|
702
730
|
return breaks;
|
|
703
731
|
}
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
732
|
+
/**
|
|
733
|
+
* Resolve list markers for every numbered / bulleted paragraph in the
|
|
734
|
+
* document, in reading order, so ordered-list counters increment correctly
|
|
735
|
+
* across paragraphs (and reset when a lower level reappears). Returns a map
|
|
736
|
+
* keyed by the paragraph object.
|
|
737
|
+
*
|
|
738
|
+
* Markers are derived from `paragraph.properties.numbering` → the matching
|
|
739
|
+
* `NumberingInstance` → its `AbstractNumbering` level definition. Bullet
|
|
740
|
+
* levels emit their symbol; ordered levels emit a counter formatted per the
|
|
741
|
+
* level's `NumberFormat` (decimal / lower-upper letter / lower-upper roman),
|
|
742
|
+
* falling back to decimal for formats we don't render numerically.
|
|
743
|
+
*/
|
|
744
|
+
function computeListMarkers(doc) {
|
|
745
|
+
const markers = new Map();
|
|
746
|
+
const instances = doc.numberingInstances;
|
|
747
|
+
const abstracts = doc.abstractNumberings;
|
|
748
|
+
if (!instances || !abstracts || instances.length === 0 || abstracts.length === 0) {
|
|
749
|
+
return markers;
|
|
750
|
+
}
|
|
751
|
+
const instById = new Map(instances.map(n => [n.numId, n]));
|
|
752
|
+
const absById = new Map(abstracts.map(a => [a.abstractNumId, a]));
|
|
753
|
+
// Per (numId) counters, one slot per level. Counters reset at deeper
|
|
754
|
+
// levels when a shallower level advances.
|
|
755
|
+
const counters = new Map();
|
|
756
|
+
// numIds whose list was interrupted by non-list content since their last
|
|
757
|
+
// item; the next item with that numId restarts its numbering. This makes
|
|
758
|
+
// two visually separate ordered lists (sharing a numId, separated by a
|
|
759
|
+
// plain paragraph) each start at 1 — matching user expectation rather than
|
|
760
|
+
// running a single continuous sequence.
|
|
761
|
+
const interrupted = new Set();
|
|
762
|
+
// numIds seen at least once, so we know which to mark interrupted.
|
|
763
|
+
const seenNumIds = new Set();
|
|
764
|
+
// Flatten paragraphs into document reading order (descending into tables),
|
|
765
|
+
// so list continuity is judged across the whole body, not per-cell.
|
|
766
|
+
const orderedParagraphs = [];
|
|
767
|
+
const walk = (items) => {
|
|
768
|
+
for (const item of items) {
|
|
769
|
+
if (item.type === "paragraph") {
|
|
770
|
+
orderedParagraphs.push(item);
|
|
771
|
+
}
|
|
772
|
+
else if (item.type === "table") {
|
|
773
|
+
for (const row of item.rows) {
|
|
774
|
+
for (const cell of row.cells) {
|
|
775
|
+
walk(cell.content);
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
};
|
|
781
|
+
const resolveParagraphMarker = (para) => {
|
|
782
|
+
const numbering = para.properties?.numbering;
|
|
783
|
+
if (!numbering) {
|
|
784
|
+
// Non-list paragraph: any list seen so far is now interrupted, so a
|
|
785
|
+
// later paragraph reusing the same numId restarts its sequence.
|
|
786
|
+
for (const id of seenNumIds) {
|
|
787
|
+
interrupted.add(id);
|
|
788
|
+
}
|
|
789
|
+
return;
|
|
790
|
+
}
|
|
791
|
+
const inst = instById.get(numbering.numId);
|
|
792
|
+
if (!inst) {
|
|
793
|
+
return;
|
|
794
|
+
}
|
|
795
|
+
const abs = absById.get(inst.abstractNumId);
|
|
796
|
+
if (!abs) {
|
|
797
|
+
return;
|
|
798
|
+
}
|
|
799
|
+
const level = numbering.level ?? 0;
|
|
800
|
+
const levelDef = inst.overrides?.find(o => o.level === level)?.levelDef ??
|
|
801
|
+
abs.levels.find(l => l.level === level);
|
|
802
|
+
if (!levelDef) {
|
|
803
|
+
return;
|
|
804
|
+
}
|
|
805
|
+
seenNumIds.add(numbering.numId);
|
|
806
|
+
const indentPt = (level + 1) * 36; // 0.5" per level
|
|
807
|
+
if (levelDef.format === "bullet") {
|
|
808
|
+
// Bullet symbol. Word authors bullets with Symbol/Wingdings private-use
|
|
809
|
+
// code points (e.g. U+F0B7 ·, U+F0A7 ▪) that PDF standard fonts can't
|
|
810
|
+
// render. Normalize the common ones to WinAnsi-renderable equivalents;
|
|
811
|
+
// fall back to a round bullet when empty or unknown.
|
|
812
|
+
const symbol = normalizeBulletGlyph(levelDef.text);
|
|
813
|
+
markers.set(para, { text: `${symbol} `, indentPt });
|
|
814
|
+
// A bullet item does not clear the interruption flag for ordered
|
|
815
|
+
// siblings, but it is itself a list item — keep it out of `interrupted`.
|
|
816
|
+
interrupted.delete(numbering.numId);
|
|
817
|
+
return;
|
|
818
|
+
}
|
|
819
|
+
// Ordered list: advance this level's counter and reset deeper levels.
|
|
820
|
+
let levelCounts = counters.get(numbering.numId);
|
|
821
|
+
if (!levelCounts) {
|
|
822
|
+
levelCounts = [];
|
|
823
|
+
counters.set(numbering.numId, levelCounts);
|
|
824
|
+
}
|
|
825
|
+
// If this numId's run was interrupted by non-list content, restart it.
|
|
826
|
+
if (interrupted.has(numbering.numId)) {
|
|
827
|
+
levelCounts.length = 0;
|
|
828
|
+
interrupted.delete(numbering.numId);
|
|
829
|
+
}
|
|
830
|
+
const startOverride = inst.overrides?.find(o => o.level === level)?.startOverride;
|
|
831
|
+
const start = startOverride ?? levelDef.start ?? 1;
|
|
832
|
+
if (levelCounts[level] === undefined) {
|
|
833
|
+
levelCounts[level] = start;
|
|
834
|
+
}
|
|
835
|
+
else {
|
|
836
|
+
levelCounts[level] += 1;
|
|
837
|
+
}
|
|
838
|
+
// Reset any deeper levels.
|
|
839
|
+
for (let l = level + 1; l < levelCounts.length; l++) {
|
|
840
|
+
levelCounts[l] = undefined;
|
|
841
|
+
}
|
|
842
|
+
const counter = levelCounts[level];
|
|
843
|
+
const numeral = formatListCounter(counter, levelDef.format);
|
|
844
|
+
// Honour the level's `text` template (e.g. "%1.") when present; else
|
|
845
|
+
// fall back to "<n>.".
|
|
846
|
+
const text = levelDef.text ? levelDef.text.replace(/%\d+/g, numeral) : `${numeral}.`;
|
|
847
|
+
markers.set(para, { text: `${text} `, indentPt });
|
|
848
|
+
};
|
|
849
|
+
walk(doc.body);
|
|
850
|
+
for (const para of orderedParagraphs) {
|
|
851
|
+
resolveParagraphMarker(para);
|
|
852
|
+
}
|
|
853
|
+
return markers;
|
|
854
|
+
}
|
|
855
|
+
/** Normalize a Word bullet glyph to a WinAnsi-renderable equivalent. */
|
|
856
|
+
function normalizeBulletGlyph(text) {
|
|
857
|
+
if (!text || text.length === 0) {
|
|
858
|
+
return "\u2022"; // round bullet
|
|
859
|
+
}
|
|
860
|
+
const cp = text.codePointAt(0);
|
|
861
|
+
switch (cp) {
|
|
862
|
+
// Symbol-font private-use code points Word emits for default bullets.
|
|
863
|
+
case 0xf0b7: // Symbol "·" → round bullet
|
|
864
|
+
case 0x00b7: // middle dot
|
|
865
|
+
return "\u2022";
|
|
866
|
+
case 0xf0a7: // Symbol filled small square
|
|
867
|
+
case 0xf0a8:
|
|
868
|
+
return "\u25aa";
|
|
869
|
+
case 0xf0fc: // Wingdings check
|
|
870
|
+
return "\u2713";
|
|
871
|
+
default:
|
|
872
|
+
// Already a renderable glyph (e.g. "o", "-", "•") — keep it.
|
|
873
|
+
return text;
|
|
874
|
+
}
|
|
875
|
+
}
|
|
876
|
+
/** Format an ordered-list counter per its OOXML number format. */
|
|
877
|
+
function formatListCounter(n, format) {
|
|
878
|
+
switch (format) {
|
|
879
|
+
case "lowerLetter":
|
|
880
|
+
return toAlpha(n).toLowerCase();
|
|
881
|
+
case "upperLetter":
|
|
882
|
+
return toAlpha(n).toUpperCase();
|
|
883
|
+
case "lowerRoman":
|
|
884
|
+
return toRoman(n).toLowerCase();
|
|
885
|
+
case "upperRoman":
|
|
886
|
+
return toRoman(n).toUpperCase();
|
|
887
|
+
case "decimalZero":
|
|
888
|
+
return n < 10 ? `0${n}` : String(n);
|
|
889
|
+
default:
|
|
890
|
+
// decimal and any non-numeric/locale formats we don't render.
|
|
891
|
+
return String(n);
|
|
892
|
+
}
|
|
893
|
+
}
|
|
894
|
+
/** 1 → "A", 26 → "Z", 27 → "AA" (spreadsheet-style alpha). */
|
|
895
|
+
function toAlpha(n) {
|
|
896
|
+
let s = "";
|
|
897
|
+
let v = n;
|
|
898
|
+
while (v > 0) {
|
|
899
|
+
const rem = (v - 1) % 26;
|
|
900
|
+
s = String.fromCharCode(65 + rem) + s;
|
|
901
|
+
v = Math.floor((v - 1) / 26);
|
|
902
|
+
}
|
|
903
|
+
return s || "A";
|
|
904
|
+
}
|
|
905
|
+
/** Convert a positive integer to a Roman numeral (uppercase). */
|
|
906
|
+
function toRoman(n) {
|
|
907
|
+
if (n <= 0) {
|
|
908
|
+
return String(n);
|
|
909
|
+
}
|
|
910
|
+
const table = [
|
|
911
|
+
[1000, "M"],
|
|
912
|
+
[900, "CM"],
|
|
913
|
+
[500, "D"],
|
|
914
|
+
[400, "CD"],
|
|
915
|
+
[100, "C"],
|
|
916
|
+
[90, "XC"],
|
|
917
|
+
[50, "L"],
|
|
918
|
+
[40, "XL"],
|
|
919
|
+
[10, "X"],
|
|
920
|
+
[9, "IX"],
|
|
921
|
+
[5, "V"],
|
|
922
|
+
[4, "IV"],
|
|
923
|
+
[1, "I"]
|
|
924
|
+
];
|
|
925
|
+
let v = n;
|
|
926
|
+
let s = "";
|
|
927
|
+
for (const [val, sym] of table) {
|
|
928
|
+
while (v >= val) {
|
|
929
|
+
s += sym;
|
|
930
|
+
v -= val;
|
|
931
|
+
}
|
|
932
|
+
}
|
|
933
|
+
return s;
|
|
934
|
+
}
|
|
935
|
+
function layoutParagraph(para, startY, contentWidth, options, pageContext, imageMap, listMarkers) {
|
|
708
936
|
const props = para.properties;
|
|
709
937
|
const spacing = props?.spacing;
|
|
710
|
-
|
|
938
|
+
// Resolve effective run properties from the paragraph's style chain. When
|
|
939
|
+
// the style supplies a concrete font size we honour it; only when it does
|
|
940
|
+
// not do we fall back to the heuristic heading scale so headings stay
|
|
941
|
+
// distinct in documents lacking a styles table.
|
|
942
|
+
const styleRunProps = activeDoc ? (0, style_resolve_1.resolveStyle)(activeDoc, para).runProperties : undefined;
|
|
943
|
+
const styleHasSize = styleRunProps?.size != null;
|
|
944
|
+
const headingScale = styleHasSize ? 1 : getHeadingFontScale(getHeadingLevel(props));
|
|
711
945
|
// Space before
|
|
712
946
|
let spaceBefore = 0;
|
|
713
947
|
if (spacing?.beforeAutoSpacing) {
|
|
@@ -717,7 +951,15 @@ function layoutParagraph(para, startY, contentWidth, options, pageContext, image
|
|
|
717
951
|
spaceBefore = twipsToPt(spacing.before);
|
|
718
952
|
}
|
|
719
953
|
const indent = props?.indent;
|
|
720
|
-
|
|
954
|
+
// Prefer an explicitly threaded map; fall back to the active layout's
|
|
955
|
+
// shared map so list markers also render inside tables, text boxes, SDTs,
|
|
956
|
+
// footnotes, etc. (whose layoutParagraph calls don't thread it through).
|
|
957
|
+
const marker = (listMarkers ?? activeListMarkers)?.get(para);
|
|
958
|
+
// List paragraphs are indented by their numbering level; the marker text
|
|
959
|
+
// is injected as a leading run below. An explicit paragraph indent (rare on
|
|
960
|
+
// list items) still wins when larger.
|
|
961
|
+
const markerIndentPt = marker ? marker.indentPt : 0;
|
|
962
|
+
const leftIndentPt = Math.max(indent?.left ? twipsToPt(indent.left) : 0, markerIndentPt);
|
|
721
963
|
const firstLineIndentPt = indent?.firstLine ? twipsToPt(indent.firstLine) : 0;
|
|
722
964
|
const alignment = props?.alignment ?? "left";
|
|
723
965
|
// Line height
|
|
@@ -738,7 +980,20 @@ function layoutParagraph(para, startY, contentWidth, options, pageContext, image
|
|
|
738
980
|
}
|
|
739
981
|
lineHeightPt *= headingScale;
|
|
740
982
|
// Collect runs
|
|
741
|
-
const segments = collectParagraphSegments(para);
|
|
983
|
+
const segments = mergeStyleRunProps(collectParagraphSegments(para), styleRunProps);
|
|
984
|
+
// Inject the list marker (bullet / number) as a leading text run so it
|
|
985
|
+
// renders inline at the start of the first line, inheriting the first
|
|
986
|
+
// text run's formatting (font / size) for visual consistency.
|
|
987
|
+
if (marker) {
|
|
988
|
+
let firstRunProps;
|
|
989
|
+
for (const s of segments) {
|
|
990
|
+
if (!("type" in s) || s.type === undefined) {
|
|
991
|
+
firstRunProps = s.properties;
|
|
992
|
+
break;
|
|
993
|
+
}
|
|
994
|
+
}
|
|
995
|
+
segments.unshift({ text: marker.text, properties: firstRunProps });
|
|
996
|
+
}
|
|
742
997
|
const fullAvailableWidth = contentWidth - leftIndentPt;
|
|
743
998
|
// When a page has wrap exclusions (square / tight / through floats)
|
|
744
999
|
// we wrap line-by-line, asking the page context for the widest free
|
|
@@ -787,7 +1042,7 @@ function layoutParagraph(para, startY, contentWidth, options, pageContext, image
|
|
|
787
1042
|
}
|
|
788
1043
|
else {
|
|
789
1044
|
const fontSize = getRunFontSizePt(seg.properties) * headingScale;
|
|
790
|
-
const fontName = (0, font_metrics_1.
|
|
1045
|
+
const fontName = (0, font_metrics_1.styledFontVariant)(resolveRunFontName(seg.properties), seg.properties?.bold, seg.properties?.italic);
|
|
791
1046
|
lineWidth += (0, font_metrics_1.measureTextWidth)(seg.text, fontName, fontSize);
|
|
792
1047
|
}
|
|
793
1048
|
}
|
|
@@ -818,7 +1073,7 @@ function layoutParagraph(para, startY, contentWidth, options, pageContext, image
|
|
|
818
1073
|
}
|
|
819
1074
|
const fontSize = getRunFontSizePt(seg.properties) * headingScale;
|
|
820
1075
|
const fontName = resolveRunFontName(seg.properties);
|
|
821
|
-
const measuredFont = (0, font_metrics_1.
|
|
1076
|
+
const measuredFont = (0, font_metrics_1.styledFontVariant)(fontName, seg.properties?.bold, seg.properties?.italic);
|
|
822
1077
|
const segWidth = (0, font_metrics_1.measureTextWidth)(seg.text, measuredFont, fontSize);
|
|
823
1078
|
runs.push({
|
|
824
1079
|
text: seg.text,
|
|
@@ -936,7 +1191,8 @@ function layoutTable(table, startY, contentWidth, sourceIndex, options, imageMap
|
|
|
936
1191
|
rect: { x: cellX, y: startY + cursorY, width: cellWidth, height: cellHeight },
|
|
937
1192
|
row: ri,
|
|
938
1193
|
col: ci,
|
|
939
|
-
content: cellContent
|
|
1194
|
+
content: cellContent,
|
|
1195
|
+
borders: resolveCellBorders(table.properties?.borders, cell.properties?.borders, ri === 0, ri === table.rows.length - 1, startCol === 0, endCol >= colWidths.length)
|
|
940
1196
|
});
|
|
941
1197
|
gridCol += span;
|
|
942
1198
|
}
|
|
@@ -955,6 +1211,38 @@ function layoutTable(table, startY, contentWidth, sourceIndex, options, imageMap
|
|
|
955
1211
|
sourceIndex
|
|
956
1212
|
};
|
|
957
1213
|
}
|
|
1214
|
+
/**
|
|
1215
|
+
* Resolve the four visible borders of a table cell into layout-model form
|
|
1216
|
+
* (`{ width: pt, color: hex }`). A cell's own border wins; otherwise the
|
|
1217
|
+
* table-level border applies — outer edges use `top/left/bottom/right`, inner
|
|
1218
|
+
* edges use `insideH/insideV`. OOXML border `size` is in eighths of a point.
|
|
1219
|
+
*/
|
|
1220
|
+
function resolveCellBorders(tableBorders, cellBorders, isTopRow, isBottomRow, isLeftCol, isRightCol) {
|
|
1221
|
+
const edge = (cellEdge, outerEdge, innerEdge, isOuter) => {
|
|
1222
|
+
const b = cellEdge ?? (isOuter ? outerEdge : innerEdge);
|
|
1223
|
+
if (!b || b.style === "none" || b.style === "nil") {
|
|
1224
|
+
return undefined;
|
|
1225
|
+
}
|
|
1226
|
+
// `size` is in eighths of a point; default to a hairline (0.5pt) when
|
|
1227
|
+
// a border is declared without an explicit size.
|
|
1228
|
+
const width = b.size != null ? b.size / 8 : 0.5;
|
|
1229
|
+
const color = !b.color || b.color === "auto" ? "000000" : b.color;
|
|
1230
|
+
return { width: Math.max(0.25, width), color };
|
|
1231
|
+
};
|
|
1232
|
+
const top = edge(cellBorders?.top, tableBorders?.top, tableBorders?.insideH, isTopRow);
|
|
1233
|
+
const bottom = edge(cellBorders?.bottom, tableBorders?.bottom, tableBorders?.insideH, isBottomRow);
|
|
1234
|
+
const left = edge(cellBorders?.left, tableBorders?.left, tableBorders?.insideV, isLeftCol);
|
|
1235
|
+
const right = edge(cellBorders?.right, tableBorders?.right, tableBorders?.insideV, isRightCol);
|
|
1236
|
+
if (!top && !bottom && !left && !right) {
|
|
1237
|
+
return undefined;
|
|
1238
|
+
}
|
|
1239
|
+
return {
|
|
1240
|
+
...(top ? { top } : {}),
|
|
1241
|
+
...(bottom ? { bottom } : {}),
|
|
1242
|
+
...(left ? { left } : {}),
|
|
1243
|
+
...(right ? { right } : {})
|
|
1244
|
+
};
|
|
1245
|
+
}
|
|
958
1246
|
/**
|
|
959
1247
|
* Resolve a table's per-column widths in points.
|
|
960
1248
|
*
|
|
@@ -1004,6 +1292,21 @@ function collectParagraphSegments(para) {
|
|
|
1004
1292
|
}
|
|
1005
1293
|
return segments;
|
|
1006
1294
|
}
|
|
1295
|
+
/**
|
|
1296
|
+
* Overlay resolved paragraph-style run properties under each segment's own
|
|
1297
|
+
* (inline) properties, so style-defined size/color/font apply when a run does
|
|
1298
|
+
* not override them. Inline run properties always win.
|
|
1299
|
+
*/
|
|
1300
|
+
function mergeStyleRunProps(segments, styleRunProps) {
|
|
1301
|
+
if (!styleRunProps) {
|
|
1302
|
+
return segments;
|
|
1303
|
+
}
|
|
1304
|
+
return segments.map(seg => {
|
|
1305
|
+
const own = seg.properties;
|
|
1306
|
+
const merged = own ? { ...styleRunProps, ...own } : styleRunProps;
|
|
1307
|
+
return { ...seg, properties: merged };
|
|
1308
|
+
});
|
|
1309
|
+
}
|
|
1007
1310
|
/**
|
|
1008
1311
|
* Emit `ParagraphSegment` tokens for a single run, preserving the
|
|
1009
1312
|
* relative order of text fragments and inline images. Consecutive
|
|
@@ -1059,7 +1362,7 @@ function wrapSegmentsToLinesWithExclusions(segments, leftIndentPt, firstLineInde
|
|
|
1059
1362
|
continue;
|
|
1060
1363
|
}
|
|
1061
1364
|
const fontSize = getRunFontSizePt(seg.properties) * headingScale;
|
|
1062
|
-
const fontName = (0, font_metrics_1.
|
|
1365
|
+
const fontName = (0, font_metrics_1.styledFontVariant)(resolveRunFontName(seg.properties), seg.properties?.bold, seg.properties?.italic);
|
|
1063
1366
|
// Split on runs of whitespace, keeping the whitespace tokens so
|
|
1064
1367
|
// wrapping can decide whether to drop trailing space at line end.
|
|
1065
1368
|
const tokens = seg.text.split(/(\s+)/);
|
|
@@ -1214,14 +1517,18 @@ function wrapSegmentsToLines(segments, availableWidth, firstLineIndent, headingS
|
|
|
1214
1517
|
}
|
|
1215
1518
|
const text = segment.text;
|
|
1216
1519
|
const fontSize = getRunFontSizePt(segment.properties) * headingScale;
|
|
1217
|
-
const fontName = (0, font_metrics_1.
|
|
1520
|
+
const fontName = (0, font_metrics_1.styledFontVariant)(resolveRunFontName(segment.properties), segment.properties?.bold, segment.properties?.italic);
|
|
1218
1521
|
const segmentWidth = (0, font_metrics_1.measureTextWidth)(text, fontName, fontSize);
|
|
1219
|
-
if (currentLineWidth + segmentWidth <= effectiveWidth
|
|
1522
|
+
if (currentLineWidth + segmentWidth <= effectiveWidth) {
|
|
1523
|
+
// Whole segment fits on the current line — fast path.
|
|
1220
1524
|
currentLine.push(segment);
|
|
1221
1525
|
currentLineWidth += segmentWidth;
|
|
1222
1526
|
}
|
|
1223
1527
|
else {
|
|
1224
|
-
//
|
|
1528
|
+
// Segment does not fit — split it into words and wrap. The inner
|
|
1529
|
+
// loop's `currentLine.length === 0 && bufferedText.length === 0`
|
|
1530
|
+
// guard guarantees at least one word per line (preventing a dead
|
|
1531
|
+
// loop when even a single word is wider than the line).
|
|
1225
1532
|
const words = text.split(/(\s+)/);
|
|
1226
1533
|
let bufferedText = "";
|
|
1227
1534
|
let bufferedWidth = 0;
|
|
@@ -15,6 +15,7 @@ exports.renderPageFromLayout = renderPageFromLayout;
|
|
|
15
15
|
const font_metrics_1 = require("../../../utils/font-metrics.js");
|
|
16
16
|
const encode_1 = require("../../xml/encode.js");
|
|
17
17
|
const text_utils_1 = require("../core/text-utils");
|
|
18
|
+
const style_resolve_1 = require("../query/style-resolve");
|
|
18
19
|
const units_1 = require("../units");
|
|
19
20
|
const layout_1 = require("./layout");
|
|
20
21
|
const layout_constants_1 = require("./layout-constants");
|
|
@@ -194,7 +195,18 @@ function renderParagraph(para, state) {
|
|
|
194
195
|
const props = para.properties;
|
|
195
196
|
const spacing = props?.spacing;
|
|
196
197
|
const headingLevel = getHeadingLevel(props);
|
|
197
|
-
|
|
198
|
+
// Resolve the paragraph's effective run properties from the style chain
|
|
199
|
+
// (Heading1 → … → docDefaults). When the style provides a concrete font
|
|
200
|
+
// size we honour it directly; only when no style size is available do we
|
|
201
|
+
// fall back to the heuristic heading scale so headings still look distinct
|
|
202
|
+
// in documents that lack a styles table.
|
|
203
|
+
const styleRunProps = (0, style_resolve_1.resolveStyle)(state.doc, para).runProperties;
|
|
204
|
+
const styleHasSize = styleRunProps.size != null;
|
|
205
|
+
const fallbackScale = getHeadingFontScale(headingLevel);
|
|
206
|
+
const headingScale = styleHasSize ? 1 : fallbackScale;
|
|
207
|
+
// Synthesise bold for headings only in fallback mode (no real style size);
|
|
208
|
+
// otherwise the style's own bold flag governs.
|
|
209
|
+
const synthesizeHeadingBold = headingLevel > 0 && !styleHasSize;
|
|
198
210
|
// Space before
|
|
199
211
|
let spaceBefore = 0;
|
|
200
212
|
if (spacing?.beforeAutoSpacing) {
|
|
@@ -236,7 +248,7 @@ function renderParagraph(para, state) {
|
|
|
236
248
|
// Apply heading scale to line height
|
|
237
249
|
lineHeightPt *= headingScale;
|
|
238
250
|
// Collect runs and render as text spans on lines
|
|
239
|
-
const runs = collectParagraphRuns(para);
|
|
251
|
+
const runs = collectParagraphRuns(para, styleRunProps);
|
|
240
252
|
if (runs.length === 0) {
|
|
241
253
|
// Empty paragraph — advance by line height
|
|
242
254
|
state.cursorY += lineHeightPt;
|
|
@@ -266,7 +278,7 @@ function renderParagraph(para, state) {
|
|
|
266
278
|
for (const segment of line) {
|
|
267
279
|
const fontSize = getRunFontSizePt(segment.properties) * headingScale;
|
|
268
280
|
const fontFamily = resolveFontFamily(getRunFontName({ properties: segment.properties, content: [] }), state.fontsMap);
|
|
269
|
-
const isBold = segment.properties?.bold ||
|
|
281
|
+
const isBold = segment.properties?.bold || synthesizeHeadingBold;
|
|
270
282
|
const isItalic = segment.properties?.italic;
|
|
271
283
|
const color = resolveColor(segment.properties?.color);
|
|
272
284
|
const underline = segment.properties?.underline;
|
|
@@ -318,20 +330,32 @@ function renderParagraph(para, state) {
|
|
|
318
330
|
state.cursorY += spaceAfter;
|
|
319
331
|
}
|
|
320
332
|
/** Collect all text segments from a paragraph's children. */
|
|
321
|
-
function collectParagraphRuns(para) {
|
|
333
|
+
function collectParagraphRuns(para, styleRunProps) {
|
|
334
|
+
// Merge the resolved paragraph-style run properties as a fallback under each
|
|
335
|
+
// run's own (inline) properties, so style-defined size/color/font apply when
|
|
336
|
+
// the run does not override them.
|
|
337
|
+
const merge = (own) => {
|
|
338
|
+
if (!styleRunProps) {
|
|
339
|
+
return own;
|
|
340
|
+
}
|
|
341
|
+
if (!own) {
|
|
342
|
+
return styleRunProps;
|
|
343
|
+
}
|
|
344
|
+
return { ...styleRunProps, ...own };
|
|
345
|
+
};
|
|
322
346
|
const segments = [];
|
|
323
347
|
for (const child of para.children) {
|
|
324
348
|
if ((0, text_utils_1.isRun)(child)) {
|
|
325
349
|
const text = getRunText(child);
|
|
326
350
|
if (text.length > 0) {
|
|
327
|
-
segments.push({ text, properties: child.properties });
|
|
351
|
+
segments.push({ text, properties: merge(child.properties) });
|
|
328
352
|
}
|
|
329
353
|
}
|
|
330
354
|
else if ((0, text_utils_1.isHyperlink)(child)) {
|
|
331
355
|
for (const run of child.children) {
|
|
332
356
|
const text = getRunText(run);
|
|
333
357
|
if (text.length > 0) {
|
|
334
|
-
segments.push({ text, properties: run.properties });
|
|
358
|
+
segments.push({ text, properties: merge(run.properties) });
|
|
335
359
|
}
|
|
336
360
|
}
|
|
337
361
|
}
|
|
@@ -523,8 +547,11 @@ function renderTable(table, state) {
|
|
|
523
547
|
let cellCursorY = rowStartY + cellPadding;
|
|
524
548
|
for (const content of cell.content) {
|
|
525
549
|
if (content.type === "paragraph") {
|
|
526
|
-
// Simplified: render first run of each paragraph
|
|
527
|
-
|
|
550
|
+
// Simplified: render first run of each paragraph, with the
|
|
551
|
+
// paragraph style's run properties as a fallback so styled cell
|
|
552
|
+
// text (e.g. a heading paragraph) honours its size/colour/font.
|
|
553
|
+
const cellStyleRunProps = (0, style_resolve_1.resolveStyle)(state.doc, content).runProperties;
|
|
554
|
+
const runs = collectParagraphRuns(content, cellStyleRunProps);
|
|
528
555
|
if (runs.length > 0) {
|
|
529
556
|
const allText = runs.map(r => r.text).join("");
|
|
530
557
|
const firstRun = runs[0];
|
|
@@ -13,21 +13,29 @@ exports.setCompatibilityMode = setCompatibilityMode;
|
|
|
13
13
|
/**
|
|
14
14
|
* Get the compatibility mode of a document.
|
|
15
15
|
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
16
|
+
* The mode is stored in `settings.compatibilityMode` (the canonical scalar
|
|
17
|
+
* field populated by the reader). For backward compatibility we also honour an
|
|
18
|
+
* explicit `compatibilityMode` entry in `settings.compatSettings` if present,
|
|
19
|
+
* since the writer accepts that advanced-override path.
|
|
20
|
+
*
|
|
21
|
+
* Returns 15 (Word 2013+) by default if nothing is stored.
|
|
18
22
|
*
|
|
19
23
|
* @param doc - The document to inspect.
|
|
20
24
|
* @returns The compatibility mode version number.
|
|
21
25
|
*/
|
|
22
26
|
function getCompatibilityMode(doc) {
|
|
23
|
-
|
|
27
|
+
const settings = doc.settings;
|
|
28
|
+
if (!settings) {
|
|
24
29
|
return 15;
|
|
25
30
|
}
|
|
26
|
-
|
|
27
|
-
|
|
31
|
+
// Prefer an explicit override entry in compatSettings (advanced path) so a
|
|
32
|
+
// hand-authored value wins, then fall back to the canonical scalar field.
|
|
33
|
+
const overrideEntry = settings.compatSettings?.find(s => s.name === "compatibilityMode");
|
|
34
|
+
const raw = overrideEntry?.val ?? settings.compatibilityMode;
|
|
35
|
+
if (raw === undefined) {
|
|
28
36
|
return 15;
|
|
29
37
|
}
|
|
30
|
-
const n = parseInt(
|
|
38
|
+
const n = typeof raw === "number" ? raw : parseInt(raw, 10);
|
|
31
39
|
if (n === 11 || n === 12 || n === 14 || n === 15) {
|
|
32
40
|
return n;
|
|
33
41
|
}
|
|
@@ -36,26 +44,26 @@ function getCompatibilityMode(doc) {
|
|
|
36
44
|
/**
|
|
37
45
|
* Set the compatibility mode of a document (mutates settings in place).
|
|
38
46
|
*
|
|
47
|
+
* Writes the canonical `settings.compatibilityMode` scalar field and removes
|
|
48
|
+
* any stale `compatibilityMode` override entry from `settings.compatSettings`
|
|
49
|
+
* so the two sources never disagree.
|
|
50
|
+
*
|
|
39
51
|
* @param doc - The document to modify (mutated in place).
|
|
40
52
|
* @param mode - The target compatibility mode (11=Word 2003, 12=Word 2007, 14=Word 2010, 15=Word 2013+).
|
|
41
53
|
*/
|
|
42
54
|
function setCompatibilityMode(doc, mode) {
|
|
43
55
|
const settings = doc.settings ? { ...doc.settings } : {};
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
}
|
|
56
|
-
else {
|
|
57
|
-
compatSettings.push(entry);
|
|
56
|
+
settings.compatibilityMode = mode;
|
|
57
|
+
// Drop any stale override entry so getCompatibilityMode/writer don't read a
|
|
58
|
+
// conflicting value from the array. The scalar field is now authoritative.
|
|
59
|
+
if (settings.compatSettings) {
|
|
60
|
+
const filtered = settings.compatSettings.filter(s => s.name !== "compatibilityMode");
|
|
61
|
+
if (filtered.length > 0) {
|
|
62
|
+
settings.compatSettings = filtered;
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
delete settings.compatSettings;
|
|
66
|
+
}
|
|
58
67
|
}
|
|
59
|
-
settings.compatSettings = compatSettings;
|
|
60
68
|
doc.settings = settings;
|
|
61
69
|
}
|