@stll/folio-core 0.7.0 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +39 -0
- package/dist/ai-edits/apply.js +18 -4
- package/dist/ai-edits/blockRange.d.ts +13 -2
- package/dist/ai-edits/blockRange.js +23 -2
- package/dist/ai-edits/headless.d.ts +31 -4
- package/dist/ai-edits/headless.js +148 -35
- package/dist/ai-edits/index.d.ts +6 -4
- package/dist/ai-edits/index.js +4 -1
- package/dist/ai-edits/scoped-reading.d.ts +9 -0
- package/dist/ai-edits/scoped-reading.js +60 -0
- package/dist/ai-edits/snapshot.js +53 -7
- package/dist/ai-edits/types.d.ts +46 -3
- package/dist/compat/eigenpal.d.ts +8 -3
- package/dist/compat/eigenpal.js +7 -2
- package/dist/controller/fontReadiness.d.ts +29 -0
- package/dist/controller/fontReadiness.js +139 -0
- package/dist/controller/latestRequestGate.d.ts +13 -0
- package/dist/controller/latestRequestGate.js +16 -0
- package/dist/controller/layoutPipeline.js +88 -10
- package/dist/document-operations.d.ts +10 -3
- package/dist/document-operations.js +29 -14
- package/dist/docx/blockContentParser.js +53 -10
- package/dist/docx/commentParser.js +1 -1
- package/dist/docx/compatibility.d.ts +30 -2
- package/dist/docx/compatibility.js +118 -39
- package/dist/docx/conformance.d.ts +6 -0
- package/dist/docx/conformance.js +18 -0
- package/dist/docx/encryption/agileDecryption.js +1 -1
- package/dist/docx/encryption/encryptionInfo.js +1 -1
- package/dist/docx/encryption/openEncryptedDocx.js +1 -1
- package/dist/docx/ensureParaIds.d.ts +29 -0
- package/dist/docx/ensureParaIds.js +423 -0
- package/dist/docx/fontTableParser.d.ts +6 -0
- package/dist/docx/fontTableParser.js +72 -0
- package/dist/docx/groupDrawingParser.js +7 -2
- package/dist/docx/headerFooterParser.js +1 -1
- package/dist/docx/normalizeBaseDirection.js +1 -1
- package/dist/docx/numberingParser.js +2 -0
- package/dist/docx/paragraphParser.js +21 -1
- package/dist/docx/paragraphTextBoxEnrichment.js +141 -2
- package/dist/docx/parser.d.ts +1 -1
- package/dist/docx/parser.js +14 -9
- package/dist/docx/rezip.js +70 -5
- package/dist/docx/runConsolidator.js +4 -0
- package/dist/docx/runParser.js +19 -1
- package/dist/docx/selectiveSave.js +3 -3
- package/dist/docx/selectiveXmlPatch.d.ts +10 -1
- package/dist/docx/selectiveXmlPatch.js +1 -1
- package/dist/docx/serializer/fontTableSerializer.d.ts +6 -0
- package/dist/docx/serializer/fontTableSerializer.js +26 -0
- package/dist/docx/serializer/headerFooterSerializer.js +1 -1
- package/dist/docx/serializer/paragraphSerializer.js +8 -0
- package/dist/docx/serializer/runSerializer.js +14 -2
- package/dist/docx/serializer/settingsSerializer.d.ts +6 -0
- package/dist/docx/serializer/settingsSerializer.js +16 -0
- package/dist/docx/serializer/stylesSerializer.d.ts +6 -0
- package/dist/docx/serializer/stylesSerializer.js +56 -0
- package/dist/docx/serializer/tableSerializer.js +29 -26
- package/dist/docx/serializer/themeSerializer.d.ts +6 -0
- package/dist/docx/serializer/themeSerializer.js +36 -0
- package/dist/docx/settingsParser.js +46 -0
- package/dist/docx/styleParser.js +26 -1
- package/dist/docx/tableParser.js +19 -1
- package/dist/docx/textBoxParser.js +6 -1
- package/dist/docx/vmlImageParser.js +146 -1
- package/dist/i18n/messages/catalogs.gen.d.ts +5916 -5916
- package/dist/i18n/messages/catalogs.gen.js +5910 -5910
- package/dist/index.d.ts +8 -3
- package/dist/index.js +7 -2
- package/dist/layout-bridge/convert/footnoteLayout.d.ts +3 -0
- package/dist/layout-bridge/convert/footnoteLayout.js +3 -1
- package/dist/layout-bridge/convert/headerFooterLayout.d.ts +3 -0
- package/dist/layout-bridge/convert/headerFooterLayout.js +22 -3
- package/dist/layout-bridge/convert/paragraphFrames.d.ts +7 -0
- package/dist/layout-bridge/convert/paragraphFrames.js +136 -0
- package/dist/layout-bridge/convert/toFlowBlocks.d.ts +14 -2
- package/dist/layout-bridge/convert/toFlowBlocks.js +93 -17
- package/dist/layout-bridge/engine/hitTest.js +2 -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 +2 -1
- package/dist/layout-engine/headerFooterRefs.d.ts +7 -0
- package/dist/layout-engine/headerFooterRefs.js +40 -0
- package/dist/layout-engine/index.d.ts +3 -2
- package/dist/layout-engine/index.js +68 -106
- package/dist/layout-engine/measure/cache.js +11 -3
- package/dist/layout-engine/measure/font-metrics.worker.js +1 -0
- package/dist/layout-engine/measure/index.d.ts +2 -1
- package/dist/layout-engine/measure/index.js +2 -1
- package/dist/layout-engine/measure/lineBreakProvider.d.ts +33 -0
- package/dist/layout-engine/measure/lineBreakProvider.js +252 -0
- package/dist/layout-engine/measure/lineBreaks.d.ts +7 -2
- package/dist/layout-engine/measure/lineBreaks.js +11 -18
- package/dist/layout-engine/measure/listMarkerWidth.d.ts +4 -1
- package/dist/layout-engine/measure/listMarkerWidth.js +33 -8
- package/dist/layout-engine/measure/measureBlocks.d.ts +4 -2
- package/dist/layout-engine/measure/measureBlocks.js +138 -86
- package/dist/layout-engine/measure/measureContainer.js +32 -9
- package/dist/layout-engine/measure/measureHelpers.js +5 -2
- package/dist/layout-engine/measure/measureParagraph.js +346 -74
- package/dist/layout-engine/measure/measureTypes.d.ts +2 -1
- package/dist/layout-engine/measure/measureWorker.d.ts +2 -2
- package/dist/layout-engine/measure/measureWorker.js +8 -19
- package/dist/layout-engine/measure/measureWorkerProtocol.d.ts +3 -14
- package/dist/layout-engine/measure/tableCellFloating.d.ts +10 -3
- package/dist/layout-engine/measure/tableCellFloating.js +37 -29
- package/dist/layout-engine/measure/tableCellFlow.d.ts +27 -0
- package/dist/layout-engine/measure/tableCellFlow.js +73 -0
- package/dist/layout-engine/measure/tableCellGrid.d.ts +15 -0
- package/dist/layout-engine/measure/tableCellGrid.js +62 -0
- package/dist/layout-engine/measure/textBoxParagraphLayout.d.ts +18 -0
- package/dist/layout-engine/measure/textBoxParagraphLayout.js +32 -0
- package/dist/layout-engine/measure/textMeasurementPolicy.d.ts +18 -0
- package/dist/layout-engine/measure/textMeasurementPolicy.js +24 -0
- package/dist/layout-engine/paginator.d.ts +2 -1
- package/dist/layout-engine/paginator.js +7 -6
- package/dist/layout-engine/paragraphFrame.d.ts +22 -0
- package/dist/layout-engine/paragraphFrame.js +17 -0
- package/dist/layout-engine/paragraphSequence.d.ts +7 -0
- package/dist/layout-engine/paragraphSequence.js +25 -0
- package/dist/layout-engine/paragraphSpacing.d.ts +17 -0
- package/dist/layout-engine/paragraphSpacing.js +30 -0
- package/dist/layout-engine/renderedBreakReconciliation.d.ts +63 -0
- package/dist/layout-engine/renderedBreakReconciliation.js +86 -0
- package/dist/layout-engine/tableRowBreak.d.ts +5 -2
- package/dist/layout-engine/tableRowBreak.js +62 -29
- package/dist/layout-engine/types.d.ts +54 -12
- package/dist/layout-engine/types.js +15 -2
- package/dist/layout-painter/anchoredImagePosition.d.ts +23 -0
- package/dist/layout-painter/anchoredImagePosition.js +95 -0
- package/dist/layout-painter/borderStroke.d.ts +23 -0
- package/dist/layout-painter/borderStroke.js +39 -0
- package/dist/layout-painter/renderImage.d.ts +5 -4
- package/dist/layout-painter/renderImage.js +18 -4
- package/dist/layout-painter/renderPage.d.ts +2 -27
- package/dist/layout-painter/renderPage.js +15 -101
- package/dist/layout-painter/renderParagraph.js +45 -26
- package/dist/layout-painter/renderTable.d.ts +2 -0
- package/dist/layout-painter/renderTable.js +257 -47
- package/dist/layout-painter/renderTextBox.js +17 -7
- package/dist/managers/DocumentLoaderManager.d.ts +1 -1
- package/dist/managers/DocumentLoaderManager.js +2 -2
- package/dist/paged-layout/sectionBlockWidths.d.ts +3 -0
- package/dist/paged-layout/sectionBlockWidths.js +9 -0
- package/dist/paged-layout/sectionGeometry.js +1 -1
- package/dist/prosemirror/attrs/index.d.ts +4 -2
- package/dist/prosemirror/attrs/index.js +32 -2
- package/dist/prosemirror/commands/propertyChangeScope.js +1 -1
- package/dist/prosemirror/conversion/fromProseDoc.js +35 -18
- package/dist/prosemirror/conversion/toProseDoc.js +73 -37
- package/dist/prosemirror/extensions/StarterKit.js +2 -0
- package/dist/prosemirror/extensions/core/ParagraphExtension.js +8 -2
- package/dist/prosemirror/extensions/features/AutoBidiDetectionExtension.js +1 -1
- package/dist/prosemirror/extensions/features/ListExtension.js +2 -0
- package/dist/prosemirror/extensions/features/ParaIdAllocatorExtension.js +68 -16
- package/dist/prosemirror/extensions/marks/FootnoteRefExtension.js +1 -0
- package/dist/prosemirror/extensions/marks/HighlightExtension.js +1 -1
- package/dist/prosemirror/extensions/marks/LanguageExtension.d.ts +7 -0
- package/dist/prosemirror/extensions/marks/LanguageExtension.js +30 -0
- package/dist/prosemirror/extensions/marks/RunShadingExtension.js +1 -1
- package/dist/prosemirror/extensions/marks/TextColorExtension.js +1 -1
- package/dist/prosemirror/extensions/marks/TrackedChangeExtensions.js +1 -1
- package/dist/prosemirror/extensions/marks/markUtils.js +12 -0
- package/dist/prosemirror/extensions/nodes/ImageExtension.js +2 -1
- package/dist/prosemirror/extensions/nodes/TableExtension.js +4 -2
- package/dist/prosemirror/extensions/nodes/TextBoxExtension.d.ts +2 -1
- package/dist/prosemirror/extensions/nodes/TextBoxExtension.js +7 -0
- package/dist/prosemirror/schema/index.d.ts +2 -2
- package/dist/prosemirror/schema/marks.d.ts +6 -1
- package/dist/prosemirror/schema/nodes.d.ts +16 -12
- package/dist/prosemirror/styles/resolvedStyleAttrs.js +2 -0
- package/dist/prosemirror/utils/tabCalculator.d.ts +3 -1
- package/dist/prosemirror/utils/tabCalculator.js +12 -10
- package/dist/prosemirror/validation.js +4 -1
- package/dist/redline.js +13 -3
- package/dist/server.d.ts +9 -4
- package/dist/server.js +7 -2
- package/dist/style-sets/extract.d.ts +35 -0
- package/dist/style-sets/extract.js +123 -0
- package/dist/style-sets/stellaStyle.d.ts +13 -0
- package/dist/style-sets/stellaStyle.js +516 -0
- package/dist/style-sets/types.d.ts +31 -0
- package/dist/style-sets/types.js +5 -0
- package/dist/types/index.d.ts +2 -1
- package/dist/utils/createDocument.d.ts +12 -2
- package/dist/utils/createDocument.js +55 -36
- package/dist/utils/fontResolver.js +6 -6
- package/dist/utils/formatToStyle.js +1 -1
- package/dist/utils/hexId.d.ts +8 -1
- package/dist/utils/hexId.js +11 -3
- package/dist/utils/paragraphFormattingMerge.d.ts +14 -3
- package/dist/utils/paragraphFormattingMerge.js +14 -4
- package/package.json +5 -1
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { ParagraphBlock } from "./types.js";
|
|
2
|
+
|
|
3
|
+
//#region src/layout-engine/paragraphSpacing.d.ts
|
|
4
|
+
/** Whether a paragraph has no visible run content. */
|
|
5
|
+
declare function isEmptyParagraph(block: ParagraphBlock): boolean;
|
|
6
|
+
/**
|
|
7
|
+
* Resolve leading paragraph spacing after applying the empty-paragraph
|
|
8
|
+
* inherited-spacing collapse rule.
|
|
9
|
+
*/
|
|
10
|
+
declare function getParagraphSpacingBefore(block: ParagraphBlock): number;
|
|
11
|
+
/**
|
|
12
|
+
* Resolve trailing paragraph spacing after applying the empty-paragraph
|
|
13
|
+
* inherited-spacing collapse rule.
|
|
14
|
+
*/
|
|
15
|
+
declare function getParagraphSpacingAfter(block: ParagraphBlock): number;
|
|
16
|
+
//#endregion
|
|
17
|
+
export { getParagraphSpacingAfter, getParagraphSpacingBefore, isEmptyParagraph };
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
//#region src/layout-engine/paragraphSpacing.ts
|
|
2
|
+
/** Whether a paragraph has no visible run content. */
|
|
3
|
+
function isEmptyParagraph(block) {
|
|
4
|
+
if (block.runs.length === 0) return true;
|
|
5
|
+
if (block.runs.length !== 1) return false;
|
|
6
|
+
const run = block.runs.at(0);
|
|
7
|
+
return run?.kind === "text" && run.text === "";
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Resolve leading paragraph spacing after applying the empty-paragraph
|
|
11
|
+
* inherited-spacing collapse rule.
|
|
12
|
+
*/
|
|
13
|
+
function getParagraphSpacingBefore(block) {
|
|
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
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Resolve trailing paragraph spacing after applying the empty-paragraph
|
|
21
|
+
* inherited-spacing collapse rule.
|
|
22
|
+
*/
|
|
23
|
+
function getParagraphSpacingAfter(block) {
|
|
24
|
+
const value = block.attrs?.spacing?.after ?? 0;
|
|
25
|
+
if (value === 0) return 0;
|
|
26
|
+
if (isEmptyParagraph(block) && !block.attrs?.styleId && !block.attrs?.hasDirectParagraphFormatting && !block.attrs?.hasDirectParagraphMarkFormatting && !block.attrs?.spacingExplicit?.after) return 0;
|
|
27
|
+
return value;
|
|
28
|
+
}
|
|
29
|
+
//#endregion
|
|
30
|
+
export { getParagraphSpacingAfter, getParagraphSpacingBefore, isEmptyParagraph };
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { FlowBlock, Page } from "./types.js";
|
|
2
|
+
|
|
3
|
+
//#region src/layout-engine/renderedBreakReconciliation.d.ts
|
|
4
|
+
type RenderedBreakState = {
|
|
5
|
+
type: "noPageAdvance";
|
|
6
|
+
} | {
|
|
7
|
+
type: "pageAdvance";
|
|
8
|
+
reason: "ordinary" | "reflowBoundary";
|
|
9
|
+
} | {
|
|
10
|
+
type: "pageAdvance";
|
|
11
|
+
reason: "authoredBoundary";
|
|
12
|
+
boundary: "hardBreak" | "sectionBreak";
|
|
13
|
+
};
|
|
14
|
+
declare const INITIAL_RENDERED_BREAK_STATE: RenderedBreakState;
|
|
15
|
+
type ReconcileBeforeBlockOptions = {
|
|
16
|
+
state: RenderedBreakState;
|
|
17
|
+
block: FlowBlock;
|
|
18
|
+
previousBlock: FlowBlock | undefined;
|
|
19
|
+
page: Page;
|
|
20
|
+
blocksById: ReadonlyMap<string, FlowBlock>;
|
|
21
|
+
hasExplicitPageBreak: boolean;
|
|
22
|
+
renderedBreakNeedsSnap: boolean;
|
|
23
|
+
};
|
|
24
|
+
type BreakDecision = {
|
|
25
|
+
forcePageBreak: boolean;
|
|
26
|
+
suppressSpaceBefore: boolean;
|
|
27
|
+
state: RenderedBreakState;
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* Reconcile an authored or cached break before laying out one block.
|
|
31
|
+
*
|
|
32
|
+
* Cached markers describe a boundary at a paragraph, not an ordinal page.
|
|
33
|
+
* Natural reflow may therefore satisfy the marker before layout reaches it.
|
|
34
|
+
* Every marker consumes the accumulated advance state so later markers make
|
|
35
|
+
* independent decisions.
|
|
36
|
+
*/
|
|
37
|
+
declare const reconcileBreakBeforeBlock: ({
|
|
38
|
+
state,
|
|
39
|
+
block,
|
|
40
|
+
previousBlock,
|
|
41
|
+
page,
|
|
42
|
+
blocksById,
|
|
43
|
+
hasExplicitPageBreak,
|
|
44
|
+
renderedBreakNeedsSnap
|
|
45
|
+
}: ReconcileBeforeBlockOptions) => BreakDecision;
|
|
46
|
+
declare const recordReflowBoundary: (state: RenderedBreakState, crossedPageBoundary: boolean) => RenderedBreakState;
|
|
47
|
+
type ReconcileAfterBlockOptions = {
|
|
48
|
+
state: RenderedBreakState;
|
|
49
|
+
block: FlowBlock;
|
|
50
|
+
pageNumberBefore: number;
|
|
51
|
+
pageNumberAfter: number;
|
|
52
|
+
previousPage: Page | undefined;
|
|
53
|
+
};
|
|
54
|
+
/** Record page movement and carry authored boundaries across pagination-empty paragraphs. */
|
|
55
|
+
declare const reconcileAfterBlock: ({
|
|
56
|
+
state,
|
|
57
|
+
block,
|
|
58
|
+
pageNumberBefore,
|
|
59
|
+
pageNumberAfter,
|
|
60
|
+
previousPage
|
|
61
|
+
}: ReconcileAfterBlockOptions) => RenderedBreakState;
|
|
62
|
+
//#endregion
|
|
63
|
+
export { INITIAL_RENDERED_BREAK_STATE, RenderedBreakState, reconcileAfterBlock, reconcileBreakBeforeBlock, recordReflowBoundary };
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { continuesNumberedSequence, continuesTabbedParagraphSequence } from "./paragraphSequence.js";
|
|
2
|
+
//#region src/layout-engine/renderedBreakReconciliation.ts
|
|
3
|
+
const INITIAL_RENDERED_BREAK_STATE = { type: "noPageAdvance" };
|
|
4
|
+
/**
|
|
5
|
+
* Reconcile an authored or cached break before laying out one block.
|
|
6
|
+
*
|
|
7
|
+
* Cached markers describe a boundary at a paragraph, not an ordinal page.
|
|
8
|
+
* Natural reflow may therefore satisfy the marker before layout reaches it.
|
|
9
|
+
* Every marker consumes the accumulated advance state so later markers make
|
|
10
|
+
* independent decisions.
|
|
11
|
+
*/
|
|
12
|
+
const reconcileBreakBeforeBlock = ({ state, block, previousBlock, page, blocksById, hasExplicitPageBreak, renderedBreakNeedsSnap }) => {
|
|
13
|
+
if (hasExplicitPageBreak) return {
|
|
14
|
+
forcePageBreak: true,
|
|
15
|
+
suppressSpaceBefore: false,
|
|
16
|
+
state: INITIAL_RENDERED_BREAK_STATE
|
|
17
|
+
};
|
|
18
|
+
if (block.kind !== "paragraph" || block.attrs?.renderedPageBreakBefore !== true) return {
|
|
19
|
+
forcePageBreak: false,
|
|
20
|
+
suppressSpaceBefore: false,
|
|
21
|
+
state
|
|
22
|
+
};
|
|
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);
|
|
25
|
+
const followsAuthoredPageBreak = previousBlock?.kind === "pageBreak";
|
|
26
|
+
const followsSectionBreak = previousBlock?.kind === "sectionBreak";
|
|
27
|
+
const followsPageBreakingSection = followsSectionBreak && previousBlock.type !== "continuous";
|
|
28
|
+
const followsCarriedSectionBoundary = state.type === "pageAdvance" && state.reason === "authoredBoundary" && state.boundary === "sectionBreak";
|
|
29
|
+
const preserveSectionLeadingSpacing = (followsSectionBreak || followsCarriedSectionBoundary) && block.attrs?.spacingExplicit?.before === true;
|
|
30
|
+
return {
|
|
31
|
+
forcePageBreak,
|
|
32
|
+
suppressSpaceBefore: (forcePageBreak || markerAlreadySatisfied || followsAuthoredPageBreak || followsPageBreakingSection) && !preserveSectionLeadingSpacing,
|
|
33
|
+
state: INITIAL_RENDERED_BREAK_STATE
|
|
34
|
+
};
|
|
35
|
+
};
|
|
36
|
+
const recordReflowBoundary = (state, crossedPageBoundary) => crossedPageBoundary ? {
|
|
37
|
+
type: "pageAdvance",
|
|
38
|
+
reason: "reflowBoundary"
|
|
39
|
+
} : state;
|
|
40
|
+
/** Record page movement and carry authored boundaries across pagination-empty paragraphs. */
|
|
41
|
+
const reconcileAfterBlock = ({ state, block, pageNumberBefore, pageNumberAfter, previousPage }) => {
|
|
42
|
+
if (isStructuralBreak(block)) {
|
|
43
|
+
if (pageNumberAfter <= pageNumberBefore) {
|
|
44
|
+
if (state.type === "pageAdvance" && state.reason === "authoredBoundary") return block.kind === "sectionBreak" ? {
|
|
45
|
+
...state,
|
|
46
|
+
boundary: "sectionBreak"
|
|
47
|
+
} : state;
|
|
48
|
+
return INITIAL_RENDERED_BREAK_STATE;
|
|
49
|
+
}
|
|
50
|
+
return {
|
|
51
|
+
type: "pageAdvance",
|
|
52
|
+
reason: "authoredBoundary",
|
|
53
|
+
boundary: block.kind === "sectionBreak" ? "sectionBreak" : "hardBreak"
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
if (block.kind === "paragraph" && isPaginationEmptyParagraph(block)) return state;
|
|
57
|
+
if (pageNumberAfter <= pageNumberBefore) return state.type === "pageAdvance" && state.reason === "authoredBoundary" ? INITIAL_RENDERED_BREAK_STATE : state;
|
|
58
|
+
return {
|
|
59
|
+
type: "pageAdvance",
|
|
60
|
+
reason: previousPage?.fragments.some(({ blockId }) => blockId === block.id) ? "reflowBoundary" : "ordinary"
|
|
61
|
+
};
|
|
62
|
+
};
|
|
63
|
+
const isPaginationEmptyParagraph = (block) => !hasVisibleEmptyParagraphDecoration(block) && block.runs.every((run) => {
|
|
64
|
+
if (run.kind === "text") return run.text.trim().length === 0;
|
|
65
|
+
return run.kind === "tab" || run.kind === "lineBreak";
|
|
66
|
+
});
|
|
67
|
+
const hasVisibleEmptyParagraphDecoration = (block) => {
|
|
68
|
+
if (block.attrs?.listMarker !== void 0 && block.attrs.listMarker.length > 0 && block.attrs.listMarkerHidden !== true) return true;
|
|
69
|
+
if (block.attrs?.shading) return true;
|
|
70
|
+
return Object.values(block.attrs?.borders ?? {}).some((border) => border !== void 0 && border.style !== "none" && border.style !== "nil" && border.width !== 0);
|
|
71
|
+
};
|
|
72
|
+
const pageHasVisibleBodyContent = (page, blocksById) => {
|
|
73
|
+
for (const fragment of page.fragments) {
|
|
74
|
+
if (fragment.kind !== "paragraph") return true;
|
|
75
|
+
const block = blocksById.get(String(fragment.blockId));
|
|
76
|
+
if (block?.kind !== "paragraph" || !isPaginationEmptyParagraph(block)) return true;
|
|
77
|
+
}
|
|
78
|
+
return false;
|
|
79
|
+
};
|
|
80
|
+
const pageStartsWithPreviousParagraphContinuation = (page, previousBlock) => {
|
|
81
|
+
if (previousBlock?.kind !== "paragraph") return false;
|
|
82
|
+
return page.fragments.some((fragment) => fragment.kind === "paragraph" && String(fragment.blockId) === String(previousBlock.id) && fragment.continuesFromPrev === true);
|
|
83
|
+
};
|
|
84
|
+
const isStructuralBreak = (block) => block.kind === "pageBreak" || block.kind === "columnBreak" || block.kind === "sectionBreak";
|
|
85
|
+
//#endregion
|
|
86
|
+
export { INITIAL_RENDERED_BREAK_STATE, reconcileAfterBlock, reconcileBreakBeforeBlock, recordReflowBoundary };
|
|
@@ -9,10 +9,13 @@ type TableRowBreakInfo = {
|
|
|
9
9
|
* at which a break is clean. Always includes the row's full height as the
|
|
10
10
|
* final boundary.
|
|
11
11
|
*/
|
|
12
|
-
breakOffsets: number[][];
|
|
12
|
+
breakOffsets: number[][]; /** Suppressible leading paragraph whitespace after each matching break offset. */
|
|
13
|
+
continuationSkips: number[][];
|
|
13
14
|
};
|
|
14
15
|
/** Build break geometry for a table from its block + measure. */
|
|
15
16
|
declare function buildTableRowBreakInfo(block: TableBlock, measure: TableMeasure): TableRowBreakInfo;
|
|
17
|
+
/** Leading empty-paragraph whitespace to consume when a row resumes after `offset`. */
|
|
18
|
+
declare function getRowContinuationSkip(info: TableRowBreakInfo, rowIndex: number, offset: number): number;
|
|
16
19
|
/**
|
|
17
20
|
* Given a row and how much of it has already been placed (`fromOffset`), return
|
|
18
21
|
* how many more px can be placed ending on a whole line, without exceeding
|
|
@@ -20,4 +23,4 @@ declare function buildTableRowBreakInfo(block: TableBlock, measure: TableMeasure
|
|
|
20
23
|
*/
|
|
21
24
|
declare function snapRowBreak(info: TableRowBreakInfo, rowIndex: number, fromOffset: number, maxSlice: number): number;
|
|
22
25
|
//#endregion
|
|
23
|
-
export { TableRowBreakInfo, buildTableRowBreakInfo, snapRowBreak };
|
|
26
|
+
export { TableRowBreakInfo, buildTableRowBreakInfo, getRowContinuationSkip, snapRowBreak };
|
|
@@ -1,28 +1,24 @@
|
|
|
1
|
-
import { isFloatingTextBoxBlock } from "./types.js";
|
|
2
1
|
import { measureParagraph } from "./measure/measureParagraph.js";
|
|
2
|
+
import { isEmptyParagraph } from "./paragraphSpacing.js";
|
|
3
|
+
import { createTableCellFlowState, placeTableCellBlock } from "./measure/tableCellFlow.js";
|
|
3
4
|
import { buildTableCellFloatingZones, getTableCellContentWidth, getTableCellFloatingImages } from "./measure/tableCellFloating.js";
|
|
4
5
|
//#region src/layout-engine/tableRowBreak.ts
|
|
5
6
|
/**
|
|
6
7
|
* Table row-break geometry. Ported from eigenpal/docx-editor#698 (folio subset).
|
|
7
8
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* was clipped (content lost; upstream #570).
|
|
9
|
+
* Table rows may break across flow regions unless `cantSplit` is set. When a
|
|
10
|
+
* permitted row exceeds the current region, the portion that fits stays and
|
|
11
|
+
* the rest continues in the next region, broken between whole text lines and
|
|
12
|
+
* never through a glyph. This also prevents oversized rows from overflowing
|
|
13
|
+
* one page and clipping content (upstream #570).
|
|
14
14
|
*
|
|
15
15
|
* This computes, per row, the safe break offsets (the y of every line bottom in
|
|
16
16
|
* the row's cells) so the paginator can snap a break to the deepest whole line
|
|
17
17
|
* that still fits. Vertically-merged cells that span into a row are not modeled
|
|
18
18
|
* here yet (a separate follow-up); each row uses its own cells' content.
|
|
19
19
|
*/
|
|
20
|
+
const BREAK_OFFSET_EPSILON = .01;
|
|
20
21
|
const DEFAULT_TABLE_CELL_PADDING_TOP = 1;
|
|
21
|
-
function getAtomicBlockHeight(measure) {
|
|
22
|
-
if ("totalHeight" in measure) return measure.totalHeight;
|
|
23
|
-
if ("height" in measure) return measure.height;
|
|
24
|
-
return 0;
|
|
25
|
-
}
|
|
26
22
|
function isInsideRange(offset, range) {
|
|
27
23
|
return offset > range.top && offset < range.bottom;
|
|
28
24
|
}
|
|
@@ -39,6 +35,10 @@ function shiftCellGeometry(geometry, offset) {
|
|
|
39
35
|
unsafeRanges: geometry.unsafeRanges.map((range) => ({
|
|
40
36
|
top: range.top + offset,
|
|
41
37
|
bottom: range.bottom + offset
|
|
38
|
+
})),
|
|
39
|
+
suppressibleLeadingRanges: geometry.suppressibleLeadingRanges.map((range) => ({
|
|
40
|
+
top: range.top + offset,
|
|
41
|
+
bottom: range.bottom + offset
|
|
42
42
|
}))
|
|
43
43
|
};
|
|
44
44
|
}
|
|
@@ -46,22 +46,28 @@ function shiftCellGeometry(geometry, offset) {
|
|
|
46
46
|
function cellBreakGeometry(cell, measure) {
|
|
47
47
|
const bottoms = [];
|
|
48
48
|
const unsafeRanges = [];
|
|
49
|
+
const suppressibleLeadingRanges = [];
|
|
49
50
|
const cellBlocks = cell?.blocks;
|
|
50
51
|
const blockMeasures = measure.blocks;
|
|
51
52
|
const padTop = cell?.padding?.top ?? DEFAULT_TABLE_CELL_PADDING_TOP;
|
|
52
53
|
const contentWidth = getTableCellContentWidth(cell, measure);
|
|
53
54
|
const floatingZones = buildTableCellFloatingZones(cell !== void 0 ? getTableCellFloatingImages(cell, measure, contentWidth) : [], contentWidth);
|
|
54
|
-
|
|
55
|
-
let paragraphY = 0;
|
|
55
|
+
const flowState = createTableCellFlowState();
|
|
56
56
|
for (let i = 0; i < blockMeasures.length; i++) {
|
|
57
57
|
let blockMeasure = blockMeasures[i];
|
|
58
58
|
if (blockMeasure?.kind === "paragraph") {
|
|
59
59
|
const block = cellBlocks?.[i];
|
|
60
|
-
if (block?.kind
|
|
60
|
+
if (block?.kind !== "paragraph") continue;
|
|
61
|
+
if (floatingZones.length > 0) blockMeasure = measureParagraph(block, contentWidth, {
|
|
61
62
|
floatingZones,
|
|
62
|
-
paragraphYOffset:
|
|
63
|
+
paragraphYOffset: placeTableCellBlock({ ...flowState }, block, blockMeasure).contentTop
|
|
64
|
+
});
|
|
65
|
+
const placement = placeTableCellBlock(flowState, block, blockMeasure);
|
|
66
|
+
if (placement.leadingSpacing > 0 && isEmptyParagraph(block)) suppressibleLeadingRanges.push({
|
|
67
|
+
top: padTop + placement.top,
|
|
68
|
+
bottom: padTop + placement.contentTop
|
|
63
69
|
});
|
|
64
|
-
|
|
70
|
+
let y = padTop + placement.contentTop;
|
|
65
71
|
for (const line of blockMeasure.lines) {
|
|
66
72
|
y += line.floatSkipBefore ?? 0;
|
|
67
73
|
const top = y;
|
|
@@ -72,16 +78,15 @@ function cellBreakGeometry(cell, measure) {
|
|
|
72
78
|
});
|
|
73
79
|
bottoms.push(y);
|
|
74
80
|
}
|
|
75
|
-
|
|
76
|
-
paragraphY += blockMeasure.totalHeight;
|
|
81
|
+
bottoms.push(padTop + flowState.height);
|
|
77
82
|
} else if (blockMeasure) {
|
|
78
83
|
const block = cellBlocks?.[i];
|
|
79
|
-
if (block
|
|
80
|
-
const
|
|
84
|
+
if (!block) continue;
|
|
85
|
+
const placement = placeTableCellBlock(flowState, block, blockMeasure);
|
|
86
|
+
const blockHeight = placement.contentHeight;
|
|
81
87
|
if (blockHeight > 0) {
|
|
82
|
-
const top =
|
|
83
|
-
y
|
|
84
|
-
paragraphY += blockHeight;
|
|
88
|
+
const top = padTop + placement.contentTop;
|
|
89
|
+
const y = top + blockHeight;
|
|
85
90
|
unsafeRanges.push({
|
|
86
91
|
top,
|
|
87
92
|
bottom: y
|
|
@@ -92,9 +97,26 @@ function cellBreakGeometry(cell, measure) {
|
|
|
92
97
|
}
|
|
93
98
|
return {
|
|
94
99
|
bottoms,
|
|
95
|
-
unsafeRanges
|
|
100
|
+
unsafeRanges,
|
|
101
|
+
suppressibleLeadingRanges
|
|
96
102
|
};
|
|
97
103
|
}
|
|
104
|
+
const continuationSkipForCell = (geometry, offset) => {
|
|
105
|
+
if (!geometry.unsafeRanges.some(({ bottom }) => bottom > offset + BREAK_OFFSET_EPSILON)) return;
|
|
106
|
+
const leadingRange = geometry.suppressibleLeadingRanges.find(({ top, bottom }) => top <= offset + BREAK_OFFSET_EPSILON && bottom > offset + BREAK_OFFSET_EPSILON);
|
|
107
|
+
return leadingRange ? leadingRange.bottom - offset : 0;
|
|
108
|
+
};
|
|
109
|
+
/**
|
|
110
|
+
* Leading spacing before an empty paragraph is consumed when a split row
|
|
111
|
+
* resumes at the top of a fresh flow region. Only a whitespace band shared by
|
|
112
|
+
* every cell with remaining content is suppressible; non-empty paragraphs,
|
|
113
|
+
* line boxes, cell padding, and explicit row height retain authored space.
|
|
114
|
+
*/
|
|
115
|
+
const continuationSkipAfter = (geometries, offset) => {
|
|
116
|
+
const skips = geometries.map((geometry) => continuationSkipForCell(geometry, offset)).filter((skip) => skip !== void 0);
|
|
117
|
+
if (skips.length === 0 || skips.some((skip) => skip <= BREAK_OFFSET_EPSILON)) return 0;
|
|
118
|
+
return Math.min(...skips);
|
|
119
|
+
};
|
|
98
120
|
/** Build break geometry for a table from its block + measure. */
|
|
99
121
|
function buildTableRowBreakInfo(block, measure) {
|
|
100
122
|
const rowCount = measure.rows.length;
|
|
@@ -106,6 +128,7 @@ function buildTableRowBreakInfo(block, measure) {
|
|
|
106
128
|
}
|
|
107
129
|
rowTops.push(acc);
|
|
108
130
|
const breakOffsets = [];
|
|
131
|
+
const continuationSkips = [];
|
|
109
132
|
for (let r = 0; r < rowCount; r++) {
|
|
110
133
|
const rowHeight = measure.rows[r]?.height ?? 0;
|
|
111
134
|
const offsets = /* @__PURE__ */ new Set();
|
|
@@ -121,14 +144,24 @@ function buildTableRowBreakInfo(block, measure) {
|
|
|
121
144
|
cellGeometries.push(geometry);
|
|
122
145
|
for (const b of geometry.bottoms) if (b > 0 && b < rowHeight) offsets.add(b);
|
|
123
146
|
}
|
|
124
|
-
const
|
|
125
|
-
breakOffsets.push(
|
|
147
|
+
const sortedOffsets = [...offsets].filter((offset) => offset === rowHeight || cellGeometries.every((geometry) => geometry.unsafeRanges.every((range) => !isInsideRange(offset, range)))).sort((a, b) => a - b);
|
|
148
|
+
breakOffsets.push(sortedOffsets);
|
|
149
|
+
continuationSkips.push(sortedOffsets.map((offset) => continuationSkipAfter(cellGeometries, offset)));
|
|
126
150
|
}
|
|
127
151
|
return {
|
|
128
152
|
rowTops,
|
|
129
|
-
breakOffsets
|
|
153
|
+
breakOffsets,
|
|
154
|
+
continuationSkips
|
|
130
155
|
};
|
|
131
156
|
}
|
|
157
|
+
/** Leading empty-paragraph whitespace to consume when a row resumes after `offset`. */
|
|
158
|
+
function getRowContinuationSkip(info, rowIndex, offset) {
|
|
159
|
+
const offsets = info.breakOffsets[rowIndex];
|
|
160
|
+
const skips = info.continuationSkips[rowIndex];
|
|
161
|
+
const index = offsets?.findIndex((candidate) => Math.abs(candidate - offset) <= BREAK_OFFSET_EPSILON);
|
|
162
|
+
if (index === void 0 || index < 0) return 0;
|
|
163
|
+
return skips?.[index] ?? 0;
|
|
164
|
+
}
|
|
132
165
|
/**
|
|
133
166
|
* Given a row and how much of it has already been placed (`fromOffset`), return
|
|
134
167
|
* how many more px can be placed ending on a whole line, without exceeding
|
|
@@ -147,4 +180,4 @@ function snapRowBreak(info, rowIndex, fromOffset, maxSlice) {
|
|
|
147
180
|
return best;
|
|
148
181
|
}
|
|
149
182
|
//#endregion
|
|
150
|
-
export { buildTableRowBreakInfo, snapRowBreak };
|
|
183
|
+
export { buildTableRowBreakInfo, getRowContinuationSkip, snapRowBreak };
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { OutlineStyleAttr } from "../types/documentEnumValues.js";
|
|
2
|
-
import { ImagePosition, ImageWrap, SdtProperties, SdtType, TableWidthType } from "@stll/docx-core/model";
|
|
2
|
+
import { ImagePosition, ImageWrap, SdtProperties, SdtType, ShapeTextBody, TableCellFormatting, TableWidthType } from "@stll/docx-core/model";
|
|
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, 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, 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.
|
|
@@ -38,7 +38,12 @@ type RunFormatting = {
|
|
|
38
38
|
* `segmentByScript`) so wrapping stays correct. Absent means CJK falls back
|
|
39
39
|
* to `fontFamily`.
|
|
40
40
|
*/
|
|
41
|
-
eastAsiaFontFamily?: string;
|
|
41
|
+
eastAsiaFontFamily?: string; /** Run language metadata resolved from `w:lang`. */
|
|
42
|
+
language?: {
|
|
43
|
+
val?: string;
|
|
44
|
+
eastAsia?: string;
|
|
45
|
+
bidi?: string;
|
|
46
|
+
};
|
|
42
47
|
fontSize?: number;
|
|
43
48
|
letterSpacing?: number;
|
|
44
49
|
superscript?: boolean;
|
|
@@ -164,7 +169,8 @@ type ImageRun = {
|
|
|
164
169
|
distTop?: number;
|
|
165
170
|
distBottom?: number;
|
|
166
171
|
distLeft?: number;
|
|
167
|
-
distRight?: number;
|
|
172
|
+
distRight?: number; /** Use the image box as the exact line height for an embedded-object preview. */
|
|
173
|
+
exactLineHeight?: boolean;
|
|
168
174
|
/**
|
|
169
175
|
* wp:srcRect crop fractions in [0, 1]; emit as CSS `clip-path: inset(...)`
|
|
170
176
|
* to match Word's visible region. eigenpal #424 (image-crop subset).
|
|
@@ -286,7 +292,26 @@ type ListNumPr = {
|
|
|
286
292
|
* Paragraph block attributes.
|
|
287
293
|
*/
|
|
288
294
|
type ParagraphAttrs = {
|
|
289
|
-
alignment?: "left" | "center" | "right" | "justify"; /**
|
|
295
|
+
alignment?: "left" | "center" | "right" | "justify"; /** East Asian first/last-character restrictions (`w:kinsoku`). */
|
|
296
|
+
kinsoku?: boolean; /** Hanging punctuation (`w:overflowPunct`), retained for layout policy. */
|
|
297
|
+
overflowPunctuation?: boolean; /** Exempt this paragraph from document automatic hyphenation. */
|
|
298
|
+
suppressAutoHyphens?: boolean; /** Document-wide automatic hyphenation controls retained for line layout. */
|
|
299
|
+
automaticHyphenation?: {
|
|
300
|
+
enabled: true;
|
|
301
|
+
doNotHyphenateCaps?: boolean;
|
|
302
|
+
consecutiveLineLimit?: number;
|
|
303
|
+
}; /** Document-wide custom line-edge characters and compatibility behavior. */
|
|
304
|
+
lineBreakRules?: {
|
|
305
|
+
noLineBreaksBefore?: {
|
|
306
|
+
language?: string;
|
|
307
|
+
characters: string;
|
|
308
|
+
};
|
|
309
|
+
noLineBreaksAfter?: {
|
|
310
|
+
language?: string;
|
|
311
|
+
characters: string;
|
|
312
|
+
};
|
|
313
|
+
useLegacyEthiopicAmharicRules?: boolean;
|
|
314
|
+
}; /** OOXML outline level (`w:outlineLvl`), where zero is the top level. */
|
|
290
315
|
outlineLevel?: number;
|
|
291
316
|
spacing?: ParagraphSpacing; /** Spacing sides resolved from OOXML automatic paragraph spacing. */
|
|
292
317
|
automaticSpacing?: {
|
|
@@ -310,7 +335,8 @@ type ParagraphAttrs = {
|
|
|
310
335
|
* source `w:pPr`. Word treats such a blank as authored and keeps its
|
|
311
336
|
* inherited spacing; a bare empty paragraph still collapses that spacing.
|
|
312
337
|
*/
|
|
313
|
-
hasDirectParagraphFormatting?: boolean;
|
|
338
|
+
hasDirectParagraphFormatting?: boolean; /** Whether an empty paragraph carries direct formatting on its paragraph mark. */
|
|
339
|
+
hasDirectParagraphMarkFormatting?: boolean;
|
|
314
340
|
indent?: ParagraphIndent;
|
|
315
341
|
keepNext?: boolean;
|
|
316
342
|
keepLines?: boolean;
|
|
@@ -338,6 +364,8 @@ type ParagraphAttrs = {
|
|
|
338
364
|
listMarkerHidden?: boolean;
|
|
339
365
|
listMarkerFontFamily?: string;
|
|
340
366
|
listMarkerFontSize?: number;
|
|
367
|
+
listMarkerBold?: boolean; /** Horizontal alignment of the marker around the paragraph's list anchor. */
|
|
368
|
+
listMarkerAlignment?: "left" | "center" | "right";
|
|
341
369
|
/**
|
|
342
370
|
* `w:suff` (§17.9.25) — what follows the marker before body text.
|
|
343
371
|
* `tab` (default) grows the marker to the next tab stop; `space` adds
|
|
@@ -411,6 +439,8 @@ type CellBorders = {
|
|
|
411
439
|
bottom?: CellBorderSpec;
|
|
412
440
|
left?: CellBorderSpec;
|
|
413
441
|
right?: CellBorderSpec;
|
|
442
|
+
topLeftToBottomRight?: CellBorderSpec;
|
|
443
|
+
topRightToBottomLeft?: CellBorderSpec;
|
|
414
444
|
};
|
|
415
445
|
/**
|
|
416
446
|
* A table cell with content.
|
|
@@ -422,6 +452,7 @@ type TableCell = {
|
|
|
422
452
|
rowSpan?: number;
|
|
423
453
|
width?: number;
|
|
424
454
|
verticalAlign?: "top" | "center" | "bottom";
|
|
455
|
+
textDirection?: NonNullable<TableCellFormatting["textDirection"]>;
|
|
425
456
|
background?: string;
|
|
426
457
|
borders?: CellBorders; /** Per-cell padding in pixels (from w:tcMar or table-level w:tblCellMar) */
|
|
427
458
|
padding?: {
|
|
@@ -443,9 +474,12 @@ type TableCell = {
|
|
|
443
474
|
type TableRow = {
|
|
444
475
|
id: BlockId;
|
|
445
476
|
cells: TableCell[];
|
|
477
|
+
gridBefore?: number;
|
|
478
|
+
gridAfter?: number;
|
|
446
479
|
height?: number;
|
|
447
480
|
heightRule?: "auto" | "atLeast" | "exact";
|
|
448
|
-
isHeader?: boolean;
|
|
481
|
+
isHeader?: boolean; /** `w:cantSplit`: keep this row in one flow region. */
|
|
482
|
+
cantSplit?: boolean;
|
|
449
483
|
hidden?: boolean;
|
|
450
484
|
};
|
|
451
485
|
/**
|
|
@@ -582,7 +616,8 @@ type TextBoxBlock = {
|
|
|
582
616
|
kind: "textBox";
|
|
583
617
|
id: BlockId; /** Width in pixels */
|
|
584
618
|
width: number; /** Height in pixels (may be auto-calculated) */
|
|
585
|
-
height?: number; /**
|
|
619
|
+
height?: number; /** Text fitting behavior */
|
|
620
|
+
autoFit?: ShapeTextBody["autoFit"]; /** Fill/background color */
|
|
586
621
|
fillColor?: string; /** Border width in pixels */
|
|
587
622
|
outlineWidth?: number; /** Border color */
|
|
588
623
|
outlineColor?: string; /** Outline dash style, or `"none"` for an explicit no-outline. */
|
|
@@ -664,7 +699,10 @@ type MeasuredLine = {
|
|
|
664
699
|
* no usable horizontal room at the natural line Y. Painters render this
|
|
665
700
|
* as marginTop on the line element; measurement adds it to totalHeight.
|
|
666
701
|
*/
|
|
667
|
-
floatSkipBefore?: number;
|
|
702
|
+
floatSkipBefore?: number; /** Decorative hyphen inserted at a dictionary break without changing source text. */
|
|
703
|
+
discretionaryHyphen?: {
|
|
704
|
+
runIndex: number;
|
|
705
|
+
};
|
|
668
706
|
};
|
|
669
707
|
/**
|
|
670
708
|
* Measurement result for a paragraph block.
|
|
@@ -913,7 +951,8 @@ type LayoutOptions = {
|
|
|
913
951
|
* overflowing first-page header without forcing pages 2+ to inherit the
|
|
914
952
|
* same extension.
|
|
915
953
|
*/
|
|
916
|
-
firstPageMargins?: PageMargins; /**
|
|
954
|
+
firstPageMargins?: PageMargins; /** Per-section body margins used on even section pages. */
|
|
955
|
+
sectionEvenPageMargins?: (PageMargins | undefined)[]; /** Body-level final section page size. */
|
|
917
956
|
finalPageSize?: {
|
|
918
957
|
w: number;
|
|
919
958
|
h: number;
|
|
@@ -1016,7 +1055,8 @@ type HeaderFooterContent = {
|
|
|
1016
1055
|
/**
|
|
1017
1056
|
* `true` when the image run is anchored at page-level coordinates instead of
|
|
1018
1057
|
* participating in inline flow. Covers the OOXML floating wrap types
|
|
1019
|
-
* (`square`, `tight`, `through`, `behind`, `inFront`)
|
|
1058
|
+
* (`square`, `tight`, `through`, `behind`, `inFront`), explicitly positioned
|
|
1059
|
+
* `topAndBottom` artwork, and the legacy
|
|
1020
1060
|
* `displayMode === "float"` escape hatch used by ProseMirror nodes that
|
|
1021
1061
|
* pre-date the wrap-type round-trip.
|
|
1022
1062
|
*
|
|
@@ -1067,5 +1107,7 @@ declare function floatingTextBoxReservesBand(block: TextBoxFlowAttrs): boolean;
|
|
|
1067
1107
|
* and the painter both consult this so their line count and `white-space` agree.
|
|
1068
1108
|
*/
|
|
1069
1109
|
declare function tableColumnsArePinned(table: TableBlock): boolean;
|
|
1110
|
+
/** Return the leading visual offset for a row with omitted grid columns. */
|
|
1111
|
+
declare const getTableRowLeadingWidth: (row: TableRow, columnWidths: readonly number[]) => number;
|
|
1070
1112
|
//#endregion
|
|
1071
|
-
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, isFloatingImageRun, isFloatingTextBoxBlock, isTextWrappingFloatingImageRun, tableColumnsArePinned, types_d_exports };
|
|
1113
|
+
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 };
|
|
@@ -9,6 +9,7 @@ var types_exports = /* @__PURE__ */ __exportAll({
|
|
|
9
9
|
FOOTNOTE_SEPARATOR_HEIGHT: () => 12,
|
|
10
10
|
floatingTextBoxReservesBand: () => floatingTextBoxReservesBand,
|
|
11
11
|
floatingTextBoxWrapsText: () => floatingTextBoxWrapsText,
|
|
12
|
+
getTableRowLeadingWidth: () => getTableRowLeadingWidth,
|
|
12
13
|
isFloatingImageRun: () => isFloatingImageRun,
|
|
13
14
|
isFloatingTextBoxBlock: () => isFloatingTextBoxBlock,
|
|
14
15
|
isTextWrappingFloatingImageRun: () => isTextWrappingFloatingImageRun,
|
|
@@ -46,7 +47,8 @@ const FOOTNOTE_FALLBACK_LINE_HEIGHT = 13;
|
|
|
46
47
|
/**
|
|
47
48
|
* `true` when the image run is anchored at page-level coordinates instead of
|
|
48
49
|
* participating in inline flow. Covers the OOXML floating wrap types
|
|
49
|
-
* (`square`, `tight`, `through`, `behind`, `inFront`)
|
|
50
|
+
* (`square`, `tight`, `through`, `behind`, `inFront`), explicitly positioned
|
|
51
|
+
* `topAndBottom` artwork, and the legacy
|
|
50
52
|
* `displayMode === "float"` escape hatch used by ProseMirror nodes that
|
|
51
53
|
* pre-date the wrap-type round-trip.
|
|
52
54
|
*
|
|
@@ -55,6 +57,7 @@ const FOOTNOTE_FALLBACK_LINE_HEIGHT = 13;
|
|
|
55
57
|
*/
|
|
56
58
|
const isFloatingImageRun = (run) => {
|
|
57
59
|
const wrapType = run.wrapType;
|
|
60
|
+
if (wrapType === "topAndBottom" && run.position !== void 0) return true;
|
|
58
61
|
if (wrapType === "square" || wrapType === "tight" || wrapType === "through" || wrapType === "behind" || wrapType === "inFront") return true;
|
|
59
62
|
return run.displayMode === "float";
|
|
60
63
|
};
|
|
@@ -114,5 +117,15 @@ function tableColumnsArePinned(table) {
|
|
|
114
117
|
if (table.layout === "fixed") return true;
|
|
115
118
|
return table.widthType === "dxa" || table.widthType === "pct";
|
|
116
119
|
}
|
|
120
|
+
const tableRowOwnsNestedGrid = (row) => {
|
|
121
|
+
const cell = row.cells.length === 1 ? row.cells.at(0) : void 0;
|
|
122
|
+
if (!cell?.blocks.some((block) => block.kind === "table")) return false;
|
|
123
|
+
return cell.blocks.every((block) => block.kind === "table" || block.kind === "paragraph" && block.runs.length === 0);
|
|
124
|
+
};
|
|
125
|
+
/** Return the leading visual offset for a row with omitted grid columns. */
|
|
126
|
+
const getTableRowLeadingWidth = (row, columnWidths) => {
|
|
127
|
+
if (tableRowOwnsNestedGrid(row)) return 0;
|
|
128
|
+
return columnWidths.slice(0, row.gridBefore ?? 0).reduce((sum, columnWidth) => sum + columnWidth, 0);
|
|
129
|
+
};
|
|
117
130
|
//#endregion
|
|
118
|
-
export { DEFAULT_TEXTBOX_MARGINS, DEFAULT_TEXTBOX_WIDTH, FOOTNOTE_ENTRY_MARGIN_BOTTOM, FOOTNOTE_FALLBACK_LINE_HEIGHT, FOOTNOTE_SEPARATOR_HEIGHT, floatingTextBoxReservesBand, floatingTextBoxWrapsText, isFloatingImageRun, isFloatingTextBoxBlock, isTextWrappingFloatingImageRun, tableColumnsArePinned, types_exports };
|
|
131
|
+
export { DEFAULT_TEXTBOX_MARGINS, DEFAULT_TEXTBOX_WIDTH, FOOTNOTE_ENTRY_MARGIN_BOTTOM, FOOTNOTE_FALLBACK_LINE_HEIGHT, FOOTNOTE_SEPARATOR_HEIGHT, floatingTextBoxReservesBand, floatingTextBoxWrapsText, getTableRowLeadingWidth, isFloatingImageRun, isFloatingTextBoxBlock, isTextWrappingFloatingImageRun, tableColumnsArePinned, types_exports };
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { ImageRun } from "../layout-engine/types.js";
|
|
2
|
+
|
|
3
|
+
//#region src/layout-painter/anchoredImagePosition.d.ts
|
|
4
|
+
type PageGeometry = {
|
|
5
|
+
pageWidth: number;
|
|
6
|
+
pageHeight: number;
|
|
7
|
+
marginLeft: number;
|
|
8
|
+
marginTop: number;
|
|
9
|
+
marginRight: number;
|
|
10
|
+
marginBottom: number;
|
|
11
|
+
contentWidth: number;
|
|
12
|
+
contentHeight: number;
|
|
13
|
+
};
|
|
14
|
+
/** Coordinates relative to the page content area's top-left corner. */
|
|
15
|
+
type AnchoredImagePosition = {
|
|
16
|
+
x: number;
|
|
17
|
+
y: number;
|
|
18
|
+
side: "left" | "right";
|
|
19
|
+
};
|
|
20
|
+
/** Resolve an anchored image into page-content-relative coordinates. */
|
|
21
|
+
declare function resolveAnchoredImagePosition(imgRun: ImageRun, fragmentY: number, geometry: PageGeometry): AnchoredImagePosition;
|
|
22
|
+
//#endregion
|
|
23
|
+
export { AnchoredImagePosition, PageGeometry, resolveAnchoredImagePosition };
|