-
Notifications
You must be signed in to change notification settings - Fork 132
Expand file tree
/
Copy pathresolved-layout.ts
More file actions
370 lines (354 loc) · 13.8 KB
/
resolved-layout.ts
File metadata and controls
370 lines (354 loc) · 13.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
import type {
DrawingBlock,
FlowMode,
Fragment,
ImageBlock,
ImageFragmentMetadata,
Line,
PageMargins,
ParagraphBorders,
SectionVerticalAlign,
TableBlock,
TableMeasure,
} from './index.js';
/** A fully resolved layout ready for the next-generation paint pipeline. */
export type ResolvedLayout = {
/** Schema version for forward compatibility. */
version: 1;
/** Rendering flow mode used to produce this layout. */
flowMode: FlowMode;
/** Gap between pages in pixels (0 when unset). */
pageGap: number;
/** Resolved pages with normalized dimensions. */
pages: ResolvedPage[];
/** Document epoch identifier from the source layout. Used for change tracking in the painter. */
layoutEpoch?: number;
};
/** A single resolved page with stable identity and normalized dimensions. */
export type ResolvedPage = {
/** Stable page identifier (e.g. `page-0`). */
id: string;
/** 0-based page index. */
index: number;
/** 1-based page number (from Page.number). */
number: number;
/** Page width in pixels (resolved from page.size?.w ?? layout.pageSize.w). */
width: number;
/** Page height in pixels (resolved from page.size?.h ?? layout.pageSize.h). */
height: number;
/** Resolved paint items for this page. */
items: ResolvedPaintItem[];
/** Page margins from the source page. Used for ruler rendering and header/footer positioning. */
margins?: PageMargins;
/** Extra bottom space reserved for footnotes (px). Used for footer space calculation. */
footnoteReserved?: number;
/** Formatted page number text (e.g. "i", "ii" for Roman numeral sections). */
numberText?: string;
/** Vertical alignment of content within this page. */
vAlign?: SectionVerticalAlign;
/** Base section margins before header/footer inflation. Used for vAlign centering calculations. */
baseMargins?: { top: number; bottom: number };
/** 0-based index of the section this page belongs to. */
sectionIndex?: number;
/** Header/footer reference IDs for this page's section. */
sectionRefs?: {
headerRefs?: { default?: string; first?: string; even?: string; odd?: string };
footerRefs?: { default?: string; first?: string; even?: string; odd?: string };
};
/** Page orientation. */
orientation?: 'portrait' | 'landscape';
};
/** Union of all resolved paint item kinds. */
export type ResolvedPaintItem =
| ResolvedGroupItem
| ResolvedFragmentItem
| ResolvedTableItem
| ResolvedImageItem
| ResolvedDrawingItem;
/** A group of nested resolved paint items (for future use). */
export type ResolvedGroupItem = {
kind: 'group';
id: string;
x: number;
y: number;
width: number;
height: number;
children: ResolvedPaintItem[];
};
/**
* A resolved fragment wrapper item.
* Carries positioning and metadata needed to create the fragment's DOM wrapper,
* while inner content rendering is delegated to legacy fragment renderers via fragmentIndex.
*/
export type ResolvedFragmentItem = {
kind: 'fragment';
/** Stable identifier matching fragmentKey() semantics from the painter. */
id: string;
/** 0-based page index this item belongs to. */
pageIndex: number;
/** Left position in pixels. */
x: number;
/** Top position in pixels. */
y: number;
/** Width in pixels. */
width: number;
/** Height in pixels (computed from measure data for para/list-item). */
height: number;
/** Stacking order for anchored images/drawings. */
zIndex?: number;
/** Source fragment kind — used by the painter for wrapper style decisions. */
fragmentKind: Fragment['kind'];
/** Block ID — written to data-block-id and used for legacy content lookup. */
blockId: string;
/** Index within page.fragments — bridge to legacy content rendering. */
fragmentIndex: number;
/** ProseMirror start position for click-to-position mapping. */
pmStart?: number;
/** ProseMirror end position for click-to-position mapping. */
pmEnd?: number;
/** Whether this fragment continues from a previous page. */
continuesFromPrev?: boolean;
/** Whether this fragment continues on the next page. */
continuesOnNext?: boolean;
/** List marker box width in pixels (para/list-item only). */
markerWidth?: number;
/** Pre-resolved paragraph content for non-table paragraph fragments. */
content?: ResolvedParagraphContent;
/** Pre-computed SDT container key for boundary grouping (`structuredContent:<id>` or `documentSection:<id>`). */
sdtContainerKey?: string | null;
/** Pre-computed hash of paragraph borders for between-border grouping. */
paragraphBorderHash?: string;
/** Pre-extracted paragraph borders for between-border rendering. */
paragraphBorders?: ParagraphBorders;
/** Pre-computed change-detection signature (blockVersion + fragment-specific data). */
version?: string;
};
/** Resolved paragraph content for non-table paragraph/list-item fragments. */
export type ResolvedParagraphContent = {
/** The lines to render, with all layout data pre-resolved. */
lines: ResolvedTextLineItem[];
/** Drop cap rendering data. Only present on first fragment of a paragraph with a drop cap. */
dropCap?: ResolvedDropCapItem;
/** List marker rendering data. Only present on first fragment of a list paragraph. */
marker?: ResolvedListMarkerItem;
/** Whether this fragment continues from a previous page. */
continuesFromPrev?: boolean;
/** Whether this fragment continues on the next page. */
continuesOnNext?: boolean;
/** Whether the source paragraph ends with a lineBreak run. */
paragraphEndsWithLineBreak?: boolean;
};
/** A single resolved text line with pre-computed rendering geometry. */
export type ResolvedTextLineItem = {
/** The source Line data (segments, leaders, bars, dimensions, run indices). */
line: Line;
/** Global line index within the paragraph (fragment.fromLine + localIndex). */
lineIndex: number;
/** Pre-computed available width for justify calculations. */
availableWidth: number;
/** Whether to skip justification for this line (last line of paragraph). */
skipJustify: boolean;
/** Pre-computed CSS paddingLeft in pixels. */
paddingLeftPx: number;
/** Pre-computed CSS paddingRight in pixels. */
paddingRightPx: number;
/** Pre-computed CSS textIndent in pixels (0 when not applicable). */
textIndentPx: number;
/** Whether this is a list first line (indent handled by marker, not CSS). */
isListFirstLine: boolean;
/** Resolved text-start position for list first lines with explicit segment positioning. */
resolvedListTextStartPx?: number;
/** Whether this line has explicit segment positioning (tabs). */
hasExplicitSegmentPositioning: boolean;
/** Pre-computed indent offset for the segment positioning path in renderLine. */
indentOffset: number;
};
/** Resolved drop cap rendering data. */
export type ResolvedDropCapItem = {
/** Drop cap text content. */
text: string;
/** Drop cap mode. */
mode: 'drop' | 'margin';
/** Font family. */
fontFamily: string;
/** Font size in pixels. */
fontSize: number;
/** Bold styling. */
bold?: boolean;
/** Italic styling. */
italic?: boolean;
/** Text color. */
color?: string;
/** Vertical position offset in pixels. */
position?: number;
/** Measured width in pixels. */
width?: number;
/** Measured height in pixels. */
height?: number;
};
// ============================================================================
// Kind-specific resolved items (PR7: table, image, drawing)
// ============================================================================
/**
* A resolved table fragment with pre-extracted block/measure data.
* Replaces blockLookup.get() in the table render path.
*/
export type ResolvedTableItem = {
kind: 'fragment';
/** Discriminant for table fragments. */
fragmentKind: 'table';
/** Stable identifier matching fragmentKey() semantics from the painter. */
id: string;
/** 0-based page index this item belongs to. */
pageIndex: number;
/** Left position in pixels. */
x: number;
/** Top position in pixels. */
y: number;
/** Width in pixels. */
width: number;
/** Height in pixels (from fragment.height). */
height: number;
/** Stacking order (tables typically don't have zIndex at fragment level). */
zIndex?: number;
/** Block ID — written to data-block-id. */
blockId: string;
/** Index within page.fragments — bridge to legacy rendering. */
fragmentIndex: number;
/** ProseMirror start position for click-to-position mapping. */
pmStart?: number;
/** ProseMirror end position for click-to-position mapping. */
pmEnd?: number;
/** Whether this table fragment continues from a previous page. */
continuesFromPrev?: boolean;
/** Whether this table fragment continues on the next page. */
continuesOnNext?: boolean;
/** Pre-extracted TableBlock (replaces blockLookup.get()). */
block: TableBlock;
/** Pre-extracted TableMeasure (replaces blockLookup.get()). */
measure: TableMeasure;
/** Pre-computed cell spacing: measure.cellSpacingPx ?? getCellSpacingPx(block.attrs?.cellSpacing). */
cellSpacingPx: number;
/** Pre-computed effective column widths: fragment.columnWidths ?? measure.columnWidths. */
effectiveColumnWidths: number[];
/** Pre-computed SDT container key for boundary grouping (`structuredContent:<id>` or `documentSection:<id>`). */
sdtContainerKey?: string | null;
/** Pre-computed change-detection signature (blockVersion + fragment-specific data). */
version?: string;
};
/**
* A resolved image fragment with pre-extracted block data.
* Replaces blockLookup.get() in the image render path.
*/
export type ResolvedImageItem = {
kind: 'fragment';
/** Discriminant for image fragments. */
fragmentKind: 'image';
/** Stable identifier matching fragmentKey() semantics from the painter. */
id: string;
/** 0-based page index this item belongs to. */
pageIndex: number;
/** Left position in pixels. */
x: number;
/** Top position in pixels. */
y: number;
/** Width in pixels. */
width: number;
/** Height in pixels. */
height: number;
/** Stacking order for anchored images. */
zIndex?: number;
/** Block ID — written to data-block-id. */
blockId: string;
/** Index within page.fragments — bridge to legacy rendering. */
fragmentIndex: number;
/** ProseMirror start position for click-to-position mapping. */
pmStart?: number;
/** ProseMirror end position for click-to-position mapping. */
pmEnd?: number;
/** Pre-extracted ImageBlock (replaces blockLookup.get()). */
block: ImageBlock;
/** Image metadata for interactive resizing (original dimensions, aspect ratio). */
metadata?: ImageFragmentMetadata;
/** Pre-computed SDT container key for boundary grouping (typically null for images). */
sdtContainerKey?: string | null;
/** Pre-computed change-detection signature (blockVersion + fragment-specific data). */
version?: string;
};
/**
* A resolved drawing fragment with pre-extracted block data.
* Replaces blockLookup.get() in the drawing render path.
*/
export type ResolvedDrawingItem = {
kind: 'fragment';
/** Discriminant for drawing fragments. */
fragmentKind: 'drawing';
/** Stable identifier matching fragmentKey() semantics from the painter. */
id: string;
/** 0-based page index this item belongs to. */
pageIndex: number;
/** Left position in pixels. */
x: number;
/** Top position in pixels. */
y: number;
/** Width in pixels. */
width: number;
/** Height in pixels. */
height: number;
/** Stacking order for anchored drawings. */
zIndex?: number;
/** Block ID — written to data-block-id. */
blockId: string;
/** Index within page.fragments — bridge to legacy rendering. */
fragmentIndex: number;
/** ProseMirror start position for click-to-position mapping. */
pmStart?: number;
/** ProseMirror end position for click-to-position mapping. */
pmEnd?: number;
/** Pre-extracted DrawingBlock (replaces blockLookup.get()). */
block: DrawingBlock;
/** Pre-computed SDT container key for boundary grouping (typically null for drawings). */
sdtContainerKey?: string | null;
/** Pre-computed change-detection signature (blockVersion + fragment-specific data). */
version?: string;
};
/** Type guard: checks whether a resolved paint item is a ResolvedTableItem. */
export function isResolvedTableItem(item: ResolvedPaintItem): item is ResolvedTableItem {
return item.kind === 'fragment' && 'fragmentKind' in item && item.fragmentKind === 'table' && 'measure' in item;
}
/** Type guard: checks whether a resolved paint item is a ResolvedImageItem. */
export function isResolvedImageItem(item: ResolvedPaintItem): item is ResolvedImageItem {
return item.kind === 'fragment' && 'fragmentKind' in item && item.fragmentKind === 'image' && 'block' in item;
}
/** Type guard: checks whether a resolved paint item is a ResolvedDrawingItem. */
export function isResolvedDrawingItem(item: ResolvedPaintItem): item is ResolvedDrawingItem {
return item.kind === 'fragment' && 'fragmentKind' in item && item.fragmentKind === 'drawing' && 'block' in item;
}
/** Resolved list marker rendering data with pre-computed positioning. */
export type ResolvedListMarkerItem = {
/** Marker text content (e.g., "1.", "a)", bullet). */
text: string;
/** Horizontal justification. */
justification: 'left' | 'right' | 'center';
/** Suffix type after marker. */
suffix: 'tab' | 'space' | 'nothing';
/** Whether marker should be hidden (vanish property). */
vanish?: boolean;
/** Pre-computed left position of marker container in pixels. */
markerStartPx: number;
/** Pre-computed tab/space suffix width in pixels. */
suffixWidthPx: number;
/** CSS paddingLeft for the first line when marker is present. */
firstLinePaddingLeftPx: number;
/** Extra padding adjustment for center-justified markers. */
centerPaddingAdjustPx?: number;
/** Marker run styling. */
run: {
fontFamily: string;
fontSize: number;
bold?: boolean;
italic?: boolean;
color?: string;
letterSpacing?: number;
};
};