Repository: GitbookIO/gitbook
Pull request #3119: Fix ogimage generation crashing when site is using a custom WOFF2 font
Author: login: SamyPesse
Created: 2025-04-08T12:40:36Z | Merged: 2025-04-08T14:44:29Z



Diff:
diff --git a/.changeset/quick-countries-allow.md b/.changeset/quick-countries-allow.md
new file mode 100644
index 0000000000..1ab3a595ae
--- /dev/null
+++ b/.changeset/quick-countries-allow.md
@@ -0,0 +1,5 @@
+---
+"gitbook": patch
+---
+
+Fix ogimage generation crashing when site is using a custom WOFF2 font
diff --git a/packages/gitbook/e2e/customers.spec.ts b/packages/gitbook/e2e/customers.spec.ts
index 73d5df1620..3d22108359 100644
--- a/packages/gitbook/e2e/customers.spec.ts
+++ b/packages/gitbook/e2e/customers.spec.ts
@@ -239,6 +239,15 @@ const testCases: TestsCase[] = [
         contentBaseURL: 'https://docs.fluentbit.io',
         tests: [{ name: 'Home', url: '/', run: waitForCookiesDialog }],
     },
+    {
+        name: 'run-ai-docs.nvidia.com',
+        contentBaseURL: 'https://run-ai-docs.nvidia.com',
+        skip: process.env.ARGOS_BUILD_NAME !== 'customers-v2',
+        tests: [
+            { name: 'Home', url: '/' },
+            { name: 'OG Image', url: '/~gitbook/ogimage/h17zQIFwy3MaafVNmItO', mode: 'image' },
+        ],
+    },
 ];
 
 runTestCases(testCases);
