@stll/folio-core 0.32.1 → 0.33.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (222) hide show
  1. package/dist/ai-edits/__fixtures__/paragraphs.d.ts +17 -0
  2. package/dist/ai-edits/__fixtures__/paragraphs.js +33 -0
  3. package/dist/ai-edits/apply.d.ts +49 -3
  4. package/dist/ai-edits/apply.js +692 -75
  5. package/dist/ai-edits/blockRange.d.ts +7 -3
  6. package/dist/ai-edits/blockRange.js +14 -6
  7. package/dist/ai-edits/headless.d.ts +39 -5
  8. package/dist/ai-edits/headless.js +76 -17
  9. package/dist/ai-edits/index.d.ts +6 -6
  10. package/dist/ai-edits/index.js +3 -3
  11. package/dist/ai-edits/read.d.ts +4 -0
  12. package/dist/ai-edits/read.js +30 -6
  13. package/dist/ai-edits/scoped-reading.js +4 -3
  14. package/dist/ai-edits/snapshot.d.ts +42 -2
  15. package/dist/ai-edits/snapshot.js +127 -62
  16. package/dist/ai-edits/table-row-column-mutations.d.ts +24 -1
  17. package/dist/ai-edits/table-row-column-mutations.js +98 -6
  18. package/dist/ai-edits/table-targets.d.ts +12 -3
  19. package/dist/ai-edits/table-targets.js +15 -5
  20. package/dist/ai-edits/types.d.ts +235 -7
  21. package/dist/ai-edits/word-diff.d.ts +54 -11
  22. package/dist/ai-edits/word-diff.js +221 -61
  23. package/dist/compare/__fixtures__/body-sequence.d.ts +33 -0
  24. package/dist/compare/__fixtures__/body-sequence.js +67 -0
  25. package/dist/compare/__fixtures__/nested-table.d.ts +16 -0
  26. package/dist/compare/__fixtures__/nested-table.js +50 -0
  27. package/dist/compare/__fixtures__/numbered-list.d.ts +34 -0
  28. package/dist/compare/__fixtures__/numbered-list.js +85 -0
  29. package/dist/compare/column-alignment.d.ts +27 -0
  30. package/dist/compare/column-alignment.js +162 -0
  31. package/dist/compare/compare.d.ts +107 -0
  32. package/dist/compare/compare.js +406 -0
  33. package/dist/compare/formatting.d.ts +29 -0
  34. package/dist/compare/formatting.js +84 -0
  35. package/dist/compare/plan.d.ts +22 -0
  36. package/dist/compare/plan.js +1059 -0
  37. package/dist/compare/reproducible-package.d.ts +17 -0
  38. package/dist/compare/reproducible-package.js +30 -0
  39. package/dist/compare/scenario.d.ts +77 -0
  40. package/dist/compare/scenario.js +263 -0
  41. package/dist/compare/types.d.ts +282 -0
  42. package/dist/compare/types.js +32 -0
  43. package/dist/compare/verification.d.ts +55 -0
  44. package/dist/compare/verification.js +146 -0
  45. package/dist/compat/eigenpal.d.ts +9 -6
  46. package/dist/compat/eigenpal.js +6 -3
  47. package/dist/controller/layoutPipeline.d.ts +13 -0
  48. package/dist/controller/layoutPipeline.js +15 -1
  49. package/dist/display-list/build/buildContext.d.ts +39 -0
  50. package/dist/display-list/build/buildContext.js +31 -0
  51. package/dist/display-list/build/buildDisplayList.d.ts +79 -0
  52. package/dist/display-list/build/buildDisplayList.js +283 -0
  53. package/dist/display-list/build/colors.d.ts +20 -0
  54. package/dist/display-list/build/colors.js +136 -0
  55. package/dist/display-list/build/floatingImages.d.ts +24 -0
  56. package/dist/display-list/build/floatingImages.js +41 -0
  57. package/dist/display-list/build/fontTable.d.ts +31 -0
  58. package/dist/display-list/build/fontTable.js +97 -0
  59. package/dist/display-list/build/furniture.d.ts +86 -0
  60. package/dist/display-list/build/furniture.js +98 -0
  61. package/dist/display-list/build/glyphs.d.ts +55 -0
  62. package/dist/display-list/build/glyphs.js +103 -0
  63. package/dist/display-list/build/headerFooterPrimitives.d.ts +17 -0
  64. package/dist/display-list/build/headerFooterPrimitives.js +40 -0
  65. package/dist/display-list/build/imagePrimitives.d.ts +35 -0
  66. package/dist/display-list/build/imagePrimitives.js +264 -0
  67. package/dist/display-list/build/pageBorderPrimitives.d.ts +23 -0
  68. package/dist/display-list/build/pageBorderPrimitives.js +111 -0
  69. package/dist/display-list/build/pageFurniture.d.ts +35 -0
  70. package/dist/display-list/build/pageFurniture.js +141 -0
  71. package/dist/display-list/build/paragraphPrimitives.d.ts +19 -0
  72. package/dist/display-list/build/paragraphPrimitives.js +819 -0
  73. package/dist/display-list/build/regions.d.ts +46 -0
  74. package/dist/display-list/build/regions.js +56 -0
  75. package/dist/display-list/build/storyPrimitives.d.ts +29 -0
  76. package/dist/display-list/build/storyPrimitives.js +279 -0
  77. package/dist/display-list/build/strokes.d.ts +19 -0
  78. package/dist/display-list/build/strokes.js +78 -0
  79. package/dist/display-list/build/tablePrimitives.d.ts +38 -0
  80. package/dist/display-list/build/tablePrimitives.js +409 -0
  81. package/dist/display-list/build/textBoxPrimitives.d.ts +14 -0
  82. package/dist/display-list/build/textBoxPrimitives.js +129 -0
  83. package/dist/display-list/build/textDecorations.d.ts +29 -0
  84. package/dist/display-list/build/textDecorations.js +29 -0
  85. package/dist/display-list/build/unsupported.d.ts +57 -0
  86. package/dist/display-list/build/unsupported.js +0 -0
  87. package/dist/display-list/build/watermarkPrimitives.d.ts +15 -0
  88. package/dist/display-list/build/watermarkPrimitives.js +128 -0
  89. package/dist/display-list/dom/renderDisplayListToDom.d.ts +26 -0
  90. package/dist/display-list/dom/renderDisplayListToDom.js +620 -0
  91. package/dist/display-list/editor/displayListPagePainter.d.ts +22 -0
  92. package/dist/display-list/editor/displayListPagePainter.js +63 -0
  93. package/dist/display-list/editor/pageRenderer.d.ts +24 -0
  94. package/dist/display-list/editor/pageRenderer.js +36 -0
  95. package/dist/display-list/primitives.d.ts +95 -0
  96. package/dist/display-list/primitives.js +127 -0
  97. package/dist/display-list/types.d.ts +472 -0
  98. package/dist/display-list/types.js +0 -0
  99. package/dist/document-operations.d.ts +42 -9
  100. package/dist/document-operations.js +171 -16
  101. package/dist/document-stories.d.ts +16 -0
  102. package/dist/document-stories.js +48 -4
  103. package/dist/docx/packageParts.d.ts +35 -0
  104. package/dist/docx/packageParts.js +120 -0
  105. package/dist/docx/paragraphParser.js +22 -22
  106. package/dist/docx/paragraphTraversal.js +1 -1
  107. package/dist/docx/renderedPageBreakNormalization.js +3 -1
  108. package/dist/docx/revisionIdNormalization.d.ts +13 -0
  109. package/dist/docx/revisionIdNormalization.js +103 -0
  110. package/dist/docx/rezip.js +26 -17
  111. package/dist/docx/selectiveSave.js +2 -2
  112. package/dist/docx/serializer/commentSerializer.js +6 -1
  113. package/dist/docx/serializer/paragraphSerializer.js +33 -4
  114. package/dist/docx/serializer/runSerializer.js +11 -10
  115. package/dist/docx/serializer/tableSerializer.js +2 -1
  116. package/dist/docx/server/applyDocxXmlPatchProposal.js +1 -1
  117. package/dist/docx/server/createBilingualDocx.js +3 -2
  118. package/dist/docx/streamingXmlParser.d.ts +13 -1
  119. package/dist/docx/streamingXmlParser.js +49 -11
  120. package/dist/docx/unzip.d.ts +1 -2
  121. package/dist/docx/unzip.js +25 -6
  122. package/dist/docx/xmlParser.d.ts +3 -1
  123. package/dist/docx/xmlParser.js +33 -27
  124. package/dist/export-pdf.d.ts +43 -0
  125. package/dist/export-pdf.js +98 -0
  126. package/dist/fonts/headlessMeasure.d.ts +76 -0
  127. package/dist/fonts/headlessMeasure.js +0 -0
  128. package/dist/fonts/sfnt/parse.d.ts +51 -0
  129. package/dist/fonts/sfnt/parse.js +525 -0
  130. package/dist/fonts/sfnt/subset.d.ts +20 -0
  131. package/dist/fonts/sfnt/subset.js +350 -0
  132. package/dist/fonts/sfnt/tables.d.ts +88 -0
  133. package/dist/fonts/sfnt/tables.js +131 -0
  134. package/dist/fonts/sfnt/woff.d.ts +14 -0
  135. package/dist/fonts/sfnt/woff.js +163 -0
  136. package/dist/generated/text_shaper.js +324 -0
  137. package/dist/generated/text_shaper_bg.wasm +0 -0
  138. package/dist/headless-layout.d.ts +75 -0
  139. package/dist/headless-layout.js +350 -0
  140. package/dist/index.d.ts +9 -6
  141. package/dist/index.js +6 -3
  142. package/dist/layout-bridge/convert/footnoteLayout.d.ts +1 -0
  143. package/dist/layout-bridge/convert/footnoteLayout.js +1 -0
  144. package/dist/layout-bridge/convert/headerFooterLayout.d.ts +1 -0
  145. package/dist/layout-bridge/convert/headerFooterLayout.js +2 -0
  146. package/dist/layout-bridge/convert/toFlowBlocks.d.ts +3 -1
  147. package/dist/layout-bridge/convert/toFlowBlocks.js +2 -0
  148. package/dist/layout-engine/measure/advanceComposition.d.ts +62 -0
  149. package/dist/layout-engine/measure/advanceComposition.js +148 -0
  150. package/dist/layout-engine/measure/cache.d.ts +2 -0
  151. package/dist/layout-engine/measure/measureContainer.js +13 -0
  152. package/dist/layout-engine/measure/measureParagraph.js +1 -2
  153. package/dist/layout-engine/measure/measureTypes.d.ts +12 -0
  154. package/dist/layout-engine/measure/tableInlinePlacement.d.ts +3 -2
  155. package/dist/layout-engine/measure/tableInlinePlacement.js +14 -1
  156. package/dist/layout-engine/tableIndentCompatibility.d.ts +5 -0
  157. package/dist/layout-engine/tableIndentCompatibility.js +15 -0
  158. package/dist/layout-engine/types.d.ts +8 -0
  159. package/dist/layout-painter/renderPage.d.ts +29 -1
  160. package/dist/layout-painter/renderPage.js +47 -19
  161. package/dist/layout-painter/renderParagraph.d.ts +21 -2
  162. package/dist/layout-painter/renderParagraph.js +1 -1
  163. package/dist/layout-painter/renderTable.js +37 -4
  164. package/dist/managers/editorShortcuts.d.ts +33 -1
  165. package/dist/managers/editorShortcuts.js +20 -1
  166. package/dist/markdown/renderRuns.js +7 -2
  167. package/dist/markdown/renderTable.js +5 -1
  168. package/dist/model.d.ts +3 -3
  169. package/dist/model.js +2 -2
  170. package/dist/pdf/contentStream.d.ts +73 -0
  171. package/dist/pdf/contentStream.js +143 -0
  172. package/dist/pdf/fonts.d.ts +96 -0
  173. package/dist/pdf/fonts.js +593 -0
  174. package/dist/pdf/images.d.ts +28 -0
  175. package/dist/pdf/images.js +439 -0
  176. package/dist/pdf/objects.d.ts +93 -0
  177. package/dist/pdf/objects.js +237 -0
  178. package/dist/pdf/pageSpace.d.ts +43 -0
  179. package/dist/pdf/pageSpace.js +57 -0
  180. package/dist/pdf/paint.d.ts +32 -0
  181. package/dist/pdf/paint.js +442 -0
  182. package/dist/pdf/writePdf.d.ts +56 -0
  183. package/dist/pdf/writePdf.js +345 -0
  184. package/dist/prosemirror/commands/comments.js +91 -6
  185. package/dist/prosemirror/conversion/fromProseDoc.js +41 -7
  186. package/dist/prosemirror/conversion/toProseDoc.js +41 -4
  187. package/dist/prosemirror/extensions/features/ParaIdAllocatorExtension.d.ts +15 -2
  188. package/dist/prosemirror/extensions/features/ParaIdAllocatorExtension.js +35 -6
  189. package/dist/prosemirror/extensions/features/ParagraphChangeTrackerExtension.js +2 -2
  190. package/dist/prosemirror/extensions/features/pasteCleanup.js +4 -3
  191. package/dist/prosemirror/extensions/nodes/BookmarkBoundaryExtension.d.ts +3 -1
  192. package/dist/prosemirror/extensions/nodes/BookmarkBoundaryExtension.js +5 -3
  193. package/dist/prosemirror/extensions/nodes/RenderedPageBreakExtension.d.ts +7 -1
  194. package/dist/prosemirror/extensions/nodes/RenderedPageBreakExtension.js +9 -3
  195. package/dist/prosemirror/extensions/nodes/TableExtension.js +56 -39
  196. package/dist/prosemirror/extensions/nodes/TextBoxAnchorExtension.d.ts +3 -1
  197. package/dist/prosemirror/extensions/nodes/TextBoxAnchorExtension.js +5 -3
  198. package/dist/prosemirror/plugins/suggestionMode.js +1 -0
  199. package/dist/prosemirror/revisionCarriers.js +19 -2
  200. package/dist/prosemirror/utils/extractTrackedChanges.d.ts +2 -2
  201. package/dist/prosemirror/utils/extractTrackedChanges.js +59 -23
  202. package/dist/prosemirror/validation.js +55 -27
  203. package/dist/prosemirror/zeroWidthAnchors.d.ts +10 -0
  204. package/dist/prosemirror/zeroWidthAnchors.js +23 -0
  205. package/dist/redline.js +12 -79
  206. package/dist/server.d.ts +4 -4
  207. package/dist/server.js +2 -2
  208. package/dist/shaping/placeRun.d.ts +51 -0
  209. package/dist/shaping/placeRun.js +84 -0
  210. package/dist/shaping/shaper.d.ts +92 -0
  211. package/dist/shaping/shaper.js +102 -0
  212. package/dist/types/block-id.d.ts +18 -2
  213. package/dist/types/block-id.js +49 -5
  214. package/dist/types/content.d.ts +2 -2
  215. package/dist/utils/clipboard.js +4 -3
  216. package/dist/utils/fontResolver.js +183 -0
  217. package/dist/utils/htmlComments.d.ts +17 -0
  218. package/dist/utils/htmlComments.js +22 -0
  219. package/dist/utils/scriptSegments.d.ts +12 -1
  220. package/dist/utils/scriptSegments.js +17 -1
  221. package/dist/version-comparison.d.ts +1 -1
  222. package/package.json +7 -3
