4 Coordinates and Units
dxtr edited this page 2026-10-18 15:55:08 +02:00
This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

Coordinates and Units

Layout units

A font's size is in layout units per em, and everything a layout returns is in layout units: glyph positions and advances, line and run extents, the layout's size, caret positions and selection rectangles. They are single-floats, unhinted and unrounded.

To draw at a scale, multiply. A layout made with a font of size 16, drawn at 2 pixels per layout unit, gives 32-pixel text; the raster and atlas backends take this factor as :scale. For UI text the simplest choice is a font size in pixels and a scale of 1, times the display's scale factor.

The y axis

layout-text's :y-axis picks one of two conventions. Every y a layout returns, and every y hit testing takes, follows it.

:y-axis :down (the default) is screen space. The first line's top is at y 0 and lines go down; the first baseline is at the font's ascent.

:y-axis :up is world space. Every y is negated, so the text hangs below the origin, as text in a scene with y up should.

(tr:layout-text (format nil "a~%b") font)               ; :down
;; line 0: baseline 14.85, line-box y 0.0, height 18.625: from 0 down to 18.625
;; line 1: baseline 33.48, line-box y 18.625, height 18.625

(tr:layout-text (format nil "a~%b") font :y-axis :up)   ; :up
;; line 0: baseline -14.85, line-box y -18.625, height 18.625: from 0 down to -18.625
;; line 1: baseline -33.48, line-box y -37.25, height 18.625

X coordinates are the same in both. A rect's x and y are always its smallest coordinates, and its width and height positive. So in an :up layout a rectangle's rect-y is its bottom edge: the first line's box runs from y -18.625 up to 0.

The raster and atlas backends draw into images whose rows grow downwards from the layout's top, whichever convention the layout uses.

Where things are

Value In horizontal text
glyph-x, glyph-y The glyph's origin, on the baseline.
line-baseline The baseline's y.
line-box The line's share of the layout: the layout's width by the line's height.
line-content The part of the line box its visible content takes up, without trailing whitespace.
run-box The part of the line box the run takes up, trailing whitespace included.
layout-width, layout-height The box from (0, 0) the lines fill.

The layout's box starts at x 0 and y 0 and is layout-width by layout-height, downwards in a :down layout and upwards from the negative y in an :up layout. Ink can stray outside it: hanging trailing spaces, accents above the ascent, and glyphs that overhang their advance. For vertical text, see Vertical Text.

To put a layout somewhere, add an offset to every position, or draw it under a transform.

Placing glyph outlines

Glyph outlines (glyph-outline) are always in font orientation, y up, relative to the glyph's origin on the baseline. For an outline point (ox, oy) of a glyph at (gx, gy):

Glyph :y-axis :down :y-axis :up
ordinary, or upright in vertical text (gx + ox, gy - oy) (gx + ox, gy + oy)
glyph-rotated-p (sideways in vertical text) (gx + oy, gy + ox) (gx + oy, gy - ox)

A rotated glyph is turned 90° clockwise: its font x axis runs down the column and its y axis to the right.

The outline is in layout units of the font it came from. When a renderer keeps outlines in ems, made from a font of size 1, it scales them by the glyph font's size: (gx + ox × size, gy - oy × size).

Pixel snapping

Layout positions are fractional. Renderers decide how to snap:

  • the raster and atlas backends round each glyph's x to the nearest quarter of a pixel and its y to a whole pixel, and draw it from a bitmap made at that quarter;
  • outline and distance-field renderers usually draw at the exact position.

Keep the layout's positions and snap at drawing time, after any transform; laying out in pixels at the target size, with a scale of 1, keeps snapping predictable.

Text in a scene

For text placed in a 3D or 2D world with y up, make the font at size 1, so a layout unit is an em, and lay out with :y-axis :up:

(tr:with-font (font "/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf" :size 1)
  (let ((layout (tr:layout-text "Sign" font :y-axis :up)))
    (list (tr:layout-width layout) (tr:layout-height layout)
          (map 'list (lambda (g) (list (tr:glyph-x g) (tr:glyph-y g)))
               (tr:layout-glyphs layout)))))
;; => (2.1811523 1.1640625
;;     ((0.0 -0.92822266) (0.6347656 -0.92822266) (0.91259766 -0.92822266)
;;      (1.5473633 -0.92822266)))

The text is 2.18 ems wide and hangs from y 0 to y -1.16. Scale it by the height you want for an em, and translate it to center it or put its baseline where you want.