@stll/folio-core 0.48.0 → 0.50.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/ai-edits/apply.js +565 -121
- package/dist/ai-edits/headless.d.ts +37 -1
- package/dist/ai-edits/headless.js +47 -16
- package/dist/ai-edits/minimal-replacement.d.ts +64 -0
- package/dist/ai-edits/minimal-replacement.js +282 -0
- package/dist/controller/layoutPipeline.js +11 -1
- package/dist/display-list/build/paragraphPrimitives.js +63 -7
- package/dist/display-list/build/textBoxPrimitives.js +1 -1
- package/dist/display-list/build/unsupported.d.ts +2 -2
- package/dist/display-list/build/unsupported.js +0 -0
- package/dist/display-list/dom/renderDisplayListToDom.d.ts +10 -1
- package/dist/display-list/dom/renderDisplayListToDom.js +8 -8
- package/dist/display-list/html/renderDisplayListToHtml.d.ts +21 -0
- package/dist/display-list/html/renderDisplayListToHtml.js +126 -0
- package/dist/display-list/selectDisplayPages.d.ts +6 -0
- package/dist/display-list/selectDisplayPages.js +44 -0
- package/dist/docx/blockContentParser.js +2 -2
- package/dist/docx/bulletMarkers.d.ts +18 -2
- package/dist/docx/bulletMarkers.js +27 -2
- package/dist/docx/drawingGroupChildren.d.ts +60 -0
- package/dist/docx/drawingGroupChildren.js +176 -0
- package/dist/docx/drawingUtils.js +3 -0
- package/dist/docx/groupDrawingParser.d.ts +7 -1
- package/dist/docx/groupDrawingParser.js +123 -32
- package/dist/docx/packageParts.d.ts +45 -1
- package/dist/docx/packageParts.js +93 -2
- package/dist/docx/paraIdAttribute.d.ts +3 -2
- package/dist/docx/paragraphParser.js +91 -7
- package/dist/docx/paragraphTextBoxEnrichment.js +126 -4
- package/dist/docx/runParser.js +10 -0
- package/dist/docx/selectiveSaveFlags.d.ts +10 -3
- package/dist/docx/selectiveSaveFlags.js +1 -1
- package/dist/docx/selectiveXmlPatch.d.ts +56 -34
- package/dist/docx/selectiveXmlPatch.js +222 -115
- package/dist/docx/serializer/groupTextBoxWriteBack.d.ts +12 -0
- package/dist/docx/serializer/groupTextBoxWriteBack.js +101 -0
- package/dist/docx/serializer/paragraphSerializer.js +5 -4
- package/dist/docx/serializer/runSerializer.d.ts +5 -1
- package/dist/docx/serializer/runSerializer.js +4 -3
- package/dist/docx/shapeAlternateContent.d.ts +25 -0
- package/dist/docx/shapeAlternateContent.js +52 -0
- package/dist/docx/textBoxParser.d.ts +10 -2
- package/dist/docx/textBoxParser.js +9 -3
- package/dist/export-pdf.d.ts +35 -2
- package/dist/export-pdf.js +51 -20
- package/dist/fonts/fontsourceFaces.d.ts +47 -0
- package/dist/fonts/fontsourceFaces.js +230 -0
- package/dist/internal/headlessRevisionResolution.js +50 -13
- package/dist/layout-bridge/convert/flowBlockPostprocessing.d.ts +8 -0
- package/dist/layout-bridge/convert/flowBlockPostprocessing.js +106 -0
- package/dist/layout-bridge/convert/flowBorders.d.ts +17 -0
- package/dist/layout-bridge/convert/flowBorders.js +31 -0
- package/dist/layout-bridge/convert/flowConversionShared.d.ts +111 -0
- package/dist/layout-bridge/convert/flowConversionShared.js +23 -0
- package/dist/layout-bridge/convert/footnoteLayout.d.ts +2 -1
- package/dist/layout-bridge/convert/footnoteLayout.js +2 -1
- package/dist/layout-bridge/convert/headerFooterLayout.d.ts +2 -1
- package/dist/layout-bridge/convert/imageConversion.d.ts +33 -0
- package/dist/layout-bridge/convert/imageConversion.js +118 -0
- package/dist/layout-bridge/convert/listMarkers.d.ts +28 -0
- package/dist/layout-bridge/convert/listMarkers.js +98 -0
- package/dist/layout-bridge/convert/noteReferences.d.ts +14 -0
- package/dist/layout-bridge/convert/noteReferences.js +29 -0
- package/dist/layout-bridge/convert/pageBreakSplitting.d.ts +16 -0
- package/dist/layout-bridge/convert/pageBreakSplitting.js +140 -0
- package/dist/layout-bridge/convert/paragraphAttrs.d.ts +20 -0
- package/dist/layout-bridge/convert/paragraphAttrs.js +234 -0
- package/dist/layout-bridge/convert/paragraphConversion.d.ts +12 -0
- package/dist/layout-bridge/convert/paragraphConversion.js +112 -0
- package/dist/layout-bridge/convert/paragraphRuns.d.ts +15 -0
- package/dist/layout-bridge/convert/paragraphRuns.js +281 -0
- package/dist/layout-bridge/convert/runFormattingMerge.d.ts +19 -0
- package/dist/layout-bridge/convert/runFormattingMerge.js +167 -0
- package/dist/layout-bridge/convert/runMarkFormatting.d.ts +11 -0
- package/dist/layout-bridge/convert/runMarkFormatting.js +291 -0
- package/dist/layout-bridge/convert/sectionBoundaries.d.ts +35 -0
- package/dist/layout-bridge/convert/sectionBoundaries.js +114 -0
- package/dist/layout-bridge/convert/tableConversion.d.ts +12 -0
- package/dist/layout-bridge/convert/tableConversion.js +303 -0
- package/dist/layout-bridge/convert/textBoxFill.d.ts +20 -0
- package/dist/layout-bridge/convert/textBoxFill.js +42 -0
- package/dist/layout-bridge/convert/textFormattingConversion.d.ts +22 -0
- package/dist/layout-bridge/convert/textFormattingConversion.js +120 -0
- package/dist/layout-bridge/convert/toFlowBlocks.d.ts +5 -113
- package/dist/layout-bridge/convert/toFlowBlocks.js +11 -2060
- package/dist/layout-bridge/engine/hitTest.js +1 -1
- package/dist/layout-bridge/engine/measuring/index.d.ts +2 -1
- package/dist/layout-bridge/engine/measuring/index.js +2 -1
- package/dist/layout-bridge/engine/selectionRects.js +1 -1
- package/dist/layout-engine/imageLayout.d.ts +9 -0
- package/dist/layout-engine/imageLayout.js +48 -0
- package/dist/layout-engine/index.d.ts +4 -19
- package/dist/layout-engine/index.js +14 -910
- package/dist/layout-engine/keep-together.d.ts +5 -2
- package/dist/layout-engine/keep-together.js +5 -1
- package/dist/layout-engine/layoutFlowShared.d.ts +6 -0
- package/dist/layout-engine/layoutFlowShared.js +8 -0
- package/dist/layout-engine/measure/advanceComposition.js +49 -3
- package/dist/layout-engine/measure/crossRunWords.d.ts +45 -0
- package/dist/layout-engine/measure/crossRunWords.js +172 -0
- package/dist/layout-engine/measure/floatingLineClearance.d.ts +24 -0
- package/dist/layout-engine/measure/floatingLineClearance.js +44 -0
- package/dist/layout-engine/measure/inlineObjectMetrics.d.ts +6 -0
- package/dist/layout-engine/measure/inlineObjectMetrics.js +52 -0
- package/dist/layout-engine/measure/justification.d.ts +32 -0
- package/dist/layout-engine/measure/justification.js +83 -0
- package/dist/layout-engine/measure/lineFitting.d.ts +48 -0
- package/dist/layout-engine/measure/lineFitting.js +145 -0
- package/dist/layout-engine/measure/lineTypography.d.ts +44 -0
- package/dist/layout-engine/measure/lineTypography.js +124 -0
- package/dist/layout-engine/measure/measureBlocks.js +7 -4
- package/dist/layout-engine/measure/measureContainer.js +46 -3
- package/dist/layout-engine/measure/measureHelpers.js +13 -3
- package/dist/layout-engine/measure/measureParagraph.d.ts +1 -20
- package/dist/layout-engine/measure/measureParagraph.js +41 -780
- package/dist/layout-engine/measure/paragraphMeasureShared.d.ts +109 -0
- package/dist/layout-engine/measure/paragraphMeasureShared.js +82 -0
- package/dist/layout-engine/measure/paragraphTabs.d.ts +18 -0
- package/dist/layout-engine/measure/paragraphTabs.js +78 -0
- package/dist/layout-engine/measure/smallCapsCasing.d.ts +56 -0
- package/dist/layout-engine/measure/smallCapsCasing.js +88 -0
- package/dist/layout-engine/paginator.d.ts +2 -0
- package/dist/layout-engine/paginator.js +5 -1
- package/dist/layout-engine/paragraphLayout.d.ts +23 -0
- package/dist/layout-engine/paragraphLayout.js +204 -0
- package/dist/layout-engine/paragraphSpacing.d.ts +4 -2
- package/dist/layout-engine/paragraphSpacing.js +2 -1
- package/dist/layout-engine/sectionLayout.d.ts +45 -0
- package/dist/layout-engine/sectionLayout.js +216 -0
- package/dist/layout-engine/tableLayout.d.ts +17 -0
- package/dist/layout-engine/tableLayout.js +369 -0
- package/dist/layout-engine/tableRowBreak.d.ts +8 -1
- package/dist/layout-engine/tableRowBreak.js +22 -1
- package/dist/layout-engine/textBoxLayout.d.ts +15 -0
- package/dist/layout-engine/textBoxLayout.js +137 -0
- package/dist/layout-engine/types.d.ts +21 -2
- package/dist/layout-painter/renderPage.js +1 -0
- package/dist/layout-painter/renderParagraph.js +41 -4
- package/dist/layout-painter/renderTextBox.d.ts +12 -2
- package/dist/layout-painter/renderTextBox.js +26 -2
- package/dist/paged-layout/sectionBlockWidths.d.ts +2 -1
- package/dist/paged-layout/sectionBlockWidths.js +1 -1
- package/dist/prosemirror/alternateContentAttrs.d.ts +9 -0
- package/dist/prosemirror/alternateContentAttrs.js +35 -0
- package/dist/prosemirror/attrs/index.js +90 -0
- package/dist/prosemirror/commands/comments.js +51 -41
- package/dist/prosemirror/contentControlRevisions.d.ts +43 -0
- package/dist/prosemirror/contentControlRevisions.js +84 -0
- package/dist/prosemirror/conversion/fromProseDoc.js +46 -22
- package/dist/prosemirror/conversion/toProseDoc.js +42 -19
- package/dist/prosemirror/extensions/features/AutoBidiDetectionExtension.d.ts +7 -7
- package/dist/prosemirror/extensions/features/AutoBidiDetectionExtension.js +7 -7
- package/dist/prosemirror/extensions/nodes/FieldExtension.js +6 -3
- package/dist/prosemirror/extensions/nodes/SdtExtension.js +9 -2
- package/dist/prosemirror/extensions/nodes/ShapeExtension.js +1 -0
- package/dist/prosemirror/extensions/nodes/TextBoxExtension.js +3 -0
- package/dist/prosemirror/listMarker.js +2 -2
- package/dist/prosemirror/paragraphDirection.d.ts +29 -2
- package/dist/prosemirror/paragraphDirection.js +21 -2
- package/dist/prosemirror/plugins/suggestionMode.js +37 -8
- package/dist/prosemirror/rejoinRunCarriers.d.ts +27 -0
- package/dist/prosemirror/rejoinRunCarriers.js +66 -0
- package/dist/prosemirror/runIdentityAcrossRevisions.d.ts +10 -0
- package/dist/prosemirror/runIdentityAcrossRevisions.js +90 -0
- package/dist/prosemirror/schema/nodes.d.ts +37 -5
- package/dist/server.d.ts +3 -2
- package/dist/types/content.d.ts +2 -2
- package/package.json +2 -2
- package/dist/layout-engine/justifiedLineFit.d.ts +0 -7
- package/dist/layout-engine/justifiedLineFit.js +0 -6
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { headingLevelOf } from "@stll/docx-core/model";
|
|
2
|
+
//#region src/layout-bridge/convert/flowBlockPostprocessing.ts
|
|
3
|
+
/**
|
|
4
|
+
* Post-conversion passes over the flow block list: terminal empty-paragraph
|
|
5
|
+
* suppression, leading empty outline reservation, and run-in paragraph
|
|
6
|
+
* merging.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Word keeps a final empty body paragraph after a table as an editable anchor,
|
|
10
|
+
* but that final anchor does not create a page of its own. Earlier authored
|
|
11
|
+
* empty paragraphs retain their height and may carry the document onto a blank
|
|
12
|
+
* page. Preserve every block and PM range while collapsing only the final one.
|
|
13
|
+
*/
|
|
14
|
+
function isPaintlessTerminalParagraph(block) {
|
|
15
|
+
if (block?.kind !== "paragraph" || block.runs.length !== 0) return false;
|
|
16
|
+
const attrs = block.attrs;
|
|
17
|
+
if (attrs?.suppressEmptyParagraphHeight === false) return false;
|
|
18
|
+
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);
|
|
19
|
+
}
|
|
20
|
+
function suppressFinalEmptyParagraphAfterTable(blocks) {
|
|
21
|
+
let suffixStart = blocks.length;
|
|
22
|
+
while (suffixStart > 0 && isPaintlessTerminalParagraph(blocks[suffixStart - 1])) suffixStart -= 1;
|
|
23
|
+
if (suffixStart === blocks.length || suffixStart === 0 || blocks[suffixStart - 1]?.kind !== "table") return;
|
|
24
|
+
const finalBlock = blocks.at(-1);
|
|
25
|
+
if (isPaintlessTerminalParagraph(finalBlock)) finalBlock.attrs = {
|
|
26
|
+
...finalBlock.attrs,
|
|
27
|
+
suppressEmptyParagraphHeight: true
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
function suppressFinalParagraphInRepeatedEmptySuffix(blocks) {
|
|
31
|
+
let suffixStart = blocks.length;
|
|
32
|
+
while (suffixStart > 0 && isPaintlessTerminalParagraph(blocks[suffixStart - 1])) suffixStart -= 1;
|
|
33
|
+
if (suffixStart === 0 || blocks.length - suffixStart < 2 || blocks[suffixStart - 1]?.kind === "table") return;
|
|
34
|
+
const finalBlock = blocks.at(-1);
|
|
35
|
+
if (isPaintlessTerminalParagraph(finalBlock)) finalBlock.attrs = {
|
|
36
|
+
...finalBlock.attrs,
|
|
37
|
+
suppressEmptyParagraphHeight: true
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
function reserveLeadingEmptyOutlineHeight(blocks) {
|
|
41
|
+
const firstBlock = blocks.at(0);
|
|
42
|
+
if (firstBlock?.kind !== "paragraph" || firstBlock.runs.length !== 0 || headingLevelOf(firstBlock.attrs?.outlineLevel) !== 0) return;
|
|
43
|
+
firstBlock.attrs = {
|
|
44
|
+
...firstBlock.attrs,
|
|
45
|
+
reserveEmptyOutlineHeight: true
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Merge consecutive paragraph blocks where the first carries
|
|
50
|
+
* `runInWithNext` (`<w:specVanish/>` on the paragraph mark).
|
|
51
|
+
*
|
|
52
|
+
* Word's run-in heading feature renders the next paragraph inline on
|
|
53
|
+
* the same line, so for layout we collapse the pair into one
|
|
54
|
+
* ParagraphBlock with combined runs. The merged block keeps the first
|
|
55
|
+
* paragraph's attrs (heading formatting, list marker, indent) and
|
|
56
|
+
* extends pmEnd to the second paragraph's range so click-to-position
|
|
57
|
+
* resolution still maps both ranges back to body content.
|
|
58
|
+
*
|
|
59
|
+
* Chains: runInWithNext on the merged block is dropped because the
|
|
60
|
+
* second paragraph's mark wasn't `specVanish`. If a chain of
|
|
61
|
+
* specVanish paragraphs needs collapsing (rare in practice), the loop
|
|
62
|
+
* naturally handles it by re-inspecting the merged block's flag (we
|
|
63
|
+
* preserve runInWithNext only when the second paragraph itself has
|
|
64
|
+
* specVanish).
|
|
65
|
+
*/
|
|
66
|
+
function sdtGroupStacksEqual(a, b) {
|
|
67
|
+
const lenA = a?.length ?? 0;
|
|
68
|
+
if (lenA !== (b?.length ?? 0)) return false;
|
|
69
|
+
if (lenA === 0) return true;
|
|
70
|
+
for (let i = 0; i < lenA; i++) if (a?.[i]?.pmPos !== b?.[i]?.pmPos) return false;
|
|
71
|
+
return true;
|
|
72
|
+
}
|
|
73
|
+
function mergeRunInParagraphs(blocks) {
|
|
74
|
+
const out = [];
|
|
75
|
+
for (let i = 0; i < blocks.length; i++) {
|
|
76
|
+
let current = blocks[i];
|
|
77
|
+
if (!current) continue;
|
|
78
|
+
while (current.kind === "paragraph" && current.attrs?.runInWithNext && i + 1 < blocks.length) {
|
|
79
|
+
const next = blocks[i + 1];
|
|
80
|
+
if (!next || next.kind !== "paragraph") break;
|
|
81
|
+
const a = current;
|
|
82
|
+
const b = next;
|
|
83
|
+
if (!sdtGroupStacksEqual(a.sdtGroups, b.sdtGroups)) break;
|
|
84
|
+
const mergedAttrs = { ...a.attrs };
|
|
85
|
+
if (b.attrs?.spacing?.after !== void 0) mergedAttrs.spacing = {
|
|
86
|
+
...mergedAttrs.spacing,
|
|
87
|
+
after: b.attrs.spacing.after
|
|
88
|
+
};
|
|
89
|
+
if (b.attrs?.runInWithNext) mergedAttrs.runInWithNext = true;
|
|
90
|
+
else delete mergedAttrs.runInWithNext;
|
|
91
|
+
const merged = {
|
|
92
|
+
...a,
|
|
93
|
+
runs: [...a.runs, ...b.runs],
|
|
94
|
+
attrs: mergedAttrs
|
|
95
|
+
};
|
|
96
|
+
const mergedPmEnd = b.pmEnd ?? a.pmEnd;
|
|
97
|
+
if (mergedPmEnd !== void 0) merged.pmEnd = mergedPmEnd;
|
|
98
|
+
current = merged;
|
|
99
|
+
i += 1;
|
|
100
|
+
}
|
|
101
|
+
out.push(current);
|
|
102
|
+
}
|
|
103
|
+
return out;
|
|
104
|
+
}
|
|
105
|
+
//#endregion
|
|
106
|
+
export { mergeRunInParagraphs, reserveLeadingEmptyOutlineHeight, suppressFinalEmptyParagraphAfterTable, suppressFinalParagraphInRepeatedEmptySuffix };
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { document_d_exports } from "../../types/document.js";
|
|
2
|
+
import { BorderStyle } from "../../layout-engine/types.js";
|
|
3
|
+
//#region src/layout-bridge/convert/flowBorders.d.ts
|
|
4
|
+
/** A parsed border, or the same shape read off ProseMirror attributes. */
|
|
5
|
+
type ConvertibleBorder = {
|
|
6
|
+
style?: document_d_exports.BorderStyleValue;
|
|
7
|
+
size?: number;
|
|
8
|
+
space?: number;
|
|
9
|
+
color?: document_d_exports.ColorValue;
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* Convert an OOXML BorderSpec to a layout-engine BorderStyle.
|
|
13
|
+
* Shared by paragraph borders, cell borders, and header/footer borders.
|
|
14
|
+
*/
|
|
15
|
+
declare function convertBorderSpecToLayout(border: ConvertibleBorder, theme?: document_d_exports.Theme | null): BorderStyle | undefined;
|
|
16
|
+
//#endregion
|
|
17
|
+
export { ConvertibleBorder, convertBorderSpecToLayout };
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { cssBorderStyle } from "../../utils/borderCss.js";
|
|
2
|
+
import { resolveColor } from "../../utils/colorResolver.js";
|
|
3
|
+
import { pointsToPixels } from "../../utils/units.js";
|
|
4
|
+
import { statesNoBorder } from "@stll/docx-core/model";
|
|
5
|
+
//#region src/layout-bridge/convert/flowBorders.ts
|
|
6
|
+
/**
|
|
7
|
+
* Converts document-model border specifications to layout BorderStyle.
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Convert border width from eighths of a point to pixels.
|
|
11
|
+
* OOXML stores border widths in eighths of a point.
|
|
12
|
+
*/
|
|
13
|
+
function borderWidthToPixels(eighthsOfPoint) {
|
|
14
|
+
return pointsToPixels(eighthsOfPoint / 8);
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Convert an OOXML BorderSpec to a layout-engine BorderStyle.
|
|
18
|
+
* Shared by paragraph borders, cell borders, and header/footer borders.
|
|
19
|
+
*/
|
|
20
|
+
function convertBorderSpecToLayout(border, theme) {
|
|
21
|
+
if (border.style === void 0 || statesNoBorder(border.style)) return;
|
|
22
|
+
const result = {
|
|
23
|
+
style: cssBorderStyle(border.style),
|
|
24
|
+
width: border.size === void 0 ? 1 : borderWidthToPixels(border.size),
|
|
25
|
+
color: border.color ? resolveColor(border.color, theme) : "#000000"
|
|
26
|
+
};
|
|
27
|
+
if (border.space !== void 0) result.space = pointsToPixels(border.space);
|
|
28
|
+
return result;
|
|
29
|
+
}
|
|
30
|
+
//#endregion
|
|
31
|
+
export { convertBorderSpecToLayout };
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { document_d_exports } from "../../types/document.js";
|
|
2
|
+
import { RunStyleResolver } from "../../prosemirror/runStyleFormatting.js";
|
|
3
|
+
import { ParagraphAttrs, ParagraphBlock, TableBlock } from "../../layout-engine/types.js";
|
|
4
|
+
import { FontAlternates } from "../../fonts/fontAlternates.js";
|
|
5
|
+
import { ListCounterStreams } from "../../prosemirror/listMarker.js";
|
|
6
|
+
import { Node } from "prosemirror-model";
|
|
7
|
+
//#region src/layout-bridge/convert/flowConversionShared.d.ts
|
|
8
|
+
/**
|
|
9
|
+
* Options for the conversion.
|
|
10
|
+
*/
|
|
11
|
+
type ToFlowBlocksOptions = {
|
|
12
|
+
/** Default font family. */
|
|
13
|
+
defaultFont?: string;
|
|
14
|
+
/** Default font size in points. */
|
|
15
|
+
defaultSize?: number;
|
|
16
|
+
/** Theme for resolving theme colors. */
|
|
17
|
+
theme?: document_d_exports.Theme | null;
|
|
18
|
+
/** Document styles used to resolve character-style toggles without per-run copies. */
|
|
19
|
+
styles?: document_d_exports.StyleDefinitions;
|
|
20
|
+
/** Document-scoped OOXML primary-font to `w:altName` lookup. */
|
|
21
|
+
fontAlternates?: FontAlternates;
|
|
22
|
+
/** Page content height in pixels (pageHeight - marginTop - marginBottom). Images taller than this are scaled down to fit. */
|
|
23
|
+
pageContentHeight?: number;
|
|
24
|
+
/** Whether a trailing page break moves its paragraph mark to the next page. */
|
|
25
|
+
splitPageBreakAndParagraphMark?: boolean;
|
|
26
|
+
/** Shared list counters for nested containers. */
|
|
27
|
+
listCounters?: Map<number, number[]>;
|
|
28
|
+
/** Latest concrete counters by abstract numbering definition. */
|
|
29
|
+
listAbstractCounters?: Map<number, number[]>;
|
|
30
|
+
/** Shared startOverride state for nested containers. */
|
|
31
|
+
listSeenNumIds?: Set<string>;
|
|
32
|
+
/**
|
|
33
|
+
* Parallel counter state for the "original" (pre-revision) document, used to
|
|
34
|
+
* number tracked-deletion list items. Word numbers inserted and deleted list
|
|
35
|
+
* runs as if they never coexist: insertions get final-document numbering,
|
|
36
|
+
* deletions keep their original numbering. Without a separate stream a deleted
|
|
37
|
+
* item continues the counter of the inserted item before it (a, b → c, d, e
|
|
38
|
+
* instead of a, b and a, b, c). Normal items advance both streams.
|
|
39
|
+
*/
|
|
40
|
+
originalListCounters?: Map<number, number[]>;
|
|
41
|
+
/** Latest concrete original-stream counters by abstract numbering definition. */
|
|
42
|
+
originalListAbstractCounters?: Map<number, number[]>;
|
|
43
|
+
/** Original-stream startOverride state. */
|
|
44
|
+
originalListSeenNumIds?: Set<string>;
|
|
45
|
+
/**
|
|
46
|
+
* Document-wide `w:defaultTabStop` (§17.6.13) in twips. Stamped onto
|
|
47
|
+
* every paragraph block so paragraph-local layout helpers (list marker
|
|
48
|
+
* tab-stop math) can read it without taking a `Document` reference.
|
|
49
|
+
* Defaults to the OOXML 720-twip value when absent.
|
|
50
|
+
*/
|
|
51
|
+
defaultTabStopTwips?: number;
|
|
52
|
+
/** Document-wide custom Word line-breaking settings. */
|
|
53
|
+
lineBreakRules?: {
|
|
54
|
+
noLineBreaksBefore?: {
|
|
55
|
+
language?: string;
|
|
56
|
+
characters: string;
|
|
57
|
+
};
|
|
58
|
+
noLineBreaksAfter?: {
|
|
59
|
+
language?: string;
|
|
60
|
+
characters: string;
|
|
61
|
+
};
|
|
62
|
+
useLegacyEthiopicAmharicRules?: boolean;
|
|
63
|
+
};
|
|
64
|
+
/** Document-generation policy for justified line fitting. */
|
|
65
|
+
justificationCompatibility?: NonNullable<ParagraphAttrs["justificationCompatibility"]>;
|
|
66
|
+
/** Document-generation policy for where `w:tblInd` is measured from. */
|
|
67
|
+
tableIndentCompatibility?: NonNullable<TableBlock["indentCompatibility"]>;
|
|
68
|
+
/**
|
|
69
|
+
* When set, every table-cell anchor lays out inside its cell regardless of
|
|
70
|
+
* an authored `wp:anchor/@layoutInCell="0"`. Compatibility mode 15 and
|
|
71
|
+
* above ignore that opt-out; see `resolveAnchorLayoutInCellCompatibility`.
|
|
72
|
+
*/
|
|
73
|
+
forceAnchorLayoutInCell?: boolean;
|
|
74
|
+
/** Document-wide automatic hyphenation policy. */
|
|
75
|
+
automaticHyphenation?: NonNullable<ParagraphAttrs["automaticHyphenation"]>;
|
|
76
|
+
/** Line pitch for the final body section, whose properties live outside the PM body. */
|
|
77
|
+
finalSectionDocumentGridLinePitchTwips?: number;
|
|
78
|
+
/**
|
|
79
|
+
* The number a note story's `w:footnoteRef`/`w:endnoteRef` marks show. The
|
|
80
|
+
* mark stands for its note's reference number and shows it where it sits
|
|
81
|
+
* among the story's runs, in that run's formatting.
|
|
82
|
+
*/
|
|
83
|
+
noteReferenceMarkText?: string;
|
|
84
|
+
/**
|
|
85
|
+
* Endnotes laid out after the body's last block (`w:pos="docEnd"`). A body
|
|
86
|
+
* that references one ends in its endnote area, so its final paragraph is no
|
|
87
|
+
* longer the document's last and keeps its height.
|
|
88
|
+
*/
|
|
89
|
+
trailingEndnoteIds?: ReadonlySet<number>;
|
|
90
|
+
};
|
|
91
|
+
type FlowConversionOptions = ToFlowBlocksOptions & {
|
|
92
|
+
firstPageBreakRunPosition: (node: Node) => number | undefined;
|
|
93
|
+
/** `w:doNotUseIndentAsNumberingTabStop`, read from the document node. */
|
|
94
|
+
numberingTabIgnoresIndent: boolean;
|
|
95
|
+
listCounterStreams: ListCounterStreams;
|
|
96
|
+
numberedRefResults?: ReadonlyMap<Node, string>;
|
|
97
|
+
textBoxAnchorBlockIds: Map<string, ParagraphBlock["id"]>;
|
|
98
|
+
styleResolver: RunStyleResolver;
|
|
99
|
+
};
|
|
100
|
+
/**
|
|
101
|
+
* Convert twips to pixels (1 twip = 1/1440 inch, 1 inch = 96 CSS px).
|
|
102
|
+
* No rounding — precision prevents cumulative layout drift across paragraphs.
|
|
103
|
+
*/
|
|
104
|
+
declare function twipsToPixels(twips: number): number;
|
|
105
|
+
declare function nextBlockId(): string;
|
|
106
|
+
/**
|
|
107
|
+
* Reset the block ID counter (useful for testing).
|
|
108
|
+
*/
|
|
109
|
+
declare function resetBlockIdCounter(): void;
|
|
110
|
+
//#endregion
|
|
111
|
+
export { FlowConversionOptions, ToFlowBlocksOptions, nextBlockId, resetBlockIdCounter, twipsToPixels };
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
//#region src/layout-bridge/convert/flowConversionShared.ts
|
|
2
|
+
/**
|
|
3
|
+
* Convert twips to pixels (1 twip = 1/1440 inch, 1 inch = 96 CSS px).
|
|
4
|
+
* No rounding — precision prevents cumulative layout drift across paragraphs.
|
|
5
|
+
*/
|
|
6
|
+
function twipsToPixels(twips) {
|
|
7
|
+
return twips / 1440 * 96;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Generate a unique block ID.
|
|
11
|
+
*/
|
|
12
|
+
let blockIdCounter = 0;
|
|
13
|
+
function nextBlockId() {
|
|
14
|
+
return `block-${++blockIdCounter}`;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Reset the block ID counter (useful for testing).
|
|
18
|
+
*/
|
|
19
|
+
function resetBlockIdCounter() {
|
|
20
|
+
blockIdCounter = 0;
|
|
21
|
+
}
|
|
22
|
+
//#endregion
|
|
23
|
+
export { nextBlockId, resetBlockIdCounter, twipsToPixels };
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { document_d_exports } from "../../types/document.js";
|
|
2
2
|
import { FOOTNOTE_ENTRY_MARGIN_BOTTOM, FOOTNOTE_FALLBACK_LINE_HEIGHT, FOOTNOTE_SEPARATOR_HEIGHT, FlowBlock, FootnoteContent, Measure, Page } from "../../layout-engine/types.js";
|
|
3
|
-
import { ToFlowBlocksOptions } from "./
|
|
3
|
+
import { ToFlowBlocksOptions } from "./flowConversionShared.js";
|
|
4
|
+
import "./toFlowBlocks.js";
|
|
4
5
|
//#region src/layout-bridge/convert/footnoteLayout.d.ts
|
|
5
6
|
type MeasureBlocksFn = (blocks: FlowBlock[], contentWidth: number) => Measure[];
|
|
6
7
|
type ConvertFootnoteOptions = {
|
|
@@ -3,7 +3,8 @@ import { layoutTextBoxContent } from "../../layout-engine/measure/textBoxParagra
|
|
|
3
3
|
import { DEFAULT_TEXTBOX_MARGINS, FOOTNOTE_ENTRY_MARGIN_BOTTOM, FOOTNOTE_FALLBACK_LINE_HEIGHT, FOOTNOTE_SEPARATOR_HEIGHT, isFloatingImageRun, resolveTableWidthPx } from "../../layout-engine/types.js";
|
|
4
4
|
import { expectPreservedXmlAttrs } from "../../prosemirror/attrs/index.js";
|
|
5
5
|
import { footnoteToProseDoc } from "../../prosemirror/conversion/toProseDoc.js";
|
|
6
|
-
import { collectNoteRefs, isNoteReferenceMarkXml
|
|
6
|
+
import { collectNoteRefs, isNoteReferenceMarkXml } from "./noteReferences.js";
|
|
7
|
+
import { toFlowBlocks } from "./toFlowBlocks.js";
|
|
7
8
|
//#region src/layout-bridge/convert/footnoteLayout.ts
|
|
8
9
|
/** Default footnote font size in points */
|
|
9
10
|
const FOOTNOTE_FONT_SIZE = 8;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { document_d_exports } from "../../types/document.js";
|
|
2
2
|
import { FlowBlock, HeaderFooterContent, ImageRun, ImageRunPosition, Measure, PageMargins } from "../../layout-engine/types.js";
|
|
3
|
-
import { ToFlowBlocksOptions } from "./
|
|
3
|
+
import { ToFlowBlocksOptions } from "./flowConversionShared.js";
|
|
4
|
+
import "./toFlowBlocks.js";
|
|
4
5
|
import { MeasureBlocksFn } from "./footnoteLayout.js";
|
|
5
6
|
import { Node } from "prosemirror-model";
|
|
6
7
|
//#region src/layout-bridge/convert/headerFooterLayout.d.ts
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { ImageAttrs } from "../../prosemirror/schema/nodes.js";
|
|
2
|
+
import { ImageBlock, ImageRun, RunFormatting } from "../../layout-engine/types.js";
|
|
3
|
+
import { Node } from "prosemirror-model";
|
|
4
|
+
//#region src/layout-bridge/convert/imageConversion.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* Constrain image dimensions to fit within the page content area.
|
|
7
|
+
* Scales proportionally if height exceeds pageContentHeight.
|
|
8
|
+
*/
|
|
9
|
+
declare function constrainImageToPage(width: number, height: number, pageContentHeight: number | undefined): {
|
|
10
|
+
width: number;
|
|
11
|
+
height: number;
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* Build an ImageRun from ProseMirror node attrs, applying conditional property assignment
|
|
15
|
+
* to satisfy exactOptionalPropertyTypes.
|
|
16
|
+
*/
|
|
17
|
+
declare function buildImageRun(attrs: ImageAttrs, constrained: {
|
|
18
|
+
width: number;
|
|
19
|
+
height: number;
|
|
20
|
+
}, pmStart: number, pmEnd: number, trackedChange?: Pick<RunFormatting, "isInsertion" | "isDeletion" | "changeAuthor" | "changeDate" | "changeRevisionId">, forceLayoutInCell?: boolean): ImageRun;
|
|
21
|
+
/** A package image whose bytes cannot paint still owns its authored line box. */
|
|
22
|
+
declare const hasRelationshipBackedImageBox: (attrs: ImageAttrs) => boolean;
|
|
23
|
+
/**
|
|
24
|
+
* A preview counts as a source: the bytes are not in the attrs, but the
|
|
25
|
+
* display list can produce them without I/O when it interns the descriptor.
|
|
26
|
+
*/
|
|
27
|
+
declare const hasPaintableImageSource: (attrs: Pick<ImageAttrs, "src" | "preview">) => boolean;
|
|
28
|
+
/**
|
|
29
|
+
* Convert an image node to an ImageBlock.
|
|
30
|
+
*/
|
|
31
|
+
declare function convertImage(node: Node, startPos: number, pageContentHeight?: number): ImageBlock | undefined;
|
|
32
|
+
//#endregion
|
|
33
|
+
export { buildImageRun, constrainImageToPage, convertImage, hasPaintableImageSource, hasRelationshipBackedImageBox };
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { expectImageAttrs } from "../../prosemirror/attrs/index.js";
|
|
2
|
+
import { cssBorderStyleOf } from "../../utils/borderCss.js";
|
|
3
|
+
import { sanitizeImageSrc } from "../../utils/sanitizeImageSrc.js";
|
|
4
|
+
import { nextBlockId } from "./flowConversionShared.js";
|
|
5
|
+
//#region src/layout-bridge/convert/imageConversion.ts
|
|
6
|
+
/**
|
|
7
|
+
* Constrain image dimensions to fit within the page content area.
|
|
8
|
+
* Scales proportionally if height exceeds pageContentHeight.
|
|
9
|
+
*/
|
|
10
|
+
function constrainImageToPage(width, height, pageContentHeight) {
|
|
11
|
+
if (!pageContentHeight || height <= pageContentHeight) return {
|
|
12
|
+
width,
|
|
13
|
+
height
|
|
14
|
+
};
|
|
15
|
+
const scale = pageContentHeight / height;
|
|
16
|
+
return {
|
|
17
|
+
width: Math.round(width * scale),
|
|
18
|
+
height: pageContentHeight
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Build an ImageRun from ProseMirror node attrs, applying conditional property assignment
|
|
23
|
+
* to satisfy exactOptionalPropertyTypes.
|
|
24
|
+
*/
|
|
25
|
+
function buildImageRun(attrs, constrained, pmStart, pmEnd, trackedChange, forceLayoutInCell) {
|
|
26
|
+
const run = {
|
|
27
|
+
kind: "image",
|
|
28
|
+
src: attrs.src,
|
|
29
|
+
...attrs.preview ? { preview: attrs.preview } : {},
|
|
30
|
+
width: constrained.width,
|
|
31
|
+
height: constrained.height,
|
|
32
|
+
pmStart,
|
|
33
|
+
pmEnd
|
|
34
|
+
};
|
|
35
|
+
if (attrs.alt !== void 0) run.alt = attrs.alt;
|
|
36
|
+
if (attrs.transform !== void 0) run.transform = attrs.transform;
|
|
37
|
+
if (attrs.opacity != null) run.opacity = attrs.opacity;
|
|
38
|
+
if (attrs.brightness != null) run.brightness = attrs.brightness;
|
|
39
|
+
if (attrs.contrast != null) run.contrast = attrs.contrast;
|
|
40
|
+
if (attrs.wrapType !== void 0) run.wrapType = attrs.wrapType;
|
|
41
|
+
if (attrs.displayMode !== void 0) run.displayMode = attrs.displayMode;
|
|
42
|
+
if (attrs.cssFloat !== void 0) run.cssFloat = attrs.cssFloat;
|
|
43
|
+
if (attrs.distTop !== void 0) run.distTop = attrs.distTop;
|
|
44
|
+
if (attrs.distBottom !== void 0) run.distBottom = attrs.distBottom;
|
|
45
|
+
if (attrs.distLeft !== void 0) run.distLeft = attrs.distLeft;
|
|
46
|
+
if (attrs.distRight !== void 0) run.distRight = attrs.distRight;
|
|
47
|
+
if (attrs._docxObjectPreview === true) run.exactLineHeight = true;
|
|
48
|
+
if (attrs.cropTop != null) run.cropTop = attrs.cropTop;
|
|
49
|
+
if (attrs.cropRight != null) run.cropRight = attrs.cropRight;
|
|
50
|
+
if (attrs.cropBottom != null) run.cropBottom = attrs.cropBottom;
|
|
51
|
+
if (attrs.cropLeft != null) run.cropLeft = attrs.cropLeft;
|
|
52
|
+
if (attrs.borderWidth != null) run.borderWidth = attrs.borderWidth;
|
|
53
|
+
if (attrs.borderColor) run.borderColor = attrs.borderColor;
|
|
54
|
+
const runBorderStyle = cssBorderStyleOf(attrs.borderStyle);
|
|
55
|
+
if (runBorderStyle) run.borderStyle = runBorderStyle;
|
|
56
|
+
if (attrs.position !== void 0) run.position = attrs.position;
|
|
57
|
+
if (forceLayoutInCell) run.layoutInCell = true;
|
|
58
|
+
else if (attrs.anchor?.layoutInCell !== void 0) run.layoutInCell = attrs.anchor.layoutInCell;
|
|
59
|
+
if (trackedChange?.isInsertion) run.isInsertion = true;
|
|
60
|
+
if (trackedChange?.isDeletion) run.isDeletion = true;
|
|
61
|
+
if (trackedChange?.changeAuthor !== void 0) run.changeAuthor = trackedChange.changeAuthor;
|
|
62
|
+
if (trackedChange?.changeDate !== void 0) run.changeDate = trackedChange.changeDate;
|
|
63
|
+
if (trackedChange?.changeRevisionId !== void 0) run.changeRevisionId = trackedChange.changeRevisionId;
|
|
64
|
+
return run;
|
|
65
|
+
}
|
|
66
|
+
/** A package image whose bytes cannot paint still owns its authored line box. */
|
|
67
|
+
const hasRelationshipBackedImageBox = (attrs) => typeof attrs.rId === "string" && attrs.rId.trim().length > 0 && typeof attrs.width === "number" && Number.isFinite(attrs.width) && attrs.width >= 0 && typeof attrs.height === "number" && Number.isFinite(attrs.height) && attrs.height >= 0;
|
|
68
|
+
/**
|
|
69
|
+
* A preview counts as a source: the bytes are not in the attrs, but the
|
|
70
|
+
* display list can produce them without I/O when it interns the descriptor.
|
|
71
|
+
*/
|
|
72
|
+
const hasPaintableImageSource = (attrs) => attrs.preview !== void 0 || sanitizeImageSrc(attrs.src) !== void 0;
|
|
73
|
+
/**
|
|
74
|
+
* Convert an image node to an ImageBlock.
|
|
75
|
+
*/
|
|
76
|
+
function convertImage(node, startPos, pageContentHeight) {
|
|
77
|
+
const attrs = expectImageAttrs(node);
|
|
78
|
+
if (!hasPaintableImageSource(attrs) && !hasRelationshipBackedImageBox(attrs)) return;
|
|
79
|
+
const wrapType = attrs.wrapType;
|
|
80
|
+
const shouldAnchor = wrapType === "behind" || wrapType === "inFront";
|
|
81
|
+
const constrained = constrainImageToPage(attrs.width ?? 100, attrs.height ?? 100, pageContentHeight);
|
|
82
|
+
const imgBlock = {
|
|
83
|
+
kind: "image",
|
|
84
|
+
id: nextBlockId(),
|
|
85
|
+
src: attrs.src,
|
|
86
|
+
...attrs.preview ? { preview: attrs.preview } : {},
|
|
87
|
+
width: constrained.width,
|
|
88
|
+
height: constrained.height,
|
|
89
|
+
pmStart: startPos,
|
|
90
|
+
pmEnd: startPos + node.nodeSize
|
|
91
|
+
};
|
|
92
|
+
if (attrs.alt) imgBlock.alt = attrs.alt;
|
|
93
|
+
if (attrs.transform) imgBlock.transform = attrs.transform;
|
|
94
|
+
if (attrs.opacity != null) imgBlock.opacity = attrs.opacity;
|
|
95
|
+
if (attrs.brightness != null) imgBlock.brightness = attrs.brightness;
|
|
96
|
+
if (attrs.contrast != null) imgBlock.contrast = attrs.contrast;
|
|
97
|
+
if (shouldAnchor) {
|
|
98
|
+
const anchor = {
|
|
99
|
+
isAnchored: true,
|
|
100
|
+
behindDoc: wrapType === "behind"
|
|
101
|
+
};
|
|
102
|
+
if (attrs.distLeft !== void 0) anchor.offsetH = attrs.distLeft;
|
|
103
|
+
if (attrs.distTop !== void 0) anchor.offsetV = attrs.distTop;
|
|
104
|
+
imgBlock.anchor = anchor;
|
|
105
|
+
}
|
|
106
|
+
if (attrs.hlinkHref) imgBlock.hlinkHref = attrs.hlinkHref;
|
|
107
|
+
if (attrs.cropTop != null) imgBlock.cropTop = attrs.cropTop;
|
|
108
|
+
if (attrs.cropRight != null) imgBlock.cropRight = attrs.cropRight;
|
|
109
|
+
if (attrs.cropBottom != null) imgBlock.cropBottom = attrs.cropBottom;
|
|
110
|
+
if (attrs.cropLeft != null) imgBlock.cropLeft = attrs.cropLeft;
|
|
111
|
+
if (attrs.borderWidth != null) imgBlock.borderWidth = attrs.borderWidth;
|
|
112
|
+
if (attrs.borderColor) imgBlock.borderColor = attrs.borderColor;
|
|
113
|
+
const imageBorderStyle = cssBorderStyleOf(attrs.borderStyle);
|
|
114
|
+
if (imageBorderStyle) imgBlock.borderStyle = imageBorderStyle;
|
|
115
|
+
return imgBlock;
|
|
116
|
+
}
|
|
117
|
+
//#endregion
|
|
118
|
+
export { buildImageRun, constrainImageToPage, convertImage, hasPaintableImageSource, hasRelationshipBackedImageBox };
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { document_d_exports } from "../../types/document.js";
|
|
2
|
+
import { ParagraphAttrs, ParagraphPropertyChangeAttrs } from "../../prosemirror/schema/nodes.js";
|
|
3
|
+
import { ParagraphAttrs as ParagraphAttrs$1 } from "../../layout-engine/types.js";
|
|
4
|
+
import { FontAlternates } from "../../fonts/fontAlternates.js";
|
|
5
|
+
import { ListCounterState } from "../../prosemirror/listMarker.js";
|
|
6
|
+
//#region src/layout-bridge/convert/listMarkers.d.ts
|
|
7
|
+
declare function applyMarkerAllCaps(marker: string | null, allCaps: boolean | undefined): string | null;
|
|
8
|
+
/**
|
|
9
|
+
* Convert PM paragraph attrs to layout engine paragraph attrs.
|
|
10
|
+
*/
|
|
11
|
+
type ListMarkerRevision = NonNullable<ParagraphAttrs$1["listMarkerRevision"]>;
|
|
12
|
+
type ListPropertyChange = ParagraphPropertyChangeAttrs;
|
|
13
|
+
type ListPropertyFormatting = NonNullable<ListPropertyChange["previousFormatting"]>;
|
|
14
|
+
declare function toListMarkerRevision(kind: ListMarkerRevision["kind"], info: ListPropertyChange["info"]): ListMarkerRevision;
|
|
15
|
+
declare function isAddedNumberingChange(change: ListPropertyChange): change is ListPropertyChange & {
|
|
16
|
+
previousFormatting: ListPropertyFormatting;
|
|
17
|
+
};
|
|
18
|
+
declare function isRemovedNumberingChange(change: ListPropertyChange): change is ListPropertyChange & {
|
|
19
|
+
previousFormatting: ListPropertyFormatting;
|
|
20
|
+
};
|
|
21
|
+
declare function isChangedNumberingChange(currentNumPr: NonNullable<ParagraphAttrs["numPr"]>, change: ListPropertyChange): change is ListPropertyChange & {
|
|
22
|
+
previousFormatting: ListPropertyFormatting;
|
|
23
|
+
};
|
|
24
|
+
declare function applyDeletedListMarkerAttrs(attrs: ParagraphAttrs$1, change: ListPropertyChange & {
|
|
25
|
+
previousFormatting: ListPropertyFormatting;
|
|
26
|
+
}, listCounterState: ListCounterState | undefined, theme: document_d_exports.Theme | null | undefined, fontAlternates: FontAlternates | undefined, paragraphMarkFormatting: () => document_d_exports.TextFormatting | undefined): void;
|
|
27
|
+
//#endregion
|
|
28
|
+
export { ListPropertyChange, ListPropertyFormatting, applyDeletedListMarkerAttrs, applyMarkerAllCaps, isAddedNumberingChange, isChangedNumberingChange, isRemovedNumberingChange, toListMarkerRevision };
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { bulletMarkerFontName, convertBulletToUnicode } from "../../docx/bulletMarkers.js";
|
|
2
|
+
import { isListNumPr } from "../../layout-engine/types.js";
|
|
3
|
+
import { advanceListMarker } from "../../prosemirror/listMarker.js";
|
|
4
|
+
import { listMarkerFormattingFor } from "./textFormattingConversion.js";
|
|
5
|
+
import { sameStatedParagraphNumbering } from "@stll/docx-core/model";
|
|
6
|
+
//#region src/layout-bridge/convert/listMarkers.ts
|
|
7
|
+
/**
|
|
8
|
+
* List-marker resolution for paragraphs, including tracked numbering changes
|
|
9
|
+
* (inserted, removed, and changed list markers).
|
|
10
|
+
*/
|
|
11
|
+
function applyMarkerAllCaps(marker, allCaps) {
|
|
12
|
+
if (marker === null || !allCaps) return marker;
|
|
13
|
+
return marker.toLocaleUpperCase();
|
|
14
|
+
}
|
|
15
|
+
function computeListMarker(pmAttrs, state) {
|
|
16
|
+
return advanceListMarker(pmAttrs, state);
|
|
17
|
+
}
|
|
18
|
+
function toListMarkerRevision(kind, info) {
|
|
19
|
+
const revision = { kind };
|
|
20
|
+
if (typeof info?.author === "string") revision.author = info.author;
|
|
21
|
+
if (typeof info?.date === "string") revision.date = info.date;
|
|
22
|
+
if (typeof info?.id === "number") revision.revisionId = info.id;
|
|
23
|
+
return revision;
|
|
24
|
+
}
|
|
25
|
+
function isAddedNumberingChange(change) {
|
|
26
|
+
const previousFormatting = change.previousFormatting;
|
|
27
|
+
return previousFormatting != null && Object.hasOwn(previousFormatting, "numPr") && previousFormatting.numPr == null;
|
|
28
|
+
}
|
|
29
|
+
function isRemovedNumberingChange(change) {
|
|
30
|
+
const previousFormatting = change.previousFormatting;
|
|
31
|
+
return previousFormatting != null && Object.hasOwn(previousFormatting, "numPr") && isListNumPr(previousFormatting.numPr);
|
|
32
|
+
}
|
|
33
|
+
function isChangedNumberingChange(currentNumPr, change) {
|
|
34
|
+
const previousFormatting = change.previousFormatting;
|
|
35
|
+
return previousFormatting != null && Object.hasOwn(previousFormatting, "numPr") && isListNumPr(previousFormatting.numPr) && !sameStatedParagraphNumbering(previousFormatting.numPr, currentNumPr);
|
|
36
|
+
}
|
|
37
|
+
function toPreviousListAttrs(previousFormatting) {
|
|
38
|
+
const attrs = {};
|
|
39
|
+
const numPr = previousFormatting.numPr;
|
|
40
|
+
if (isListNumPr(numPr)) attrs.numPr = numPr;
|
|
41
|
+
const listIsBullet = previousFormatting.listIsBullet;
|
|
42
|
+
if (listIsBullet !== void 0) attrs.listIsBullet = listIsBullet;
|
|
43
|
+
const listIsLegal = previousFormatting.listIsLegal;
|
|
44
|
+
if (listIsLegal !== void 0) attrs.listIsLegal = listIsLegal;
|
|
45
|
+
const listMarker = previousFormatting.listMarker;
|
|
46
|
+
if (listMarker !== void 0) attrs.listMarker = listMarker;
|
|
47
|
+
const listMarkerTemplate = previousFormatting.listMarkerTemplate;
|
|
48
|
+
if (listMarkerTemplate !== void 0) attrs.listMarkerTemplate = listMarkerTemplate;
|
|
49
|
+
const listNumFmt = previousFormatting.listNumFmt;
|
|
50
|
+
if (listNumFmt !== void 0) attrs.listNumFmt = listNumFmt;
|
|
51
|
+
const listLevelNumFmts = previousFormatting.listLevelNumFmts;
|
|
52
|
+
if (listLevelNumFmts !== void 0) attrs.listLevelNumFmts = listLevelNumFmts;
|
|
53
|
+
const listLevelStarts = previousFormatting.listLevelStarts;
|
|
54
|
+
if (listLevelStarts !== void 0) attrs.listLevelStarts = listLevelStarts;
|
|
55
|
+
const listAbstractNumId = previousFormatting.listAbstractNumId;
|
|
56
|
+
if (listAbstractNumId !== void 0) attrs.listAbstractNumId = listAbstractNumId;
|
|
57
|
+
const listStartOverride = previousFormatting.listStartOverride;
|
|
58
|
+
if (listStartOverride !== void 0) attrs.listStartOverride = listStartOverride;
|
|
59
|
+
const listMarkerHidden = previousFormatting.listMarkerHidden;
|
|
60
|
+
if (listMarkerHidden !== void 0) attrs.listMarkerHidden = listMarkerHidden;
|
|
61
|
+
const listMarkerFormatting = previousFormatting.listMarkerFormatting;
|
|
62
|
+
if (listMarkerFormatting !== void 0) attrs.listMarkerFormatting = listMarkerFormatting;
|
|
63
|
+
const listMarkerAlignment = previousFormatting.listMarkerAlignment;
|
|
64
|
+
if (listMarkerAlignment !== void 0) attrs.listMarkerAlignment = listMarkerAlignment;
|
|
65
|
+
const listMarkerSuffix = previousFormatting.listMarkerSuffix;
|
|
66
|
+
if (listMarkerSuffix !== void 0) attrs.listMarkerSuffix = listMarkerSuffix;
|
|
67
|
+
const listMarkerAllCaps = previousFormatting.listMarkerAllCaps;
|
|
68
|
+
if (listMarkerAllCaps !== void 0) attrs.listMarkerAllCaps = listMarkerAllCaps;
|
|
69
|
+
const listImplicitChildLevelAdvances = previousFormatting.listImplicitChildLevelAdvances;
|
|
70
|
+
if (listImplicitChildLevelAdvances !== void 0) attrs.listImplicitChildLevelAdvances = listImplicitChildLevelAdvances;
|
|
71
|
+
const listMarkerSecondSlotOffsetTwips = previousFormatting.listMarkerSecondSlotOffsetTwips;
|
|
72
|
+
if (listMarkerSecondSlotOffsetTwips !== void 0) attrs.listMarkerSecondSlotOffsetTwips = listMarkerSecondSlotOffsetTwips;
|
|
73
|
+
return attrs;
|
|
74
|
+
}
|
|
75
|
+
function resolveDeletedListMarker(previousListAttrs, listCounterState) {
|
|
76
|
+
if (listCounterState && previousListAttrs.numPr) {
|
|
77
|
+
const marker = computeListMarker(previousListAttrs, listCounterState);
|
|
78
|
+
if (marker) return marker;
|
|
79
|
+
}
|
|
80
|
+
if (previousListAttrs.listMarker) return previousListAttrs.listIsBullet ? convertBulletToUnicode(previousListAttrs.listMarker, bulletMarkerFontName(previousListAttrs.listMarkerFormatting)) : previousListAttrs.listMarker;
|
|
81
|
+
if (previousListAttrs.listIsBullet) return "•";
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
function applyDeletedListMarkerAttrs(attrs, change, listCounterState, theme, fontAlternates, paragraphMarkFormatting) {
|
|
85
|
+
const previousListAttrs = toPreviousListAttrs(change.previousFormatting);
|
|
86
|
+
const marker = resolveDeletedListMarker(previousListAttrs, listCounterState);
|
|
87
|
+
if (!marker) return;
|
|
88
|
+
attrs.listMarker = marker;
|
|
89
|
+
attrs.listMarkerRevision = toListMarkerRevision("del", change.info);
|
|
90
|
+
if (previousListAttrs.listIsBullet !== void 0) attrs.listIsBullet = previousListAttrs.listIsBullet;
|
|
91
|
+
if (previousListAttrs.listMarkerHidden !== void 0) attrs.listMarkerHidden = previousListAttrs.listMarkerHidden;
|
|
92
|
+
const listMarkerFormatting = listMarkerFormattingFor(previousListAttrs.listMarkerFormatting, paragraphMarkFormatting(), theme, fontAlternates);
|
|
93
|
+
if (listMarkerFormatting) attrs.listMarkerFormatting = listMarkerFormatting;
|
|
94
|
+
if (previousListAttrs.listMarkerAlignment) attrs.listMarkerAlignment = previousListAttrs.listMarkerAlignment;
|
|
95
|
+
if (previousListAttrs.listMarkerSuffix) attrs.listMarkerSuffix = previousListAttrs.listMarkerSuffix;
|
|
96
|
+
}
|
|
97
|
+
//#endregion
|
|
98
|
+
export { applyDeletedListMarkerAttrs, applyMarkerAllCaps, isAddedNumberingChange, isChangedNumberingChange, isRemovedNumberingChange, toListMarkerRevision };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { FlowBlock } from "../../layout-engine/types.js";
|
|
2
|
+
//#region src/layout-bridge/convert/noteReferences.d.ts
|
|
3
|
+
/**
|
|
4
|
+
* Runs with `idKey` set, in document order: the note references a block list
|
|
5
|
+
* makes, through table cells (and tables within cells) and text boxes.
|
|
6
|
+
*/
|
|
7
|
+
declare function collectNoteRefs(blocks: readonly FlowBlock[], idKey: "footnoteRefId" | "endnoteRefId"): {
|
|
8
|
+
noteId: number;
|
|
9
|
+
pmPos: number;
|
|
10
|
+
}[];
|
|
11
|
+
/** Whether captured run-child markup is a note story's `w:footnoteRef`/`w:endnoteRef`. */
|
|
12
|
+
declare function isNoteReferenceMarkXml(xml: string): boolean;
|
|
13
|
+
//#endregion
|
|
14
|
+
export { collectNoteRefs, isNoteReferenceMarkXml };
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
//#region src/layout-bridge/convert/noteReferences.ts
|
|
2
|
+
const NOTE_REFERENCE_MARK_XML = /^<(?:[^\s:/>]+:)?(?:footnoteRef|endnoteRef)[\s/>]/u;
|
|
3
|
+
/**
|
|
4
|
+
* Runs with `idKey` set, in document order: the note references a block list
|
|
5
|
+
* makes, through table cells (and tables within cells) and text boxes.
|
|
6
|
+
*/
|
|
7
|
+
function collectNoteRefs(blocks, idKey) {
|
|
8
|
+
const refs = [];
|
|
9
|
+
const walk = (containerBlocks) => {
|
|
10
|
+
for (const block of containerBlocks) if (block.kind === "paragraph") for (const run of block.runs) {
|
|
11
|
+
if (run.kind !== "text") continue;
|
|
12
|
+
const noteId = run[idKey];
|
|
13
|
+
if (noteId !== void 0) refs.push({
|
|
14
|
+
noteId,
|
|
15
|
+
pmPos: run.pmStart ?? 0
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
else if (block.kind === "table") for (const row of block.rows) for (const cell of row.cells) walk(cell.blocks);
|
|
19
|
+
else if (block.kind === "textBox") walk(block.content);
|
|
20
|
+
};
|
|
21
|
+
walk(blocks);
|
|
22
|
+
return refs;
|
|
23
|
+
}
|
|
24
|
+
/** Whether captured run-child markup is a note story's `w:footnoteRef`/`w:endnoteRef`. */
|
|
25
|
+
function isNoteReferenceMarkXml(xml) {
|
|
26
|
+
return NOTE_REFERENCE_MARK_XML.test(xml);
|
|
27
|
+
}
|
|
28
|
+
//#endregion
|
|
29
|
+
export { collectNoteRefs, isNoteReferenceMarkXml };
|