// Regenerates src/panel/fonts/googleFontsCatalog.json, the font browser's offline list of Google // Fonts families (name, category, weight axis or static weights, popularity rank). Development-time // only: an app never fetches this list; it downloads font files natively when the owner installs // one (fonts.googleapis.com / fonts.gstatic.com, see waybetter-host gfonts.rs and fonts.rs). // // node packages/shell/scripts/google-fonts-catalog.mjs [metadata.json] // // Without an argument it reads https://fonts.google.com/metadata/fonts. Only families with a Latin // subset and a name the native side accepts (letters, digits, single spaces) are kept. import { readFileSync, writeFileSync } from 'node:fs'; const CATEGORY = { 'Sans Serif': 'sans', Serif: 'serif', Display: 'display', Handwriting: 'handwriting', Monospace: 'mono' }; const NAME = /^[A-Za-z0-9]+( [A-Za-z0-9]+)*$/; const text = process.argv[2] ? readFileSync(process.argv[2], 'utf8') : await (await fetch('https://fonts.google.com/metadata/fonts')).text(); const metadata = JSON.parse(text.replace(/^\)\]\}'\s*/, '')); const families = metadata.familyMetadataList .filter(font => font.subsets?.includes('latin') && NAME.test(font.family) && font.family.length <= 48 && CATEGORY[font.category]) .sort((a, b) => a.popularity - b.popularity) .map((font, rank) => { const axis = font.axes?.find(item => item.tag === 'wght'); const weights = Object.keys(font.fonts ?? {}).filter(key => /^\d+$/.test(key)).map(Number).sort((a, b) => a - b); return [font.family, CATEGORY[font.category], axis ? [axis.min, axis.max] : null, weights, rank + 1]; }); const out = { source: 'https://fonts.google.com/metadata/fonts', generated: new Date().toISOString().slice(0, 10), fields: ['family', 'category', 'wghtAxis', 'weights', 'rank'], families, }; writeFileSync(new URL('../src/panel/fonts/googleFontsCatalog.json', import.meta.url), `${JSON.stringify(out)}\n`); console.log(`${families.length} families`);