@unmade/text-renderer 1.1.1 → 1.2.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.
@@ -5,9 +5,9 @@ var e = Object.create, t = Object.defineProperty, n = Object.getOwnPropertyDescr
5
5
  enumerable: !(s = n(i, d)) || s.enumerable
6
6
  });
7
7
  return e;
8
- }, c = (n, r, a) => (a = n == null ? {} : e(i(n)), s(r || !n || !n.__esModule ? t(a, "default", {
8
+ }, c = (n, r, o) => (o = n == null ? {} : e(i(n)), s(r || !n || !n.__esModule || !a.call(n, "default") ? t(o, "default", {
9
9
  value: n,
10
10
  enumerable: !0
11
- }) : a, n));
11
+ }) : o, n));
12
12
  //#endregion
13
13
  export { c as n, o as t };
package/dist/types.d.ts CHANGED
@@ -1,4 +1,7 @@
1
+ import { DOMMatrix } from '@unmade/platform';
1
2
  import { LoadedFont } from './loading';
3
+ /** The platform's matrix, which is not the same shape as the DOM's DOMMatrix. */
4
+ export type PlatformMatrix = InstanceType<typeof DOMMatrix>;
2
5
  export type MeasurementUnits = 'mm' | 'in';
3
6
  export type BoxDimensions = {
4
7
  width: number;
@@ -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: for flat and custom baselines
71
+ * it's a direct O(1) calculation; for curved baselines it's a bounded
72
+ * 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,6 +1,7 @@
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';
5
6
  export { getFontCache, type LoadedFont, loadFont } from './loading';
6
7
  export { type GetFontSizeFromPhysicalSizeOptions, type GetTextMetricsAtFontSizeOptions, getFontSizeFromPhysicalSize, getTextMetricsAtFontSize, } from './measuring';