Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Disable subfont subset when original font fallback was loaded #39

Open
2 of 12 tasks
Munter opened this issue Jul 27, 2018 · 3 comments
Open
2 of 12 tasks

Disable subfont subset when original font fallback was loaded #39

Munter opened this issue Jul 27, 2018 · 3 comments

Comments

@Munter
Copy link
Owner

Munter commented Jul 27, 2018

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

  • Chrome desktop
  • Firefox desktop
  • Safari desktop
  • Chrome android
  • Firefox android
  • Safari iOS

Replaces #33

TODOs

  • Normalize font-weights
  • Unquote font-family
  • Fill in default values for empty strings in Css style rules
  • Set CssFontFamilyRule src url to be the original fonts url. This is to avoid the font matching algorithm matching a different __subset font and synthesising the one we just disabled
    • Re-resolve href to original font url from where the subset font declaration resides. Possibly save this in a non-standard -subfont-original-src property at build time
  • What if the original @font-face-declarations don't reside in the HTML, but in an external CSS file? Maybe we can move the declarations next to the subfont generated ones at build time

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
Copy link
Owner Author

Munter commented Jul 28, 2018

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
Copy link

Can we have a status update on this one?

@Munter
Copy link
Owner Author

Munter commented Jan 16, 2021

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

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

No branches or pull requests

2 participants