Collaborating with the FontForge auto-instructor
The method described here replaces an earlier, more cumbersome method, which will, however, still work. That earlier method is described here.
Recent versions of FontForge do an excellent job of "writing" instructions for TrueType fonts. Yet the output of the FontForge auto-instructor may not please you for every glyph. You may want finer control over the most commonly displayed characters in your font, or you may want to add deltas and other refinements to the instructions generated by FontForge.
You can easily merge your own hand-written instructions with automatically generated ones by running Xgridfit in merge-mode. Indeed, Xgridfit running in merge-mode can manage most aspects of font instructing: working through FontForge, it can set a font's "blue values," generate PostScript hints and TrueType instructions based on those hints, and merge your own Xgridfit programming into the font in such a way that the two sets of instructions do not conflict with each other. (It is up to you, of course, to make sure that glyphs instructed by you harmonize with those instructed by FontForge.)
You can also use merge-mode to add instructions to a font that already contains them.
Running in merge-mode
To run in merge-mode, include the -m option on the command-line when you invoke Xgridfit. When invoked in this way, Xgridfit creates a Python script that merges the programming in newstuff.xgf into an existing font, which presumably contains TrueType instructions:
xgridfit -m -i oldfont.sfd -o oldfont.ttf newstuff.xgf
The resulting script, newstuff.py, reads a file oldfont.sfd, merges in Xgridfit programming without disturbing the programming it already contains, and generates the font oldfont.ttf:
fontforge -script newstuff.py
To make FontForge auto-instruct the font before merging in Xgridfit programming, include the -A option:
xgridfit -m -A -i oldfont.sfd -o oldfont.ttf newstuff.xgf
The resulting script makes FontForge auto-hint the font before auto-instructing it. If you have added to or refined the hints by hand, add an option -H no to skip this step.
How merge-mode works
Merge-mode replaces a font's glyph programs with Xgridfit glyph programs. It adds Xgridfit functions after a font's functions; either adds to or replaces a font's prep program; adds its own variables; and merges Xgridfit <control-value> elements with those already in a font, avoiding duplication when possible. Because the Xgridfit compiler knows nothing about the target font, it defers resolving various indexes (function, control-value and storage); these are fixed up by the Python script when it is run in FontForge. The Python script also adjusts a font's maxp settings to make room for both the font's legacy programming and the Xgridfit programming you add.
Most of this happens behind the scenes in such a way that you need not be concerned about it. But you may need to think some about the way control-values, the pre-program and functions are handled.
Control-values
The default behavior of merge-mode, when it adds control-values to a font, is to avoid duplication. If your Xgridfit program contains a control-value like this one:
<control-value name="lc-vert-stem" value="125"/>
and the target font already has a control-value with value 125 at index 23, then the control-value "lc-vert-stem" is not added to the font. Rather, any <move> instruction that refers to the control-value "lc-vert-stem" actually uses the control-value at index 23.
You may have good reasons for overriding this behavior. For example, the x-height of your font may be 1000, and also the width of o. You may want to apply a <control-value-delta> to the width of o but not the x-height, and in that case the control-value 1000 should be in the font twice. To make sure that a value is appended to the end of the control-value table, add the attribute index="append":
<control-value name="lc-vert-stem" value="1010" index="append"/>
You may find that the font you are working with already has two control-values with the same value; in that case, supply the index of the control-value you want to use:
<control-value name="lc-vert-stem" value="1063" index="156"/>
Note that if you specify a value different from the one found at the index you specify, a warning will be printed and the value in the control-value table will be updated.
In merge-mode, the index of a control-value is unknown at compile-time. This can cause problems with the <compile-if> element, which may have to decide, on the basis of a control-value, whether to compile, say, a <move> with or without a distance attribute. The usual way of accomplishing this has been as follows:
<macro name="move-pt"> <param name="cv" value="-1"/> <param name="p"/> <compile-if test="cv >= 0"> <move distance="cv"> <point num="p"/> </move> <else> <move> <point num="p"/> </move> </else> </compile-if> </macro>
In merge-mode, a control-value index cannot be evaluated against a number at compile-time without causing an error. However, since you can count on a control-value index evaluating to something other than a number in merge-mode, this will work:
<macro name="move-pt"> <param name="cv" value="1"/> <param name="p"/> <compile-if test="nan(cv)"> <move distance="cv"> <point num="p"/> </move> <else> <move> <point num="p"/> </move> </else> </compile-if> </macro>
If you want the macro to compile the same way in merge-mode and other modes, you can do it this way:
<macro name="move-pt"> <param name="cv" value="-1"/> <param name="p"/> <compile-if test="(not(merge-mode) and cv >= 0) or (merge-mode and nan(cv))"> <move distance="cv"> <point num="p"/> </move> <else> <move> <point num="p"/> </move> </else> </compile-if> </macro>
If you like, you can approximate the behavior of other modes in merge-mode by including this <default> element in the top level of your file:
<default type="cv-num-in-compile-if" value="yes"/>
Now a control-value index in the test attribute of a <compile-if> element is always resolved as a number. This number is not guaranteed to be the one actually used in the font. However, it is guaranteed to be useful in validity and equality tests; that is, it will evaluate as >= 0 if the referenced control-value is present in the file, and an <alias> that references a control-value will evaluate to the same index as that control-value. Thus the first example above remains valid in merge-mode if the cv-num-in-compile-if directive is present.
The pre-program
The pre-program generated by the FontForge auto-instructor is quite simple: it disables instructions at certain resolutions, sets dropout control, and sets the default value of the control-value cut-in for various resolutions. By default, merge-mode appends your pre-program to the one generated by FontForge; but you can override this behavior if you wish, by adding the option -P no on the command line:
xgridfit -m -A -P no oldfont.xgf
Functions
The num attribute for functions has been deprecated since version 1.0; it is incompatible with the <legacy-functions> element. In merge-mode, the <legacy-functions> element is unneeded, and it is ignored. Also, any <function> elements with the num attribute are ignored. If your code contains a <legacy-functions> element, you should edit to convert the functions it contains to <function> elements. If your code contains <function> elements with the num attribute, you should remove that attribute. The num attribute, of course, accommodates "raw" code that looks like this:
<push>3</push> <command name="CALL"/>
To keep your functions, search out these CALL instructions and substitute the name of the function for the number in the <push> command. The Python script generated by Xgridfit will correctly fix up the number.
Setting blue values
"Blue values" are appropriate to PostScript (Type 1 and OpenType/CFF) fonts; but FontForge needs them for proper auto-hinting, and it needs hints for auto-instructing. If you wish to accept the values supplied by FontForge, then simply press the "Guess" button for the blue values in the font info dialog; otherwise, you may set them here. Include <ps-private> as a top-level element (a child of <xgridfit>), thus:
<ps-private> <entry name="BlueValues" value="-33 -2 856 873 1358 1385"/> <entry name="OtherBlues" value="-578 -553"/> <entry name="BlueFuzz" value="0"/> </ps-private>
Any entries here are set in the font's PS private dictionary; any existing entries not specified here are left alone. Values for all entries except the one for "BlueFuzz" must be space-delimited lists; FontForge will complain if there is not an even number of entries. "BlueFuzz" must be a single number.
The <ps-private> element is ignored in all of Xgridfit's modes other than merge-mode.
Defaults relating to merge-mode
The command-line options relevant to merge-mode can also be set in the file with <default> elements. Here they are:
<default type="auto-instruct" value="yes"/> <default type="auto-hint" value="no"/> <default type="delete-all" value="yes"/> <default type="combine-prep" value="no"/>
Remember that command-line options always override <default> elements.
Saving in merge-mode
When Xgridfit running in merge-mode first reads a font (either ttf or sfd) containing TrueType programming, it reads the existing fpgm and prep tables, and also the relevant maxp entries, and saves them in a dictionary of its own. It also stores in this dictionary a record of the control-values it installs in the font. When merge-mode saves the font in sfd format, it attempts to save this Xgridfit dictionary in FontForge's font.persistent object.
When merge-mode opens an sfd file containing an Xgridfit dictionary, it uses the saved information as follows:
- Merge-mode uses the saved information to revert the font's fpgm, prep and maxp tables to their original state. Fresh copies of your Xgridfit functions, pre-program, and maxp settings are merged in; thus you need not worry about stale copies of your work.
- Merge-mode keeps the revised cvt table (the control-values) from the previous run, but when it finds that a control-value has previously been added to the font it declines to add it again. This is true even if the <control-value> element has the index attribute. However, if you have changed the value of a control-value, the new value is substituted for the old one (a warning is printed telling you that this has been done).
If merge-mode finds that an object other than a dictionary has already been saved as font.persistent, then it cannot save its Xgridfit dictionary there. In that case, it will issue a warning, and you should work with the original font instead of the saved copy. In fact, an alternative to relying on the Xgridfit dictionary to recover the original state of the font is always to run merge-mode against the original, unaltered font: either lacking all TrueType data or containing only the data it contained before you added Xgridfit programming.