Features, Part 3: Advanced Contextual Alternates

  • by Rainer Erich Scheichelbauer
  • Tutorial
  • – Modified on

There’s nothing more boring than a font imitating handwriting but showing two identical glyphs when you type a double letter, as in ‘eel’ or ‘better’. What about having your font cycle through multiple alternatives for each of your glyphs?

Glyph Alternates

Now, first we need to set up alternate glyphs. A good idea to do so are the stylistic alternates. So, imagine you have the glyph A, and you’ll add A.ss01 and A.ss02. Likewise, for B, you’ll add B.ss01 and B.ss02. Do this with every glyph you want to cycle, perhaps even all your glyphs. And if you want, you can even go up all the way to .ss20. For the sake of simplicity, I’ll keep it to two alternatives for each glyph. Your A and B variations could look like this:

The AB on the left are the default glyphs. The two sets of variations following are the .ss01 and .ss02 glyphs.

Obviously, we need contextual substitutions for really cycling through all the alternate glyphs. We cannot seriously expect the user to select each letter individually and choose an alternative from the glyphs palette.

Contextual Cycling

Let’s start with a simple set-up and let’s just cycle one letter, e.g. the A. Click on the Features tab of your Font Info (Cmd-I), add a calt feature and type the following:

sub A A' by A.ss01;
sub A.ss01 A' by A.ss02;

When you’re finished, it could look something like this:

Now, hit the Compile button at the bottom left. Then, go back to the main window, open a new Edit tab (Cmd-T), activate the calt feature in the bottom left pop-up menu, and type your A a couple of times. Yeah, it works:

So, what does the feature do? The first line looks for two consecutive A glyphs. If it finds such a pair, it will replace the second A by A.ss01. Now, if we type a third A, the third A would be a default A again, thus yielding an A.ss01 A sequence. Ha, gotcha, this is where the second line kicks in: it will find this combination and replace the A after the A.ss01 by an A.ss02. Cool.

Cycling Through Glyph Classes

Now, wouldn’t it be cool if we could cycle not just through individual glyphs but through whole alphabets? No worries, my dear old friend, as it so happens, this is what glyph classes are for. Go back to your Features tab in the Font Info window (Cmd-I) and add these three classes:

  • DEFAULT, in which you’ll write, for example, A B C D E F, and so on
  • ALT1, in which you’ll list all glyphs of your font that contain the ending .ss01: A.ss01 B.ss01 C.ss01, etc.
  • ALT2, in which you’ll list all glyphs of your font that contain the ending .ss02: A.ss02 B.ss02 C.ss02, and so on

Now, our classes look like this:

Of course, you can add as many glyph names as you wish, as long as you actually have those glyphs in your font and you keep all three classes in sync. This means that all three classes must have the same number of glyphs, and should also have the same order of glyphs, otherwise your A will turn into a B etc. and you probably don’t want that.

Now, we can alter our calt feature as follows:

sub @DEFAULT @DEFAULT' by @ALT1;
sub @ALT1 @DEFAULT' by @ALT2;

Remember, the at sign (@) denotes a glyph class in feature code. What we are doing here is exactly the same as above, except we’re not just doing a single glyph, but working with complete classes. When you’re done, your Features tab should look like this:

Hit the Compile button and ta-ta-ta-taaaa, let’s see if it works. In the Edit tab, make sure the calt feature is active and type ‘BABABA’ or whatever you can think of with the letters you have in your classes.

FTW, high five!

Two Separate Cycles for More Randomness

Are we done yet? No, of course not. We still have one serious problem. We have a cycle that goes 1-2-3-1-2-3-etc. with each and every letter. Okay, now imagine that we want to write the name of our favorite band ever:

Up until the second B, everything works fine. The first A is from the DEFAULT group, the first B is from ALT1, the second B is from ALT2. But now comes the bummer. The second A is taken from DEFAULT again. So, while our cycling thingie is working great for consecutive letters, we will run into problems with two letters that are as far apart as our cycle is great. In our case, we are cycling through three versions of a letter. This means that every third letter will stem from the same class. Darn.

Luckily, we can have two different cycles. A logical differentiation is one cycle for consonants and another one for vowels. For the following code samples, I assume that usually no more than two vowels will come after each other and no more than three consonants. (Actually, you will need more than that for most languages, but after reading this, you should be able to stretch it to whatever size fits your needs.)

So, let’s set up our consonant classes like this:

Con0: B C D
Con1: B.ss01 C.ss01 D.ss01
Con2: B.ss02 C.ss02 D.ss02

Voc0: A E I
Voc1: A.ss01 E.ss01 I.ss01
Voc2: A.ss02 E.ss02 I.ss02

Etc: space comma

Again, expand them to your needs. The Etc class is for space and all the letters you were to lazy to draw alternates for. Obviously, you can separate your glyphs any old way into those two groups of classes. You can even put a (preferably rare) glyph in both. And it’s up to you where you want to place things like punctuation and figures. You may want to consider that some letters, like y or w, are considered consonants in some languages and vowels in others. Also, don’t forget your diacritics.

So, here we go. We’ll start with the consonants. Let’s recreate what we already had:

# consonant-consonant
sub @Con0 @Con0' by @Con1;
sub @Con1 @Con0' by @Con2;

