Skip to content

How TT Hinting Works

svenper edited this page Dec 2, 2019 · 2 revisions

Introduction

This document was written in pieces ca. 2008, and amended in 2009. Some of its parts may need improvement. Also, it reflects knowledge and point of view of just one man. Any improvement is welcomed.

The purpose of this document is to help FontForge user to get familiar with automatic instructing (hinting) of TrueType fonts. It starts with a general procedure to produce auto instructed font. Then it summarises some common errors in auto instructed fonts, and how to circumvent them. Next, it presents some various types of hinting seen today (how hinted font is expected to look is somewhat affected by current fashion...). Finally, there come two bulky pieces of information, that explain font-level and glyph-level hints, how they work, and how they can be tweaked to give desired results.

Very basic hinting

FontForge needs font to be hinted with the PostScript means to produce acceptable truetype instructions. So:

  1. Learn a bit (or better a lot) about PostScript and Truetype hinting...
  2. Make sure your foreground layer contains quadratic splines, not cubic.
  3. In Settings -> PS Hinting, turn diagonal hinting on.
  4. Autohint (NOT Auto Instruct) the font.
  5. In Element -> FontInfo -> PS Private, add these entries (FF will provide default values): BlueValues, OtherBlues, StemSnapH, StdHW, StemSnapV and StdVW.
  6. FontForge also supports FamilyBlues and FamilyOtherBlues, but these need to be hand-created if needed. It's usually enough to copy them from most regular font variant in the family, from Blues and OtherBlues entries.
  7. Repeating steps 4, 5 and 6 can occassionaly produce more coherent PS hints.
  8. Save the font as a separate sfd - it most probably will need tuning.
  9. Now the font can be auto-instructed.

After generating the font, there surely will be some noticeable errors.

Here are some most popular:

  1. Letters are fuzzy
    • Letters are fuzzy at baseline and/or X-height in some sizes! (this problem is visible only in antialiased mode, in medium ppem ranges).
      • Looks like BlueScale in PS Private dictionary should be somewhat larger than default 0.03.
    • Some letters are fuzzy at baseline and/or X-height in some sizes! (this problem is visible only in antialiased mode, in medium ppem ranges; in monochrome rendering, these letters may be vertically offset).
      • Looks like they're not affected by BlueValues/OtherBlues. Either they are set wrongly (lesser than overshoots; check in PS Private), or there are no hints controlling overshoots (check these letters).
    • At some sizes, x-heights of, say, regular and bold variants, differ significantly!
      • Looks like FamilyBlues/FamilyOtherBlues in PS Private are not set, or were set incorrectly. FF won't guess these, they must be set manually. It is usually sufficient to copy BlueValues/OtherBlues from most regular font in the family there, but sometimes it's enough. A family zone must overlap with a blue zone that is intended to be adjusted.
  2. Widths of horizontal and vertical stems are inconsistent! (this problem is visible only in monochromatic mode). FontForge picks StdHW from StemSnapH (and StdVW from StemSnapV) using the most frequently occuring stem width. This is not always the best choice. Try to pick other values - usually stem widths of latin small letter 'o' work well.
  3. Stems are too thick or thin
    • Stems are too thick or thin in certain size range! (this problem is visible only in monochromatic mode).
      • It's technically very similar to problem 2. Try to pick smaller/larger values for StdHW/StdVW, or even for StemSnapH/StemSnapV.
    • Stems are too thick in small sizes! (this problem is visible only in grayscale mode).
      • Try to add, say, "ELSE 44 SMD" before final "EIF" clause in hinting->prep. This will set the minimal global thickness to 44/64 pixel. It is not yet implemented in FontForge, because it sometimes clashes with diagonal hinting.
  4. Stems aren't aligned to pixel grid! (this problem is visible only in antialiased mode).
    • Well... they are aligned, but their widths are letf unrounded so that things scale more naturally in antialiased mode. If you really, really want to have stems hammered into pixel grid like in monochromatic mode (this can be useful for UI fonts for low-dpi displays), then change function 6 in FPGM to always return 0.
  5. Stems of 'm' are unequally spaced!
    • Try to play with File->Properties->TTF hinting; enabling '!ControlCounters' will probably do the trick (but advance widths will scale nonuniformly).
  6. The text flickers while being animated!
    • (After ruling out other possible causes) hinting was meant for static text. Try not to use it, or get rid of it.

