Multiple contextual alternates in sequence, based on proximity?

Jess McCarty
Jess McCarty Posts: 97
edited December 2015 in Technique and Theory
I'm seeking advice for an unusual sequential substitution that I've been toying around with; namely, substituting the second letter in a ligature as well as the following letter, if that following letter meets certain criteria. In this particular script font there are some letter pairings that require a ligature to connect 'realistically' - things like os, etc. What I'm trying to figure out is how I might connect multiple strings of these ligature substitutions without creating a 'hall of mirrors' in the code.

Example: est - where es is a ligature and st is a ligature, and both the s & t are substituted based on proximity to one another.

Obviously I can set up sub e s' by es.lig; sub s t' by st.lig; in a feature, however the second substitution will of course be dropped if the user types est. In my typical contextual alternates feature, I'd simply set up class substitution w/lookups (sub @ligs @lwrcse' by @ligs) but in this case I'm working with an uneven number of extraneous glyphs that don't line up 1:1 with what I'd like to substitute, and it's not really apples→apples anyway. i.e. There are multiple ligatures ending in 's', as one example, so class substitutions, at least in the ways I can think of, won't work.
 
How on earth do I code for this without writing a never-ending stream of subs? There's probably a really simple solution, short of redesigning the whole shebang, but I'm having a bit of a headscratcher moment. Doesn't help that I'm self-taught and always feeling a bit like I don't know what I don't know. I've dug down into the usual resources (TD, Opentype Cookbook, FL manuals/forums, Googlefu) but as far as I can tell, no one has asked a variant of this "special snowflake" question before. Hopefully someone out there has an answer/slap-on-the-back-of-the-head! What's the best practice here? Should I redesign the glyphs instead of attacking this via code? School me, please. :wink: 


