TrueType italic hinting examples
As far as hinting is concerned, the most difficult feature of an italic glyph is its slanted strokes. An italic "O" can be hinted just like a roman "O", and likewise for a "V". For this reason, the following examples use an UC "H".
The italic stroke is hinted using the "Diagonal" command or tool in VTT, which works equally well for general diagonal strokes and for italic strokes. Accordingly, for hinting italics, we need to know how to properly use the diagonal stroke.
A diagonal stroke command takes two parent points and two child points, along with a CVT. The two parent points are anchored to the grid, and then the two child points are controlled relative to the parent points by the diagonal stroke command. This is similar to the Link command or tool, where a child point is controlled relative to a parent point, except that we have two of each. The reason for having two is basic geometry: For example, an "XLink" across a vertical stroke fully defines the direction in which the width of the stroke is controlled, simply because it is an "_X_Link", as opposed to a "_Y_Link". For a diagonal stroke, we need to specify the direction of that diagonal, and the only way we can do so is by specifying two parent points. In geometry, two points define a straight line.
To properly use the diagonal stroke, we also have to keep in mind that the final "Smooth" command will adjust all "unhinted" or "untouched" points in both the x- and y-directions. While the smooth command can do a perfect job when only x- or y-commands are present, it may not do what we want with diagonal commands, such as "Diagonal" or "DAlign". For example, if we wanted to use a diagonal command to hint an italic stroke, and if we hinted the parent points in x-direction only, the final smooth command might adjust the parent points in y-direction in a way we may not approve of.
Therefore, we will need to hint both parent points in both directions before we can use the diagonal command. The diagonal command, in turn, will hint the child points in a direction perpendicular to the line defined by the two parent points. The keyword here is a direction, as opposed to two directions. As with parent points, the child points might get adjusted by the final smooth in a disagreeable way. Accordingly, for italic strokes, we should hint the child points in y-direction before using the diagonal stroke command.
Last-but-not-least, there is a mechanism built into the VTT templates that permits some global control of the "italicness" of the italic strokes. For black-and-white rendering, as opposed to ClearType rendering, an italic stroke will render as a "stepping pattern" of pixels, going x pixels across and y pixels up. Like with many other raster tragedies, this stepping pattern may be "too italic" (i.e. too many pixels across for the given number of pixels up) or "not italic enough" (i.e. not enough pixels across). Worse still, it may look too italic on one character, while not italic enough on another, even though the characters have been designed with a similar or identical italic angle.
To use this mechanism, we'll have to define CVTs for the italic run and rise, as explained in "Hinting italics using Visual TrueType", and use the GlyphStrokeAngle command or tool. Then we'll link from the bottom parent point to the top parent point using the XDist command or tool, i.e. a link in x-direction but without CVT. Finally, we choose the italic angle rounding method for the child point, which is a "forward slash" when typing the command (i.e. "XDist/[0, 1]"), or the slash with the dot in the middle when using the link tool (i.e. right-click the control point and choose the slash with the dot in the middle).
Example: Arial UC "H"
The VTTTalk for this example looks like this (with line numbers added for further comments below):
1: /* VTTTalk glyph 43, char 0x48 (H) */
2: /* GUI generated Tue Aug 24 17:00:37 2004 */
3:
4: GlyphStrokeAngle(21,20)
5: SetItalicStrokePhase()
6: SetItalicStrokeAngle()
7:
8: /* Y direction */
9: YAnchor(0,2)
10: YDist(0,7)
11: YDist(0,8)
12: YDist(0,11)
13: YAnchor(1,8)
14: YDist(1,2)
15: YDist(1,5)
16: YDist(1,6)
17: YIPAnchor(0,10,1)
18: YDist(10,9)
19: YLink(10,3,72,>=)
20: YDist(3,4)
21:
22: /* X direction */
23: XIPAnchor(13,7,0,12)
24: XDist/(7,6)
25: Diagonal>>(6,5,7,8,76)
26: DAlign(8,9,4,5)
27: XDist/(0,1)
28: Diagonal>>(0,11,1,2,76)
29: DAlign(11,10,3,2)
30:
31: Smooth()
Comments:
- Lines 4 to 6: Generated by the use of the 'GlyphStrokeAngle' tool; italic run is CVT #20, italic rise is CVT #21
- Line 9: Hint bottom (left) parent point #0 of left stroke in y-direction
- Line 13: Hint top (left) parent point #1 of left stroke in y-direction
- Lines 10 to 12 and 14 to 16: Hint the parent points of the right stroke (#6 and #7), along with all the strokes' child points (#2, #5, #8, and #11), in y-direction. Using 'YDist' instead of 'YAnchor' simply creates smaller code.
- Lines 17 to 20: Hint points of the crossbar in y-direction (#10, #9, #3, and #4).
- Line 23: Hint bottom parent points (#0 and #7) in x-direction. At the same time, this defines the spacing of the character, hence this is the point to set about when having to improve the spacing.
- Line 24: Hint top right parent of right stroke (#6) relative to bottom right parent (#7). Notice the "/" between "XDist" and "(". This will use the glyph stroke angle previously established by the 'GlyphStrokeAngle' command.
- Line 25: At this point, both parents of the right stroke have been hinted in both x- and y-direction, and further more, both children have been hinted in y-direction, hence the diagonal command can be properly applied. In this example, it uses CVT #76 to control the weight of the diagonal stroke with parents #6 and #7, and with children #5 and #8.
- Line 26: At this point, both children of the right italic stroke have been hinted in both x- and y-direction, hence the diagonal align command can be properly applied, to align points #4 and #9 with the previously hinted points #5 and #8.
- Lines 27 to 29: Analogous to lines 24 to 26 for the left stroke.
Notice that both the 'Diagonal' and the 'DAlign' command can have an extra (optional) parameter that specifies, at and above which ppem size the geometrically strict "grip" they exert can be "loosened" up somewhat. In the above example, this may not be necessary, but e.g. for slightly tapered strokes it may be useful to completely "parallelize" the stroke's sides for small ppem sizes, while trying to render that tapering if the ppem size is high enough to permit doing so. Likewise, the strokes may be flared, in which case the strict "grip" may be useful at small ppem sizes but not at large ones. To use this feature when typing the commands add a ppem size at the end of the list of parameters, like so:
Diagonal>>(6,5,7,8,76,@42)
DAlign(8,9,4,5,@42)
This will "loosen" the strict "grip" at and above ppem size 42. When using the diagonal and align tools, refer to the "ppem limits" tool ("padlock tool") explained in "Hinting italics using Visual TrueType" and "Visual TrueType 4.2 release notes".
The above example assumes the CVTs for italic run and rise to be present in the control program, like so (see "ItalicRun" and "ItalicRise" below):
InstructionsOn @8..2047
DropOutCtrlOff @144
CvtCutIn = 4, 2.5@29, 0@128
/* Heights */
UpperCase
Grey
Y
SquareHeight
2: 1466
8: 0
/* Glyph Stroke Angle */
UpperCase
Grey
X
ItalicRun
20: 307
Y
ItalicRise
21: 1466
/* Strokes */
UpperCase
Black
Y
StraightStroke
72: 166
Diag
StraightStroke
76: 191
If we wanted to change the "italicness" of all uppercase italic strokes at a particular ppem size, all we'd have to do is to add a "Delta" to CVT #20, like so:
UpperCase
Grey
X
ItalicRun
20: 307 Delta(1@13)
This would increase the italic run by 1 pixel at ppem size 13. Likewise, by putting -1 instead of 1, we could decrease the italic run by 1 pixel. We may want to do so only for black-and-white rendering, in which case we'd use "BDelta" instead of "Delta".
The above method in general does a fairly good job at normalizing stepping patterns of italic strokes, rendering italic fonts in a systematic way, which hopefully makes it easier to read. But there may be "outlier" pixels, for technical reasons that go way beyond the scope of this example. In these cases, the easiest remedy is to apply "XBDelta" or possibly "YBDelta" on individual glyphs at individual ppem sizes, as appropriate.
Serif fonts pose an added difficulty because the strokes' parent and child points likely are not on the base or capsline, as is typically the case with sans-serif fonts. A practical solution to this problem is to interpolate the parent and child points, as opposed to anchoring and linking them.
Example: Times New Roman UC "H"
The VTTTalk for this example looks like this:
/* VTTTalk glyph 43, char 0x48 (H) */
/* GUI generated Tue Aug 24 21:19:21 2004 */
1: GlyphStrokeAngle(21,20)
2: SetItalicStrokePhase()
3: SetItalicStrokeAngle()
4:
5: /* Y direction */
6: YAnchor(42,8)
7: YDist(42,24)
8: YDist(24,23)
9: YDist(42,25)
10: YDist(25,26)
11: YDist(42,41)
12: YDist(41,40)
13: YDist(42,43)
14: YAnchor(57,2)
15: YDist(57,9)
16: YDist(9,8)
17: YDist(57,10)
18: YDist(10,11)
19: YInterpolate(11,17,18,23)
20: YIPAnchor(57,34,42)
21: YLink(34,0,72)
22: YDist(0,1)
23: YInterpolate(1,2,8)
24: YDist(34,33)
25: YInterpolate(33,32,26)
26: YInterpolate(34,35,40)
27: YDist(57,56)
28: YInterpolate(43,49,50,56)
29: YDist(57,58)
30: YDist(58,59)
31: YInterpolate(0,65,59)
32:
33: /* X direction */
34: XInterpolate(67,18,49,66)
35: XNoRound(17)
36: XDist/(18,17)
37: Diagonal>>(17,2,18,32,76)
38: DAlign(32,33,1,2)
39: XNoRound(50)
40: XDist/(49,50)
41: Diagonal>>(49,35,50,65,76)
42: DAlign(35,34,0,65)
43:
44: Smooth()
At first glance, this looks a lot more complicated than the sans-serif "H", and it is not even finished yet (for instance, the lengths of the serifs have not been hinted in x-direction). But on closer inspection we'll find that it is pretty much the same. For the left diagonal stroke, the parent points are #49 and #50, and the child points are #35 and #65, which in turn are hinted as further explained in the following comments:
Comments:
- Line 28: Hint the parent points #49 and #50 in y-direction, by y-interpolating them between points #43 and #56.
- Lines 13 and 27: Hint points #43 and #56 in y-direction by y-dist from points #42 and #57, respectively. This y-dist at the same time establishes the "thickness" of the serifs, hence we may want to use a CVT for the serif thickness and replace the "YDist" by a "YLink".
- Lines 6 and 14: Hint points #42 and #57 in y-direction y-anchoring them to base and capsline, respectively, much like in the sans-serif case.
- Lines 26 and 31: Hint the child points #35 and #65 in y-direction, again by y-interpolating them.
- #35 is y-interpolated between #34 and #40
- Line 12: y-dist point #40 from point #41
- Line 11: y-dist point #41 from point #42, which in turn was y-anchored to the baseline.
- Line 20: y-interpolates #34 between the base and capsline
- #65 is y-interpolated between #0 and #59, which in turn are y-disted and y-linked similarly to points #34 and #40.
- Line 34: Hint bottom parent points (#18 and #49) in x-direction, similar to the sans-serif case, but notice the absence of rounding; it is usually better not to force these points on the nearest grid line in x-direction.
- Lines 39 and 40: Hint top left parent (#50) relative to bottom left parent (#49). Again, notice the "/", and also notice the "XNoRound" to turn-off rounding for the same reasons as above.
- Line 41: At this point, both parents of the left stroke have been hinted in both x- and y-direction, and moreover, both children have been hinted in y-direction, hence the diagonal command can be properly applied.
So, the long and the short of it: with serifs, the parent and child points of the italic strokes are hinted "via" the serifs. This leads to a relatively smooth outline, even at small ppem sizes, which is advantageous for ClearType rendering, while typically it also leads to a reasonable pixel pattern for black-and-white rendering. As with the sans-serif example, there may be "outlier" pixels, for which the remedy again is to apply "XBDelta" or "YBDelta", as appropriate.
The same assumptions with respect to the presence of "ItalicRun" and "ItalicRise" apply, and as with the sans-serif example, the italic run of the serif case can be "delta-ed" as well.
Lowercase characters and figures in general can be hinted in a similar way, keeping in mind that they use separate sets of CVTs for "ItalicRun" and "ItalicRise". Since the italic run and rise are defined using CVTs, "CVT inheritance" may be used to equalize different italic angles between upper and lowercase, or between different groups of lowercase characters. Last but not least, it should be mentioned that these two examples merely represent one way to hint italic characters, and while this way has proven rather useful, there may be other ways to achieve the same goal.