Skip to content
This repository has been archived by the owner on Mar 7, 2023. It is now read-only.

The "frac" OpenType feature is defined too loosely for easy use on the web #34

Open
jyasskin opened this issue Dec 3, 2020 · 3 comments

Comments

@jyasskin
Copy link
Member

jyasskin commented Dec 3, 2020

Apologies if this is the wrong venue to raise this issue; I was pointed here by w3c/csswg-drafts#5756 (comment).

There are at least 4 ways to implement the "frac" OpenType feature, which is enabled by the CSS font-variant-numeric: diagonal-fractions property:

  1. Define ligatures for common fractions. Ubuntu Mono does this.
  2. Turn all digits into numerator digits unless they immediately follow a /, in which case turn them into denominator digits. Described at https://ilovetypography.com/OpenType/opentype-features.html#:~:text=Substitute%20%E2%80%94%20depending%20on%20the%20context. The Arial and Times New Roman fonts preinstalled by Windows do this.
  3. Turn up to N consecutive digits before a / into numerators, and all consecutive digits afterwards into denominators. Described at http://opentypecookbook.com/common-techniques/#method-2-contextual. Gimlet does this.
  4. [I haven't seen this technique in a font or tested it, so it may not work.] Use reverse chaining to turn all consecutive digits before a / into numerators and forward chaining as above to turn all consecutive following digits into denominators.

None of these are clearly ruled out by the frac feature definition.

In a design tool, an author is probably working with just one font, so they can adapt which spans they mark as frac to work with the single font they've selected. On the web, however, CSS is designed to let the choice of font fall back through a sequence of options, usually ending with a user-configurable generic font family. That means the author can't be sure which implementation technique will be in use for the span they mark up with font-variant-numeric: diagonal-fractions. Therefore, the definition of the font-variant-numeric: diagonal-fractions property, either in CSS or via the definition for frac in OpenType, needs to constrain font behavior enough that the CSS spec can tell authors when to use it.

Here are several possible markup patterns we might say are good or bad, with <frac>...</frac> marking the region with the feature turned on:

  • <frac>In 1973 I bought 1 1/2 cup of sugar</frac> — breaks with implementation (2), rendering "1973" and "1" as numerators.
  • In 1973 I bought <frac>1 1/2</frac> cup of sugar — breaks with implementation (2), rendering "1" as a numerator. Implementations (3) and (4) can pull the "1" closer to the fraction if they choose.
  • In 1973 I bought 1 <frac>1/2</frac> cup of sugar — works everywhere, but if fonts using (3) and (4) want to reduce the space, I think they'd need to do it unconditionally for number space numerator instead of just under the frac feature.
  • π is approximately <frac>22/7</frac> — breaks with implementation (1) by rendering as 2 2/7.
  • <frac>Today is 3/12.</frac> — breaks with all implementations. This is recoverable in HTML by marking it up as <frac>Today is <time>3/12</time>.</frac> with appropriate style on <time>.
  • <frac>Today is 3/12/2020.</frac> — (3) and (4) can special-case this as described in http://opentypecookbook.com/common-techniques/#method-2-contextual.

I'd appreciate if this group would think through what markup patterns y'all would recommend and what extra definition is needed in the frac feature to make them work reliably.

There may be other OpenType features with the same problem (e.g. afrc), but I haven't investigated them.

@tiroj
Copy link

tiroj commented Dec 3, 2020

My take on this is that the span of the frac feature needs to be strictly limited to the fraction itself, and avoid encompassing any characters before or after. I have seen some pretty clever efforts to implement larger contextual analysis to try to identify the fraction within a larger string, but I would argue that not only is this prone to error but that OpenType Layout glyph processing contextual lookup types are simply not designed to perform that kind of operation.

If the frac feature is to be applied based on text analysis, rather than discrete, user-defined spans, then that has to be performed at the character level, not within glyph processing lookups. This means a shaping engine using an algorithm to identify a fraction and then applying the frac feature to that run (similar to the way such engines apply features for complex scripts to specific glyph sequences based on character level analysis). The HarfBuzz engine already does this, as I understand, when the Unicode fraction slash U+2044 is used between a sequence of numeral characters e.g. 12345⁄67890.

So either spans should be defined in CSS strictly to the limits of the fraction string, or U+2044 should be used.

@svgeesus
Copy link
Contributor

svgeesus commented Dec 7, 2020

I agree that the OpenType feature definition is underspecified. It does state that the feature should be off by default, and that lookups are performed for the span of characters over which this feature has been applied by the user which implies a tightly scoped span. It fails to state handling of some commonly expected characters, in particular thousands separator and space, which might reasonably be expected in a fraction.

  • <frac>In 1973 I bought 1 1/2 cup of sugar</frac>

I would argue this is poor markup (but a good test) because "I bought" and so on are not consistent with fractions and the user should not have selected them. This is why the feature is off by default.

  • In 1973 I bought <frac>1 1/2</frac> cup of sugar

This is unclear from the feature definition. I would however argue that this compound fraction case should be covered and should be considered good practice - the markup here could easily be <span class="quantity"> for example. Indeed the CSS Fonts 4 definition of the font-variant-numeric property uses the compound fraction string "2 1/3" as an example for both the diagonal-fractions (frac) and stacked-fractions (afrc) features.

This type of example, with an integer followed by a space and a fraction inside the span, is correctly handled by, for example, Gimlet by David Jonathan Ross (as @jyasskin pointed out in his original issue on CSS Fonts 4. I asked David about this an his reply was:

As far as the fractions go, I believe that Gimlet more-or-less follows Tal Leming’s approach to fractions, detailed at his OpenType Cookbook. It’s not a perfect system, but it handles mixed fractions nicely, even if I can’t tell the difference between fractions and dates (12/2). You can see more information on his old website and on the Glyphs app page about fractions, if helpful.

  • In 1973 I bought 1 <frac>1/2</frac> cup of sugar — works everywhere, but if fonts using (3) and (4) want to reduce the space, I think they'd need to do it unconditionally for number space numerator instead of just under the frac feature.

"1/2" works on every font which has a glyph for Unicode VULGAR FRACTION ONE HALF (½) and can do the glyph substitution, but there are a very limited number of these Unicode points and "7/9" for example is less likely to work everywhere.

And yes, using a thinner space glyph to pull the parts of the compound fraction together is desirable. Which in turn implies that the user selected span should be wider, to enclose the entire compound fraction.

  • π is approximately <frac>22/7</frac> — breaks with implementation (1) by rendering as 2 2/7.

This should be correctly handled per the OpenType frac definition, and thus any font which merely substitutes with glyphs for common fractions is a poor implementation.

  • <frac>Today is 3/12.</frac> — breaks with all implementations. This is recoverable in HTML by marking it up as <frac>Today is <time>3/12</time>.</frac> with appropriate style on <time>.

This should be considered poor markup, because something which is not a fraction has been selected for processing as a fraction. This is why the feature is off by default. Tal mentions this case:

some problems with dates, German tax numbers and things like that

but I would argue that this falls into the category of superbly crafted fonts which even try to recover from mistakes made by the user.

This is also poor markup for the same reasons as the previous example.

Here is another case

  • <frac>2,768/7,124</frac> or, depending on what is considered the thousands separator in the current language and locale, the same string with a period or with ARABIC THOUSANDS SEPARATOR (U+066C). This is clearly an integer numerator and denominator, separated with a slash; the integers are just larger than is common and so could contain thousands (or millions, etc) separators.

Concretely, I suggest that the definition of this feature should

  1. explicitly mention compound fractions, where the integer part and the simple fractional part are separated by a space-class character
  2. explicitly mention Unicode FRACTION SLASH (U+2044) in addition to SOLIDUS
  3. explicitly mention the class of thousands separators

@jbowtie
Copy link

jbowtie commented Dec 5, 2021

The only time I've ever wanted to use the 'frac' or 'afrc' feature is when adding a recipe to the web. And I generally want to turn it on at the level of a <ul> element, for the ingredients list. In fact I've been researching the feature for exactly this use case, where I have been unable to provide a good authoring experience.

What I want is output that looks something like:

<ul style="font-variant-numeric: diagonal-fractions;">
  <li>100g sugar</li>
  <li>2 1/2 tablespoons butter</li>
</ul>

Unfortunately, this fails miserably and becomes unusable with many fonts (which use method 2). Cases 3 and 4 are clearly preferable in this use case, which I suspect is the most common use case in the English language web where this feature is likely to be used.

In my limited experience, what happens is this:

  • Authors try to turn on the feature at the block level.
  • They get the behaviour of case 1 or case 2.
  • They might try 2 or 3 other fonts, thinking they got a badly-behaved font.
  • They decide the feature is badly implemented and never touch it again.

What they do not try is explicitly wrapping every fraction in every ingredient, and most would rather rewrite it as a decimal than try and figure out the markup.

Is it worth taking this particular use case into account? Is my experience typical, or is it some outlier?

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

5 participants