From db731856f806aabd15af8bfdb085ea296ac05adf Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 26 Feb 2026 17:21:36 +0000 Subject: [PATCH] Fix missing OpenSans fonts in monograph Docker image Co-authored-by: thecodrr <7473959+thecodrr@users.noreply.github.com> --- .../app/utils/generate-og-image.server.ts | 4 +- apps/monograph/scripts/font-loader.mjs | 85 ++++++++++++++++--- 2 files changed, 76 insertions(+), 13 deletions(-) diff --git a/apps/monograph/app/utils/generate-og-image.server.ts b/apps/monograph/app/utils/generate-og-image.server.ts index 97b42862a..ac514ef0c 100644 --- a/apps/monograph/app/utils/generate-og-image.server.ts +++ b/apps/monograph/app/utils/generate-og-image.server.ts @@ -39,12 +39,12 @@ const fontMap = JSON.parse( const OpenSans = path.join( __dirname, import.meta.env.DEV ? "../assets/fonts/" : "../../assets/fonts/", - "open-sans-v34-vietnamese_latin-ext_latin_hebrew_greek-ext_greek_cyrillic-ext_cyrillic-regular.ttf" + "OpenSans-Regular.ttf" ); const OpenSansBold = path.join( __dirname, import.meta.env.DEV ? "../assets/fonts/" : "../../assets/fonts/", - "open-sans-v34-vietnamese_latin-ext_latin_hebrew_greek-ext_greek_cyrillic-ext_cyrillic-600.ttf" + "OpenSans-SemiBold.ttf" ); console.log("OpenSans", GlobalFonts.registerFromPath(OpenSans, "OpenSans")); diff --git a/apps/monograph/scripts/font-loader.mjs b/apps/monograph/scripts/font-loader.mjs index 10882f1c4..2d7b29605 100644 --- a/apps/monograph/scripts/font-loader.mjs +++ b/apps/monograph/scripts/font-loader.mjs @@ -21,6 +21,11 @@ import { createWriteStream, existsSync, writeFileSync } from "fs"; import { constructURL } from "google-fonts-helper"; import { Writable } from "stream"; import { mkdir } from "fs/promises"; +import path from "path"; +import { fileURLToPath } from "url"; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); const FONTS = [ { locale: "ja-JP", slug: "Noto+Sans+JP", name: "Noto Sans JP" }, @@ -50,6 +55,22 @@ const FONTS = [ // { locale: "math", slug: "Noto+Sans+Math", name: "Noto Sans Math" } ]; +const OPEN_SANS_FONTS = [ + { weight: 400, filename: "OpenSans-Regular.ttf" }, + { weight: 600, filename: "OpenSans-SemiBold.ttf" } +]; + +async function downloadFont(url, destPath) { + const fileStream = Writable.toWeb( + createWriteStream(destPath, { + autoClose: true, + emitClose: true, + flush: true + }) + ); + await (await fetch(url)).body.pipeTo(fileStream); +} + async function loadFonts(dir) { const families = {}; for (const font of FONTS) { @@ -93,28 +114,70 @@ async function loadFonts(dir) { if (!font) continue; const fileName = `${font.slug}+${face.weight}.ttf`; - const path = `fonts/${fileName}`; - if (existsSync(path)) continue; + const filePath = `fonts/${fileName}`; + if (existsSync(filePath)) continue; - const fileStream = Writable.toWeb( - createWriteStream(path, { - autoClose: true, - emitClose: true, - flush: true - }) - ); - await (await fetch(face.src)).body.pipeTo(fileStream); + await downloadFont(face.src, filePath); fontMap.push({ name: font.name, locale: font.locale, weight: face.weight, - path + path: filePath }); } writeFileSync("fonts/fonts.json", JSON.stringify(fontMap, null, 2)); + // Download OpenSans fonts into app/assets/fonts/ + const assetsFontsDir = path.join(__dirname, "../app/assets/fonts"); + await mkdir(assetsFontsDir, { recursive: true }); + + const openSansCss = await ( + await fetch( + constructURL({ families: { "Open Sans": [400, 600] } }), + { + headers: { + // Make sure it returns TTF. + "User-Agent": + "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_8; de-at) AppleWebKit/533.21.1 (KHTML, like Gecko) Version/5.0.5 Safari/533.21.1" + } + } + ) + ).text(); + + const openSansFaces = openSansCss + .split("@font-face") + .filter(Boolean) + .map((fontFace) => { + const src = fontFace.match( + /src: url\((.+)\) format\('(opentype|truetype)'\)/ + ); + const weight = fontFace.match(/font-weight: (\d+)/); + if (!src || !weight) return null; + return { src: src[1], weight: weight[1] }; + }) + .filter(Boolean); + + for (const openSansFont of OPEN_SANS_FONTS) { + const face = openSansFaces.find( + (f) => f.weight === String(openSansFont.weight) + ); + if (!face) { + console.log( + `Could not find OpenSans font face for weight ${openSansFont.weight}. The Google Fonts API response may have changed format or a network error occurred.` + ); + continue; + } + const destPath = path.join(assetsFontsDir, openSansFont.filename); + if (existsSync(destPath)) { + console.log(`OpenSans ${openSansFont.weight} already exists, skipping`); + continue; + } + console.log(`Downloading OpenSans ${openSansFont.weight}`); + await downloadFont(face.src, destPath); + } + console.log("Done"); }