@stll/folio-core 0.47.7 → 0.48.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/controller/fontReadiness.js +3 -3
- package/dist/controller/hyphenationReadiness.d.ts +19 -0
- package/dist/controller/hyphenationReadiness.js +47 -0
- package/dist/controller/layoutPipeline.d.ts +3 -0
- package/dist/controller/layoutPipeline.js +59 -17
- package/dist/display-list/build/buildDisplayList.js +39 -12
- package/dist/display-list/build/pageFurniture.d.ts +7 -1
- package/dist/display-list/build/pageFurniture.js +21 -8
- package/dist/display-list/build/paragraphPrimitives.js +5 -4
- package/dist/display-list/build/tablePrimitives.js +10 -6
- package/dist/docx/attributeRemainder.js +10 -2
- package/dist/docx/blockContentParser.d.ts +2 -0
- package/dist/docx/blockContentParser.js +77 -62
- package/dist/docx/commentParser.d.ts +2 -1
- package/dist/docx/commentParser.js +57 -45
- package/dist/docx/containerChildren.d.ts +21 -8
- package/dist/docx/containerChildren.js +39 -9
- package/dist/docx/documentParser.d.ts +5 -1
- package/dist/docx/documentParser.js +8 -5
- package/dist/docx/documentSectionFacts.d.ts +22 -0
- package/dist/docx/documentSectionFacts.js +43 -0
- package/dist/docx/fieldParser.d.ts +8 -1
- package/dist/docx/fieldParser.js +47 -3
- package/dist/docx/fontTableParser.js +34 -27
- package/dist/docx/footnoteParser.d.ts +3 -2
- package/dist/docx/footnoteParser.js +15 -12
- package/dist/docx/groupDrawingParser.d.ts +2 -1
- package/dist/docx/groupDrawingParser.js +3 -6
- package/dist/docx/headerFooterParser.d.ts +7 -2
- package/dist/docx/headerFooterParser.js +15 -5
- package/dist/docx/hyperlinkParser.d.ts +48 -36
- package/dist/docx/hyperlinkParser.js +97 -82
- package/dist/docx/numberingParser.js +108 -84
- package/dist/docx/paragraphParser.d.ts +5 -1
- package/dist/docx/paragraphParser.js +381 -362
- package/dist/docx/paragraphProperties.js +123 -109
- package/dist/docx/paragraphPropertySource.d.ts +6 -1
- package/dist/docx/paragraphPropertySource.js +9 -1
- package/dist/docx/paragraphTextBoxEnrichment.d.ts +2 -1
- package/dist/docx/paragraphTextBoxEnrichment.js +17 -6
- package/dist/docx/parser.d.ts +7 -1
- package/dist/docx/parser.js +33 -16
- package/dist/docx/previewBudget.d.ts +55 -33
- package/dist/docx/previewBudget.js +59 -65
- package/dist/docx/rezip.js +41 -58
- package/dist/docx/runParser.d.ts +4 -1
- package/dist/docx/runParser.js +172 -184
- package/dist/docx/sdtProperties.js +87 -84
- package/dist/docx/sectionParser.js +169 -160
- package/dist/docx/serializer/paragraphSerializer.js +42 -38
- package/dist/docx/serializer/settingsSerializer.js +19 -1
- package/dist/docx/settingsParser.js +8 -0
- package/dist/docx/streamingXmlParser.d.ts +6 -2
- package/dist/docx/streamingXmlParser.js +12 -7
- package/dist/docx/styleParser.js +34 -8
- package/dist/docx/tableParser.d.ts +21 -12
- package/dist/docx/tableParser.js +518 -441
- package/dist/docx/textBoxParser.d.ts +8 -3
- package/dist/docx/textBoxParser.js +3 -3
- package/dist/docx/verbatimCapture.d.ts +10 -8
- package/dist/docx/verbatimCapture.js +45 -2
- package/dist/docx/vmlImageParser.d.ts +15 -13
- package/dist/docx/vmlImageParser.js +75 -60
- package/dist/docx/vmlPreview.d.ts +3 -3
- package/dist/docx/vmlPreview.js +3 -4
- package/dist/docx/xmlNamespaceContext.d.ts +9 -0
- package/dist/docx/xmlNamespaceContext.js +54 -0
- package/dist/docx/xmlParser.d.ts +6 -3
- package/dist/docx/xmlParser.js +11 -41
- package/dist/docx/xmlResourceLimits.d.ts +2 -1
- package/dist/docx/xmlResourceLimits.js +4 -1
- package/dist/fonts/headlessMeasure.js +0 -0
- package/dist/headless-layout.d.ts +11 -2
- package/dist/headless-layout.js +153 -107
- package/dist/internal/paragraphFormattingSerialization.js +3 -3
- package/dist/layout-bridge/convert/endnoteLayout.d.ts +65 -0
- package/dist/layout-bridge/convert/endnoteLayout.js +227 -0
- package/dist/layout-bridge/convert/footnoteLayout.d.ts +19 -3
- package/dist/layout-bridge/convert/footnoteLayout.js +40 -31
- package/dist/layout-bridge/convert/headerFooterLayout.d.ts +1 -7
- package/dist/layout-bridge/convert/headerFooterLayout.js +5 -20
- package/dist/layout-bridge/convert/paragraphMarkFormatting.d.ts +10 -0
- package/dist/layout-bridge/convert/paragraphMarkFormatting.js +59 -0
- package/dist/layout-bridge/convert/toFlowBlocks.d.ts +29 -1
- package/dist/layout-bridge/convert/toFlowBlocks.js +258 -35
- package/dist/layout-bridge/engine/hitTest.js +5 -3
- package/dist/layout-bridge/engine/selectionRects.js +5 -3
- package/dist/layout-engine/anchorLayoutInCellCompatibility.d.ts +10 -0
- package/dist/layout-engine/anchorLayoutInCellCompatibility.js +22 -0
- package/dist/layout-engine/index.d.ts +2 -2
- package/dist/layout-engine/index.js +70 -11
- package/dist/layout-engine/justificationCompatibility.d.ts +1 -1
- package/dist/layout-engine/justificationCompatibility.js +9 -5
- package/dist/layout-engine/keep-together.d.ts +11 -5
- package/dist/layout-engine/keep-together.js +39 -16
- package/dist/layout-engine/layoutInstrumentation.d.ts +8 -2
- package/dist/layout-engine/layoutInstrumentation.js +8 -1
- package/dist/layout-engine/measure/advanceComposition.js +4 -7
- package/dist/layout-engine/measure/hyphenationDictionaries.d.ts +74 -0
- package/dist/layout-engine/measure/hyphenationDictionaries.js +138 -0
- package/dist/layout-engine/measure/hyphenationPreload.d.ts +11 -0
- package/dist/layout-engine/measure/hyphenationPreload.js +24 -0
- package/dist/layout-engine/measure/lineBreakProvider.d.ts +12 -2
- package/dist/layout-engine/measure/lineBreakProvider.js +30 -30
- package/dist/layout-engine/measure/lineBreaks.d.ts +6 -1
- package/dist/layout-engine/measure/lineBreaks.js +10 -3
- package/dist/layout-engine/measure/listMarkerWidth.d.ts +9 -6
- package/dist/layout-engine/measure/listMarkerWidth.js +20 -22
- package/dist/layout-engine/measure/measureBlocks.js +42 -8
- package/dist/layout-engine/measure/measureContainer.js +5 -5
- package/dist/layout-engine/measure/measureHelpers.d.ts +15 -3
- package/dist/layout-engine/measure/measureHelpers.js +21 -5
- package/dist/layout-engine/measure/measureParagraph.js +109 -30
- package/dist/layout-engine/measure/measureTypes.d.ts +5 -0
- package/dist/layout-engine/measure/tabCalculator.d.ts +2 -0
- package/dist/layout-engine/measure/tabCalculator.js +2 -1
- package/dist/layout-engine/measure/tableCellFloating.d.ts +2 -0
- package/dist/layout-engine/measure/tableCellFloating.js +51 -19
- package/dist/layout-engine/measure/tableCellGrid.d.ts +28 -3
- package/dist/layout-engine/measure/tableCellGrid.js +45 -5
- package/dist/layout-engine/measure/tableFragmentBorderGeometry.js +2 -1
- package/dist/layout-engine/noteAreaFlow.d.ts +19 -0
- package/dist/layout-engine/noteAreaFlow.js +83 -0
- package/dist/layout-engine/paginator.d.ts +10 -1
- package/dist/layout-engine/paginator.js +13 -1
- package/dist/layout-engine/tableRowBreak.js +3 -1
- package/dist/layout-engine/types.d.ts +89 -3
- package/dist/layout-engine/types.js +96 -1
- package/dist/layout-painter/index.d.ts +3 -0
- package/dist/layout-painter/renderPage.js +32 -83
- package/dist/layout-painter/renderParagraph.d.ts +6 -1
- package/dist/layout-painter/renderParagraph.js +34 -22
- package/dist/layout-painter/renderTable.js +26 -13
- package/dist/prosemirror/attrs/index.d.ts +9 -9
- package/dist/prosemirror/attrs/index.js +43 -11
- package/dist/prosemirror/conversion/fromProseDoc.js +8 -1
- package/dist/prosemirror/conversion/markInterner.d.ts +21 -0
- package/dist/prosemirror/conversion/markInterner.js +90 -0
- package/dist/prosemirror/conversion/toProseDoc.js +77 -41
- package/dist/prosemirror/extensions/core/DocExtension.js +2 -1
- package/dist/prosemirror/extensions/core/ParagraphExtension.js +1 -0
- package/dist/prosemirror/extensions/marks/markUtils.d.ts +6 -2
- package/dist/prosemirror/extensions/marks/markUtils.js +39 -51
- package/dist/prosemirror/extensions/nodes/TextBoxExtension.js +2 -1
- package/dist/prosemirror/listMarker.d.ts +1 -0
- package/dist/prosemirror/listMarker.js +2 -0
- package/dist/prosemirror/listRenderingAttrs.d.ts +1 -0
- package/dist/prosemirror/listRenderingAttrs.js +3 -0
- package/dist/prosemirror/schema/nodes.d.ts +13 -1
- package/dist/prosemirror/textBoxHostParagraph.d.ts +13 -0
- package/dist/prosemirror/textBoxHostParagraph.js +21 -0
- package/dist/prosemirror/validation.js +91 -80
- package/dist/utils/createDocument.js +10 -1
- package/dist/utils/scriptSegments.d.ts +33 -4
- package/dist/utils/scriptSegments.js +39 -6
- package/dist/utils/textFormattingMerge.d.ts +17 -3
- package/dist/utils/textFormattingMerge.js +18 -12
- package/dist/utils/trailingText.d.ts +22 -0
- package/dist/utils/trailingText.js +32 -0
- package/package.json +2 -2
|
@@ -17,6 +17,7 @@ import { directionIsRtl, directionToBidi } from "../../prosemirror/paragraphDire
|
|
|
17
17
|
import { resolveEffectiveRunStyleFormatting } from "../../prosemirror/runStyleFormatting.js";
|
|
18
18
|
import { parseSectionBreakType } from "../../prosemirror/sectionCarrier.js";
|
|
19
19
|
import { expectTextBoxAnchorAttrs } from "../../prosemirror/textBoxAnchorAttrs.js";
|
|
20
|
+
import { textBoxHostParagraph } from "../../prosemirror/textBoxHostParagraph.js";
|
|
20
21
|
import { assertValidProseMirrorDocument } from "../../prosemirror/validation.js";
|
|
21
22
|
import { createStyleEngine } from "../../style-engine/styleEngine.js";
|
|
22
23
|
import { normalizeShapeTextAnchor } from "../../types/documentEnumValues.js";
|
|
@@ -26,12 +27,14 @@ import { resolveThemeFont } from "../../utils/fontResolver.js";
|
|
|
26
27
|
import { resolveShadingFill } from "../../utils/formatToStyle.js";
|
|
27
28
|
import { normalizeHorizontalScalePercent } from "../../utils/horizontalScale.js";
|
|
28
29
|
import { decodeOoxmlSymbolCharacter } from "../../utils/ooxmlSymbol.js";
|
|
30
|
+
import { mergeParagraphTabStops } from "../../utils/paragraphFormattingMerge.js";
|
|
29
31
|
import { sanitizeImageSrc } from "../../utils/sanitizeImageSrc.js";
|
|
30
32
|
import { tableOfContentsStyleLevel } from "../../utils/tableOfContentsStyle.js";
|
|
31
|
-
import { STYLE_TOGGLE_KEYS } from "../../utils/textFormattingMerge.js";
|
|
33
|
+
import { STYLE_TOGGLE_KEYS, mergeTextFormatting } from "../../utils/textFormattingMerge.js";
|
|
32
34
|
import { AUTO_PARAGRAPH_SPACING_PX, halfPointsToPixels, halfPointsToPoints, pointsToPixels } from "../../utils/units.js";
|
|
33
35
|
import { getColumns } from "../sectionColumns.js";
|
|
34
36
|
import { groupParagraphFrames } from "./paragraphFrames.js";
|
|
37
|
+
import { resolveParagraphMarkFormatting, resolveParagraphMarkOwnFormatting } from "./paragraphMarkFormatting.js";
|
|
35
38
|
import { panic } from "better-result";
|
|
36
39
|
import { headingLevelOf, paragraphNumberingLevel, paragraphNumberingReferenceId, sameStatedParagraphNumbering, statesNoBorder } from "@stll/docx-core/model";
|
|
37
40
|
//#region src/layout-bridge/convert/toFlowBlocks.ts
|
|
@@ -42,6 +45,32 @@ const expectDetachedWatermarkHostAttr = (attrs) => {
|
|
|
42
45
|
if (typeof value !== "boolean") panic("Invalid ProseMirror detached watermark host attrs:\nparagraph.attrs._detachedWatermarkHost: Expected a boolean.");
|
|
43
46
|
return value;
|
|
44
47
|
};
|
|
48
|
+
const NOTE_REFERENCE_MARK_XML = /^<(?:[^\s:/>]+:)?(?:footnoteRef|endnoteRef)[\s/>]/u;
|
|
49
|
+
/**
|
|
50
|
+
* Runs with `idKey` set, in document order: the note references a block list
|
|
51
|
+
* makes, through table cells (and tables within cells) and text boxes.
|
|
52
|
+
*/
|
|
53
|
+
function collectNoteRefs(blocks, idKey) {
|
|
54
|
+
const refs = [];
|
|
55
|
+
const walk = (containerBlocks) => {
|
|
56
|
+
for (const block of containerBlocks) if (block.kind === "paragraph") for (const run of block.runs) {
|
|
57
|
+
if (run.kind !== "text") continue;
|
|
58
|
+
const noteId = run[idKey];
|
|
59
|
+
if (noteId !== void 0) refs.push({
|
|
60
|
+
noteId,
|
|
61
|
+
pmPos: run.pmStart ?? 0
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
else if (block.kind === "table") for (const row of block.rows) for (const cell of row.cells) walk(cell.blocks);
|
|
65
|
+
else if (block.kind === "textBox") walk(block.content);
|
|
66
|
+
};
|
|
67
|
+
walk(blocks);
|
|
68
|
+
return refs;
|
|
69
|
+
}
|
|
70
|
+
/** Whether captured run-child markup is a note story's `w:footnoteRef`/`w:endnoteRef`. */
|
|
71
|
+
function isNoteReferenceMarkXml(xml) {
|
|
72
|
+
return NOTE_REFERENCE_MARK_XML.test(xml);
|
|
73
|
+
}
|
|
45
74
|
/**
|
|
46
75
|
* Word's layout fallback when an imported OOXML style hierarchy never supplies
|
|
47
76
|
* `w:sz`. ECMA-376 deliberately leaves that terminal fallback to the consumer;
|
|
@@ -256,6 +285,7 @@ function extractRunFormatting(marks, theme, fontAlternates) {
|
|
|
256
285
|
const alternate = getFontAlternate(eastAsiaFont, fontAlternates);
|
|
257
286
|
if (alternate) formatting.eastAsiaAlternateFontFamily = alternate;
|
|
258
287
|
}
|
|
288
|
+
if (attrs.hint) formatting.eastAsiaHint = attrs.hint === "eastAsia";
|
|
259
289
|
const complexScriptFont = resolveComplexScriptThemeFont(attrs, theme);
|
|
260
290
|
if (complexScriptFont) {
|
|
261
291
|
formatting.complexScriptFontFamily = complexScriptFont;
|
|
@@ -564,6 +594,7 @@ function textFormattingToRunFormatting(defaultTextFormatting, theme, fontAlterna
|
|
|
564
594
|
const alternate = getFontAlternate(eastAsiaFontFamily, fontAlternates);
|
|
565
595
|
if (alternate) result.eastAsiaAlternateFontFamily = alternate;
|
|
566
596
|
}
|
|
597
|
+
if (defaultTextFormatting.fontFamily?.hint) result.eastAsiaHint = defaultTextFormatting.fontFamily.hint === "eastAsia";
|
|
567
598
|
const complexScriptFontFamily = defaultTextFormatting.fontFamily ? resolveComplexScriptThemeFont(defaultTextFormatting.fontFamily, theme) : void 0;
|
|
568
599
|
if (complexScriptFontFamily) {
|
|
569
600
|
result.complexScriptFontFamily = complexScriptFontFamily;
|
|
@@ -608,6 +639,34 @@ function textFormattingToRunFormatting(defaultTextFormatting, theme, fontAlterna
|
|
|
608
639
|
if (defaultTextFormatting.emphasisMark && defaultTextFormatting.emphasisMark !== "none") result.emphasisMark = defaultTextFormatting.emphasisMark;
|
|
609
640
|
return result;
|
|
610
641
|
}
|
|
642
|
+
const LIST_MARKER_FORMATTING_KEYS = [
|
|
643
|
+
"fontFamily",
|
|
644
|
+
"alternateFontFamily",
|
|
645
|
+
"eastAsiaFontFamily",
|
|
646
|
+
"eastAsiaAlternateFontFamily",
|
|
647
|
+
"complexScriptFontFamily",
|
|
648
|
+
"complexScriptAlternateFontFamily",
|
|
649
|
+
"fontSize",
|
|
650
|
+
"complexScriptFontSize",
|
|
651
|
+
"bold",
|
|
652
|
+
"complexScriptBold",
|
|
653
|
+
"italic",
|
|
654
|
+
"complexScriptItalic",
|
|
655
|
+
"rtl",
|
|
656
|
+
"forceComplexScript",
|
|
657
|
+
"color"
|
|
658
|
+
];
|
|
659
|
+
/**
|
|
660
|
+
* The numbering symbol's typography: the numbering level's `w:rPr` applied
|
|
661
|
+
* over the paragraph mark's run properties (ECMA-376 §17.9.24, §17.3.1.29).
|
|
662
|
+
* Text runs of the paragraph play no part.
|
|
663
|
+
*/
|
|
664
|
+
function listMarkerFormattingFor(levelFormatting, paragraphMarkFormatting, theme, fontAlternates) {
|
|
665
|
+
const runFormatting = textFormattingToRunFormatting(mergeTextFormatting(paragraphMarkFormatting, levelFormatting), theme, fontAlternates);
|
|
666
|
+
const formatting = {};
|
|
667
|
+
for (const key of LIST_MARKER_FORMATTING_KEYS) if (runFormatting[key] !== void 0) Reflect.set(formatting, key, runFormatting[key]);
|
|
668
|
+
return Object.keys(formatting).length > 0 ? formatting : void 0;
|
|
669
|
+
}
|
|
611
670
|
function paragraphRunDefaults(pmAttrs, theme, fontAlternates) {
|
|
612
671
|
return textFormattingToRunFormatting(pmAttrs.defaultTextFormatting, theme, fontAlternates);
|
|
613
672
|
}
|
|
@@ -615,7 +674,7 @@ function paragraphRunDefaults(pmAttrs, theme, fontAlternates) {
|
|
|
615
674
|
* Build an ImageRun from ProseMirror node attrs, applying conditional property assignment
|
|
616
675
|
* to satisfy exactOptionalPropertyTypes.
|
|
617
676
|
*/
|
|
618
|
-
function buildImageRun(attrs, constrained, pmStart, pmEnd, trackedChange) {
|
|
677
|
+
function buildImageRun(attrs, constrained, pmStart, pmEnd, trackedChange, forceLayoutInCell) {
|
|
619
678
|
const run = {
|
|
620
679
|
kind: "image",
|
|
621
680
|
src: attrs.src,
|
|
@@ -647,7 +706,8 @@ function buildImageRun(attrs, constrained, pmStart, pmEnd, trackedChange) {
|
|
|
647
706
|
const runBorderStyle = cssBorderStyleOf(attrs.borderStyle);
|
|
648
707
|
if (runBorderStyle) run.borderStyle = runBorderStyle;
|
|
649
708
|
if (attrs.position !== void 0) run.position = attrs.position;
|
|
650
|
-
if (
|
|
709
|
+
if (forceLayoutInCell) run.layoutInCell = true;
|
|
710
|
+
else if (attrs.anchor?.layoutInCell !== void 0) run.layoutInCell = attrs.anchor.layoutInCell;
|
|
651
711
|
if (trackedChange?.isInsertion) run.isInsertion = true;
|
|
652
712
|
if (trackedChange?.isDeletion) run.isDeletion = true;
|
|
653
713
|
if (trackedChange?.changeAuthor !== void 0) run.changeAuthor = trackedChange.changeAuthor;
|
|
@@ -790,7 +850,8 @@ function paragraphToRuns(node, startPos, _options, pageBreaks) {
|
|
|
790
850
|
return;
|
|
791
851
|
}
|
|
792
852
|
if (child.type.name === "preservedXml") {
|
|
793
|
-
const { text } = expectPreservedXmlAttrs(child);
|
|
853
|
+
const { xml, text: capturedText } = expectPreservedXmlAttrs(child);
|
|
854
|
+
const text = capturedText === "" && _options.noteReferenceMarkText !== void 0 && isNoteReferenceMarkXml(xml) ? _options.noteReferenceMarkText : capturedText;
|
|
794
855
|
if (text === "") return;
|
|
795
856
|
const formatting = extractRunFormatting(effectiveMarks, theme, fontAlternates);
|
|
796
857
|
applyCharacterStyleToggleFormatting({
|
|
@@ -838,7 +899,7 @@ function paragraphToRuns(node, startPos, _options, pageBreaks) {
|
|
|
838
899
|
if (!hasPaintableImageSource(attrs) && !hasRelationshipBackedImageBox(attrs)) return;
|
|
839
900
|
const constrained = constrainImageToPage(attrs.width ?? 100, attrs.height ?? 100, _options.pageContentHeight);
|
|
840
901
|
const trackedFmt = extractRunFormatting(effectiveMarks, theme, fontAlternates);
|
|
841
|
-
const run = buildImageRun(attrs, constrained, childPos, childPos + child.nodeSize, trackedFmt);
|
|
902
|
+
const run = buildImageRun(attrs, constrained, childPos, childPos + child.nodeSize, trackedFmt, _options.forceAnchorLayoutInCell);
|
|
842
903
|
runs.push(run);
|
|
843
904
|
return;
|
|
844
905
|
}
|
|
@@ -996,7 +1057,7 @@ function resolveDeletedListMarker(previousListAttrs, listCounterState) {
|
|
|
996
1057
|
if (previousListAttrs.listIsBullet) return "•";
|
|
997
1058
|
return null;
|
|
998
1059
|
}
|
|
999
|
-
function applyDeletedListMarkerAttrs(attrs, change, listCounterState, theme, fontAlternates) {
|
|
1060
|
+
function applyDeletedListMarkerAttrs(attrs, change, listCounterState, theme, fontAlternates, paragraphMarkFormatting) {
|
|
1000
1061
|
const previousListAttrs = toPreviousListAttrs(change.previousFormatting);
|
|
1001
1062
|
const marker = resolveDeletedListMarker(previousListAttrs, listCounterState);
|
|
1002
1063
|
if (!marker) return;
|
|
@@ -1004,7 +1065,8 @@ function applyDeletedListMarkerAttrs(attrs, change, listCounterState, theme, fon
|
|
|
1004
1065
|
attrs.listMarkerRevision = toListMarkerRevision("del", change.info);
|
|
1005
1066
|
if (previousListAttrs.listIsBullet !== void 0) attrs.listIsBullet = previousListAttrs.listIsBullet;
|
|
1006
1067
|
if (previousListAttrs.listMarkerHidden !== void 0) attrs.listMarkerHidden = previousListAttrs.listMarkerHidden;
|
|
1007
|
-
|
|
1068
|
+
const listMarkerFormatting = listMarkerFormattingFor(previousListAttrs.listMarkerFormatting, paragraphMarkFormatting(), theme, fontAlternates);
|
|
1069
|
+
if (listMarkerFormatting) attrs.listMarkerFormatting = listMarkerFormatting;
|
|
1008
1070
|
if (previousListAttrs.listMarkerAlignment) attrs.listMarkerAlignment = previousListAttrs.listMarkerAlignment;
|
|
1009
1071
|
if (previousListAttrs.listMarkerSuffix) attrs.listMarkerSuffix = previousListAttrs.listMarkerSuffix;
|
|
1010
1072
|
}
|
|
@@ -1041,7 +1103,7 @@ const resolveFlowAlignment = (alignment, rightToLeft) => {
|
|
|
1041
1103
|
default: return flow;
|
|
1042
1104
|
}
|
|
1043
1105
|
};
|
|
1044
|
-
function convertParagraphAttrs(pmAttrs, { theme, fontAlternates, listCounterStreams, defaultTabStopTwips }) {
|
|
1106
|
+
function convertParagraphAttrs(pmAttrs, { theme, fontAlternates, listCounterStreams, defaultTabStopTwips, paragraphMarkFormatting }) {
|
|
1045
1107
|
const attrs = {};
|
|
1046
1108
|
if (pmAttrs.alignment) attrs.alignment = resolveFlowAlignment(pmAttrs.alignment, directionIsRtl(pmAttrs.direction));
|
|
1047
1109
|
if (pmAttrs.outlineLevel !== void 0) attrs.outlineLevel = pmAttrs.outlineLevel;
|
|
@@ -1116,9 +1178,10 @@ function convertParagraphAttrs(pmAttrs, { theme, fontAlternates, listCounterStre
|
|
|
1116
1178
|
}
|
|
1117
1179
|
const shadingRgb = pmAttrs.shading?.fill?.rgb?.toUpperCase();
|
|
1118
1180
|
if (shadingRgb && shadingRgb !== "FFFFFF" && shadingRgb !== "FFFFFE") attrs.shading = `#${pmAttrs.shading?.fill?.rgb}`;
|
|
1119
|
-
|
|
1181
|
+
const tabs = mergeParagraphTabStops(pmAttrs.listLevelTabs ?? void 0, pmAttrs.tabs ?? void 0);
|
|
1182
|
+
if (tabs && tabs.length > 0) {
|
|
1120
1183
|
const rightToLeft = directionIsRtl(pmAttrs.direction);
|
|
1121
|
-
attrs.tabs =
|
|
1184
|
+
attrs.tabs = tabs.map((tab) => {
|
|
1122
1185
|
const tabStop = {
|
|
1123
1186
|
val: resolveTabAlignment(tab.alignment, rightToLeft),
|
|
1124
1187
|
pos: tab.position
|
|
@@ -1159,14 +1222,17 @@ function convertParagraphAttrs(pmAttrs, { theme, fontAlternates, listCounterStre
|
|
|
1159
1222
|
else if (pmAttrs.listMarker) attrs.listMarker = pmAttrs.listIsBullet ? convertBulletToUnicode(pmAttrs.listMarker) : pmAttrs.listMarker;
|
|
1160
1223
|
if (pmAttrs.listIsBullet !== void 0) attrs.listIsBullet = pmAttrs.listIsBullet;
|
|
1161
1224
|
if (pmAttrs.listMarkerHidden) attrs.listMarkerHidden = true;
|
|
1162
|
-
if (
|
|
1225
|
+
if (attrs.listMarker !== void 0 || pmAttrs.listMarkerFormatting) {
|
|
1226
|
+
const listMarkerFormatting = listMarkerFormattingFor(pmAttrs.listMarkerFormatting, paragraphMarkFormatting(), theme, fontAlternates);
|
|
1227
|
+
if (listMarkerFormatting) attrs.listMarkerFormatting = listMarkerFormatting;
|
|
1228
|
+
}
|
|
1163
1229
|
if (pmAttrs.listMarkerAlignment) attrs.listMarkerAlignment = pmAttrs.listMarkerAlignment;
|
|
1164
1230
|
if (pmAttrs.listMarkerSuffix) attrs.listMarkerSuffix = pmAttrs.listMarkerSuffix;
|
|
1165
1231
|
if (pmAttrs.listMarkerSecondSlotOffsetTwips !== void 0) attrs.listMarkerSecondSlotOffsetTwips = pmAttrs.listMarkerSecondSlotOffsetTwips;
|
|
1166
1232
|
if (!pmAttrs.numPr) {
|
|
1167
1233
|
const numberingRemovedChange = propertyChanges.find(isRemovedNumberingChange);
|
|
1168
1234
|
if (numberingRemovedChange) {
|
|
1169
|
-
applyDeletedListMarkerAttrs(attrs, numberingRemovedChange, void 0, theme, fontAlternates);
|
|
1235
|
+
applyDeletedListMarkerAttrs(attrs, numberingRemovedChange, void 0, theme, fontAlternates, paragraphMarkFormatting);
|
|
1170
1236
|
if (resolvedMarker !== null) {
|
|
1171
1237
|
attrs.listMarker = resolvedMarker;
|
|
1172
1238
|
attrs.listMarkerRevision = toListMarkerRevision("del", numberingRemovedChange.info);
|
|
@@ -1231,6 +1297,18 @@ function hasOnlyHiddenTextRuns(runs) {
|
|
|
1231
1297
|
function hasVisibleParagraphPayload(attrs) {
|
|
1232
1298
|
return attrs.listMarker !== void 0 && !attrs.listMarkerHidden || attrs.borders?.top !== void 0 || attrs.borders?.bottom !== void 0 || attrs.borders?.left !== void 0 || attrs.borders?.right !== void 0 || attrs.borders?.between !== void 0 || attrs.borders?.bar !== void 0 || attrs.shading !== void 0;
|
|
1233
1299
|
}
|
|
1300
|
+
/** Whether any `w:pBdr` side draws a rule (`none`/`nil` sides draw nothing). */
|
|
1301
|
+
function drawsParagraphBorder(borders) {
|
|
1302
|
+
if (!borders) return false;
|
|
1303
|
+
return [
|
|
1304
|
+
borders.top,
|
|
1305
|
+
borders.bottom,
|
|
1306
|
+
borders.left,
|
|
1307
|
+
borders.right,
|
|
1308
|
+
borders.between,
|
|
1309
|
+
borders.bar
|
|
1310
|
+
].some((border) => border?.style !== void 0 && !statesNoBorder(border.style));
|
|
1311
|
+
}
|
|
1234
1312
|
function convertParagraph(node, startPos, options, pageBreaks) {
|
|
1235
1313
|
const pmAttrs = expectParagraphAttrs(node);
|
|
1236
1314
|
const runs = paragraphToRuns(node, startPos, options, pageBreaks);
|
|
@@ -1238,8 +1316,10 @@ function convertParagraph(node, startPos, options, pageBreaks) {
|
|
|
1238
1316
|
theme: options.theme,
|
|
1239
1317
|
fontAlternates: options.fontAlternates,
|
|
1240
1318
|
listCounterStreams: options.listCounterStreams,
|
|
1241
|
-
defaultTabStopTwips: options.defaultTabStopTwips
|
|
1319
|
+
defaultTabStopTwips: options.defaultTabStopTwips,
|
|
1320
|
+
paragraphMarkFormatting: () => resolveParagraphMarkFormatting(pmAttrs, options.styleResolver)
|
|
1242
1321
|
});
|
|
1322
|
+
if (options.numberingTabIgnoresIndent && attrs.listMarker !== void 0) attrs.listNumberingTabIgnoresIndent = true;
|
|
1243
1323
|
if (options.defaultFont !== void 0) attrs.defaultFontFamily ??= options.defaultFont;
|
|
1244
1324
|
if (options.defaultSize !== void 0) attrs.defaultFontSize ??= options.defaultSize;
|
|
1245
1325
|
if (options.lineBreakRules) attrs.lineBreakRules = options.lineBreakRules;
|
|
@@ -1248,8 +1328,9 @@ function convertParagraph(node, startPos, options, pageBreaks) {
|
|
|
1248
1328
|
const defaultTextFormatting = pmAttrs.defaultTextFormatting;
|
|
1249
1329
|
if (runs.length === 0 || hasOnlyVisuallyEmptyTextRuns(runs)) {
|
|
1250
1330
|
if (pmAttrs._originalFormatting && Object.entries(pmAttrs._originalFormatting).some(([key, value]) => key !== "runProperties" && value !== void 0 && value !== null)) attrs.hasDirectParagraphFormatting = true;
|
|
1251
|
-
const
|
|
1252
|
-
if (
|
|
1331
|
+
const directParagraphMarkFormatting = pmAttrs._originalFormatting?.runProperties;
|
|
1332
|
+
if (directParagraphMarkFormatting && Object.values(directParagraphMarkFormatting).some((value) => value !== void 0 && value !== null)) attrs.hasDirectParagraphMarkFormatting = true;
|
|
1333
|
+
const paragraphMarkFormatting = resolveParagraphMarkOwnFormatting(directParagraphMarkFormatting, options.styleResolver);
|
|
1253
1334
|
if (paragraphMarkFormatting?.fontSize !== void 0) attrs.defaultFontSize = paragraphMarkFormatting.fontSize / 2;
|
|
1254
1335
|
const paragraphMarkFontFamily = paragraphMarkFormatting?.fontFamily ? resolveWesternThemeFont(paragraphMarkFormatting.fontFamily, options.theme) : void 0;
|
|
1255
1336
|
if (paragraphMarkFontFamily) {
|
|
@@ -1393,14 +1474,18 @@ function splitParagraphAtPageBreaks({ pageBreaks, paragraph, splitPageBreakAndPa
|
|
|
1393
1474
|
fragmentStart = pageBreak.pmEnd;
|
|
1394
1475
|
}
|
|
1395
1476
|
const remainingRuns = partitioned.remaining;
|
|
1477
|
+
let paragraphMarkCarrier;
|
|
1396
1478
|
if (remainingRuns.length > 0) appendParagraph(remainingRuns, fragmentStart, paragraph.pmEnd ?? fragmentStart);
|
|
1397
1479
|
else if (paragraph.runs.length === 0 || splitPageBreakAndParagraphMark && pageBreaks.at(-1)?.pmEnd === (paragraph.pmEnd ?? fragmentStart) - 1) {
|
|
1398
1480
|
appendParagraph([], fragmentStart, paragraph.pmEnd ?? fragmentStart);
|
|
1399
1481
|
const carrier = result.at(-1);
|
|
1400
|
-
if (carrier?.kind === "paragraph" && !splitPageBreakAndParagraphMark && !hasVisibleParagraphPayload(carrier.attrs ?? {}))
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1482
|
+
if (carrier?.kind === "paragraph" && !splitPageBreakAndParagraphMark && !hasVisibleParagraphPayload(carrier.attrs ?? {})) {
|
|
1483
|
+
carrier.attrs = {
|
|
1484
|
+
...carrier.attrs,
|
|
1485
|
+
suppressEmptyParagraphHeight: true
|
|
1486
|
+
};
|
|
1487
|
+
paragraphMarkCarrier = carrier;
|
|
1488
|
+
}
|
|
1404
1489
|
}
|
|
1405
1490
|
const fragments = result.filter((block) => block.kind === "paragraph");
|
|
1406
1491
|
for (let index = 0; index < fragments.length; index += 1) {
|
|
@@ -1410,8 +1495,20 @@ function splitParagraphAtPageBreaks({ pageBreaks, paragraph, splitPageBreakAndPa
|
|
|
1410
1495
|
if (attrs) fragment.attrs = attrs;
|
|
1411
1496
|
else delete fragment.attrs;
|
|
1412
1497
|
}
|
|
1498
|
+
if (paragraphMarkCarrier?.attrs) paragraphMarkCarrier.attrs = withoutParagraphSpacing(paragraphMarkCarrier.attrs);
|
|
1413
1499
|
return result;
|
|
1414
1500
|
}
|
|
1501
|
+
function withoutParagraphSpacing(source) {
|
|
1502
|
+
const attrs = { ...source };
|
|
1503
|
+
if (attrs.spacing) {
|
|
1504
|
+
const { before: _before, after: _after, ...lineSpacing } = attrs.spacing;
|
|
1505
|
+
if (Object.keys(lineSpacing).length > 0) attrs.spacing = lineSpacing;
|
|
1506
|
+
else delete attrs.spacing;
|
|
1507
|
+
}
|
|
1508
|
+
delete attrs.automaticSpacing;
|
|
1509
|
+
delete attrs.spacingExplicit;
|
|
1510
|
+
return attrs;
|
|
1511
|
+
}
|
|
1415
1512
|
/**
|
|
1416
1513
|
* Word keeps a final empty body paragraph after a table as an editable anchor,
|
|
1417
1514
|
* but that final anchor does not create a page of its own. Earlier authored
|
|
@@ -1513,12 +1610,22 @@ function convertTableCell(node, startPos, options, tableCellMargins) {
|
|
|
1513
1610
|
const authoredPageBreakPosition = options.firstPageBreakRunPosition(node);
|
|
1514
1611
|
const leadingParagraph = node.firstChild?.type.name === "paragraph" && countPageBreakRuns(node) === 1 && options.firstPageBreakRunPosition(node.firstChild) === authoredPageBreakPosition ? node.firstChild : void 0;
|
|
1515
1612
|
const pageBreaks = [];
|
|
1516
|
-
|
|
1613
|
+
const convertCellChild = (child, childStart) => {
|
|
1517
1614
|
if (child.type.name === "paragraph") {
|
|
1518
|
-
const block = convertParagraph(child,
|
|
1615
|
+
const block = convertParagraph(child, childStart, options, child === leadingParagraph ? pageBreaks : void 0);
|
|
1519
1616
|
blocks.push(block);
|
|
1520
|
-
} else if (child.type.name === "table") blocks.push(convertTable(child,
|
|
1521
|
-
else if (child.type.name === "textBox") blocks.push(convertTextBoxNode(child,
|
|
1617
|
+
} else if (child.type.name === "table") blocks.push(convertTable(child, childStart, options));
|
|
1618
|
+
else if (child.type.name === "textBox") blocks.push(convertTextBoxNode(child, childStart, options));
|
|
1619
|
+
else if (child.type.name === "blockSdt") {
|
|
1620
|
+
let sdtChildStart = childStart + 1;
|
|
1621
|
+
child.forEach((sdtChild) => {
|
|
1622
|
+
convertCellChild(sdtChild, sdtChildStart);
|
|
1623
|
+
sdtChildStart += sdtChild.nodeSize;
|
|
1624
|
+
});
|
|
1625
|
+
}
|
|
1626
|
+
};
|
|
1627
|
+
node.forEach((child) => {
|
|
1628
|
+
convertCellChild(child, offset);
|
|
1522
1629
|
offset += child.nodeSize;
|
|
1523
1630
|
});
|
|
1524
1631
|
const trailingBlock = blocks.at(-1);
|
|
@@ -1669,6 +1776,8 @@ function convertTable(node, startPos, options) {
|
|
|
1669
1776
|
if (originalFormatting?.layout !== void 0) tableBlock.layout = originalFormatting.layout;
|
|
1670
1777
|
if (justification) tableBlock.justification = justification;
|
|
1671
1778
|
if (indentPx !== void 0) tableBlock.indent = indentPx;
|
|
1779
|
+
const cellSpacing = originalFormatting?.cellSpacing;
|
|
1780
|
+
if (cellSpacing?.type === "dxa" && cellSpacing.value > 0) tableBlock.cellSpacing = twipsToPixels(cellSpacing.value);
|
|
1672
1781
|
if (options.tableIndentCompatibility && !dropsAuthoredIndent) tableBlock.indentCompatibility = options.tableIndentCompatibility;
|
|
1673
1782
|
if (floatingPx) tableBlock.floating = floatingPx;
|
|
1674
1783
|
if (rightToLeft) tableBlock.bidi = true;
|
|
@@ -1718,6 +1827,26 @@ function convertImage(node, startPos, pageContentHeight) {
|
|
|
1718
1827
|
if (imageBorderStyle) imgBlock.borderStyle = imageBorderStyle;
|
|
1719
1828
|
return imgBlock;
|
|
1720
1829
|
}
|
|
1830
|
+
const EMUS_PER_PIXEL = 9525;
|
|
1831
|
+
/**
|
|
1832
|
+
* The `wp:effectExtent` of an inline (`wp:inline`) box, in unrounded pixels.
|
|
1833
|
+
*
|
|
1834
|
+
* An inline object occupies its extent plus the effect extent on every side,
|
|
1835
|
+
* so the box itself sits `l`/`t` inside the space the line gives it. On a
|
|
1836
|
+
* `wp:anchor` the position offsets already locate the shape's own extent, so
|
|
1837
|
+
* the reservation only affects wrapping and is not applied here.
|
|
1838
|
+
*/
|
|
1839
|
+
function inlineEffectExtentPx(wrapType, extent) {
|
|
1840
|
+
if (wrapType !== void 0 && wrapType !== "inline" || extent === void 0) return;
|
|
1841
|
+
const px = (emu) => Math.max(0, (emu ?? 0) / EMUS_PER_PIXEL);
|
|
1842
|
+
const resolved = {
|
|
1843
|
+
top: px(extent.top),
|
|
1844
|
+
bottom: px(extent.bottom),
|
|
1845
|
+
left: px(extent.left),
|
|
1846
|
+
right: px(extent.right)
|
|
1847
|
+
};
|
|
1848
|
+
return resolved.top === 0 && resolved.bottom === 0 && resolved.left === 0 && resolved.right === 0 ? void 0 : resolved;
|
|
1849
|
+
}
|
|
1721
1850
|
/** Convert a textBox PM node to a TextBoxBlock. */
|
|
1722
1851
|
function convertTextBoxNode(node, startPos, opts) {
|
|
1723
1852
|
const attrs = expectTextBoxAttrs(node);
|
|
@@ -1763,6 +1892,19 @@ function convertTextBoxNode(node, startPos, opts) {
|
|
|
1763
1892
|
if (attrs.distLeft !== void 0) textBox.distLeft = attrs.distLeft;
|
|
1764
1893
|
if (attrs.distRight !== void 0) textBox.distRight = attrs.distRight;
|
|
1765
1894
|
if (attrs.position !== void 0) textBox.position = attrs.position;
|
|
1895
|
+
const effectExtent = inlineEffectExtentPx(attrs.wrapType, attrs.wrapEffectExtentSlots?.drawing);
|
|
1896
|
+
if (effectExtent !== void 0) textBox.effectExtent = effectExtent;
|
|
1897
|
+
const host = textBoxHostParagraph(node);
|
|
1898
|
+
if (host && attrs.position === void 0 && (attrs.wrapType ?? "inline") === "inline") {
|
|
1899
|
+
const hostAttrs = expectParagraphAttrs(host);
|
|
1900
|
+
textBox.hostParagraph = convertParagraphAttrs(hostAttrs, {
|
|
1901
|
+
theme: opts.theme,
|
|
1902
|
+
fontAlternates: opts.fontAlternates,
|
|
1903
|
+
listCounterStreams: opts.listCounterStreams,
|
|
1904
|
+
defaultTabStopTwips: opts.defaultTabStopTwips,
|
|
1905
|
+
paragraphMarkFormatting: () => resolveParagraphMarkFormatting(hostAttrs, opts.styleResolver)
|
|
1906
|
+
});
|
|
1907
|
+
}
|
|
1766
1908
|
if (attrs._docxGroupId !== void 0) setTextBoxGroupId(textBox, attrs._docxGroupId);
|
|
1767
1909
|
const anchorBlockId = attrs._docxAnchorId ? opts.textBoxAnchorBlockIds.get(attrs._docxAnchorId) : void 0;
|
|
1768
1910
|
if (anchorBlockId !== void 0) Reflect.set(textBox, TEXT_BOX_ANCHOR_BLOCK_ID, anchorBlockId);
|
|
@@ -1804,8 +1946,12 @@ function applySectionStartsToBoundaries(blocks, finalSectionStart) {
|
|
|
1804
1946
|
* continuous section resumes there too. Accept both the legacy generated
|
|
1805
1947
|
* carrier and the inline atom's one-token paragraph-mark fragment; neither is
|
|
1806
1948
|
* a physical layout boundary.
|
|
1949
|
+
*
|
|
1950
|
+
* A section break in `breaksWithoutMarker` ends at an empty w:sectPr paragraph
|
|
1951
|
+
* that projected no block, so a page break just before it ends an earlier
|
|
1952
|
+
* paragraph and still starts the next page.
|
|
1807
1953
|
*/
|
|
1808
|
-
function coalesceTrailingPageBreakBeforeContinuousSection(blocks, splitPageBreakAndParagraphMark) {
|
|
1954
|
+
function coalesceTrailingPageBreakBeforeContinuousSection(blocks, splitPageBreakAndParagraphMark, breaksWithoutMarker) {
|
|
1809
1955
|
if (splitPageBreakAndParagraphMark) return [...blocks];
|
|
1810
1956
|
const result = [];
|
|
1811
1957
|
for (let index = 0; index < blocks.length; index += 1) {
|
|
@@ -1815,8 +1961,8 @@ function coalesceTrailingPageBreakBeforeContinuousSection(blocks, splitPageBreak
|
|
|
1815
1961
|
const isLegacyGeneratedCarrier = block?.kind === "pageBreak" && carrier?.kind === "paragraph" && carrier.runs.length === 0 && block.pmStart !== void 0 && block.pmStart === carrier.pmStart && carrier.pmStart === carrier.pmEnd;
|
|
1816
1962
|
const isInlineParagraphMarkCarrier = block?.kind === "pageBreak" && carrier?.kind === "paragraph" && carrier.runs.length === 0 && block.pmEnd !== void 0 && carrier.pmStart === block.pmEnd && carrier.pmEnd === block.pmEnd + 1;
|
|
1817
1963
|
const isUnresolvedInsertion = block?.kind === "pageBreak" && block.isInsertion === true;
|
|
1818
|
-
if (block?.kind === "pageBreak" && !isUnresolvedInsertion && carrier?.kind === "sectionBreak" && carrier.type === "continuous") continue;
|
|
1819
|
-
if (block?.kind === "pageBreak" && !isUnresolvedInsertion && (isLegacyGeneratedCarrier || isInlineParagraphMarkCarrier) && section?.kind === "sectionBreak" && section.type === "continuous") {
|
|
1964
|
+
if (block?.kind === "pageBreak" && !isUnresolvedInsertion && carrier?.kind === "sectionBreak" && carrier.type === "continuous" && !breaksWithoutMarker.has(carrier.id)) continue;
|
|
1965
|
+
if (block?.kind === "pageBreak" && !isUnresolvedInsertion && (isLegacyGeneratedCarrier || isInlineParagraphMarkCarrier) && section?.kind === "sectionBreak" && section.type === "continuous" && !breaksWithoutMarker.has(section.id)) {
|
|
1820
1966
|
index += 1;
|
|
1821
1967
|
continue;
|
|
1822
1968
|
}
|
|
@@ -1884,6 +2030,7 @@ function toFlowBlocks(doc, options = {}) {
|
|
|
1884
2030
|
},
|
|
1885
2031
|
firstPageBreakRunPosition,
|
|
1886
2032
|
textBoxAnchorBlockIds: /* @__PURE__ */ new Map(),
|
|
2033
|
+
numberingTabIgnoresIndent: doc.attrs["_doNotUseIndentAsNumberingTabStop"] === true,
|
|
1887
2034
|
styleResolver: createStyleEngine(options.styles),
|
|
1888
2035
|
numberedRefResults: resolveNumberedRefFields(doc, {
|
|
1889
2036
|
listCounterState: cloneListCounterState(listCounterState),
|
|
@@ -1917,11 +2064,72 @@ function toFlowBlocks(doc, options = {}) {
|
|
|
1917
2064
|
const pageBreaks = [];
|
|
1918
2065
|
const paragraph = convertParagraph(node, pos, opts, pageBreaks);
|
|
1919
2066
|
if (stripLeadingLineBreak && paragraph.runs.at(0)?.kind === "lineBreak") paragraph.runs.shift();
|
|
1920
|
-
|
|
2067
|
+
const projected = splitParagraphAtPageBreaks({
|
|
1921
2068
|
pageBreaks,
|
|
1922
2069
|
paragraph,
|
|
1923
2070
|
splitPageBreakAndParagraphMark: options.splitPageBreakAndParagraphMark === true
|
|
1924
|
-
})
|
|
2071
|
+
});
|
|
2072
|
+
if (pageBreaks.length > 0) placeTextBoxAnchorsBeforePageBreaks(node, pos, projected);
|
|
2073
|
+
for (const block of projected) trackedPush(block);
|
|
2074
|
+
};
|
|
2075
|
+
/** Blocks emitted before the current section's first block. */
|
|
2076
|
+
let sectionStartBlockCount = 0;
|
|
2077
|
+
/** Section breaks whose empty w:sectPr paragraph projected no block. */
|
|
2078
|
+
const sectionBreaksWithoutMarker = /* @__PURE__ */ new Set();
|
|
2079
|
+
/**
|
|
2080
|
+
* The page break a text box anchored in an earlier part of its paragraph
|
|
2081
|
+
* must be laid out before, keyed by `_docxAnchorId`.
|
|
2082
|
+
*/
|
|
2083
|
+
const textBoxPageBreakByAnchorId = /* @__PURE__ */ new Map();
|
|
2084
|
+
/** Text boxes held back until every block is projected. */
|
|
2085
|
+
const textBoxesBeforePageBreak = /* @__PURE__ */ new Map();
|
|
2086
|
+
/**
|
|
2087
|
+
* An anchored object sits on the page holding its anchor run. When that run
|
|
2088
|
+
* precedes a `w:br w:type="page"` in the same paragraph, the object belongs
|
|
2089
|
+
* to the page the break ends. Either way its paragraph-relative position is
|
|
2090
|
+
* measured from the part of the paragraph holding the anchor.
|
|
2091
|
+
*/
|
|
2092
|
+
const placeTextBoxAnchorsBeforePageBreaks = (node, pos, projected) => {
|
|
2093
|
+
let breakIndex = 0;
|
|
2094
|
+
node.descendants((child, childOffset) => {
|
|
2095
|
+
if (child.type.name !== "textBoxAnchor") return true;
|
|
2096
|
+
const anchorPos = pos + 1 + childOffset;
|
|
2097
|
+
while (breakIndex < projected.length) {
|
|
2098
|
+
const block = projected[breakIndex];
|
|
2099
|
+
if (block?.kind === "pageBreak" && (block.pmStart ?? 0) > anchorPos) break;
|
|
2100
|
+
breakIndex += 1;
|
|
2101
|
+
}
|
|
2102
|
+
const { anchorId } = expectTextBoxAnchorAttrs(child);
|
|
2103
|
+
const pageBreak = projected[breakIndex];
|
|
2104
|
+
if (pageBreak?.kind === "pageBreak") textBoxPageBreakByAnchorId.set(anchorId, pageBreak);
|
|
2105
|
+
const anchorFragment = pageBreak ? projected[breakIndex - 1] : projected.at(-1);
|
|
2106
|
+
if (anchorFragment?.kind === "paragraph") opts.textBoxAnchorBlockIds.set(anchorId, anchorFragment.id);
|
|
2107
|
+
else opts.textBoxAnchorBlockIds.delete(anchorId);
|
|
2108
|
+
return false;
|
|
2109
|
+
});
|
|
2110
|
+
};
|
|
2111
|
+
const pushTextBox = (node, pos) => {
|
|
2112
|
+
const textBox = convertTextBoxNode(node, pos, opts);
|
|
2113
|
+
const anchorId = expectTextBoxAttrs(node)._docxAnchorId;
|
|
2114
|
+
const pageBreak = anchorId === void 0 ? void 0 : textBoxPageBreakByAnchorId.get(anchorId);
|
|
2115
|
+
if (!pageBreak) {
|
|
2116
|
+
trackedPush(textBox);
|
|
2117
|
+
return;
|
|
2118
|
+
}
|
|
2119
|
+
tagBlockWithSdtStack(textBox);
|
|
2120
|
+
const heldBack = textBoxesBeforePageBreak.get(pageBreak);
|
|
2121
|
+
if (heldBack) heldBack.push(textBox);
|
|
2122
|
+
else textBoxesBeforePageBreak.set(pageBreak, [textBox]);
|
|
2123
|
+
};
|
|
2124
|
+
/** Put every held-back text box just before its page break, in one pass. */
|
|
2125
|
+
const releaseTextBoxesBeforePageBreaks = () => {
|
|
2126
|
+
if (textBoxesBeforePageBreak.size === 0) return;
|
|
2127
|
+
const inFlowOrder = blocks.splice(0);
|
|
2128
|
+
for (const block of inFlowOrder) {
|
|
2129
|
+
const heldBack = block.kind === "pageBreak" ? textBoxesBeforePageBreak.get(block) : void 0;
|
|
2130
|
+
for (const textBox of heldBack ?? []) blocks.push(textBox);
|
|
2131
|
+
blocks.push(block);
|
|
2132
|
+
}
|
|
1925
2133
|
};
|
|
1926
2134
|
const trailingPageBreakSectionPositions = /* @__PURE__ */ new Set();
|
|
1927
2135
|
const consumedPageBreakPositions = /* @__PURE__ */ new Set();
|
|
@@ -2004,7 +2212,11 @@ function toFlowBlocks(doc, options = {}) {
|
|
|
2004
2212
|
const hasListFormatting = pmAttrs.numPr !== null && pmAttrs.numPr !== void 0 || pmAttrs.listMarker !== null && pmAttrs.listMarker !== void 0;
|
|
2005
2213
|
const firstChild = node.firstChild;
|
|
2006
2214
|
const startsWithColumnBreak = firstChild?.type.name === "hardBreak" && expectHardBreakAttrs(firstChild).breakType === "column";
|
|
2007
|
-
|
|
2215
|
+
const isStandaloneColumnBreak = node.childCount === 1 && startsWithColumnBreak;
|
|
2216
|
+
const isEmptySectionMark = hasSectionBreak && node.content.size === 0;
|
|
2217
|
+
const opensSection = blocks.length === sectionStartBlockCount;
|
|
2218
|
+
let markerDropped = false;
|
|
2219
|
+
if (isStandaloneColumnBreak) {
|
|
2008
2220
|
const columnBreak = {
|
|
2009
2221
|
kind: "columnBreak",
|
|
2010
2222
|
id: nextBlockId(),
|
|
@@ -2021,7 +2233,12 @@ function toFlowBlocks(doc, options = {}) {
|
|
|
2021
2233
|
};
|
|
2022
2234
|
trackedPush(columnBreak);
|
|
2023
2235
|
pushParagraphProjection(node, pos, true);
|
|
2024
|
-
} else if (
|
|
2236
|
+
} else if (!isEmptySectionMark) pushParagraphProjection(node, pos);
|
|
2237
|
+
else if (hasListFormatting || drawsParagraphBorder(pmAttrs.borders) || opensSection) {
|
|
2238
|
+
pushParagraphProjection(node, pos);
|
|
2239
|
+
const mark = blocks.at(-1);
|
|
2240
|
+
if (!opensSection && mark?.kind === "paragraph" && mark.attrs?.pageBreakBefore) delete mark.attrs.pageBreakBefore;
|
|
2241
|
+
} else markerDropped = true;
|
|
2025
2242
|
if (hasSectionBreak) {
|
|
2026
2243
|
if (trailingPageBreakSectionPositions.has(pos)) {
|
|
2027
2244
|
const sourceParagraph = blocks.at(-1);
|
|
@@ -2093,6 +2310,8 @@ function toFlowBlocks(doc, options = {}) {
|
|
|
2093
2310
|
if (columns) sectionBreak.columns = columns;
|
|
2094
2311
|
}
|
|
2095
2312
|
trackedPush(sectionBreak);
|
|
2313
|
+
sectionStartBlockCount = blocks.length;
|
|
2314
|
+
if (markerDropped) sectionBreaksWithoutMarker.add(sectionBreak.id);
|
|
2096
2315
|
}
|
|
2097
2316
|
break;
|
|
2098
2317
|
}
|
|
@@ -2105,7 +2324,7 @@ function toFlowBlocks(doc, options = {}) {
|
|
|
2105
2324
|
break;
|
|
2106
2325
|
}
|
|
2107
2326
|
case "textBox":
|
|
2108
|
-
|
|
2327
|
+
pushTextBox(node, pos);
|
|
2109
2328
|
break;
|
|
2110
2329
|
case "horizontalRule":
|
|
2111
2330
|
case "pageBreak": {
|
|
@@ -2125,15 +2344,19 @@ function toFlowBlocks(doc, options = {}) {
|
|
|
2125
2344
|
doc.forEach((node, nodeOffset) => {
|
|
2126
2345
|
visit(node, offset + nodeOffset);
|
|
2127
2346
|
});
|
|
2347
|
+
releaseTextBoxesBeforePageBreaks();
|
|
2128
2348
|
reserveLeadingEmptyOutlineHeight(blocks);
|
|
2129
|
-
|
|
2130
|
-
|
|
2349
|
+
const trailingEndnoteIds = options.trailingEndnoteIds;
|
|
2350
|
+
if (!(trailingEndnoteIds !== void 0 && trailingEndnoteIds.size > 0 && collectNoteRefs(blocks, "endnoteRefId").some(({ noteId }) => trailingEndnoteIds.has(noteId)))) {
|
|
2351
|
+
suppressFinalEmptyParagraphAfterTable(blocks);
|
|
2352
|
+
suppressFinalParagraphInRepeatedEmptySuffix(blocks);
|
|
2353
|
+
}
|
|
2131
2354
|
const mergedBlocks = mergeRunInParagraphs(blocks);
|
|
2132
2355
|
const tableCellLinePitch = doc.attrs["_adjustLineHeightInTable"] === true ? "sectionGrid" : void 0;
|
|
2133
2356
|
return groupParagraphFrames(coalesceTrailingPageBreakBeforeContinuousSection(applySectionStartsToBoundaries(applySectionDocumentGrid(mergedBlocks, {
|
|
2134
2357
|
finalLinePitchTwips: opts.finalSectionDocumentGridLinePitchTwips,
|
|
2135
2358
|
tableCellLinePitch
|
|
2136
|
-
}), readFinalSectionStart(doc)), options.splitPageBreakAndParagraphMark === true), nextBlockId);
|
|
2359
|
+
}), readFinalSectionStart(doc)), options.splitPageBreakAndParagraphMark === true, sectionBreaksWithoutMarker), nextBlockId);
|
|
2137
2360
|
}
|
|
2138
2361
|
function applySectionDocumentGrid(blocks, { finalLinePitchTwips, tableCellLinePitch }) {
|
|
2139
2362
|
const result = [...blocks];
|
|
@@ -2234,4 +2457,4 @@ function mergeRunInParagraphs(blocks) {
|
|
|
2234
2457
|
return out;
|
|
2235
2458
|
}
|
|
2236
2459
|
//#endregion
|
|
2237
|
-
export { WORD_UNSPECIFIED_FONT_SIZE, convertBorderSpecToLayout, formatCounter, resetBlockIdCounter, resolveListTemplate, toFlowBlocks };
|
|
2460
|
+
export { WORD_UNSPECIFIED_FONT_SIZE, collectNoteRefs, convertBorderSpecToLayout, formatCounter, isNoteReferenceMarkXml, resetBlockIdCounter, resolveListTemplate, toFlowBlocks };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { getHeaderRowsHeight } from "../../layout-engine/index.js";
|
|
2
2
|
import { measuredLineRangeHeight } from "../../layout-engine/lineFlow.js";
|
|
3
3
|
import { getTableCellContentWidth } from "../../layout-engine/measure/tableCellFloating.js";
|
|
4
|
-
import { buildTableCellGrid, buildTableCellPlacements } from "../../layout-engine/measure/tableCellGrid.js";
|
|
4
|
+
import { buildTableCellGrid, buildTableCellPlacements, getTableCellSpacingInsetsY } from "../../layout-engine/measure/tableCellGrid.js";
|
|
5
5
|
import { resolveTableCellPadding } from "../../layout-engine/types.js";
|
|
6
6
|
//#region src/layout-bridge/engine/hitTest.ts
|
|
7
7
|
/**
|
|
@@ -222,7 +222,8 @@ function hitTestTableCell(pageHit, blocks, measures, pagePoint) {
|
|
|
222
222
|
const cellPlacements = buildTableCellPlacements({
|
|
223
223
|
grid: buildTableCellGrid(tableBlock.rows, tableMeasure.columnWidths.length),
|
|
224
224
|
columnWidths: tableMeasure.columnWidths,
|
|
225
|
-
bidi: tableBlock.bidi === true
|
|
225
|
+
bidi: tableBlock.bidi === true,
|
|
226
|
+
cellSpacing: tableBlock.cellSpacing
|
|
226
227
|
});
|
|
227
228
|
let nearestDistance = Infinity;
|
|
228
229
|
for (let c = 0; c < row.cells.length; c++) {
|
|
@@ -267,7 +268,8 @@ function hitTestTableCell(pageHit, blocks, measures, pagePoint) {
|
|
|
267
268
|
const cellLocalX = localX - cellLeft - padLeft;
|
|
268
269
|
const cellContentWidth = getTableCellContentWidth(cell, cellMeasure);
|
|
269
270
|
const clipOffset = isClickOnHeader ? 0 : tableFragment.topClip ?? 0;
|
|
270
|
-
const
|
|
271
|
+
const cellSpacingTop = getTableCellSpacingInsetsY(tableBlock.cellSpacing, rowIndex, cell.rowSpan ?? 1, tableBlock.rows.length).top;
|
|
272
|
+
const cellLocalY = localY - rowTop + clipOffset - cellSpacingTop;
|
|
271
273
|
return {
|
|
272
274
|
fragment: tableFragment,
|
|
273
275
|
block: tableBlock,
|
|
@@ -4,7 +4,7 @@ import { buildRunFontStyle } from "../../layout-engine/measure/measureHelpers.js
|
|
|
4
4
|
import { measureParagraph } from "../../layout-engine/measure/measureParagraph.js";
|
|
5
5
|
import { measureRun } from "../../layout-engine/measure/measureProvider.js";
|
|
6
6
|
import { buildTableCellFloatingZones, getTableCellContentWidth, getTableCellFloatingImages } from "../../layout-engine/measure/tableCellFloating.js";
|
|
7
|
-
import { buildTableCellGrid, buildTableCellPlacements } from "../../layout-engine/measure/tableCellGrid.js";
|
|
7
|
+
import { buildTableCellGrid, buildTableCellPlacements, getTableCellSpacingInsetsY } from "../../layout-engine/measure/tableCellGrid.js";
|
|
8
8
|
import { resolveTableCellPadding } from "../../layout-engine/types.js";
|
|
9
9
|
import { normalizeHorizontalScalePercent } from "../../utils/horizontalScale.js";
|
|
10
10
|
import { resolvePhysicalParagraphInlineLayout } from "../../utils/paragraphInlineLayout.js";
|
|
@@ -312,7 +312,8 @@ function selectionToRects(layout, blocks, measures, from, to) {
|
|
|
312
312
|
cellPlacements = buildTableCellPlacements({
|
|
313
313
|
grid: buildTableCellGrid(tableBlock.rows, tableMeasure.columnWidths.length),
|
|
314
314
|
columnWidths: tableMeasure.columnWidths,
|
|
315
|
-
bidi: tableBlock.bidi === true
|
|
315
|
+
bidi: tableBlock.bidi === true,
|
|
316
|
+
cellSpacing: tableBlock.cellSpacing
|
|
316
317
|
});
|
|
317
318
|
tableCellPlacements.set(tableBlock, cellPlacements);
|
|
318
319
|
}
|
|
@@ -333,7 +334,8 @@ function selectionToRects(layout, blocks, measures, from, to) {
|
|
|
333
334
|
const contentWidth = getTableCellContentWidth(cell, cellMeasure);
|
|
334
335
|
const floatingZones = buildTableCellFloatingZones(getTableCellFloatingImages(cell, cellMeasure, contentWidth), contentWidth);
|
|
335
336
|
const contentOffsetX = getCellContentOffsetX(cell);
|
|
336
|
-
const
|
|
337
|
+
const spacing = getTableCellSpacingInsetsY(tableBlock.cellSpacing, rowIndex, cell.rowSpan ?? 1, tableBlock.rows.length);
|
|
338
|
+
const contentOffsetY = spacing.top + getCellContentOffsetY(cell, cellMeasure, Math.max(0, rowMeasure.height - spacing.top - spacing.bottom));
|
|
337
339
|
let blockY = 0;
|
|
338
340
|
for (let blockIdx = 0; blockIdx < cell.blocks.length; blockIdx++) {
|
|
339
341
|
const cellBlock = cell.blocks[blockIdx];
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
//#region src/layout-engine/anchorLayoutInCellCompatibility.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* `true` when the document's compatibility mode forces every table-cell
|
|
4
|
+
* anchor to lay out inside its cell, ignoring an authored
|
|
5
|
+
* `layoutInCell="0"`. `false` means the authored attribute (or its
|
|
6
|
+
* OOXML default of "confined to the cell") should be honoured as stated.
|
|
7
|
+
*/
|
|
8
|
+
declare const resolveAnchorLayoutInCellCompatibility: (compatibilityMode: number | undefined) => boolean;
|
|
9
|
+
//#endregion
|
|
10
|
+
export { resolveAnchorLayoutInCellCompatibility };
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
//#region src/layout-engine/anchorLayoutInCellCompatibility.ts
|
|
2
|
+
/**
|
|
3
|
+
* First `compatibilityMode` that ignores `wp:anchor/@layoutInCell="0"`.
|
|
4
|
+
*
|
|
5
|
+
* From `compatibilityMode` 15 onward, an anchor inside a table cell is laid
|
|
6
|
+
* out in the cell regardless of what `wp:anchor/@layoutInCell` states: the
|
|
7
|
+
* attribute stops opting the anchor out of the cell's coordinate space.
|
|
8
|
+
* Earlier modes still honour an explicit `layoutInCell="0"` and let the
|
|
9
|
+
* anchor position itself relative to the page. A document that declares no
|
|
10
|
+
* `compatSetting` at all is read with the oldest semantics, so it honours
|
|
11
|
+
* the attribute too.
|
|
12
|
+
*/
|
|
13
|
+
const FIRST_ALWAYS_IN_CELL_COMPATIBILITY_MODE = 15;
|
|
14
|
+
/**
|
|
15
|
+
* `true` when the document's compatibility mode forces every table-cell
|
|
16
|
+
* anchor to lay out inside its cell, ignoring an authored
|
|
17
|
+
* `layoutInCell="0"`. `false` means the authored attribute (or its
|
|
18
|
+
* OOXML default of "confined to the cell") should be honoured as stated.
|
|
19
|
+
*/
|
|
20
|
+
const resolveAnchorLayoutInCellCompatibility = (compatibilityMode) => compatibilityMode !== void 0 && compatibilityMode >= FIRST_ALWAYS_IN_CELL_COMPATIBILITY_MODE;
|
|
21
|
+
//#endregion
|
|
22
|
+
export { resolveAnchorLayoutInCellCompatibility };
|