@solidtv/renderer 1.2.0 → 1.2.2
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.
- package/dist/src/common/CommonTypes.d.ts +5 -0
- package/dist/src/core/CoreTextNode.d.ts +17 -0
- package/dist/src/core/CoreTextNode.js +27 -3
- package/dist/src/core/CoreTextNode.js.map +1 -1
- package/dist/src/core/shaders/templates/BorderTemplate.d.ts +1 -1
- package/dist/src/core/shaders/templates/BorderTemplate.js +1 -1
- package/dist/src/core/shaders/templates/BorderTemplate.js.map +1 -1
- package/dist/src/core/text-rendering/CanvasTextRenderer.js +7 -0
- package/dist/src/core/text-rendering/CanvasTextRenderer.js.map +1 -1
- package/dist/src/core/text-rendering/SdfFontHandler.js +4 -3
- package/dist/src/core/text-rendering/SdfFontHandler.js.map +1 -1
- package/dist/src/core/text-rendering/SdfTextRenderer.js +11 -0
- package/dist/src/core/text-rendering/SdfTextRenderer.js.map +1 -1
- package/dist/src/core/text-rendering/TextLayoutEngine.js +32 -24
- package/dist/src/core/text-rendering/TextLayoutEngine.js.map +1 -1
- package/dist/src/core/text-rendering/TextRenderer.d.ts +34 -8
- package/dist/src/main-api/Renderer.d.ts +8 -5
- package/dist/src/main-api/Renderer.js +1 -1
- package/dist/src/main-api/Renderer.js.map +1 -1
- package/dist/tsconfig.dist.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/common/CommonTypes.ts +5 -0
- package/src/core/CoreTextNode.ts +28 -3
- package/src/core/shaders/templates/BorderTemplate.ts +2 -2
- package/src/core/text-rendering/CanvasTextRenderer.ts +9 -0
- package/src/core/text-rendering/SdfFontHandler.ts +4 -3
- package/src/core/text-rendering/SdfTextRenderer.ts +13 -0
- package/src/core/text-rendering/TextLayoutEngine.ts +31 -24
- package/src/core/text-rendering/TextRenderer.ts +34 -8
- package/src/main-api/Renderer.ts +9 -6
|
@@ -69,10 +69,10 @@ export const normalizeFontMetrics = (
|
|
|
69
69
|
/**
|
|
70
70
|
* Engine-wide per-line baseline anchor. Configured once at renderer creation
|
|
71
71
|
* via {@link RendererMainSettings.textBaselineMode}, not exposed per node so
|
|
72
|
-
* a single app can't mix anchor models across its text. Defaults to
|
|
73
|
-
* — see {@link TextBaselineMode} for the rationale.
|
|
72
|
+
* a single app can't mix anchor models across its text. Defaults to
|
|
73
|
+
* `'optical'` — see {@link TextBaselineMode} for the rationale.
|
|
74
74
|
*/
|
|
75
|
-
let baselineMode: TextBaselineMode = '
|
|
75
|
+
let baselineMode: TextBaselineMode = 'optical';
|
|
76
76
|
|
|
77
77
|
/**
|
|
78
78
|
* Sets the engine-wide baseline anchor. Called by `Stage` during construction;
|
|
@@ -185,44 +185,51 @@ export const mapTextLayout = (
|
|
|
185
185
|
|
|
186
186
|
// line[4] stores the alphabetic baseline Y of each line in screen px.
|
|
187
187
|
//
|
|
188
|
-
// ── Per-line anchor:
|
|
188
|
+
// ── Per-line anchor: optical centering (default) ─────────────────────
|
|
189
189
|
//
|
|
190
|
-
//
|
|
190
|
+
// anchor = (capHeight + xHeight) / 2
|
|
191
|
+
// baselineY = lineHeight/2 + anchor/2 + i × lineHeight
|
|
191
192
|
//
|
|
192
|
-
//
|
|
193
|
-
//
|
|
194
|
-
//
|
|
195
|
-
//
|
|
196
|
-
//
|
|
197
|
-
//
|
|
198
|
-
// CSS button behavior in browsers.
|
|
193
|
+
// Pure cap-height centering reads slightly low for lowercase-heavy
|
|
194
|
+
// mixed-case strings like "Button" because the visual mass sits in
|
|
195
|
+
// the x-height band, below cap-center. Pure x-height centering goes
|
|
196
|
+
// the other way — caps appear high. Splitting the difference (mean of
|
|
197
|
+
// cap and x heights) lands at the optical center for the broadest mix
|
|
198
|
+
// of UI text. This is the same heuristic macOS/iOS controls use.
|
|
199
199
|
//
|
|
200
200
|
// Alternative anchors considered (kept here for the record):
|
|
201
201
|
//
|
|
202
|
+
// ── cap-height centering ────────────────────────────────────────────
|
|
203
|
+
// baselineY = lineHeight/2 + capHeight/2 + i × lineHeight
|
|
204
|
+
// Caps bracket the mid-line symmetrically. Right for all-caps or
|
|
205
|
+
// numeric content (timers, badges); reads low for "Button".
|
|
206
|
+
//
|
|
202
207
|
// ── x-height centering ──────────────────────────────────────────────
|
|
203
|
-
// baselineY
|
|
204
|
-
// Centers lowercase
|
|
205
|
-
//
|
|
206
|
-
// capitals appear high in headings/labels — wrong default for TV UI.
|
|
208
|
+
// baselineY = lineHeight/2 + xHeight/2 + i × lineHeight
|
|
209
|
+
// Centers lowercase. Matches CSS inline `vertical-align: middle`.
|
|
210
|
+
// Reads well for body text; capitals appear high in headings.
|
|
207
211
|
//
|
|
208
212
|
// ── line-box centering (pre-cap-height behavior) ───────────────────
|
|
209
213
|
// const halfLeading = (lineHeightPx − bareLineHeight) / 2
|
|
210
|
-
// baselineY
|
|
211
|
-
// Centers the abstract asc-to-desc-plus-leading rectangle.
|
|
212
|
-
//
|
|
213
|
-
//
|
|
214
|
-
// wrong.
|
|
214
|
+
// baselineY = halfLeading + ascender + i × lineHeight
|
|
215
|
+
// Centers the abstract asc-to-desc-plus-leading rectangle. Ink lands
|
|
216
|
+
// noticeably high because asc/(asc−desc) is asymmetric for most
|
|
217
|
+
// Latin fonts (~4.2:1 for Ubuntu). Mathematically tidy, visually wrong.
|
|
215
218
|
//
|
|
216
219
|
// The active anchor is configured at renderer creation via
|
|
217
|
-
// `RendererMainSettings.textBaselineMode`. Defaults to `'
|
|
220
|
+
// `RendererMainSettings.textBaselineMode`. Defaults to `'optical'`.
|
|
218
221
|
let firstBaselineY: number;
|
|
219
|
-
if (baselineMode === '
|
|
222
|
+
if (baselineMode === 'cap') {
|
|
223
|
+
firstBaselineY = (lineHeightPx + metrics.capHeight) * 0.5;
|
|
224
|
+
} else if (baselineMode === 'x') {
|
|
220
225
|
firstBaselineY = (lineHeightPx + metrics.xHeight) * 0.5;
|
|
221
226
|
} else if (baselineMode === 'linebox') {
|
|
222
227
|
const halfLeading = (lineHeightPx - bareLineHeight) * 0.5;
|
|
223
228
|
firstBaselineY = halfLeading + metrics.ascender;
|
|
224
229
|
} else {
|
|
225
|
-
|
|
230
|
+
// 'optical' — mean of cap-height and x-height
|
|
231
|
+
const opticalAnchor = (metrics.capHeight + metrics.xHeight) * 0.5;
|
|
232
|
+
firstBaselineY = (lineHeightPx + opticalAnchor) * 0.5;
|
|
226
233
|
}
|
|
227
234
|
for (let i = 0; i < effectiveLineAmount; i++) {
|
|
228
235
|
const line = lines[i] as TextLineStruct;
|
|
@@ -21,17 +21,20 @@ export type TextRenderers = 'canvas' | 'sdf';
|
|
|
21
21
|
* cannot be overridden per node — see the engine-wide reasoning in
|
|
22
22
|
* `TextLayoutEngine.mapTextLayout`.
|
|
23
23
|
*
|
|
24
|
-
* - `'
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
* - `'
|
|
24
|
+
* - `'optical'` (default): the midpoint of cap-height and x-height is
|
|
25
|
+
* centered. Looks visually centered for mixed-case text — the
|
|
26
|
+
* sweet spot between `'cap'` (which can read low for lowercase-heavy
|
|
27
|
+
* strings) and `'x'` (which can read high for headings).
|
|
28
|
+
* - `'cap'`: capital letters centered. Use when content is mostly
|
|
29
|
+
* uppercase or numeric (badges, timers). Mixed-case strings like
|
|
30
|
+
* "Button" may read slightly low.
|
|
31
|
+
* - `'x'`: lowercase x-height centered. Best for running body text;
|
|
29
32
|
* capitals appear slightly high in headings.
|
|
30
33
|
* - `'linebox'`: legacy. Centers the abstract asc-to-desc-plus-leading
|
|
31
34
|
* rectangle. Mathematically tidy but visually unbalanced because most
|
|
32
35
|
* Latin fonts have asymmetric asc/desc ratios.
|
|
33
36
|
*/
|
|
34
|
-
export type TextBaselineMode = 'cap' | 'x' | 'linebox';
|
|
37
|
+
export type TextBaselineMode = 'optical' | 'cap' | 'x' | 'linebox';
|
|
35
38
|
/**
|
|
36
39
|
* Structure mapping font family names to a set of font faces.
|
|
37
40
|
*/
|
|
@@ -231,10 +234,13 @@ export interface TrProps extends TrFontProps {
|
|
|
231
234
|
*/
|
|
232
235
|
maxLines: number;
|
|
233
236
|
/**
|
|
234
|
-
* Vertical alignment of the text block within
|
|
237
|
+
* Vertical alignment of the text block within its containing box.
|
|
235
238
|
*
|
|
236
239
|
* @remarks
|
|
237
|
-
*
|
|
240
|
+
* The containing box is `maxHeight` if set, otherwise the node's
|
|
241
|
+
* own `h` (which a flex parent or the user may have grown beyond
|
|
242
|
+
* the intrinsic text height). Activates whenever the box is taller
|
|
243
|
+
* than the intrinsic text height. Composes with `textBaselineMode`
|
|
238
244
|
* (per-line anchor). CSS line-box semantics — `'top'` leaves
|
|
239
245
|
* half-leading above the first line's cap-top; `'bottom'` leaves
|
|
240
246
|
* half-leading below the last line's descender.
|
|
@@ -314,6 +320,11 @@ export interface TextLayout {
|
|
|
314
320
|
* Total text height
|
|
315
321
|
*/
|
|
316
322
|
height: number;
|
|
323
|
+
/**
|
|
324
|
+
* Trimmed text height — cap-top of the first line to descender bottom
|
|
325
|
+
* of the last line. See `TextRenderInfo.trimmedHeight`.
|
|
326
|
+
*/
|
|
327
|
+
trimmedHeight: number;
|
|
317
328
|
/**
|
|
318
329
|
* Font scale factor
|
|
319
330
|
*/
|
|
@@ -410,6 +421,21 @@ export interface TextRenderProps {
|
|
|
410
421
|
export interface TextRenderInfo {
|
|
411
422
|
width: number;
|
|
412
423
|
height: number;
|
|
424
|
+
/**
|
|
425
|
+
* Height of the visible glyph extent — from the first line's cap-top to
|
|
426
|
+
* the last line's descender bottom. Excludes half-leading and the slack
|
|
427
|
+
* between the font's ascender and cap-top.
|
|
428
|
+
*
|
|
429
|
+
* @remarks
|
|
430
|
+
* Formula: `capHeight − descender + (lines − 1) × lineHeightPx`
|
|
431
|
+
* (descender is negative in font metrics, so subtracting it adds the
|
|
432
|
+
* descender depth). For empty text, this is 0.
|
|
433
|
+
*
|
|
434
|
+
* Use this when you want flex `alignItems: 'center'` (or any layout
|
|
435
|
+
* that aligns by node `h`) to optically center the visible glyphs.
|
|
436
|
+
* Set `node.h = node.trimmedHeight` after the `loaded` event.
|
|
437
|
+
*/
|
|
438
|
+
trimmedHeight: number;
|
|
413
439
|
hasRemainingText?: boolean;
|
|
414
440
|
remainingLines?: number;
|
|
415
441
|
imageData?: ImageData | null; // Image data for Canvas Text Renderer
|
package/src/main-api/Renderer.ts
CHANGED
|
@@ -374,17 +374,20 @@ export type RendererMainSettings = RendererRuntimeSettings & {
|
|
|
374
374
|
* mid-line. This is engine-wide and intentionally not exposed per node —
|
|
375
375
|
* mixing anchor models within one app produces visually inconsistent text.
|
|
376
376
|
*
|
|
377
|
-
* - `'
|
|
378
|
-
*
|
|
379
|
-
*
|
|
380
|
-
*
|
|
377
|
+
* - `'optical'` (default): the mean of cap-height and x-height is centered.
|
|
378
|
+
* Reads visually centered for mixed-case UI text like "Button"; the
|
|
379
|
+
* sweet spot between `'cap'` (low for lowercase-heavy) and `'x'` (high
|
|
380
|
+
* for headings). Matches macOS/iOS control behavior.
|
|
381
|
+
* - `'cap'`: capital letters and digits sit centered on the line. Use
|
|
382
|
+
* when content is mostly uppercase or numeric (timers, badges); mixed-
|
|
383
|
+
* case "Button" can read slightly low.
|
|
381
384
|
* - `'x'`: lowercase x-height is centered. Better for running body text;
|
|
382
385
|
* capitals appear slightly high in headings.
|
|
383
386
|
* - `'linebox'`: legacy mode. Centers the asc/lineGap/desc rectangle.
|
|
384
387
|
* Mathematically tidy but visually unbalanced because most Latin fonts
|
|
385
388
|
* have asymmetric asc/desc ratios.
|
|
386
389
|
*
|
|
387
|
-
* @defaultValue `'
|
|
390
|
+
* @defaultValue `'optical'`
|
|
388
391
|
*/
|
|
389
392
|
textBaselineMode: TextBaselineMode;
|
|
390
393
|
|
|
@@ -555,7 +558,7 @@ export class RendererMain extends EventEmitter {
|
|
|
555
558
|
renderEngine: settings.renderEngine,
|
|
556
559
|
quadBufferSize: settings.quadBufferSize ?? 4 * 1024 * 1024,
|
|
557
560
|
fontEngines: settings.fontEngines ?? [],
|
|
558
|
-
textBaselineMode: settings.textBaselineMode ?? '
|
|
561
|
+
textBaselineMode: settings.textBaselineMode ?? 'optical',
|
|
559
562
|
textureProcessingTimeLimit: settings.textureProcessingTimeLimit || 10,
|
|
560
563
|
canvas: settings.canvas,
|
|
561
564
|
createImageBitmapSupport: settings.createImageBitmapSupport || 'full',
|