Comments

  • Nick Shinn
    Nick Shinn Posts: 2,216
    You have to use the “ignore” command.

    If no one else shows how, I will take a crack at it later.

  • I'm a little unclear. Are you using many-to-one substitutions ( sub f i by f_i ) or sequencing one-to-one substitutions ( sub f i' by i.alt )? Usually the right sequence of stacked substitutions can pull this off. Sometimes times you need to chunk out parts in separate lookups. Usually it requires a few ignore subs. 
  • Kent Lew
    Kent Lew Posts: 944
    I had a similar (I think), but not identical, challenge when working on features for Big Caslon Italic. There were multiple discretionary ligatures (across a couple different stylistic sets) with some triplets and many conflicting “overlaps” — e.g., n_t, t_a, a_s.

    Mostly we had to make decisions about hierarchy/priority and I had to do some careful ordering to properly handle the relevant combinations. It’s basically a logic puzzle.



    The solution in this case required declaring a number of lookups outside of feature blocks in order to more carefully control the lookup ordering.

    And yes, there was a rather long stream of subs.

    I think we’d need to see examples of the sort of things you’re trying to accomplish in order to be able to advise more specifically.
  • Kent Lew
    Kent Lew Posts: 944
    Also, fwiw, and again based on a fuzzy interpretation of what you might be trying to achieve —

    Depending upon how you are drawing your connecting script, you might consider recasting your ligatures as individual component parts, especially if you think you might ever expand language coverage.

    I came to this the hard way, after working on production for Jessica Hische’s Tilda, for which we had to generate a number of accented o_s, o_r, p_r, b_r, etc. ligatures.



    After this experience, I encouraged the designer of an upcoming semi-connecting script to split his ligatures into individually combining parts instead. This was then much easier to automate the composition of accented variants based on the positioning of the defaults — less than an hour, rather than a day or so.

    For example, rather than e_s and s_t ligatures, you might have e.calt_s, s.e_calt, s.calt_t, s.e_calt_t, and t.s_calt glyphs.

    The logic puzzle becomes a bit more straightforward as a result. And it will much more straightforward to manage extended Latin development (to handle the village of Leština or footballer Néstor Álvarez, for example) later, should you so choose.

    I don’t know if this kind of approach will mesh with the way you conceive your script.

    It depends in part upon whether the alternates are purely functional or highly individual & stylistic and whether you can generalize the alternate connections to a manageable handful.
  • Kent Lew said:
    Also, fwiw, and again based on a fuzzy interpretation of what you might be trying to achieve —

    Depending upon how you are drawing your connecting script, you might consider recasting your ligatures as individual component parts, especially if you think you might ever expand language coverage.

    I came to this the hard way....

    ....The logic puzzle becomes a bit more straightforward as a result. And it will much more straightforward to manage extended Latin development (to handle the village of Leština or footballer Néstor Álvarez, for example) later, should you so choose.

    This. Oh, so very much this.

  • Kent Lew said:
    After this experience, I encouraged the designer of an upcoming semi-connecting script to split his ligatures into individually combining parts instead. This was then much easier to automate the composition of accented variants based on the positioning of the defaults — less than an hour, rather than a day or so.

    I remember @John Hudson, I think?, once calling these ‘ligaturings’, as in, the separate parts of a ligature. Something like ligaturelings might make more sense. Ligatidbits. Ligaments.

  • Kent Lew
    Kent Lew Posts: 944
    Ligaments.
    :-D
  • I just use the, very uninspiring, contextual alternates name.
  • Nick Shinn
    Nick Shinn Posts: 2,216
    Hudson’s term is dynamic ligaturing, IIRC.

    Contextual alternates don’t necessarily join into one fixed entity, although some would say ligatures don’t have to, but what makes dynamic ligatures distinctive, visually, is that their shape is mutable, subject to tracking/kerning.
  • Kent Lew
    Kent Lew Posts: 944
    That mutability is the downside to approaching ligatures as “ligaments.” However, that mutability is a vulnerability in general with a connecting script (which I am assuming Jess is talking about here; I could be wrong). So, in that case, the onus is already on the user not to muck it up, anyway.


  • Jess McCarty
    Jess McCarty Posts: 97
    edited December 2015
    Thanks for all the ideas & feedback everyone! Sorry I've been MIA in this thread - we've had a crazy week here on the farm and I'm just now working on the code again.

    @Jackson Cavanaugh Yes, it's a one-to-one substitution, but sometimes in sequence chains. I've played a bit with lookups and am experimenting with the ignore command (a useful first for me) but haven't figured out the logic puzzle just yet.

    @Kent Lew The 'conflicting overlaps' in your BCI example is exactly what's happening in this instance, thanks for explaining that so much more clearly! Your advice/approach for the upcoming semi-connected script makes sense. I think I have the 'ligatures' set up as the individual components you're describing, just not named properly/conventionally. o_s isn't actually a connected o & s, but rather an 's' designed specifically to follow the 'o' when calt is engaged. (It should probably be named o.s_calt.) Right now they're set up like this:


    The magenta/cyan indicate the shape of the individual components I've set up. Is this what you'd meant by extrapolating the ligs into individual components? Also, do you have a generic example of a lookup outside of a feature block? Is it a separate 'lookups block'?

    @Nick Shinn Thanks for the Hudson reference! (And thanks @John Hudson .) God bless archived list-servs, I thought this 'dynamic ligaturing' explanation of his was illuminating and spot-on:

    The mis-analysis looks at a discrete sequence of Arabic letters and says 'This first one takes initial form, and this second one takes medial form, and this third one takes final form', and then says 'These particular medial and final letters take a special ligature shape that differs from their default connection'.

    The better analysis looks at the same sequence and says 'Each of these letters has a particular appropriate form based on its neighbour(s).'

  • Kent Lew
    Kent Lew Posts: 944
    From the image you post, it does indeed look as though you’ve already conceived of things separate “ligaments,” as we’ve been suggesting. So, really you’re talking about various contextual alternates that have both before and after contexts (in technical terms: “backtrack” and “lookahead” sequences) and which connect visually to form ligatures (but aren’t composed as explicitly connected outlines in a single glyph).

    You should name your ligament glyphs differently. There is no one proper way, other than you should not use the kind of ligature-style base names. The base name should be the individual represented letter itself, followed by a dot-suffix. The form of the suffix is really up to you, since it is only of internal value.

    The examples I gave are just one convention that I’ve worked with. Because I work on other people’s fonts (and am not always the only one), it's useful to have a system that clearly indicates the context that a glyph is intended for. But it is a little verbose, and in a more personal workflow, I’d probably simplify that for myself.

    Regardless of the naming, in the specific example you showed I think the rules are pretty straightforward:
        sub o s' by s.o_;
    sub [s s.o_] t' by t.s_;
    But, of course, other combinations could introduce further complexity.