Implementing some features in a way that swaps glyphs in both directions?

Let's say you have a font that has a set of Cyrillic alternates in the “round upright cursive with extenders” aka “Bulgarian” style, and the default Cyrillic glyphs are in the “square” aka “Russian” style.  

You could implement a substitution lookup that replaces the square with the round style in the “locl” (Localized Forms) feature for the “cyrl BGR” (Bulgarian) languagesystem. Apps that support it would automatically do the replacement if the user tags the text as being set in the Bulgarian language (also for spellcheck and hyphenation purposes). 

In addition, you could implement the replacement in a stylistic set (e.g. “ss04”) for use in apps that don't support localized forms. 

But imagine that a user who users an app that supports localized forms tags the text as Bulgarian for spellcheck and hyphenation purposes, but they would prefer (for whatever reasons) to set it in the square (“Russian”) typographic style. It's not obvious how they could achieve that, because the app might not offer any UI to deactivate “locl”. 

I think the solution should be that the same “ss04” feature works both ways: 

One and the same lookup replaces the alternate forms with the default ones and the default ones with the alternates.

So if the input string had the default forms, with “ss04” applied, you get the alternates, but if it had the alternates (as a result of the previously automatically applied "locl"), you get the defaults. So effectively, you get a “swap”. There is no risk of any circular replacements if the swapping is done inside the same lookup. 

This could be of course also done in two different stylistic sets, but if your font has many stylistic sets, this might be unreasonable. So it is a question of UX. 

This technique could be used for any replacements that are performed automatically which the user might want to “undo”. What do you think? 