(No idea on how to effectively condense information below into simple symptoms-cause-cure triplets as presented above.)

Hinting paradigms

This section explains what FontForge does to improve truetype font rasterization. What exactly is "better", depends on display/printer.

People with low resolution and/or monochrome devices prefer "brutal" hinting, with all edges snapped to grid and all widths rounded to integer values. Rounding should be smart, to accomodate for subtle differences in stems' thicknesses at small text sizes. For unsmoothed text (even for first Microsoft's implementation of ClearType (c): in Windows XP (c)), this is the only way to go, and FF will do just that, provided it was given decent settings in Element -> Font Info -> PS Private. See "Font-wide hints" below.

For antialiased rendering, hovewer, there are more possibilities. Hinting as for monochrome produces acceptable results, and is perhaps preferable for user interface fonts, and certainly for low resolution displays. Another way is to snap stems to pixel grid, but without adjusting their thicknesses if not under certain threshold. As this method produces smooth weight gradation across sizes, and doesn't distort outlines so much, it is preferable nowadays, even though it loses some contrast. FontForge uses it.

  • Note 1: Whether to render with better contrast, or with better weight and shapes, is controlled with fpgm function 6. This is font-wide.
  • Note 2: At small sizes, with stems just a pixel wide, but letters still legible, it's sometimes better to turn antialiasing off to enhance contrast. This can be manually set in GASP table (Element -> Font Info -> Gridfitting). On Linux, though, external contextual settings (fontconfig) have to be used, because of patent reasons (that might have expired (?)).

FontForge also tries to mantain consistent advance widths across grayscale and monochrome modes, but sometimes fails if there are vertical stems that share an edge, and one does not enclose the other.

Modern, antialiased subpixel rendering is such an advantage, that hinting in one direction can be almost completely discarded. http://www.antigrain.com/research/font_rasterization

Typically subpixels are arranged in vertical stripes, allowing for not hinting along x axis. This is beneficial for scripts written in horiontal lines, as there would be no distortions in letter spacing.

  • Caveat 1: FontForge can't do this yet, for lack of decent documentation. For limited subset of conditions, this can be simulated by deleting most of vertical or horizontal stems (by hand or from a script) before instructing.
  • Caveat 2: Most text processors have yet to to be redone to benefit from this.
  • Caveat 3: XP's cleartype is NOT antialiased in the usual sense (this is visible in large sizes). On newer MS implementations (like WPF), one has to manually set "do symmetric smoothing" flag in GASP to get "more antialiased" cleartype.
  • Caveat 4: Hinting in the second direction has to be retained.

Written above are just personal opinions of the author. You are free to disagree with them and rewrite FontForge's auto-instructor to satisfy your needs better. But if something you consider a "bug" was mentioned as a "good thing" above, it won't be changed unless author's opinion change.

  • Note 3: Experience shows that hinting CJK characters needs to be done quite differently that for LGC and perhaps Arabic characters. FontForge currently doesn't do that. Any help is appreciated.

Font-wide hints

