Table of Contents
Typography
Tabs, justification, and cutting text short with an ellipsis. In the
examples, font is DejaVu Sans at size 16, whose space is 5.09 layout units
wide.
Tabs
A tab advances to the next tab stop. :tab-stops sets where they are:
:tab-stops |
Stops |
|---|---|
nil (the default) |
Every eight spaces of the primary font. |
| a positive real | Every so many layout units. |
| a list of increasing positive reals | At those distances, then every eight spaces of the primary font after the last. |
The primary font is the first font of layout-text's font argument.
If it has no space glyph, eight spaces count as four ems. A stop that is
not a positive real is a type-error, and a list whose stops do not
increase a textrender-error (see Errors).
(map 'list (lambda (g) (list (tr:glyph-text-index g) (tr:glyph-x g)))
(tr:layout-glyphs
(tr:layout-text (format nil "a~Cb~Cc" #\Tab #\Tab) font
:tab-stops '(40 100))))
;; => ((0 0.0) (1 9.8046875) (2 40.0) (3 50.15625) (4 100.0))
The a is at 0, the tab after it at 9.80, and the b at the first stop,
40. The second tab takes the c to 100.
- Stops are measured from the line's start edge: from the left in a left-to-right paragraph, and from the right in a right-to-left one.
- A tab right at a stop goes to the next one. Two tabs in a row never share a stop.
- Tabs count when wrapping, at their expanded width.
- A tab draws as a space. Its glyph is the font's space glyph, with the advance stretched to the stop, so renderers need no special case.
Justification
:align :justify widens the spaces of a line so that it fills :width
exactly:
(tr:layout-text "The quick brown fox jumps over the lazy dog" font
:width 120 :align :justify)
;; lines "The quick ", "brown fox ", "jumps over " end at x 120.0;
;; the last line, "the lazy dog", keeps its natural width, 98.65
- Which lines. Every line except a paragraph's last, a line ended by a forced break such as U+2028 LINE SEPARATOR, and a line ending in an ellipsis. Those stay at the paragraph's start edge.
- Which spaces. Every space separator (Unicode category Zs: the space, no-break space, ideographic space and so on) except trailing ones, each widened by the same amount.
- No spaces. A line without spaces cannot be justified and stays at the start: the left in left-to-right text, the right in right-to-left text.
- No width. Without
:widththere is nothing to fill, and:justifyaligns lines at the start.
The widened spaces change glyph-advance of the space glyphs, and caret
positions and selections follow them.
Ellipsis
:max-lines keeps at most that many lines. Text that does not fit is left
out, and the last line kept ends in :ellipsis, "…" by default:
(let ((layout (tr:layout-text "The quick brown fox jumps over the lazy dog" font
:width 120 :max-lines 2)))
(list (lines-of layout) ; LINES-OF from Laying Out Text
(tr:layout-truncated-p layout)
(map 'list #'tr:line-truncated-p (tr:layout-lines layout))))
;; => (("The quick " "brown fox") T (NIL T))
The second line now reads "brown fox…": its trailing space is gone and an ellipsis run follows "fox".
- Paragraphs count as lines.
(format nil "one~%two~%three")with:max-lines 2keeps "one" and "two…". - The last line keeps its own text. The ellipsis goes after the text
the line was broken with, without its trailing whitespace. If that would
be wider than
:width, the line is cut back a grapheme at a time until the ellipsis fits. A line too short for even one grapheme and the ellipsis keeps just the ellipsis. Without:width, nothing is cut. - The logical end. The ellipsis goes at the end of the text in reading order: on the right in left-to-right text, on the left in right-to-left text.
- Its style and fonts. The ellipsis takes the style of the last
character kept, so it has that character's
:data(its color, say). Each of its graphemes comes from that style's fallback list, as text does: the last character's font if it has the glyph, or else the first font in the list that has it. CJK text in Droid Sans Fallback, which has no "…", gets its ellipsis from the next font in the list. :ellipsis nilleaves text out without marking it, and any other string, such as"...", replaces the default.- Reporting.
layout-truncated-pis true when text was left out, andline-truncated-pis true for the line that was cut. Show the full text in a tooltip when a label is truncated.
Cutting lines short
:wrap nil keeps each paragraph on one line, as a cell of a table or a
tree does, and cuts a line longer than :width at the last grapheme
boundary where it fits with the ellipsis after it. Wrapping with
:max-lines 1 cuts at a word instead:
(list (lines-of (tr:layout-text "Node number 12" font :width 100 :wrap nil))
(lines-of (tr:layout-text "Node number 12" font :width 100 :max-lines 1)))
;; => (("Node num") ("Node")), each drawn with "…" after it
- Each paragraph is cut on its own. Every paragraph keeps its line, and
each that is too long is cut:
(format nil "Node number 12~%Second paragraph here")gives "Node num…" and "Second pa…". :ellipsis nilkeeps all that fits, here "Node numb".- A line cut short covers only the text it shows. The text cut off
belongs to no line, and
line-truncated-pis true for the line, aslayout-truncated-pis for the layout. - Without
:width, nothing is cut: each paragraph is one line as long as it is.
The cut is found from the advances the paragraph was shaped with, then checked by shaping the line cut there, so a long line costs a few shapings rather than one for every grapheme it loses.
The ellipsis run
The ellipsis is a run of its own, or one run per font when its graphemes
come from different fonts, whose run-start equals its run-end: it covers
no text. Its glyphs' glyph-text-index is the index where the text was cut,
and run-script is nil. The line's height makes room for the ellipsis's
fonts.
For hit testing, the ellipsis has no caret stops, and every index in text
left out maps to the end of the line before it: line-at-index returns
that line, and caret-position the position after the last character it
kept, before the ellipsis. move-caret moves a caret there from where it
is drawn. A click on the ellipsis gives that index too.