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

Optimising fonts not in use #158

Closed bramstein closed 2 years ago

bramstein commented 2 years ago

:wave:

I'm working on a type-tester like tool where the user can select different fonts. The site is entirely static. The fonts are defined in @font-face rules in CSS, but aren't "in-use" before the user interacts with the tool. Unfortunately that means subfont strips out the @font-face rules for those fonts.

I got around this by adding a hidden span <span style="font-family: fontA, fontB, fontC, ...">ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz01234567890!?&., </span> in the page with all the families and the list of allowed characters. It works, but feels fragile.

I know this is probably outside the normal use case for subfont, but I was wondering if you would consider supporting some type of annotation. Basically something that says "I know what I'm doing, just create subsets for me".

papandreou commented 2 years ago

Hey Bram! Long time! 🤗

Subfont should leave the original @font-face rules intact, it just doesn't inject a subsets for unused ones, right? Except if you pass --no-fallbacks IIRC.

I can't find the discussion right now, but we've talked addressing this usecase by eg. supporting this "I know what I'm doing" setting per font source as a CSS property within one of the original @font-face rules:

@font-face {
  font-family: seemingly-unused-font;
  font-weight: 700;
  font-style: normal;
  -subfont-unicode-range: U+41-U+61, ...;
}

Is this what you had in mind wrt. how to specify it?

bramstein commented 2 years ago

Hi Andreas! Far too long! We should catch up sometime soon.

I used --no-fallbacks because the fonts are proprietary and didn't want to share an unsubsetted version. I could pre-subset them, but that might break in the future.

Yes, that's pretty much what I had in mind. I was thinking a custom URL query parameter though:

@font-face {
  font-family: seemingly-unused-font;
  src: url(path/to/font.otf?subfont-keep);
  font-weight: 700;
  font-style: normal;
  unicode-range: U+41-U+61, ...;
}

I'm not attached to that idea though.

Munter commented 2 years ago

I think we once talked about the CMS case, where you'd like to subset, but don't actually have the ability to predict which fonts will be used on which pages, or if they would be used on any at all.

I know the people I talked to there asked for an ability to feed the command line with a manual Unicode range or character list that should be included in all subsets of all font variations by default. They would query their database once in a while to create that set, or just be optimistic about their ability to manually predict usage.

Could such a command-line flag solve your problem as well?

bramstein commented 2 years ago

A command line argument would solve my problem as well. I prefer the subsetting instructions to be close to the definition of the @font-face rules so I don't need to keep command line arguments in sync with the CSS.

(But yes please, any way to do this would be great! :wink: )

papandreou commented 2 years ago

The problem with a cli switch is that it would need to be able to target a specific @font-face declaration, so it would need to specify a font-family/font-style/font-weight/font-stretch/whatnot combination. At least if we want it to work at that level. An "include these characters in every font" feature could be useful too, of course.

papandreou commented 2 years ago

It's not super hard to implement support for a custom property: https://github.com/Munter/subfont/compare/tech/explicitlyIncludeCharacters

papandreou commented 2 years ago

Also implemented support for a --text switch on that branch now. Does it seem like what you had in mind? :hugs:

papandreou commented 2 years ago

@bramstein @Munter, WDYT?

bramstein commented 2 years ago

Sorry, I dropped the ball on this. I just checked it out, and for I'm getting an error on line 133 of lib/subsetFonts.js. If I remove the remove call, it seems to work correctly. I'll try to do some debugging tonight.

papandreou commented 2 years ago

Thanks for taking a look! I can reproduce that problem if I change the casing of the -subfont-text property:

TypeError: Cannot read properties of undefined (reading 'remove')
    at groupTextsByFontFamilyProps (/home/andreas/work/subfont/lib/subsetFonts.js:133:9)
    at subsetFonts (/home/andreas/work/subfont/lib/subsetFonts.js:756:52)
    at async subfont (/home/andreas/work/subfont/lib/subfont.js:222:24)

Pushed a fix to make it a bit more resilient, I hope that helps :crossed_fingers:

bramstein commented 2 years ago

I can confirm that it works now, but the -subfont-text property was all in lowercase in my test code. The example I had used a mixed collection of @font-face rules with and without the property.

papandreou commented 2 years ago

I think I figured it out, I could reproduce the breakage when multiple pages were utilizing the same @font-face. Pushed a fix to the branch :innocent:

bramstein commented 2 years ago

Thanks, it is working as expected now!

papandreou commented 2 years ago

Cool! I released it in 6.5.0 just now.