Skip to content

Commit 5730b6c

Browse files
authored
[3/16] refactor(layout): pre-compute SDT container keys in resolved layout (#2812)
* refactor(layout): lift page metadata into ResolvedPage * refactor(layout): lift fragment metadata into resolved paint items Add pmStart, pmEnd, continuesFromPrev, continuesOnNext, markerWidth, and metadata fields to resolved paint item types. Populate them in the resolvers and update the painter to prefer resolved item data over legacy Fragment reads with fallbacks. * refactor(layout): pre-compute SDT container keys in resolved layout * refactor(layout): pre-compute paragraph border data in resolved layout (#2813)
1 parent c1f616e commit 5730b6c

8 files changed

Lines changed: 801 additions & 14 deletions

File tree

packages/layout-engine/contracts/src/resolved-layout.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import type {
66
ImageFragmentMetadata,
77
Line,
88
PageMargins,
9+
ParagraphBorders,
910
SectionVerticalAlign,
1011
TableBlock,
1112
TableMeasure,
@@ -118,6 +119,12 @@ export type ResolvedFragmentItem = {
118119
markerWidth?: number;
119120
/** Pre-resolved paragraph content for non-table paragraph fragments. */
120121
content?: ResolvedParagraphContent;
122+
/** Pre-computed SDT container key for boundary grouping (`structuredContent:<id>` or `documentSection:<id>`). */
123+
sdtContainerKey?: string | null;
124+
/** Pre-computed hash of paragraph borders for between-border grouping. */
125+
paragraphBorderHash?: string;
126+
/** Pre-extracted paragraph borders for between-border rendering. */
127+
paragraphBorders?: ParagraphBorders;
121128
};
122129

123130
/** Resolved paragraph content for non-table paragraph/list-item fragments. */
@@ -232,6 +239,8 @@ export type ResolvedTableItem = {
232239
cellSpacingPx: number;
233240
/** Pre-computed effective column widths: fragment.columnWidths ?? measure.columnWidths. */
234241
effectiveColumnWidths: number[];
242+
/** Pre-computed SDT container key for boundary grouping (`structuredContent:<id>` or `documentSection:<id>`). */
243+
sdtContainerKey?: string | null;
235244
};
236245

237246
/**
@@ -268,6 +277,8 @@ export type ResolvedImageItem = {
268277
block: ImageBlock;
269278
/** Image metadata for interactive resizing (original dimensions, aspect ratio). */
270279
metadata?: ImageFragmentMetadata;
280+
/** Pre-computed SDT container key for boundary grouping (typically null for images). */
281+
sdtContainerKey?: string | null;
271282
};
272283

273284
/**
@@ -302,6 +313,8 @@ export type ResolvedDrawingItem = {
302313
pmEnd?: number;
303314
/** Pre-extracted DrawingBlock (replaces blockLookup.get()). */
304315
block: DrawingBlock;
316+
/** Pre-computed SDT container key for boundary grouping (typically null for drawings). */
317+
sdtContainerKey?: string | null;
305318
};
306319

307320
/** Type guard: checks whether a resolved paint item is a ResolvedTableItem. */
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import type { ParagraphBorder, ParagraphBorders } from '@superdoc/contracts';
2+
3+
/**
4+
* Hashes a single paragraph border for equality comparison.
5+
*
6+
* Duplicated from painters/dom/src/paragraph-hash-utils.ts to avoid a
7+
* circular dependency (painter-dom → layout-resolved is not allowed).
8+
* Keep the two copies in sync.
9+
*/
10+
const hashParagraphBorder = (border: ParagraphBorder): string => {
11+
const parts: string[] = [];
12+
if (border.style !== undefined) parts.push(`s:${border.style}`);
13+
if (border.width !== undefined) parts.push(`w:${border.width}`);
14+
if (border.color !== undefined) parts.push(`c:${border.color}`);
15+
if (border.space !== undefined) parts.push(`sp:${border.space}`);
16+
return parts.join(',');
17+
};
18+
19+
/**
20+
* Hashes a full paragraph borders object for grouping comparison.
21+
*
22+
* Two paragraph fragments with the same hash belong to the same border group
23+
* per ECMA-376 §17.3.1.24.
24+
*/
25+
export const hashParagraphBorders = (borders: ParagraphBorders): string => {
26+
const parts: string[] = [];
27+
if (borders.top) parts.push(`t:[${hashParagraphBorder(borders.top)}]`);
28+
if (borders.right) parts.push(`r:[${hashParagraphBorder(borders.right)}]`);
29+
if (borders.bottom) parts.push(`b:[${hashParagraphBorder(borders.bottom)}]`);
30+
if (borders.left) parts.push(`l:[${hashParagraphBorder(borders.left)}]`);
31+
if (borders.between) parts.push(`bw:[${hashParagraphBorder(borders.between)}]`);
32+
return parts.join(';');
33+
};

0 commit comments

Comments
 (0)