diff --git a/packages/gitbook/e2e/util.ts b/packages/gitbook/e2e/util.ts
index e5703dddf6..a18b5b0444 100644
--- a/packages/gitbook/e2e/util.ts
+++ b/packages/gitbook/e2e/util.ts
@@ -34,6 +34,10 @@ export interface Test {
      * Test to run
      */
     run?: (page: Page, response: Response | null) => Promise<unknown>;
+    /**
+     * Mode for the test.
+     */
+    mode?: 'page' | 'image';
     /**
      * Whether the test should be fullscreened during testing.
      */
@@ -154,6 +158,7 @@ export function runTestCases(testCases: TestsCase[]) {
 
         test.describe(testCase.name, () => {
             for (const testEntry of testCase.tests) {
+                const { mode = 'page' } = testEntry;
                 const testFn = testEntry.only ? test.only : test;
                 testFn(testEntry.name, async ({ page, context }) => {
                     const testEntryPathname =
@@ -194,24 +199,33 @@ export function runTestCases(testCases: TestsCase[]) {
                     }
                     const screenshotOptions = testEntry.screenshot;
                     if (screenshotOptions !== false) {
-                        await argosScreenshot(page, `${testCase.name} - ${testEntry.name}`, {
-                            viewports: ['macbook-16', 'macbook-13', 'ipad-2', 'iphone-x'],
-                            argosCSS: `
+                        const screenshotName = `${testCase.name} - ${testEntry.name}`;
+                        if (mode === 'image') {
+                            await argosScreenshot(page, screenshotName, {
+                                viewports: ['macbook-13'],
+                                threshold: screenshotOptions?.threshold ?? undefined,
+                                fullPage: true,
+                            });
+                        } else {
+                            await argosScreenshot(page, screenshotName, {
+                                viewports: ['macbook-16', 'macbook-13', 'ipad-2', 'iphone-x'],
+                                argosCSS: `
                             /* Hide Intercom */
                             .intercom-lightweight-app {
                                 display: none !important;
                             }
                             `,
-                            threshold: screenshotOptions?.threshold ?? undefined,
-                            fullPage: testEntry.fullPage ?? false,
-                            beforeScreenshot: async ({ runStabilization }) => {
-                                await runStabilization();
-                                await waitForIcons(page);
-                                if (screenshotOptions?.waitForTOCScrolling !== false) {
-                                    await waitForTOCScrolling(page);
-                                }
-                            },
-                        });
+                                threshold: screenshotOptions?.threshold ?? undefined,
+                                fullPage: testEntry.fullPage ?? false,
+                                beforeScreenshot: async ({ runStabilization }) => {
+                                    await runStabilization();
+                                    await waitForIcons(page);
+                                    if (screenshotOptions?.waitForTOCScrolling !== false) {
+                                        await waitForTOCScrolling(page);
+                                    }
+                                },
+                            });
+                        }
                     }
                 });
             }
diff --git a/packages/gitbook/src/lib/assets.ts b/packages/gitbook/src/lib/assets.ts
index b073422caa..0b53e31e73 100644
--- a/packages/gitbook/src/lib/assets.ts
+++ b/packages/gitbook/src/lib/assets.ts
@@ -1,8 +1,8 @@
-import { GITBOOK_ASSETS_URL } from '@v2/lib/env';
+import { GITBOOK_ASSETS_URL, GITBOOK_URL } from '@v2/lib/env';
 
 /**
  * Create a public URL for an asset.
  */
 export function getAssetURL(path: string): string {
-    return `${GITBOOK_ASSETS_URL || ''}/~gitbook/static/${path}`;
+    return `${GITBOOK_ASSETS_URL || GITBOOK_URL}/~gitbook/static/${path}`;
 }
diff --git a/packages/gitbook/src/routes/ogimage.tsx b/packages/gitbook/src/routes/ogimage.tsx
index c9dd5d3969..41842ab241 100644
--- a/packages/gitbook/src/routes/ogimage.tsx
+++ b/packages/gitbook/src/routes/ogimage.tsx
@@ -89,12 +89,15 @@ export async function serveOGImage(baseContext: GitBookSiteContext, params: Page
             await Promise.all(
                 primaryFontWeights.map((face) => {
                     const { weight, sources } = face;
-                    if (sources.length === 0) {
+                    const source = sources[0];
+
+                    // Satori doesn't support WOFF2, so we skip it
+                    // https://github.com/vercel/satori?tab=readme-ov-file#fonts
+                    if (!source || source.format === 'woff2' || source.url.endsWith('.woff2')) {
                         return null;
                     }
-                    const url = sources[0].url;
 
-                    return loadCustomFont({ url, weight });
+                    return loadCustomFont({ url: source.url, weight });
                 })
             )
         ).filter(filterOutNullable);


Reviews:
user: login: gregberge
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-04-08T13:28:13Z
user: login: SamyPesse
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-08T13:30:36Z

Review comments:
diff_hunk: @@ -89,12 +89,15 @@ export async function serveOGImage(baseContext: GitBookSiteContext, params: Page
             await Promise.all(
                 primaryFontWeights.map((face) => {
                     const { weight, sources } = face;
-                    if (sources.length === 0) {
+                    const source = sources[0];
path: packages/gitbook/src/routes/ogimage.tsx
user: login: gregberge
body: Why are we taking the first source like that? Shouldn't we iterate? What are the sources?
created_at: 2025-04-08T13:28:05Z
author_association: CONTRIBUTOR
diff_hunk: @@ -89,12 +89,15 @@ export async function serveOGImage(baseContext: GitBookSiteContext, params: Page
             await Promise.all(
                 primaryFontWeights.map((face) => {
                     const { weight, sources } = face;
-                    if (sources.length === 0) {
+                    const source = sources[0];
path: packages/gitbook/src/routes/ogimage.tsx
user: login: SamyPesse
body: I' not sure tbh, I'm just adapting the code not to crash. Maybe @emmerich knows?
created_at: 2025-04-08T13:30:35Z
author_association: MEMBER

PR comments:
user: login: changeset-bot[bot]
created_at: 2025-04-08T12:40:40Z
body: ###  🦋  Changeset detected

Latest commit: 129ea0ea9535314514cf9f64af7e6883582c4da4

**The changes in this PR will be included in the next version bump.**

<details><summary>This PR includes changesets to release 1 package</summary>

  | Name    | Type  |
| ------- | ----- |
| gitbook | Patch |

</details>

Not sure what this means? [Click here  to learn what changesets are](https://github.com/changesets/changesets/blob/main/docs/adding-a-changeset.md).

[Click here if you're a maintainer who wants to add another changeset to this PR](https://github.com/GitbookIO/gitbook/new/fix-woff2-ogimage?filename=.changeset/short-kiwis-vanish.md&value=---%0A%22gitbook%22%3A%20patch%0A---%0A%0AFix%20ogimage%20generation%20crashing%20when%20site%20is%20using%20a%20custom%20WOFF2%20font%0A)


author_association: NONE
user: login: github-actions[bot]
created_at: 2025-04-08T12:44:55Z
body: Summary of the deployments:

### Version 1 (production)

| Version | URL | Status |
| --- | --- | --- |
| Latest commit | [https://9a2626ce.gitbook-open.pages.dev](https://9a2626ce.gitbook-open.pages.dev) | ✅ |
| PR | [https://pr3119.gitbook-open.pages.dev](https://pr3119.gitbook-open.pages.dev) | ✅ |

### Version 2 (experimental)

| Version | URL | Status |
| --- | --- | --- |
| Vercel | [https://gitbook-v2-jzj8xoky8-gitbook.vercel.app](https://gitbook-v2-jzj8xoky8-gitbook.vercel.app) | ✅ |
| Cloudflare | []() | ✅ |

### Test content

| Site | v1 | v2 |
| --- | --- | --- |
| GitBook | [https://9a2626ce.gitbook-open.pages.dev/docs.gitbook.com](https://9a2626ce.gitbook-open.pages.dev/docs.gitbook.com) | [https://gitbook-v2-jzj8xoky8-gitbook.vercel.app/url/docs.gitbook.com](https://gitbook-v2-jzj8xoky8-gitbook.vercel.app/url/docs.gitbook.com) |
| E2E | [https://9a2626ce.gitbook-open.pages.dev/gitbook.gitbook.io/test-gitbook-open](https://9a2626ce.gitbook-open.pages.dev/gitbook.gitbook.io/test-gitbook-open) | [https://gitbook-v2-jzj8xoky8-gitbook.vercel.app/url/gitbook.gitbook.io/test-gitbook-open](https://gitbook-v2-jzj8xoky8-gitbook.vercel.app/url/gitbook.gitbook.io/test-gitbook-open) |
author_association: CONTRIBUTOR
user: login: argos-ci[bot]
created_at: 2025-04-08T12:47:05Z
body: **The latest updates on your projects.** Learn more about [Argos notifications ↗︎](https://argos-ci.com/docs/notifications)

| Build | Status | Details | Updated (UTC) |
| :---- | :----- | :------ | :------------ |
| **customers-v1** ([Inspect](https://app.argos-ci.com/GitbookIO/gitbook/builds/6304)) | ✅ No changes detected | - | Apr 8, 2025, 2:05 PM |
| **customers-v2** ([Inspect](https://app.argos-ci.com/GitbookIO/gitbook/builds/6305)) | 👍 Changes approved | 5 added | Apr 8, 2025, 2:08 PM |
| **default** ([Inspect](https://app.argos-ci.com/GitbookIO/gitbook/builds/6306)) | 👍 Changes approved | 1 changed | Apr 8, 2025, 2:09 PM |
| **v2-vercel** ([Inspect](https://app.argos-ci.com/GitbookIO/gitbook/builds/6307)) | ✅ No changes detected | - | Apr 8, 2025, 2:10 PM |
author_association: NONE