Inter: Premature wrap inside flex with font-features in Safari

Created on 28 Sep 2020  Â·  6Comments  Â·  Source: rsms/inter

Describe the bug
Hi folks! 👋 Apparently, any font-feature-settings is making strings of a particular length that are placed inside a flex environment to be prematurely wrapped in Safari.

To Reproduce
Steps to reproduce the behavior:

  1. Open this codepen in Safari
  2. Check how the first two strings are being wrapped
  3. Check how the same two strings, but with an extra . at the end, are not wrapped
  4. Check how the original strings without the font features are not wrapped

Expected behavior
All the examples should not wrap, with or without any font-features-settings.

Screenshots
image

Environment

technical

Most helpful comment

The reason this exists in WebKit is we use floating point math, which is famously non-commutative, to perform inline text layout.

Flex-item elements like these are "shrink wrapped," which are laid out by the browser in two passes: The first pass calculates how much space its content requires and reserves that much space for the element, and the second pass places all the text within the reserved space. The first pass can take some shortcuts because it doesn't have to place all the text exactly; it just needs to determine how much space is needed in total. These shortcuts are mathematically sound, but rely on the commutative property of addition. However, floating point addition is _not_ commutative, resulting in tiny ULP-sized size differences between the first and second pass's measurements.

For this specific example, the first pass measures that the text requires 104.5625 pixels of space, but when the second pass places all the text within that space, it discovers that the text is actually 104.562508 pixels wide (0.000008 pixels wider than the available space). Because the text apparently ends up requiring more space than is available, we trigger line wrapping.

You're right that this should be considered a browser bug. I've filed https://bugs.webkit.org/show_bug.cgi?id=217136 so we can track it.

It should be possible to work around this bug even before a browser fix gets shipped to customers. For this specific example, just jostling the letters around slightly (e.g. letter-spacing: 0.000001px) fixes it, but doing this wouldn't be a robust solution. A more robust solution would be using font sizes (in px) which divide more evenly into the font's unitsPerEm, so the advance widths of the characters (in px) can fit better into floating point values. Using these "rounder" float values should help with the non-commutative property of floating point math.

All 6 comments

I've been working with with Thiago one this issue and wanted to add some additional notes.

  • Also tested in Safari 14 and Safari Technology Preview, I get the same issue there.
  • All other browsers seem to work fine, this only impacts Safari.
  • The total length of the string doesn't seem to matter. The strings which break feel almost random.
  • Changing the font size may fix one string, but causes different strings to break.
  • Enabling alternate settings will trigger the bug for a given string, even when the string that contains no characters affected by the alternate settings. e.g. "tnum" will cause the bug on a string containing no numbers.
  • Have not been able to reproduce a similar bug using other variable or fixed fonts.
  • The bug still occurs using the non-variable version of Inter.

I would typically bucket this is a Webkit bug, but the fact that we can only reproduce it using Inter is leading me to believe that there may be something quirky about the font metrics in Inter when enabling alternates and we wanted to bring it to your attention.

This is fascinating!

Wild slightly educated guess from working on text layout code: When Safari computes the bounding box for the text (so it can know how long it ideally should be, for flexbox) it does so separately from its word-wrapping mechanics. A rounding error could lead to the word-wrapping code moving "Davis" to a second line.

Here's the most minimal repro I can find: https://codepen.io/rsms/pen/abNrXgq?editors=1100

what text causes wrapping?

I tried to find what text causes this wrapping:

Screen Shot 2020-09-30 at 09 07 53

Interestingly it varies with characters.

display

Reproduces with:

  • display:flex
  • display:inline-block

Does not reproduce with:

  • display:block
  • display:inline

font-feature-settings

Next I tried different variations of font-feature-settings which seems to be at the center stage of this bug. It turns out that the wrapping bug is triggered no matter what value I give this property! This is a good clue — it may be Safari applying different features or configuration when measuring text.

If I instead of enabling an optional feature like ss01 enable kerning, which AFAIK in Safari can't be disabled so this should be a no-op (font-feature-settings: "kern") the bug is triggered still! Reinforces my theory about this being a rounding error of some sort.

wght ranges

Another thing I tried was to alter the weight of the variable font to find the range in which this occurs (at the specific font size 14px at 100% browser zoom) and found these ranges to be affected:

Screen Shot 2020-09-30 at 09 26 20

browser zoom

Finally this was a strong indicator that the theory of a metrics measurement rounding error is the culprit: Changing the browser zoom alters the outcome of the bug (triggered or not.)

sdfomio2


I bet that this is a bug in Safari/WebKit. I bet we can find other fonts that trigger the bug as well. Since Inter uses an uncommon EM multiplier (2816) it could be so that the testing done around the Safari/WebKit type layout engine has only been tested with power-of-two and the classic 1000 Units per EM values. If they use IEEE-754 floating point arithmetic it's not only likely but guaranteed that at some conditions precision is lost and rounded. If not handled explicitly (i.e. with ceil and floor instructions) outcome of equivalent calculations could yield different results in practice.

I don't know what I can do with Inter to work around what definitely seems to be a WebKit bug. I think it's best if the WebKit team can look into this and chime in. Even if I tried to find a work-around for the issue, it would be an incredible amount of effort trying to simply guess the reason. It could be months of guesswork on my end, which does not seem worth it.

The reason this exists in WebKit is we use floating point math, which is famously non-commutative, to perform inline text layout.

Flex-item elements like these are "shrink wrapped," which are laid out by the browser in two passes: The first pass calculates how much space its content requires and reserves that much space for the element, and the second pass places all the text within the reserved space. The first pass can take some shortcuts because it doesn't have to place all the text exactly; it just needs to determine how much space is needed in total. These shortcuts are mathematically sound, but rely on the commutative property of addition. However, floating point addition is _not_ commutative, resulting in tiny ULP-sized size differences between the first and second pass's measurements.

For this specific example, the first pass measures that the text requires 104.5625 pixels of space, but when the second pass places all the text within that space, it discovers that the text is actually 104.562508 pixels wide (0.000008 pixels wider than the available space). Because the text apparently ends up requiring more space than is available, we trigger line wrapping.

You're right that this should be considered a browser bug. I've filed https://bugs.webkit.org/show_bug.cgi?id=217136 so we can track it.

It should be possible to work around this bug even before a browser fix gets shipped to customers. For this specific example, just jostling the letters around slightly (e.g. letter-spacing: 0.000001px) fixes it, but doing this wouldn't be a robust solution. A more robust solution would be using font sizes (in px) which divide more evenly into the font's unitsPerEm, so the advance widths of the characters (in px) can fit better into floating point values. Using these "rounder" float values should help with the non-commutative property of floating point math.

Amazing research here! Thank you all so much for looking into this.

An initial test seems to confirm using a global letter-spacing: 0.000001px will work as an interim solution and we'll keep an eye on that webkit bug.

Again, thanks so much for your deep analysis. Your response blew us away.

Second that, @estesmax . Thank you very much @rsms and @litherum , for the research, the fast response, the alternate solution and the more appropriate bug report.

Thank you, @rsms and @litherum, in behalf of the Design Technology team at Carta. Deeply appreciate the help.

Was this page helpful?
0 / 5 - 0 ratings