Tool for ordering features.fea ligatures by longest first?

Question: Is there a tool or script to order features.fea so longer ligatures are arranged by the longest substitution first, so they have the correct priority in ordering lookups?

So, instead of:

sub a by X;
sub a b by Y;

it is ordered correctly as:

sub a b by Y;
sub a by X;

thanks!


Edit: Maybe Excel will be the tool :thinking:

Your first example is not incorrect, it is exactly what someone might want in some circumstances. So I don’t think an automated tool could guess where reordering is desired and where it should be left as is.

If you use the automatic feature generation for features such as liga, Glyphs will generate the code in the correct oder since Glyphs knows the intention of the code.

1 Like

FWIW, makeotf is supposed to do this automatically when compiling feature code, but I don’t know if Glyphs 3 does the same.

1 Like

Glyphs is not reordering. And I don’t think that makeOTF did it in Glyphs 2.

Glyphs 2 does the same re-ordering as AFDKO’s makeotf.

1 Like

Glyphs 3 does the same reordering as well, actually.

1 Like

I didn’t know that.

2 Likes

Thanks for all the replies, it works! :smiley:

I did some more testing because this issue was confusing me more than I expected it to :‌) Automatic reordering is supported by Glyphs 2 and Glyphs 3, but it does not apply to the sample code from the first post.

A contiguous set of ligature rules is reordered (in makeOTF, Glyphs 2, and Glyphs 3), but only ligature rules, so once a single substitution rule is present the reordering does not work anymore.

Consider the following liga code:

sub a     by X;
sub a b   by Y;
sub a b c by Z;

This is shaped as follows (input text → displayed glyphs):

a   → X
ab  → Xb
abc → Xbc

The first feature rule (sub a by X) is not a ligature rule. If we remove it, we have the following liga code:

sub a b   by Y;
sub a b c by Z;

This code is shaped as such:

a   → a
ab  → Y
abc → Z

So here the rules are reordered and abc is correctly displayed as Z and not as Yc. This applies to makeOTF/Glyphs 2 and Glyphs 3.


Test files

Test 1: Glyphs 2: T1-G2.glyphs, Glyphs 3: T1-G3.glyphs
Test 2: Glyphs 2: T2-G2.glyphs, Glyphs 3: T2-G3.glyphs
Exported OTF files: Test.zip

1 Like

A lookup can only contain substitutions of one kind, so I guess that

sub a     by X;
sub a b   by Y;
sub a b c by Z;

is internally split into 2 lookups, one with a single substitution type:

sub a     by X;

and one with a ligature substitution type:

sub a b   by Y;
sub a b c by Z;

The rule in the first lookup is evaluated before the second lookup is being processed, so the a never appears in the input glyph sequence for the second lookup.

This may give the impression that the rules are not reordered when in fact they are, per lookup.

2 Likes

A ttx dump of your T1-G3 font shows that there are indeed 2 lookups:

<LookupList>
  <!-- LookupCount=2 -->
  <Lookup index="0">
    <LookupType value="1"/>
    <LookupFlag value="0"/>
    <!-- SubTableCount=1 -->
    <SingleSubst index="0" Format="1">
      <Substitution in="a" out="X"/>
    </SingleSubst>
  </Lookup>
  <Lookup index="1">
    <LookupType value="4"/>
    <LookupFlag value="0"/>
    <!-- SubTableCount=1 -->
    <LigatureSubst index="0" Format="1">
      <LigatureSet glyph="a">
        <Ligature components="b,c" glyph="Z"/>
        <Ligature components="b" glyph="Y"/>
      </LigatureSet>
    </LigatureSubst>
  </Lookup>
</LookupList>
2 Likes

:point_up_2:This.

Thanks, Jens :slight_smile:

The third sub will never be called, with splitting or without. Because in ether case, the first sub will catch all as.

And in some cases, (e.g. if you put that code into an explicit lookup block) it will ‘upgrade the single subs to match the more complex subsections to avoid splitting.