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.
This commit is contained in:
Danila Poyarkov 2026-03-04 09:32:45 +03:00
parent 68cfba3b6d
commit 630277b896

View file

@ -50,6 +50,55 @@ const BUNDLED_FONTS: Record<string, string> = {
'Inter|Regular': '/Inter-Regular.ttf'
}
const GOOGLE_FONTS_API_KEY = 'AIzaSyD1tYDR_dUEiV-Tw1vksEhZbUytgKW5pc8'
const googleFontsCache = new Map<string, Record<string, string>>()
const googleFontsFailed = new Set<string>()
async function fetchGoogleFontFiles(family: string): Promise<Record<string, string> | 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<string, string> }> }
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<ArrayBuffer | null> {
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<ArrayBuffer | null> {
const cacheKey = `${family}|${style}`
if (loadedFamilies.has(cacheKey)) {
@ -80,6 +129,21 @@ export async function loadFont(family: string, style = 'Regular'): Promise<Array
}
}
// Try Google Fonts
if (typeof fetch !== 'undefined') {
try {
const buffer = await fetchGoogleFont(family, style)
if (buffer) {
loadedFamilies.set(cacheKey, buffer)
registerFontInCanvasKit(family, buffer)
registerFontInBrowser(family, style, buffer)
return buffer
}
} catch {
/* fall through to bundled */
}
}
// Fall back to bundled font
const bundledUrl = BUNDLED_FONTS[cacheKey]
if (bundledUrl) {