Tutorial 3: Working with FontForge auto-instructions
Detailed recommendations for working with fonts auto-instructed by FontForge may be found in Collaborating with the FontForge auto-instructor; here is a practical example drawn from Junicode Regular, in which b is instructed "by hand," while bbar (U+0180) has been auto-instructed.
Although the outlines of b and bbar are exactly the same except for the bar, the instructed versions of these glyphs look rather different at 20 ppem:
![]() |
![]() |
There are various subtle differences here, but the obvious ones are that the top of the bowl is thicker and higher in bbar than in b and that bbar is narrower. If we can change these things, the two glyphs will match reasonably well.
The first step is to get a copy of the program for bbar, which has been extracted from a generated font using TTX and ttx2xgf. The source files for Junicode are arranged by Unicode range; we copy the bbar program into Junicode-Regular-LatExtB.xgf. Here is the program, in which we have identified the problem areas by running the FontForge TrueType debugger:
<glyph ps-name="bbar" xml:id="bbar" init-graphics="no"> <command name="SVTCA" modifier="0"/> <push>3 1 0</push> <command name="CALL"/> <push>9</push> <command name="SHP" modifier="1"/> <push>60</push> <command name="MDRP" modifier="01101"/> <push>27 3 0</push> <command name="CALL"/> <push>24</push> <command name="MDRP" modifier="01101"/> <push>27 24 10</push> <command name="CALL"/> <push>64 27 31 9</push> <command name="CALL"/> <!-- top of bowl --> <push>42 52 3 31 13</push> <command name="CALL"/> <push>42</push> <command name="MDRP" modifier="01101"/> <push>42 52 10</push> <command name="CALL"/> <push>64 42 46 9</push> <command name="CALL"/> <!-- end top of bowl --> <push>20 17 3 31 13</push> <command name="CALL"/> <push>38</push> <command name="SHP" modifier="1"/> <push>20</push> <command name="MDRP" modifier="01101"/> <push>34</push> <command name="SHP" modifier="0"/> <command name="SVTCA" modifier="1"/> <push>63</push> <command name="MDAP" modifier="1"/> <push>15</push> <command name="MDRP" modifier="10110"/> <push>21</push> <command name="SHP" modifier="0"/> <push>57</push> <command name="MDRP" modifier="01101"/> <push>32 39</push> <command name="SHP" modifier="0"/> <command name="SHP" modifier="0"/> <push>57 15 10</push> <command name="CALL"/> <push>64 57 37 9</push> <command name="CALL"/> <push>15 57 10</push> <command name="CALL"/> <push>64 15 26 9</push> <command name="CALL"/> <push>64 15 19 9</push> <command name="CALL"/> <!-- width of character --> <push>57</push> <command name="SRP0"/> <push>49 1</push> <command name="CALL"/> <push>0</push> <command name="MDRP" modifier="01101"/> <!-- end width of character --> <push>64 1</push> <command name="CALL"/> <push>57 15</push> <command name="SRP1"/> <command name="SRP2"/> <push>7 10 30</push> <command name="IP"/> <command name="IP"/> <command name="IP"/> <push>49</push> <command name="SRP1"/> <push>6 3 46</push> <command name="IP"/> <command name="IP"/> <command name="IP"/> <command name="SVTCA" modifier="0"/> <push>60 3</push> <command name="SRP1"/> <command name="SRP2"/> <push>6 13</push> <command name="IP"/> <command name="IP"/> <push>52</push> <command name="SRP1"/> <push>0</push> <command name="IP"/> <command name="IUP" modifier="0"/> <command name="IUP" modifier="1"/> </glyph>
The FontForge code looks a little obscure because it does much of its work by making calls to a library of functions, which the auto-instructor inserts into every font. (These well-crafted functions may also be of interest to Xgridfit programmers. They are documented in the FontForge source code: see the file nowakowskittfinstr.c). However, it is not really necessary to understand the function calls to see in general terms what the auto-instructor is doing.
We'll start by defining some constants for point numbers: first the two points that define the top and inside top of the bowl, and next some key points for the straight stem on the left of the glyph and the round stem on the right:
<constant name="bowl-top" value="46"/> <constant name="bowl-top-inside" value="52"/> <constant name="left-left" value="15"/> <constant name="left-right" value="57"/> <constant name="right-right" value="0"/> <constant name="right-left" value="49"/>
![]() |
The next step is to notice how the Junicode b is instructed. There, bowl-top is positioned at the x-height with a control value (lc-x-height), and the distance between bowl-top and bowl-top-inside is regulated with another control value (lc-horz-thin-curve). We'll comment out the offending instructions and replace them with Xgridfit code that employs the same control values:
<move distance="lc-x-height"> <point num="bowl-top"/> <move distance="lc-horz-thin-curve"> <point num="bowl-top-inside"/> </move> </move> <!-- top of bowl <push>42 52 3 31 13</push> <command name="CALL"/> <push>42</push> <command name="MDRP" modifier="01101"/> <push>42 52 10</push> <command name="CALL"/> <push>64 42 46 9</push> <command name="CALL"/> -->
Next, the FontForge auto-instructor has instructed along the x-axis by first positioning left-left, then left-right relative to that, then right-left, then right-right. In other words, it has gone strictly left-to-right. The Junicode b is instructed in a different order: first the left stem is regulated, then the overall width of the glyph (left-left to right-right, with a control value), and finally the right stem. We can approximate this behavior in revising the FontForge instructions (we'll ignore the width of the left stem for now):
<move distance="bpq-char-width"> <reference> <point num="left-left"/> </reference> <point num="right-right"/> <move distance="lc-vert-curve"> <point num="right-left"/> </move> </move> <!-- width of character <push>57</push> <command name="SRP0"/> <push>49 1</push> <command name="CALL"/> <push>0</push> <command name="MDRP" modifier="01101"/> -->
We'll address two more details before testing the code. First, the FontForge auto-instructor regulates the left sidebearing with an MDRP instruction:
<push>63</push> <command name="MDAP" modifier="1"/> <push>15</push> <command name="MDRP" modifier="10110"/>
This is the equivalent of the following:
<move> <point num="left-sidebearing"/> <move> <point num="left-left"/> </move> </move>
But Junicode uses a control value to regulate this distance. We can revise the code very simply, by adding the control value and changing MDRP to MIRP.
<push>63</push> <command name="MDAP" modifier="1"/> <push>15 b-left-side</push> <command name="MIRP" modifier="10110"/>
Or we could replace the code (omitting to touch the left-sidebearing point, since this is unnecessary):
<move distance="b-left-side"> <reference> <point num="left-sidebearing"/> <reference> <point num="left-left"/> </move>
Junicode also uses a control value to regulate standard vertical stems, and so we can make a similar change to the way the auto-instructor regulates the distance between left-left and left-right. We'll just change this:
<push>57</push> <command name="MDRP" modifier="01101"/>
to this:
<push>57 lc-vert-stem</push> <command name="MIRP" modifier="01101"/>
What remains is to merge the custom Junicode programming with the automatically generated program and compile (just b and bbar, since we haven't got all day). Since we're feeling confident, we'll use the -f option to pipe Xgridfit's output to FontForge:
$ xgfmerge -o out.xgf -p -c auto-Junicode-Regular.xgf Junicode-Regular*.xgf $ xgridfit -g b+bbar -f out.xgf
Here is the result from Junicode-Regular.ttf as viewed in FontForge, with b again for comparison:
![]() |
![]() |
It might have been just as easy to copy the program for b and edit it; but editing an auto-instructed glyph will usually be quick and very often the easiest thing to do.