Skip to content

Text API #224

@FlutterIssues

Description

@FlutterIssues

Text consists of a series of glyphs from one or more fonts. Each series of glyphs that uses a specific font is placed in a box. The boxes are all aligned along a common baseline, which is either the alphabetic baseline (e.g. for English text), the ideographic baseline (e.g. for CJK characters), the hanging baseline (e.g. for Thai), or the mathematical baseline. The boxes have several heights: the em height (the font size), the line height (the font size times the line height factor for that box), the font box height, and the bounding box height (see note 2). The line height is positioned such that it has the same difference from the em height above and below the baseline. The boxes are line-wrapped and placed vertically below each other. The text origin is the point at the start (i.e. before the first character) of the first line, on the chosen baseline.

We should expose the following inputs for the text painting and layout algorithm:

  • font size (in pixels), weight (100, 200, 300, ... 900), whether it should be italicised, whether a small-caps variant should be chosen, text decoration style (zero or more of underline, overline, linethrough), text decoration color, text decoration style (solid line, wavy line, etc). *
  • the Paint to use when drawing the text. *
  • a way to change the Paint efficiently (e.g. change colour without a new layout).
  • the line height, as a multiple of the font-size. *
  • whether or not this span's line height should contribute to the line's actual height calculations. Defaults to false except for the outermost box where it's always true. *
  • a list of font family names, in preference order. *
  • the chosen dominant text direction (ltr, rtl, same-as-parent-default-ltr, same-as-parent-default-rtl). *
  • whether to enable the bidi algorithm inside this span, or whether to force all text to be in the given direction ("bidi override") *
  • whether to isolate this span from any parent span, treating it as if it didn't have a parent for bidi purposes, and ignoring this span's bidi settings for the purposes of the parent's bidi algorithm ("bidi isolate"). * See note 4 below.
  • the type of CJK ideographs to use (traditional Chinese, simplified Chinese, Korean, Vietnamese, Japanese), when a codepoint corresponding to a CJK unified ideograph is used. *
  • the text to show, and any nested text boxes with other styles. *
  • the baselines and vertical-alignment offset to use for box alignment (e.g. "align the alphabetic baseline a superscript offset from the parent's alphabetic baseline", or "align the ideographic baseline 20 pixels down from the parent's hanging baseline"). *
  • the baseline to use for the text origin.
  • font registration API, as per https://github.com/flutter/engine/blob/master/sky/specs/fonts.md
  • width at which to soft wrap (wrap before this point if there is an available line break opportunity).
  • width at which to hard wrap (wrap before this point regardless of available line break opportunities).
  • the width to use for centering and right-alignment; by default, if it's not set, then it should use the soft-wrap-width, or if that's not set, the hard-wrap-width, or if that's not set, the intrinsic max width. (Right now, there are minWidth and maxWidth properties. As far as I can tell, minWidth is useless, and maxWidth is just the actual width, except it has a crazy default (infinity?) so if you center text without setting a maxWidth, it disappears (many kilometers to the right of your screen, probably).)
  • the dimensions of placeholders in the text.
  • rules for indenting text (how far wrapped lines should be indented (positive or negative) relative to the first line and lines after hard line breaks, expressed as an indent for the first line relative to the start edge.

The input consists of a tree of boxes annotated with the data marked with asterisks * in the list above. The baselines of the outermost box set the metrics to use for baseline alignment. On each line, the boxes are positioned vertically according to the settings, and then those that contribute their line height are examined. The distance from one line to the next is the distance from the baseline of the first, to the bottom of the bottommost line height box on that line, plus the distance from the top of the topmost line height box on the next line, to the baseline of that second line.

We should expose the following metrics for text:

  • whether the text origin is on the right (e.g. arabic, hebrew) or on the left (e.g. latin, cyrillic). This corresponds to the configured text direction. (automatic direction detection would be done at the Dart layer)
  • advance width of the entire text relative to text origin, as if the text was not wrapped. (a positive number, regardless of text direction)
  • advance width from the origin to the end of the glyph to which the character at a particular index corresponds (or codepoint, if we're using UTF-16, or byte, for UTF-8...), as if the text was not wrapped. See note 1 below.
  • number of lines.
  • vertical distance from the text origin to the chosen common baseline of each subsequent line.
  • advance widths of each line. See note 1 below.
  • advance width from the start of the line to the end of the glyph to which the character at a particular index corresponds.
  • line number for the glyph to which the character at a particular index corresponds.
  • direction of text at the end of the glyph to which the character at a particular index corresponds. See note 1 below.
  • index of last character corresponding to the glyph closest to a particular Offset from the text origin.
  • distance from the text origin to the left edge of the actual bounding box of the text. See note 2 below.
  • distance from the text origin to the right edge of the actual bounding box of the text. See note 2 below.
  • distance from the text origin to the top edge of the actual bounding box of the text. This corresponds to the first line's actual bounding box ascent.
  • distance from the text origin to the bottom edge of the actual bounding box of the text. If the text does not wrap, this corresponds to the actual bounding box descent. Otherwise, it's the last line's actual bounding box descent plus the distance from the first line's baseline to the last line's baseline.
  • max font box ascent relative to baseline. This is the distance from the chosen baseline to the top of the font box of the font with the highest font box amongst all the fonts used. See note 3.
  • max font box descent relative to baseline. This is the distance from the chosen baseline to the bottom of the font box of the font with the lowest font box amongst all the fonts used. See note 3.
  • the distance from the baseline to the top of the em box of the highest font box (the em height ascent).
  • the distance from the baseline to the bottom of the em box of the lowest font box (the em height descent).
  • the upwards distance from the chosen baseline to the various other baselines (alphabetic, ideographic, hanging, mathematical).
  • the precise positions of placeholders in the resulting output.

Note 1: Because of bidi, the "end" of a character could be on the left or the right. For example, for "bب", if both characters are 10 units wide, and the configured text direction is ltr, the advance widths would be 10 and 10, even though the advance width of the whole string would be 20. The direction at the end of each character would be "ltr" and "rtl" respectively. If the string was wrapped in between the two characters, the line-specific advance width for the second line's first character would be 0, and both lines would have an advance width of 10 (the total advance width would still be 20).

Note 2: Each font has a font bounding box, which is the space the font would need to render the character that reaches the highest and the character that reaches the lowest. A specific span of text has a bounding box that is usually smaller than this (because most text doesn't include the extreme characters). If the text is a single period, it's much smaller than if it's a lowercase "o", which would itself be much smaller than if the text was an uppercase "F". Some fonts have particular characters that on their own will wildly increase the bounding box dimensions (e.g. Zapfino's lowercase "f").

Note 3: The fonts used depends on the characters used. If the text only consists of the letter "a", then it'll all be from the same font. If the text is "a林" then it might come from two fonts, one with latin characters and one with ideographic characters. However, the font box height given a particular set of fonts is independent of the actual characters. That is, if "a" and "b" end up using the same font, then they have the same font box dimensions (but probably different actual bounding box dimensions).

Note 4: We don't need to support bidi embeddings. Those can be done using explicit bidi formatting characters. We don't need to support the equivalent of "unicode-bidi: plaintext", that can be implemented at the Dart level.

Metadata

Metadata

Assignees

No one assigned

    Labels

    P3Issues that are less important to the Flutter projecta: text inputEntering text in a text field or keyboard related problemsa: typographyText rendering, possibly libtxtc: new featureNothing broken; request for a new capabilityengineflutter/engine related. See also e: labels.frameworkflutter/packages/flutter repository. See also f: labels.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions