April 16, 2008 9:57 am

Fraction Fever

I’ve never liked the way fraction feature is usually implemented in OpenType fonts. I thought I was the only person until Kent Lew wrote to me with the same frustration. We talked about it a bit and I worked up a possible solution. Here goes…

The Current Situation

The standard fraction feature algorithm works like this:

  1. Convert all numbers in the string to numerators.
  2. Starting at the beginning of the string, convert any numerator following a slash or a denominator to a denominator.
  3. Convert all instances of a slash to a fraction bar.

This works perfectly well from a programming standpoint, but from a user standpoint it is cumbersome. The user is required to select the precise text to which to apply fractions. This is a lot of work in practice. For example, it requires nine (9!) steps for the designer to set fractions in this simple list of ingredients.

1

Select “1/2”

2

Activate the fractions feature.

3

Select “1/4”

4

Activate the fractions feature.

5

Select “7/8”

6

Activate the fractions feature.

7

Select “1/3”

8

Activate the fractions feature.

9

Remove the spaces.

10

That is a lot of work. Now think about doing that in a 150 page cookbook.

A Possible Solution

So, what can be done? I kicked around the idea and came up with this algorithm:

  1. Convert every instance of a slash preceded and followed by a figure to a fraction bar.
  2. For every found fraction bar or numerator, look back one glyph. If the found glyph is a figure, convert it to a numerator. Do this for the 10 glyphs preceding the fraction bar until a non-figure is encountered.
  3. For every glyph following a fraction bar or a denominator, if the glyph is a figure, convert it to a denominator. Do this until a non-figure is encountered.
  4. Convert every space preceded by a figure and followed by a numerator to a thin space.

In code it looks like this:

@figures = [
    zero
    one
    two
    three
    four
    five
    six
    seven
    eight
    nine
];
@numerators = [
    zero.numerator
    one.numerator
    two.numerator
    three.numerator
    four.numerator
    five.numerator
    six.numerator
    seven.numerator
    eight.numerator
    nine.numerator
];
@denominators = [
    zero.denominator
    one.denominator
    two.denominator
    three.denominator
    four.denominator
    five.denominator
    six.denominator
    seven.denominator
    eight.denominator
nine.denominator
];

feature frac {
    sub @figures slash' @figures by fraction;

    lookup Numerator_1 {
        sub @figures'
        fraction by @numerators;
    } Numerator_1;

    lookup Numerator_2 {
        sub @figures'
        @numerators
        fraction by @numerators;
    } Numerator_2;

    lookup Numerator_3 {
        sub @figures'
        @numerators
        @numerators
        fraction by @numerators;
    } Numerator_3;

    lookup Numerator_4 {
        sub @figures'
        @numerators
        @numerators
        @numerators
        fraction by @numerators;
    } Numerator_4;

    lookup Numerator_5 {
        sub @figures'
        @numerators
        @numerators
        @numerators
        @numerators
        fraction by @numerators;
    } Numerator_5;

    lookup Numerator_6 {
        sub @figures'
        @numerators
        @numerators
        @numerators
        @numerators
        @numerators
        fraction by @numerators;
    } Numerator_6;

    lookup Numerator_7 {
        sub @figures'
        @numerators
        @numerators
        @numerators
        @numerators
        @numerators
        @numerators
        fraction by @numerators;
    } Numerator_7;

    lookup Numerator_8 {
        sub @figures'
        @numerators
        @numerators
        @numerators
        @numerators
        @numerators
        @numerators
        @numerators
        fraction by @numerators;
    } Numerator_8;

    lookup Numerator_9 {
        sub @figures'
        @numerators
        @numerators
        @numerators
        @numerators
        @numerators
        @numerators
        @numerators
        @numerators
        fraction by @numerators;
    } Numerator_9;

    lookup Numerator_10 {
        sub @figures'
        @numerators
        @numerators
        @numerators
        @numerators
        @numerators
        @numerators
        @numerators
        @numerators
        @numerators
        fraction by @numerators;
    } Numerator_10;

    lookup Denominator {
        sub [fraction @denominators]
        @figures' by @denominators;
    } Denominator;

    sub @figures space' @numerators by thinspace;
} frac;

How many steps does this require the user to perform? Two (2!). Actually, it could be less than two because they could activate fractions in their style sheet definition. In that case it would require them to perform zero (0!!!) steps to apply the fraction feature. In any case, here are the two steps:

Select all of the text.

1

Activate the fractions feature.

2

Done.

But

There are some drawbacks. This is what Kent and I have come up with so far:

  1. This only works with fractions that have 10 or fewer numerators. In reality, is this an issue? I’m not certain, but it doesn’t seem likely. In any case, this could probably be extended to 20 numerators. I stopped at 10 to avoid the dreaded table overflow issue.
  2. It requires a non-figure to act as an indication of the leading edge of the fraction. In this case I used the space. This won’t work for fractions formatted as “21/2” to represent 2 and one-half. Are fractions ever formatted this way? Maybe, maybe not.
  3. If the user uses a font that implements this type of fraction feature and then switches to a font that implements an older style fraction feature, things will be broken. Any instances of this problem should be very visible to the user and manual intervention will solve it.
  4. The common date format 04/16/08 will be considered a fraction and formatted as such. The user could simply turn off fractions in this text.
  5. It isn’t what users are used to. This could be a legitimate issue, but if a user uses it in the way that they have been using the old fraction feature it will work properly.

That said, there are some clear advantages:

  1. It makes much more sense from a user point of view. We are in the age of the “smart” OpenType font. We now make fonts that automatically insert ligatures and swashes, and users have come to expect this behavior. It seems that fractions should work the same way. I don’t think it would be a good idea for a user to activate fractions at all times, but this should make it much easier for users to apply the fraction feature to lots of text with a broad stroke.
  2. This new algorithm preserves the space separating the integer and the fraction. This makes it far more usable in situations when this text needs to be sent to a feature-less environment, for example searching a PDF. If the user searches for “1 1/4” the appropriate result will be found. Using the old algorithm, plus the space removal mentioned above, the user would have to search for “11/4”. That looks like “eleven fourths” not “one and one fourth.”

I don’t think the potential problems outweigh the benefits for the user, but they should be taken into consideration. I tend to think in terms of “most case scenario” rather than “worst case scenario.” This algorithm isn’t perfect and it will fail 1% of the time. However, I think that 99% of the time it will work infinitely better than what users are used to.

Now What?

I haven’t actually deployed this in a font yet, but I’m going to give it some thought next time I write a fraction feature. Feel free to use this if you want to. If you do, please let me know how it works out.