[css-fonts] Issues with font matching algorithm when weights are missing

The font matching algorithm outlined in [1] results in cases of very odd matching when it comes to missing font weights and it should be changed. For example, if the requested font weight is 100 (extra light) and only 900 is available (which is black) or vice versa, this one will be used instead of falling back to the next family in the font stack. When the difference between desired and available weights is so dramatic, falling back would be a more appropriate choice in most cases.

This issue was brought to my attention when a colleague sent me [2], asking why the different behavior between Firefox & Chrome. Upon further investigation, it appeared that he only had UltraBold for Gill Sans and Chrome used that for all weights to avoid falling back to Gill Sans MT. One would expect that Chrome’s behavior is the buggy one here, but according to the existing algorithm [2], Chrome is perfectly correct and Firefox is buggy!

In addition, even when the difference between available and requested weights is smaller, the current algorithm can still result in suboptimal matching (i.e. picking weights that are not the closest possible to the requested weight). Observe the following examples:

Desired | Available | Matched | Better option
--------+---------------+---------------+----------------
100 | 900  | 900 (+800) | Fall back!
900 | 100  | 100 (-800) | Fall back!
400 | 100, 600 | 100 (-300) | 600 (+200)
500 | 300, 900 | 900 (+400) | 300 (-200)
600 | 500, 900 | 900 (+300) | 500 (-100)
300 | 100, 400 | 100 (-300) | 400 (+100)

I believe it would be better to alternately try to match weights with an offset of -100, +100, -200, +200, ... up to a certain maximum (by absolute value) offset to avoid the issue shown in [2] (200 or 300 looks like a good candidate). I’ve written a simple JS function [3] so you can test this out with different parameters (the possible combinations of missing weights are more than you might think: as many as 9*2^8=2304).
Not only this algorithm always results in matching the closest possible weight (results perfectly match the “Better option” column above), but it avoids having to special case 400 and 500 like the existing one.

Currently it prioritizes lighter fonts over bolder fonts when two weights exist with the same distance, but that’s an arbitrary choice. Since it’s arbitrary, we could easily start from the positive offset if the weight is divisible by 200, which results in matching 500 when the desired weight is 400 and 400 when the desired weight is 500, just like the existing special casing which checks 500 first for 400, and 500 first for 400.

Thoughts?

[1]: http://dev.w3.org/csswg/css-fonts/#font-matching-algorithm
[2]: (URL: cssfontstack.com/Gill-Sans, OS: Windows 8) http://lea.verou.me/images/font-matching.png
[3]: https://gist.github.com/LeaVerou/adbc2e4750ad809b6aef

Lea Verou ✿ http://lea.verou.me ✿ @leaverou

Received on Sunday, 28 December 2014 18:12:49 UTC