Nothing new here. Wherever consonants follow each other, they’ll cycle through all three variants. But what if they are interrupted by another letter?

We need to add the following fragment. It is the same as above, but tries to consider one ‘interrupting’ letter:

# consonant-other-consonant
sub @Con0 [@Voc0 @Voc1 @Voc2 @Etc] @Con0' by @Con1;
sub @Con1 [@Voc0 @Voc1 @Voc2 @Etc] @Con0' by @Con2;

With the brackets, you can create a class on the fly, i.e. you don’t need to define it first. In this case we take all the vowel classes and the etcetera class and mold them into one. Seems pretty straightforward. To do the same thing with two interrupting letters, we add:

# consonant-other-other-consonant
sub @Con0 [@Voc0 @Voc1 @Voc2 @Etc] [@Voc0 @Voc1 @Voc2 @Etc] @Con0' by @Con1;
sub @Con1 [@Voc0 @Voc1 @Voc2 @Etc] [@Voc0 @Voc1 @Voc2 @Etc] @Con0' by @Con2;

Okay, now we have to do the same thing with the vowels. To cut it short, here’s the vowel code:

# vowel-vowel
sub @Voc0 @Voc0' by @Voc1;
sub @Voc1 @Voc0' by @Voc2;

# vowel-other-vowel
sub @Voc0 [@Con0 @Con1 @Con2 @Etc] @Voc0' by @Voc1;
sub @Voc1 [@Con0 @Con1 @Con2 @Etc] @Voc0' by @Voc2;

# vowel-other-other-vowel
sub @Voc0 [@Con0 @Con1 @Con2 @Etc] [@Con0 @Con1 @Con2 @Etc] @Voc0' by @Voc1;
sub @Voc1 [@Con0 @Con1 @Con2 @Etc] [@Con0 @Con1 @Con2 @Etc] @Voc0' by @Voc2;

# vowel-other-other-other-vowel
sub @Voc0 [@Con0 @Con1 @Con2 @Etc] [@Con0 @Con1 @Con2 @Etc] [@Con0 @Con1 @Con2 @Etc] @Voc0' by @Voc1;
sub @Voc1 [@Con0 @Con1 @Con2 @Etc] [@Con0 @Con1 @Con2 @Etc] [@Con0 @Con1 @Con2 @Etc] @Voc0' by @Voc2;

As you can see, I added an extra iteration for the vowel-other-other-other-vowel sequence. That’s because I assumed a maximum of three consonants between vowel a couple of paragraphs above. From this it should be clear how to extend this. If you need your substitution rule to span more interrupting letters, just add more groups with more bracket classes. If you have an extra alternate alphabet, each group of rules gets an extra line with the respective third class.

Alright, let’s sum up. This is what we have so far:

# consonant-consonant
sub @Con0 @Con0' by @Con1;
sub @Con1 @Con0' by @Con2;

# consonant-other-consonant
sub @Con0 [@Voc0 @Voc1 @Voc2 @Etc] @Con0' by @Con1;
sub @Con1 [@Voc0 @Voc1 @Voc2 @Etc] @Con0' by @Con2;

# consonant-other-other-consonant
sub @Con0 [@Voc0 @Voc1 @Voc2 @Etc] [@Voc0 @Voc1 @Voc2 @Etc] @Con0' by @Con1;
sub @Con1 [@Voc0 @Voc1 @Voc2 @Etc] [@Voc0 @Voc1 @Voc2 @Etc] @Con0' by @Con2;

# vowel-vowel
sub @Voc0 @Voc0' by @Voc1;
sub @Voc1 @Voc0' by @Voc2;

# vowel-other-vowel
sub @Voc0 [@Con0 @Con1 @Con2 @Etc] @Voc0' by @Voc1;
sub @Voc1 [@Con0 @Con1 @Con2 @Etc] @Voc0' by @Voc2;

# vowel-other-other-vowel
sub @Voc0 [@Con0 @Con1 @Con2 @Etc] [@Con0 @Con1 @Con2 @Etc] @Voc0' by @Voc1;
sub @Voc1 [@Con0 @Con1 @Con2 @Etc] [@Con0 @Con1 @Con2 @Etc] @Voc0' by @Voc2;

# vowel-other-other-other-vowel
sub @Voc0 [@Con0 @Con1 @Con2 @Etc] [@Con0 @Con1 @Con2 @Etc] [@Con0 @Con1 @Con2 @Etc] @Voc0' by @Voc1;
sub @Voc1 [@Con0 @Con1 @Con2 @Etc] [@Con0 @Con1 @Con2 @Etc] [@Con0 @Con1 @Con2 @Etc] @Voc0' by @Voc2;

Let’s try that out again. Hit compile, go to an Edit tab, activate the calt feature and here we go:

Oh yeah! See how the second A is different from the first? This makes you wanna go, ‘You are the dancing queen, young and sweet, only seventeen!’ Thank you for your attention, and dear Agnetha, Björn, Benny and Anni-Frid, thank you for the music.

Now, if you’re really geeky, you’ll figure out three separate cycles. But you’ll have to do that on your own. This should do for this installment.

2016-02-16: Updated Glyph Classes for better understanding (thanks Stephen Nixon!)