@stll/folio-core 0.11.0 → 0.12.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 +602 -50
- package/dist/ai-edits/block-identity.d.ts +4 -0
- package/dist/ai-edits/block-identity.js +9 -0
- package/dist/ai-edits/blockRange.d.ts +28 -1
- package/dist/ai-edits/blockRange.js +67 -1
- package/dist/ai-edits/headless.d.ts +4 -4
- package/dist/ai-edits/headless.js +9 -9
- package/dist/ai-edits/index.d.ts +2 -2
- package/dist/ai-edits/index.js +2 -2
- package/dist/ai-edits/read.d.ts +7 -7
- package/dist/ai-edits/read.js +110 -3
- package/dist/ai-edits/table-cell-mutations.d.ts +48 -0
- package/dist/ai-edits/table-cell-mutations.js +237 -0
- package/dist/ai-edits/table-mutation-plan.d.ts +38 -0
- package/dist/ai-edits/table-mutation-plan.js +90 -0
- package/dist/ai-edits/table-row-column-mutations.d.ts +100 -0
- package/dist/ai-edits/table-row-column-mutations.js +300 -0
- package/dist/ai-edits/table-targets.d.ts +32 -0
- package/dist/ai-edits/table-targets.js +70 -0
- package/dist/ai-edits/types.d.ts +34 -0
- package/dist/compat/eigenpal.js +1 -1
- package/dist/content-controls/mutateContentControls.js +1 -1
- package/dist/controller/activeEditorStory.d.ts +36 -0
- package/dist/controller/activeEditorStory.js +30 -0
- package/dist/controller/contentControlWidgetController.d.ts +55 -0
- package/dist/controller/contentControlWidgetController.js +141 -0
- package/dist/controller/fontReadiness.js +2 -0
- package/dist/controller/headerFooterEditorManager.js +1 -1
- package/dist/controller/hiddenEditorApi.js +2 -2
- package/dist/controller/hiddenEditorManager.d.ts +12 -1
- package/dist/controller/hiddenEditorManager.js +32 -11
- package/dist/controller/layoutPipeline.d.ts +2 -0
- package/dist/controller/layoutPipeline.js +16 -1
- package/dist/controller/noteEditorManager.d.ts +36 -0
- package/dist/controller/noteEditorManager.js +210 -0
- package/dist/document-operations.d.ts +35 -3
- package/dist/document-operations.js +161 -3
- package/dist/docx/blockContentParser.js +2 -2
- package/dist/docx/commentParser.js +6 -1
- package/dist/docx/conformance.js +8 -1
- package/dist/docx/documentGrid.d.ts +8 -0
- package/dist/docx/documentGrid.js +8 -0
- package/dist/docx/drawingUtils.js +1 -1
- package/dist/docx/ensureParaIds.js +1 -1
- package/dist/docx/fieldParser.js +1 -1
- package/dist/docx/notePropertiesParser.js +1 -1
- package/dist/docx/numberingParser.js +1 -1
- package/dist/docx/paragraphParser.js +63 -58
- package/dist/docx/paragraphTextBoxEnrichment.d.ts +2 -1
- package/dist/docx/paragraphTextBoxEnrichment.js +67 -13
- package/dist/docx/paragraphTraversal.js +1 -1
- package/dist/docx/parser.js +3 -3
- package/dist/docx/rezip.js +1 -1
- package/dist/docx/runParser.js +4 -2
- package/dist/docx/sdtProperties.js +1 -1
- package/dist/docx/sectionParser.js +1 -1
- package/dist/docx/selectiveSave.js +1 -1
- package/dist/docx/serializer/commentSerializer.js +1 -2
- package/dist/docx/serializer/documentSerializer.js +2 -2
- package/dist/docx/serializer/headerFooterSerializer.js +2 -2
- package/dist/docx/serializer/noteSerializer.js +2 -2
- package/dist/docx/serializer/paragraphSerializer.js +7 -1
- package/dist/docx/serializer/runSerializer.js +4 -2
- package/dist/docx/serializer/stylesSerializer.js +1 -1
- package/dist/docx/serializer/tableSerializer.d.ts +5 -4
- package/dist/docx/serializer/tableSerializer.js +17 -13
- package/dist/docx/server/applyDocxXmlPatchProposal.d.ts +87 -0
- package/dist/docx/server/applyDocxXmlPatchProposal.js +204 -0
- package/dist/docx/server/boundedArchive.d.ts +14 -3
- package/dist/docx/server/boundedArchive.js +56 -11
- package/dist/docx/server/evaluateDocxXmlPatchProposal.d.ts +85 -0
- package/dist/docx/server/evaluateDocxXmlPatchProposal.js +361 -0
- package/dist/docx/server/inspectDocxPackage.d.ts +70 -0
- package/dist/docx/server/inspectDocxPackage.js +217 -0
- package/dist/docx/server/validateDocxConformance.d.ts +41 -0
- package/dist/docx/server/validateDocxConformance.js +336 -0
- package/dist/docx/settingsParser.js +11 -1
- package/dist/docx/shapeParser.js +1 -1
- package/dist/docx/styleParser.js +4 -2
- package/dist/docx/tableParser.js +18 -7
- package/dist/docx/textBoxParser.d.ts +3 -3
- package/dist/docx/textBoxParser.js +16 -12
- package/dist/docx/xmlSafety.d.ts +5 -0
- package/dist/docx/xmlSafety.js +9 -0
- package/dist/fields/resolveFieldValues.js +1 -3
- package/dist/i18n/messages/catalogs.gen.d.ts +17 -0
- package/dist/i18n/messages/catalogs.gen.js +17 -0
- package/dist/i18n/messages/messages.gen.d.ts +1 -0
- package/dist/index.js +1 -1
- package/dist/layout-bridge/convert/footnoteLayout.d.ts +1 -0
- package/dist/layout-bridge/convert/footnoteLayout.js +47 -40
- package/dist/layout-bridge/convert/headerFooterLayout.d.ts +1 -0
- package/dist/layout-bridge/convert/headerFooterLayout.js +3 -0
- package/dist/layout-bridge/convert/templatePreviewFlow.js +3 -3
- package/dist/layout-bridge/convert/toFlowBlocks.d.ts +4 -2
- package/dist/layout-bridge/convert/toFlowBlocks.js +136 -20
- package/dist/layout-bridge/dom/noteStoryDom.d.ts +6 -0
- package/dist/layout-bridge/dom/noteStoryDom.js +17 -0
- package/dist/layout-bridge/engine/clickToPosition.js +2 -2
- package/dist/layout-engine/index.d.ts +2 -2
- package/dist/layout-engine/index.js +43 -28
- package/dist/layout-engine/justificationCompatibility.d.ts +6 -0
- package/dist/layout-engine/justificationCompatibility.js +8 -0
- package/dist/layout-engine/keep-together.js +11 -1
- package/dist/layout-engine/measure/cache.js +21 -5
- package/dist/layout-engine/measure/lineBreakProvider.js +53 -34
- package/dist/layout-engine/measure/measureBlocks.js +6 -3
- package/dist/layout-engine/measure/measureContainer.js +1 -1
- package/dist/layout-engine/measure/measureHelpers.d.ts +1 -1
- package/dist/layout-engine/measure/measureHelpers.js +3 -3
- package/dist/layout-engine/measure/measureParagraph.js +69 -25
- package/dist/layout-engine/measure/measureProvider.js +4 -3
- package/dist/layout-engine/measure/tableCellFlow.js +5 -2
- package/dist/layout-engine/measure/textBoxParagraphLayout.d.ts +9 -7
- package/dist/layout-engine/measure/textBoxParagraphLayout.js +22 -6
- package/dist/layout-engine/measure/textMeasurementPolicy.d.ts +3 -1
- package/dist/layout-engine/measure/textMeasurementPolicy.js +7 -1
- package/dist/layout-engine/paginator.js +5 -1
- package/dist/layout-engine/paragraphSpacing.d.ts +28 -4
- package/dist/layout-engine/paragraphSpacing.js +107 -13
- package/dist/layout-engine/renderedBreakReconciliation.js +1 -1
- package/dist/layout-engine/types.d.ts +26 -13
- package/dist/layout-painter/registry/modules/textBox.js +5 -1
- package/dist/layout-painter/renderPage.d.ts +5 -2
- package/dist/layout-painter/renderPage.js +56 -12
- package/dist/layout-painter/renderParagraph.js +22 -6
- package/dist/layout-painter/renderTable.d.ts +5 -1
- package/dist/layout-painter/renderTable.js +5 -2
- package/dist/layout-painter/renderTextBox.d.ts +2 -1
- package/dist/layout-painter/renderTextBox.js +25 -14
- package/dist/managers/DocumentLoaderManager.js +1 -1
- package/dist/model.js +1 -1
- package/dist/prosemirror/attrs/index.d.ts +4 -2
- package/dist/prosemirror/attrs/index.js +154 -18
- package/dist/prosemirror/commands/comments.d.ts +3 -3
- package/dist/prosemirror/commands/comments.js +248 -12
- package/dist/prosemirror/commands/contentControls.js +1 -1
- package/dist/prosemirror/commands/index.js +1 -1
- package/dist/prosemirror/commands/pageBreak.js +1 -1
- package/dist/prosemirror/commands/propertyChangeScope.d.ts +2 -2
- package/dist/prosemirror/commands/propertyChangeScope.js +3 -2
- package/dist/prosemirror/commands/tableCellMergeResolution.d.ts +9 -0
- package/dist/prosemirror/commands/tableCellMergeResolution.js +181 -0
- package/dist/prosemirror/conversion/fromProseDoc.d.ts +2 -1
- package/dist/prosemirror/conversion/fromProseDoc.js +286 -61
- package/dist/prosemirror/conversion/sdtAttrs.d.ts +9 -0
- package/dist/prosemirror/conversion/sdtAttrs.js +49 -0
- package/dist/prosemirror/conversion/toProseDoc.d.ts +2 -1
- package/dist/prosemirror/conversion/toProseDoc.js +347 -98
- package/dist/prosemirror/extensions/ExtensionManager.js +1 -1
- package/dist/prosemirror/extensions/StarterKit.js +6 -1
- package/dist/prosemirror/extensions/core/ParagraphExtension.js +4 -1
- package/dist/prosemirror/extensions/features/BaseKeymapExtension.js +1 -0
- package/dist/prosemirror/extensions/features/ListExtension.js +1 -1
- package/dist/prosemirror/extensions/features/ParaIdAllocatorExtension.d.ts +10 -1
- package/dist/prosemirror/extensions/features/ParaIdAllocatorExtension.js +93 -2
- package/dist/prosemirror/extensions/features/ParagraphChangeTrackerExtension.d.ts +2 -1
- package/dist/prosemirror/extensions/features/ParagraphChangeTrackerExtension.js +6 -2
- package/dist/prosemirror/extensions/marks/CharacterSpacingExtension.js +1 -1
- package/dist/prosemirror/extensions/marks/TrackedChangeExtensions.d.ts +9 -1
- package/dist/prosemirror/extensions/marks/TrackedChangeExtensions.js +24 -1
- package/dist/prosemirror/extensions/nodes/FieldExtension.js +1 -1
- package/dist/prosemirror/extensions/nodes/RenderedPageBreakExtension.d.ts +7 -0
- package/dist/prosemirror/extensions/nodes/RenderedPageBreakExtension.js +22 -0
- package/dist/prosemirror/extensions/nodes/TableExtension.js +9 -3
- package/dist/prosemirror/extensions/nodes/TextBoxAnchorExtension.d.ts +13 -0
- package/dist/prosemirror/extensions/nodes/TextBoxAnchorExtension.js +41 -0
- package/dist/prosemirror/extensions/nodes/TextBoxExtension.d.ts +5 -2
- package/dist/prosemirror/extensions/nodes/TextBoxExtension.js +4 -1
- package/dist/prosemirror/plugins/contentControlWidgets.d.ts +34 -8
- package/dist/prosemirror/plugins/contentControlWidgets.js +125 -91
- package/dist/prosemirror/plugins/selectionTracker.js +1 -0
- package/dist/prosemirror/plugins/suggestionMode.js +1 -1
- package/dist/prosemirror/schema/index.d.ts +2 -2
- package/dist/prosemirror/schema/marks.d.ts +5 -1
- package/dist/prosemirror/schema/nodes.d.ts +64 -7
- package/dist/prosemirror/selectionState.js +1 -0
- package/dist/prosemirror/styles/resolvedStyleAttrs.js +1 -0
- package/dist/prosemirror/tableCellMergeRevision.d.ts +9 -0
- package/dist/prosemirror/tableCellMergeRevision.js +22 -0
- package/dist/prosemirror/textBoxAnchorAttrs.d.ts +9 -0
- package/dist/prosemirror/textBoxAnchorAttrs.js +33 -0
- package/dist/prosemirror/utils/extractTrackedChanges.d.ts +1 -1
- package/dist/prosemirror/utils/extractTrackedChanges.js +62 -14
- package/dist/prosemirror/validation.js +9 -1
- package/dist/server.d.ts +6 -2
- package/dist/server.js +5 -1
- package/dist/utils/fontFamilyMerge.js +9 -11
- package/dist/utils/fontLoader.js +3 -2
- package/dist/utils/fontResolver.js +22 -4
- package/dist/utils/fontWeights.d.ts +1 -1
- package/dist/utils/fontWeights.js +1 -1
- package/dist/utils/paragraphFormattingMerge.js +1 -0
- package/package.json +1 -1
|
@@ -1,4 +1,7 @@
|
|
|
1
|
+
import { continuesNumberedSequence } from "./paragraphSequence.js";
|
|
1
2
|
//#region src/layout-engine/paragraphSpacing.ts
|
|
3
|
+
/** Collapse adjacent paragraph spacing to the larger authored side. */
|
|
4
|
+
const collapseParagraphSpacing = ({ before, after }) => Math.max(before, after);
|
|
2
5
|
/** Whether a paragraph has no visible run content. */
|
|
3
6
|
function isEmptyParagraph(block) {
|
|
4
7
|
if (block.runs.length === 0) return true;
|
|
@@ -10,21 +13,112 @@ function isEmptyParagraph(block) {
|
|
|
10
13
|
* Resolve leading paragraph spacing after applying the empty-paragraph
|
|
11
14
|
* inherited-spacing collapse rule.
|
|
12
15
|
*/
|
|
13
|
-
|
|
14
|
-
const value = block.attrs?.spacing?.before ?? 0;
|
|
15
|
-
if (value === 0) return 0;
|
|
16
|
-
if (isEmptyParagraph(block) && !block.attrs?.styleId && !block.attrs?.hasDirectParagraphFormatting && !block.attrs?.hasDirectParagraphMarkFormatting && !block.attrs?.spacingExplicit?.before) return 0;
|
|
17
|
-
return value;
|
|
18
|
-
}
|
|
16
|
+
const getParagraphSpacingBefore = (block) => resolveEffectiveParagraphSpacing(block).before;
|
|
19
17
|
/**
|
|
20
18
|
* Resolve trailing paragraph spacing after applying the empty-paragraph
|
|
21
19
|
* inherited-spacing collapse rule.
|
|
22
20
|
*/
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
21
|
+
const getParagraphSpacingAfter = (block) => resolveEffectiveParagraphSpacing(block).after;
|
|
22
|
+
/** Resolve empty-paragraph spacing without modifying the authored block. */
|
|
23
|
+
const resolveEffectiveParagraphSpacing = (block) => {
|
|
24
|
+
const before = block.attrs?.spacing?.before ?? 0;
|
|
25
|
+
const after = block.attrs?.spacing?.after ?? 0;
|
|
26
|
+
if (!isEmptyParagraph(block) || preservesInheritedEmptySpacing(block)) return {
|
|
27
|
+
before,
|
|
28
|
+
after
|
|
29
|
+
};
|
|
30
|
+
return {
|
|
31
|
+
before: block.attrs?.spacingExplicit?.before ? before : 0,
|
|
32
|
+
after: block.attrs?.spacingExplicit?.after ? after : 0
|
|
33
|
+
};
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* Derive sequence-level spacing suppression with structural sharing.
|
|
37
|
+
*
|
|
38
|
+
* Contextual and automatic list spacing affect layout values, not authored
|
|
39
|
+
* OOXML formatting. Only changed branches are cloned; the input tree remains
|
|
40
|
+
* untouched and repeated resolution is idempotent.
|
|
41
|
+
*/
|
|
42
|
+
const resolveEffectiveParagraphSpacingTree = (blocks) => {
|
|
43
|
+
let changed = false;
|
|
44
|
+
const resolved = blocks.map((block, index) => {
|
|
45
|
+
const next = resolveBlockSpacing(blocks, index);
|
|
46
|
+
if (next !== block) changed = true;
|
|
47
|
+
return next;
|
|
48
|
+
});
|
|
49
|
+
return changed ? resolved : blocks;
|
|
50
|
+
};
|
|
51
|
+
const paragraphsShareStyle = (previous, current) => previous.attrs?.styleId === current.attrs?.styleId;
|
|
52
|
+
const preservesInheritedEmptySpacing = (block) => Boolean(block.attrs?.styleId || block.attrs?.hasDirectParagraphFormatting || block.attrs?.hasDirectParagraphMarkFormatting);
|
|
53
|
+
const resolveBlockSpacing = (blocks, index) => {
|
|
54
|
+
const block = blocks[index];
|
|
55
|
+
if (block.kind === "paragraph") return resolveParagraphSequenceSpacing(blocks, index, block);
|
|
56
|
+
if (block.kind === "table") return resolveTableSpacing(block);
|
|
57
|
+
if (block.kind === "textBox") return resolveTextBoxSpacing(block);
|
|
58
|
+
return block;
|
|
59
|
+
};
|
|
60
|
+
const resolveParagraphSequenceSpacing = (blocks, index, block) => {
|
|
61
|
+
const spacing = block.attrs?.spacing;
|
|
62
|
+
if (!spacing) return block;
|
|
63
|
+
const previous = blocks[index - 1];
|
|
64
|
+
const next = blocks[index + 1];
|
|
65
|
+
const suppressBefore = previous?.kind === "paragraph" && (block.attrs?.contextualSpacing === true && paragraphsShareStyle(previous, block) || block.attrs?.automaticSpacing?.before === true && continuesNumberedSequence(previous, block));
|
|
66
|
+
const suppressAfter = next?.kind === "paragraph" && (block.attrs?.contextualSpacing === true && paragraphsShareStyle(block, next) || block.attrs?.automaticSpacing?.after === true && continuesNumberedSequence(block, next));
|
|
67
|
+
const beforeChanged = suppressBefore && spacing.before !== 0;
|
|
68
|
+
const afterChanged = suppressAfter && spacing.after !== 0;
|
|
69
|
+
if (!beforeChanged && !afterChanged) return block;
|
|
70
|
+
const effectiveSpacing = { ...spacing };
|
|
71
|
+
if (beforeChanged) effectiveSpacing.before = 0;
|
|
72
|
+
if (afterChanged) effectiveSpacing.after = 0;
|
|
73
|
+
return {
|
|
74
|
+
...block,
|
|
75
|
+
attrs: {
|
|
76
|
+
...block.attrs,
|
|
77
|
+
spacing: effectiveSpacing
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
};
|
|
81
|
+
const resolveTextBoxContentSpacing = (blocks) => {
|
|
82
|
+
let changed = false;
|
|
83
|
+
const resolved = blocks.map((block, index) => {
|
|
84
|
+
const next = block.kind === "table" ? resolveTableSpacing(block) : resolveParagraphSequenceSpacing(blocks, index, block);
|
|
85
|
+
if (next !== block) changed = true;
|
|
86
|
+
return next;
|
|
87
|
+
});
|
|
88
|
+
return changed ? resolved : blocks;
|
|
89
|
+
};
|
|
90
|
+
const resolveTableSpacing = (block) => {
|
|
91
|
+
let changed = false;
|
|
92
|
+
const rows = block.rows.map((row) => {
|
|
93
|
+
let rowChanged = false;
|
|
94
|
+
const cells = row.cells.map((cell) => {
|
|
95
|
+
const blocks = resolveEffectiveParagraphSpacingTree(cell.blocks);
|
|
96
|
+
if (blocks === cell.blocks) return cell;
|
|
97
|
+
rowChanged = true;
|
|
98
|
+
return {
|
|
99
|
+
...cell,
|
|
100
|
+
blocks
|
|
101
|
+
};
|
|
102
|
+
});
|
|
103
|
+
if (!rowChanged) return row;
|
|
104
|
+
changed = true;
|
|
105
|
+
return {
|
|
106
|
+
...row,
|
|
107
|
+
cells
|
|
108
|
+
};
|
|
109
|
+
});
|
|
110
|
+
return changed ? {
|
|
111
|
+
...block,
|
|
112
|
+
rows
|
|
113
|
+
} : block;
|
|
114
|
+
};
|
|
115
|
+
const resolveTextBoxSpacing = (block) => {
|
|
116
|
+
const content = resolveTextBoxContentSpacing(block.content);
|
|
117
|
+
if (content === block.content) return block;
|
|
118
|
+
return {
|
|
119
|
+
...block,
|
|
120
|
+
content
|
|
121
|
+
};
|
|
122
|
+
};
|
|
29
123
|
//#endregion
|
|
30
|
-
export { getParagraphSpacingAfter, getParagraphSpacingBefore, isEmptyParagraph };
|
|
124
|
+
export { collapseParagraphSpacing, getParagraphSpacingAfter, getParagraphSpacingBefore, isEmptyParagraph, paragraphsShareStyle, resolveEffectiveParagraphSpacing, resolveEffectiveParagraphSpacingTree };
|
|
@@ -21,7 +21,7 @@ const reconcileBreakBeforeBlock = ({ state, block, previousBlock, page, blocksBy
|
|
|
21
21
|
state
|
|
22
22
|
};
|
|
23
23
|
const markerAlreadySatisfied = pageStartsWithPreviousParagraphContinuation(page, previousBlock) || state.type === "pageAdvance" && state.reason === "authoredBoundary" || state.type === "pageAdvance" && state.reason === "reflowBoundary" || state.type === "pageAdvance" && isPaginationEmptyParagraph(block) || state.type === "pageAdvance" && (continuesNumberedSequence(previousBlock, block) || continuesTabbedParagraphSequence(previousBlock, block));
|
|
24
|
-
const forcePageBreak = (renderedBreakNeedsSnap || block.attrs?.keepNext === true) && !markerAlreadySatisfied && pageHasVisibleBodyContent(page, blocksById);
|
|
24
|
+
const forcePageBreak = (renderedBreakNeedsSnap || block.attrs?.keepNext === true || (block.attrs?.spacing?.before ?? 0) > 0 && previousBlock?.kind !== "sectionBreak" || previousBlock?.kind === "table") && !markerAlreadySatisfied && pageHasVisibleBodyContent(page, blocksById);
|
|
25
25
|
const followsAuthoredPageBreak = previousBlock?.kind === "pageBreak";
|
|
26
26
|
const followsSectionBreak = previousBlock?.kind === "sectionBreak";
|
|
27
27
|
const followsPageBreakingSection = followsSectionBreak && previousBlock.type !== "continuous";
|
|
@@ -3,7 +3,7 @@ import { ImagePosition, ImageWrap, SdtProperties, SdtType, ShapeTextBody, TableC
|
|
|
3
3
|
|
|
4
4
|
//#region src/layout-engine/types.d.ts
|
|
5
5
|
declare namespace types_d_exports {
|
|
6
|
-
export { BlockId, BorderStyle, CellBorderSpec, CellBorders, ColumnBreakBlock, ColumnBreakMeasure, ColumnLayout, DEFAULT_TEXTBOX_MARGINS, DEFAULT_TEXTBOX_WIDTH, DocumentPosition, FOOTNOTE_ENTRY_MARGIN_BOTTOM, FOOTNOTE_FALLBACK_LINE_HEIGHT, FOOTNOTE_SEPARATOR_HEIGHT, FieldRun, FloatingTablePosition, FlowBlock, FootnoteContent, Fragment, FragmentBase, HeaderFooterContent, HeaderFooterContentHeights, HeaderFooterLayout, HitTestResult, HyperlinkInfo, ImageBlock, ImageFragment, ImageMeasure, ImageRun, ImageRunPosition, Layout, LayoutOptions, LineBreakRun, ListNumPr, MathRun, Measure, MeasuredLine, Page, PageBreakBlock, PageBreakMeasure, PageHeaderFooterRefs, PageMargins, ParagraphAttrs, ParagraphBlock, ParagraphBorders, ParagraphFragment, ParagraphIndent, ParagraphMeasure, ParagraphSpacing, Run, RunFormatting, SdtGroup, SectionBreakBlock, SectionBreakMeasure, TabAlignment, TabRun, TabStop, TableBlock, TableCell, TableCellMeasure, TableFragment, TableMeasure, TableRow, TableRowMeasure, TextBoxBlock, TextBoxFlowAttrs, TextBoxFragment, TextBoxMeasure, TextRun, floatingTextBoxReservesBand, floatingTextBoxWrapsText, getTableRowLeadingWidth, isFloatingImageRun, isFloatingTextBoxBlock, isTextWrappingFloatingImageRun, tableColumnsArePinned };
|
|
6
|
+
export { BlockId, BorderStyle, CellBorderSpec, CellBorders, ColumnBreakBlock, ColumnBreakMeasure, ColumnLayout, DEFAULT_TEXTBOX_MARGINS, DEFAULT_TEXTBOX_WIDTH, DocumentPosition, FOOTNOTE_ENTRY_MARGIN_BOTTOM, FOOTNOTE_FALLBACK_LINE_HEIGHT, FOOTNOTE_SEPARATOR_HEIGHT, FieldRun, FloatingTablePosition, FlowBlock, FootnoteContent, Fragment, FragmentBase, HeaderFooterContent, HeaderFooterContentHeights, HeaderFooterLayout, HitTestResult, HyperlinkInfo, ImageBlock, ImageFragment, ImageMeasure, ImageRun, ImageRunPosition, Layout, LayoutOptions, LineBreakRun, ListNumPr, MathRun, Measure, MeasuredLine, Page, PageBreakBlock, PageBreakMeasure, PageHeaderFooterRefs, PageMargins, ParagraphAttrs, ParagraphBlock, ParagraphBorders, ParagraphFragment, ParagraphIndent, ParagraphMeasure, ParagraphSpacing, RenderedPageBreakRun, Run, RunFormatting, SdtGroup, SectionBreakBlock, SectionBreakMeasure, TabAlignment, TabRun, TabStop, TableBlock, TableCell, TableCellMeasure, TableFragment, TableMeasure, TableRow, TableRowMeasure, TextBoxBlock, TextBoxFlowAttrs, TextBoxFragment, TextBoxMeasure, TextRun, floatingTextBoxReservesBand, floatingTextBoxWrapsText, getTableRowLeadingWidth, isFloatingImageRun, isFloatingTextBoxBlock, isTextWrappingFloatingImageRun, tableColumnsArePinned };
|
|
7
7
|
}
|
|
8
8
|
/**
|
|
9
9
|
* Unique identifier for a block in the document.
|
|
@@ -196,6 +196,12 @@ type LineBreakRun = {
|
|
|
196
196
|
pmStart?: number;
|
|
197
197
|
pmEnd?: number;
|
|
198
198
|
};
|
|
199
|
+
/** Zero-width cached page boundary inside a paragraph. */
|
|
200
|
+
type RenderedPageBreakRun = RunFormatting & {
|
|
201
|
+
kind: "renderedPageBreak";
|
|
202
|
+
pmStart?: number;
|
|
203
|
+
pmEnd?: number;
|
|
204
|
+
};
|
|
199
205
|
/**
|
|
200
206
|
* A field run (PAGE, NUMPAGES, etc.) that gets substituted at render time.
|
|
201
207
|
*/
|
|
@@ -230,7 +236,7 @@ type MathRun = RunFormatting & {
|
|
|
230
236
|
/**
|
|
231
237
|
* Union of all run types.
|
|
232
238
|
*/
|
|
233
|
-
type Run = TextRun | TabRun | ImageRun | LineBreakRun | FieldRun | MathRun;
|
|
239
|
+
type Run = TextRun | TabRun | ImageRun | LineBreakRun | RenderedPageBreakRun | FieldRun | MathRun;
|
|
234
240
|
/**
|
|
235
241
|
* Paragraph spacing configuration.
|
|
236
242
|
*/
|
|
@@ -293,7 +299,10 @@ type ListNumPr = {
|
|
|
293
299
|
* Paragraph block attributes.
|
|
294
300
|
*/
|
|
295
301
|
type ParagraphAttrs = {
|
|
296
|
-
alignment?: "left" | "center" | "right" | "justify"; /**
|
|
302
|
+
alignment?: "left" | "center" | "right" | "justify"; /** Document-generation policy for justified line fitting. */
|
|
303
|
+
justificationCompatibility?: {
|
|
304
|
+
type: "legacy";
|
|
305
|
+
}; /** East Asian first/last-character restrictions (`w:kinsoku`). */
|
|
297
306
|
kinsoku?: boolean; /** Hanging punctuation (`w:overflowPunct`); defaults to enabled when omitted. */
|
|
298
307
|
overflowPunctuation?: boolean; /** Exempt this paragraph from document automatic hyphenation. */
|
|
299
308
|
suppressAutoHyphens?: boolean; /** Document-wide automatic hyphenation controls retained for line layout. */
|
|
@@ -315,7 +324,9 @@ type ParagraphAttrs = {
|
|
|
315
324
|
useLegacyEthiopicAmharicRules?: boolean;
|
|
316
325
|
}; /** OOXML outline level (`w:outlineLvl`), where zero is the top level. */
|
|
317
326
|
outlineLevel?: number;
|
|
318
|
-
spacing?: ParagraphSpacing; /**
|
|
327
|
+
spacing?: ParagraphSpacing; /** Whether this paragraph participates in the active section line grid. */
|
|
328
|
+
snapToGrid?: boolean; /** Active section line pitch in pixels; present only on top-level body paragraphs. */
|
|
329
|
+
documentGridLinePitch?: number; /** Spacing sides resolved from OOXML automatic paragraph spacing. */
|
|
319
330
|
automaticSpacing?: {
|
|
320
331
|
before?: boolean;
|
|
321
332
|
after?: boolean;
|
|
@@ -343,7 +354,7 @@ type ParagraphAttrs = {
|
|
|
343
354
|
keepNext?: boolean;
|
|
344
355
|
keepLines?: boolean;
|
|
345
356
|
widowControl?: boolean;
|
|
346
|
-
pageBreakBefore?: boolean; /**
|
|
357
|
+
pageBreakBefore?: boolean; /** Cached OOXML pagination hint (`w:lastRenderedPageBreak`). */
|
|
347
358
|
renderedPageBreakBefore?: boolean;
|
|
348
359
|
styleId?: string;
|
|
349
360
|
contextualSpacing?: boolean;
|
|
@@ -572,7 +583,8 @@ type SectionBreakBlock = {
|
|
|
572
583
|
};
|
|
573
584
|
orientation?: "portrait" | "landscape";
|
|
574
585
|
margins?: PageMargins;
|
|
575
|
-
columns?: ColumnLayout;
|
|
586
|
+
columns?: ColumnLayout; /** Line pitch authored by the section properties that end at this boundary, in twips. */
|
|
587
|
+
documentGridLinePitchTwips?: number;
|
|
576
588
|
};
|
|
577
589
|
type PageHeaderFooterRefs = {
|
|
578
590
|
titlePg?: boolean;
|
|
@@ -612,7 +624,7 @@ declare const DEFAULT_TEXTBOX_MARGINS: {
|
|
|
612
624
|
/** Default text box width in pixels when no width is specified */
|
|
613
625
|
declare const DEFAULT_TEXTBOX_WIDTH = 200;
|
|
614
626
|
/**
|
|
615
|
-
* Text box block — positioned container with
|
|
627
|
+
* Text box block — positioned container with block content.
|
|
616
628
|
*/
|
|
617
629
|
type TextBoxBlock = {
|
|
618
630
|
kind: "textBox";
|
|
@@ -629,8 +641,8 @@ type TextBoxBlock = {
|
|
|
629
641
|
bottom: number;
|
|
630
642
|
left: number;
|
|
631
643
|
right: number;
|
|
632
|
-
}; /**
|
|
633
|
-
content: ParagraphBlock[]; /** Display mode copied from the ProseMirror text box node. */
|
|
644
|
+
}; /** Flow blocks inside the text box */
|
|
645
|
+
content: (ParagraphBlock | TableBlock)[]; /** Display mode copied from the ProseMirror text box node. */
|
|
634
646
|
displayMode?: "inline" | "float" | "block"; /** CSS float direction copied from the ProseMirror text box node. */
|
|
635
647
|
cssFloat?: "left" | "right" | "none"; /** OOXML wrap type for anchored text boxes. */
|
|
636
648
|
wrapType?: ImageWrap["type"]; /** OOXML wrapText direction for anchored text boxes. */
|
|
@@ -704,7 +716,8 @@ type MeasuredLine = {
|
|
|
704
716
|
floatSkipBefore?: number; /** Decorative hyphen inserted at a dictionary break without changing source text. */
|
|
705
717
|
discretionaryHyphen?: {
|
|
706
718
|
runIndex: number;
|
|
707
|
-
};
|
|
719
|
+
}; /** A cached pagination boundary occurs immediately before this line. */
|
|
720
|
+
renderedPageBreakBefore?: boolean;
|
|
708
721
|
};
|
|
709
722
|
/**
|
|
710
723
|
* Measurement result for a paragraph block.
|
|
@@ -782,8 +795,8 @@ type ColumnBreakMeasure = {
|
|
|
782
795
|
type TextBoxMeasure = {
|
|
783
796
|
kind: "textBox";
|
|
784
797
|
width: number;
|
|
785
|
-
height: number; /** Pre-measured inner
|
|
786
|
-
innerMeasures: ParagraphMeasure[];
|
|
798
|
+
height: number; /** Pre-measured inner content (avoids re-measuring during render) */
|
|
799
|
+
innerMeasures: (ParagraphMeasure | TableMeasure)[];
|
|
787
800
|
};
|
|
788
801
|
/**
|
|
789
802
|
* Union of all measurement types.
|
|
@@ -1112,4 +1125,4 @@ declare function tableColumnsArePinned(table: TableBlock): boolean;
|
|
|
1112
1125
|
/** Return the leading visual offset for a row with omitted grid columns. */
|
|
1113
1126
|
declare const getTableRowLeadingWidth: (row: TableRow, columnWidths: readonly number[]) => number;
|
|
1114
1127
|
//#endregion
|
|
1115
|
-
export { BlockId, BorderStyle, CellBorderSpec, CellBorders, ColumnBreakBlock, ColumnBreakMeasure, ColumnLayout, DEFAULT_TEXTBOX_MARGINS, DEFAULT_TEXTBOX_WIDTH, DocumentPosition, FOOTNOTE_ENTRY_MARGIN_BOTTOM, FOOTNOTE_FALLBACK_LINE_HEIGHT, FOOTNOTE_SEPARATOR_HEIGHT, FieldRun, FloatingTablePosition, FlowBlock, FootnoteContent, Fragment, FragmentBase, HeaderFooterContent, HeaderFooterContentHeights, HeaderFooterLayout, HitTestResult, HyperlinkInfo, ImageBlock, ImageFragment, ImageMeasure, ImageRun, ImageRunPosition, Layout, LayoutOptions, LineBreakRun, ListNumPr, MathRun, Measure, MeasuredLine, Page, PageBreakBlock, PageBreakMeasure, PageHeaderFooterRefs, PageMargins, ParagraphAttrs, ParagraphBlock, ParagraphBorders, ParagraphFragment, ParagraphIndent, ParagraphMeasure, ParagraphSpacing, Run, RunFormatting, SdtGroup, SectionBreakBlock, SectionBreakMeasure, TabAlignment, TabRun, TabStop, TableBlock, TableCell, TableCellMeasure, TableFragment, TableMeasure, TableRow, TableRowMeasure, TextBoxBlock, TextBoxFlowAttrs, TextBoxFragment, TextBoxMeasure, TextRun, floatingTextBoxReservesBand, floatingTextBoxWrapsText, getTableRowLeadingWidth, isFloatingImageRun, isFloatingTextBoxBlock, isTextWrappingFloatingImageRun, tableColumnsArePinned, types_d_exports };
|
|
1128
|
+
export { BlockId, BorderStyle, CellBorderSpec, CellBorders, ColumnBreakBlock, ColumnBreakMeasure, ColumnLayout, DEFAULT_TEXTBOX_MARGINS, DEFAULT_TEXTBOX_WIDTH, DocumentPosition, FOOTNOTE_ENTRY_MARGIN_BOTTOM, FOOTNOTE_FALLBACK_LINE_HEIGHT, FOOTNOTE_SEPARATOR_HEIGHT, FieldRun, FloatingTablePosition, FlowBlock, FootnoteContent, Fragment, FragmentBase, HeaderFooterContent, HeaderFooterContentHeights, HeaderFooterLayout, HitTestResult, HyperlinkInfo, ImageBlock, ImageFragment, ImageMeasure, ImageRun, ImageRunPosition, Layout, LayoutOptions, LineBreakRun, ListNumPr, MathRun, Measure, MeasuredLine, Page, PageBreakBlock, PageBreakMeasure, PageHeaderFooterRefs, PageMargins, ParagraphAttrs, ParagraphBlock, ParagraphBorders, ParagraphFragment, ParagraphIndent, ParagraphMeasure, ParagraphSpacing, RenderedPageBreakRun, Run, RunFormatting, SdtGroup, SectionBreakBlock, SectionBreakMeasure, TabAlignment, TabRun, TabStop, TableBlock, TableCell, TableCellMeasure, TableFragment, TableMeasure, TableRow, TableRowMeasure, TextBoxBlock, TextBoxFlowAttrs, TextBoxFragment, TextBoxMeasure, TextRun, floatingTextBoxReservesBand, floatingTextBoxWrapsText, getTableRowLeadingWidth, isFloatingImageRun, isFloatingTextBoxBlock, isTextWrappingFloatingImageRun, tableColumnsArePinned, types_d_exports };
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { renderTextBoxFragment } from "../../renderTextBox.js";
|
|
2
|
+
import { renderNestedTable } from "../../renderTable.js";
|
|
2
3
|
//#region src/layout-painter/registry/modules/textBox.ts
|
|
3
4
|
/**
|
|
4
5
|
* Text-box feature module — renders `TextBoxFragment`.
|
|
@@ -6,7 +7,10 @@ import { renderTextBoxFragment } from "../../renderTextBox.js";
|
|
|
6
7
|
const textBoxModule = {
|
|
7
8
|
kind: "textBox",
|
|
8
9
|
render({ fragment, block, measure, context, doc }) {
|
|
9
|
-
return renderTextBoxFragment(fragment, block, measure, context, {
|
|
10
|
+
return renderTextBoxFragment(fragment, block, measure, context, {
|
|
11
|
+
document: doc,
|
|
12
|
+
renderTable: renderNestedTable
|
|
13
|
+
});
|
|
10
14
|
}
|
|
11
15
|
};
|
|
12
16
|
//#endregion
|
|
@@ -71,7 +71,8 @@ declare const getDefaultPageFontFamily: () => string;
|
|
|
71
71
|
* A single footnote item ready for rendering at page bottom.
|
|
72
72
|
*/
|
|
73
73
|
type FootnoteRenderItem = {
|
|
74
|
-
/** Display number (e.g. "1", "2") */
|
|
74
|
+
/** Stable story id used by editable-note surfaces. */noteId?: number; /** Display number (e.g. "1", "2") */
|
|
75
|
+
displayNumber: string; /** Plain text content */
|
|
75
76
|
text?: string; /** Pre-measured structured footnote content. */
|
|
76
77
|
content?: Pick<FootnoteContent, "blocks" | "measures" | "height">;
|
|
77
78
|
};
|
|
@@ -87,7 +88,9 @@ type RenderPageOptions = {
|
|
|
87
88
|
pageClassName?: string; /** Show page borders (for debugging) */
|
|
88
89
|
showBorders?: boolean; /** Background color for pages */
|
|
89
90
|
backgroundColor?: string; /** Drop shadow on pages */
|
|
90
|
-
showShadow?: boolean; /**
|
|
91
|
+
showShadow?: boolean; /** Show the effective body-content boundary for each page. */
|
|
92
|
+
showMarginGuides?: boolean; /** CSS color used for margin guides. */
|
|
93
|
+
marginGuideColor?: string; /** Header content to render (used for all pages, or pages 2+ when titlePg is set). */
|
|
91
94
|
headerContent?: HeaderFooterContent; /** Footer content to render (used for all pages, or pages 2+ when titlePg is set). */
|
|
92
95
|
footerContent?: HeaderFooterContent; /** Header content by part relationship id, used for section-scoped rendering. */
|
|
93
96
|
headerContentByRId?: ReadonlyMap<string, HeaderFooterContent>; /** Footer content by part relationship id, used for section-scoped rendering. */
|
|
@@ -10,7 +10,7 @@ import { renderFragment } from "./renderFragment.js";
|
|
|
10
10
|
import { applyImageVisualAttrs, hasImageVisualAttrs, renderImageFragment } from "./renderImage.js";
|
|
11
11
|
import { renderParagraphFragment } from "./renderParagraph.js";
|
|
12
12
|
import { renderTextBoxFragment } from "./renderTextBox.js";
|
|
13
|
-
import { renderTableFragment } from "./renderTable.js";
|
|
13
|
+
import { renderNestedTable, renderTableFragment } from "./renderTable.js";
|
|
14
14
|
import { renderWatermarkLayer } from "./renderWatermark.js";
|
|
15
15
|
import { panic } from "better-result";
|
|
16
16
|
//#region src/layout-painter/renderPage.ts
|
|
@@ -35,6 +35,7 @@ const PAGE_CLASS_NAMES = {
|
|
|
35
35
|
footer: "layout-page-footer"
|
|
36
36
|
};
|
|
37
37
|
const HEADER_FOOTER_RULE_CLIP_MARGIN = pointsToPixels(1.5);
|
|
38
|
+
const DEFAULT_MARGIN_GUIDE_COLOR = "#c0c0c0";
|
|
38
39
|
const getDefaultPageFontFamily = () => resolveFontFamily("Calibri").cssFallback;
|
|
39
40
|
/**
|
|
40
41
|
* Apply page styles to an element
|
|
@@ -127,6 +128,26 @@ function renderPageBorderOverlay(page, options, doc) {
|
|
|
127
128
|
applyPageBorderSide(overlay, pb.right, "Right", options.theme);
|
|
128
129
|
return overlay;
|
|
129
130
|
}
|
|
131
|
+
function renderPageMarginGuideOverlay(page, options, doc) {
|
|
132
|
+
if (options.showMarginGuides !== true) return null;
|
|
133
|
+
const overlay = doc.createElement("div");
|
|
134
|
+
overlay.className = "layout-page-margin-guide";
|
|
135
|
+
overlay.style.position = "absolute";
|
|
136
|
+
overlay.style.top = `${page.margins.top}px`;
|
|
137
|
+
overlay.style.right = `${page.margins.right}px`;
|
|
138
|
+
overlay.style.bottom = `${page.margins.bottom}px`;
|
|
139
|
+
overlay.style.left = `${page.margins.left}px`;
|
|
140
|
+
overlay.style.boxSizing = "border-box";
|
|
141
|
+
overlay.style.border = `1px dashed ${options.marginGuideColor ?? DEFAULT_MARGIN_GUIDE_COLOR}`;
|
|
142
|
+
overlay.style.pointerEvents = "none";
|
|
143
|
+
overlay.style.zIndex = "19";
|
|
144
|
+
return overlay;
|
|
145
|
+
}
|
|
146
|
+
function syncPageMarginGuideOverlay(pageEl, page, options, doc) {
|
|
147
|
+
for (const stale of Array.from(pageEl.querySelectorAll(":scope > .layout-page-margin-guide"))) stale.remove();
|
|
148
|
+
const overlay = renderPageMarginGuideOverlay(page, options, doc);
|
|
149
|
+
if (overlay) pageEl.append(overlay);
|
|
150
|
+
}
|
|
130
151
|
/**
|
|
131
152
|
* Refresh the page-border overlay on an existing page shell so incremental
|
|
132
153
|
* rerenders pick up changes to size, margins, page number, or pageBorders
|
|
@@ -470,7 +491,10 @@ function renderHeaderFooterContent(content, context, options, layout) {
|
|
|
470
491
|
height: measure.height,
|
|
471
492
|
...block.pmStart !== void 0 ? { pmStart: block.pmStart } : {},
|
|
472
493
|
...block.pmEnd !== void 0 ? { pmEnd: block.pmEnd } : {}
|
|
473
|
-
}, block, measure, hfContext, {
|
|
494
|
+
}, block, measure, hfContext, {
|
|
495
|
+
document: doc,
|
|
496
|
+
renderTable: renderNestedTable
|
|
497
|
+
});
|
|
474
498
|
const textBoxTop = block.position ? resolveHeaderFooterFloatTop({
|
|
475
499
|
height: measure.height,
|
|
476
500
|
paragraphY: cursorY,
|
|
@@ -546,6 +570,10 @@ function renderFootnoteArea(footnotes, contentWidth, doc, context) {
|
|
|
546
570
|
container.append(separator);
|
|
547
571
|
for (const fn of footnotes) {
|
|
548
572
|
const fnEl = doc.createElement("div");
|
|
573
|
+
if (fn.noteId !== void 0) {
|
|
574
|
+
fnEl.dataset["noteKind"] = "footnote";
|
|
575
|
+
fnEl.dataset["noteId"] = String(fn.noteId);
|
|
576
|
+
}
|
|
549
577
|
fnEl.style.marginBottom = `0px`;
|
|
550
578
|
fnEl.style.color = "var(--doc-canvas-text, #000)";
|
|
551
579
|
if (fn.content) {
|
|
@@ -642,7 +670,10 @@ function renderFootnoteBlock(block, measure, contentWidth, y, context, doc) {
|
|
|
642
670
|
y,
|
|
643
671
|
width: measure.width,
|
|
644
672
|
height: measure.height
|
|
645
|
-
}, renderBlock, measure, context, {
|
|
673
|
+
}, renderBlock, measure, context, {
|
|
674
|
+
document: doc,
|
|
675
|
+
renderTable: renderNestedTable
|
|
676
|
+
});
|
|
646
677
|
positionFootnoteBlock(element, y, measure.width, measure.height);
|
|
647
678
|
return element;
|
|
648
679
|
}
|
|
@@ -651,13 +682,7 @@ function renderFootnoteBlock(block, measure, contentWidth, y, context, doc) {
|
|
|
651
682
|
function stripFootnotePmAnchors(block) {
|
|
652
683
|
switch (block.kind) {
|
|
653
684
|
case "paragraph": return stripFootnoteParagraphPmAnchors(block);
|
|
654
|
-
case "table":
|
|
655
|
-
const { pmStart: _pmStart, pmEnd: _pmEnd, ...table } = block;
|
|
656
|
-
return {
|
|
657
|
-
...table,
|
|
658
|
-
rows: table.rows.map(stripFootnoteTableRowPmAnchors)
|
|
659
|
-
};
|
|
660
|
-
}
|
|
685
|
+
case "table": return stripFootnoteTablePmAnchors(block);
|
|
661
686
|
case "image": {
|
|
662
687
|
const { pmStart: _pmStart, pmEnd: _pmEnd, ...image } = block;
|
|
663
688
|
return image;
|
|
@@ -666,7 +691,7 @@ function stripFootnotePmAnchors(block) {
|
|
|
666
691
|
const { pmStart: _pmStart, pmEnd: _pmEnd, ...textBox } = block;
|
|
667
692
|
return {
|
|
668
693
|
...textBox,
|
|
669
|
-
content: textBox.content.map(stripFootnoteParagraphPmAnchors)
|
|
694
|
+
content: textBox.content.map((contentBlock) => contentBlock.kind === "table" ? stripFootnoteTablePmAnchors(contentBlock) : stripFootnoteParagraphPmAnchors(contentBlock))
|
|
670
695
|
};
|
|
671
696
|
}
|
|
672
697
|
case "pageBreak":
|
|
@@ -678,6 +703,13 @@ function stripFootnotePmAnchors(block) {
|
|
|
678
703
|
default: return block;
|
|
679
704
|
}
|
|
680
705
|
}
|
|
706
|
+
function stripFootnoteTablePmAnchors(block) {
|
|
707
|
+
const { pmStart: _pmStart, pmEnd: _pmEnd, ...table } = block;
|
|
708
|
+
return {
|
|
709
|
+
...table,
|
|
710
|
+
rows: table.rows.map(stripFootnoteTableRowPmAnchors)
|
|
711
|
+
};
|
|
712
|
+
}
|
|
681
713
|
function stripFootnoteTableRowPmAnchors(row) {
|
|
682
714
|
return {
|
|
683
715
|
...row,
|
|
@@ -931,7 +963,10 @@ function renderPage(page, context, options = {}) {
|
|
|
931
963
|
fragmentEl = renderImageFragment(fragment, blockData.block, blockData.measure, fragmentContext, { document: doc });
|
|
932
964
|
prevParagraphBorders = void 0;
|
|
933
965
|
} else if (fragment.kind === "textBox" && blockData?.block.kind === "textBox" && blockData.measure.kind === "textBox") {
|
|
934
|
-
fragmentEl = renderTextBoxFragment(fragment, blockData.block, blockData.measure, fragmentContext, {
|
|
966
|
+
fragmentEl = renderTextBoxFragment(fragment, blockData.block, blockData.measure, fragmentContext, {
|
|
967
|
+
document: doc,
|
|
968
|
+
renderTable: renderNestedTable
|
|
969
|
+
});
|
|
935
970
|
prevParagraphBorders = void 0;
|
|
936
971
|
} else {
|
|
937
972
|
fragmentEl = renderFragment(fragment, fragmentContext, { document: doc });
|
|
@@ -1074,6 +1109,8 @@ function renderPage(page, context, options = {}) {
|
|
|
1074
1109
|
moveBehindHeaderFooterElementsToPage(footerEl, pageEl, footerNaturalTop, page.margins.left);
|
|
1075
1110
|
}
|
|
1076
1111
|
if (pageBorderEl && options.pageBorders?.zOrder !== "back") pageEl.append(pageBorderEl);
|
|
1112
|
+
const marginGuideEl = renderPageMarginGuideOverlay(page, options, doc);
|
|
1113
|
+
if (marginGuideEl) pageEl.append(marginGuideEl);
|
|
1077
1114
|
return pageEl;
|
|
1078
1115
|
}
|
|
1079
1116
|
function moveBehindHeaderFooterElementsToPage(slotEl, pageEl, contentTop, contentLeft) {
|
|
@@ -1265,6 +1302,7 @@ function collectContentFingerprint(block, parts) {
|
|
|
1265
1302
|
}
|
|
1266
1303
|
function runContentKey(run) {
|
|
1267
1304
|
if (run.kind === "lineBreak") return "lb";
|
|
1305
|
+
if (run.kind === "renderedPageBreak") return "rpb";
|
|
1268
1306
|
if (run.kind === "image") return `img:${run.src}|${run.width}x${run.height}${run.transform ? `|tr:${run.transform}` : ""}`;
|
|
1269
1307
|
const parts = [run.kind];
|
|
1270
1308
|
if (run.kind === "text") {
|
|
@@ -1324,6 +1362,7 @@ function computeOptionsHash(options) {
|
|
|
1324
1362
|
if (options.titlePg) parts.push("titlePg");
|
|
1325
1363
|
if (options.theme) parts.push(`thm:${options.theme.name ?? "default"}`);
|
|
1326
1364
|
if (options.pageBorders) parts.push(`pb:${JSON.stringify(options.pageBorders)}`);
|
|
1365
|
+
if (options.showMarginGuides === true) parts.push(`mg:${options.marginGuideColor ?? DEFAULT_MARGIN_GUIDE_COLOR}`);
|
|
1327
1366
|
if (options.headerDistance !== void 0) parts.push(`hd:${options.headerDistance}`);
|
|
1328
1367
|
if (options.footerDistance !== void 0) parts.push(`fd:${options.footerDistance}`);
|
|
1329
1368
|
if (options.watermark) parts.push(`wm:${JSON.stringify(options.watermark)}`);
|
|
@@ -1412,6 +1451,7 @@ function renderPages(pages, container, options = {}) {
|
|
|
1412
1451
|
prev.renderFingerprint = null;
|
|
1413
1452
|
applyPageStyles(prev.element, page.size.w, page.size.h, options);
|
|
1414
1453
|
syncPageBorderOverlay(prev.element, page, options, options.document ?? document);
|
|
1454
|
+
syncPageMarginGuideOverlay(prev.element, page, options, options.document ?? document);
|
|
1415
1455
|
prev.element.dataset["pageNumber"] = String(page.number);
|
|
1416
1456
|
continue;
|
|
1417
1457
|
}
|
|
@@ -1424,6 +1464,7 @@ function renderPages(pages, container, options = {}) {
|
|
|
1424
1464
|
populatePageShell(shell, prevDataMap, totalPages, options);
|
|
1425
1465
|
applyPageStyles(shell, page.size.w, page.size.h, options);
|
|
1426
1466
|
syncPageBorderOverlay(shell, page, options, options.document ?? document);
|
|
1467
|
+
syncPageMarginGuideOverlay(shell, page, options, options.document ?? document);
|
|
1427
1468
|
shell.dataset["pageNumber"] = String(page.number);
|
|
1428
1469
|
continue;
|
|
1429
1470
|
}
|
|
@@ -1433,6 +1474,7 @@ function renderPages(pages, container, options = {}) {
|
|
|
1433
1474
|
prev.renderFingerprint = newRenderFp;
|
|
1434
1475
|
applyPageStyles(shell, page.size.w, page.size.h, options);
|
|
1435
1476
|
syncPageBorderOverlay(shell, page, options, options.document ?? document);
|
|
1477
|
+
syncPageMarginGuideOverlay(shell, page, options, options.document ?? document);
|
|
1436
1478
|
shell.dataset["pageNumber"] = String(page.number);
|
|
1437
1479
|
}
|
|
1438
1480
|
if (pages.length > prevShells.length) {
|
|
@@ -1445,6 +1487,7 @@ function renderPages(pages, container, options = {}) {
|
|
|
1445
1487
|
pageEl.dataset["pageIndex"] = String(i);
|
|
1446
1488
|
applyPageStyles(pageEl, page.size.w, page.size.h, options);
|
|
1447
1489
|
syncPageBorderOverlay(pageEl, page, options, doc);
|
|
1490
|
+
syncPageMarginGuideOverlay(pageEl, page, options, doc);
|
|
1448
1491
|
container.append(pageEl);
|
|
1449
1492
|
prevShells.push({
|
|
1450
1493
|
element: pageEl,
|
|
@@ -1505,6 +1548,7 @@ function renderPages(pages, container, options = {}) {
|
|
|
1505
1548
|
pageEl.dataset["pageIndex"] = String(i);
|
|
1506
1549
|
applyPageStyles(pageEl, page.size.w, page.size.h, options);
|
|
1507
1550
|
syncPageBorderOverlay(pageEl, page, options, doc);
|
|
1551
|
+
syncPageMarginGuideOverlay(pageEl, page, options, doc);
|
|
1508
1552
|
}
|
|
1509
1553
|
container.insertBefore(pageEl, overlayBefore);
|
|
1510
1554
|
pageShells.push(pageEl);
|
|
@@ -2,8 +2,8 @@ import { parseXmlDocument } from "../docx/xmlParser.js";
|
|
|
2
2
|
import { resolveFontFamily } from "../utils/fontResolver.js";
|
|
3
3
|
import { detectBaseDirection } from "../utils/baseDirection.js";
|
|
4
4
|
import { AUTHOR_COLORS, getAuthorColorIdx } from "../utils/authorColors.js";
|
|
5
|
+
import { FONT_KERNING_MODE, countCompressibleSpaces, getFontKerningMode, getRunFontKerningMode, toPaintedText } from "../layout-engine/measure/textMeasurementPolicy.js";
|
|
5
6
|
import "../utils/fontWeights.js";
|
|
6
|
-
import { FONT_KERNING_MODE, countCompressibleSpaces, getFontKerningMode, getRunFontKerningMode } from "../layout-engine/measure/textMeasurementPolicy.js";
|
|
7
7
|
import "../layout-engine/measure/measureHelpers.js";
|
|
8
8
|
import { calculateTabWidth } from "../prosemirror/utils/tabCalculator.js";
|
|
9
9
|
import { inlineImageBoundingBox, parseRotationDegrees, rotatedBoundingBox } from "../utils/rotationBoundingBox.js";
|
|
@@ -113,7 +113,7 @@ function getRaisedRunFontSize(run) {
|
|
|
113
113
|
function applyRunStyles(element, run) {
|
|
114
114
|
if (run.fontFamily) element.style.fontFamily = resolveFontFamily(run.fontFamily).cssFallback;
|
|
115
115
|
if (run.fontSize) element.style.fontSize = `${fontSizePtToPx(run.fontSize)}px`;
|
|
116
|
-
if (run.bold) element.style.fontWeight = "
|
|
116
|
+
if (run.bold) element.style.fontWeight = "700";
|
|
117
117
|
if (run.italic) element.style.fontStyle = "italic";
|
|
118
118
|
let hasExplicitTextColor = false;
|
|
119
119
|
const textColor = getRenderableTextColor(run);
|
|
@@ -246,12 +246,20 @@ function applyPmPositions(element, pmStart, pmEnd) {
|
|
|
246
246
|
function renderTextRun(run, doc) {
|
|
247
247
|
const span = doc.createElement("span");
|
|
248
248
|
span.className = `${PARAGRAPH_CLASS_NAMES.run} ${PARAGRAPH_CLASS_NAMES.text}`;
|
|
249
|
+
if (run.footnoteRefId !== void 0) {
|
|
250
|
+
span.dataset["noteKind"] = "footnote";
|
|
251
|
+
span.dataset["noteId"] = String(run.footnoteRefId);
|
|
252
|
+
} else if (run.endnoteRefId !== void 0) {
|
|
253
|
+
span.dataset["noteKind"] = "endnote";
|
|
254
|
+
span.dataset["noteId"] = String(run.endnoteRefId);
|
|
255
|
+
}
|
|
249
256
|
if (run.templatePreview) {
|
|
250
257
|
span.classList.add("folio-template-preview-run");
|
|
251
258
|
if (run.templatePreview === "highlighted") span.classList.add("folio-template-preview-run--highlighted");
|
|
252
259
|
}
|
|
253
260
|
applyRunStyles(span, run);
|
|
254
261
|
applyPmPositions(span, run.pmStart, run.pmEnd);
|
|
262
|
+
const paintedText = toPaintedText(run.text);
|
|
255
263
|
if (run.hyperlink) {
|
|
256
264
|
const anchor = doc.createElement("a");
|
|
257
265
|
anchor.href = run.hyperlink.href;
|
|
@@ -260,7 +268,7 @@ function renderTextRun(run, doc) {
|
|
|
260
268
|
anchor.rel = "noopener noreferrer";
|
|
261
269
|
}
|
|
262
270
|
if (run.hyperlink.tooltip) anchor.title = run.hyperlink.tooltip;
|
|
263
|
-
anchor.textContent =
|
|
271
|
+
anchor.textContent = paintedText;
|
|
264
272
|
if (!run.hyperlink.noDefaultStyle) {
|
|
265
273
|
const hyperlinkColor = getHyperlinkTextColor(run, span.style.color);
|
|
266
274
|
anchor.style.color = hyperlinkColor;
|
|
@@ -270,7 +278,7 @@ function renderTextRun(run, doc) {
|
|
|
270
278
|
span.style.setProperty("--doc-run-color", hyperlinkColor);
|
|
271
279
|
}
|
|
272
280
|
span.append(anchor);
|
|
273
|
-
} else span.textContent =
|
|
281
|
+
} else span.textContent = paintedText;
|
|
274
282
|
applyWhitespaceUnderline(span, run);
|
|
275
283
|
return span;
|
|
276
284
|
}
|
|
@@ -642,6 +650,14 @@ function renderRun(run, doc, context) {
|
|
|
642
650
|
if (isLineBreakRun(run)) return renderLineBreakRun(run, doc);
|
|
643
651
|
if (isFieldRun(run) && context) return renderFieldRun(run, doc, context);
|
|
644
652
|
if (isMathRun(run)) return renderMathRun(run, doc);
|
|
653
|
+
if (run.kind === "renderedPageBreak") {
|
|
654
|
+
const span = doc.createElement("span");
|
|
655
|
+
span.className = PARAGRAPH_CLASS_NAMES.run;
|
|
656
|
+
span.style.display = "none";
|
|
657
|
+
span.dataset["docxRenderedPageBreak"] = "true";
|
|
658
|
+
applyPmPositions(span, run.pmStart, run.pmEnd);
|
|
659
|
+
return span;
|
|
660
|
+
}
|
|
645
661
|
const span = doc.createElement("span");
|
|
646
662
|
span.className = PARAGRAPH_CLASS_NAMES.run;
|
|
647
663
|
return span;
|
|
@@ -913,7 +929,7 @@ function createTextMeasurer(doc) {
|
|
|
913
929
|
const fontPrefixParts = [];
|
|
914
930
|
if (style.italic) fontPrefixParts.push("italic");
|
|
915
931
|
if (style.smallCaps) fontPrefixParts.push("small-caps");
|
|
916
|
-
if (style.bold) fontPrefixParts.push("
|
|
932
|
+
if (style.bold) fontPrefixParts.push("700");
|
|
917
933
|
fontPrefixParts.push(`${fontSizePx}px`);
|
|
918
934
|
const fontPrefix = fontPrefixParts.join(" ");
|
|
919
935
|
if (style.eastAsiaFontFamily && !style.letterSpacing && hasCjk(text)) {
|
|
@@ -1391,7 +1407,7 @@ function renderListMarker(marker, inlineWidth, visualOffset, doc, fontFamily, fo
|
|
|
1391
1407
|
span.style.display = "inline-block";
|
|
1392
1408
|
if (fontFamily) span.style.fontFamily = resolveFontFamily(fontFamily).cssFallback;
|
|
1393
1409
|
if (fontSize) span.style.fontSize = `${fontSize * 96 / 72}px`;
|
|
1394
|
-
if (bold !== void 0) span.style.fontWeight = bold ? "
|
|
1410
|
+
if (bold !== void 0) span.style.fontWeight = bold ? "700" : "normal";
|
|
1395
1411
|
span.style.textAlign = "left";
|
|
1396
1412
|
span.style.textAlignLast = "left";
|
|
1397
1413
|
span.style.boxSizing = "border-box";
|
|
@@ -23,6 +23,10 @@ type RenderTableFragmentOptions = {
|
|
|
23
23
|
document?: Document;
|
|
24
24
|
pageGeometry?: PageGeometry;
|
|
25
25
|
};
|
|
26
|
+
/**
|
|
27
|
+
* Render a nested table (within a cell)
|
|
28
|
+
*/
|
|
29
|
+
declare function renderNestedTable(block: TableBlock, measure: TableMeasure, context: RenderContext, doc: Document): HTMLElement;
|
|
26
30
|
/**
|
|
27
31
|
* Render a table fragment to DOM
|
|
28
32
|
*
|
|
@@ -35,4 +39,4 @@ type RenderTableFragmentOptions = {
|
|
|
35
39
|
*/
|
|
36
40
|
declare function renderTableFragment(fragment: TableFragment, block: TableBlock, measure: TableMeasure, context: RenderContext, options?: RenderTableFragmentOptions): HTMLElement;
|
|
37
41
|
//#endregion
|
|
38
|
-
export { RenderTableFragmentOptions, TABLE_CLASS_NAMES, renderTableFragment };
|
|
42
|
+
export { RenderTableFragmentOptions, TABLE_CLASS_NAMES, renderNestedTable, renderTableFragment };
|
|
@@ -156,7 +156,10 @@ function renderCellContent({ cell, cellMeasure, context, doc, contentWidthOverri
|
|
|
156
156
|
}, textBoxBlock, textBoxMeasure, {
|
|
157
157
|
...context,
|
|
158
158
|
insideTableCell: true
|
|
159
|
-
}, {
|
|
159
|
+
}, {
|
|
160
|
+
document: doc,
|
|
161
|
+
renderTable: renderNestedTable
|
|
162
|
+
});
|
|
160
163
|
if (isFloatingTextBoxBlock(textBoxBlock)) {
|
|
161
164
|
floatingTextBoxesLayer ??= createCellFloatingTextBoxesLayer(doc);
|
|
162
165
|
textBoxEl.style.left = `${resolveCellTextBoxX(textBoxBlock, contentWidth)}px`;
|
|
@@ -737,4 +740,4 @@ function renderTableFragment(fragment, block, measure, context, options = {}) {
|
|
|
737
740
|
return tableEl;
|
|
738
741
|
}
|
|
739
742
|
//#endregion
|
|
740
|
-
export { TABLE_CLASS_NAMES, renderTableFragment };
|
|
743
|
+
export { TABLE_CLASS_NAMES, renderNestedTable, renderTableFragment };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { TextBoxBlock, TextBoxFragment, TextBoxMeasure } from "../layout-engine/types.js";
|
|
1
|
+
import { TableBlock, TableMeasure, TextBoxBlock, TextBoxFragment, TextBoxMeasure } from "../layout-engine/types.js";
|
|
2
2
|
import { RenderContext } from "./renderUtils.js";
|
|
3
3
|
|
|
4
4
|
//#region src/layout-painter/renderTextBox.d.ts
|
|
@@ -13,6 +13,7 @@ declare const TEXTBOX_CLASS_NAMES: {
|
|
|
13
13
|
*/
|
|
14
14
|
type RenderTextBoxFragmentOptions = {
|
|
15
15
|
document?: Document;
|
|
16
|
+
renderTable?: (block: TableBlock, measure: TableMeasure, context: RenderContext, document: Document) => HTMLElement;
|
|
16
17
|
};
|
|
17
18
|
/**
|
|
18
19
|
* Render a text box fragment to DOM
|