Comments

  • Adam Twardoch
    Adam Twardoch Posts: 515
    edited April 2018
    This might be relevant especially in cases where a certain regional preference or localized form is not supported by “virtually all” users but instead there is some “reformed” vs. “traditional” style, or where a reform was relatively recent and there may be good editorial reasons to use some earlier typographic practice, for example to preserve historical authenticity of texts written pre-reform. 

    This pertains to the recently released Minion 3. The reformed round Bulgarian style appeared in the 1960s and only recently gained popularity.

    Say that a publisher wants to set a book written in Bulgarian in the 1940s, or maybe issue a re-edition of a book that was published earlier and was set in older Minion  

    Then the publisher may wish to use InDesign’s “Bulgarian” language setting to get proper hyphenation, but may wish to preserve the “Russian style” of the Cyrillic, which may have been previously used in an earlier edition of the text. 

    If the font implements the round style using both “locl” and “ss04” (does Minion 3 do that?), then achieving it might be tricky (or is there a way to switch from Bulgarian to default already?).
  • Also, this way, it doesn’t matter which Cyrillic style you put as default and which is activated via automatic localized forms. E.g. Botio Nikoltchev has done some fonts where the round style is default and the square is secondary. 

    With the method I’m describing, the stylistic set always means “switch to the other style”, regardless of which one is automatically on.
  • Ps. Of course for Bulgarian Cyrillic forms, an appropriate way to switch them over to the square forms would also be the “hist” (historical forms) feature, but that's another matter entirely.
  • John Hudson
    John Hudson Posts: 3,229
    So long as the substitutions are all in the same lookup, I think this should work. If it doesn't work somewhere, then its a good test for inconsistent OTL implementation.
  • Adam Twardoch
    Adam Twardoch Posts: 515
    edited April 2018

    More generally, the reason to implement the Cyrillic replacement in both “locl” and “ssXX“ features is not just that the stylistic set needs to a fallback choice for apps that don't support localized forms. 

    The round Cyrillic forms may be the increasingly preferred localized forms for the Bulgarian language today, while many other Cyrillic language communities currently prefer the square forms — but it doesn't mean that this will always be that way.

    The Bulgarian designers from the 1960s proposed this new style as a reformed Cyrillic, not just reformed Bulgarian Cyrillic. 

    This has gained traction just in Bulgaria so far, but who knows — perhaps some communities will also like this style for another language. So offering this as a stylistic set makes sense as well, especially since fonts tend to be quite long-lived. 

  • Kent Lew
    Kent Lew Posts: 944
    Rather than just flag each of your posts as “insightful,” I’ll just come out and say it. ;-)

    Interesting line of thinking, and I think it has a lot of merit. Particularly in situations where there are fluid and evolving norms.
  • So long as the substitutions are all in the same lookup, I think this should work. If it doesn't work somewhere, then its a good test for inconsistent OTL implementation.
    To make the test even more interesting, put the substitutions within different subtables of a lookup table: once one subtable has been applied, processing of that lookup table should end.

    Of course, if the substitutions are in two separate lookup tables, then it will work for only half the scenarios you care about.
  • Well, of course, I'm stupid. If I only want to let the user counteract the effects of locl by using ss04, I could implement the alternates->defaults in a lookup which is only referenced in the cyrl BGR languagesystem for ss04, while for all the other languagesystems, ss04 works have the lookup that goes defaults->alternates. Should work, shouldn't it? 
  • Kent Lew
    Kent Lew Posts: 944
    Specifying the corresponding languagesystem for the “turn-off-locl” lookup in the sset would certainly reduce the possibility of buggy interpretation like John and Peter are pondering.
  • Alex Kaczun
    Alex Kaczun Posts: 163
    Adam—This is very interesting and useful OpenType functionality you bring up. Can you please provide all scripting examples on how to implement this in FontLab. Thank you.
  • Alex, 

    will do. Very briefly: 

    My friend and colleague Botio Nikoltchev has created this guide for implementing Bulgarian "locl" alternates: 

    https://www.lettersoup.de/what-shall-be-done-for-bulgarian-cyrillic-loclbgr/

    To extend this, instead of the current recommended implementation down there as: 

    feature ss04{

     sub uni0414 by uni0414.loclBGR;

     sub uni041B by uni041B.loclBGR;

     sub uni0424 by uni0424.loclBGR;

    ...

    }ss04;


    you would do this: 

    feature ss04{

     sub uni0414 by uni0414.loclBGR;

     sub uni0414.loclBGR by uni0414;

     sub uni041B by uni041B.loclBGR;

     sub uni041B.loclBGR by uni041B;

     sub uni0424 by uni0424.loclBGR;

     sub uni0424.loclBGR by uni0424;

    ...

    }ss04;


    Of course it doesn't have to be "ss04" but can be any ssXX feature. 

    The principle is that in one and the same lookup, you have substitutions that go both ways. If you put your code right between the "feature XXXX{ ... }XXXX" keywords as shown above, without using the keyword "lookup", all the substitutions will indeed be in the same lookup, which is what you want. 

    In short, the code in "locl" goes just one way, and the code in "ssXX" goes both ways so you have twice as many substitution lines. That's the easiest way to make sure it works. 

    I'll work on some more info about this and will help extend Botio's article on his Lettersoup website. 

    Adam


  • Adam Twardoch
    Adam Twardoch Posts: 515
    edited April 2018
    A different way to implement this would be using classes. 

    # In the FEA code in the Features panel, the prefix section (in FLS5, bottom-right section, in FLVI top section), define all languagesystems that your font will support, e.g.:  


    languagesystem DFLT dflt;

    languagesystem latn dflt;

    languagesystem cyrl dflt;

    languagesystem cyrl BGR;


    # Define your "loclBGR1" OpenType class in the classes panel — or in the prefix section of the FEA code using the "loclBGR1 = [glyphname glyphname...];" syntax. 

    # That class should list all the default ("square/Russian") glyphs, i.e. the glyphs that have Unicode codepoints assigned. 

    # Define your loclBGR2 OpenType class in the classes panel — or in the prefix section of the FEA code using the "loclBGR2 = [glyphname glyphname...];" syntax. 

    # That class should list all the alternate ("round/Bulgarian") glyphs, and should have the same number of glyphs as loclBGR1, and the order of the glyphs in the class definitions should correspond. 


    # Add the feature definition for locl (in this example only for Bulgarian but your font may have other localized forms for other languages also from other scripts): 


    feature locl {

     script cyrl;

     language BGR;

     sub @loclBGR1 by @loclBGR2;

     } locl;


    # Add the feature definition for the stylistic set (here, ss04 is used as an example). Note that this feature definition makes the substitutions go both ways, which is what we're talking about here. 


    feature ss04 {

     sub @loclBGR1 by @loclBGR2;

     sub @loclBGR2 by @loclBGR1; # This very line is my "new idea", everything else has been done previously by font developers.

     } ss04;


    # I believe that this should work fine. I'll need to double-check if the two sub-class-by-class statements will correctly expand to just a list of simple substitutions in the same lookup, but I think they will. 


  • Kent Lew
    Kent Lew Posts: 944
    Or, following your second thought about this:
    feature ss04 {<br>    sub @loclBGR1 by @loclBGR2;<br>    script cyrl;<br>    language BGR exclude_dflt;<br>        sub @loclBGR2 by @loclBGR1;<br>} ss04;<br>
    (Did I get that right?)
  • @Adam Twardoch
    feature ss04 {
    
      sub uni0414 by uni0414.loclBGR;
      sub uni0414.loclBGR by uni0414;
    
    } ss04
    Just checked and it really work, although technically it looks like a substitution there and then back.. I don't quite understand why the second line doesn't override the first line?
  • Simon Cozens
    Simon Cozens Posts: 752
    Rules which are part of the same lookup are executed "all at the same time". Please have a look at Lookup application in my font engineering book for how it works.
  • John Savard
    John Savard Posts: 1,135
    I'm a bit confused here. Obviously, fonts don't do spell checking or hyphenation. So in any application where this kind of concern exists, specifying the language tag to use with the font, and the languge to be used for processing the text, should be two separate things, even if, for convenience, one normally can specify the language once, and have both of them set correspondingly.
    The problem seems to arise from an error in the design of the application, where its capabilities of hyphenation and spell checking are incorrectly piggybacking on the font specification.
  • John Hudson
    John Hudson Posts: 3,229
    The problem seems to arise from an error in the design of the application, where its capabilities of hyphenation and spell checking are incorrectly piggybacking on the font specification.
    Other way round. In a lot of software, there is no way to independently specify OTL langsys settings, so these piggyback on document language settings—when they are applied at all—as used by spellcheckers etc., even though OTL langsys was not designed to be equivalent to typical language properties.

  • @Simon Cozens
    Thank you for the link! I found the whole article is more understandable than an official documentation.
  • RichardW
    RichardW Posts: 100
    Rules which are part of the same lookup are executed "all at the same time". Please have a look at Lookup application in my font engineering book for how it works.

    One thing I didn't see in your book is the statement of the convention that a sequence of rules not explicitly written within a lookup is implicitly grouped into a single lookup.
  • Michael Rafailyk
    Michael Rafailyk Posts: 146
    edited July 2022
  • RichardW
    RichardW Posts: 100
    OK, so it's just someone in a hurry who might have a problem.  There's a lot on that page.
  • Simon Cozens
    Simon Cozens Posts: 752
    Yeah, if you skim read a deeply technical book out of order, you might miss stuff.
  • Thomas Phinney
    Thomas Phinney Posts: 2,899
    Good grief. It’s a single sentence set off as its own paragraph, AND in boldface! How much more emphasis could you reasonably want?
  • Peter Baker
    Peter Baker Posts: 190
    I do a thing like this in my fonts for medievalists (Junicode and Elstob), where locl gives you a different shape of þ and ð depending on whether the language is English, but you can reverse the choice with ss01. But this is a far more elegant implementation, and I hope you won't mind if I borrow it.
  • Michael Rafailyk
    Michael Rafailyk Posts: 146
    edited July 2022
    The related question: What the name should be given to such a two-way Stylistic Set so that it is not misleading for the end user? Those who use Bulgarian by default should understand that the set will switch Cyrillic to Russian too, from its name.

    Instead of "Bulgarian Cyrillic" the Set could be named like a "Bulgarian/Russian Cyrillic". Not sure about the slash in a featureNames, but what do you think?
  • The set could also be named “Square-Round Cyrillic”. I tend to think of these two styles of Cyrillic like of blackletter vs. roman or katakana vs. hiragana. The so-called Bulgarian Cyrillic is not necessarily a »local-only« variant, but simply a style, such happens to be preferred in Bulgaria as the default style.