Skip to content

Commit 7c34d58

Browse files
committed
placement support (#1554)
1 parent 0cc3e5e commit 7c34d58

1 file changed

Lines changed: 136 additions & 6 deletions

File tree

specs/glyph-protocol.md

Lines changed: 136 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,7 @@ For non-PUA codepoints only `0` and `1` are possible.
201201
### 6.1 Request
202202

203203
```
204-
ESC _ 25a1 ; r ; cp=<hex> ; fmt=glyf ; reply=<0|1|2> ; upm=<int> ; <base64-payload> ESC \
204+
ESC _ 25a1 ; r ; cp=<hex> ; fmt=glyf ; reply=<0|1|2> ; upm=<int> ; aw=<int> ; lh=<int> ; width=<1|2> ; size=<mode> ; align=<h>,<v> ; pad=<t>,<r>,<b>,<l> ; <base64-payload> ESC \
205205
```
206206

207207
Parameters:
@@ -225,6 +225,28 @@ Parameters:
225225
Unknown values fall back to `reply=1`.
226226
- `upm` — units per em, the coordinate space the outline is
227227
authored in. Optional; default `1000`.
228+
- `aw` — authored advance width, in upm units. The intended
229+
horizontal extent of the glyph, NOT the outline's bounding box.
230+
Optional; default `upm`.
231+
- `lh` — authored line height, in upm units. The intended vertical
232+
extent (descender-to-ascender), NOT the outline's bounding box.
233+
Optional; default `upm`.
234+
- `width` — the codepoint's Unicode width, in the `wcwidth` /
235+
UAX #11 sense. One of `1` (narrow) or `2` (wide). Optional;
236+
default `1`. Authoritative for all terminal layout decisions
237+
(cursor advance, wrapping, selection geometry), overriding the
238+
codepoint's UAX #11 East Asian Width (all PUA ranges are
239+
Ambiguous by default).
240+
- `size` — scale policy. One of `height`, `advance`, `contain`,
241+
`cover`, `stretch`. Optional; default `height`. See §8.5.
242+
- `align` — placement of the scaled outline within the render span,
243+
as a comma-separated pair `<h>,<v>`. `<h>` is one of `start`,
244+
`center`, `end`; `<v>` is one of `start`, `center`, `end`,
245+
`baseline`. Optional; default `center,center`. See §8.5.
246+
- `pad` — insets from the render span edges, as comma-separated
247+
fractions `<top>,<right>,<bottom>,<left>` (each `0.0``1.0`;
248+
top/bottom are fractions of cell height, left/right of render
249+
span width). Optional; default `0,0,0,0`. See §8.5.
228250
- payload — base64-encoded payload for the declared `fmt`.
229251

230252
### 6.2 Response
@@ -359,12 +381,115 @@ TrueType semantics:
359381
current foreground color. For colored icons see the `colrv0` and
360382
`colrv1` formats in §8.6 / §8.7.
361383

