@unmade/text-renderer 1.1.1 → 1.3.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 (36) hide show
  1. package/README.md +231 -0
  2. package/dist/{__vite-browser-external-CijFC1_R.js → __vite-browser-external-oLchLuMx.js} +1 -1
  3. package/dist/baselines/index.d.ts +19 -0
  4. package/dist/ce-adapter/index.cjs +1 -1
  5. package/dist/ce-adapter/index.js +1 -1
  6. package/dist/fitting/index.d.ts +86 -0
  7. package/dist/helpers/bbox.d.ts +2 -1
  8. package/dist/index.d.ts +2 -0
  9. package/dist/index.js +1143 -339
  10. package/dist/layout/index.d.ts +77 -0
  11. package/dist/node/baselines/index.d.ts +19 -0
  12. package/dist/node/fitting/index.d.ts +86 -0
  13. package/dist/node/helpers/bbox.d.ts +2 -1
  14. package/dist/node/index.d.ts +2 -0
  15. package/dist/node/index.js +446 -201
  16. package/dist/node/layout/index.d.ts +77 -0
  17. package/dist/node/renderText.d.ts +3 -0
  18. package/dist/node/rendering/index.d.ts +2 -2
  19. package/dist/node/types.d.ts +3 -0
  20. package/dist/renderText.d.ts +3 -0
  21. package/dist/rendering/index.d.ts +2 -2
  22. package/dist/{chunk-3b4jIN3o.js → rolldown-runtime-DtPi1Y-2.js} +2 -2
  23. package/dist/types.d.ts +3 -0
  24. package/dist/worker/baselines/index.d.ts +19 -0
  25. package/dist/worker/fitting/index.d.ts +86 -0
  26. package/dist/worker/helpers/bbox.d.ts +2 -1
  27. package/dist/worker/index.d.ts +2 -0
  28. package/dist/worker/index.js +2846 -2377
  29. package/dist/worker/layout/index.d.ts +77 -0
  30. package/dist/worker/renderText.d.ts +3 -0
  31. package/dist/worker/rendering/index.d.ts +2 -2
  32. package/dist/worker/types.d.ts +3 -0
  33. package/package.json +12 -5
  34. package/dist/helpers/matrix.d.ts +0 -8
  35. package/dist/node/helpers/matrix.d.ts +0 -8
  36. package/dist/worker/helpers/matrix.d.ts +0 -8
@@ -0,0 +1,77 @@
1
+ import { LoadedFont } from '../loading';
2
+ import { PathEntry } from '../rendering';
3
+ import { BaselineConfig, BaselineType, BoxDimensions, ColourMap, HorizontalTextAlignmentValues, MeasurementUnits, PhysicalSize, TextSpacing, VerticalTextAlignmentValues } from '../types';
4
+ export interface LayoutTextOptions {
5
+ font: LoadedFont;
6
+ text: string;
7
+ boxDimensions: BoxDimensions;
8
+ physicalSize: PhysicalSize;
9
+ /** When provided, bypasses the physicalSize → font size calculation. */
10
+ fontSize?: number;
11
+ spacing?: TextSpacing;
12
+ baseline?: BaselineType | BaselineConfig;
13
+ horizontalAlignment?: HorizontalTextAlignmentValues;
14
+ verticalAlignment?: VerticalTextAlignmentValues;
15
+ colourMap?: ColourMap;
16
+ }
17
+ export interface TextLayout {
18
+ fontSize: number;
19
+ /** Lines as laid out — curved and custom baselines collapse to one. */
20
+ lines: string[];
21
+ /** Every glyph, positioned. Rendering draws these; measuring bounds them. */
22
+ paths: PathEntry[];
23
+ spacing: TextSpacing;
24
+ tallestHeight: number;
25
+ totalHeight: number;
26
+ maxBaseline: number;
27
+ }
28
+ /**
29
+ * Position every glyph of `text` within `boxDimensions`.
30
+ *
31
+ * Shared by rendering and measuring so the two cannot disagree: what gets
32
+ * measured is the same set of positioned glyphs that gets drawn. The
33
+ * configuration engine has two independent measurement paths that have drifted
34
+ * apart over time, which is the outcome this exists to prevent.
35
+ */
36
+ export declare const layoutText: (options: LayoutTextOptions) => TextLayout;
37
+ export interface MeasuredLine {
38
+ text: string;
39
+ width: number;
40
+ height: number;
41
+ baseline: number;
42
+ }
43
+ export interface TextMeasurement {
44
+ fontSize: number;
45
+ /** Bounds of the drawn glyphs, in the box's pixel coordinates. */
46
+ bbox: {
47
+ x: number;
48
+ y: number;
49
+ width: number;
50
+ height: number;
51
+ };
52
+ physicalWidth: number;
53
+ physicalHeight: number;
54
+ physicalUnits: MeasurementUnits;
55
+ /**
56
+ * Physical height of a standard capital at this font size — the cap height.
57
+ *
58
+ * Not derivable from `physicalHeight`: that measures the glyphs actually
59
+ * present, so it moves with descenders and accents, whereas cap height is a
60
+ * property of the font and size alone. It is persisted with the design and
61
+ * reaches the factory, so it has to be reported rather than inferred.
62
+ */
63
+ physicalCapHeight: number;
64
+ lines: MeasuredLine[];
65
+ }
66
+ /**
67
+ * Measure text as it would be drawn.
68
+ *
69
+ * Bounds the same positioned glyphs `renderText` draws, rather than
70
+ * re-deriving dimensions from font metrics — so a measurement can't disagree
71
+ * with the artwork it describes. In particular a curved line's bounds follow
72
+ * the arch, which a flat metrics calculation cannot know about.
73
+ *
74
+ * Throws where `renderText` would throw, for the same reasons. If this
75
+ * succeeds, rendering the same options will too.
76
+ */
77
+ export declare const measureText: (options: LayoutTextOptions) => TextMeasurement;
@@ -28,6 +28,25 @@ export interface GenerateCurvedBaselineOptions {
28
28
  textHeight: number;
29
29
  verticalAlignment: VerticalTextAlignmentValues;
30
30
  }
