5 Replies Latest reply: Oct 21, 2005 1:42 PM by (Chris_Lozos) RSS

    Contextual feature code samples

    Thomas Phinney Community Member
      Here are some rather unusual examples of contextual features used to achieve some special effects. I hope they inspire interesting and creative uses.

      Here are three examples of interesting effects one can achieve by using contextual alternates in OpenType. I presented and discussed the code for these at the Type Tech Forum at the ATypI Helsinki conference, Sep 14 2005. I will be showing the effects without going into detail on the code at the St Bride "Temporary Type" conference, Oct 10-12 2005.

      * * * * * *

      I came up with this code trying to help make a pseodo-random font like Beowolf work in OpenType, without using the "random" ('rand') feature.

      This example is for a case where you have eight different glyph forms for each character. While typing, the first character is shown using the first form ("default"), and each character after will use the next ascending glyph set, until you run out, when it wraps back to the beginning of the cycle.

      Repeatedly calling a single lookup allows us to keep on jumping ahead another eight characters for each lookup, making for more compact code (both written and compiled). Some assumption needs to be made about maximum line length to decide just how many times to do this.

      feature calt { # Connection or other contextual Forms
      # Latin
      lookup rotate {
      sub @default @default' by @alt1;
      sub @calt1 @default' by @alt2;
      sub @calt2 @default' by @calt3;
      sub @calt3 @default' by @calt4;
      sub @calt4 @default' by @calt5;
      sub @calt5 @default' by @calt6;
      sub @calt6 @default' by @calt7;
      } rotate;
      lookup rotate;
      lookup rotate;
      lookup rotate;
      lookup rotate;
      lookup rotate;
      lookup rotate;
      lookup rotate;
      lookup rotate;
      lookup rotate;
      lookup rotate;
      lookup rotate;
      lookup rotate;
      lookup rotate;
      } calt;

      * * * * * * *

      This second example is similar to the first one in some ways, but I had some additional objectives that made it harder. First, I wanted to do a font where the weight of the glyphs cycles from light to black *and back*, oscillating continuously. So in terms of glyphs, it was just a full set of glyphs for each of the weights in the cycle. Lots of glyphs, but conceptually easy.

      The oscillation was a problem, because in the desired results, sometimes semibold is followed by bold, and sometimes it is followed by light, depending on whether one is on the upswing or the downswing of the cycle. I thought of two ways of solving that problem.

      One solution is two have two sets of glyphs for each of the intermediate weights. One set of semibold would be used going up in weight, and the other semibold set going down. As far as the substitutions are concerned, it starts to look like the problem above, and the substitution code is elegant, you just have even more glyphs.

      However, my second special objective was that I wanted the font to be flashy to use. In the code shown above, glyphs cycle when you add glyphs *in front* of them. But that isn't something that happens so much in normal usage. I wanted the glyphs to cycle as you type. That meant a context that stretches way out in front of the glyph being changed.

      I welcome a more elegant solution here, but between the desire to cycle and the desire to have the glyphs visually changing as you add more, I went for a pure brute force approach. This code is best read from the bottom up!

      feature calt { # Connection Forms
      # Latin
      sub @LIGHT' @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT

      @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT

      @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT by @BLACK;
      sub @LIGHT' @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT

      @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT

      @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT by @BOLD;
      sub @LIGHT' @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT

      @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT

      @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT by @SEMIBOLD;
      sub @LIGHT' @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT

      @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT

      @LIGHT @LIGHT @LIGHT @LIGHT by @LIGHT;
      sub @LIGHT' @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT

      @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT

      @LIGHT @LIGHT @LIGHT by @SEMIBOLD;
      sub @LIGHT' @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT

      @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT

      @LIGHT @LIGHT by @BOLD;
      sub @LIGHT' @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT

      @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT

      @LIGHT by @BLACK;
      sub @LIGHT' @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT

      @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT by

      @BOLD;
      sub @LIGHT' @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT

      @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT by

      @SEMIBOLD;
      sub @LIGHT' @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT

      @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT by @LIGHT;
      sub @LIGHT' @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT

      @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT by @SEMIBOLD;
      sub @LIGHT' @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT

      @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT by @BOLD;
      sub @LIGHT' @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT

      @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT by @BLACK;
      sub @LIGHT' @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT

      @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT by @BOLD;
      sub @LIGHT' @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT

      @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT by @SEMIBOLD;
      sub @LIGHT' @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT

      @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT by @LIGHT;
      sub @LIGHT' @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT

      @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT by @SEMIBOLD;
      sub @LIGHT' @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT

      @LIGHT @LIGHT @LIGHT @LIGHT by @BOLD;
      sub @LIGHT' @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT

      @LIGHT @LIGHT @LIGHT by @BLACK;
      sub @LIGHT' @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT

      @LIGHT @LIGHT by @BOLD;
      sub @LIGHT' @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT

      @LIGHT by @SEMIBOLD;
      sub @LIGHT' @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT by

      @LIGHT;
      sub @LIGHT' @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT by

      @SEMIBOLD;
      sub @LIGHT' @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT by @BOLD;
      sub @LIGHT' @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT by @BLACK;
      sub @LIGHT' @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT by @BOLD;
      sub @LIGHT' @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT by @SEMIBOLD;
      sub @LIGHT' @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT by @LIGHT;
      sub @LIGHT' @LIGHT @LIGHT @LIGHT @LIGHT @LIGHT by @SEMIBOLD;
      sub @LIGHT' @LIGHT @LIGHT @LIGHT @LIGHT by @BOLD;
      sub @LIGHT' @LIGHT @LIGHT @LIGHT by @BLACK;
      sub @LIGHT' @LIGHT @LIGHT by @BOLD;
      sub @LIGHT' @LIGHT by @SEMIBOLD;
      # sub @LIGHT' by @LIGHT; you don't actually need this line or others like it, since the glyph

      being substituted is already light. But it makes the code more readable to see it here, I think.
      } calt;

      * * * * * * *

      This last example was inspired by a talk given by Amy Papaelias at TypeCon in New York City in July, 2005. She achived the exact same effect I do here, but she used ligatures rather than contextual alternates. I claim no superiority for a contextual approach to the problem, it just seemed an interesting application of the technology.

      WARNING: This code uses (and changes) bad language. The goal is to take nasty words and censor them, either in comic book fashion with punctuation and symbols, or by changing to a different word. "F--- off" becomes "go away," "hell" becomes "heck," etc.

      feature calt {

      lookup calt1 {
      sub f u c' k by asterisk ;
      } calt1;

      sub f' u' asterisk k space y o u by g ;
      sub g asterisk' k space y o u by o ;
      sub g o k' space y o u by space ;
      sub g o space space' y o u by a ;
      sub g o space a y' o u by w ;
      sub g o space a w o' u by a ;
      sub g o space a w a u' by y ;

      sub f' u' asterisk k space o f f by g ;
      sub g asterisk' k space o f f by o ;
      sub g o k' space o f f by space ;
      sub g o space space' o f f by a ;
      sub g o space a o' f f by w ;
      sub g o space a w f' f by a ;
      sub g o space a w a f' by y ;

      sub h e l' l by c ;
      sub h e c l' by k ;

      sub b i' t c h by asterisk ;
      sub b asterisk t' c h by asterisk ;

      sub a' s s by at ;
      sub at s' s by asterisk ;
      sub at asterisk s' by dollar ;

      sub b a' s t a r d by at ;
      sub b at s' t a r d by dollar ;
      sub b at dollar t' a r d by asterisk ;
      sub b at dollar asterisk a' r d by at ;
      sub b at dollar asterisk at r' d by asterisk ;
      } calt ;