362-
### 8.5 Scaling
384+
### 8.5 Sizing, placement, and coordinate convention
385+
386+
Every registered outline passes through three transforms at render
387+
time, in order: **pad** (compute the effective render span),
388+
**size** (pick scale factors), **align** (position the scaled
389+
outline within the span).
390+
391+
#### 8.5.1 Coordinate convention
392+
393+
Outlines are authored in `upm`-unit space, Y-up, with `y=0` at the
394+
baseline. The authored extent is the rectangle `[0, aw] × [y_min,
395+
y_max]` where `lh = y_max − y_min` (matching OpenType line height:
396+
descender to ascender, with descender ≤ 0). Points outside this
397+
rectangle are allowed — they clip or overflow per the `size`/align`
398+
rules below.
399+
400+
#### 8.5.2 Render span and padding
401+
402+
The render span is the rectangle of pixels the outline is scaled
403+
into. Before scaling, the span is:
404+
405+
```
406+
W = width × cell_width_px (cell_width_px from the terminal)
407+
H = cell_height_px
408+
```
409+
410+
`pad=<t>,<r>,<b>,<l>` shrinks the span:
411+
412+
```
413+
W' = W × (1 − l − r)
414+
H' = H × (1 − t − b)
415+
```
416+
417+
Padding values are fractions. If `l + r ≥ 1` or `t + b ≥ 1` the
418+
terminal MUST treat the request as if `pad=0,0,0,0` (no padding)
419+
— a degenerate span is not useful and suggests a client bug. The
420+
effective span `W' × H'` is what `size` and `align` operate on.
421+
422+
#### 8.5.3 Size modes
423+
424+
Given the authored extent `aw × lh` and effective span `W' × H'`:
425+
426+
| `size` | `sx` | `sy` | Aspect ratio | Notes |
427+
|------------|-------------------|-------------------|--------------|-------|
428+
| `height` | `H' / lh` | `H' / lh` | Preserved | Default. Line-height drives. Horizontal extent is whatever `aw` scales to; may overflow the span. |
429+
| `advance` | `W' / aw` | `W' / aw` | Preserved | Advance drives. Vertical extent is whatever `lh` scales to; may overflow. |
430+
| `contain` | `min(W'/aw, H'/lh)` | same | Preserved | Outline fits entirely inside the span on both axes. May leave empty space on one axis. |
431+
| `cover` | `max(W'/aw, H'/lh)` | same | Preserved | Outline fills the span on both axes. May overflow on one axis. |
432+
| `stretch` | `W' / aw` | `H' / lh` | Not preserved | Each axis independent. Useful for box-drawing. |
433+
434+
`height` is the default because it matches how characters behave
435+
(the terminal's line-height maps to the cell's vertical pixels,
436+
the horizontal footprint is what the glyph's own advance dictates).
437+
For icons that must stay inside the cell regardless of aspect,
438+
prefer `contain`.
439+
440+
#### 8.5.4 Alignment
441+
442+
After scaling, the outline has a scaled authored extent `(aw×sx)
443+
× (lh×sy)` positioned somewhere within the effective span. `align`
444+
picks where.
445+
446+
Horizontal:
447+
448+
- `start` — outline's `x=0` aligns with the span's left edge
449+
(`pad_left`).
450+
- `center` — outline's horizontal midpoint aligns with the span's
451+
horizontal midpoint.
452+
- `end` — outline's `x=aw` aligns with the span's right edge
453+
(`W − pad_right`).
454+
455+
Vertical (Y-up):
456+
457+
- `start` — outline's `y=y_min` aligns with the span's bottom edge
458+
(`pad_bottom`).
459+
- `center` — outline's vertical midpoint aligns with the span's
460+
vertical midpoint.
461+
- `end` — outline's `y=y_max` aligns with the span's top edge
462+
(`H − pad_top`).
463+
- `baseline` — outline's `y=0` aligns with the terminal's text
464+
baseline within the cell. Preferred for character-like glyphs
465+
that must sit on the same baseline as surrounding text;
466+
descenders extend below the baseline naturally.
467+
468+
When `size=stretch`, the scaled extent exactly matches the span on
469+
both axes, so `align` has no visible effect. When `size=cover`,
470+
the scaled extent overflows on one axis; `align` picks which edge
471+
to anchor (and therefore which overflow is visible vs. clipped).
472+
When `size=contain`/`height`/`advance`, the scaled extent may be
473+
smaller than the span on at least one axis; `align` picks where
474+
the empty space goes.
475+
476+
#### 8.5.5 Resolution independence
363477

364-
The `upm` value defines the glyph's authoring coordinate space.
365-
The terminal maps that space onto its cell at render time.
366478
Applications MUST NOT assume a particular cell size and MUST NOT
367-
re-register glyphs on font size change.
479+
re-register glyphs on font size change. All scaling is computed
480+
at render time from the parameters above and the terminal's
481+
current cell metrics.
482+
483+
#### 8.5.6 Coordinated sets (no scale groups)
484+
485+
There is no `group` parameter. A set of glyphs that must visually
486+
align — spinner frames, progress-bar steps, a multi-glyph logo —
487+
aligns automatically if authored with identical `aw`, `lh`, `size`,
488+
`align`, and `pad`, and if their outline geometry is coordinated
489+
(e.g. all spinner frames sized inside a common bounding circle).
490+
Scale-group semantics can be added later if authoring experience
491+
shows they are genuinely needed; for v1 the burden sits with
492+
the author, not the protocol.
368493

369494
### 8.6 Payload format: `colrv0`
370495

@@ -539,7 +664,11 @@ A terminal emulator is Glyph Protocol v1 conformant if it:
539664
color; renders `colrv0`/`colrv1` glyphs using the COLR paint
540665
graph, resolving palette index `0xFFFF` to the current
541666
foreground color.
542-
7. Scales glyphs according to `upm` and the current cell size.
667+
7. Scales and positions glyphs according to `upm`, `aw`, `lh`,
668+
`width`, `size`, `align`, and `pad` as specified in §8.5, and
669+
treats the registered codepoint as having the declared `width`
670+
(`1` or `2`) for every layout decision, overriding the
671+
codepoint's UAX #11 East Asian Width.
543672
8. Enforces the cell-buffer authority invariant in §9: selection,
544673
copy, and search return the raw codepoint.
545674
9. Ignores unrecognized parameters rather than failing the
@@ -662,3 +791,4 @@ rather than serving a stale bitmap.
662791
| 2026-04-19 | v1.4 | Raised the glossary capacity from 256 to 1024 simultaneous registrations per session, and raised the `n_glyphs` cap in `fmt=colrv0`/`colrv1` containers from 256 to 1024 outlines. Both bumps quadruple the worst-case memory footprint; the 64 KiB per-payload cap is unchanged. |
663792
| 2026-04-21 | v1.5 | Added `cp=auto` to the `r` verb: the terminal allocates a free PUA codepoint (SHOULD come from PUA-B) and echoes it in the success reply so the client can emit it. Added `reason=auto_unsupported` and `reason=glossary_exhausted` error codes. `cp=auto` forces a success reply regardless of `reply=0|2` because the allocated codepoint is only carried in the reply. |
664793
| 2026-04-23 | v1.6 | Removed `cp=auto` from the `r` verb (introduced in v1.5). Auto-allocation forced a stateful round-trip reply the client depended on to learn its codepoint, which recording tools like `asciinema` and `tee` cannot capture or replay — making `cp=auto` output impossible to reproduce from a transcript. Clients must pick their own PUA codepoint. The `auto_unsupported` and `glossary_exhausted` error codes are withdrawn. |
794+
| 2026-04-23 | v1.7 | Added a sizing and placement model to the `r` verb: `aw` / `lh` (authored extent in upm units), `width` (Unicode/wcwidth width, `1` or `2`, authoritative), `size` (`height`/`advance`/`contain`/`cover`/`stretch`), `align` (`<h>,<v>` positioning after scale, with `v=baseline` for character-like glyphs), and `pad` (fractional insets from the render span). Pinned the coordinate convention: Y-up, `y=0` at baseline, `lh` measured descender-to-ascender (OpenType). Scale groups are intentionally omitted — coordinated sets align via matching parameters and outline geometry. |

0 commit comments

Comments
 (0)