5 Vertical Text
dxtr edited this page 2026-10-18 15:55:08 +02:00

Vertical Text

:orientation :vertical sets text in columns, read top to bottom, with the columns running from right to left, as in traditional Chinese and Japanese.

(tr:with-font (cjk "/usr/share/fonts/truetype/droid/DroidSansFallbackFull.ttf" :size 16)
  (tr:with-font (latin "/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf" :size 16)
    (let ((layout (tr:layout-text "日本語のLisp入門" (list cjk latin)
                                  :orientation :vertical :width 64)))
      (list (tr:layout-width layout)
            (tr:layout-height layout)
            (map 'list (lambda (column)
                         (list (subseq (tr:layout-string layout)
                                       (tr:line-start column)
                                       (tr:line-end column))
                               (tr:rect-x (tr:line-box column))
                               (tr:rect-width (tr:line-box column))
                               (tr:line-baseline column)))
                 (tr:layout-lines layout))))))
;; => (41.875 64.0
;;     (("日本語の" 20.9375 20.9375 31.40625)
;;      ("Lisp入門" 0.0 20.9375 10.46875)))

Each column's box is 64 units tall, the :width, and as wide as the line height of the fonts on it, 20.94. The first column is on the right, from x 20.94. "Lisp" is set sideways at the top of the second column.

Upright and sideways

How each character is set comes from its Unicode Vertical_Orientation property (UTR #50):

  • Upright. CJK ideographs, kana, hangul, full-width forms and other characters whose orientation is upright stand upright. They are shaped top to bottom, so the font's vertical forms (its vert feature) apply: the ideographic comma and full stop move to the top right, brackets turn. Their runs have the direction :ttb.
  • Sideways. Latin, Arabic, Hebrew and the other characters whose orientation is rotated are turned 90° clockwise, and their glyphs have glyph-rotated-p true. Their runs have the direction :ltr (or :rtl).

Fonts with vertical metrics (vhea and vmtx tables) and a vert feature give the best results. Droid Sans Fallback and Noto Sans CJK have both.

Columns

In a vertical layout the roles of the axes swap:

Horizontal text Vertical text
A line is a line, stacked downwards a column, stacked right to left
:width is the line length the column height
layout-width the line length the total width of the columns
layout-height the total height of the lines the column length
line-box the layout's width by the line's height the column: its width by the layout's height
line-content the stretch along x the text takes up the stretch along y the text takes up
line-baseline the y of the baseline the x of the column's central baseline
run-box the run's stretch along x the run's stretch along y
glyph-advance along x along y

:align works along the column: :start and :left put text at the top, :end and :right at the bottom, and :center in the middle. Columns read top to bottom whatever the script, so line-direction is :ltr for every column. Tabs, :max-lines and :ellipsis work as they do in horizontal text, with columns counting as lines. The ellipsis is set upright, and takes its fonts from the last character's fallback list: Droid Sans Fallback has no "…", so give it a fallback that has one.

Drawing vertical text

An upright glyph is drawn like any glyph: its outline, in font orientation, goes at glyph-x and glyph-y with y flipped. Its origin is placed so that its horizontal advance is centered on the column's central baseline, and at the height in its cell that the font's vertical metrics give it.

A rotated glyph is turned 90° clockwise about its origin: the font's x axis runs down the column and its y axis to the right. Its origin is on a baseline placed so the glyph's em box is centered in the column. Coordinates and Units has the formulas. The raster and atlas backends draw rotated glyphs turned; a distance-field or Slug renderer turns the glyph's quad.

Carets and selections

Hit testing works along the columns:

  • caret-position returns the caret's y and the column's left and right edges; caret-segment returns a horizontal bar across the column.
  • hit-test picks the column by x and the character by y.
  • selection-rectangles returns pieces of columns.
  • move-caret moves along a column with :up and :down, and between columns with :left and :right, keeping the height as its goal.
(tr:caret-position layout 2)
;; => 32.0, 20.9375, 41.875: y 32, across the first column

Not covered yet

  • Characters whose orientation is "transformed rotated" (Tr) are shaped upright. One the font has no vertical form for stands upright rather than turning.
  • Short horizontal runs inside a column (tate-chū-yoko), such as two-digit numbers, are not set specially; they turn sideways like other Latin text.