remotion icon indicating copy to clipboard operation
remotion copied to clipboard

`@remotion/google-fonts`: Support Variable Fonts

Open empz opened this issue 8 months ago • 5 comments

I'm trying to build a multi-language font picker. I need to use fonts subsets info to know what is supported by each font.

For example, doing a GET https://www.googleapis.com/webfonts/v1/webfonts?family=Noto Sans SC returns the following:

{
  "kind": "webfonts#webfontList",
  "items": [
    {
      "family": "Noto Sans SC",
      "variants": [
        "100",
        "200",
        "300",
        "regular",
        "500",
        "600",
        "700",
        "800",
        "900"
      ],
      "subsets": [
        "chinese-simplified",
        "cyrillic",
        "latin",
        "latin-ext",
        "vietnamese"
      ],
      "version": "v37",
      "lastModified": "2024-07-30",
      "files": {
        "100": "https://fonts.gstatic.com/s/notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_EnYlNbPzS5HE.woff2",
        "200": "https://fonts.gstatic.com/s/notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG1_FnYlNbPzS5HE.woff2",
        "300": "https://fonts.gstatic.com/s/notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG4HFnYlNbPzS5HE.woff2",
        "regular": "https://fonts.gstatic.com/s/notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYlNbPzS5HE.woff2",
        "500": "https://fonts.gstatic.com/s/notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG-3FnYlNbPzS5HE.woff2",
        "600": "https://fonts.gstatic.com/s/notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaGwHCnYlNbPzS5HE.woff2",
        "700": "https://fonts.gstatic.com/s/notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaGzjCnYlNbPzS5HE.woff2",
        "800": "https://fonts.gstatic.com/s/notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG1_CnYlNbPzS5HE.woff2",
        "900": "https://fonts.gstatic.com/s/notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG3bCnYlNbPzS5HE.woff2"
      },
      "category": "sans-serif",
      "kind": "webfonts#webfont",
      "menu": "https://fonts.gstatic.com/s/notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FrY1HbQ.woff2"
    }
  ]
}

This is very useful as I know this font supports Chinese Simplified subset.

Now, the same font retrieved from getAvailableFonts contains a series of really really long unicodeRanges that are numbered. I can't even paste it here because it's too long. Attaching instead.

noto-sans-sc-info.json

Not sure why this is happening. If it's intended for a specific reason that can't be changed, would you consider adding a function that loads a font from the Google Fonts API info? That way I can simply pass the JSON returned from their API and just send it to this function for loading.

empz avatar Apr 14 '25 12:04 empz

For easier maintenance, I don't want the burden of supporting multiple source of truths, we expose the same information as JSON.

But I will expose a subsets field as part of FontInfo, that I think is a good idea!

JonnyBurger avatar Apr 15 '25 10:04 JonnyBurger

For easier maintenance, I don't want the burden of supporting multiple source of truths, we expose the same information as JSON.

But I will expose a subsets field as part of FontInfo, that I think is a good idea!

That's good enough for me!

I'm curious to understand why do we need all those unicodeRanges and where are they being retrieved from? The Google Fonts API doesn't include that in their response: https://developers.google.com/fonts/docs/developer_api - Do we really need that huge font info with all those single character unicode ranges just to load Noto Sans SC?

