Prioritizing certain ligatures

I have discretionary ligature glyphs for A_T, T_T, T_L, and L_E. Right now I can type out the word RATTLE, and the A_T and L_E ligatures both work fine (as in the screenshot).

But I would actually like to “prioritize” the T_T ligature, meaning that the glyphs would be R, A, T_T, and L_E.

I 'm not exactly sure how prioritize the T_T, or what I documentation I should be looking for, or even if I’m thinking about this the right way. I’m comfortable writing Python, so if that’s the way to do this, just let me know. Any help would be great!

In the dlig feature, place the sub T T by T_T; line first to prioritize it.

1 Like

The safest way would be to put the prioritized ligature in a separate lookup:

lookup DLIG1 {
  sub T T by T_T;
} DLIG1;

lookup DLIG2 {
  sub A T by A_T;
  sub L E by L_E;
  sub T L by T_L;
} DLIG2;
4 Likes

This worked great, thanks!

1 Like

I have a similar issue but with liga.
I have the ligatures “greater_hyphen” and “hyphen_hyphen_hyphen,” and I want to prioritize “hyphen_hyphen_hyphen” over “greater_hyphen.”
What should I do? I´m completely lost on this topic

Put the longer substitution above.

It’s not working :upside_down_face: :melting_face:

Can you show your feature code?

Sure! I placed both hyphen_hyphen_hyphen and hyphen_hyphen above but it’s not working :frowning:

Can you also show the code for the greater_hyphen?

Is it also part of the liga feature, and if so, where in relation to the hyphen_hyphen_hyphen?

You can also post the full feature code here on the forum as text or as a private message.

When you have a text >--- and those ligature rules

sub hyphen hyphen hyphen by hyphen_hyphen_hyphen;
sub greater hyphen by greater_hyphen;

the greater_hyphen ligature will always take precedence because it matches “earlier” in the input string. To avoid that, you need to use separate lookups, or use ignore sub rules:

lookup TRIPLE_HYPHEN {
  sub hyphen hyphen hyphen by hyphen_hyphen_hyphen;
} TRIPLE_HYPHEN;

lookup OTHER {
  sub greater hyphen by greater_hyphen;
  # ...
} OTHER;

or

sub hyphen hyphen hyphen by hyphen_hyphen_hyphen;
ignore sub greater' hyphen' hyphen hyphen;
sub greater hyphen by greater_hyphen;

Another caveat is that the substitutions are not necessarily stored in the font in the same order as in the Glyphs file. To combine the two examples above, if this is the glyph order:

Bildschirmfoto 2023-11-15 um 09.15.28

and this is the feature code, trying to order the substitutions to achieve the desired result:

sub hyphen hyphen hyphen by hyphen_hyphen_hyphen;
sub greater hyphen by greater_hyphen;

sub T T by T_T;
sub A T by A_T;
sub L E by L_E;
sub T L by T_L;

… when we inspect the font with OTMaster, the actual order of substitutions is sorted by target glyph and glyph order:

The only way to reliably influence the order and precedence is by putting the critical substitutions into separate lookups.

Using ignore rules would also work, but might get complicated when there are a lot of overlapping input sequences.

The OpenType spec does not require such sorting, it even explicitly states:

The order in the Ligature offset array defines the preference for using the ligatures. For example, if the “ffl” ligature is preferable to the “ff” ligature, then the Ligature array would list the offset to the “ffl” Ligature table before the offset to the “ff” Ligature table.

I think the sorting is required be FEA spec, though:

A contiguous set of ligature rules does not need to be ordered in any particular way by the font editor; the implementation software must do the appropriate sorting. So:

sub f f     by f_f;
sub f i     by f_i;
sub f f i   by f_f_i;
sub o f f i by o_f_f_i;

will produce an identical representation in the font as:

sub o f f i by o_f_f_i;
sub f f i   by f_f_i;
sub f f     by f_f;
sub f i     by f_i;

Another case where FEA tries to outsmart the font designer.

There are places where sorting makes sense: in single subs, the order doesn’t matter but ordered GIDs are easier to store. But in this case, it influences the behavior, so it seems that sticking the order in the code makes more sense.

Made worse by the fact that single substitutions are considered a different lookup type and thus split from the rest. See this topic for details:

Yes, different lookup types are an issue, but that is not the one here since they are all ligature substitution. The real issue is that FEA spec want to be “helpful” but without giving a way to disable it.

Anyway I opened Reordering of ligature substitution is considered harmful · Issue #1727 · adobe-type-tools/afdko · GitHub.

Re-single substitution considered a different lookup, I wonder if it is possible to create a ligature substitution with a single component.

I actually think that the automatic reordering is a good thing. Otherwise, rules like

sub f f     by f_f;
sub f i     by f_i;
sub f f i   by f_f_i;
sub o f f i by o_f_f_i;

look sensible and descriptive, but the last two rules would never fire as they would be blocked by the first two rules. Automatic reordering makes every rule count.

The main issue I see is that automatic reordering does not play nice with automatic lookup splitting (as demonstrated in Tool for ordering features.fea ligatures by longest first? - #10 by jkutilek).

I learned to always use explicit lookups in FEA code, that already prevents a number of issues.

I believe this is what happens when putting a single substitution into an explicit lookup containing ligature substitutions.

But there are a million ways someone can write rules that will never be applied because another rule blocks them:

lookup foo {
  sub a' b by c;
  sub a' d by e;
} foo;

The second substitution will never be applied because the OpenType layout engine stops applying rules at the first match. So I don’t see why this particular case is so important to be handled automatically and without any sane way to override it.

Same here, I try to avoid implicit lookups as much as possible because they often mask subtle bugs in the feature code, and the rules for creating them is not always obvious.

BTW & AFAIK, the only tool that doesn’t do any reordering at all is VOLT.

1 Like

I don’t think so.

As the order is so important, it shouldn’t be messed with.