Merge-mode
The method described here replaces earlier, more cumbersome methods, which do, however, still work. Those earlier methods are described here and here.
If you have used Xgridfit to write all the TrueType programming for a font, the process of getting that programming into the font is quite simple: just run the Xgridfit compiler to generate a Python script and run the script in FontForge. But your job may not be so simple. You may need to add instructions to a font that already contains them, or you may wish to let FontForge's excellent auto-instructor do the bulk of the work, hinting only a few glyphs by hand. If this is your situation, merge-mode may be the tool you need. Xgridfit running in merge-mode produces a Python script that can:
- Set a font's "Blue" values.
- Run FontForge's auto-hinter and auto-instructor.
- Merge Xgridfit programming into the font without disturbing existing programming.
- Preserve information about the original state of the font so that Xgridfit can be run against it repeatedly without adding redundant code.
In short, Xgridfit in merge-mode aims to serve as a one-stop shop for everything related to TrueType programming.
Running in merge-mode
To run in merge-mode, include the -m option on the command-line when you invoke Xgridfit:
xgridfit -m -i oldfont.sfd -o oldfont.ttf newstuff.xgf
This command produces a script, newstuff.py, that reads a file oldfont.sfd, merges in Xgridfit programming without disturbing the programming it already contains, and generates the font oldfont.ttf. Run it in FontForge thus:
fontforge -script newstuff.py
Auto-instructing the font
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
Now newstuff.py makes FontForge delete all existing TrueType programming and data, auto-hint the font, and then auto-instruct it. If you have already hinted the font or refined the hints by hand, add an option -H no to skip the auto-hint step.
Programming for merge-mode
There are almost no differences between a program written for merge-mode and any other Xgridfit program: programs you have already written will probably compile in merge-mode without alteration. To understand the few differences, it may help to know a little about how merge-mode handles functions, the pre-program, variables (storage locations) and control-values:
- Xgridfit functions are added to the end of the font's fpgm table. TrueType functions are indexed, not named, and Xgridfit functions always have higher indexes than those already in the fpgm table.
- The Xgridfit pre-program is normally appended to the existing prep table, but you can choose instead to discard a font's prep table and use only your own pre-programming.
- Like functions, TrueType variables are indexed, not named; Xgridfit expands the existing storage area to make room for its own variables, which all receive higher indexes than those already in the font.
- Xgridfit adds control-values to the font only when necessary; it attempts to avoid duplication in the control-value table by adopting existing entries for its own use.
The Xgridfit compiler knows nothing about the font that is the target of its operations. Instead, the Python script generated by the compiler queries the font and fixes all the indexing in the Xgridfit-generated TrueType code before installing it. The following sections explain the implications of merge-mode's behavior.
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 appended to the control-value table. 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 and the compile-if attribute, which may have to decide, on the basis of a control-value, whether to compile, say, a <move> with or without a distance attribute. Before the appearance of merge-mode, the recommended way of accomplishing this was 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
Now your own pre-program is substituted for the one in the font (or the one generated by the auto-instructor) instead of added to it.
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 such older 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.
Variables
Xgridfit uses the TrueType storage area to track the graphics state and to store variables. It has never been advisable to use number literals to refer to storage locations, and in merge-mode it is impossible to do so.
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. Note also that the PS private dictionary is not stored in a TrueType font, but only in the FontForge .sfd file.
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, with their default values:
<default type="auto-instruct" value="no"/> <default type="auto-hint" value="yes"/> <default type="delete-all" value="no"/> <default type="combine-prep" value="yes"/> <default type="cv-num-in-compile-if" 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 and functions 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; if you prefer, you may instead store it in an external file.
When merge-mode opens an .sfd file containing an Xgridfit dictionary, it uses the saved information as follows:
- reverts the font's fpgm, prep and maxp tables to their original state. Earlier Xgridfit programming is discarded and freshly compiled versions of your functions, pre-program, and maxp settings are merged in. You need not worry about stale copies of your Xgridfit programming getting stranded in the font.
- keeps the control-value table as revised by an earlier Xgridfit script, 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).
Things happen a little differently when Xgridfit in merge-mode compiles an Xgridfit program with option -c no or a <default> element with type="compile-globals" and value="no". The generated Python script uses the saved data from the previous run to resolve the indexes of control-values, functions and variables; it cannot install its glyph programs unless this data is available. Thus you must always run a script that installs these global elements before you run any script that installs only glyph programs.
As mentioned earlier, merge-mode normally stores data in FontForge's font.persistent object. This can be saved only in an .sfd file, not in a .ttf font. You may, if you prefer, store the data in an external file. To do so, either include the option -F filename on the command-line or include a <datafile> element in every Xgridfit file that needs one:
<datafile>myfont.data</datafile>
Now the generated Python script will store data in, and read it from, the file myfont.data.