@@ -0,0 +1,46 @@
1
+ import { BlockId } from "../../layout-engine/types.js";
2
+ import { DisplayHitRegion, DisplayHitRegionKind, DisplayHitRegionModel, DisplayPrimitive, DisplayRect, DisplayStoryRef } from "../types.js";
3
+ //#region src/display-list/build/regions.d.ts
4
+ type RegionDescriptor = {
5
+ readonly kind: DisplayHitRegionKind;
6
+ readonly rect: DisplayRect;
7
+ readonly model?: DisplayHitRegionModel;
8
+ };
9
+ type PageComposer = {
10
+ /** Append primitives to the page, in paint order. */
11
+ readonly push: (primitives: readonly DisplayPrimitive[]) => void;
12
+ /**
13
+ * Everything `paint` appends belongs to this region. Regions opened inside
14
+ * `paint` become its children, so the tree is the nesting of the walk.
15
+ */
16
+ readonly region: (descriptor: RegionDescriptor, paint: () => void) => void;
17
+ readonly primitives: () => readonly DisplayPrimitive[];
18
+ readonly regions: () => readonly DisplayHitRegion[];
19
+ };
20
+ declare const createPageComposer: () => PageComposer;
21
+ /**
22
+ * The region a whole block occupies, wherever it is painted.
23
+ *
24
+ * A body fragment, a picture inside a header, a text box inside a cell: each is
25
+ * one block at one place, and a surface asks the same questions of all of them.
26
+ * Taking the box and the range from the same fragment the painting uses is what
27
+ * keeps the answer the one that was drawn.
28
+ */
29
+ type BlockRegionOptions = {
30
+ readonly fragment: {
31
+ readonly blockId: BlockId;
32
+ readonly x: number;
33
+ readonly y: number;
34
+ readonly width: number;
35
+ readonly height: number;
36
+ readonly pmStart?: number;
37
+ readonly pmEnd?: number;
38
+ };
39
+ readonly kind: DisplayHitRegionKind;
40
+ readonly context: {
41
+ readonly story: DisplayStoryRef;
42
+ };
43
+ };
44
+ declare const blockRegion: ({ fragment, kind, context }: BlockRegionOptions) => RegionDescriptor;
45
+ //#endregion
46
+ export { PageComposer, RegionDescriptor, blockRegion, createPageComposer };
@@ -0,0 +1,56 @@
1
+ //#region src/display-list/build/regions.ts
2
+ const createPageComposer = () => {
3
+ const primitives = [];
4
+ const roots = [];
5
+ const open = [];
6
+ const push = (added) => {
7
+ primitives.push(...added);
8
+ };
9
+ const region = (descriptor, paint) => {
10
+ const entry = {
11
+ descriptor,
12
+ from: primitives.length,
13
+ children: []
14
+ };
15
+ open.push(entry);
16
+ try {
17
+ paint();
18
+ } finally {
19
+ open.pop();
20
+ }
21
+ const closed = {
22
+ kind: descriptor.kind,
23
+ rect: descriptor.rect,
24
+ from: entry.from,
25
+ to: primitives.length,
26
+ children: entry.children,
27
+ ...descriptor.model === void 0 ? {} : { model: descriptor.model }
28
+ };
29
+ (open.at(-1)?.children ?? roots).push(closed);
30
+ };
31
+ return {
32
+ push,
33
+ region,
34
+ primitives: () => primitives,
35
+ regions: () => roots
36
+ };
37
+ };
38
+ const blockRegion = ({ fragment, kind, context }) => ({
39
+ kind,
40
+ rect: {
41
+ xPx: fragment.x,
42
+ yPx: fragment.y,
43
+ widthPx: fragment.width,
44
+ heightPx: fragment.height
45
+ },
46
+ model: {
47
+ blockId: String(fragment.blockId),
48
+ ...fragment.pmStart === void 0 || fragment.pmEnd === void 0 ? {} : { pmRange: {
49
+ start: fragment.pmStart,
50
+ end: fragment.pmEnd,
51
+ story: context.story
52
+ } }
53
+ }
54
+ });
55
+ //#endregion
56
+ export { blockRegion, createPageComposer };
@@ -0,0 +1,29 @@
1
+ import { FlowBlock, Measure } from "../../layout-engine/types.js";
2
+ import { BuildContext } from "./buildContext.js";
3
+ import { PageComposer } from "./regions.js";
4
+ //#region src/display-list/build/storyPrimitives.d.ts
5
+ type PaintStoryOptions = {
6
+ readonly composer: PageComposer;
7
+ readonly blocks: readonly FlowBlock[];
8
+ readonly measures: readonly Measure[];
9
+ /** Page-absolute origin of the story's own content box. */
10
+ readonly xPx: number;
11
+ readonly yPx: number;
12
+ readonly widthPx: number;
13
+ readonly context: BuildContext;
14
+ readonly label: string;
15
+ };
16
+ /**
17
+ * A footnote body: every block stacks by its measured height, which is the
18
+ * height the paginator reserved the band from.
19
+ */
20
+ declare const paintFootnoteBlocks: ({ composer, blocks, measures, xPx, yPx, widthPx, context, label }: PaintStoryOptions) => void;
21
+ /**
22
+ * A header or footer story. Blocks that Word takes out of the flow (a floating
23
+ * table, an anchored image, a positioned text box) contribute no height, and
24
+ * they are reported rather than painted: their position resolves against page
25
+ * and margin anchors the display list producer does not carry.
26
+ */
27
+ declare const paintHeaderFooterBlocks: ({ composer, blocks, measures, xPx, yPx, widthPx, context, label }: PaintStoryOptions) => void;
28
+ //#endregion
29
+ export { PaintStoryOptions, paintFootnoteBlocks, paintHeaderFooterBlocks };
@@ -0,0 +1,279 @@
1
+ import { isFloatingImageRun, isFloatingTextBoxBlock } from "../../layout-engine/types.js";
2
+ import { HIT_REGION_KINDS } from "../primitives.js";
3
+ import { paintImageFragment } from "./imagePrimitives.js";
4
+ import { paintParagraphFragment } from "./paragraphPrimitives.js";
5
+ import { blockRegion } from "./regions.js";
6
+ import { paintTableFragment } from "./tablePrimitives.js";
7
+ import { paintTextBoxFragment } from "./textBoxPrimitives.js";
8
+ import { UNSUPPORTED_CONSTRUCT } from "./unsupported.js";
9
+ //#region src/display-list/build/storyPrimitives.ts
10
+ /**
11
+ * Stacked story blocks: the header, the footer and a footnote body.
12
+ *
13
+ * These three are not fragments. The layout engine paginates the body only, so
14
+ * a story arrives as a `(blocks, measures)` pair already measured against its
15
+ * own width, and whoever paints it stacks the blocks itself. Every block is
16
+ * then handed to the *same* paragraph, table, image and text-box builders the
17
+ * body uses: a second text painter for headers is exactly how a header comes to
18
+ * disagree with the body about a tab stop or a justified space.
19
+ *
20
+ * The stacking rules differ between the two callers and both are the painter's:
21
+ * `renderFootnoteContent` advances by the measured height of every block, while
22
+ * `renderHeaderFooterContent` honours explicit `w:spacing w:before`, offsets an
23
+ * inline table by `w:jc`/`w:tblInd`, and lets a floating or page-anchored
24
+ * object out of the flow entirely.
25
+ */
26
+ /** Mirrors `isPositionedHeaderFooterTextBoxBlock`: an explicit display mode keeps a box in the flow. */
27
+ const isPositionedTextBox = (block) => block.displayMode !== "block" && block.displayMode !== "inline" && isFloatingTextBoxBlock(block);
28
+ const isAnchoredImage = (block) => block.anchor?.isAnchored === true;
29
+ /** `w:jc` / `w:tblInd` for an inline story table, as the body's `desiredX` resolves them. */
30
+ const inlineTableOffsetPx = (block, tableWidthPx, widthPx) => {
31
+ if (block.justification === "center") return (widthPx - tableWidthPx) / 2;
32
+ if (block.justification === "right") return widthPx - tableWidthPx;
33
+ return block.indent ?? 0;
34
+ };
35
+ /**
36
+ * Explicit `w:spacing w:before` only. `normalizeHeaderFooterMeasureBlocks`
37
+ * strips style-inherited spacing from the measurement copy, so offsetting by an
38
+ * inherited value would push the first line below the space its own measure
39
+ * reserved and every following block with it.
40
+ */
41
+ const explicitSpaceBeforePx = (block) => block.attrs?.spacingExplicit?.before === true ? block.attrs.spacing?.before ?? 0 : 0;
42
+ /** One block of a story, painted at an absolute page position. */
43
+ const paintStoryBlock = ({ composer, block, measure, xPx, yPx, widthPx, context, label, construct }) => {
44
+ if (block.kind === "paragraph" && measure.kind === "paragraph") {
45
+ const fragment = {
46
+ kind: "paragraph",
47
+ blockId: block.id,
48
+ x: xPx,
49
+ y: yPx,
50
+ width: widthPx,
51
+ height: measure.totalHeight,
52
+ fromLine: 0,
53
+ toLine: measure.lines.length
54
+ };
55
+ composer.region(blockRegion({
56
+ fragment,
57
+ kind: HIT_REGION_KINDS.paragraph,
58
+ context
59
+ }), () => {
60
+ paintParagraphFragment({
61
+ fragment,
62
+ block,
63
+ measure,
64
+ context,
65
+ composer
66
+ });
67
+ });
68
+ return;
69
+ }
70
+ if (block.kind === "table" && measure.kind === "table") {
71
+ const fragment = {
72
+ kind: "table",
73
+ blockId: block.id,
74
+ x: xPx,
75
+ y: yPx,
76
+ width: measure.totalWidth,
77
+ height: measure.totalHeight,
78
+ fromRow: 0,
79
+ toRow: block.rows.length
80
+ };
81
+ composer.region(blockRegion({
82
+ fragment,
83
+ kind: HIT_REGION_KINDS.table,
84
+ context
85
+ }), () => {
86
+ paintTableFragment({
87
+ fragment,
88
+ block,
89
+ measure,
90
+ context,
91
+ composer,
92
+ paintTextBox: paintTextBoxFragment
93
+ });
94
+ });
95
+ return;
96
+ }
97
+ if (block.kind === "image" && measure.kind === "image") {
98
+ const fragment = {
99
+ kind: "image",
100
+ blockId: block.id,
101
+ x: xPx,
102
+ y: yPx,
103
+ width: measure.width,
104
+ height: measure.height
105
+ };
106
+ composer.region(blockRegion({
107
+ fragment,
108
+ kind: HIT_REGION_KINDS.image,
109
+ context
110
+ }), () => {
111
+ composer.push(paintImageFragment(fragment, block, context));
112
+ });
113
+ return;
114
+ }
115
+ if (block.kind === "textBox" && measure.kind === "textBox") {
116
+ const fragment = {
117
+ kind: "textBox",
118
+ blockId: block.id,
119
+ x: xPx,
120
+ y: yPx,
121
+ width: measure.width,
122
+ height: measure.height
123
+ };
124
+ composer.region(blockRegion({
125
+ fragment,
126
+ kind: HIT_REGION_KINDS.textBox,
127
+ context
128
+ }), () => {
129
+ paintTextBoxFragment({
130
+ composer,
131
+ fragment,
132
+ block,
133
+ measure,
134
+ context
135
+ });
136
+ });
137
+ return;
138
+ }
139
+ context.unsupported.report(construct, context.pageIndex, `${label}: a ${block.kind} block with a ${measure.kind} measure is not painted`);
140
+ };
141
+ const measuredHeightPx = (measure) => {
142
+ switch (measure.kind) {
143
+ case "paragraph":
144
+ case "table": return measure.totalHeight;
145
+ case "image":
146
+ case "textBox": return measure.height;
147
+ case "sectionBreak":
148
+ case "pageBreak":
149
+ case "columnBreak": return 0;
150
+ default: return 0;
151
+ }
152
+ };
153
+ /**
154
+ * A footnote body: every block stacks by its measured height, which is the
155
+ * height the paginator reserved the band from.
156
+ */
157
+ const paintFootnoteBlocks = ({ composer, blocks, measures, xPx, yPx, widthPx, context, label }) => {
158
+ let cursorYPx = yPx;
159
+ for (const [index, block] of blocks.entries()) {
160
+ const measure = measures[index];
161
+ if (measure === void 0) {
162
+ context.unsupported.report(UNSUPPORTED_CONSTRUCT.missingBlock, context.pageIndex, `${label}: block ${String(block.id)} has no measure`);
163
+ continue;
164
+ }
165
+ paintStoryBlock({
166
+ composer,
167
+ block,
168
+ measure,
169
+ xPx,
170
+ yPx: cursorYPx,
171
+ widthPx,
172
+ context,
173
+ label,
174
+ construct: UNSUPPORTED_CONSTRUCT.footnoteContent
175
+ });
176
+ cursorYPx += measuredHeightPx(measure);
177
+ }
178
+ };
179
+ /**
180
+ * A header or footer story. Blocks that Word takes out of the flow (a floating
181
+ * table, an anchored image, a positioned text box) contribute no height, and
182
+ * they are reported rather than painted: their position resolves against page
183
+ * and margin anchors the display list producer does not carry.
184
+ */
185
+ const paintHeaderFooterBlocks = ({ composer, blocks, measures, xPx, yPx, widthPx, context, label }) => {
186
+ let cursorYPx = yPx;
187
+ const reportOutOfFlow = (detail) => {
188
+ context.unsupported.report(UNSUPPORTED_CONSTRUCT.headerFooterContent, context.pageIndex, `${label}: ${detail}`);
189
+ };
190
+ for (const [index, block] of blocks.entries()) {
191
+ const measure = measures[index];
192
+ if (measure === void 0) {
193
+ context.unsupported.report(UNSUPPORTED_CONSTRUCT.missingBlock, context.pageIndex, `${label}: block ${String(block.id)} has no measure`);
194
+ continue;
195
+ }
196
+ switch (block.kind) {
197
+ case "paragraph":
198
+ if (measure.kind !== "paragraph") break;
199
+ if (block.runs.some((run) => run.kind === "image" && (isFloatingImageRun(run) || run.position !== void 0))) reportOutOfFlow(`paragraph ${String(block.id)} anchors a floating picture, which resolves against page and margin anchors the display list does not carry`);
200
+ paintStoryBlock({
201
+ composer,
202
+ block,
203
+ measure,
204
+ xPx,
205
+ yPx: cursorYPx + explicitSpaceBeforePx(block),
206
+ widthPx,
207
+ context,
208
+ label,
209
+ construct: UNSUPPORTED_CONSTRUCT.headerFooterContent
210
+ });
211
+ cursorYPx += measure.totalHeight;
212
+ continue;
213
+ case "table":
214
+ if (measure.kind !== "table") break;
215
+ if (block.floating) {
216
+ reportOutOfFlow(`table ${String(block.id)} is floating (w:tblpPr) and is not painted`);
217
+ continue;
218
+ }
219
+ paintStoryBlock({
220
+ composer,
221
+ block,
222
+ measure,
223
+ xPx: xPx + inlineTableOffsetPx(block, measure.totalWidth, widthPx),
224
+ yPx: cursorYPx,
225
+ widthPx,
226
+ context,
227
+ label,
228
+ construct: UNSUPPORTED_CONSTRUCT.headerFooterContent
229
+ });
230
+ cursorYPx += measure.totalHeight;
231
+ continue;
232
+ case "image":
233
+ if (measure.kind !== "image") break;
234
+ if (isAnchoredImage(block)) {
235
+ reportOutOfFlow(`image ${String(block.id)} is anchored and is not painted`);
236
+ continue;
237
+ }
238
+ paintStoryBlock({
239
+ composer,
240
+ block,
241
+ measure,
242
+ xPx,
243
+ yPx: cursorYPx,
244
+ widthPx,
245
+ context,
246
+ label,
247
+ construct: UNSUPPORTED_CONSTRUCT.headerFooterContent
248
+ });
249
+ cursorYPx += measure.height;
250
+ continue;
251
+ case "textBox":
252
+ if (measure.kind !== "textBox") break;
253
+ if (isPositionedTextBox(block)) {
254
+ reportOutOfFlow(`text box ${String(block.id)} is positioned and is not painted`);
255
+ continue;
256
+ }
257
+ paintStoryBlock({
258
+ composer,
259
+ block,
260
+ measure,
261
+ xPx,
262
+ yPx: cursorYPx,
263
+ widthPx,
264
+ context,
265
+ label,
266
+ construct: UNSUPPORTED_CONSTRUCT.headerFooterContent
267
+ });
268
+ cursorYPx += measure.height;
269
+ continue;
270
+ case "pageBreak":
271
+ case "columnBreak":
272
+ case "sectionBreak": continue;
273
+ default: continue;
274
+ }
275
+ context.unsupported.report(UNSUPPORTED_CONSTRUCT.measureMismatch, context.pageIndex, `${label}: block ${String(block.id)} is a ${block.kind} with a ${measure.kind} measure`);
276
+ }
277
+ };
278
+ //#endregion
279
+ export { paintFootnoteBlocks, paintHeaderFooterBlocks };
@@ -0,0 +1,19 @@
1
+ import { BorderStyle } from "../../layout-engine/types.js";
2
+ import { DisplayStroke, DisplayStrokePattern } from "../types.js";
3
+ //#region src/display-list/build/strokes.d.ts
4
+ type BorderInput = Pick<BorderStyle, "color" | "style" | "width">;
5
+ type ResolveStrokeResult = {
6
+ readonly stroke?: DisplayStroke;
7
+ /** The authored colour, when it could not be parsed. Report it, do not paint. */
8
+ readonly unresolvedColor?: string;
9
+ };
10
+ /**
11
+ * Turn an authored border into a paintable stroke. `undefined` stroke means
12
+ * "draw nothing": an explicit `none`/`nil` style, a non-positive width, or a
13
+ * colour this producer cannot resolve.
14
+ */
15
+ declare const resolveBorderStroke: (border: BorderInput) => ResolveStrokeResult;
16
+ /** CSS `text-decoration-style` → the display list's stroke patterns. */
17
+ declare const decorationPatternForStyle: (style: string | undefined) => DisplayStrokePattern;
18
+ //#endregion
19
+ export { ResolveStrokeResult, decorationPatternForStyle, resolveBorderStroke };
@@ -0,0 +1,78 @@
1
+ import { resolveCssBorderStroke } from "../../layout-painter/borderStroke.js";
2
+ import { parseDisplayColor } from "./colors.js";
3
+ //#region src/display-list/build/strokes.ts
4
+ /**
5
+ * Border and rule strokes.
6
+ *
7
+ * One deliberate divergence from `layout-painter/borderStroke.ts`: that helper
8
+ * encodes a sub-pixel border width as a `color-mix` alpha because CSS quantizes
9
+ * a fractional border to one device pixel. A display list has no device pixel,
10
+ * and a PDF strokes 0.4pt natively, so the authored fractional width and the
11
+ * plain colour survive here. Everything else (colour/style/width defaults) is
12
+ * taken from the painter's helper so the two cannot drift.
13
+ */
14
+ /**
15
+ * CSS `border-style` and the raw OOXML `w:val` names both reach
16
+ * `BorderStyle.style` (the bridge maps most, `renderParagraph` maps the rest),
17
+ * so both vocabularies are resolved here. Everything decorative that folio does
18
+ * not draw degrades to a plain line, matching how Word degrades on a platform
19
+ * without the specialised glyphs.
20
+ */
21
+ const STROKE_PATTERN_BY_STYLE = {
22
+ none: "none",
23
+ nil: "none",
24
+ hidden: "none",
25
+ solid: "solid",
26
+ single: "solid",
27
+ thick: "solid",
28
+ inset: "solid",
29
+ outset: "solid",
30
+ ridge: "solid",
31
+ groove: "solid",
32
+ double: "double",
33
+ dotted: "dotted",
34
+ dotdash: "dashed",
35
+ dotdotdash: "dashed",
36
+ dashed: "dashed",
37
+ dash: "dashed",
38
+ dashsmallgap: "dashed",
39
+ dashlong: "dashed",
40
+ wavy: "wavy",
41
+ wave: "wavy",
42
+ wavydouble: "double"
43
+ };
44
+ const DEFAULT_PATTERN = "solid";
45
+ const strokePatternForStyle = (style) => {
46
+ if (style === void 0) return DEFAULT_PATTERN;
47
+ return STROKE_PATTERN_BY_STYLE[style.trim().toLowerCase()] ?? DEFAULT_PATTERN;
48
+ };
49
+ /**
50
+ * Turn an authored border into a paintable stroke. `undefined` stroke means
51
+ * "draw nothing": an explicit `none`/`nil` style, a non-positive width, or a
52
+ * colour this producer cannot resolve.
53
+ */
54
+ const resolveBorderStroke = (border) => {
55
+ const pattern = strokePatternForStyle(border.style);
56
+ if (pattern === "none") return {};
57
+ const css = resolveCssBorderStroke(border);
58
+ const isSubPixelEncoded = css.color.startsWith("color-mix");
59
+ const authoredWidth = border.width ?? css.width;
60
+ const thicknessPx = isSubPixelEncoded ? authoredWidth : css.width;
61
+ if (thicknessPx <= 0) return {};
62
+ const colorSource = isSubPixelEncoded ? border.color ?? "#000000" : css.color;
63
+ const color = parseDisplayColor(colorSource);
64
+ if (!color) return { unresolvedColor: colorSource };
65
+ return { stroke: {
66
+ color,
67
+ thicknessPx,
68
+ pattern
69
+ } };
70
+ };
71
+ /** CSS `text-decoration-style` → the display list's stroke patterns. */
72
+ const decorationPatternForStyle = (style) => {
73
+ if (style === void 0) return DEFAULT_PATTERN;
74
+ const pattern = STROKE_PATTERN_BY_STYLE[style.trim().toLowerCase()];
75
+ return pattern === void 0 || pattern === "none" ? DEFAULT_PATTERN : pattern;
76
+ };
77
+ //#endregion
78
+ export { decorationPatternForStyle, resolveBorderStroke };
@@ -0,0 +1,38 @@
1
+ import { TableBlock, TableFragment, TableMeasure, TextBoxBlock, TextBoxFragment, TextBoxMeasure } from "../../layout-engine/types.js";
2
+ import { BuildContext } from "./buildContext.js";
3
+ import { PageComposer } from "./regions.js";
4
+ //#region src/display-list/build/tablePrimitives.d.ts
5
+ type PaintTextBox = (options: {
6
+ readonly composer: PageComposer;
7
+ readonly fragment: TextBoxFragment;
8
+ readonly block: TextBoxBlock;
9
+ readonly measure: TextBoxMeasure;
10
+ readonly context: BuildContext;
11
+ }) => void;
12
+ type TableBlockPaintOptions = {
13
+ readonly composer: PageComposer;
14
+ readonly block: TableBlock;
15
+ readonly measure: TableMeasure;
16
+ readonly xPx: number;
17
+ readonly yPx: number;
18
+ readonly context: BuildContext;
19
+ readonly paintTextBox: PaintTextBox;
20
+ };
21
+ /** A whole, unpaginated table: what a nested table inside a cell or box is. */
22
+ declare const paintTableBlock: ({ composer, block, measure, xPx, yPx, context, paintTextBox }: TableBlockPaintOptions) => void;
23
+ type TablePaintOptions = {
24
+ readonly composer: PageComposer;
25
+ readonly fragment: TableFragment;
26
+ readonly block: TableBlock;
27
+ readonly measure: TableMeasure;
28
+ readonly context: BuildContext;
29
+ readonly paintTextBox: PaintTextBox;
30
+ };
31
+ /**
32
+ * A table fragment's primitives. A fragment that cuts a row mid-content wraps
33
+ * its rows in a `clipGroup` and starts them `topClip` px higher, which is what
34
+ * the painter's negative row-stack offset plus `overflow: hidden` does.
35
+ */
36
+ declare const paintTableFragment: ({ composer, fragment, block, measure, context, paintTextBox }: TablePaintOptions) => void;
37
+ //#endregion
38
+ export { TableBlockPaintOptions, TablePaintOptions, paintTableBlock, paintTableFragment };