@stll/folio-core 0.28.0 → 0.29.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/docx/styleParser.js +15 -14
- package/dist/layout-bridge/convert/toFlowBlocks.d.ts +1 -2
- package/dist/layout-bridge/convert/toFlowBlocks.js +135 -118
- package/dist/layout-engine/footnoteColumnReflow.d.ts +15 -0
- package/dist/layout-engine/footnoteColumnReflow.js +74 -0
- package/dist/layout-engine/index.d.ts +3 -5
- package/dist/layout-engine/index.js +36 -15
- package/dist/layout-engine/measure/cache.d.ts +1 -1
- package/dist/layout-engine/measure/cache.js +11 -2
- package/dist/layout-engine/measure/listMarkerWidth.d.ts +3 -1
- package/dist/layout-engine/measure/listMarkerWidth.js +6 -4
- package/dist/layout-engine/paginator.d.ts +6 -0
- package/dist/layout-engine/paginator.js +30 -11
- package/dist/layout-engine/types.d.ts +11 -6
- package/dist/markdown/renderParagraph.js +7 -2
- package/dist/prosemirror/attrs/index.js +5 -3
- package/dist/prosemirror/bookmarkBoundaryAttrs.d.ts +8 -0
- package/dist/prosemirror/bookmarkBoundaryAttrs.js +66 -0
- package/dist/prosemirror/commands/formatPainter.js +45 -1
- package/dist/prosemirror/conversion/fromProseDoc.d.ts +4 -1
- package/dist/prosemirror/conversion/fromProseDoc.js +224 -91
- package/dist/prosemirror/conversion/toProseDoc.d.ts +4 -1
- package/dist/prosemirror/conversion/toProseDoc.js +325 -94
- package/dist/prosemirror/extensions/StarterKit.js +11 -3
- package/dist/prosemirror/extensions/features/AutoBidiDetectionExtension.js +8 -4
- package/dist/prosemirror/extensions/features/PasteCleanupExtension.d.ts +4 -1
- package/dist/prosemirror/extensions/features/PasteCleanupExtension.js +9 -5
- package/dist/prosemirror/extensions/features/pasteCleanup.d.ts +10 -23
- package/dist/prosemirror/extensions/features/pasteCleanup.js +77 -22
- package/dist/prosemirror/extensions/marks/RunFormattingOverrideExtension.js +22 -22
- package/dist/prosemirror/extensions/nodes/BookmarkBoundaryExtension.d.ts +9 -0
- package/dist/prosemirror/extensions/nodes/BookmarkBoundaryExtension.js +67 -0
- package/dist/prosemirror/extensions/nodes/FieldExtension.d.ts +10 -4
- package/dist/prosemirror/extensions/nodes/FieldExtension.js +77 -59
- package/dist/prosemirror/extensions/nodes/TextBoxAnchorExtension.d.ts +4 -1
- package/dist/prosemirror/extensions/nodes/TextBoxAnchorExtension.js +4 -3
- package/dist/prosemirror/listMarker.d.ts +58 -0
- package/dist/prosemirror/listMarker.js +185 -0
- package/dist/prosemirror/numberedRefFields.d.ts +24 -0
- package/dist/prosemirror/numberedRefFields.js +276 -0
- package/dist/prosemirror/paraText.js +56 -17
- package/dist/prosemirror/plugins/anonymizationDecorations.js +1 -1
- package/dist/prosemirror/plugins/pmTextScan.d.ts +3 -1
- package/dist/prosemirror/plugins/pmTextScan.js +28 -7
- package/dist/prosemirror/plugins/templateDirectives.js +2 -2
- package/dist/prosemirror/schema/index.d.ts +2 -2
- package/dist/prosemirror/schema/marks.d.ts +3 -1
- package/dist/prosemirror/schema/nodes.d.ts +13 -1
- package/dist/prosemirror/styles/styleResolver.d.ts +0 -1
- package/dist/prosemirror/styles/styleResolver.js +22 -29
- package/dist/prosemirror/styles/styleToggleCascade.d.ts +37 -0
- package/dist/prosemirror/styles/styleToggleCascade.js +52 -0
- package/dist/prosemirror/validation.js +93 -0
- package/dist/utils/textFormattingMerge.d.ts +9 -1
- package/dist/utils/textFormattingMerge.js +32 -1
- package/package.json +1 -1
package/dist/docx/styleParser.js
CHANGED
|
@@ -1,23 +1,24 @@
|
|
|
1
1
|
import { isValidHexColor } from "../utils/colorResolver.js";
|
|
2
2
|
import { mergeParagraphFormatting } from "../utils/paragraphFormattingMerge.js";
|
|
3
|
-
import {
|
|
3
|
+
import { mergeStyleTextFormatting } from "../utils/textFormattingMerge.js";
|
|
4
4
|
import { BorderStyleSchema, ConditionalStyleTypeSchema, EmphasisMarkSchema, FontHintSchema, FontThemeSchema, HighlightColorSchema, LineSpacingRuleSchema, ParagraphAlignmentSchema, ShadingPatternSchema, StyleTypeSchema, TabLeaderSchema, TabStopAlignmentSchema, TableCellTextDirectionSchema, TableRowHeightRuleSchema, TableWidthTypeSchema, TextEffectSchema, ThemeColorSlotSchema, UnderlineStyleSchema, narrowEnum } from "./parserEnums.js";
|
|
5
5
|
import { resolveThemeFontRef } from "./themeParser.js";
|
|
6
6
|
import { findChild, findChildren, getAttribute, getLocalName, parseBooleanElement, parseNumberingLevelAttribute, parseNumericAttribute, parseOnOffValue, parseTableMeasurementValue, parseXmlDocument } from "./xmlParser.js";
|
|
7
7
|
//#region src/docx/styleParser.ts
|
|
8
|
+
const findLastRunToggle = (rPr, localName) => findChildren(rPr, "w", localName).at(-1) ?? null;
|
|
8
9
|
/**
|
|
9
10
|
* Parse text formatting properties (w:rPr)
|
|
10
11
|
*/
|
|
11
12
|
function parseRunProperties(rPr, theme) {
|
|
12
13
|
if (!rPr) return;
|
|
13
14
|
const formatting = {};
|
|
14
|
-
const b =
|
|
15
|
+
const b = findLastRunToggle(rPr, "b");
|
|
15
16
|
if (b) formatting.bold = parseBooleanElement(b);
|
|
16
|
-
const bCs =
|
|
17
|
+
const bCs = findLastRunToggle(rPr, "bCs");
|
|
17
18
|
if (bCs) formatting.boldCs = parseBooleanElement(bCs);
|
|
18
|
-
const i =
|
|
19
|
+
const i = findLastRunToggle(rPr, "i");
|
|
19
20
|
if (i) formatting.italic = parseBooleanElement(i);
|
|
20
|
-
const iCs =
|
|
21
|
+
const iCs = findLastRunToggle(rPr, "iCs");
|
|
21
22
|
if (iCs) formatting.italicCs = parseBooleanElement(iCs);
|
|
22
23
|
const u = findChild(rPr, "w", "u");
|
|
23
24
|
if (u) {
|
|
@@ -29,7 +30,7 @@ function parseRunProperties(rPr, theme) {
|
|
|
29
30
|
if (colorVal || themeColor) formatting.underline.color = parseColorValue(colorVal, themeColor, getAttribute(u, "w", "themeTint"), getAttribute(u, "w", "themeShade"));
|
|
30
31
|
}
|
|
31
32
|
}
|
|
32
|
-
const strike =
|
|
33
|
+
const strike = findLastRunToggle(rPr, "strike");
|
|
33
34
|
if (strike) formatting.strike = parseBooleanElement(strike);
|
|
34
35
|
const dstrike = findChild(rPr, "w", "dstrike");
|
|
35
36
|
if (dstrike) formatting.doubleStrike = parseBooleanElement(dstrike);
|
|
@@ -38,11 +39,11 @@ function parseRunProperties(rPr, theme) {
|
|
|
38
39
|
const val = getAttribute(vertAlign, "w", "val");
|
|
39
40
|
if (val === "superscript" || val === "subscript" || val === "baseline") formatting.vertAlign = val;
|
|
40
41
|
}
|
|
41
|
-
const smallCaps =
|
|
42
|
+
const smallCaps = findLastRunToggle(rPr, "smallCaps");
|
|
42
43
|
if (smallCaps) formatting.smallCaps = parseBooleanElement(smallCaps);
|
|
43
|
-
const caps =
|
|
44
|
+
const caps = findLastRunToggle(rPr, "caps");
|
|
44
45
|
if (caps) formatting.allCaps = parseBooleanElement(caps);
|
|
45
|
-
const vanish =
|
|
46
|
+
const vanish = findLastRunToggle(rPr, "vanish");
|
|
46
47
|
if (vanish) formatting.hidden = parseBooleanElement(vanish);
|
|
47
48
|
const color = findChild(rPr, "w", "color");
|
|
48
49
|
if (color) formatting.color = parseColorValue(getAttribute(color, "w", "val"), getAttribute(color, "w", "themeColor"), getAttribute(color, "w", "themeTint"), getAttribute(color, "w", "themeShade"));
|
|
@@ -154,13 +155,13 @@ function parseRunProperties(rPr, theme) {
|
|
|
154
155
|
const val = narrowEnum(getAttribute(em, "w", "val"), EmphasisMarkSchema);
|
|
155
156
|
if (val) formatting.emphasisMark = val;
|
|
156
157
|
}
|
|
157
|
-
const emboss =
|
|
158
|
+
const emboss = findLastRunToggle(rPr, "emboss");
|
|
158
159
|
if (emboss) formatting.emboss = parseBooleanElement(emboss);
|
|
159
|
-
const imprint =
|
|
160
|
+
const imprint = findLastRunToggle(rPr, "imprint");
|
|
160
161
|
if (imprint) formatting.imprint = parseBooleanElement(imprint);
|
|
161
|
-
const outline =
|
|
162
|
+
const outline = findLastRunToggle(rPr, "outline");
|
|
162
163
|
if (outline) formatting.outline = parseBooleanElement(outline);
|
|
163
|
-
const shadow =
|
|
164
|
+
const shadow = findLastRunToggle(rPr, "shadow");
|
|
164
165
|
if (shadow) formatting.shadow = parseBooleanElement(shadow);
|
|
165
166
|
const rtl = findChild(rPr, "w", "rtl");
|
|
166
167
|
if (rtl) formatting.rtl = parseBooleanElement(rtl);
|
|
@@ -807,7 +808,7 @@ function resolveStyleInheritance(style, styleMap, visited = /* @__PURE__ */ new
|
|
|
807
808
|
const resolved = { ...style };
|
|
808
809
|
const mergedPPr = mergeParagraphFormatting(resolvedParent.pPr, style.pPr);
|
|
809
810
|
if (mergedPPr) resolved.pPr = mergedPPr;
|
|
810
|
-
const mergedRPr =
|
|
811
|
+
const mergedRPr = mergeStyleTextFormatting(resolvedParent.rPr, style.rPr);
|
|
811
812
|
if (mergedRPr) resolved.rPr = mergedRPr;
|
|
812
813
|
if (style.type === "table") {
|
|
813
814
|
if (resolvedParent.tblPr || style.tblPr) resolved.tblPr = {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { document_d_exports } from "../../types/document.js";
|
|
2
2
|
import { BorderStyle, FlowBlock, ParagraphAttrs } from "../../layout-engine/types.js";
|
|
3
|
+
import { formatCounter, resolveListTemplate } from "../../prosemirror/listMarker.js";
|
|
3
4
|
import { Node } from "prosemirror-model";
|
|
4
5
|
//#region src/layout-bridge/convert/toFlowBlocks.d.ts
|
|
5
6
|
/**
|
|
@@ -59,8 +60,6 @@ type ToFlowBlocksOptions = {
|
|
|
59
60
|
/** Line pitch for the final body section, whose properties live outside the PM body. */
|
|
60
61
|
finalSectionDocumentGridLinePitchTwips?: number;
|
|
61
62
|
};
|
|
62
|
-
declare function formatCounter(value: number, format: document_d_exports.NumberFormat | undefined): string;
|
|
63
|
-
declare function resolveListTemplate(template: string, counters: number[], levelFormats: document_d_exports.NumberFormat[] | undefined, forceDecimal?: boolean): string;
|
|
64
63
|
/**
|
|
65
64
|
* Reset the block ID counter (useful for testing).
|
|
66
65
|
*/
|
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
import { convertBulletToUnicode } from "../../docx/bulletMarkers.js";
|
|
2
2
|
import { resolveDocumentGridLinePitch } from "../../docx/documentGrid.js";
|
|
3
|
-
import { formatOoxmlCounter } from "../../docx/ooxmlCounterFormatter.js";
|
|
4
3
|
import { setHyperlinkInstanceIndex } from "../../layout-engine/measure/hyperlinkInstance.js";
|
|
5
4
|
import { setParagraphFrame } from "../../layout-engine/paragraphFrame.js";
|
|
6
5
|
import { setTextBoxGroupId } from "../../layout-engine/textBoxGroup.js";
|
|
7
6
|
import { DEFAULT_TEXTBOX_MARGINS } from "../../layout-engine/types.js";
|
|
8
7
|
import { getPageNumbering } from "../../paged-layout/sectionGeometry.js";
|
|
9
|
-
import { expectBlockSdtAttrs, expectCharacterSpacingMarkAttrs, expectCommentMarkAttrs, expectEmphasisMarkAttrs, expectFieldAttrs, expectFontFamilyMarkAttrs, expectFontSizeMarkAttrs, expectFootnoteRefMarkAttrs, expectHardBreakAttrs, expectHighlightMarkAttrs, expectHyperlinkMarkAttrs, expectImageAttrs, expectLanguageMarkAttrs, expectMathAttrs, expectParagraphAttrs, expectRunFormattingOverrideMarkAttrs, expectRunShadingMarkAttrs, expectSymbolAttrs, expectTableAttrs, expectTableCellAttrs, expectTableRowAttrs, expectTextBoxAttrs, expectTextColorMarkAttrs, expectTextEffectMarkAttrs, expectTrackedChangeMarkAttrs, expectUnderlineMarkAttrs } from "../../prosemirror/attrs/index.js";
|
|
8
|
+
import { expectBlockSdtAttrs, expectCharacterSpacingMarkAttrs, expectCharacterStyleMarkAttrs, expectCommentMarkAttrs, expectEmphasisMarkAttrs, expectFieldAttrs, expectFontFamilyMarkAttrs, expectFontSizeMarkAttrs, expectFootnoteRefMarkAttrs, expectHardBreakAttrs, expectHighlightMarkAttrs, expectHyperlinkMarkAttrs, expectImageAttrs, expectLanguageMarkAttrs, expectMathAttrs, expectParagraphAttrs, expectRunFormattingOverrideMarkAttrs, expectRunShadingMarkAttrs, expectSymbolAttrs, expectTableAttrs, expectTableCellAttrs, expectTableRowAttrs, expectTextBoxAttrs, expectTextColorMarkAttrs, expectTextEffectMarkAttrs, expectTrackedChangeMarkAttrs, expectUnderlineMarkAttrs } from "../../prosemirror/attrs/index.js";
|
|
10
9
|
import { autospacingMatchesBase } from "../../prosemirror/autospacingBase.js";
|
|
11
10
|
import { runShadingAttrsToShading } from "../../prosemirror/conversion/runShadingMark.js";
|
|
11
|
+
import { advanceListMarker, advanceVisibleListMarker, cloneListCounterState, formatCounter, resolveListTemplate } from "../../prosemirror/listMarker.js";
|
|
12
|
+
import { resolveNumberedRefFields } from "../../prosemirror/numberedRefFields.js";
|
|
12
13
|
import { directionToBidi } from "../../prosemirror/paragraphDirection.js";
|
|
14
|
+
import { cascadeStyleTextFormatting } from "../../prosemirror/styles/styleToggleCascade.js";
|
|
13
15
|
import { assertValidProseMirrorDocument } from "../../prosemirror/validation.js";
|
|
14
16
|
import { resolveColor, resolveHighlightToCss } from "../../utils/colorResolver.js";
|
|
15
17
|
import { resolveThemeFont } from "../../utils/fontResolver.js";
|
|
@@ -67,79 +69,12 @@ let blockIdCounter = 0;
|
|
|
67
69
|
function nextBlockId() {
|
|
68
70
|
return `block-${++blockIdCounter}`;
|
|
69
71
|
}
|
|
70
|
-
function formatNumberedMarker(counters, level) {
|
|
71
|
-
const parts = [];
|
|
72
|
-
for (let i = 0; i <= level; i += 1) {
|
|
73
|
-
const value = counters[i] ?? 0;
|
|
74
|
-
if (!Number.isFinite(value) || value <= 0) break;
|
|
75
|
-
parts.push(value);
|
|
76
|
-
}
|
|
77
|
-
if (parts.length === 0) return "1.";
|
|
78
|
-
return `${parts.join(".")}.`;
|
|
79
|
-
}
|
|
80
|
-
function formatCounter(value, format) {
|
|
81
|
-
return formatOoxmlCounter(value, format);
|
|
82
|
-
}
|
|
83
|
-
function resolveListTemplate(template, counters, levelFormats, forceDecimal = false) {
|
|
84
|
-
return template.replace(/%(?<digit>\d)(?<punct>[.):\]])?/gu, (...args) => {
|
|
85
|
-
const { digit, punct = "" } = args.at(-1);
|
|
86
|
-
const index = Number.parseInt(digit, 10) - 1;
|
|
87
|
-
if (index < 0) return "";
|
|
88
|
-
const counter = counters[index];
|
|
89
|
-
if (counter === void 0 || Number.isNaN(counter)) return "";
|
|
90
|
-
const formatted = formatCounter(counter, forceDecimal ? "decimal" : levelFormats?.[index]);
|
|
91
|
-
return formatted ? `${formatted}${punct}` : "";
|
|
92
|
-
});
|
|
93
|
-
}
|
|
94
|
-
function getLastListCounters(listCounters) {
|
|
95
|
-
let lastCounters;
|
|
96
|
-
for (const counters of listCounters.values()) lastCounters = counters;
|
|
97
|
-
return lastCounters;
|
|
98
|
-
}
|
|
99
72
|
function applyMarkerAllCaps(marker, allCaps) {
|
|
100
73
|
if (marker === null || !allCaps) return marker;
|
|
101
74
|
return marker.toLocaleUpperCase();
|
|
102
75
|
}
|
|
103
|
-
function computeListMarker(pmAttrs,
|
|
104
|
-
|
|
105
|
-
if (numId === void 0 || numId === 0) {
|
|
106
|
-
if (pmAttrs.listMarker?.includes("%") && !pmAttrs.listIsBullet) {
|
|
107
|
-
const counters = getLastListCounters(listCounters);
|
|
108
|
-
if (counters) return resolveListTemplate(pmAttrs.listMarker, counters, pmAttrs.listLevelNumFmts, pmAttrs.listIsLegal);
|
|
109
|
-
}
|
|
110
|
-
return null;
|
|
111
|
-
}
|
|
112
|
-
if (pmAttrs.listIsBullet) return convertBulletToUnicode(pmAttrs.listMarker || "");
|
|
113
|
-
const level = pmAttrs.numPr?.ilvl ?? 0;
|
|
114
|
-
const counters = listCounters.get(numId) ?? Array.from({ length: 9 }, () => NaN);
|
|
115
|
-
const abstractNumId = pmAttrs.listAbstractNumId;
|
|
116
|
-
if (level > 0) {
|
|
117
|
-
const latestAbstractCounters = abstractNumId === void 0 ? void 0 : abstractCounters.get(abstractNumId);
|
|
118
|
-
if (counters.slice(0, level).every((counter) => !Number.isFinite(counter))) for (let i = 0; i < level; i += 1) {
|
|
119
|
-
const latestCounter = latestAbstractCounters?.[i];
|
|
120
|
-
counters[i] = latestCounter !== void 0 && Number.isFinite(latestCounter) ? latestCounter : pmAttrs.listLevelStarts?.[i] ?? 1;
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
const seenKey = `${numId}:${level}`;
|
|
124
|
-
if (!seenNumIds.has(seenKey)) {
|
|
125
|
-
seenNumIds.add(seenKey);
|
|
126
|
-
if (pmAttrs.listStartOverride != null) counters[level] = pmAttrs.listStartOverride - 1;
|
|
127
|
-
}
|
|
128
|
-
if (!Number.isFinite(counters[level])) counters[level] = (pmAttrs.listLevelStarts?.[level] ?? 1) - 1;
|
|
129
|
-
counters[level] = (counters[level] ?? 0) + 1;
|
|
130
|
-
for (let i = level + 1; i < counters.length; i += 1) counters[i] = NaN;
|
|
131
|
-
const childAdvances = pmAttrs.listImplicitChildLevelAdvances ?? 0;
|
|
132
|
-
if (childAdvances > 0 && level + 1 < counters.length) {
|
|
133
|
-
const childCounter = counters[level + 1];
|
|
134
|
-
counters[level + 1] = (childCounter === void 0 || !Number.isFinite(childCounter) ? 0 : childCounter) + childAdvances;
|
|
135
|
-
}
|
|
136
|
-
listCounters.set(numId, counters);
|
|
137
|
-
if (abstractNumId !== void 0) abstractCounters.set(abstractNumId, [...counters]);
|
|
138
|
-
const levelFormats = pmAttrs.listLevelNumFmts ?? (pmAttrs.listNumFmt ? [pmAttrs.listNumFmt] : void 0);
|
|
139
|
-
if (pmAttrs.listMarker && pmAttrs.listMarker.includes("%")) return resolveListTemplate(pmAttrs.listMarker, counters, levelFormats, pmAttrs.listIsLegal);
|
|
140
|
-
if (pmAttrs.listMarker) return pmAttrs.listMarker;
|
|
141
|
-
if ((levelFormats?.[level] ?? pmAttrs.listNumFmt) === "none" || pmAttrs.listMarker === "") return null;
|
|
142
|
-
return formatNumberedMarker(counters, level);
|
|
76
|
+
function computeListMarker(pmAttrs, state) {
|
|
77
|
+
return advanceListMarker(pmAttrs, state);
|
|
143
78
|
}
|
|
144
79
|
/**
|
|
145
80
|
* Reset the block ID counter (useful for testing).
|
|
@@ -348,17 +283,49 @@ function mergeRunFormatting(paraDefaults, formatting) {
|
|
|
348
283
|
if (merged.letterSpacing === 0) delete merged.letterSpacing;
|
|
349
284
|
return merged;
|
|
350
285
|
}
|
|
286
|
+
/** Restore character-style toggle values that plain visual marks cannot represent. */
|
|
287
|
+
function applyCharacterStyleToggleFormatting({ formatting, marks, paraDefaults }) {
|
|
288
|
+
const characterStyleMark = marks.find((mark) => mark.type.name === "characterStyle");
|
|
289
|
+
if (!characterStyleMark) return;
|
|
290
|
+
const styleRPr = expectCharacterStyleMarkAttrs(characterStyleMark)._styleRPr;
|
|
291
|
+
if (!styleRPr) return;
|
|
292
|
+
const effectiveStyleFormatting = cascadeStyleTextFormatting([{
|
|
293
|
+
formatting: {
|
|
294
|
+
bold: paraDefaults.bold ?? false,
|
|
295
|
+
boldCs: paraDefaults.complexScriptBold ?? false,
|
|
296
|
+
italic: paraDefaults.italic ?? false,
|
|
297
|
+
italicCs: paraDefaults.complexScriptItalic ?? false
|
|
298
|
+
},
|
|
299
|
+
type: "direct"
|
|
300
|
+
}, {
|
|
301
|
+
formatting: styleRPr,
|
|
302
|
+
type: "style"
|
|
303
|
+
}]).formatting;
|
|
304
|
+
if (styleRPr.bold !== void 0 && formatting.bold === void 0) formatting.bold = effectiveStyleFormatting?.bold ?? false;
|
|
305
|
+
if (styleRPr.boldCs !== void 0 && formatting.complexScriptBold === void 0) formatting.complexScriptBold = effectiveStyleFormatting?.boldCs ?? false;
|
|
306
|
+
if (styleRPr.italic !== void 0 && formatting.italic === void 0) formatting.italic = effectiveStyleFormatting?.italic ?? false;
|
|
307
|
+
if (styleRPr.italicCs !== void 0 && formatting.complexScriptItalic === void 0) formatting.complexScriptItalic = effectiveStyleFormatting?.italicCs ?? false;
|
|
308
|
+
if (styleRPr.allCaps === true && formatting.allCaps === void 0) formatting.allCaps = false;
|
|
309
|
+
if (styleRPr.emboss === true && formatting.emboss === void 0) formatting.emboss = false;
|
|
310
|
+
if (styleRPr.imprint === true && formatting.imprint === void 0) formatting.imprint = false;
|
|
311
|
+
if (styleRPr.outline === true && formatting.textOutline === void 0) formatting.textOutline = false;
|
|
312
|
+
if (styleRPr.shadow === true && formatting.textShadow === void 0) formatting.textShadow = false;
|
|
313
|
+
if (styleRPr.smallCaps === true && formatting.smallCaps === void 0) formatting.smallCaps = false;
|
|
314
|
+
if (styleRPr.strike === true && formatting.strike === void 0) formatting.strike = false;
|
|
315
|
+
if (styleRPr.hidden === true && formatting.hidden === void 0) formatting.hidden = false;
|
|
316
|
+
}
|
|
351
317
|
function applyRunFormattingOverrides(formatting, attrs) {
|
|
352
|
-
if (attrs.bold
|
|
353
|
-
if (attrs.italic
|
|
318
|
+
if (attrs.bold !== void 0) formatting.bold = attrs.bold;
|
|
319
|
+
if (attrs.italic !== void 0) formatting.italic = attrs.italic;
|
|
354
320
|
if (attrs.underline === "none") formatting.underline = false;
|
|
355
|
-
if (attrs.strike
|
|
356
|
-
if (attrs.allCaps
|
|
357
|
-
if (attrs.smallCaps
|
|
358
|
-
if (attrs.
|
|
359
|
-
if (attrs.
|
|
360
|
-
if (attrs.
|
|
361
|
-
if (attrs.
|
|
321
|
+
if (attrs.strike !== void 0) formatting.strike = attrs.strike;
|
|
322
|
+
if (attrs.allCaps !== void 0) formatting.allCaps = attrs.allCaps;
|
|
323
|
+
if (attrs.smallCaps !== void 0) formatting.smallCaps = attrs.smallCaps;
|
|
324
|
+
if (attrs.hidden !== void 0) formatting.hidden = attrs.hidden;
|
|
325
|
+
if (attrs.emboss !== void 0) formatting.emboss = attrs.emboss;
|
|
326
|
+
if (attrs.imprint !== void 0) formatting.imprint = attrs.imprint;
|
|
327
|
+
if (attrs.shadow !== void 0) formatting.textShadow = attrs.shadow;
|
|
328
|
+
if (attrs.outline !== void 0) formatting.textOutline = attrs.outline;
|
|
362
329
|
if (attrs.rtl === false) formatting.rtl = false;
|
|
363
330
|
if (attrs.boldCs !== void 0) formatting.complexScriptBold = attrs.boldCs;
|
|
364
331
|
if (attrs.italicCs !== void 0) formatting.complexScriptItalic = attrs.italicCs;
|
|
@@ -482,6 +449,7 @@ function paragraphToRuns(node, startPos, _options) {
|
|
|
482
449
|
const inTocParagraph = pmAttrs._tableOfContentsLevel !== void 0 || tableOfContentsStyleLevel({ styleId: paragraphStyleId }) !== void 0;
|
|
483
450
|
let leadingRenderedPageBreakPending = pmAttrs.renderedPageBreakBefore === true;
|
|
484
451
|
function pushRunsForChild(child, childPos) {
|
|
452
|
+
if (child.type.name === "bookmarkBoundary") return;
|
|
485
453
|
if (child.type.name === "renderedPageBreak") {
|
|
486
454
|
if (leadingRenderedPageBreakPending) {
|
|
487
455
|
leadingRenderedPageBreakPending = false;
|
|
@@ -497,6 +465,11 @@ function paragraphToRuns(node, startPos, _options) {
|
|
|
497
465
|
if (child.type.name !== "sdt") leadingRenderedPageBreakPending = false;
|
|
498
466
|
if (child.isText && child.text) {
|
|
499
467
|
const formatting = extractRunFormatting(child.marks, theme);
|
|
468
|
+
applyCharacterStyleToggleFormatting({
|
|
469
|
+
formatting,
|
|
470
|
+
marks: child.marks,
|
|
471
|
+
paraDefaults
|
|
472
|
+
});
|
|
500
473
|
if (inTocParagraph) stripTocHyperlinkStyle(formatting);
|
|
501
474
|
const run = {
|
|
502
475
|
kind: "text",
|
|
@@ -513,6 +486,11 @@ function paragraphToRuns(node, startPos, _options) {
|
|
|
513
486
|
const text = decodeOoxmlSymbolCharacter(attrs.char);
|
|
514
487
|
if (text === null) return;
|
|
515
488
|
const formatting = extractRunFormatting(child.marks, theme);
|
|
489
|
+
applyCharacterStyleToggleFormatting({
|
|
490
|
+
formatting,
|
|
491
|
+
marks: child.marks,
|
|
492
|
+
paraDefaults
|
|
493
|
+
});
|
|
516
494
|
if (inTocParagraph) stripTocHyperlinkStyle(formatting);
|
|
517
495
|
runs.push({
|
|
518
496
|
kind: "text",
|
|
@@ -534,6 +512,11 @@ function paragraphToRuns(node, startPos, _options) {
|
|
|
534
512
|
}
|
|
535
513
|
if (child.type.name === "tab") {
|
|
536
514
|
const formatting = extractRunFormatting(child.marks, theme);
|
|
515
|
+
applyCharacterStyleToggleFormatting({
|
|
516
|
+
formatting,
|
|
517
|
+
marks: child.marks,
|
|
518
|
+
paraDefaults
|
|
519
|
+
});
|
|
537
520
|
const run = {
|
|
538
521
|
kind: "tab",
|
|
539
522
|
...mergeRunFormatting(paraDefaults, formatting),
|
|
@@ -552,7 +535,7 @@ function paragraphToRuns(node, startPos, _options) {
|
|
|
552
535
|
runs.push(run);
|
|
553
536
|
return;
|
|
554
537
|
}
|
|
555
|
-
if (child.type.name === "field") {
|
|
538
|
+
if (child.type.name === "field" || child.type.name === "structuredField") {
|
|
556
539
|
const attrs = expectFieldAttrs(child);
|
|
557
540
|
const ft = attrs.fieldType;
|
|
558
541
|
let mappedType = "OTHER";
|
|
@@ -561,13 +544,18 @@ function paragraphToRuns(node, startPos, _options) {
|
|
|
561
544
|
else if (ft === "DATE") mappedType = "DATE";
|
|
562
545
|
else if (ft === "TIME") mappedType = "TIME";
|
|
563
546
|
const extractedFieldFormatting = extractRunFormatting(child.marks, theme);
|
|
547
|
+
applyCharacterStyleToggleFormatting({
|
|
548
|
+
formatting: extractedFieldFormatting,
|
|
549
|
+
marks: child.marks,
|
|
550
|
+
paraDefaults
|
|
551
|
+
});
|
|
564
552
|
if (inTocParagraph) stripTocHyperlinkStyle(extractedFieldFormatting);
|
|
565
553
|
const fieldFormatting = markDefaultBlackTextColorSource(extractedFieldFormatting, paraDefaults);
|
|
566
554
|
const run = {
|
|
567
555
|
kind: "field",
|
|
568
556
|
fieldType: mappedType,
|
|
569
557
|
instruction: attrs.instruction,
|
|
570
|
-
fallback: attrs.displayText
|
|
558
|
+
fallback: _options.numberedRefResults?.get(child) ?? attrs.displayText ?? "",
|
|
571
559
|
...attrs.fldLock ? { fldLock: true } : {},
|
|
572
560
|
pmStart: childPos,
|
|
573
561
|
pmEnd: childPos + child.nodeSize,
|
|
@@ -658,18 +646,18 @@ function toPreviousListAttrs(previousFormatting) {
|
|
|
658
646
|
if (listMarkerSuffix !== void 0) attrs.listMarkerSuffix = listMarkerSuffix;
|
|
659
647
|
return attrs;
|
|
660
648
|
}
|
|
661
|
-
function resolveDeletedListMarker(previousListAttrs,
|
|
662
|
-
if (
|
|
663
|
-
const marker = computeListMarker(previousListAttrs,
|
|
649
|
+
function resolveDeletedListMarker(previousListAttrs, listCounterState) {
|
|
650
|
+
if (listCounterState && previousListAttrs.numPr) {
|
|
651
|
+
const marker = computeListMarker(previousListAttrs, listCounterState);
|
|
664
652
|
if (marker) return marker;
|
|
665
653
|
}
|
|
666
654
|
if (previousListAttrs.listMarker) return previousListAttrs.listIsBullet ? convertBulletToUnicode(previousListAttrs.listMarker) : previousListAttrs.listMarker;
|
|
667
655
|
if (previousListAttrs.listIsBullet) return "•";
|
|
668
656
|
return null;
|
|
669
657
|
}
|
|
670
|
-
function applyDeletedListMarkerAttrs(attrs, change,
|
|
658
|
+
function applyDeletedListMarkerAttrs(attrs, change, listCounterState, theme) {
|
|
671
659
|
const previousListAttrs = toPreviousListAttrs(change.previousFormatting);
|
|
672
|
-
const marker = resolveDeletedListMarker(previousListAttrs,
|
|
660
|
+
const marker = resolveDeletedListMarker(previousListAttrs, listCounterState);
|
|
673
661
|
if (!marker) return;
|
|
674
662
|
attrs.listMarker = marker;
|
|
675
663
|
attrs.listMarkerRevision = toListMarkerRevision("del", change.info);
|
|
@@ -679,7 +667,7 @@ function applyDeletedListMarkerAttrs(attrs, change, listCounters, listAbstractCo
|
|
|
679
667
|
if (previousListAttrs.listMarkerAlignment) attrs.listMarkerAlignment = previousListAttrs.listMarkerAlignment;
|
|
680
668
|
if (previousListAttrs.listMarkerSuffix) attrs.listMarkerSuffix = previousListAttrs.listMarkerSuffix;
|
|
681
669
|
}
|
|
682
|
-
function convertParagraphAttrs(pmAttrs, theme,
|
|
670
|
+
function convertParagraphAttrs(pmAttrs, { theme, listCounterStreams, defaultTabStopTwips }) {
|
|
683
671
|
const attrs = {};
|
|
684
672
|
if (pmAttrs.alignment) {
|
|
685
673
|
const align = pmAttrs.alignment;
|
|
@@ -798,15 +786,7 @@ function convertParagraphAttrs(pmAttrs, theme, listCounters, listAbstractCounter
|
|
|
798
786
|
if (numberingInsertionChange) attrs.listMarkerRevision = toListMarkerRevision("ins", numberingInsertionChange.info);
|
|
799
787
|
}
|
|
800
788
|
}
|
|
801
|
-
const
|
|
802
|
-
const streamCounters = useOriginalStream ? originalListCounters : listCounters;
|
|
803
|
-
const resolvedMarker = applyMarkerAllCaps(streamCounters ? computeListMarker(pmAttrs, streamCounters, (useOriginalStream ? originalListAbstractCounters : listAbstractCounters) ?? /* @__PURE__ */ new Map(), (useOriginalStream ? originalListSeenNumIds : listSeenNumIds) ?? /* @__PURE__ */ new Set()) : null, pmAttrs.listMarkerAllCaps);
|
|
804
|
-
const numberedNumId = pmAttrs.numPr?.numId;
|
|
805
|
-
if (attrs.listMarkerRevision === void 0 && !pmAttrs.listIsBullet && originalListCounters !== void 0 && numberedNumId !== void 0 && numberedNumId !== 0) computeListMarker(pmAttrs, originalListCounters, originalListAbstractCounters ?? /* @__PURE__ */ new Map(), originalListSeenNumIds ?? /* @__PURE__ */ new Set());
|
|
806
|
-
else if (changedNumberingChange !== void 0 && originalListCounters !== void 0) {
|
|
807
|
-
const previousListAttrs = toPreviousListAttrs(changedNumberingChange.previousFormatting);
|
|
808
|
-
if (previousListAttrs.numPr && !previousListAttrs.listIsBullet) computeListMarker(previousListAttrs, originalListCounters, originalListAbstractCounters ?? /* @__PURE__ */ new Map(), originalListSeenNumIds ?? /* @__PURE__ */ new Set());
|
|
809
|
-
}
|
|
789
|
+
const resolvedMarker = applyMarkerAllCaps(advanceVisibleListMarker(pmAttrs, listCounterStreams).marker, pmAttrs.listMarkerAllCaps);
|
|
810
790
|
if (resolvedMarker !== null) attrs.listMarker = resolvedMarker;
|
|
811
791
|
else if (pmAttrs.listMarker) attrs.listMarker = pmAttrs.listIsBullet ? convertBulletToUnicode(pmAttrs.listMarker) : pmAttrs.listMarker;
|
|
812
792
|
if (pmAttrs.listIsBullet !== void 0) attrs.listIsBullet = pmAttrs.listIsBullet;
|
|
@@ -817,7 +797,13 @@ function convertParagraphAttrs(pmAttrs, theme, listCounters, listAbstractCounter
|
|
|
817
797
|
if (pmAttrs.listMarkerSecondSlotOffsetTwips !== void 0) attrs.listMarkerSecondSlotOffsetTwips = pmAttrs.listMarkerSecondSlotOffsetTwips;
|
|
818
798
|
if (!pmAttrs.numPr) {
|
|
819
799
|
const numberingRemovedChange = propertyChanges.find(isRemovedNumberingChange);
|
|
820
|
-
if (numberingRemovedChange)
|
|
800
|
+
if (numberingRemovedChange) {
|
|
801
|
+
applyDeletedListMarkerAttrs(attrs, numberingRemovedChange, void 0, theme);
|
|
802
|
+
if (resolvedMarker !== null) {
|
|
803
|
+
attrs.listMarker = resolvedMarker;
|
|
804
|
+
attrs.listMarkerRevision = toListMarkerRevision("del", numberingRemovedChange.info);
|
|
805
|
+
}
|
|
806
|
+
}
|
|
821
807
|
}
|
|
822
808
|
if (defaultTabStopTwips !== void 0) attrs.defaultTabStopTwips = defaultTabStopTwips;
|
|
823
809
|
const dtf = pmAttrs.defaultTextFormatting;
|
|
@@ -855,7 +841,11 @@ function hasOnlyVisuallyEmptyTextRuns(runs) {
|
|
|
855
841
|
function convertParagraph(node, startPos, options) {
|
|
856
842
|
const pmAttrs = expectParagraphAttrs(node);
|
|
857
843
|
const runs = paragraphToRuns(node, startPos, options);
|
|
858
|
-
const attrs = convertParagraphAttrs(pmAttrs,
|
|
844
|
+
const attrs = convertParagraphAttrs(pmAttrs, {
|
|
845
|
+
theme: options.theme,
|
|
846
|
+
listCounterStreams: options.listCounterStreams,
|
|
847
|
+
defaultTabStopTwips: options.defaultTabStopTwips
|
|
848
|
+
});
|
|
859
849
|
if (options.lineBreakRules) attrs.lineBreakRules = options.lineBreakRules;
|
|
860
850
|
if (options.justificationCompatibility) attrs.justificationCompatibility = options.justificationCompatibility;
|
|
861
851
|
if (options.automaticHyphenation) attrs.automaticHyphenation = options.automaticHyphenation;
|
|
@@ -902,27 +892,25 @@ function convertParagraph(node, startPos, options) {
|
|
|
902
892
|
return block;
|
|
903
893
|
}
|
|
904
894
|
/**
|
|
905
|
-
* Word keeps
|
|
906
|
-
*
|
|
907
|
-
*
|
|
908
|
-
*
|
|
895
|
+
* Word keeps a final empty body paragraph after a table as an editable anchor,
|
|
896
|
+
* but that final anchor does not create a page of its own. Earlier authored
|
|
897
|
+
* empty paragraphs retain their height and may carry the document onto a blank
|
|
898
|
+
* page. Preserve every block and PM range while collapsing only the final one.
|
|
909
899
|
*/
|
|
910
900
|
function isPaintlessTerminalParagraph(block) {
|
|
911
901
|
if (block?.kind !== "paragraph" || block.runs.length !== 0) return false;
|
|
912
902
|
const attrs = block.attrs;
|
|
913
903
|
return !(attrs?.listMarker !== void 0 && !attrs.listMarkerHidden || attrs?.borders?.top || attrs?.borders?.bottom || attrs?.borders?.left || attrs?.borders?.right || attrs?.borders?.between || attrs?.borders?.bar || attrs?.shading || attrs?.spacingExplicit?.before || attrs?.spacingExplicit?.after || attrs?.pageBreakBefore || attrs?.renderedPageBreakBefore);
|
|
914
904
|
}
|
|
915
|
-
function
|
|
905
|
+
function suppressFinalEmptyParagraphAfterTable(blocks) {
|
|
916
906
|
let suffixStart = blocks.length;
|
|
917
907
|
while (suffixStart > 0 && isPaintlessTerminalParagraph(blocks[suffixStart - 1])) suffixStart -= 1;
|
|
918
908
|
if (suffixStart === blocks.length || suffixStart === 0 || blocks[suffixStart - 1]?.kind !== "table") return;
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
};
|
|
925
|
-
}
|
|
909
|
+
const finalBlock = blocks.at(-1);
|
|
910
|
+
if (isPaintlessTerminalParagraph(finalBlock)) finalBlock.attrs = {
|
|
911
|
+
...finalBlock.attrs,
|
|
912
|
+
suppressEmptyParagraphHeight: true
|
|
913
|
+
};
|
|
926
914
|
}
|
|
927
915
|
function suppressFinalParagraphInRepeatedEmptySuffix(blocks) {
|
|
928
916
|
let suffixStart = blocks.length;
|
|
@@ -1232,6 +1220,11 @@ function convertTextBoxNode(node, startPos, opts) {
|
|
|
1232
1220
|
if (attrs._docxGroupId !== void 0) setTextBoxGroupId(textBox, attrs._docxGroupId);
|
|
1233
1221
|
return textBox;
|
|
1234
1222
|
}
|
|
1223
|
+
function getLastMapKey(map) {
|
|
1224
|
+
let lastKey;
|
|
1225
|
+
for (const key of map.keys()) lastKey = key;
|
|
1226
|
+
return lastKey;
|
|
1227
|
+
}
|
|
1235
1228
|
/**
|
|
1236
1229
|
* Convert a ProseMirror document to FlowBlock array.
|
|
1237
1230
|
*
|
|
@@ -1241,16 +1234,40 @@ function convertTextBoxNode(node, startPos, opts) {
|
|
|
1241
1234
|
function toFlowBlocks(doc, options = {}) {
|
|
1242
1235
|
assertValidProseMirrorDocument(doc, "Cannot layout invalid ProseMirror document");
|
|
1243
1236
|
resetBlockIdCounter();
|
|
1237
|
+
const listCounters = options.listCounters ?? /* @__PURE__ */ new Map();
|
|
1238
|
+
const originalListCounters = options.originalListCounters ?? /* @__PURE__ */ new Map();
|
|
1239
|
+
const lastAdvancedNumId = getLastMapKey(listCounters);
|
|
1240
|
+
const lastAdvancedOriginalNumId = getLastMapKey(originalListCounters);
|
|
1241
|
+
const listCounterState = {
|
|
1242
|
+
counters: listCounters,
|
|
1243
|
+
abstractCounters: options.listAbstractCounters ?? /* @__PURE__ */ new Map(),
|
|
1244
|
+
seenLevels: options.listSeenNumIds ?? /* @__PURE__ */ new Set(),
|
|
1245
|
+
...lastAdvancedNumId !== void 0 ? { lastAdvancedNumId } : {}
|
|
1246
|
+
};
|
|
1247
|
+
const originalListCounterState = {
|
|
1248
|
+
counters: originalListCounters,
|
|
1249
|
+
abstractCounters: options.originalListAbstractCounters ?? /* @__PURE__ */ new Map(),
|
|
1250
|
+
seenLevels: options.originalListSeenNumIds ?? /* @__PURE__ */ new Set(),
|
|
1251
|
+
...lastAdvancedOriginalNumId !== void 0 ? { lastAdvancedNumId: lastAdvancedOriginalNumId } : {}
|
|
1252
|
+
};
|
|
1244
1253
|
const opts = {
|
|
1245
1254
|
...options,
|
|
1246
1255
|
defaultFont: options.defaultFont ?? DEFAULT_FONT,
|
|
1247
1256
|
defaultSize: options.defaultSize ?? DEFAULT_SIZE,
|
|
1248
|
-
listCounters:
|
|
1249
|
-
listAbstractCounters:
|
|
1250
|
-
listSeenNumIds:
|
|
1251
|
-
originalListCounters:
|
|
1252
|
-
originalListAbstractCounters:
|
|
1253
|
-
originalListSeenNumIds:
|
|
1257
|
+
listCounters: listCounterState.counters,
|
|
1258
|
+
listAbstractCounters: listCounterState.abstractCounters,
|
|
1259
|
+
listSeenNumIds: listCounterState.seenLevels,
|
|
1260
|
+
originalListCounters: originalListCounterState.counters,
|
|
1261
|
+
originalListAbstractCounters: originalListCounterState.abstractCounters,
|
|
1262
|
+
originalListSeenNumIds: originalListCounterState.seenLevels,
|
|
1263
|
+
listCounterStreams: {
|
|
1264
|
+
final: listCounterState,
|
|
1265
|
+
original: originalListCounterState
|
|
1266
|
+
},
|
|
1267
|
+
numberedRefResults: resolveNumberedRefFields(doc, {
|
|
1268
|
+
listCounterState: cloneListCounterState(listCounterState),
|
|
1269
|
+
originalListCounterState: cloneListCounterState(originalListCounterState)
|
|
1270
|
+
})
|
|
1254
1271
|
};
|
|
1255
1272
|
const blocks = [];
|
|
1256
1273
|
const offset = 0;
|
|
@@ -1477,7 +1494,7 @@ function toFlowBlocks(doc, options = {}) {
|
|
|
1477
1494
|
visit(node, offset + nodeOffset);
|
|
1478
1495
|
});
|
|
1479
1496
|
reserveLeadingEmptyOutlineHeight(blocks);
|
|
1480
|
-
|
|
1497
|
+
suppressFinalEmptyParagraphAfterTable(blocks);
|
|
1481
1498
|
suppressFinalParagraphInRepeatedEmptySuffix(blocks);
|
|
1482
1499
|
return groupParagraphFrames(applySectionDocumentGrid(mergeRunInParagraphs(blocks), opts.finalSectionDocumentGridLinePitchTwips), nextBlockId);
|
|
1483
1500
|
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Layout } from "./types.js";
|
|
2
|
+
//#region src/layout-engine/footnoteColumnReflow.d.ts
|
|
3
|
+
type ReflowFootnoteColumnsOptions = {
|
|
4
|
+
initialLayout: Layout;
|
|
5
|
+
initialReserveFloors?: ReadonlyMap<number, number>;
|
|
6
|
+
runLayout: (reserveFloors: Map<number, number>) => Layout;
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* Re-run a multi-column layout until its body flow clears the shared footnote band.
|
|
10
|
+
* Reserve floors follow the current page-to-footnote assignment. Repeated floor states
|
|
11
|
+
* and persistent overlap use a stable fallback layout instead of throwing.
|
|
12
|
+
*/
|
|
13
|
+
declare function reflowFootnoteColumns({ initialLayout, initialReserveFloors, runLayout }: ReflowFootnoteColumnsOptions): Layout;
|
|
14
|
+
//#endregion
|
|
15
|
+
export { reflowFootnoteColumns };
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
//#region src/layout-engine/footnoteColumnReflow.ts
|
|
2
|
+
function isFlowFragment(fragment) {
|
|
3
|
+
switch (fragment.kind) {
|
|
4
|
+
case "paragraph": return true;
|
|
5
|
+
case "table": return !fragment.isFloating;
|
|
6
|
+
case "image": return !fragment.isAnchored;
|
|
7
|
+
case "textBox": return !fragment.isPositioned;
|
|
8
|
+
default: return fragment;
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
function hasFootnoteColumnOverlap(layout) {
|
|
12
|
+
for (const page of layout.pages) {
|
|
13
|
+
if ((page.columns?.count ?? 1) < 2 || !page.footnoteReservedHeight) continue;
|
|
14
|
+
const bodyBottom = page.size.h - page.margins.bottom - page.footnoteReservedHeight;
|
|
15
|
+
if (page.fragments.some((fragment) => isFlowFragment(fragment) && fragment.y + fragment.height > bodyBottom)) return true;
|
|
16
|
+
}
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
function growFootnoteReserveFloors(layout, initialReserveFloors) {
|
|
20
|
+
const next = /* @__PURE__ */ new Map();
|
|
21
|
+
for (const page of layout.pages) {
|
|
22
|
+
if ((page.footnoteIds?.length ?? 0) === 0) continue;
|
|
23
|
+
const observed = page.footnoteReservedHeight ?? 0;
|
|
24
|
+
const callerFloor = initialReserveFloors?.get(page.number) ?? 0;
|
|
25
|
+
const floor = Math.max(callerFloor, observed);
|
|
26
|
+
if (floor > 0) next.set(page.number, floor);
|
|
27
|
+
}
|
|
28
|
+
return next;
|
|
29
|
+
}
|
|
30
|
+
function reserveFloorFingerprint(floors) {
|
|
31
|
+
return JSON.stringify([...floors.entries()].sort(([leftPage], [rightPage]) => leftPage - rightPage));
|
|
32
|
+
}
|
|
33
|
+
function clearOrphanedFootnoteReservations(layout) {
|
|
34
|
+
let pagesChanged = false;
|
|
35
|
+
const pages = layout.pages.map((page) => {
|
|
36
|
+
if ((page.footnoteIds?.length ?? 0) > 0 || page.footnoteReservedHeight === void 0) return page;
|
|
37
|
+
pagesChanged = true;
|
|
38
|
+
const cleanedPage = { ...page };
|
|
39
|
+
delete cleanedPage.footnoteReservedHeight;
|
|
40
|
+
return cleanedPage;
|
|
41
|
+
});
|
|
42
|
+
if (!pagesChanged) return layout;
|
|
43
|
+
return {
|
|
44
|
+
...layout,
|
|
45
|
+
pages
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
function getPassBudget(layout) {
|
|
49
|
+
const fragmentCount = layout.pages.reduce((count, page) => count + page.fragments.length, 0);
|
|
50
|
+
const footnoteCount = new Set(layout.pages.flatMap((page) => page.footnoteIds ?? [])).size;
|
|
51
|
+
return layout.pages.length + fragmentCount + footnoteCount + 1;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Re-run a multi-column layout until its body flow clears the shared footnote band.
|
|
55
|
+
* Reserve floors follow the current page-to-footnote assignment. Repeated floor states
|
|
56
|
+
* and persistent overlap use a stable fallback layout instead of throwing.
|
|
57
|
+
*/
|
|
58
|
+
function reflowFootnoteColumns({ initialLayout, initialReserveFloors, runLayout }) {
|
|
59
|
+
let layout = initialLayout;
|
|
60
|
+
if (!hasFootnoteColumnOverlap(layout)) return clearOrphanedFootnoteReservations(layout);
|
|
61
|
+
const seenReserveStates = /* @__PURE__ */ new Set();
|
|
62
|
+
const passBudget = getPassBudget(layout);
|
|
63
|
+
for (let pass = 0; pass < passBudget; pass += 1) {
|
|
64
|
+
const reserveFloors = growFootnoteReserveFloors(layout, initialReserveFloors);
|
|
65
|
+
const reserveState = reserveFloorFingerprint(reserveFloors);
|
|
66
|
+
if (seenReserveStates.has(reserveState)) return clearOrphanedFootnoteReservations(layout);
|
|
67
|
+
seenReserveStates.add(reserveState);
|
|
68
|
+
layout = runLayout(reserveFloors);
|
|
69
|
+
if (!hasFootnoteColumnOverlap(layout)) return clearOrphanedFootnoteReservations(layout);
|
|
70
|
+
}
|
|
71
|
+
return clearOrphanedFootnoteReservations(layout);
|
|
72
|
+
}
|
|
73
|
+
//#endregion
|
|
74
|
+
export { reflowFootnoteColumns };
|
|
@@ -32,11 +32,9 @@ declare function applyContextualSpacing(blocks: FlowBlock[]): void;
|
|
|
32
32
|
/**
|
|
33
33
|
* Layout a document: convert blocks + measures into pages with positioned fragments.
|
|
34
34
|
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
* 3. Use paginator to manage page/column state
|
|
39
|
-
* 4. Handle page breaks, section breaks, and keepNext chains
|
|
35
|
+
* A reference discovered after the first column has already been placed can shrink the
|
|
36
|
+
* shared body band below earlier-column fragments. Retry those documents with the observed
|
|
37
|
+
* reservations as page floors so every column participates in the same footnote geometry.
|
|
40
38
|
*/
|
|
41
39
|
declare function layoutDocument(blocks: FlowBlock[], measures: Measure[], options: LayoutOptions): Layout;
|
|
42
40
|
/**
|