Skip to content

fix(fonts): Fixed page title fonts not downloadable to local #1898

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

Open
wants to merge 2 commits into
base: v4
Choose a base branch
from

Conversation

Stephen-X
Copy link
Contributor

@Stephen-X Stephen-X commented Apr 3, 2025

This PR addresses the issue where page title fonts introduced in #1848 are not downloadable and still reaches Google Fonts API when cdnCaching is set to false. Caught this bug because I have CSP set on my website.

Details

The issue is the PR introduced a new Google Fonts API to extract only a subset of fonts needed for the page title text, but didn't update the parser regex to recognize the new font URLs returned by the new API, which causes a fallback to the cloud.

Example of a standard API call that downloads the full font: https://fonts.googleapis.com/css2?family=Roboto+Slab&display=swap, which returns the following CSS:

/* cyrillic-ext */
@font-face {
  font-family: 'Roboto Slab';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url(https://fonts.gstatic.com/s/robotoslab/v34/BngbUXZYTXPIvIBgJJSb6s3BzlRRfKOFbvjojISmYmRjV9Su1caiTVo.woff2) format('woff2');
  unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
...

Example of a subset API call that downloads only what's needed to render given texts: https://fonts.googleapis.com/css2?family=Roboto+Slab&text=hello&display=swap, which returns the following CSS:

@font-face {
  font-family: 'Roboto Slab';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url(https://fonts.gstatic.com/l/font?kit=BngbUXZYTXPIvIBgJJSb6s3BzlRRfKOFbvjojISWbW5iddC21OSnEDNSkw&skey=a9ad6a4717e923f0&v=v34) format('woff2');
}

I updated the regex to cover these two cases, we're also now getting the API URL, file name and the extension entirely through capture groups. Manually tested working on a number of fonts.

export async function processGoogleFonts(
stylesheet: string,
baseUrl: string,
): Promise<{
processedStylesheet: string
fontFiles: GoogleFontFile[]
}> {
const fontSourceRegex = /url\((https:\/\/fonts.gstatic.com\/s\/[^)]+\.(woff2|ttf))\)/g
const fontSourceRegex =
/url\((https:\/\/fonts.gstatic.com\/.+(?:\/|(?:kit=))(.+?)[.&].+?)\)\sformat\('(\w+?)'\);/g
Copy link
Contributor Author

@Stephen-X Stephen-X Apr 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a note here, there's no public API spec for fonts.gstatic.com so unfortunately I'd have to base this on existing URLs in a number of fonts' CSS from fonts.googleapis.com/css2. To explain this regex a bit, local file name is either:

fonts.gstatic.com/s/robotoslab/v34/<filename>.woff2

or

fonts.gstatic.com/l/font?kit=<filename>&skey=a9ad6a4717e923f0&v=v34

With the file extension now intepreted from the format annotation because fonts.gstatic.com doesn't include a file extension in the CSS returned by its subset API.

From testing the file name could contain alphanumeric characters, underscores or dashes (mostly the subset API, naming is quite liberal), but they are unique and they wouldn't at least contain a dot or an ampersand.

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

Successfully merging this pull request may close these issues.

1 participant