Also, what about variable fonts? I see the load functions from the @remotion/google-fonts package loads multiple .woff2 files for a single font? How can we add support for variable fonts since they are supported by most browsers nowadays (https://v-fonts.com/support)

empz avatar Apr 15 '25 10:04 empz

I'm also a bit doubtful to be honest, this comes from here:

https://fonts.googleapis.com/css2?family=Noto+Sans+SC

/* [4] / @font-face { font-family: 'Noto Sans SC'; font-style: normal; font-weight: 400; src: url(https://fonts.gstatic.com/s/notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYlNbPzT7HEL7j12XCOHJKg4RgZw3nFTvwZ8atTsBvwlvRUk7mYP2g.4.woff2) format('woff2'); unicode-range: U+1f1e9-1f1f5, U+1f1f7-1f1ff, U+1f21a, U+1f232, U+1f234-1f237, U+1f250-1f251, U+1f300, U+1f302-1f308, U+1f30a-1f311, U+1f315, U+1f319-1f320, U+1f324, U+1f327, U+1f32a, U+1f32c-1f32d, U+1f330-1f357, U+1f359-1f37e; } / [5] */ @font-face { font-family: 'Noto Sans SC'; font-style: normal; font-weight: 400; src: url(https://fonts.gstatic.com/s/notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYlNbPzT7HEL7j12XCOHJKg4RgZw3nFTvwZ8atTsBvwlvRUk7mYP2g.5.woff2) format('woff2'); unicode-range: U+fee3, U+fef3, U+ff03-ff04, U+ff07, U+ff0a, U+ff17-ff19, U+ff1c-ff1d, U+ff20-ff3a, U+ff3c, U+ff3e-ff5b, U+ff5d, U+ff61-ff65, U+ff67-ff6a, U+ff6c, U+ff6f-ff78, U+ff7a-ff7d, U+ff80-ff84, U+ff86, U+ff89-ff8e, U+ff92, U+ff97-ff9b, U+ff9d-ff9f, U+ffe0-ffe4, U+ffe6, U+ffe9, U+ffeb, U+ffed, U+fffc, U+1f004, U+1f170-1f171, U+1f192-1f195, U+1f198-1f19a, U+1f1e6-1f1e8; }

Do you know what it's for and if we can omit them? Do you wanna do some research?

JonnyBurger avatar Apr 15 '25 13:04 JonnyBurger

I'm also a bit doubtful to be honest, this comes from here:

https://fonts.googleapis.com/css2?family=Noto+Sans+SC

/* [4] / @font-face { font-family: 'Noto Sans SC'; font-style: normal; font-weight: 400; src: url(https://fonts.gstatic.com/s/notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYlNbPzT7HEL7j12XCOHJKg4RgZw3nFTvwZ8atTsBvwlvRUk7mYP2g.4.woff2) format('woff2'); unicode-range: U+1f1e9-1f1f5, U+1f1f7-1f1ff, U+1f21a, U+1f232, U+1f234-1f237, U+1f250-1f251, U+1f300, U+1f302-1f308, U+1f30a-1f311, U+1f315, U+1f319-1f320, U+1f324, U+1f327, U+1f32a, U+1f32c-1f32d, U+1f330-1f357, U+1f359-1f37e; } / [5] */ @font-face { font-family: 'Noto Sans SC'; font-style: normal; font-weight: 400; src: url(https://fonts.gstatic.com/s/notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYlNbPzT7HEL7j12XCOHJKg4RgZw3nFTvwZ8atTsBvwlvRUk7mYP2g.5.woff2) format('woff2'); unicode-range: U+fee3, U+fef3, U+ff03-ff04, U+ff07, U+ff0a, U+ff17-ff19, U+ff1c-ff1d, U+ff20-ff3a, U+ff3c, U+ff3e-ff5b, U+ff5d, U+ff61-ff65, U+ff67-ff6a, U+ff6c, U+ff6f-ff78, U+ff7a-ff7d, U+ff80-ff84, U+ff86, U+ff89-ff8e, U+ff92, U+ff97-ff9b, U+ff9d-ff9f, U+ffe0-ffe4, U+ffe6, U+ffe9, U+ffeb, U+ffed, U+fffc, U+1f004, U+1f170-1f171, U+1f192-1f195, U+1f198-1f19a, U+1f1e6-1f1e8; }

Do you know what it's for and if we can omit them? Do you wanna do some research?

It looks like a unicode-range is needed in the CSS to tell the browser that the font supports them. In that case the response from Google's API is not very useful as it doesn't include any unicode ranges, just subsets. It looks like one needs to build its own CSS with the unicode ranges. I'm assuming that chinese ideograms are treated specially and they are not inside any range, just one per ideogram. Hence the huge unicode range list.

Anyway, it looks like leveraging what's served by Google's fonts CSS (i.e. https://fonts.googleapis.com/css2?family=Noto+Sans+SC) is the best choice.

I do need subset info to properly group and categorize them.

empz avatar Apr 15 '25 13:04 empz

I'd still like to use variable fonts when possible as it's much more network efficient.

This is the font info for "Noto Sans" that Remotion has on the package. It points to this CSS file: https://fonts.googleapis.com/css2?family=Noto+Sans:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900 This is not using the variable variation of the font and so it needs to load one font per weight.

If you go to Google Fonts page, the default embed code uses variable fonts:

Image

The resulting CSS and needed fonts is much smaller: https://fonts.googleapis.com/css2?family=Noto+Sans:ital,wght@0,100..900;1,100..900&display=swap

empz avatar Apr 15 '25 13:04 empz