Skip navigation
Currently Being Moderated

Contextual feature code samples

Sep 14, 2005 5:27 AM

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 ;
 
Replies
  • Currently Being Moderated
    Sep 14, 2005 7:37 AM   in reply to Thomas Phinney
    well, i'm a bit of a novice, but i've attempted some similar things. in Cezanne Pro, there's a bit of code that looks something like this:

    feature calt{
    sub @calt1 @calt1' by @calt2;
    } calt;

    where @calt1 = [a a.salt a.salt2 a.salt3]
    and @calt2 = [a.salt a.salt2 a.salt3 a]

    there may be some problems to this logic, but it seems to work. so basically i was able to cycle through any number of alternates using just two classes.

    also, for your oscillating weights, wouldn't this work as well?:

    feature calt{
    sub @light @light' by @semibold;
    sub @light @semibold @light' by @bold;
    sub @semibold @bold @light' by @black;
    sub @bold @black @light' by @bold;
    sub @black @bold @light' by semibold;
    } calt;

    I figure that to substitute properly, you only need to know which direction the oscillation is going, so knowing the two characters preceeding should do the trick.

    now that i've posted these with out testing them. i guess i should try them in application to see if the actually work.
     
    |
    Mark as:
  • Currently Being Moderated
    Oct 6, 2005 9:25 AM   in reply to Thomas Phinney
    aha! i never read closely enough. as always, thanks for sharing your code with us!
     
    |
    Mark as:
  • Currently Being Moderated
    Oct 21, 2005 1:42 PM   in reply to Thomas Phinney
    Thomas,
    Thanks to the link from Typophile! I am going to take a hard look at your code. Also, thanks for the many tips you and Nick Shinn gave us at TypeCon during the .calt session.

    ChrisL
     
    |
    Mark as:

More Like This

  • Retrieving data ...

Bookmarked By (0)