Trouble getting Contextual Alternates to alternate

Roger S. Nelsson's picture

I am working on a script font where several letters have 2 versions: one swashed (main) and one plain (alternate). When the same letter comes twice in a row I want to switch one of them around - to avoid colliding swashes.

I mainly use the calt feature like this: sub l l' by l.calt;
This works as expected, and if I keep repeating the letter the two versions alternate all the way.

But sometimes I want to switch the first letter: sub g' g by g.calt;
When I now keep repeating the letter I just get a long row of the switched glyph with the original glyph at the end - no continous alternation. How do I write the code to avoid this?

For simple letter pairs this is just a minor niggle (I really don't expect text to be set with many identical letters in a row), but as I want to group/class similar letters to also avoid collisions between different letters, I need to get this sorted.

I would really appreciate some input from someone with a better grasp on OpenType feature programming than myself ;)

Arno Enslin's picture

Continues alternation, second one of each pair replaced:

sub g g' g by g.calt;
sub g.calt g g' by g.calt;

Continues alternation, first one of each pair replaced:

sub g by g.calt;
sub g.calt g.calt' g.calt by g;
sub g g.calt g.calt' by g;

Both successfully tested in FontLab. Is this that, what you were searching for?

Roger S. Nelsson's picture

Thanks for your input! :)

Your first block: well, this was never a problem - it worked with a simple "sub l l' by l.calt;". They way you suggest it it looks like only three-letter repeats will be changed, and not double-letters...? I haven't tried it out, though - as I already had a working solution.

Your second block: will not the first sub change every single-letter occurrence of the letter to the alternate form, too? Wouldn't want that...

After some experimentation I have almost sussed it - using lookups! :)
This is very close to working:

lookup calt1 {
sub g' g by g.calt;
} calt1 ;
lookup calt2 {
sub g.calt g.calt' by g;
} calt2 ;

Only caveat is that with odd repeats of letters the last two in the sequence will be the colliding main forms. Hmm... My head hurts from trying to find a perfect solution to this... ;)

Arno Enslin's picture

let’s stay with the g.

sub g g' by g.calt;

results in:

g g.calt g.calt g.calt g.calt …

if followed by

sub g.calt g.calt' by g;

the result is probably:

g g.calt g g g g …

M Y P R O P O S A L

sub g g' g by g.calt;
sub g.calt g g' by g.calt;

results in

g g.calt g g.calt g g.calt g g.calt …

and

sub g by g.calt;
sub g.calt g.calt' g.calt by g;
sub g g.calt g.calt' by g;

results in

g.calt g g.calt g g.calt g g.calt g …

I think, I would understand your wish better, if you post the output, that you want to have.

Would you like to have one of the following outputs?:

g g g g g g g g.calt

or

g g g g g g g.calt g

The first one probably works in this way:

ignore sub g' g;
sub g' by g.calt;

The second one should work in this way:

ignore sub g' g g;
sub g' by g.calt;
sub g.calt g.calt' by g;

or

ignore sub g' g g;
sub g' g by g.calt;

This time untested.

Roger S. Nelsson's picture

sub g g' by g.calt;
actually results in g g.calt g g.calt g g.calt ... just as I want it to when the second letter in a pair should be switched. Easy. ;)

The problem is when I want to sub the first letter in a pair (because the combination alt/main looks better that way).
For repeated letters the perfect solution would be code that generates the result:
g.calt g g.calt g .... when there are even repetitions, and
g g.calt g g.calt g ... when there are odd repetitions
But that would probably take some sort of backwards changing in the text string that I'm not even sure is possible...

Arno Enslin's picture

Looks like a loop would be required after

ignore sub g' g g;
sub g' g by g.calt;

But actually I don’t have an idea. But I am tired. Maybe I find an elegant solution later.

sub g g' by g.calt;
actually results in g g.calt g g.calt g g.calt

Sure? I thought, that I had unsuccessfully tested it in FontLab. Normally all letters g, that are successors of letters g should be substituted. But again I am tired. Maybe I am actually messing it up.

Roger S. Nelsson's picture

Yeah, it is straining my brain, too... ;)
But I suddenly noticed that you have used "ignore" on some subs. I'll study up on what that syntax can do...
(and it is a bit confusing when you edit your posts after I have read them ;)
Thanks again for the input. :)

Arno Enslin's picture

Actually I only see a step by step solution, but no way to program a kind of backwards loop – a substitution beginning with the end of a string. The contradiction of elegance.

Maybe the problem would not appear, if the alternates would be your standard letters and the actual standard letters the alternates.

Roger S. Nelsson's picture

The basic letters have looped ascenders/descenders. When there are two consecutive loops (overlapping) I want to switch one letter to a straight form "piercing" the (other) loop. So for the ascenders the loop goes to the right, and the right letter has to switch. Easy. But the descenders are looped to the left, so the left letter has to switch. Which looks to be difficult to program for alternate substitution for multiple loops. Bummer.
I think I have to compromise a bit... Perhaps it is possible to program it (for odd numbers of loops) so that I get the sequence "g.calt g g.calt g ... g.calt g g.calt g.calt g"? So ending the sequence with two consecutive straighs and one final loop? Can that be done?

oldnick's picture

I don't know if this is relevant, but apparently there isn't a single word in the English language containing three gs in a row...

Roger S. Nelsson's picture

