Munter / subfont

Command line tool to optimize your webfont loading. Aggressive subsetting based on your font use, self-hosting of Google fonts and preloading
MIT License
1.56k stars 29 forks source link

Disable subfont subset when original font fallback was loaded #39

Open Munter opened 6 years ago

Munter commented 6 years ago

When a text consists of a mix of two different font files, for example a overly aggressive subset and an original font fallback for a few glyphs, the font metrics across the boundary of the glyphs from different files are wrong. This is because kerning pairs, ligatures and possibly other font tables cannot apply at these boundaries.

This means that in the worst case scenario where a subfont subset and an original font fallback are both in play, any typographer would become very unhappy.

A solution to keeping the intended font metrics could be to disable the subfont subset when we detect that the original font has been loaded. This can be achieved by using the CSS font loading API to observe when the original font loads, then remove the subfont subset font-face rule from the stylesheet.

Example implementation:

    var fontProps = ['font-family', 'font-weight', 'font-style'];

    function findMatchingSubsetFontFaceDelaration(originalFontFace) {
      for (var stylesheet of [...document.styleSheets]) {
        for (var [idx, rule] of [...stylesheet.cssRules].entries()) {
          if (rule.type === rule.FONT_FACE_RULE) {
            var props = {};

            for (var prop of fontProps) {
              props[prop.replace('font-', '')] = rule.style.getPropertyValue(prop) || 'normal';
            }

            var strippedFamily = originalFontFace.family.replace(/^["']|["']$/g, '');

            if (props.family === strippedFamily + '__subset' && props.weight === originalFontFace.weight && props.style === originalFontFace.style) {
              return function removeSubset() {
                console.log('deleting', rule);
                stylesheet.deleteRule(idx);
              }
            }
          }
        }
      }
    }

    if (document.fonts) {
      document.fonts.forEach(function (fontFace) {
        if (!fontFace.family.endsWith('__subset')) {
          fontFace.loaded.then(function () {
            var removeSubset = findMatchingSubsetFontFaceDelaration(fontFace);
            if (removeSubset) {
              removeSubset();
            }
          });
        }
      });
    }

Tested in

Replaces #33

TODOs

Example repository to iterate on and test in different browsers: https://github.com/Munter/font-subset-delete-POC

Deployed at https://font-subset-delete.netlify.com/

Munter commented 6 years ago

If we save the original font-face blocks src property as -subfont-src in the subset font-face block, then we only need to compare that property and can maybe skip font-family unquoting, font-weight normalization, default fallbacks

Mouvedia commented 3 years ago

Can we have a status update on this one?

Munter commented 3 years ago

Status is that this issue is mostly a brain dump from a collaboration session and no further work has gone into it.

It would be great to have the remaining browsers tested. If they all work I think the task can be considered ready for development