Filling Blues and StdStems in PS private will improve the output very much. Actually, fonts instructed without Blues usually look much worse than not instructed at all. Please consult Type1 specification to see how to fill these entries well (for example, it's sometimes good to know what "top zones" and "bottom zones" are). FontForge can usually guess good approximate values, but they may need tuning, especially in italic fonts.

If some glyphs seem unaligned horizontally with others, they either don't have important horizontal stems/tips hinted (see "Glyph level hints"), or BlueValues/OtherBlues need to be adjusted.

If stems appear consistent but also too thin or thick in monochrome mode, picking different StdStemW from given StemSnap[] should help. If overshoots starts appearing at too small or too large sizes; then adjusting BlueScale entry may help. Much experimentation is allowed and encouraged with exact values.

!FamilyBlues are supported too: below certain ppem, base edge of a blue zone will be moved where family zone's base edge is. Currently a family zone must overlap with a blue to link to it. Family overshoot edges serves solely for this purpose. That's not perfect, but usable.

Many font families have traits like separate capitals and figure heights in some styles but not in others. There are several ways to hint these: one could add a blue zone (and treat it with DELTAC if needed), or enlarge existing one (but overusing this can distort overshoots at larger sizes). BlueFuzz in PS private dictionary is supported too. Finally, ghost hints can explicitly tie features to blue zones at short distances (this is a glyph-level method).

Hinting Unicode fonts is challenging, as various scripts can have distinct key heights. For example, x-height for Cyrillic may be different than for Latin. This is not necessarily a design error; readers might expect different x-heights. FontForge can't help here - you have to experiment.

It's also important to fill the GASP table properly. As LCD screens get more and more common, using version 1 is advised, as it enables tuning for subpixel rendering.

If a font is already instructed, it's better to reinstruct it as a whole, because FF sets up it's own constantly evolving code in prep/fpgm. Custom functions in fpgm should be given high numbers, to allow easy appending to FF's fpgm after each FF update. FF's functions are documented within code of init_fpgm() in nowakowskittfinstr.c; adventurous and skilled hinters could easily (?) get better overall results with some modifications to 'fpgm'.

  • Note 4: any modifications to 'fpgm'/'prep' should be done after instructing the last glyph, or in a separate test font. FontForge generates inferior hints when it fails to find its default code there.

Glyph level hints

Contours need to have the right direction, otherwise snapping to blue zones is impaired. The less points form the (accurate) shape, the better. But curves hinted with H/V stems should have points at extrema, otherwise they won't get snapped. Its unelegant if such a point is 'implicit'.

If an edge of a HStem falls within a blue zone, it is snapped, unless the other edge is already done or falls within a better zone: baseline zone wins with any other; bottom zones (those in OtherBlues) win with top zones; the further a zone from the baseline, the better.

Turning diagonal stems off will usually cause apparent weight inconsistency with H/V stems. Current autoinstructor don't work well with massed horizontal, vertical and diagonal stems acting on single feature - removing some may help in such cases. For example, many diagonal stems in an italic font don't really need vertical stems at their tips, and sometimes removing one or both will improve overall result.

It's generally good not to hint too much. Please note that FontForge not only instruct stem edges, but also features between them. Thus, some cases may be acceptably resolved without much hinting.

Currently, autoinstructor doesn't distinguish accents' hints from hints of their base glyphs. Composing accented glyphs from references may help. For accents physically tied to their base glyphs, like often Polish "ogonek", omitting vertical hints may sometimes help.

Supported "exotic" Type1 features:

  • vertical ghost hints (it's implemented, but we have no feedback from users),
  • horizontal ghost hints not tied to blue zones (with remarks as above),
  • counter hints (but these are slightly different from those in T1 spec).

Partially supported features:

  • overlapping stems (experiments suggested for each case).

Currently unsupported features:

  • flex hints.

Glyph references

FF doesn't instruct references. Seting "round to grid" flag allows a reference to benefit from referred contour's hinting. For complicated cases, point matching is worth trying, if used wisely.

In accented glyphs consisting of references, setting flag "use my metrics" for reference to base character ("N" in "Ń"), ensures identical spacing; but must not be used for flipped references (rasterisers would interpret things differently).

OpenType device tables

Yet another apparatus for hinting accents, anchors and many other features. It's superior to manually instructing them, because it comes with nice GUI. On the other hand, OpenType featureas are not handled as commonly as truetype bytecodes. Pay attention not to mix the methods when handling a contour, unless you really know what you are doing.