@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.
Files changed (43) hide show
  1. package/dist/controller/layoutPipeline.js +7 -3
  2. package/dist/docx/blockContentParser.js +2 -47
  3. package/dist/docx/fieldParser.d.ts +1 -1
  4. package/dist/docx/fieldParser.js +12 -4
  5. package/dist/docx/numberingParser.d.ts +2 -11
  6. package/dist/docx/numberingParser.js +3 -79
  7. package/dist/docx/ooxmlCounterFormatter.d.ts +8 -0
  8. package/dist/docx/ooxmlCounterFormatter.js +134 -0
  9. package/dist/docx/serializer/tableSerializer.js +1 -1
  10. package/dist/docx/tableParser.js +2 -1
  11. package/dist/fields/bookmarkPages.js +1 -1
  12. package/dist/fields/evaluateField.js +1 -1
  13. package/dist/fields/fieldContext.d.ts +2 -0
  14. package/dist/fields/resolveFieldValues.js +5 -2
  15. package/dist/layout-bridge/convert/headerFooterLayout.js +6 -0
  16. package/dist/layout-bridge/convert/toFlowBlocks.js +16 -48
  17. package/dist/layout-engine/index.d.ts +3 -2
  18. package/dist/layout-engine/index.js +40 -16
  19. package/dist/layout-engine/measure/cache.js +1 -1
  20. package/dist/layout-engine/measure/complexScriptFormatting.d.ts +18 -0
  21. package/dist/layout-engine/measure/complexScriptFormatting.js +11 -0
  22. package/dist/layout-engine/measure/measureContainer.js +31 -16
  23. package/dist/layout-engine/measure/measureHelpers.js +4 -0
  24. package/dist/layout-engine/measure/measureParagraph.js +13 -3
  25. package/dist/layout-engine/measure/measureTypes.d.ts +4 -0
  26. package/dist/layout-engine/paginator.d.ts +5 -3
  27. package/dist/layout-engine/paginator.js +34 -13
  28. package/dist/layout-engine/types.d.ts +31 -4
  29. package/dist/layout-painter/index.js +2 -1
  30. package/dist/layout-painter/renderPage.js +9 -3
  31. package/dist/layout-painter/renderParagraph.js +42 -25
  32. package/dist/layout-painter/renderUtils.d.ts +2 -0
  33. package/dist/paged-layout/sectionGeometry.d.ts +3 -2
  34. package/dist/paged-layout/sectionGeometry.js +17 -1
  35. package/dist/prosemirror/attrs/index.js +5 -0
  36. package/dist/prosemirror/conversion/fromProseDoc.js +4 -16
  37. package/dist/prosemirror/conversion/toProseDoc.js +4 -1
  38. package/dist/prosemirror/extensions/marks/RunFormattingOverrideExtension.d.ts +3 -2
  39. package/dist/prosemirror/extensions/marks/RunFormattingOverrideExtension.js +31 -17
  40. package/dist/prosemirror/extensions/nodes/TableExtension.js +1 -0
  41. package/dist/prosemirror/schema/marks.d.ts +8 -0
  42. package/dist/prosemirror/schema/nodes.d.ts +2 -0
  43. 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) => block.rows[rowIndex]?.cells.some((cell) => {
478
- const firstBlock = cell.blocks.at(0);
479
- return firstBlock?.kind === "paragraph" && firstBlock.attrs?.renderedPageBreakBefore === true;
480
- }) ?? false;
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 hasVerticalMerges = block.rows.some((row) => row.cells.some((cell) => (cell.rowSpan ?? 1) > 1));
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 || hasVerticalMerges) return false;
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, style) {
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, fontFamily) {
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 Boolean(style.complexScriptFontFamily) && hasComplexScript(measuredText);
187
+ return hasComplexScriptFormatting(style) && hasComplexScript(measuredText);
183
188
  }
184
- /** The font slot a script class selects, falling back to the western one. */
185
- function scriptFontFamily(style, script) {
186
- if (script === SCRIPT_CLASS.eastAsia) return style.eastAsiaFontFamily ?? style.fontFamily;
187
- if (script === SCRIPT_CLASS.complex) return style.complexScriptFontFamily ?? style.fontFamily;
188
- return style.fontFamily;
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(style, scriptFontFamily(style, segment.script)));
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, style) {
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.complexScriptFontFamily !== void 0 && !style.letterSpacing ? buildFontString({
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 section pages. */
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.sectionPageNumber ?? currentSectionPageNumber));
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, sectionPageNumber) {
67
- const evenMargins = sectionPageNumber % 2 === 0 ? options.sectionEvenPageMargins?.[currentSectionIndex] : void 0;
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, 1).left;
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, currentSectionPageNumber);
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 && currentPageUsesActiveLayout(current)) {
247
+ if (breakOptions.coalesceBlankPage && current && current.page.fragments.length === 0 && current.cursorY === current.topMargin) {
240
248
  if (current.page.sectionIndex !== currentSectionIndex) {
241
- currentSectionPageNumber = 1;
242
- applySectionMetadata(current.page);
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
- const pageMargins = getPageMargins(current.page.number, 1);
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
- /** Page number (1-indexed). */
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 section pages. */
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.number,
84
+ pageNumber: page.logicalNumber,
85
+ ...page.logicalNumberFormat === void 0 ? {} : { pageNumberFormat: page.logicalNumberFormat },
85
86
  totalPages: this.totalPages,
86
87
  section: "body"
87
88
  };