From 630277b89631f84be51f37e596a990c05984640e Mon Sep 17 00:00:00 2001 From: Danila Poyarkov Date: Wed, 4 Mar 2026 09:32:45 +0300 Subject: [PATCH] Add Google Fonts fallback via Developer API Fetch TTF files from Google Fonts when local and bundled fonts are unavailable. Uses the Webfonts API for direct TTF URLs per variant. Font file listings are cached per family; failed lookups are recorded to avoid retries. --- packages/core/src/fonts.ts | 64 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/packages/core/src/fonts.ts b/packages/core/src/fonts.ts index 6c45b94a7..8c2c58580 100644 --- a/packages/core/src/fonts.ts +++ b/packages/core/src/fonts.ts @@ -50,6 +50,55 @@ const BUNDLED_FONTS: Record = { 'Inter|Regular': '/Inter-Regular.ttf' } +const GOOGLE_FONTS_API_KEY = 'AIzaSyD1tYDR_dUEiV-Tw1vksEhZbUytgKW5pc8' + +const googleFontsCache = new Map>() +const googleFontsFailed = new Set() + +async function fetchGoogleFontFiles(family: string): Promise | null> { + if (googleFontsCache.has(family)) return googleFontsCache.get(family)! + if (googleFontsFailed.has(family)) return null + + const url = `https://www.googleapis.com/webfonts/v1/webfonts?family=${encodeURIComponent(family)}&key=${GOOGLE_FONTS_API_KEY}` + const response = await fetch(url) + if (!response.ok) { + googleFontsFailed.add(family) + return null + } + + const data = (await response.json()) as { items?: Array<{ files?: Record }> } + const files = data.items?.[0]?.files + if (!files) { + googleFontsFailed.add(family) + return null + } + + googleFontsCache.set(family, files) + return files +} + +function styleToVariant(style: string): string { + const weight = styleToWeight(style) + const italic = style.toLowerCase().includes('italic') + if (weight === 400 && !italic) return 'regular' + if (weight === 400 && italic) return 'italic' + return italic ? `${weight}italic` : `${weight}` +} + +async function fetchGoogleFont(family: string, style: string): Promise { + const files = await fetchGoogleFontFiles(family) + if (!files) return null + + const variant = styleToVariant(style) + const ttfUrl = files[variant] ?? files['regular'] + if (!ttfUrl) return null + + const response = await fetch(ttfUrl) + if (!response.ok) return null + + return response.arrayBuffer() +} + export async function loadFont(family: string, style = 'Regular'): Promise { const cacheKey = `${family}|${style}` if (loadedFamilies.has(cacheKey)) { @@ -80,6 +129,21 @@ export async function loadFont(family: string, style = 'Regular'): Promise