@openpresentation/opf 0.8.0 → 0.10.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.
@@ -1,5 +1,5 @@
1
- import { assertValidPresentation } from './chunk-HIY26FJV.js';
2
- import { resolveFontFamilies, resolveCanvasDimensions, composeSlide, tableRowBoundaries } from './chunk-NEQAPMS2.js';
1
+ import { assertValidPresentation } from './chunk-GCB6LGJM.js';
2
+ import { resolveFontFamilies, resolveCanvasDimensions, composeSlide, tableRowBoundaries } from './chunk-BAWLYSUN.js';
3
3
  import { catalogs } from './chunk-TWMRZ43O.js';
4
4
 
5
5
  // src/pagination.ts
@@ -218,7 +218,7 @@ function paginatePresentation(input, options = {}) {
218
218
  const fontReference = design.fontScheme ?? theme.fontScheme ?? "roboto";
219
219
  const fontScheme = typeof fontReference === "string" ? resolve("fontSchemes", fontReference) : { ...resolve("fontSchemes", fontReference.id), ...fontReference };
220
220
  const fonts = resolveFontFamilies(fontScheme);
221
- const result = paginateSlide(slide, { ...resolveCanvasDimensions(design.dimensions ?? theme.dimensions), layout, fonts, textMeasurement: options.textMeasurement, ...overrides, slideIndex: index, maxSlides: maxSlides - output.length, minFontSize: options.minFontSize, reservedIds });
221
+ const result = paginateSlide(slide, { ...resolveCanvasDimensions(design.dimensions ?? theme.dimensions), layout, fonts, contentAlignment: design.contentAlignment, titleAlignment: design.titleAlignment, contentBox: design.contentBox, textMeasurement: options.textMeasurement, textRasterPadding: options.textRasterPadding, ...overrides, slideIndex: index, maxSlides: maxSlides - output.length, minFontSize: options.minFontSize, reservedIds });
222
222
  const outputStart = output.length;
223
223
  result.pages.forEach((page, pageIndex) => {
224
224
  pages.push({ sourceSlideIndex: index, slideIndex: outputStart + pageIndex, mappings: page.mappings.map((mapping) => ({ ...mapping, outputPath: mapping.outputPath.replace(/^slides\.\d+/, `slides.${outputStart + pageIndex}`) })) });
@@ -41,6 +41,20 @@ declare function tableGrid(value: unknown, path?: string): TableGrid;
41
41
  /** Row boundaries that preserve every vertical merge, used by pagination. */
42
42
  declare function tableRowBoundaries(value: unknown): number[];
43
43
 
44
+ declare function colorContrast(foreground: string, background: string): number | undefined;
45
+ /** Keep an inherited text color at >=4.5:1; otherwise select black or white.
46
+ * Callers must preserve explicit text-color overrides before using this fallback.
47
+ * An unresolved/translucent fill preserves the preferred color, without a contrast claim.
48
+ */
49
+ declare function textColorForFill(fill: string, preferred: string): string;
50
+ /** Keep an inherited chart mark at >=3:1 against its opaque panel. Otherwise
51
+ * mix toward black or white in bounded 1/255 steps, choosing the first passing
52
+ * step (higher contrast breaks a tie). This retains a recognizable palette;
53
+ * it does not establish distinction between adjacent series or colorblind safety.
54
+ * Explicit mark overrides belong to the caller; unresolved alpha is preserved.
55
+ */
56
+ declare function chartColorForFill(fill: string, preferred: string): string;
57
+
44
58
  /** Portable layout geometry. No fonts, DOM, renderer, or network dependencies. */
45
59
  interface Composition {
46
60
  mode?: "auto" | "grid" | "row" | "column";
@@ -63,11 +77,18 @@ interface LayoutDiagnostic {
63
77
  path: string;
64
78
  message: string;
65
79
  }
80
+ /** Physical legacy-family selection supplied by a font provider, independently of its numeric weight. */
81
+ interface FontFaceSelection {
82
+ family: string;
83
+ bold: boolean;
84
+ italic: boolean;
85
+ }
66
86
  interface TextStyle {
67
87
  fontFamily: string;
68
88
  fontWeight: number;
69
89
  italic?: boolean;
70
90
  path?: string;
91
+ fontFace?: FontFaceSelection;
71
92
  }
72
93
  interface FontFamilies {
73
94
  heading: string;
@@ -78,16 +99,48 @@ declare function resolveFontFamilies(input: unknown): FontFamilies;
78
99
  interface TextMeasurement {
79
100
  measure: (text: string, fontSize: number, style: TextStyle) => number;
80
101
  resolveStyle?: (style: TextStyle) => TextStyle;
102
+ /** Shaped vector ink relative to the left baseline origin (positive y down).
103
+ * Null means no outline. These are not hinted/antialiased raster bounds. */
104
+ outlineBounds?: (text: string, fontSize: number, style: TextStyle) => LayoutBox | null;
81
105
  }
82
106
  type MeasureTextWidth = (text: string, fontSize: number) => number;
83
107
  declare function resolveTextStyle(style: TextStyle, measurement?: TextMeasurement): TextStyle;
84
108
  declare function textWidthMeasurer(style: TextStyle, measurement?: TextMeasurement): MeasureTextWidth;
109
+ /** Validate an optional host outline measurement without inventing raster coverage. */
110
+ declare function measureTextOutline(text: string, fontSize: number, style: TextStyle, measurement?: TextMeasurement): LayoutBox | null | undefined;
111
+ interface TextLineInk {
112
+ width: number;
113
+ y: number;
114
+ baseline: number;
115
+ height: number;
116
+ outline: LayoutBox | null;
117
+ }
118
+ interface TextPlacementLine {
119
+ x: number;
120
+ y: number;
121
+ baseline: number;
122
+ height: number;
123
+ width: number;
124
+ outline: LayoutBox | null;
125
+ }
126
+ interface TextPlacement {
127
+ alignment: 'left' | 'center' | 'right';
128
+ /** Reference-pixel clearance around vector outlines; not a universal raster guarantee. */
129
+ rasterPadding: number;
130
+ lines: TextPlacementLine[];
131
+ height: number;
132
+ overflow: boolean;
133
+ }
85
134
  interface TextFit {
86
135
  lines: string[];
87
136
  fontSize: number;
88
137
  lineHeight: number;
89
138
  overflow: boolean;
139
+ placement?: TextPlacement;
90
140
  }
141
+ /** Place complete measured lines, preserving alignment where it leaves room for ink.
142
+ * Move following baselines together when outlines need more vertical separation. */
143
+ declare function placeTextLines(lines: readonly TextLineInk[], box: LayoutBox, alignment?: TextPlacement['alignment'], rasterPadding?: number): TextPlacement;
91
144
  interface ComposedItem {
92
145
  path: string;
93
146
  field: string;
@@ -95,10 +148,18 @@ interface ComposedItem {
95
148
  value: unknown;
96
149
  payload: Record<string, unknown>;
97
150
  box: LayoutBox;
98
- text?: TextFit | RichTextFit | ListFit;
151
+ /** Optional visible card allocation; box and all accepted internals occupy its padded interior. */
152
+ frameBox?: LayoutBox;
153
+ text?: TextFit | RichTextFit | ListFit | CodeTextFit;
99
154
  textStyle?: TextStyle;
100
155
  /** Complete accepted quote internals; consumers must reuse these fits and styles. */
101
156
  quoteLayout?: QuoteLayout;
157
+ /** Complete accepted code internals, including source lines and literal tab positions. */
158
+ codeLayout?: CodeLayout;
159
+ /** Complete shared metric geometry, including its unit, label and metadata. */
160
+ metricLayout?: MetricLayout;
161
+ /** Complete timeline fields, markers and connector accepted by composition. */
162
+ timelineLayout?: TimelineLayout;
102
163
  /** Effective container settings, including inherited readability constraints. */
103
164
  composition: Composition;
104
165
  }
@@ -123,7 +184,7 @@ interface ComposedFlow {
123
184
  itemCount: number;
124
185
  slotCount: number;
125
186
  }
126
- /** Additive penalties in grid-score-v2; lower is preferred. These are not quality percentages. */
187
+ /** Additive penalties in grid-score-v8; lower is preferred. These are not quality percentages. */
127
188
  interface CompositionPenalties {
128
189
  cellProportions: number;
129
190
  fontReduction: number;
@@ -147,9 +208,13 @@ interface CompositionDecision {
147
208
  candidates: CompositionCandidate[];
148
209
  }
149
210
  interface CompositionExplanation {
150
- algorithm: 'grid-score-v2';
211
+ algorithm: 'grid-score-v8';
151
212
  /** Provided widths do not establish shaping, glyph coverage or native fidelity. */
152
213
  textMeasurement: 'estimated' | 'provided';
214
+ /** Optional vector coverage for headings and scalar/rich text, not every payload. */
215
+ textOutlines: 'provided' | 'unavailable';
216
+ /** Effective canvas reference pixels; not a universal raster tolerance. */
217
+ textRasterPadding: number;
153
218
  decisions: CompositionDecision[];
154
219
  /** Payloads whose complete internal fit is not covered by this scoring model. */
155
220
  unmeasuredPayloads: string[];
@@ -167,6 +232,13 @@ interface SlideComposition {
167
232
  }
168
233
  interface ComposeSlideOptions {
169
234
  fonts?: Partial<FontFamilies>;
235
+ /** Host-resolved alignment for shared content; slide design can override it. */
236
+ contentAlignment?: 'left' | 'center' | 'right';
237
+ titleAlignment?: 'left' | 'center' | 'right';
238
+ /** Unscaled reference pixels around provided vector outlines; defaults to 1. */
239
+ textRasterPadding?: number;
240
+ /** Host-resolved body cards. A slide's explicit design.contentBox overrides this value. */
241
+ contentBox?: boolean;
170
242
  textMeasurement?: TextMeasurement;
171
243
  width?: number;
172
244
  height?: number;
@@ -184,7 +256,7 @@ declare class OPFCompositionError extends Error {
184
256
  /** Deterministic estimate, not a font shaping engine. Preserves explicit line breaks. */
185
257
  declare function measureText(text: string, fontSize: number): number;
186
258
  declare function wrapText(text: string, width: number, fontSize: number, measure?: MeasureTextWidth): string[];
187
- declare function fitText(text: string, box: LayoutBox, requestedSize?: number, minFontSize?: number, measure?: MeasureTextWidth): TextFit;
259
+ declare function fitText(text: string, box: LayoutBox, requestedSize?: number, minFontSize?: number, measure?: MeasureTextWidth): SourceTextFit;
188
260
  interface QuoteContent {
189
261
  text: string;
190
262
  attribution?: string;
@@ -238,6 +310,193 @@ interface QuoteLayoutOptions {
238
310
  * check overflow before accepting the parts. Line boxes are not shaped glyph or native raster bounds.
239
311
  */
240
312
  declare function layoutQuote(value: string | QuoteContent, box: LayoutBox, options?: QuoteLayoutOptions): QuoteLayout;
313
+ interface MetricContent {
314
+ value: string | number;
315
+ label?: string;
316
+ description?: string;
317
+ unit?: string;
318
+ delta?: string | number;
319
+ trend?: 'up' | 'down' | 'flat';
320
+ }
321
+ /** Ranges address String(sourceValue), not the numeric token spelling in serialized JSON. */
322
+ interface MetricTextSource {
323
+ path: string;
324
+ value: string | number;
325
+ start: number;
326
+ end: number;
327
+ }
328
+ interface MetricTextPart {
329
+ role: keyof MetricContent;
330
+ path: string;
331
+ text: string;
332
+ sources: MetricTextSource[];
333
+ /** Empty optional fields retain their source mapping but occupy no visible space. */
334
+ visible: boolean;
335
+ /** Accepted absolute origin/baseline for each fit.sourceLines entry, including blank lines. */
336
+ linePositions: {
337
+ x: number;
338
+ baseline: number;
339
+ }[];
340
+ box: LayoutBox;
341
+ requestedFontSize: number;
342
+ minFontSize: number;
343
+ requestedStyle: TextStyle;
344
+ style: TextStyle;
345
+ /** Source-preserving line/segment representation shared with code, with proportional fonts. */
346
+ fit?: CodeTextFit;
347
+ }
348
+ interface MetricLayoutDiagnostic extends LayoutDiagnostic {
349
+ reason: 'invalid-part-box' | 'part-outside-cell' | 'text-fit' | 'part-overlap';
350
+ parts: MetricTextPart['role'][];
351
+ }
352
+ interface MetricLayout {
353
+ algorithm: 'metric-flow-v1';
354
+ alignment: 'left' | 'center' | 'right';
355
+ textMeasurement: 'estimated' | 'provided';
356
+ arrangement: 'inline-unit' | 'stacked';
357
+ /** At most 48 arrangements; value fitting is bounded by 77 reference-size trials per arrangement. */
358
+ attempts: number;
359
+ parts: MetricTextPart[];
360
+ diagnostics: MetricLayoutDiagnostic[];
361
+ overflow: boolean;
362
+ }
363
+ interface MetricLayoutOptions extends QuoteLayoutOptions {
364
+ align?: 'left' | 'center' | 'right';
365
+ /** Unscaled clearance around available vector outlines; defaults to one reference pixel. */
366
+ textRasterPadding?: number;
367
+ }
368
+ /**
369
+ * Measure every metric field before accepting geometry. Short single-line values and units can
370
+ * share a baseline; longer values or units stack. No locale formatting, trend icons or rewritten
371
+ * source text are invented. Consumers must reuse these accepted parts and source ranges.
372
+ */
373
+ declare function layoutMetric(value: string | number | MetricContent, box: LayoutBox, options?: MetricLayoutOptions): MetricLayout;
374
+ interface TimelineEvent {
375
+ when?: string;
376
+ what: string;
377
+ description?: string;
378
+ }
379
+ interface TimelineContent {
380
+ name?: string;
381
+ description?: string;
382
+ events: TimelineEvent[];
383
+ }
384
+ interface TimelineTextPart {
385
+ role: 'name' | 'description' | 'when' | 'what' | 'event-description';
386
+ eventIndex?: number;
387
+ path: string;
388
+ text: string;
389
+ sources: {
390
+ path: string;
391
+ start: number;
392
+ end: number;
393
+ }[];
394
+ box: LayoutBox;
395
+ alignment: 'left' | 'center';
396
+ requestedFontSize: number;
397
+ minFontSize: number;
398
+ requestedStyle: TextStyle;
399
+ style: TextStyle;
400
+ fit?: SourceTextFit;
401
+ }
402
+ interface TimelineLayoutDiagnostic extends LayoutDiagnostic {
403
+ reason: 'text-fit' | 'part-outside-cell' | 'event-space';
404
+ }
405
+ interface TimelineLayout {
406
+ algorithm: 'timeline-flow-v1';
407
+ arrangement: 'alternating' | 'vertical';
408
+ attempts: number;
409
+ textMeasurement: 'provided' | 'estimated';
410
+ textOutlines: 'provided' | 'unavailable';
411
+ parts: TimelineTextPart[];
412
+ markers: {
413
+ path: string;
414
+ eventIndex: number;
415
+ x: number;
416
+ y: number;
417
+ radius: number;
418
+ }[];
419
+ connector: {
420
+ x1: number;
421
+ y1: number;
422
+ x2: number;
423
+ y2: number;
424
+ };
425
+ diagnostics: TimelineLayoutDiagnostic[];
426
+ overflow: boolean;
427
+ }
428
+ interface TimelineLayoutOptions extends QuoteLayoutOptions {
429
+ textRasterPadding?: number;
430
+ }
431
+ /** Preserve event order and field boundaries while trying at most 50 readable arrangements. */
432
+ declare function layoutTimeline(value: readonly TimelineEvent[] | TimelineContent, box: LayoutBox, options?: TimelineLayoutOptions): TimelineLayout;
433
+ interface CodeContent {
434
+ source: string;
435
+ language?: string;
436
+ filename?: string;
437
+ }
438
+ interface TextLineSegment {
439
+ kind: 'text' | 'tab';
440
+ start: number;
441
+ end: number;
442
+ /** Measured position relative to this displayed line's origin, in reference pixels. */
443
+ x: number;
444
+ width: number;
445
+ }
446
+ /** Exact half-open UTF-16 offsets into the part text, including consumed hard line breaks. */
447
+ interface TextLineSource {
448
+ start: number;
449
+ end: number;
450
+ nextStart: number;
451
+ boundary: 'soft' | 'hard' | 'end';
452
+ width: number;
453
+ /** Explicit tab placement is required in SVG; CSS tab-size alone does not implement it. */
454
+ segments: TextLineSegment[];
455
+ }
456
+ interface SourceTextFit extends TextFit {
457
+ sourceLines: TextLineSource[];
458
+ /** Tabs advance to the next multiple of four measured spaces from each displayed line's origin. */
459
+ tabSize: 4;
460
+ tabWidth: number;
461
+ }
462
+ /** Compatibility names for the shared source-preserving line contract. */
463
+ type CodeLineSegment = TextLineSegment;
464
+ type CodeLineSource = TextLineSource;
465
+ interface CodeTextFit extends SourceTextFit {
466
+ }
467
+ interface CodeTextPart {
468
+ role: 'filename' | 'language' | 'body';
469
+ path: string;
470
+ /** Original text, without case conversion, whitespace normalization or discarded newlines. */
471
+ text: string;
472
+ sources: {
473
+ path: string;
474
+ start: number;
475
+ end: number;
476
+ }[];
477
+ generated: boolean;
478
+ box: LayoutBox;
479
+ requestedFontSize: number;
480
+ minFontSize: number;
481
+ requestedStyle: TextStyle;
482
+ style: TextStyle;
483
+ fit?: CodeTextFit;
484
+ }
485
+ interface CodeLayoutDiagnostic extends LayoutDiagnostic {
486
+ reason: 'invalid-part-box' | 'part-outside-cell' | 'text-fit' | 'part-overlap';
487
+ parts: CodeTextPart['role'][];
488
+ }
489
+ interface CodeLayout {
490
+ algorithm: 'code-flow-v1';
491
+ textMeasurement: 'estimated' | 'provided';
492
+ parts: CodeTextPart[];
493
+ diagnostics: CodeLayoutDiagnostic[];
494
+ overflow: boolean;
495
+ }
496
+ interface CodeLayoutOptions extends QuoteLayoutOptions {
497
+ }
498
+ /** Shared filename/language/body allocation. Consumers must reuse the accepted fits and styles. */
499
+ declare function layoutCode(value: string | CodeContent, box: LayoutBox, options?: CodeLayoutOptions): CodeLayout;
241
500
  interface RichTextRun {
242
501
  text: string;
243
502
  bold?: boolean;
@@ -362,4 +621,4 @@ declare function resolveCanvasDimensions(input: unknown): {
362
621
  height: number;
363
622
  };
364
623
 
365
- export { type ComposeSlideOptions, type ComposedFlow, type ComposedGroup, type ComposedItem, type Composition, type CompositionCandidate, type CompositionDecision, type CompositionExplanation, type CompositionPenalties, type CompositionTrack, type FontFamilies, type LayoutBox, type LayoutDiagnostic, type ListEntryLayout, type ListFit, type ListText, type ListValue, MAX_COMPOSITION_DEPTH, type MeasureTextWidth, OPFCompositionError, type QuoteContent, type QuoteLayout, type QuoteLayoutDiagnostic, type QuoteLayoutOptions, type QuoteTextPart, type QuoteTextSource, type RichTextFit, type RichTextFragment, type RichTextLine, type RichTextOptions, type RichTextRun, type SlideComposition, type TableBorder, type TableCellLayout, type TableCellStyle, type TableGrid, type TableGridCell, type TableGridIssue, type TableLayout, type TableLayoutOptions, type TableRowLayout, type TextFit, type TextMeasurement, type TextStyle, composeSlide, fitList, fitRichText, fitText, layoutQuote, layoutTable, measureText, resolveCanvasDimensions, resolveFontFamilies, resolveTextStyle, tableGrid, tableRowBoundaries, textWidthMeasurer, wrapText };
624
+ export { type CodeContent, type CodeLayout, type CodeLayoutDiagnostic, type CodeLayoutOptions, type CodeLineSegment, type CodeLineSource, type CodeTextFit, type CodeTextPart, type ComposeSlideOptions, type ComposedFlow, type ComposedGroup, type ComposedItem, type Composition, type CompositionCandidate, type CompositionDecision, type CompositionExplanation, type CompositionPenalties, type CompositionTrack, type FontFaceSelection, type FontFamilies, type LayoutBox, type LayoutDiagnostic, type ListEntryLayout, type ListFit, type ListText, type ListValue, MAX_COMPOSITION_DEPTH, type MeasureTextWidth, type MetricContent, type MetricLayout, type MetricLayoutDiagnostic, type MetricLayoutOptions, type MetricTextPart, type MetricTextSource, OPFCompositionError, type QuoteContent, type QuoteLayout, type QuoteLayoutDiagnostic, type QuoteLayoutOptions, type QuoteTextPart, type QuoteTextSource, type RichTextFit, type RichTextFragment, type RichTextLine, type RichTextOptions, type RichTextRun, type SlideComposition, type SourceTextFit, type TableBorder, type TableCellLayout, type TableCellStyle, type TableGrid, type TableGridCell, type TableGridIssue, type TableLayout, type TableLayoutOptions, type TableRowLayout, type TextFit, type TextLineInk, type TextLineSegment, type TextLineSource, type TextMeasurement, type TextPlacement, type TextPlacementLine, type TextStyle, type TimelineContent, type TimelineEvent, type TimelineLayout, type TimelineLayoutDiagnostic, type TimelineLayoutOptions, type TimelineTextPart, chartColorForFill, colorContrast, composeSlide, fitList, fitRichText, fitText, layoutCode, layoutMetric, layoutQuote, layoutTable, layoutTimeline, measureText, measureTextOutline, placeTextLines, resolveCanvasDimensions, resolveFontFamilies, resolveTextStyle, tableGrid, tableRowBoundaries, textColorForFill, textWidthMeasurer, wrapText };
@@ -1 +1 @@
1
- export { MAX_COMPOSITION_DEPTH, OPFCompositionError, composeSlide, fitList, fitRichText, fitText, layoutQuote, layoutTable, measureText, resolveCanvasDimensions, resolveFontFamilies, resolveTextStyle, tableGrid, tableRowBoundaries, textWidthMeasurer, wrapText } from './chunk-NEQAPMS2.js';
1
+ export { MAX_COMPOSITION_DEPTH, OPFCompositionError, chartColorForFill, colorContrast, composeSlide, fitList, fitRichText, fitText, layoutCode, layoutMetric, layoutQuote, layoutTable, layoutTimeline, measureText, measureTextOutline, placeTextLines, resolveCanvasDimensions, resolveFontFamilies, resolveTextStyle, tableGrid, tableRowBoundaries, textColorForFill, textWidthMeasurer, wrapText } from './chunk-BAWLYSUN.js';