@stll/folio-core 0.23.0 → 0.23.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/controller/layoutPipeline.js +7 -3
- package/dist/docx/blockContentParser.js +2 -47
- package/dist/docx/fieldParser.d.ts +1 -1
- package/dist/docx/fieldParser.js +12 -4
- package/dist/docx/numberingParser.d.ts +2 -11
- package/dist/docx/numberingParser.js +3 -79
- package/dist/docx/ooxmlCounterFormatter.d.ts +8 -0
- package/dist/docx/ooxmlCounterFormatter.js +134 -0
- package/dist/docx/serializer/tableSerializer.js +1 -1
- package/dist/docx/tableParser.js +2 -1
- package/dist/fields/bookmarkPages.js +1 -1
- package/dist/fields/evaluateField.js +1 -1
- package/dist/fields/fieldContext.d.ts +2 -0
- package/dist/fields/resolveFieldValues.js +5 -2
- package/dist/layout-bridge/convert/headerFooterLayout.js +6 -0
- package/dist/layout-bridge/convert/toFlowBlocks.js +16 -48
- package/dist/layout-engine/index.d.ts +3 -2
- package/dist/layout-engine/index.js +40 -16
- package/dist/layout-engine/measure/cache.js +1 -1
- package/dist/layout-engine/measure/complexScriptFormatting.d.ts +18 -0
- package/dist/layout-engine/measure/complexScriptFormatting.js +11 -0
- package/dist/layout-engine/measure/measureContainer.js +31 -16
- package/dist/layout-engine/measure/measureHelpers.js +4 -0
- package/dist/layout-engine/measure/measureParagraph.js +13 -3
- package/dist/layout-engine/measure/measureTypes.d.ts +4 -0
- package/dist/layout-engine/paginator.d.ts +5 -3
- package/dist/layout-engine/paginator.js +34 -13
- package/dist/layout-engine/types.d.ts +31 -4
- package/dist/layout-painter/index.js +2 -1
- package/dist/layout-painter/renderPage.js +9 -3
- package/dist/layout-painter/renderParagraph.js +42 -25
- package/dist/layout-painter/renderUtils.d.ts +2 -0
- package/dist/paged-layout/sectionGeometry.d.ts +3 -2
- package/dist/paged-layout/sectionGeometry.js +17 -1
- package/dist/prosemirror/attrs/index.js +5 -0
- package/dist/prosemirror/conversion/fromProseDoc.js +4 -16
- package/dist/prosemirror/conversion/toProseDoc.js +4 -1
- package/dist/prosemirror/extensions/marks/RunFormattingOverrideExtension.d.ts +3 -2
- package/dist/prosemirror/extensions/marks/RunFormattingOverrideExtension.js +31 -17
- package/dist/prosemirror/extensions/nodes/TableExtension.js +1 -0
- package/dist/prosemirror/schema/marks.d.ts +8 -0
- package/dist/prosemirror/schema/nodes.d.ts +2 -0
- package/package.json +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { 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 } from "./types.js";
|
|
1
|
+
import { 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, SectionPageNumbering, TabAlignment, TabRun, TabStop, TableBlock, TableCell, TableCellMeasure, TableFragment, TableMeasure, TableRow, TableRowMeasure, TextBoxBlock, TextBoxFlowAttrs, TextBoxFragment, TextBoxMeasure, TextRun, floatingTextBoxReservesBand, floatingTextBoxWrapsText, getTableRowLeadingWidth, isFloatingImageRun, isFloatingTextBoxBlock, isTextWrappingFloatingImageRun, tableColumnsArePinned } from "./types.js";
|
|
2
2
|
import { PageState, Paginator, PaginatorOptions, createPaginator } from "./paginator.js";
|
|
3
3
|
import { KeepNextChain, calculateChainHeight, computeKeepNextChains, getMidChainIndices, hasKeepLines, hasPageBreakBefore } from "./keep-together.js";
|
|
4
4
|
import { resolveSectionHeaderFooterRefs } from "./headerFooterRefs.js";
|
|
@@ -11,6 +11,7 @@ type SectionLayoutConfig = {
|
|
|
11
11
|
h: number;
|
|
12
12
|
};
|
|
13
13
|
margins: PageMargins;
|
|
14
|
+
pageNumbering: SectionPageNumbering;
|
|
14
15
|
columns?: ColumnLayout;
|
|
15
16
|
};
|
|
16
17
|
declare function collectSectionConfigs(blocks: FlowBlock[], initialConfig: SectionLayoutConfig, finalConfig: SectionLayoutConfig): {
|
|
@@ -43,4 +44,4 @@ declare function layoutDocument(blocks: FlowBlock[], measures: Measure[], option
|
|
|
43
44
|
*/
|
|
44
45
|
declare function getHeaderRowsHeight(measure: TableMeasure, headerRowCount: number): number;
|
|
45
46
|
//#endregion
|
|
46
|
-
export { BlockId, BorderStyle, type BreakDecision, 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, type KeepNextChain, Layout, LayoutOptions, LineBreakRun, ListNumPr, MathRun, Measure, MeasuredLine, Page, PageBreakBlock, PageBreakMeasure, PageHeaderFooterRefs, PageMargins, type PageState, type Paginator, type PaginatorOptions, ParagraphAttrs, ParagraphBlock, ParagraphBorders, ParagraphFragment, ParagraphIndent, ParagraphMeasure, ParagraphSpacing, RenderedPageBreakRun, Run, RunFormatting, SdtGroup, SectionBreakBlock, SectionBreakMeasure, SectionLayoutConfig, type SectionState, TabAlignment, TabRun, TabStop, TableBlock, TableCell, TableCellMeasure, TableFragment, TableMeasure, TableRow, TableRowMeasure, TextBoxBlock, TextBoxFlowAttrs, TextBoxFragment, TextBoxMeasure, TextRun, applyContextualSpacing, applyPendingToActive, assertExhaustiveFlowBlock, calculateChainHeight, collectSectionConfigs, computeKeepNextChains, createInitialSectionState, createPaginator, findPageIndexContainingPmPos, floatingTextBoxReservesBand, floatingTextBoxWrapsText, getEffectiveColumns, getEffectiveMargins, getEffectivePageSize, getHeaderRowsHeight, getMidChainIndices, getTableRowLeadingWidth, hasKeepLines, hasPageBreakBefore, isFloatingImageRun, isFloatingTextBoxBlock, isTextWrappingFloatingImageRun, layoutDocument, resolveSectionHeaderFooterRefs, scheduleSectionBreak, tableColumnsArePinned };
|
|
47
|
+
export { BlockId, BorderStyle, type BreakDecision, 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, type KeepNextChain, Layout, LayoutOptions, LineBreakRun, ListNumPr, MathRun, Measure, MeasuredLine, Page, PageBreakBlock, PageBreakMeasure, PageHeaderFooterRefs, PageMargins, type PageState, type Paginator, type PaginatorOptions, ParagraphAttrs, ParagraphBlock, ParagraphBorders, ParagraphFragment, ParagraphIndent, ParagraphMeasure, ParagraphSpacing, RenderedPageBreakRun, Run, RunFormatting, SdtGroup, SectionBreakBlock, SectionBreakMeasure, SectionLayoutConfig, SectionPageNumbering, type SectionState, TabAlignment, TabRun, TabStop, TableBlock, TableCell, TableCellMeasure, TableFragment, TableMeasure, TableRow, TableRowMeasure, TextBoxBlock, TextBoxFlowAttrs, TextBoxFragment, TextBoxMeasure, TextRun, applyContextualSpacing, applyPendingToActive, assertExhaustiveFlowBlock, calculateChainHeight, collectSectionConfigs, computeKeepNextChains, createInitialSectionState, createPaginator, findPageIndexContainingPmPos, floatingTextBoxReservesBand, floatingTextBoxWrapsText, getEffectiveColumns, getEffectiveMargins, getEffectivePageSize, getHeaderRowsHeight, getMidChainIndices, getTableRowLeadingWidth, hasKeepLines, hasPageBreakBefore, isFloatingImageRun, isFloatingTextBoxBlock, isTextWrappingFloatingImageRun, layoutDocument, resolveSectionHeaderFooterRefs, scheduleSectionBreak, tableColumnsArePinned };
|
|
@@ -5,7 +5,7 @@ import { measuredLineAdvance } from "./lineFlow.js";
|
|
|
5
5
|
import { resolveFloatingTableX } from "./measure/floatingTablePosition.js";
|
|
6
6
|
import { createPaginator } from "./paginator.js";
|
|
7
7
|
import { getParagraphFragmentPmRange } from "./paragraphFragmentRange.js";
|
|
8
|
-
import { collapseParagraphSpacing, getParagraphSpacingAfter, getParagraphSpacingBefore, paragraphsShareStyle, resolveEffectiveParagraphSpacingTree } from "./paragraphSpacing.js";
|
|
8
|
+
import { collapseParagraphSpacing, getParagraphSpacingAfter, getParagraphSpacingBefore, isEmptyParagraph, paragraphsShareStyle, resolveEffectiveParagraphSpacingTree } from "./paragraphSpacing.js";
|
|
9
9
|
import { assertExhaustiveFlowBlock, findPageIndexContainingPmPos } from "./pmPageIndex.js";
|
|
10
10
|
import { INITIAL_RENDERED_BREAK_STATE, reconcileAfterBlock, reconcileBreakBeforeBlock, recordReflowBoundary } from "./renderedBreakReconciliation.js";
|
|
11
11
|
import { applyPendingToActive, createInitialSectionState, getEffectiveColumns, getEffectiveMargins, getEffectivePageSize, scheduleSectionBreak } from "./section-breaks.js";
|
|
@@ -25,6 +25,7 @@ const DEFAULT_COLUMNS = {
|
|
|
25
25
|
gap: 0
|
|
26
26
|
};
|
|
27
27
|
const DEFAULT_SECTION_BREAK_TYPE = "nextPage";
|
|
28
|
+
const CONTINUE_PAGE_NUMBERING = { type: "continue" };
|
|
28
29
|
function collectSectionConfigs(blocks, initialConfig, finalConfig) {
|
|
29
30
|
const configs = [];
|
|
30
31
|
const breakIndices = [];
|
|
@@ -35,7 +36,8 @@ function collectSectionConfigs(blocks, initialConfig, finalConfig) {
|
|
|
35
36
|
const sectionBreak = block;
|
|
36
37
|
const config = {
|
|
37
38
|
pageSize: sectionBreak.pageSize ?? previousConfig.pageSize,
|
|
38
|
-
margins: sectionBreak.margins ?? previousConfig.margins
|
|
39
|
+
margins: sectionBreak.margins ?? previousConfig.margins,
|
|
40
|
+
pageNumbering: sectionBreak.pageNumbering ?? CONTINUE_PAGE_NUMBERING
|
|
39
41
|
};
|
|
40
42
|
if (sectionBreak.columns !== void 0) config.columns = sectionBreak.columns;
|
|
41
43
|
configs.push(config);
|
|
@@ -160,12 +162,14 @@ function layoutDocument(blocks, measures, options) {
|
|
|
160
162
|
if (pageSize.w - margins.left - margins.right <= 0) panic("layoutDocument: page size and margins yield no content area");
|
|
161
163
|
const bodyConfig = {
|
|
162
164
|
pageSize,
|
|
163
|
-
margins
|
|
165
|
+
margins,
|
|
166
|
+
pageNumbering: options.pageNumbering ?? CONTINUE_PAGE_NUMBERING
|
|
164
167
|
};
|
|
165
168
|
if (options.columns !== void 0) bodyConfig.columns = options.columns;
|
|
166
169
|
const finalConfig = {
|
|
167
170
|
pageSize: finalPageSize,
|
|
168
|
-
margins: finalMargins
|
|
171
|
+
margins: finalMargins,
|
|
172
|
+
pageNumbering: options.finalPageNumbering ?? bodyConfig.pageNumbering
|
|
169
173
|
};
|
|
170
174
|
const finalColumns = options.finalColumns ?? options.columns;
|
|
171
175
|
if (finalColumns !== void 0) finalConfig.columns = finalColumns;
|
|
@@ -179,6 +183,7 @@ function layoutDocument(blocks, measures, options) {
|
|
|
179
183
|
...options.firstPageMargins !== void 0 ? { firstPageMargins: options.firstPageMargins } : {},
|
|
180
184
|
...options.sectionEvenPageMargins !== void 0 ? { sectionEvenPageMargins: options.sectionEvenPageMargins } : {},
|
|
181
185
|
columns: initialConfig.columns ?? DEFAULT_COLUMNS,
|
|
186
|
+
pageNumbering: initialConfig.pageNumbering,
|
|
182
187
|
...options.footnoteReservedHeights !== void 0 ? { footnoteReservedHeights: options.footnoteReservedHeights } : {},
|
|
183
188
|
...options.sectionHeaderFooterRefs !== void 0 ? { sectionHeaderFooterRefs: options.sectionHeaderFooterRefs } : {}
|
|
184
189
|
});
|
|
@@ -474,10 +479,27 @@ function getHeaderRowsHeight(measure, headerRowCount) {
|
|
|
474
479
|
for (let i = 0; i < headerRowCount && i < measure.rows.length; i++) height += measure.rows[i].height;
|
|
475
480
|
return height;
|
|
476
481
|
}
|
|
477
|
-
const tableRowStartsWithRenderedPageBreak = (block, rowIndex) =>
|
|
478
|
-
const
|
|
479
|
-
|
|
480
|
-
|
|
482
|
+
const tableRowStartsWithRenderedPageBreak = (block, rowIndex) => {
|
|
483
|
+
const visibleCells = block.rows[rowIndex]?.cells.filter((cell) => cell.blocks.some((cellBlock) => cellBlock.kind !== "paragraph" || !isEmptyParagraph(cellBlock)));
|
|
484
|
+
if (!visibleCells || visibleCells.length === 0) return false;
|
|
485
|
+
return visibleCells.every((cell) => {
|
|
486
|
+
const firstVisibleBlock = cell.blocks.find((cellBlock) => cellBlock.kind !== "paragraph" || !isEmptyParagraph(cellBlock));
|
|
487
|
+
return firstVisibleBlock?.kind === "paragraph" && firstVisibleBlock.attrs?.renderedPageBreakBefore === true;
|
|
488
|
+
});
|
|
489
|
+
};
|
|
490
|
+
const getVerticallyMergedRows = (block) => {
|
|
491
|
+
const mergedRows = /* @__PURE__ */ new Set();
|
|
492
|
+
for (let rowIndex = 0; rowIndex < block.rows.length; rowIndex += 1) {
|
|
493
|
+
const row = block.rows[rowIndex];
|
|
494
|
+
if (!row) continue;
|
|
495
|
+
for (const cell of row.cells) {
|
|
496
|
+
const rowSpan = cell.rowSpan ?? 1;
|
|
497
|
+
if (rowSpan <= 1) continue;
|
|
498
|
+
for (let mergedRowIndex = rowIndex; mergedRowIndex < Math.min(block.rows.length, rowIndex + rowSpan); mergedRowIndex += 1) mergedRows.add(mergedRowIndex);
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
return mergedRows;
|
|
502
|
+
};
|
|
481
503
|
const flowBlockHasTrackedChanges = (block) => {
|
|
482
504
|
if (block.kind === "paragraph") return block.runs.some((run) => {
|
|
483
505
|
if (run.kind === "lineBreak") return false;
|
|
@@ -499,7 +521,7 @@ function layoutTable(block, measure, paginator, footnoteHeightById) {
|
|
|
499
521
|
const headerRowsHeight = getHeaderRowsHeight(measure, headerRowCount);
|
|
500
522
|
let currentRowIndex = 0;
|
|
501
523
|
const breakInfo = buildTableRowBreakInfo(block, measure);
|
|
502
|
-
const
|
|
524
|
+
const verticallyMergedRows = getVerticallyMergedRows(block);
|
|
503
525
|
const computeTableX = (columnIndex) => {
|
|
504
526
|
let x = paginator.getColumnX(columnIndex);
|
|
505
527
|
if (block.justification === "center") x += (paginator.columnWidth - measure.totalWidth) / 2;
|
|
@@ -520,7 +542,7 @@ function layoutTable(block, measure, paginator, footnoteHeightById) {
|
|
|
520
542
|
const canSplitRow = (rowIndex, state = paginator.getCurrentState()) => {
|
|
521
543
|
const row = rows[rowIndex];
|
|
522
544
|
const sourceRow = block.rows[rowIndex];
|
|
523
|
-
if (!row || !sourceRow || sourceRow.cantSplit || sourceRow.isHeader ||
|
|
545
|
+
if (!row || !sourceRow || sourceRow.cantSplit || sourceRow.isHeader || verticallyMergedRows.has(rowIndex)) return false;
|
|
524
546
|
if ((breakInfo.breakOffsets[rowIndex]?.length ?? 0) <= 1) return false;
|
|
525
547
|
const freshHeaderOverhead = headerRowCount > 0 && rowIndex >= headerRowCount ? headerRowsHeight : 0;
|
|
526
548
|
const oversized = row.height + freshHeaderOverhead > getCurrentRowCapacity(state);
|
|
@@ -881,24 +903,26 @@ function handleSectionBreak(_block, paginator, nextSectionConfig, nextSectionTyp
|
|
|
881
903
|
switch (nextSectionType) {
|
|
882
904
|
case "nextPage":
|
|
883
905
|
paginator.updatePageLayout(nextSectionConfig.pageSize, nextSectionConfig.margins);
|
|
884
|
-
if (nextSectionIndex !== void 0) paginator.startSection(nextSectionIndex);
|
|
906
|
+
if (nextSectionIndex !== void 0) paginator.startSection(nextSectionIndex, nextSectionConfig.pageNumbering);
|
|
885
907
|
paginator.forcePageBreak({ coalesceBlankPage: true });
|
|
886
908
|
break;
|
|
887
909
|
case "evenPage":
|
|
888
|
-
paginator.updatePageLayout(nextSectionConfig.pageSize, nextSectionConfig.margins);
|
|
889
|
-
if (nextSectionIndex !== void 0) paginator.startSection(nextSectionIndex);
|
|
890
910
|
if (paginator.forcePageBreak({ coalesceBlankPage: true }).page.number % 2 !== 0) paginator.forcePageBreak();
|
|
911
|
+
paginator.updatePageLayout(nextSectionConfig.pageSize, nextSectionConfig.margins);
|
|
912
|
+
if (nextSectionIndex !== void 0) paginator.startSection(nextSectionIndex, nextSectionConfig.pageNumbering);
|
|
913
|
+
if (!paginator.retargetCurrentBlankPage()) panic("Even-page section target must be blank");
|
|
891
914
|
break;
|
|
892
915
|
case "oddPage":
|
|
893
|
-
paginator.updatePageLayout(nextSectionConfig.pageSize, nextSectionConfig.margins);
|
|
894
|
-
if (nextSectionIndex !== void 0) paginator.startSection(nextSectionIndex);
|
|
895
916
|
if (paginator.forcePageBreak({ coalesceBlankPage: true }).page.number % 2 === 0) paginator.forcePageBreak();
|
|
917
|
+
paginator.updatePageLayout(nextSectionConfig.pageSize, nextSectionConfig.margins);
|
|
918
|
+
if (nextSectionIndex !== void 0) paginator.startSection(nextSectionIndex, nextSectionConfig.pageNumbering);
|
|
919
|
+
if (!paginator.retargetCurrentBlankPage()) panic("Odd-page section target must be blank");
|
|
896
920
|
break;
|
|
897
921
|
case "continuous": {
|
|
898
922
|
const currentPage = paginator.pages.at(-1);
|
|
899
923
|
const nextSize = nextSectionConfig.pageSize;
|
|
900
924
|
const pageSizeChanges = currentPage != null && (Math.round(nextSize.w) !== Math.round(currentPage.size.w) || Math.round(nextSize.h) !== Math.round(currentPage.size.h));
|
|
901
|
-
if (nextSectionIndex !== void 0) paginator.startSection(nextSectionIndex);
|
|
925
|
+
if (nextSectionIndex !== void 0) paginator.startSection(nextSectionIndex, nextSectionConfig.pageNumbering);
|
|
902
926
|
if (pageSizeChanges) {
|
|
903
927
|
paginator.updatePageLayout(nextSize, nextSectionConfig.margins);
|
|
904
928
|
if (!paginator.retargetCurrentBlankPage()) paginator.forcePageBreak({ coalesceBlankPage: true });
|
|
@@ -170,7 +170,7 @@ const paragraphMeasureCache = /* @__PURE__ */ new Map();
|
|
|
170
170
|
*/
|
|
171
171
|
function hashParagraphBlock(block) {
|
|
172
172
|
const parts = [`lbp:${getLineBreakProviderGeneration()}`];
|
|
173
|
-
for (const run of block.runs) if (run.kind === "text") parts.push(`t:${run.text}|${run.fontFamily}|${run.eastAsiaFontFamily}|${run.complexScriptFontFamily}|${run.fontSize}|${run.bold}|${run.italic}|${run.allCaps}|${run.smallCaps}|${run.horizontalScale}|${run.letterSpacing}|${run.language?.val}|${run.language?.eastAsia}|${run.language?.bidi}`);
|
|
173
|
+
for (const run of block.runs) if (run.kind === "text") parts.push(`t:${run.text}|${run.fontFamily}|${run.eastAsiaFontFamily}|${run.complexScriptFontFamily}|${run.fontSize}|${run.complexScriptFontSize}|${run.bold}|${run.complexScriptBold}|${run.italic}|${run.complexScriptItalic}|${run.forceComplexScript}|${run.allCaps}|${run.smallCaps}|${run.horizontalScale}|${run.letterSpacing}|${run.language?.val}|${run.language?.eastAsia}|${run.language?.bidi}`);
|
|
174
174
|
else if (run.kind === "tab") parts.push(`tab:${run.width}`);
|
|
175
175
|
else if (run.kind === "image") parts.push(`img:${run.width}x${run.height}:${run.exactLineHeight === true ? "exact" : "text"}`);
|
|
176
176
|
else if (run.kind === "lineBreak") parts.push("br");
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
//#region src/layout-engine/measure/complexScriptFormatting.d.ts
|
|
2
|
+
type ComplexScriptFormattingSource = {
|
|
3
|
+
complexScriptFontFamily?: string;
|
|
4
|
+
complexScriptFontSize?: number;
|
|
5
|
+
complexScriptBold?: boolean;
|
|
6
|
+
complexScriptItalic?: boolean;
|
|
7
|
+
};
|
|
8
|
+
type ResolvedComplexScriptFormatting = {
|
|
9
|
+
fontFamily?: string;
|
|
10
|
+
fontSize?: number;
|
|
11
|
+
bold?: boolean;
|
|
12
|
+
italic?: boolean;
|
|
13
|
+
};
|
|
14
|
+
/** Resolve Word's independent complex-script slot into ordinary paint fields. */
|
|
15
|
+
declare const resolveComplexScriptFormatting: (source: ComplexScriptFormattingSource) => ResolvedComplexScriptFormatting;
|
|
16
|
+
declare const hasComplexScriptFormatting: (source: ComplexScriptFormattingSource) => boolean;
|
|
17
|
+
//#endregion
|
|
18
|
+
export { ResolvedComplexScriptFormatting, hasComplexScriptFormatting, resolveComplexScriptFormatting };
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
//#region src/layout-engine/measure/complexScriptFormatting.ts
|
|
2
|
+
/** Resolve Word's independent complex-script slot into ordinary paint fields. */
|
|
3
|
+
const resolveComplexScriptFormatting = (source) => ({
|
|
4
|
+
...source.complexScriptFontFamily !== void 0 ? { fontFamily: source.complexScriptFontFamily } : {},
|
|
5
|
+
...source.complexScriptFontSize !== void 0 ? { fontSize: source.complexScriptFontSize } : {},
|
|
6
|
+
...source.complexScriptBold !== void 0 ? { bold: source.complexScriptBold } : {},
|
|
7
|
+
...source.complexScriptItalic !== void 0 ? { italic: source.complexScriptItalic } : {}
|
|
8
|
+
});
|
|
9
|
+
const hasComplexScriptFormatting = (source) => source.complexScriptFontFamily !== void 0 || source.complexScriptFontSize !== void 0 || source.complexScriptBold !== void 0 || source.complexScriptItalic !== void 0;
|
|
10
|
+
//#endregion
|
|
11
|
+
export { hasComplexScriptFormatting, resolveComplexScriptFormatting };
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { SCRIPT_CLASS, hasCjk, hasComplexScript, scriptClassOf, segmentByScript } from "../../utils/scriptSegments.js";
|
|
2
2
|
import { getCachedFontMetrics, getCachedTextWidth, getTextWidthCacheGeneration, setCachedFontMetrics, setCachedTextWidth } from "./cache.js";
|
|
3
|
+
import { hasComplexScriptFormatting, resolveComplexScriptFormatting } from "./complexScriptFormatting.js";
|
|
3
4
|
import { buildFontString, getResolvedData, ptToPx } from "./measureHelpers.js";
|
|
4
5
|
import { setMeasureProvider } from "./measureProvider.js";
|
|
5
6
|
import { canPrefetchMeasurement, prefetchMeasurement } from "./measureWorker.js";
|
|
@@ -115,10 +116,14 @@ function canvasGetFontMetrics(style) {
|
|
|
115
116
|
* @param style - Font styling properties
|
|
116
117
|
* @returns Width in pixels
|
|
117
118
|
*/
|
|
118
|
-
function canvasMeasureTextWidth(text,
|
|
119
|
+
function canvasMeasureTextWidth(text, sourceStyle) {
|
|
119
120
|
if (!text) return 0;
|
|
121
|
+
const style = sourceStyle.forceComplexScript ? {
|
|
122
|
+
...sourceStyle,
|
|
123
|
+
...resolveComplexScriptFormatting(sourceStyle)
|
|
124
|
+
} : sourceStyle;
|
|
120
125
|
const measuredText = applyTextTransform(text, style);
|
|
121
|
-
if (!style.letterSpacing && needsPerScriptFonts(style, measuredText)) return measureMixedScriptWidth(measuredText, style);
|
|
126
|
+
if (!sourceStyle.forceComplexScript && !style.letterSpacing && needsPerScriptFonts(style, measuredText)) return measureMixedScriptWidth(measuredText, style);
|
|
122
127
|
const ctx = getCanvasContext();
|
|
123
128
|
const font = buildFontString(style);
|
|
124
129
|
const letterSpacing = style.letterSpacing ?? 0;
|
|
@@ -156,9 +161,9 @@ function canvasMeasureTextWidth(text, style) {
|
|
|
156
161
|
* letter spacing, horizontal scale, transform, and the EA font so the segment
|
|
157
162
|
* takes the single-font path (no double-counting, no recursion).
|
|
158
163
|
*/
|
|
159
|
-
function glyphAdvanceStyle(style
|
|
164
|
+
function glyphAdvanceStyle(style) {
|
|
160
165
|
const result = {};
|
|
161
|
-
if (fontFamily !== void 0) result.fontFamily = fontFamily;
|
|
166
|
+
if (style.fontFamily !== void 0) result.fontFamily = style.fontFamily;
|
|
162
167
|
if (style.fontSize !== void 0) result.fontSize = style.fontSize;
|
|
163
168
|
if (style.bold !== void 0) result.bold = style.bold;
|
|
164
169
|
if (style.italic !== void 0) result.italic = style.italic;
|
|
@@ -179,19 +184,28 @@ function glyphAdvanceStyle(style, fontFamily) {
|
|
|
179
184
|
*/
|
|
180
185
|
function needsPerScriptFonts(style, measuredText) {
|
|
181
186
|
if (style.eastAsiaFontFamily && hasCjk(measuredText)) return true;
|
|
182
|
-
return
|
|
187
|
+
return hasComplexScriptFormatting(style) && hasComplexScript(measuredText);
|
|
183
188
|
}
|
|
184
|
-
/**
|
|
185
|
-
function
|
|
186
|
-
if (script === SCRIPT_CLASS.eastAsia)
|
|
187
|
-
|
|
188
|
-
|
|
189
|
+
/** Resolve the independent font/style slot selected by a script segment. */
|
|
190
|
+
function scriptStyle(style, script) {
|
|
191
|
+
if (script === SCRIPT_CLASS.eastAsia) {
|
|
192
|
+
if (style.eastAsiaFontFamily === void 0) return style;
|
|
193
|
+
return {
|
|
194
|
+
...style,
|
|
195
|
+
fontFamily: style.eastAsiaFontFamily
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
if (script === SCRIPT_CLASS.complex) return {
|
|
199
|
+
...style,
|
|
200
|
+
...resolveComplexScriptFormatting(style)
|
|
201
|
+
};
|
|
202
|
+
return style;
|
|
189
203
|
}
|
|
190
204
|
function measureMixedScriptWidth(measuredText, style) {
|
|
191
205
|
const letterSpacing = style.letterSpacing ?? 0;
|
|
192
206
|
const horizontalScale = getHorizontalScaleFactor(style);
|
|
193
207
|
let glyphWidth = 0;
|
|
194
|
-
for (const segment of segmentByScript(measuredText)) glyphWidth += canvasMeasureTextWidth(segment.text, glyphAdvanceStyle(
|
|
208
|
+
for (const segment of segmentByScript(measuredText)) glyphWidth += canvasMeasureTextWidth(segment.text, glyphAdvanceStyle(scriptStyle(style, segment.script)));
|
|
195
209
|
let width = glyphWidth;
|
|
196
210
|
if (letterSpacing) {
|
|
197
211
|
const codePoints = countCodePoints(measuredText);
|
|
@@ -247,7 +261,11 @@ function canvasMeasureText(text, style) {
|
|
|
247
261
|
* @param style - Font styling properties
|
|
248
262
|
* @returns Run measurement with width and per-character widths
|
|
249
263
|
*/
|
|
250
|
-
function canvasMeasureRun(text,
|
|
264
|
+
function canvasMeasureRun(text, sourceStyle) {
|
|
265
|
+
const style = sourceStyle.forceComplexScript ? {
|
|
266
|
+
...sourceStyle,
|
|
267
|
+
...resolveComplexScriptFormatting(sourceStyle)
|
|
268
|
+
} : sourceStyle;
|
|
251
269
|
const metrics = canvasGetFontMetrics(style);
|
|
252
270
|
if (!text) return {
|
|
253
271
|
width: 0,
|
|
@@ -262,10 +280,7 @@ function canvasMeasureRun(text, style) {
|
|
|
262
280
|
...style,
|
|
263
281
|
fontFamily: style.eastAsiaFontFamily
|
|
264
282
|
}) : void 0;
|
|
265
|
-
const complexScriptFont = style
|
|
266
|
-
...style,
|
|
267
|
-
fontFamily: style.complexScriptFontFamily
|
|
268
|
-
}) : void 0;
|
|
283
|
+
const complexScriptFont = hasComplexScriptFormatting(style) && !style.letterSpacing ? buildFontString(scriptStyle(style, SCRIPT_CLASS.complex)) : void 0;
|
|
269
284
|
const letterSpacing = style.letterSpacing ?? 0;
|
|
270
285
|
const scale = getHorizontalScaleFactor(style);
|
|
271
286
|
const charWidths = [];
|
|
@@ -32,6 +32,10 @@ function buildRunFontStyle(run, fallbackFontFamily, fallbackFontSize) {
|
|
|
32
32
|
fontFamily: run.fontFamily ?? fallbackFontFamily,
|
|
33
33
|
...run.eastAsiaFontFamily !== void 0 ? { eastAsiaFontFamily: run.eastAsiaFontFamily } : {},
|
|
34
34
|
...run.complexScriptFontFamily !== void 0 ? { complexScriptFontFamily: run.complexScriptFontFamily } : {},
|
|
35
|
+
...run.complexScriptFontSize !== void 0 ? { complexScriptFontSize: run.complexScriptFontSize } : {},
|
|
36
|
+
...run.complexScriptBold !== void 0 ? { complexScriptBold: run.complexScriptBold } : {},
|
|
37
|
+
...run.complexScriptItalic !== void 0 ? { complexScriptItalic: run.complexScriptItalic } : {},
|
|
38
|
+
...run.forceComplexScript !== void 0 ? { forceComplexScript: run.forceComplexScript } : {},
|
|
35
39
|
fontSize,
|
|
36
40
|
...run.bold !== void 0 ? { bold: run.bold } : {},
|
|
37
41
|
...run.italic !== void 0 ? { italic: run.italic } : {},
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { calculateTabWidth, pixelsToTwips } from "../../prosemirror/utils/tabCalculator.js";
|
|
2
2
|
import { CJK_FALLBACK_FONT_FAMILY, isCjkFont } from "../../utils/fontResolver.js";
|
|
3
3
|
import { inlineImageBoundingBox } from "../../utils/rotationBoundingBox.js";
|
|
4
|
-
import { hasCjk } from "../../utils/scriptSegments.js";
|
|
4
|
+
import { hasCjk, hasComplexScript } from "../../utils/scriptSegments.js";
|
|
5
5
|
import { measuredLineAdvance } from "../lineFlow.js";
|
|
6
6
|
import { isFloatingImageRun } from "../types.js";
|
|
7
7
|
import { clampFloatingWrapMargins } from "./clampFloatingWrapMargins.js";
|
|
8
|
+
import { hasComplexScriptFormatting, resolveComplexScriptFormatting } from "./complexScriptFormatting.js";
|
|
8
9
|
import { resolveEffectiveLineBreakPolicy } from "./effectiveLineBreakPolicy.js";
|
|
9
10
|
import { getFloatingAvailableWidth, getFloatingMargins } from "./floatingZones.js";
|
|
10
11
|
import { defaultLineBreakProvider, getLineBreakProvider } from "./lineBreakProvider.js";
|
|
@@ -97,6 +98,15 @@ function cjkLineHeightStyle(run, baseStyle) {
|
|
|
97
98
|
fontFamily: CJK_FALLBACK_FONT_FAMILY
|
|
98
99
|
};
|
|
99
100
|
}
|
|
101
|
+
/** Pick the dominant typography metrics for a run containing complex script. */
|
|
102
|
+
function complexScriptLineHeightStyle(run, baseStyle) {
|
|
103
|
+
if (!hasComplexScriptFormatting(run) || !run.forceComplexScript && !hasComplexScript(run.text)) return baseStyle;
|
|
104
|
+
const complexStyle = {
|
|
105
|
+
...baseStyle,
|
|
106
|
+
...resolveComplexScriptFormatting(run)
|
|
107
|
+
};
|
|
108
|
+
return (complexStyle.fontSize ?? DEFAULT_FONT_SIZE) >= (baseStyle.fontSize ?? DEFAULT_FONT_SIZE) ? complexStyle : baseStyle;
|
|
109
|
+
}
|
|
100
110
|
/**
|
|
101
111
|
* Calculate typography metrics from font size and spacing settings
|
|
102
112
|
*
|
|
@@ -1018,7 +1028,7 @@ function measureParagraph(block, maxWidth, options) {
|
|
|
1018
1028
|
run: textRun
|
|
1019
1029
|
});
|
|
1020
1030
|
const breakPolicy = effectiveLineBreakPolicy.provider;
|
|
1021
|
-
const lineHeightStyle = cjkLineHeightStyle(textRun, style);
|
|
1031
|
+
const lineHeightStyle = cjkLineHeightStyle(textRun, complexScriptLineHeightStyle(textRun, style));
|
|
1022
1032
|
updateMaxFont(lineHeightStyle);
|
|
1023
1033
|
if (!text || text.length === 0) {
|
|
1024
1034
|
currentLine.toRun = runIndex;
|
|
@@ -1095,7 +1105,7 @@ function measureParagraph(block, maxWidth, options) {
|
|
|
1095
1105
|
}
|
|
1096
1106
|
}
|
|
1097
1107
|
if (fittingPrefix) {
|
|
1098
|
-
for (const segment of fittingPrefix.segments) updateMaxFont(cjkLineHeightStyle(segment.run, segment.style));
|
|
1108
|
+
for (const segment of fittingPrefix.segments) updateMaxFont(cjkLineHeightStyle(segment.run, complexScriptLineHeightStyle(segment.run, segment.style)));
|
|
1099
1109
|
currentLine.width += fittingPrefix.width + measureTextWidth("-", fittingPrefix.hyphenStyle);
|
|
1100
1110
|
currentLine.trailingWhitespaceWidth = 0;
|
|
1101
1111
|
currentLine.toRun = fittingPrefix.endRunIndex;
|
|
@@ -26,6 +26,10 @@ type FontStyle = {
|
|
|
26
26
|
* points. Same contract as `eastAsiaFontFamily`, over a different slot.
|
|
27
27
|
*/
|
|
28
28
|
complexScriptFontFamily?: string;
|
|
29
|
+
complexScriptFontSize?: number;
|
|
30
|
+
complexScriptBold?: boolean;
|
|
31
|
+
complexScriptItalic?: boolean;
|
|
32
|
+
forceComplexScript?: boolean;
|
|
29
33
|
fontSize?: number;
|
|
30
34
|
bold?: boolean;
|
|
31
35
|
italic?: boolean;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ColumnLayout, FOOTNOTE_SEPARATOR_HEIGHT, Fragment, Page, PageHeaderFooterRefs, PageMargins } from "./types.js";
|
|
1
|
+
import { ColumnLayout, FOOTNOTE_SEPARATOR_HEIGHT, Fragment, Page, PageHeaderFooterRefs, PageMargins, SectionPageNumbering } from "./types.js";
|
|
2
2
|
//#region src/layout-engine/paginator.d.ts
|
|
3
3
|
/**
|
|
4
4
|
* Current state of a page being laid out.
|
|
@@ -45,8 +45,10 @@ type PaginatorOptions = {
|
|
|
45
45
|
* first-page header) vs. its body pages. Pages 2+ use `margins`.
|
|
46
46
|
*/
|
|
47
47
|
firstPageMargins?: PageMargins;
|
|
48
|
-
/** Per-section body margins used on even
|
|
48
|
+
/** Per-section body margins used on even authored page numbers. */
|
|
49
49
|
sectionEvenPageMargins?: (PageMargins | undefined)[];
|
|
50
|
+
/** Page-number policy for the initial section. */
|
|
51
|
+
pageNumbering?: SectionPageNumbering;
|
|
50
52
|
/** Column configuration (optional). */
|
|
51
53
|
columns?: ColumnLayout;
|
|
52
54
|
/** Per-page footnote reserved heights (pageNumber → height in pixels). */
|
|
@@ -114,7 +116,7 @@ declare function createPaginator(options: PaginatorOptions): {
|
|
|
114
116
|
h: number;
|
|
115
117
|
}, newMargins?: PageMargins, applyImmediately?: boolean) => void;
|
|
116
118
|
/** Mark the next created page as the first page of a new section. */
|
|
117
|
-
startSection: (sectionIndex: number) => void;
|
|
119
|
+
startSection: (sectionIndex: number, pageNumbering?: SectionPageNumbering) => void;
|
|
118
120
|
};
|
|
119
121
|
type Paginator = ReturnType<typeof createPaginator>;
|
|
120
122
|
//#endregion
|
|
@@ -35,6 +35,9 @@ function createPaginator(options) {
|
|
|
35
35
|
let pendingMargins;
|
|
36
36
|
let currentSectionIndex = 0;
|
|
37
37
|
let currentSectionPageNumber = 0;
|
|
38
|
+
let currentPageNumbering = options.pageNumbering ?? { type: "continue" };
|
|
39
|
+
let nextLogicalPageNumber = currentPageNumbering.type === "restart" ? currentPageNumbering.start : 1;
|
|
40
|
+
let sectionStartPending = true;
|
|
38
41
|
const pages = [];
|
|
39
42
|
const states = [];
|
|
40
43
|
function getContentBottom() {
|
|
@@ -48,7 +51,7 @@ function createPaginator(options) {
|
|
|
48
51
|
}
|
|
49
52
|
function currentPageUsesActiveLayout(state) {
|
|
50
53
|
if (pendingPageSize || pendingMargins) return false;
|
|
51
|
-
return arePageSizesEqual(state.page.size, pageSize) && areMarginsEqual(state.page.margins, getPageMargins(state.page.number, state.page.
|
|
54
|
+
return arePageSizesEqual(state.page.size, pageSize) && areMarginsEqual(state.page.margins, getPageMargins(state.page.number, state.page.logicalNumber));
|
|
52
55
|
}
|
|
53
56
|
if (getContentHeight() <= 0) panic("Paginator: page size and margins yield no content area");
|
|
54
57
|
let columnWidths = calculateColumnWidths(pageSize.w, margins.left, margins.right, columns);
|
|
@@ -63,8 +66,8 @@ function createPaginator(options) {
|
|
|
63
66
|
if (getContentHeight() <= 0) panic("Paginator: section page size and margins yield no content area");
|
|
64
67
|
recalculateColumnWidths();
|
|
65
68
|
}
|
|
66
|
-
function getPageMargins(pageNumber,
|
|
67
|
-
const evenMargins =
|
|
69
|
+
function getPageMargins(pageNumber, logicalPageNumber) {
|
|
70
|
+
const evenMargins = logicalPageNumber % 2 === 0 ? options.sectionEvenPageMargins?.[currentSectionIndex] : void 0;
|
|
68
71
|
const pageMargins = pageNumber === 1 && options.firstPageMargins ? { ...options.firstPageMargins } : { ...evenMargins ?? margins };
|
|
69
72
|
if (options.mirrorMargins === true && pageNumber % 2 === 0) return {
|
|
70
73
|
...pageMargins,
|
|
@@ -79,7 +82,7 @@ function createPaginator(options) {
|
|
|
79
82
|
* Get X position for a given column index.
|
|
80
83
|
*/
|
|
81
84
|
function getColumnX(columnIndex) {
|
|
82
|
-
let x = states.at(-1)?.page.margins.left ?? getPageMargins(1,
|
|
85
|
+
let x = states.at(-1)?.page.margins.left ?? getPageMargins(1, nextLogicalPageNumber).left;
|
|
83
86
|
for (let index = 0; index < columnIndex; index++) x += (columnWidths[index] ?? columnWidths[0] ?? 0) + gapAfterColumn(columns, index);
|
|
84
87
|
return x;
|
|
85
88
|
}
|
|
@@ -89,14 +92,19 @@ function createPaginator(options) {
|
|
|
89
92
|
function createNewPage() {
|
|
90
93
|
if (pendingPageSize || pendingMargins) applyPendingLayout();
|
|
91
94
|
const pageNumber = pages.length + 1;
|
|
95
|
+
const logicalNumber = nextLogicalPageNumber;
|
|
96
|
+
nextLogicalPageNumber += 1;
|
|
97
|
+
sectionStartPending = false;
|
|
92
98
|
currentSectionPageNumber += 1;
|
|
93
|
-
const pageMargins = getPageMargins(pageNumber,
|
|
99
|
+
const pageMargins = getPageMargins(pageNumber, logicalNumber);
|
|
94
100
|
const topMargin = pageMargins.top;
|
|
95
101
|
const contentBottom = pageSize.h - pageMargins.bottom;
|
|
96
102
|
const footnoteHeight = options.footnoteReservedHeights?.get(pageNumber) ?? 0;
|
|
97
103
|
const pageContentBottom = contentBottom - footnoteHeight;
|
|
98
104
|
const page = {
|
|
99
105
|
number: pageNumber,
|
|
106
|
+
logicalNumber,
|
|
107
|
+
...currentPageNumbering.format === void 0 ? {} : { logicalNumberFormat: currentPageNumbering.format },
|
|
100
108
|
fragments: [],
|
|
101
109
|
margins: pageMargins,
|
|
102
110
|
size: { ...pageSize },
|
|
@@ -236,12 +244,12 @@ function createPaginator(options) {
|
|
|
236
244
|
*/
|
|
237
245
|
function forcePageBreak(breakOptions = {}) {
|
|
238
246
|
const current = states.at(-1);
|
|
239
|
-
if (breakOptions.coalesceBlankPage && current && current.page.fragments.length === 0 && current.cursorY === current.topMargin
|
|
247
|
+
if (breakOptions.coalesceBlankPage && current && current.page.fragments.length === 0 && current.cursorY === current.topMargin) {
|
|
240
248
|
if (current.page.sectionIndex !== currentSectionIndex) {
|
|
241
|
-
|
|
242
|
-
|
|
249
|
+
retargetCurrentBlankPage();
|
|
250
|
+
return current;
|
|
243
251
|
}
|
|
244
|
-
return current;
|
|
252
|
+
if (currentPageUsesActiveLayout(current)) return current;
|
|
245
253
|
}
|
|
246
254
|
return createNewPage();
|
|
247
255
|
}
|
|
@@ -249,7 +257,8 @@ function createPaginator(options) {
|
|
|
249
257
|
const current = states.at(-1);
|
|
250
258
|
if (!current || current.page.fragments.length > 0 || current.cursorY !== current.topMargin) return false;
|
|
251
259
|
if (pendingPageSize || pendingMargins) applyPendingLayout();
|
|
252
|
-
|
|
260
|
+
retargetSectionMetadata(current.page);
|
|
261
|
+
const pageMargins = getPageMargins(current.page.number, current.page.logicalNumber);
|
|
253
262
|
const topMargin = pageMargins.top;
|
|
254
263
|
const rawContentBottom = pageSize.h - pageMargins.bottom;
|
|
255
264
|
const footnoteHeight = options.footnoteReservedHeights?.get(current.page.number) ?? 0;
|
|
@@ -267,8 +276,6 @@ function createPaginator(options) {
|
|
|
267
276
|
current.contentBottom = rawContentBottom - footnoteHeight;
|
|
268
277
|
current.trailingSpacing = 0;
|
|
269
278
|
columnRegionTop = topMargin;
|
|
270
|
-
currentSectionPageNumber = 1;
|
|
271
|
-
applySectionMetadata(current.page);
|
|
272
279
|
return true;
|
|
273
280
|
}
|
|
274
281
|
/**
|
|
@@ -310,9 +317,23 @@ function createPaginator(options) {
|
|
|
310
317
|
pendingPageSize = void 0;
|
|
311
318
|
pendingMargins = void 0;
|
|
312
319
|
}
|
|
313
|
-
function startSection(sectionIndex) {
|
|
320
|
+
function startSection(sectionIndex, pageNumbering = { type: "continue" }) {
|
|
314
321
|
currentSectionIndex = sectionIndex;
|
|
315
322
|
currentSectionPageNumber = 0;
|
|
323
|
+
currentPageNumbering = pageNumbering;
|
|
324
|
+
sectionStartPending = true;
|
|
325
|
+
if (pageNumbering.type === "restart") nextLogicalPageNumber = pageNumbering.start;
|
|
326
|
+
}
|
|
327
|
+
function retargetSectionMetadata(page) {
|
|
328
|
+
currentSectionPageNumber = 1;
|
|
329
|
+
if (sectionStartPending && currentPageNumbering.type === "restart") {
|
|
330
|
+
page.logicalNumber = currentPageNumbering.start;
|
|
331
|
+
nextLogicalPageNumber = currentPageNumbering.start + 1;
|
|
332
|
+
}
|
|
333
|
+
sectionStartPending = false;
|
|
334
|
+
if (currentPageNumbering.format === void 0) delete page.logicalNumberFormat;
|
|
335
|
+
else page.logicalNumberFormat = currentPageNumbering.format;
|
|
336
|
+
applySectionMetadata(page);
|
|
316
337
|
}
|
|
317
338
|
function applySectionMetadata(page) {
|
|
318
339
|
page.sectionIndex = currentSectionIndex;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { OutlineStyleAttr } from "../types/documentEnumValues.js";
|
|
2
2
|
import { ImagePosition, ImageWrap, SdtProperties, SdtType, ShapeTextBody, TableCellFormatting, TableWidthType } from "@stll/docx-core/model";
|
|
3
3
|
declare namespace types_d_exports {
|
|
4
|
-
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 };
|
|
4
|
+
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, SectionPageNumbering, TabAlignment, TabRun, TabStop, TableBlock, TableCell, TableCellMeasure, TableFragment, TableMeasure, TableRow, TableRowMeasure, TextBoxBlock, TextBoxFlowAttrs, TextBoxFragment, TextBoxMeasure, TextRun, floatingTextBoxReservesBand, floatingTextBoxWrapsText, getTableRowLeadingWidth, isFloatingImageRun, isFloatingTextBoxBlock, isTextWrappingFloatingImageRun, tableColumnsArePinned };
|
|
5
5
|
}
|
|
6
6
|
/**
|
|
7
7
|
* Unique identifier for a block in the document.
|
|
@@ -46,6 +46,14 @@ type RunFormatting = {
|
|
|
46
46
|
* does NOT do and is why this slot has to reach layout at all.
|
|
47
47
|
*/
|
|
48
48
|
complexScriptFontFamily?: string;
|
|
49
|
+
/** Independent complex-script size (`w:szCs`), in points. */
|
|
50
|
+
complexScriptFontSize?: number;
|
|
51
|
+
/** Independent complex-script weight (`w:bCs`). */
|
|
52
|
+
complexScriptBold?: boolean;
|
|
53
|
+
/** Independent complex-script slant (`w:iCs`). */
|
|
54
|
+
complexScriptItalic?: boolean;
|
|
55
|
+
/** Force the complex-script formatting slot for every character (`w:cs`). */
|
|
56
|
+
forceComplexScript?: boolean;
|
|
49
57
|
/** Run language metadata resolved from `w:lang`. */
|
|
50
58
|
language?: {
|
|
51
59
|
val?: string;
|
|
@@ -689,9 +697,20 @@ type SectionBreakBlock = {
|
|
|
689
697
|
orientation?: "portrait" | "landscape";
|
|
690
698
|
margins?: PageMargins;
|
|
691
699
|
columns?: ColumnLayout;
|
|
700
|
+
/** Authored page-number continuation or restart policy for the section ending here. */
|
|
701
|
+
pageNumbering?: SectionPageNumbering;
|
|
692
702
|
/** Line pitch authored by the section properties that end at this boundary, in twips. */
|
|
693
703
|
documentGridLinePitchTwips?: number;
|
|
694
704
|
};
|
|
705
|
+
/** Normalized section page-number policy. An omitted OOXML start continues numbering. */
|
|
706
|
+
type SectionPageNumbering = {
|
|
707
|
+
type: "continue";
|
|
708
|
+
format?: string;
|
|
709
|
+
} | {
|
|
710
|
+
type: "restart";
|
|
711
|
+
start: number;
|
|
712
|
+
format?: string;
|
|
713
|
+
};
|
|
695
714
|
type PageHeaderFooterRefs = {
|
|
696
715
|
titlePg?: boolean;
|
|
697
716
|
evenAndOddHeaders?: boolean;
|
|
@@ -1057,8 +1076,12 @@ type PageMargins = {
|
|
|
1057
1076
|
* A rendered page containing positioned fragments.
|
|
1058
1077
|
*/
|
|
1059
1078
|
type Page = {
|
|
1060
|
-
/**
|
|
1079
|
+
/** Physical page number (1-indexed), used for navigation and page counts. */
|
|
1061
1080
|
number: number;
|
|
1081
|
+
/** Authored page number shown by PAGE fields. */
|
|
1082
|
+
logicalNumber: number;
|
|
1083
|
+
/** OOXML number format for this page's section. */
|
|
1084
|
+
logicalNumberFormat?: string;
|
|
1062
1085
|
/** Fragments positioned on this page. */
|
|
1063
1086
|
fragments: Fragment[];
|
|
1064
1087
|
/** Page margins. */
|
|
@@ -1167,8 +1190,10 @@ type LayoutOptions = {
|
|
|
1167
1190
|
* same extension.
|
|
1168
1191
|
*/
|
|
1169
1192
|
firstPageMargins?: PageMargins;
|
|
1170
|
-
/** Per-section body margins used on even
|
|
1193
|
+
/** Per-section body margins used on even authored page numbers. */
|
|
1171
1194
|
sectionEvenPageMargins?: (PageMargins | undefined)[];
|
|
1195
|
+
/** Page-number policy for the initial section. */
|
|
1196
|
+
pageNumbering?: SectionPageNumbering;
|
|
1172
1197
|
/** Body-level final section page size. */
|
|
1173
1198
|
finalPageSize?: {
|
|
1174
1199
|
w: number;
|
|
@@ -1178,6 +1203,8 @@ type LayoutOptions = {
|
|
|
1178
1203
|
finalMargins?: PageMargins;
|
|
1179
1204
|
/** Body-level final section column configuration. */
|
|
1180
1205
|
finalColumns?: ColumnLayout;
|
|
1206
|
+
/** Body-level final section page-number policy. */
|
|
1207
|
+
finalPageNumbering?: SectionPageNumbering;
|
|
1181
1208
|
/** Column configuration. */
|
|
1182
1209
|
columns?: ColumnLayout;
|
|
1183
1210
|
/** Gap between rendered pages (for UI). */
|
|
@@ -1353,4 +1380,4 @@ declare function tableColumnsArePinned(table: TableBlock): boolean;
|
|
|
1353
1380
|
/** Return the leading visual offset for a row with omitted grid columns. */
|
|
1354
1381
|
declare const getTableRowLeadingWidth: (row: TableRow, columnWidths: readonly number[]) => number;
|
|
1355
1382
|
//#endregion
|
|
1356
|
-
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 };
|
|
1383
|
+
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, SectionPageNumbering, 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 };
|
|
@@ -81,7 +81,8 @@ var LayoutPainter = class {
|
|
|
81
81
|
for (const i_item of pages) {
|
|
82
82
|
const page = i_item;
|
|
83
83
|
const context = {
|
|
84
|
-
pageNumber: page.
|
|
84
|
+
pageNumber: page.logicalNumber,
|
|
85
|
+
...page.logicalNumberFormat === void 0 ? {} : { pageNumberFormat: page.logicalNumberFormat },
|
|
85
86
|
totalPages: this.totalPages,
|
|
86
87
|
section: "body"
|
|
87
88
|
};
|