Text Rendering Notes

From Inkscape Wiki
Revision as of 14:12, 10 December 2020 by Tavmjong (talk | contribs) (Created page with " <div style="width: 60em"> = A summary of observations about text rendering! = This page is the result of an on-going process of planning a refactor of Inkscape's text handl...")
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search
The printable version is no longer supported and may have rendering errors. Please update your browser bookmarks and please use the default browser print function instead.

A summary of observations about text rendering!

This page is the result of an on-going process of planning a refactor of Inkscape's text handling.

General Comments

Our software stack relies on the FreeType, HarfBuzz, Pango, and Cairo libraries for rendering text.

  • FreeType: Access to font internals.
  • HarfBuzz: Converts characters to glyphs (i.e. shaping).
  • Pango: Determines best fonts to render text for given style and characters.
  • Cairo: Rendering of glyphs.

The boundaries between these libraries is somewhat murky and often one can do the same things using routines from different libraries.

HarfBuzz is under active development and is used by many major pieces of software. FreeType has some development. Pango and Cairo are poorly maintained at the moment.

Changes for Inkscape

Font Hash

Currently Inkscape uses a custom hash of the Pango Font descriptor to index fonts in a font map. Pango has a built in hash which we should probably use instead. This font hash includes "gravity" which is missing in our custom hash.

Use Clusters

When adjusting glyphs, especially in vertical-upright text, we should move glyphs in the same cluster by the same amount.

(A cluster is a set of glyphs that belong to the same grapheme, the smallest typographic unit. For example, an 'a' with a 'grave' accent can (but need not) be composed of two separate glyphs, which would then belong to the same cluster. Complex scripts (such as south-Asian) can have a many-character to many-glyph mapping within one cluster.

Currently Inkscape does some limited guessing as to which glyphs belong together (basically just that non-spacing marks are considered to belong to the preceding glyphs).

Clusters can also help with positioning the cursor so that one can indicate which character within a cluster is subject to editing. For example, if 'ffi' is represented by one glyph and thus one cluster, one can position the cursor before the cluster (before the first 'f'), one-third of the way into the cluster (between the two 'f's), two-thirds of the way into the cluster (between the second 'f' and the 'i') or at the end of the cluster. In this case, the cursor position closely indicates which character is being edited, with complex scripts, this might not be the case but still will give a visual indication that one is "moving" through the cluster.

General Text Layout Algorithm

  1. Add text with the same style and SVG positioning attributes as spans.
  2. Use Pango to itemize spans to find appropriate fonts for each item in span. (An item is a group of characters that share the same font face which can differ with a span if glyphs are missing in the nominal font face.)
  3. Shape the Pango items to determine which glyphs with positions should be used to render the text. Either Pango or HarfBuzz can be used (Pango uses HarfBuzz under the hood).
  4. Create Character, Glyph, Cluster mappings. These will be needed for text editing. Use a specialized iterator to walk through maps.
  5. Layout items in allocated space, determining glyph positions. Must handle SVG kerning, text-length attributes, white-space (spaces, tabs, line-returns), etc.
  6. Apply SVG positioning attributes.
  7. Render glyphs. We use FreeType to extract out glyph paths. We could use Cairo directly but as we need access to the paths when converting text to path, it doesn't reduce code.
  8. Determine cursor position (with link via iterator into Character, Glyph, and Cluster maps). Cursor should use cluster position with fractional adjustment based on character number within cluster.