@stll/folio-core 0.22.3 → 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 (78) hide show
  1. package/dist/ai-edits/apply.d.ts +1 -1
  2. package/dist/controller/layoutPipeline.js +7 -3
  3. package/dist/document-operations.d.ts +4 -4
  4. package/dist/document-operations.js +17 -1
  5. package/dist/docx/blockContentParser.js +2 -47
  6. package/dist/docx/fieldParser.d.ts +1 -1
  7. package/dist/docx/fieldParser.js +12 -4
  8. package/dist/docx/numberingParser.d.ts +2 -11
  9. package/dist/docx/numberingParser.js +3 -79
  10. package/dist/docx/ooxmlCounterFormatter.d.ts +8 -0
  11. package/dist/docx/ooxmlCounterFormatter.js +134 -0
  12. package/dist/docx/rezip.d.ts +1 -1
  13. package/dist/docx/rezip.js +12 -31
  14. package/dist/docx/serializer/tableSerializer.js +1 -1
  15. package/dist/docx/tableParser.js +2 -1
  16. package/dist/fields/bookmarkPages.js +1 -1
  17. package/dist/fields/evaluateField.js +1 -1
  18. package/dist/fields/fieldContext.d.ts +2 -0
  19. package/dist/fields/resolveFieldValues.js +5 -2
  20. package/dist/layout-bridge/convert/headerFooterLayout.js +6 -0
  21. package/dist/layout-bridge/convert/toFlowBlocks.js +51 -109
  22. package/dist/layout-engine/index.d.ts +3 -2
  23. package/dist/layout-engine/index.js +40 -16
  24. package/dist/layout-engine/measure/cache.js +1 -1
  25. package/dist/layout-engine/measure/complexScriptFormatting.d.ts +18 -0
  26. package/dist/layout-engine/measure/complexScriptFormatting.js +11 -0
  27. package/dist/layout-engine/measure/measureContainer.js +31 -16
  28. package/dist/layout-engine/measure/measureHelpers.js +4 -0
  29. package/dist/layout-engine/measure/measureParagraph.js +13 -3
  30. package/dist/layout-engine/measure/measureTypes.d.ts +4 -0
  31. package/dist/layout-engine/paginator.d.ts +5 -3
  32. package/dist/layout-engine/paginator.js +34 -13
  33. package/dist/layout-engine/types.d.ts +31 -4
  34. package/dist/layout-painter/imageLayout.d.ts +1 -1
  35. package/dist/layout-painter/index.js +2 -1
  36. package/dist/layout-painter/renderPage.js +9 -3
  37. package/dist/layout-painter/renderParagraph.js +42 -25
  38. package/dist/layout-painter/renderUtils.d.ts +2 -0
  39. package/dist/managers/AutoSaveManager.js +8 -40
  40. package/dist/managers/TableSelectionManager.d.ts +1 -1
  41. package/dist/managers/autoSaveCodec.d.ts +17 -0
  42. package/dist/managers/autoSaveCodec.js +109 -0
  43. package/dist/paged-layout/sectionGeometry.d.ts +3 -2
  44. package/dist/paged-layout/sectionGeometry.js +17 -1
  45. package/dist/prosemirror/attrs/index.js +278 -25
  46. package/dist/prosemirror/commands/comments.js +3 -3
  47. package/dist/prosemirror/commands/formatting.js +19 -19
  48. package/dist/prosemirror/commands/index.d.ts +2 -2
  49. package/dist/prosemirror/commands/paragraph.js +32 -32
  50. package/dist/prosemirror/commands/propertyChangeScope.d.ts +4 -2
  51. package/dist/prosemirror/commands/propertyChangeScope.js +8 -8
  52. package/dist/prosemirror/commands/table.d.ts +10 -52
  53. package/dist/prosemirror/commands/table.js +34 -34
  54. package/dist/prosemirror/conversion/fromProseDoc.js +24 -33
  55. package/dist/prosemirror/conversion/sdtAttrs.d.ts +2 -1
  56. package/dist/prosemirror/conversion/sdtAttrs.js +9 -4
  57. package/dist/prosemirror/conversion/toProseDoc.js +4 -1
  58. package/dist/prosemirror/extensions/ExtensionManager.d.ts +6 -3
  59. package/dist/prosemirror/extensions/ExtensionManager.js +5 -3
  60. package/dist/prosemirror/extensions/core/ParagraphExtension.d.ts +1 -1
  61. package/dist/prosemirror/extensions/features/ListExtension.js +4 -4
  62. package/dist/prosemirror/extensions/marks/RunFormattingOverrideExtension.d.ts +3 -2
  63. package/dist/prosemirror/extensions/marks/RunFormattingOverrideExtension.js +31 -17
  64. package/dist/prosemirror/extensions/nodes/ShapeExtension.d.ts +1 -1
  65. package/dist/prosemirror/extensions/nodes/TableExtension.js +1 -0
  66. package/dist/prosemirror/extensions/nodes/TextBoxExtension.d.ts +1 -1
  67. package/dist/prosemirror/extensions/types.d.ts +134 -1
  68. package/dist/prosemirror/index.d.ts +2 -2
  69. package/dist/prosemirror/plugins/selectionTracker.js +25 -99
  70. package/dist/prosemirror/revisionCarriers.js +3 -1
  71. package/dist/prosemirror/schema/index.d.ts +2 -2
  72. package/dist/prosemirror/schema/index.js +87 -0
  73. package/dist/prosemirror/schema/marks.d.ts +8 -0
  74. package/dist/prosemirror/schema/nodes.d.ts +15 -2
  75. package/dist/prosemirror/selectionState.d.ts +11 -5
  76. package/dist/prosemirror/selectionState.js +94 -57
  77. package/dist/utils/tableOperations.d.ts +7 -6
  78. package/package.json +3 -3
@@ -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 };
@@ -1,7 +1,7 @@
1
1
  import { FlowBlock, Measure } from "../layout-engine/types.js";
2
+ import { ImageAttrs } from "../prosemirror/schema/nodes.js";
2
3
  import { BlockLookup } from "./index.js";
3
4
  import { WrapType } from "../docx/wrapTypes.js";
4
- import { ImageAttrs } from "../prosemirror/schema/nodes.js";
5
5
  //#region src/layout-painter/imageLayout.d.ts
6
6
  /**
7
7
  * Anchored wrap-type targets — the OOXML wrap types except `inline`.
@@ -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
  };
@@ -601,6 +601,7 @@ function renderFootnoteContent(footnote, contentWidth, doc, context) {
601
601
  wrapper.style.height = `${content.height}px`;
602
602
  const renderContext = {
603
603
  pageNumber: context?.pageNumber ?? 0,
604
+ ...context?.pageNumberFormat === void 0 ? {} : { pageNumberFormat: context.pageNumberFormat },
604
605
  totalPages: context?.totalPages ?? 0,
605
606
  section: context?.section ?? "body",
606
607
  contentWidth
@@ -1133,8 +1134,7 @@ function applySectionHeaderFooterOptions(page, pageOptions, options) {
1133
1134
  if (!refs) return false;
1134
1135
  const isFirstSectionPage = page.sectionPageNumber === 1;
1135
1136
  const useFirst = refs.titlePg === true && isFirstSectionPage;
1136
- const sectionPageNumber = page.sectionPageNumber ?? page.number;
1137
- const useEven = refs.evenAndOddHeaders === true && sectionPageNumber % 2 === 0;
1137
+ const useEven = refs.evenAndOddHeaders === true && page.logicalNumber % 2 === 0;
1138
1138
  const headerRId = (() => {
1139
1139
  if (useFirst) return refs.headerFirst;
1140
1140
  if (useEven) return refs.headerEven;
@@ -1176,7 +1176,8 @@ function sectionPagesContext(page, counts) {
1176
1176
  */