31
+ /** Where the arch sits vertically: its peak, and the baseline at its ends. */
32
+ export interface CurvedBaselineGeometry {
33
+ /** y of the peak — the highest point of the baseline. */
34
+ curveTop: number;
35
+ /** y of the two ends — the lowest points of the baseline. */
36
+ curveBottom: number;
37
+ /** Horizontal gap left at each end so the outer glyphs are not clipped. */
38
+ curveXInset: number;
39
+ }
40
+ /**
41
+ * The arch's placement, on its own so that the path and anything reasoning
42
+ * about where the text will land come from one calculation.
43
+ *
44
+ * Sizing needs this: it has to know where the renderer will actually put the
45
+ * baseline before it can say whether the text riding it clears the box. Working
46
+ * that out separately is what let the fit engine believe a size fitted while
47
+ * the render clipped.
48
+ */
49
+ export declare const curvedBaselineGeometry: ({ boxDimensions, baseline, textHeight, verticalAlignment, }: GenerateCurvedBaselineOptions) => CurvedBaselineGeometry;
31
50
  /**
32
51
  * Generate a path command to curve text and align that text to the top of the box.
33
52
  * Uses two quadratic bezier curves — one from the bottom left to the peak, and a
@@ -0,0 +1,86 @@
1
+ import { LoadedFont } from '../loading';
2
+ import { BaselineConfig, BaselineType, BoxDimensions, PhysicalSize, TextSpacing, VerticalTextAlignmentValues } from '../types';
3
+ /**
4
+ * How the font size is arrived at.
5
+ *
6
+ * `fitToBox` solves for the largest size the box allows. The other two pin the
7
+ * size and leave the text length as the free variable instead — which is what a
8
+ * fixed size preset is: hold 60mm, and fit as many characters as will go. All
9
+ * three then behave identically downstream, so a pinned size is an ordinary mode
10
+ * rather than a special path.
11
+ */
12
+ export type FitSize = {
13
+ mode: 'fitToBox';
14
+ minFontSize: number;
15
+ maxFontSize: number;
16
+ } | {
17
+ mode: 'fontSize';
18
+ fontSize: number;
19
+ } | {
20
+ mode: 'physicalSize';
21
+ physicalSize: PhysicalSize;
22
+ };
23
+ export interface FitTextToBoxOptions {
24
+ font: LoadedFont;
25
+ text: string;
26
+ boxDimensions: BoxDimensions;
27
+ size: FitSize;
28
+ spacing?: TextSpacing;
29
+ baseline?: BaselineType | BaselineConfig;
30
+ verticalAlignment?: VerticalTextAlignmentValues;
31
+ }
32
+ /**
33
+ * Which limit decided the outcome.
34
+ *
35
+ * - `width` — the box width, or the arc/custom path length for a non-flat baseline
36
+ * - `height` — the box height
37
+ * - `requestedSize` — the size the caller asked for, reached before the box
38
+ * mattered. Covers both hitting `maxFontSize` when solving and simply getting
39
+ * the pinned size that was requested.
40
+ */
41
+ export type FitConstraint = 'width' | 'height' | 'requestedSize';
42
+ export interface FitTextToBoxResult {
43
+ fontSize: number;
44
+ /** False when the text cannot fit within the box even at minFontSize. */
45
+ fits: boolean;
46
+ /**
47
+ * When `fits`, the limit that capped the size. When not, the limit that could
48
+ * not be satisfied.
49
+ *
50
+ * Callers need this to tell one failure from another: overflowing the width
51
+ * and overflowing the height call for different responses, and in the
52
+ * configuration engine they surface as different messages to the user.
53
+ */
54
+ boundBy: FitConstraint;
55
+ }
56
+ export interface FitAndTruncateTextToBoxResult extends FitTextToBoxResult {
57
+ text: string;
58
+ truncated: boolean;
59
+ /**
60
+ * How the text was shortened. Reported separately because dropping a line and
61
+ * trimming characters are different outcomes with different messages, and a
62
+ * single pass can do both — drop a line, then still need to trim.
63
+ */
64
+ linesDropped: number;
65
+ charactersRemoved: number;
66
+ }
67
+ /**
68
+ * Calculate the font size at which `text` fills `boxDimensions` as closely
69
+ * as possible without overflowing, for any baseline shape. This replaces
70
+ * binary-searching for a fitting font size: flat and custom baselines are
71
+ * solved in one step, from a single measurement, while curved baselines need
72
+ * a bounded numeric solve (see solveCurvedFit).
73
+ *
74
+ * `fits: false` means the text doesn't fit even at minFontSize — the caller
75
+ * (e.g. fitAndTruncateTextToBox) should either shorten the text or accept
76
+ * the overflow at minFontSize.
77
+ */
78
+ export declare const getFontSizeToFitBox: (options: FitTextToBoxOptions) => FitTextToBoxResult;
79
+ /**
80
+ * Like getFontSizeToFitBox, but when text doesn't fit even at minFontSize,
81
+ * shortens it (dropping the last whole line first if there is more than
82
+ * one, otherwise trimming trailing characters) and retries until it fits
83
+ * or there's nothing left to remove. Mirrors CE's existing truncation UX —
84
+ * text lost this way is a caller/product concern, not a rendering one.
85
+ */
86
+ export declare const fitAndTruncateTextToBox: (options: FitTextToBoxOptions) => FitAndTruncateTextToBoxResult;
@@ -1,3 +1,4 @@
1
+ import { PlatformMatrix } from '../types';
1
2
  export declare class Box {
2
3
  corners: DOMPoint[];
3
4
  x1: number;
@@ -13,7 +14,7 @@ export declare class Box {
13
14
  get x4(): number;
14
15
  get y4(): number;
15
16
  constructor(x1: number, y1: number, x2: number, y2: number, x3?: number, y3?: number, x4?: number, y4?: number);
16
- transform(matrix: DOMMatrix): void;
17
+ transform(matrix: PlatformMatrix): void;
17
18
  toPointsArray(): number[][];
18
19
  getBBox(): BBox;
19
20
  }
@@ -1,7 +1,9 @@
1
1
  import { GetRenderedTextOptions } from './types';
2
2
  export { type GenerateCurvedBaselineOptions, type GenerateFlatBaselineOptions, generateCurvedBaseline, generateFlatBaseline, normaliseBaselineOption, validateCustomBaselinePath, } from './baselines';
3
3
  export { DEFAULT_OUTLINE_FACTOR, DEFAULT_SPACING, LINE_HEIGHT, } from './constants';
4
+ export { type FitAndTruncateTextToBoxResult, type FitConstraint, type FitSize, type FitTextToBoxOptions, type FitTextToBoxResult, fitAndTruncateTextToBox, getFontSizeToFitBox, } from './fitting';
4
5
  export { getFontColours } from './glyph/svg-glyph-converter';
6
+ export { type LayoutTextOptions, layoutText, type MeasuredLine, measureText, type TextLayout, type TextMeasurement, } from './layout';
5
7
  export { getFontCache, type LoadedFont, loadFont } from './loading';
6
8
  export { type GetFontSizeFromPhysicalSizeOptions, type GetTextMetricsAtFontSizeOptions, getFontSizeFromPhysicalSize, getTextMetricsAtFontSize, } from './measuring';
7
9
  export type { GetTextAsPathDataOptions, SvgGlyphEntry } from './rendering';