It is just an example - to keep it simple while discussing... ;)
The plan is to expand the feature so that ANY sequence of overlapping looped letters will have the switching!
There are MANY words that contain more than two of the letters "b d h k l" or "g j p y" in a row, and I want the calt feature to fix all these...

Arno Enslin's picture

Okay, here is the dirty solution. (Personally I hate those solutions.) You can extend it. Actually it is enough to cover a string with a maximum of 32 letters, that have an alternate. If the string is broken by a letter, that does not have an alternate, the feature applies to both parts of the string and both parts can consist of a maximum of 32 letters. (For those ones, that have not read the whole thread: g and g.calt need to be replaced by classes.)

feature calt {
ignore sub g' g;
sub g' by g.calt;
lookup CALT_1 {
sub g' g g.calt by g.calt;
sub g' g g g g.calt by g.calt;
sub g' g g g g g g.calt by g.calt;
sub g' g g g g g g g g.calt by g.calt;
sub g' g g g g g g g g g g.calt by g.calt;
} CALT_1;
lookup CALT_2 {
sub g' g g.calt by g.calt;
sub g' g g g g.calt by g.calt;
sub g' g g g g g g.calt by g.calt;
sub g' g g g g g g g g.calt by g.calt;
sub g' g g g g g g g g g g.calt by g.calt;
} CALT_2;
lookup CALT_3 {
sub g' g g.calt by g.calt;
sub g' g g g g.calt by g.calt;
sub g' g g g g g g.calt by g.calt;
sub g' g g g g g g g g.calt by g.calt;
sub g' g g g g g g g g g g.calt by g.calt;
} CALT_3;
} calt;

Roger S. Nelsson's picture

wow, that was some routine! Impressive! :)
How do I shorten it a bit? I don't foresee the use of 32 consecutive looped letters, maybe 6-8 tops? ;)
This routine elegantly solves the odd/even "switched" sequences.
The only problem that remains is that this routine switches any standalone g to its alternate form, whereas I want the single occurrences to remain in their basic looped form. Do you know how to fix that as well?

Roger S. Nelsson's picture

On closer inspection ALL sequences are completely switched!
It gives alternate glyphs wherever I want plain, and plain wherever I want alternate ;)

Arno Enslin's picture

Here it is the other way around:

feature calt {
sub g by g.calt;
ignore sub g.calt' g.calt;
sub g.calt' by g;
lookup CALT {
sub g.calt' g.calt g.calt g.calt g.calt g.calt g by g;
sub g.calt' g.calt g.calt g.calt g by g;
sub g.calt' g.calt g by g;
} CALT;
} calt;

Results in

g
g.calt g
g g.calt g
g.calt g g.calt g
g g.calt g g.calt g
g.calt g g.calt g g.calt g
g g.calt g g.calt g g.calt g
g.calt g g.calt g g.calt g g.calt g

g.calt g.calt g g.calt g g.calt g g.calt g
g.calt g.calt g.calt g g.calt g g.calt g g.calt g

But now I am not sure, if you want to have a single letter replaced.

Roger S. Nelsson's picture

I think I got it! :D

This routine will make up to 8 letters in sequence alternate - and always ending with the base letter:

feature calt {
sub g' g by g.calt;
lookup CALT_1 {
sub g.calt' g.calt g by g;
sub g.calt' g.calt g.calt g.calt g by g;
sub g.calt' g.calt g.calt g.calt g.calt g.calt g by g;
} CALT_1;
} calt;

Thanks for the valuable input, Arno! I would never had sussed this in a million years on my own...

Arno Enslin's picture

Yours seems to result in the same, but it is shorter and therefore better.

Arno Enslin's picture

By the way, there is also the command "reversesub", but I think FontLab cannot compile it. The actual AFDKO can and with the help of batch files it easy to handle. And you can use both, the AFDKO and FontLab. The GPOS and the GSUB table of a font can be extracted with TTX and merged into the OTF, that was generated with FontLab. You even can avoid, that TTX changes anything, if you temporarily remove the following files from the FontTools directory (in your Python directory):

C:\Python26\Lib\site-packages\FontTools\fontTools\ttLib\tables\G_P_O_S_.py
C:\Python26\Lib\site-packages\FontTools\fontTools\ttLib\tables\G_S_U_B_.py

and

C:\Python26\Lib\site-packages\FontTools\fontTools\ttLib\tables\G_P_O_S_.pyc
C:\Python26\Lib\site-packages\FontTools\fontTools\ttLib\tables\G_S_U_B_.pyc

Then TTX extracts the tables as hexdata! (TTX is unable to dump the FeatureParams-Tags correctly [TTX empties them], if they are present in the GSUB table.)

The AFDKO and TTX are really cool! And for free.

Stephen Rapp's picture

If you are looking for a way to have any single letter to alternate every other instance of it appearing; that is way more complex.
I think Paul Hunt knows how to do this by setting up classes and alternating back and forth. I'm not sure, but this may only work if all letters have alternates so you substitute one class by another repeatedly. I may be fuzzy on that, but maybe he can tell you more.

Nick Shinn's picture

That's right.
You need to have alternates of every character, including punctuation and space, for the pseudo-random effect.
I've done it in a couple of typefaces.
In Duffy Script, I made three alternates of every character, but for Fontesque Pro, only one alternate, because it's very rare that you get three of the same character in a row, in any language. (That's the subject of another thread!)

Syndicate content Syndicate content