On this page:
This page is addressed to any font developers who want to add smart Graphite or AAT features to their fonts. It is not intended as a comprehensive guide, but as an introduction. We hope we can share the experiences we’ve gained from adding smart Graphite and AAT features to the Free Tengwar Font Project. On this page, we will try to explain problems we’ve run into and solutions we’ve found.
When we started with adding both Graphite and AAT features to the Free Tengwar Font Project fonts in late 2009, we were scared off because we thought it would be too difficult for us. Half a year later, we’re not scared off any more (and it certainly didn’t take the entire half year, but only some hours of our spare time every few weeks). Looking back, we think a major problem was lack of information. There is plenty of technical information, but there is hardly any low-level information. Normally, when you run into a computer problem, google will find lots of low-level information like forum threads or mailing list archives or blog posts or introductory sites. There’s hardly anything like that for smart font technologies. Hopefully, this page will contribute to changing that.
Developing a (tengwar) computer font requires smart font features. The first problem about smart font features is that there are several smart font technologies, each one with its shortcomings:
The basic requirement for building Graphite fonts is the Graphite compiler, a command-line tool that allows adding Graphite information to a font. There are several ways of installing the Graphite compiler, depending on your OS:
On Windows, download GraphiteCompiler_2_4.exe from the SIL page Download Graphite Compiler and install it.
On Ubuntu Linux, you should be able to get the grcompiler
package by enabling the SIL repository. Follow the instructions given there (to find out the codename for your distribution, run lsb_release -a
from the terminal) and then use Synaptic to install the grcompiler
package. Note, however, that at the time of this writing there is unfortunately a bug which, on some versions of Ubuntu (such as 10.04 “Lucid” 64-bit), makes grcompiler
consistently abort with the error message error(137): Could not open font--error code = 2
.
In case you are using a Linux version other than Ubuntu, or if you are afflicted by the bug mentioned above, you can instead compile grcompiler
from sources. The following procedure works on Ubuntu 10.04:
sudo apt-get install subversion build-essential automake libtool gettext libicu-dev libgraphite-dev docbook2x svn co http://scripts.sil.org/svn-public/graphite/grcompiler/trunk@1081 grcompiler cd grcompiler autoreconf -i ./configure make sudo make install
(It should also be possible to compile the sources on Mac OS, if you know how – we don’t).
On Mac OS and on Linux, the Windows GraphiteCompiler_2_4.exe will run smoothly with Wine (that is how we are running the Graphite compiler on Mac OS):
Install Wine. On Linux, all you need to do is installing the package wine
. It is a little bit more complicated on Mac OS. We have installed Wine with MacPorts. After installing MacPorts (this also requires Xcode from Apple’s Developer Tools and the latest release of XQuartz), we have typed the following command in Terminal.app:
sudo port install wine
With that command, MacPorts will install Wine on your computer. There will be a prompt for an administrator password, so you need to have administrator rights for your computer. The installation process will probably take a long time (it might be hours).
Download the GraphiteCompiler_2_4.exe
for Windows. You can simply install it by typing the following command in a terminal (assuming that it has been downloaded to the folder Downloads
and that you haven’t changed the directory in your terminal):
wine Downloads/GraphiteCompiler_2_4.exe
This will open the Windows Graphite Compiler Setup Wizard. Just go ahead and confirm the default installation folder C:\Program Files\Graphite Compiler
(the Wine C:\
folder is really ~/.wine/drive_c/
).
Optionally, if you want grcompiler
to be available as a command in a terminal, you may create a small executable file of that name and place in a directory included in your $PATH (for instance, in /usr/local/bin
): The file should contain the following:
#!/bin/bash wine ~/.wine/drive_c/Program\ Files/Graphite\ Compiler/GrCompiler.exe "$@"
Graphite documentation is available from the Graphite page at the SIL site. The documents that are relevant for adding Graphite to a font are GDL Tutorial for smart font developers (GraphiteTutorial_zip.zip
) and Graphite Description Language (GDL.pdf
).
In addition to these files, you may want to check out the GDL files that are included with the various Graphite fonts from the Graphite Font Download page at the SIL site. These are helpful real-world samples, and they contain plenty of annotations.
In comparison to the AAT documentation, the Graphite documentation is more extensive and more reliable. Additionally, there is a better chance that google will provide helpful pages when you encounter a problem.
A Graphite Description Language file (GDL) is what you will be working with when adding Graphite to a font. Some observations:
If adding the Graphite information dramatically increases the font size, try the following:
In the substitution table, try separating the GDL rules into more passes.
Don’t use the method of defining a glyph class (with rounded brackets) in the substitution table. Define all glyph classes in the glyph table, even if they only differ by a single glyph.
For what we know, Graphite has no special class for out of bound glyphs – unlike AAT that has the special class OOT
. Suppose for instance you wanted to create a silly font that automatically inserts an O before every word (if you like Irish). In AAT, this would be easy: First you would define a glyph class anyletter
that contains all possible word letters. Then you’d write a rule that inserts an O whenever a glyph of the glyph class anyletter
is preceded by a glyph that of the special glyph class OOT
(which contains all glyphs that are not in the class anyletter
). In Graphite, there is no such special glyph class. You have to use a workaround:
You might create a second glyph class notanyletter
that contains all glyphs that are not in the glyph class anyletter
. However, this may not be feasible in a huge font that contains thousands of characters from many different scripts.
You might create a pseudoglyph of O in the glyph table (assuming the glyph O has the postscript name O
):
pseudoO = pseudo(postscript("O"));
This allows you to write a sequence of two rules in the substitution table:
pass(1) _ > pseudoO:2 / _ anyletter ^ ; endpass; pass(2) pseudoO > _ / anyletter _ ; endpass;
The first rule inserts pseudoO
before every letter (this would change kay into OkOaOy). The second rule deletes every pseudoO
that is not at the beginning of a word (thus changing OkOaOy into Okay). Note that you should place these two rules in separate passes.
If you want to ease a conversion of your Graphite rules into AAT rules, then you should not position your diacritics (or other glyphs) by using Graphite’s elaborate anchor point attachment rules (unless you find a way to make AAT kerning really work – please tell us if you do!). Instead, you should add alternative combining glyphs to your font that have the proper metrics, or add special precomposed glyphs.
If you are adding both Graphite and AAT to a font, be sure to always add AAT first. The tool that adds AAT will mix up the names of AAT features and Graphite features, but the Graphite compiler will not.
If you are using FontForge to create your fonts, you should know that grcompiler has troubles with fonts where the OS/2 version is set to 4. The default in FontForge is Automatic, which used to result in a version lower than 4; however, with a patch committed on 20100611, FontForge now sometimes will use version 4 instead. If you use a newer version of FontForge and run into the error
Could not open font--error code = 26
when running grcompiler, you should try to manually set the OS/2 version to 3, in the Element menu, Font Info..., OS/2, Misc.
AAT will only work on Mac OS, so if you don’t have a Mac, the following information will not be very helpful to you. If you wonder why this page has much more information about AAT than about Graphite: It is because the available Graphite documentation is more extensive and more reliable.
Install Apple’s Font Tools. You can download the installer package of Font Tools Release 3.1.0 for free from Fonts & Text Downloads - Apple Developer. It is a set of command-line tools and of various documentation files. For all we know, you can only install them on Mac OS X. You need administrator access to your computer for the installation.
You need a TTF font. Apple’s Font Tools do not create fonts; they only add AAT rules existing fonts. If you want to create a font yourself, we’d recommend the free outline font editor FontForge. If you already have a font, install FontForge anyway (also requires administrator access). It will be a handy tool for inspecting your font.
Some knowledge of the command line interface will be helpful, since Apple’s Font Tools mainly consist of command-line tools. However, the tutorial that is installed with the Font Tools documentation is very considerate of people unfamiliar with the command line, so we guess previous knowledge of the command line is not necessarily required.
The documentation that is installed with Apple’s Font Tools is most helpful. It will be installed in the folder /Developer/Documentation/FontTools/
. It is the starting point for anybody interested in AAT development. Especially the tutorial and the file Apple Font Tool Suite.pdf are very important resources to anyone who wants to do AAT development.
In fact, we have found hardly any other useful documentation about AAT on the net. The Mac Malayalam page by Manoj and Vinod Prabhakaran provides a helpful example of a Morph Input File (it taught us how to do insertion). You can get another MIF from Michael Everson’s page on Designing and producing fonts for N’Ko.
This page is no substitute for Apple’s documentation. These are just some remarks about what we experienced:
For the purpose of developing a smart (tengwar) font, the tutorial is somewhat imbalanced. The first three lessons are all about completing the font so it will perfectly validate, while only the fourth lesson is really about smart font features. Completedness and validation, however, are not necessary for the smart font features. They are nice to have, but fonts will work perfectly without them. We still recommend the first three lessons of the tutorial. They will get you acquainted with the way the Font Tools work.
The tutorial supposes you’re working with a commercial font editor, especially with FontLab. Don’t worry about that, FontForge will do just fine.
man
pages); second detailed descriptions of input files that are required for adding certain AAT features.Add lists work differently (if you’d ever wish to use them): The utility ftxenhancer -A
will not accept the plain text add lists that are described in the documentation, but requires an XML add list. Luckily, the tool ftxanalyzer -g
will create such an XML add list, and not a plain text add list as desribed in the documentation. The correspondance between the described plain text add lists and the required XML add lists is pretty straightforward.
The tools used for font examination are long outdated, and many don’t exist any more, for instance the tool TrueEdit for viewing glyph numbers, or the tool WorldText for controlling typographic features. Glyph numbers can be viewed with the Character Palette or with FontForge, typographic features can be controlled with TextEdit.
The tutorial mentions a Kerning Input File (KIF), parallel to the Morph Input File (MIF) and the Justification Input File (JIF). However, there is no documentation whatsoever of a Kerning Input File. Appearently, Apple’s Font Tools are incapable of adding AAT kerning to a font. We’ve found only one way of adding AAT kerning to a font: FontForge’s Mac State Machine Dialog. All Apple provides are two Mac OS 8.5 tools from 1999, DumpKERN and FuseKERN (available at Apple - Font Tools), but we haven’t been able to use them (when we tried SheepShaver, an open source PowerPC emulator, DumpKERN and FuseKERN would run, but we couldn’t actually open any font).
For the most part, however, the outdated 2002 documentation at Apple’s developer site is not very different from the more recent 2006 revision, so we’re including the links here: There is the main file of Apple’s Font Tools tutorial, Tutorial.pdf, as well as the file Apple Font Tool Suite.pdf. Additionally, some sections of the latter are available as independent HTML pages from How to use the AAT Font Tool, especially the section about Morph Input Files (see below).
The most important aspect of Apple’s Font Tools are the Morph Input Files (MIF). It comes down to this: First you write a Morph Input File in a text editor (if you use TextEdit, make sure it is a plain text document); then you add this Morph Input File to your font with the command-line utility ftxenhancer
. Since that utility overwrites the font, you might want to copy it first, for instance with the command-line utility cp
:
cp noAATfont.ttf AATfont.ttf ftxenhancer -m MorphInputFile.mif AATfont.ttf
For most questions, Apple’s AAT documentation will give the answers. Both the sections in Tutorial.pdf and Apple Font Tool Suite.pdf are very useful. However, there is a number of tricks we have stumbled upon that were not answered there. Not that these will probably not be very useful unless you have read Apple’s AAT documentation (for instance the Morph Input Files section of the 2002 Apple Font Tool Suite.pdf).
A Morph Input File will generally consist of more than one rule. Each rule has a header that describes type and name of the rule as well as some properties, and something like a body. The body depends on the type of the rule. If the type is Noncontextual, then it will be a simple list of substitutions. The more elaborate types Contextual and Insertion have the following structure:
The error messages ftxenhancer
produces when it fails to process the MIF are often useful, but sometimes they’re not. Unfortunately, the line number that is indicated in the error message does not refer to your MIF.
Some smart features require more than one rule. An example: If you wanted the input yes to appear as no, then you would need two rules. The first rule could look like this:
// (header:) Type Contextual Name Conversion of "yes" into "no" Namecode 16000 Setting Convert any "yes" into "no" Settingcode 16001 Default no Orientation HV Forward yes Exclusive yes // (class list:) y y e e s s // (state array:) EOT OOB DEL EOL y e s StartText 1 1 1 1 2 1 1 StartLine 1 1 1 1 2 1 1 sawy 1 1 3 1 2 4 1 sawye 1 1 4 1 2 1 5 // (actions list:) GoTo Mark? Advance? SubstMark SubstCurrent 1 StartText no yes none none 2 sawy yes yes none none 3 sawy no yes none none 4 sawye no yes none none 5 StartText no yes change change // (substitutions list:) change y .null s 100
This first rule changes the input glyph string y e s
into .null e 100
. The 100 is an arbitrary number. The important thing about that number is that it must be higher than any Glyph ID number (GID) in the font. This works because the AAT machine sees only numbers and does not check whether they correspond to any glyphs (this is one of the hints that is not found in Apple’s 2002 documentation, but only in the 2006 revision). The .null
essentially means a glyph deletion. Of course, you have to convert the number back into a real glyph, for instance with this second rule:
Type Contextual Name Conversion of "yes" into "no" Namecode 16000 Setting Convert any "yes" into "no" Settingcode 16001 Default no Orientation HV Forward yes Exclusive yes e e dummy 100 EOT OOB DEL EOL e dummy StartText 1 1 1 1 2 1 StartLine 1 1 1 1 2 1 sawe 1 1 3 1 2 4 GoTo Mark? Advance? SubstMark SubstCurrent 1 StartText no yes none none 2 sawe yes yes none none 3 sawe no yes none none 4 StartText no yes change change change e n 100 o
This second rule converts the sequence e 100
into n o
. Together, the two rules will convert a yes into a no!
Connecting two rules by using an arbitrary number as in the above sample has a drawback: The higher the number you choose, the bigger the resulting font will get, especially if the number is used in many rules. So you better use a low number. There is an alternative to using a number: You may use instead a glyph that is not assigned to any Unicode number. If you use a glyph that is assigned to a Unicode number, you will not only risk that direct input may interfere with your rules, but for some reason we don’t understand, the font size will grow larger again. It may be a glyph that is specially created for this purpose, or it may be another glyph that will only surface from other rules that come later in your Morph Input File (the sequence of the rules matters!). Again, the lower the Glyph ID number of that glyph is, the smaller the size of the resulting font.
With regard to MIF syntax, lines that begin with a whitespace character (one or more spaces or tabs) must begin with a whitespace character, while lines that do not begin with a whitespace character must not begin with a whitespace character. While whitespace is mandatory, it’s up to you whether you use one whitespace character or more. While linebreaks are mandatory, it’s up to you whether you use one linebreak or more. Morph Input File are not case sensitive except for the various names you define (class names in the class list, state names in the state array etc.). Lines that only consist of hypen-minus or whitespace will be ignored. You can make a comment after two solidus signs: //
. The Morph Input File does not require the filename extension .mif
(you might as well use .txt
).
Appearently, Rearrangement rules do not work. The 2006 Apple Font Tool Suite.pdf, while describing Rearrangement rules, says in a sidenote: “since insertion actions are broken as of Mac OS X 10.4, this does not currently work”. As far as we can tell, insertion works well, as long as it IsKashidaLike
, but rearrangement doesn’t. We guess any rearrangement rule can be replaced by sequences of deletion (.null substitution) and insertion rules.
Sometimes, after successfully adding the MIF information to the font, when we’d type something it would look awfully messed up, with large blank space, or glyphs shifted way above the line where they should appear. This is likely to be a bug in the Mac OS text rendering since it does not happen in XeTeX. It seems to happen especially in very complex substitution chains. Most of the times we got around it by making things simpler. As an example, SVN revision 165 of FreeMonoTengwar.mif addressed such an issue (in addition to some unrelated changes concerning the glyph zwj
). This issue might have been caused by using the DEL
class for glyph deletion.
We don’t really understand how the numbers for the settings Namecode and Settingcode have to be chosen. There is a list of settings at Apple’s Font Feature Registry. You might want to use the numbers that figure there for features that figure there. Tengwar features will not figure there. Apple’s documentation says that for features not in their Registry, Namecodes and Settingcodes between 16000 and 32000 should be chosen. So we did.
A rule with the setting Exclusive no
must have an even number for the Settingcode.
The entries for Name and Setting will be selectable in applications that provide the required interface for advanced typography options, for instance TextEdit or XeTeX. Note that more than one rule may share the same Name, Namecode, Setting, and Settincode. If a feature requires more than one rule, then all these rules must have identical Name, Namecode, Setting, and Settingcode (as in the headings of the above samples). You may even use parenthesis or apostrophes for Name or Setting. A font that would include the above samples and the below samples would produce the following Typography dialog in TextEdit:
The very same entries for Name and Setting will validly pass XeTeX, in spite of their numerous brackets and apostrophes (but note that AAT works only in XeTeX on Max OS):
\documentclass{article} \usepackage{fontspec} \font\LaTeX="AATfont:LateX='([lL][aA][tT][eE][xX])' --> 'LaTeX'" \font\YesNo='AATfont:Conversion of "yes" into "no"=Convert any "yes" into "no"' \begin{document} \LaTeX latex \YesNo yes \end{document}
The proper encoding for a MIF seems to be macroman
. When the MIF is properly encoded, it is possible to use signs such as ä, ö, ü or typographic quotes in the entries for Name and Setting. However, you better don’t use signs like that! XeTeX seems to have a bug that will prevent it from selecting any AAT rule with non-ASCII signs in the entries for Name or Setting. So the safest thing to do is sticking with plain ASCII.
Sometimes, TextEdit’s Typography user interface would not reflect the changes we’d just made. This is very likely to happen when you add AAT to your font after having added Graphite, so always add AAT first! However, we’ve also experienced this with fonts that have no Graphite at all. Our idea is that it could be some font cache issue, though it might rather be related to the command-line environment or X11 than to Mac OS proper. Since we don’t know what exactly causes this issue, we don’t know how it can be prevented (except for adding AAT before Graphite). When it’s popped up, we’ve usually tried a couple of different things, then despaired, then found it worked again. We can’t say what of the various things we did really helped. Here’s some things we’ve tried:
Thoroughly remove the font you’re working with from all your system’s font folders. If you want to check whether there are multiple copies of the font in your system’s font folders, then the Mac OS application FontBook is of great help (rightclick the font in FontBook and select Reveal in Finder). Also remove the files encodings.dir
, fonts.dir
, fonts.list
and fonts.scale
from the system font folder where the font was located (if they are there). These files are automatically created in all your system font folders whenever you launch X11.app (they must be related to the way X11 font management works).
Log out and log in again.
Restart your computer.
Try renaming the font – not just the name of the font file, but the internal name of the font (for instance with FontForge). If it really were an issue of the system caches, then a font with a new name should not be affected.
Use the action Clean system font caches... from the application Linotype FontExplorer X (it is a free application, but it is somewhat hard to get, since it no longer figures on the vendor’s website, but the download via FontExplorer X at versiontracker.com still works). Note that this action requires you to have administrator access to your computer and restarting you computer.
Note that the headers setting Forward no
can be used to achieve certain complex substitutions quite elegantly. An example: If you want the words latex or LATEX (or other upper and lowercase combinations) to appear as LaTeX, you will only need two rules:
Type Contextual Name LaTeX Namecode 16010 Setting '([lL][aA][tT][eE][xX])' --> 'LaTeX' Settingcode 16011 Default no Orientation HV Forward yes Exclusive yes anyl l L anya a A anyt t T anye e E anyx x X EOT OOB DEL EOL anyl anya anyt anye anyx StartText 1 1 1 1 2 1 1 1 1 StartLine 1 1 1 1 2 1 1 1 1 sawl 1 1 2 1 2 3 1 1 1 sawla 1 1 3 1 2 1 4 1 1 sawlat 1 1 4 1 2 1 1 5 1 sawlate 1 1 5 1 2 1 1 1 6 GoTo Mark? Advance? SubstMark SubstCurrent 1 StartText no yes none none 2 sawl no yes none none 3 sawla no yes none none 4 sawlat no yes none none 5 sawlate no yes none none 6 StartText no yes none change change x 100 X 100
This first rule will change the glyphs x or X into the arbitrary number 100, if they are preceded by the letters late or LATE (or other upper and lowercase combinations).
Type Contextual Name LaTeX Namecode 16010 Setting '([lL][aA][tT][eE][xX])' --> 'LaTeX' Settingcode 16011 Default no Orientation HV Forward no Exclusive yes dummy 100 late l L a A t T e E EOT OOB DEL EOL dummy late StartText 1 1 1 1 2 1 StartLine 1 1 1 1 2 1 sawdummy 1 1 2 1 2 2 GoTo Mark? Advance? SubstMark SubstCurrent 1 StartText no yes none none 2 sawdummy no yes none change change 100 X e e E e t T T T a a A a l L L L
This second rule uses the setting Forward no
. It begins at the end of a line and looks for the arbitrary number 100 that has been produced by the first rule. If it encounters the number, no more glyphs need to be checked. The first rule has already assured that any 100 will be preceded by the glyphs late or LATE.
A Morph Input File with duplicates in its class list will pass ftxenhancer
. Duplicates in the class list are very likely to nullify the effect of a MIF rule. If a MIF rule has no effect, check for duplicates in the class list!
The glyph classes that have been defined in the class list must reappear in the same order in the state array. Otherwise, ftxenhancer
will quit with the following error message: The states in the state matrix are not in numerical order.
A state array that has extra rows with glyph classes that have not been defined will pass ftxenhancer
. That may come handy if you have a sequence of similar rules that differ in the number of glyph classes (as an example, you may note that the state array on lines 616–621 of SVN revision 166 of FreeMonoTengwar.mif has a row for the class other
, even though no such row is defined).
Mind the DEL class in the state arrays! We think of it as an invisible glyph that could appear between any pair of two glyphs. With any given state, when the DEL class follows nothing happens but the same state starts again (see for instance in the state array of the third above sample). This may often require special DEL class actions in the action lists (for instance action 3 in the action list of the first above sample).
We have found two ways for doing glyph deletion. Neither method really deletes the glyph, but only substitutes it by something invisible. One method is replacing a glyph by DEL
. This has the benefit that in subsequent rules, you need not pay special attention to the deletion since you must mind the DEL
class anyway. However, we suspect that this method may be connected to strange glyph shifts in Mac OS applications, especially when the Morph Input File is very complicated.
Another method is substituting the glyph by the .null
glyph, as in the first above sample. The .null
glyph is required in every TTF font. It is invisible, so when you substitute it for a glyph, that glyph will disappear. While this seems to prevent glyph shifts, it will complicate subsequent MIF rules because you need to take the .null
glyph into account. When there are multiple subsequent MIF rules, taking the .null
glyph into account seems to somehow increase the resulting font size. The resulting font size can be decreased by using an intermediate dummy glyph: First substitute the glyph you want to delete by a dummy glyph (for instance an arbitrary number); then use that dummy glyph in subsequent MIF rules, and in the end append a simple Noncontextual rule that substitutes the dummy glyph by .null
(for an example of this, see SVN revision 168 of FreeMonoTengwar.mif).
In a substitution list, you must include all glyphs of the affected glyph class, even if some glyphs don’t need to be substituted. You must still include them, see for instance T and L in the substitution list of the fourth above sample. If you forget to include these glyphs, the Morph Input File will still pass ftxenhancer
, but the resulting font will not display the affected glyphs correctly.
If you choose the setting IsKashidaLike no
, then the resulting font will have serious display issues, with characters appearing way above the line. We dont’t know why. Neither do we know what this setting’s supposed to do.
OpenType is very powerful with standardized scripts. It is not suitable for non-standardized scripts such as the tengwar (see for instance the section about the Pollard script in John H. Jenkins The Unicode Character-Glyph Model: Case Studies).
We’ve found one OpenType feature that works even in the Personal Use Area of Unicode: OpenType Ligatures (at least in some applications such as Firefox or OpenOffice.org). They can be added to a font for instance with FontForge, as described in the section Lookups and Features of the FontForge tutorial. Of course, ligatures have only very limited capabilities and cannot do all the smart rendering required for a proper tengwar font.