1177
1177
  function buildPageRenderArgs(page, totalPages, options) {
1178
1178
  const context = {
1179
- pageNumber: page.number,
1179
+ pageNumber: page.logicalNumber,
1180
+ ...page.logicalNumberFormat === void 0 ? {} : { pageNumberFormat: page.logicalNumberFormat },
1180
1181
  totalPages,
1181
1182
  section: "body",
1182
1183
  ...options.bookmarkPages ? { bookmarkPages: options.bookmarkPages } : {},
@@ -1224,6 +1225,7 @@ function computePageFingerprintInternal(page, blockLookup, options) {
1224
1225
  parts.push(`m:${pageMarginsFingerprint(page.margins)}`);
1225
1226
  if (page.authoredMargins) parts.push(`am:${pageMarginsFingerprint(page.authoredMargins)}`);
1226
1227
  parts.push(`n:${page.number}`);
1228
+ parts.push(`ln:${page.logicalNumber},${page.logicalNumberFormat ?? ""}`);
1227
1229
  if (page.sectionIndex !== void 0) parts.push(`si:${page.sectionIndex}`);
1228
1230
  if (page.sectionPageNumber !== void 0) parts.push(`sp:${page.sectionPageNumber}`);
1229
1231
  if (page.headerFooterRefs) parts.push(`hf:${JSON.stringify(page.headerFooterRefs)}`);
@@ -1334,6 +1336,10 @@ function runContentKey(run) {
1334
1336
  if (run.highlight) parts.push(`hi:${run.highlight}`);
1335
1337
  if (run.fontFamily) parts.push(`ff:${run.fontFamily}`);
1336
1338
  if (run.complexScriptFontFamily) parts.push(`cs:${run.complexScriptFontFamily}`);
1339
+ if (run.complexScriptFontSize !== void 0) parts.push(`cs-size:${run.complexScriptFontSize}`);
1340
+ if (run.complexScriptBold !== void 0) parts.push(`cs-bold:${run.complexScriptBold}`);
1341
+ if (run.complexScriptItalic !== void 0) parts.push(`cs-italic:${run.complexScriptItalic}`);
1342
+ if (run.forceComplexScript !== void 0) parts.push(`cs-force:${run.forceComplexScript}`);
1337
1343
  if (run.eastAsiaFontFamily) parts.push(`ea:${run.eastAsiaFontFamily}`);
1338
1344
  if (run.fontSize !== void 0) parts.push(`fs:${run.fontSize}`);
1339
1345
  if (run.letterSpacing !== void 0) parts.push(`ls:${run.letterSpacing}`);
@@ -1,6 +1,7 @@
1
1
  import { ommlToMathml } from "../docx/mathToMathml.js";
2
2
  import { parseXmlDocument } from "../docx/xmlParser.js";
3
3
  import { evaluateFieldInstruction } from "../fields/evaluateField.js";
4
+ import { hasComplexScriptFormatting, resolveComplexScriptFormatting } from "../layout-engine/measure/complexScriptFormatting.js";
4
5
  import { getListMarkerInlineWidth, getListMarkerVisualOffset } from "../layout-engine/measure/listMarkerWidth.js";
5
6
  import { DOCX_SCRIPT_FONT_SCALE } from "../layout-engine/measure/measureHelpers.js";
6
7
  import { FONT_KERNING_MODE, countCompressibleSpaces, getFontKerningMode, getRunFontKerningMode, toPaintedText } from "../layout-engine/measure/textMeasurementPolicy.js";
@@ -571,6 +572,7 @@ function resolveFieldText(run, context) {
571
572
  if (!context) return fallback;
572
573
  const fieldContext = {
573
574
  pageNumber: context.pageNumber,
575
+ ...context.pageNumberFormat === void 0 ? {} : { pageNumberFormat: context.pageNumberFormat },
574
576
  totalPages: context.totalPages,
575
577
  bookmarkPages: context.bookmarkPages ?? EMPTY_BOOKMARK_PAGES,
576
578
  bookmarkText: context.bookmarkText ?? EMPTY_BOOKMARK_TEXT,
@@ -591,6 +593,10 @@ function renderFieldRun(run, doc, context) {
591
593
  kind: "text",
592
594
  text
593
595
  };
596
+ if (resolvedRun.forceComplexScript && hasComplexScriptFormatting(resolvedRun)) return renderTextRun({
597
+ ...resolvedRun,
598
+ ...resolveComplexScriptFormatting(resolvedRun)
599
+ }, doc);
594
600
  if (needsPerScriptSpans({
595
601
  ...resolvedRun,
596
602
  text
@@ -607,7 +613,7 @@ function renderFieldRun(run, doc, context) {
607
613
  const segmentRun = {
608
614
  ...segmentBase,
609
615
  text: segment.text,
610
- ...scriptFontOverride(resolvedRun, segment.script)
616
+ ...scriptFormattingOverride(resolvedRun, segment.script)
611
617
  };
612
618
  wrapper.append(renderTextRun(segmentRun, doc));
613
619
  }
@@ -725,9 +731,9 @@ function sliceRunsForLine(block, line) {
725
731
  * The font a script segment paints with, mirroring the measurer's
726
732
  * `scriptFontFamily`. Returning undefined leaves the run's own `fontFamily`.
727
733
  */
728
- const scriptFontOverride = (run, script) => {
734
+ const scriptFormattingOverride = (run, script) => {
735
+ if (run.forceComplexScript || script === SCRIPT_CLASS.complex) return resolveComplexScriptFormatting(run);
729
736
  if (script === SCRIPT_CLASS.eastAsia && run.eastAsiaFontFamily !== void 0) return { fontFamily: run.eastAsiaFontFamily };
730
- if (script === SCRIPT_CLASS.complex && run.complexScriptFontFamily !== void 0) return { fontFamily: run.complexScriptFontFamily };
731
737
  return {};
732
738
  };
733
739
  /**
@@ -739,7 +745,7 @@ const scriptFontOverride = (run, script) => {
739
745
  const needsPerScriptSpans = (run) => {
740
746
  if (run.letterSpacing) return false;
741
747
  if (run.eastAsiaFontFamily !== void 0 && hasCjk(run.text)) return true;
742
- return run.complexScriptFontFamily !== void 0 && hasComplexScript(run.text);
748
+ return hasComplexScriptFormatting(run) && hasComplexScript(run.text);
743
749
  };
744
750
  /**
745
751
  * Split each text run carrying an East-Asian font into per-script sub-runs, so
@@ -753,6 +759,13 @@ const needsPerScriptSpans = (run) => {
753
759
  function splitTextRunsByEastAsia(runs) {
754
760
  const result = [];
755
761
  for (const run of runs) {
762
+ if (isTextRun(run) && run.forceComplexScript && hasComplexScriptFormatting(run)) {
763
+ result.push({
764
+ ...run,
765
+ ...resolveComplexScriptFormatting(run)
766
+ });
767
+ continue;
768
+ }
756
769
  if (!isTextRun(run) || !needsPerScriptSpans(run)) {
757
770
  result.push(run);
758
771
  continue;
@@ -762,7 +775,7 @@ function splitTextRunsByEastAsia(runs) {
762
775
  result.push({
763
776
  ...run,
764
777
  text: segment.text,
765
- ...scriptFontOverride(run, segment.script),
778
+ ...scriptFormattingOverride(run, segment.script),
766
779
  ...run.pmStart !== void 0 ? {
767
780
  pmStart: run.pmStart + offset,
768
781
  pmEnd: run.pmStart + offset + segment.text.length
@@ -866,6 +879,10 @@ function runMeasureStyle(run) {
866
879
  ...run.smallCaps !== void 0 ? { smallCaps: run.smallCaps } : {},
867
880
  ...run.eastAsiaFontFamily !== void 0 ? { eastAsiaFontFamily: run.eastAsiaFontFamily } : {},
868
881
  ...run.complexScriptFontFamily !== void 0 ? { complexScriptFontFamily: run.complexScriptFontFamily } : {},
882
+ ...run.complexScriptFontSize !== void 0 ? { complexScriptFontSize: run.complexScriptFontSize } : {},
883
+ ...run.complexScriptBold !== void 0 ? { complexScriptBold: run.complexScriptBold } : {},
884
+ ...run.complexScriptItalic !== void 0 ? { complexScriptItalic: run.complexScriptItalic } : {},
885
+ ...run.forceComplexScript !== void 0 ? { forceComplexScript: run.forceComplexScript } : {},
869
886
  kerning: getRunFontKerningMode(run, 11) === FONT_KERNING_MODE.enabled
870
887
  };
871
888
  }
@@ -972,27 +989,32 @@ function createTextMeasurer(doc) {
972
989
  return (text, fontSize = 11, fontFamily = "Calibri", style = {}) => {
973
990
  if (!ctx) return applyLetterSpacingToMeasuredWidth(text.length * 7, text, style.letterSpacing);
974
991
  ctx.fontKerning = getFontKerningMode(style);
975
- const cssFallback = resolveFontFamily(fontFamily).cssFallback;
976
- const fontSizePx = fontSize * 96 / 72;
977
- const fontPrefixParts = [];
978
- if (style.italic) fontPrefixParts.push("italic");
979
- if (style.smallCaps) fontPrefixParts.push("small-caps");
980
- if (style.bold) fontPrefixParts.push("700");
981
- fontPrefixParts.push(`${fontSizePx}px`);
982
- const fontPrefix = fontPrefixParts.join(" ");
983
- const perScriptFallback = {
984
- ...style.eastAsiaFontFamily ? { [SCRIPT_CLASS.eastAsia]: resolveFontFamily(style.eastAsiaFontFamily).cssFallback } : {},
985
- ...style.complexScriptFontFamily ? { [SCRIPT_CLASS.complex]: resolveFontFamily(style.complexScriptFontFamily).cssFallback } : {}
992
+ const fontString = (family, size, bold, italic) => {
993
+ const fontPrefixParts = [];
994
+ if (italic) fontPrefixParts.push("italic");
995
+ if (style.smallCaps) fontPrefixParts.push("small-caps");
996
+ if (bold) fontPrefixParts.push("700");
997
+ fontPrefixParts.push(`${size * 96 / 72}px`);
998
+ return `${fontPrefixParts.join(" ")} ${resolveFontFamily(family).cssFallback}`;
986
999
  };
987
- if (!style.letterSpacing && (style.eastAsiaFontFamily && hasCjk(text) || style.complexScriptFontFamily && hasComplexScript(text))) {
1000
+ const baseFont = fontString(fontFamily, fontSize, style.bold, style.italic);
1001
+ const complex = resolveComplexScriptFormatting(style);
1002
+ const complexFont = fontString(complex.fontFamily ?? fontFamily, complex.fontSize ?? fontSize, complex.bold ?? style.bold, complex.italic ?? style.italic);
1003
+ if (style.forceComplexScript) {
1004
+ ctx.font = complexFont;
1005
+ return applyLetterSpacingToMeasuredWidth(ctx.measureText(text).width, text, style.letterSpacing);
1006
+ }
1007
+ if (!style.letterSpacing && (style.eastAsiaFontFamily && hasCjk(text) || hasComplexScriptFormatting(style) && hasComplexScript(text))) {
988
1008
  let segmentedWidth = 0;
989
1009
  for (const segment of segmentByScript(text)) {
990
- ctx.font = `${fontPrefix} ${perScriptFallback[segment.script] ?? cssFallback}`;
1010
+ if (segment.script === SCRIPT_CLASS.eastAsia && style.eastAsiaFontFamily) ctx.font = fontString(style.eastAsiaFontFamily, fontSize, style.bold, style.italic);
1011
+ else if (segment.script === SCRIPT_CLASS.complex) ctx.font = complexFont;
1012
+ else ctx.font = baseFont;
991
1013
  segmentedWidth += ctx.measureText(segment.text).width;
992
1014
  }
993
1015
  return segmentedWidth;
994
1016
  }
995
- ctx.font = `${fontPrefix} ${cssFallback}`;
1017
+ ctx.font = baseFont;
996
1018
  return applyLetterSpacingToMeasuredWidth(ctx.measureText(text).width, text, style.letterSpacing);
997
1019
  };
998
1020
  }
@@ -1180,12 +1202,7 @@ function renderLine(block, line, alignment, doc, options) {
1180
1202
  if (!measureText) continue;
1181
1203
  const fontSize = run.fontSize || 11;
1182
1204
  const fontFamily = run.fontFamily || "Calibri";
1183
- const measuredWidth = measureText(run.allCaps ? run.text.toLocaleUpperCase() : run.text, fontSize, fontFamily, {
1184
- ...run.bold !== void 0 ? { bold: run.bold } : {},
1185
- ...run.italic !== void 0 ? { italic: run.italic } : {},
1186
- ...run.letterSpacing !== void 0 ? { letterSpacing: run.letterSpacing } : {},
1187
- ...run.smallCaps !== void 0 ? { smallCaps: run.smallCaps } : {}
1188
- });
1205
+ const measuredWidth = measureText(run.allCaps ? run.text.toLocaleUpperCase() : run.text, fontSize, fontFamily, runMeasureStyle(run));
1189
1206
  reserveScaledAdvance(runEl, measuredWidth, run.horizontalScale);
1190
1207
  currentX += measuredWidth * ((run.horizontalScale ?? 100) / 100);
1191
1208
  } else if (isImageRun(run)) {
@@ -16,6 +16,8 @@ declare function resolveImageLineAlign(imageRun: ImageRun, paragraphAlignment: "
16
16
  type RenderContext = {
17
17
  /** Current page number (1-indexed) */
18
18
  pageNumber: number;
19
+ /** OOXML section-level format for the current logical page number. */
20
+ pageNumberFormat?: string;
19
21
  /** Total number of pages */
20
22
  totalPages: number;
21
23
  /** Which section is being rendered */
@@ -1,10 +1,10 @@
1
1
  import { Subscribable } from "./Subscribable.js";
2
+ import { decodeAutoSaveEnvelope, encodeAutoSaveDocument, encodeAutoSaveEnvelopeFromDocument } from "./autoSaveCodec.js";
2
3
  //#region src/managers/AutoSaveManager.ts
3
4
  const DEFAULT_STORAGE_KEY = "docx-editor-autosave";
4
5
  const DEFAULT_INTERVAL = 3e4;
5
6
  const DEFAULT_MAX_AGE = 1440 * 60 * 1e3;
6
7
  const DEFAULT_DEBOUNCE_DELAY = 2e3;
7
- const SAVE_VERSION = 1;
8
8
  function isLocalStorageAvailable() {
9
9
  try {
10
10
  const testKey = "__docx_editor_test__";
@@ -16,31 +16,7 @@ function isLocalStorageAvailable() {
16
16
  }
17
17
  }
18
18
  function serializeForStorage(document) {
19
- return JSON.stringify({
20
- ...document,
21
- originalBuffer: null
22
- });
23
- }
24
- function isDocumentLike(value) {
25
- return typeof value === "object" && value !== null && "package" in value;
26
- }
27
- function parseSavedData(json) {
28
- let parsed;
29
- try {
30
- parsed = JSON.parse(json);
31
- } catch {
32
- return null;
33
- }
34
- if (typeof parsed !== "object" || parsed === null) return null;
35
- if (!("document" in parsed) || !("savedAt" in parsed) || !("version" in parsed)) return null;
36
- const { document, savedAt, version } = parsed;
37
- if (typeof savedAt !== "string" || typeof version !== "number") return null;
38
- if (!isDocumentLike(document)) return null;
39
- return {
40
- document,
41
- savedAt,
42
- version
43
- };
19
+ return encodeAutoSaveDocument(document);
44
20
  }
45
21
  function isStale(savedAt, maxAge) {
46
22
  const savedTime = new Date(savedAt).getTime();
@@ -99,12 +75,12 @@ var AutoSaveManager = class extends Subscribable {
99
75
  this.updateStatus("saving");
100
76
  try {
101
77
  const serialized = serializeForStorage(doc);
102
- if (serialized === this.lastSavedJson) {
78
+ if (serialized.json === this.lastSavedJson) {
103
79
  this.updateStatus("saved");
104
80
  return true;
105
81
  }
106
- this.persistToStorage(doc);
107
- this.lastSavedJson = serialized;
82
+ this.persistToStorage(serialized);
83
+ this.lastSavedJson = serialized.json;
108
84
  const saveTime = /* @__PURE__ */ new Date();
109
85
  this.lastSaveTime = saveTime;
110
86
  this.updateStatus("saved");
@@ -133,7 +109,7 @@ var AutoSaveManager = class extends Subscribable {
133
109
  if (!this.storageAvailable) return null;
134
110
  const savedJson = localStorage.getItem(this.storageKey);
135
111
  if (!savedJson) return null;
136
- const savedData = parseSavedData(savedJson);
112
+ const savedData = decodeAutoSaveEnvelope(savedJson);
137
113
  if (!savedData) return null;
138
114
  if (isStale(savedData.savedAt, this.maxAge)) {
139
115
  this.clear();
@@ -179,7 +155,7 @@ var AutoSaveManager = class extends Subscribable {
179
155
  destroy() {
180
156
  this.stopTimers();
181
157
  if (this.isEnabled && this.currentDocument && this.storageAvailable) try {
182
- this.persistToStorage(this.currentDocument);
158
+ this.persistToStorage(serializeForStorage(this.currentDocument));
183
159
  } catch (error) {
184
160
  this.onErrorCallback?.(error instanceof Error ? error : new Error(String(error)));
185
161
  }
@@ -194,15 +170,7 @@ var AutoSaveManager = class extends Subscribable {
194
170
  }
195
171
  }
196
172
  persistToStorage(document) {
197
- const dataToSave = {
198
- document: {
199
- ...document,
200
- originalBuffer: null
201
- },
202
- savedAt: (/* @__PURE__ */ new Date()).toISOString(),
203
- version: SAVE_VERSION
204
- };
205
- localStorage.setItem(this.storageKey, JSON.stringify(dataToSave));
173
+ localStorage.setItem(this.storageKey, encodeAutoSaveEnvelopeFromDocument(document, (/* @__PURE__ */ new Date()).toISOString()));
206
174
  }
207
175
  debounceSave() {
208
176
  if (this.debounceTimer) clearTimeout(this.debounceTimer);
@@ -1,7 +1,7 @@
1
1
  import { document_d_exports } from "../types/document.js";
2
+ import { TableAction, TableContext } from "../utils/tableOperations.js";
2
3
  import { Subscribable } from "./Subscribable.js";
3
4
  import { CellCoordinates } from "./types.js";
4
- import { TableAction, TableContext } from "../utils/tableOperations.js";
5
5
  //#region src/managers/TableSelectionManager.d.ts
6
6
  /** Data attributes for table elements in the rendered DOM */
7
7
  declare const TABLE_DATA_ATTRIBUTES: {
@@ -0,0 +1,17 @@
1
+ import { document_d_exports } from "../types/document.js";
2
+ //#region src/managers/autoSaveCodec.d.ts
3
+ declare const AUTOSAVE_FORMAT_VERSION = 2;
4
+ type DecodedAutoSave = {
5
+ document: document_d_exports.Document;
6
+ savedAt: string;
7
+ version: typeof AUTOSAVE_FORMAT_VERSION;
8
+ };
9
+ type EncodedAutoSaveDocument = {
10
+ json: string;
11
+ };
12
+ declare const encodeAutoSaveDocument: (document: document_d_exports.Document) => EncodedAutoSaveDocument;
13
+ declare const encodeAutoSaveEnvelopeFromDocument: (document: EncodedAutoSaveDocument, savedAt: string) => string;
14
+ declare const encodeAutoSaveEnvelope: (document: document_d_exports.Document, savedAt: string) => string;
15
+ declare const decodeAutoSaveEnvelope: (json: string) => DecodedAutoSave | null;
16
+ //#endregion
17
+ export { AUTOSAVE_FORMAT_VERSION, DecodedAutoSave, EncodedAutoSaveDocument, decodeAutoSaveEnvelope, encodeAutoSaveDocument, encodeAutoSaveEnvelope, encodeAutoSaveEnvelopeFromDocument };