From 14bb3756745f8660e2997a4e48f09277782d6e87 Mon Sep 17 00:00:00 2001 From: Codex Agent Date: Fri, 23 Jan 2026 21:46:09 +0100 Subject: [PATCH] Add from-disk rebuild for font manifest --- app/Console/Commands/SyncGoogleFonts.php | 217 ++- public/fonts/google/fonts.css | 970 +++++++++++-- public/fonts/google/manifest.json | 1194 +++++++++++++++-- tests/Feature/Console/SyncGoogleFontsTest.php | 46 + 4 files changed, 2224 insertions(+), 203 deletions(-) diff --git a/app/Console/Commands/SyncGoogleFonts.php b/app/Console/Commands/SyncGoogleFonts.php index 75ce069..4f8f9b4 100644 --- a/app/Console/Commands/SyncGoogleFonts.php +++ b/app/Console/Commands/SyncGoogleFonts.php @@ -12,7 +12,7 @@ use Illuminate\Support\Str; class SyncGoogleFonts extends Command { - protected $signature = 'fonts:sync-google {--count=50 : Number of popular fonts to fetch} {--weights=400,700 : Comma separated numeric font weights to download} {--italic : Also download italic variants where available} {--force : Re-download files even if they exist} {--path= : Optional custom output directory (defaults to public/fonts/google)} {--family= : Download specific family name(s), comma separated (case-insensitive)} {--category= : Filter by category, comma separated (e.g. sans-serif,serif)} {--prune : Remove local font families not included in this sync} {--dry-run : Show what would be downloaded without writing files}'; + protected $signature = 'fonts:sync-google {--count=50 : Number of popular fonts to fetch} {--weights=400,700 : Comma separated numeric font weights to download} {--italic : Also download italic variants where available} {--force : Re-download files even if they exist} {--path= : Optional custom output directory (defaults to public/fonts/google)} {--family= : Download specific family name(s), comma separated (case-insensitive)} {--category= : Filter by category, comma separated (e.g. sans-serif,serif)} {--prune : Remove local font families not included in this sync} {--dry-run : Show what would be downloaded without writing files} {--from-disk : Rebuild manifest + CSS from existing font files without downloading}'; protected $description = 'Download the most popular Google Fonts to the local public/fonts/google directory and generate a manifest + CSS file.'; @@ -20,6 +20,17 @@ class SyncGoogleFonts extends Command public function handle(): int { + $dryRun = (bool) $this->option('dry-run'); + $fromDisk = (bool) $this->option('from-disk'); + $pathOption = $this->option('path'); + $basePath = $pathOption + ? (Str::startsWith($pathOption, DIRECTORY_SEPARATOR) ? $pathOption : base_path($pathOption)) + : public_path('fonts/google'); + + if ($fromDisk) { + return $this->syncFromDisk($basePath, $dryRun); + } + $apiKey = config('services.google_fonts.key'); if (! $apiKey) { @@ -32,16 +43,10 @@ class SyncGoogleFonts extends Command $weights = $this->prepareWeights($this->option('weights')); $includeItalic = (bool) $this->option('italic'); $force = (bool) $this->option('force'); - $dryRun = (bool) $this->option('dry-run'); $families = $this->normalizeFamilyOption($this->option('family')); $categories = $this->prepareCategories($this->option('category')); $prune = (bool) $this->option('prune'); - $pathOption = $this->option('path'); - $basePath = $pathOption - ? (Str::startsWith($pathOption, DIRECTORY_SEPARATOR) ? $pathOption : base_path($pathOption)) - : public_path('fonts/google'); - if (count($families)) { $label = count($families) > 1 ? 'families' : 'family'; $this->info(sprintf('Fetching Google Font %s "%s" (weights: %s, italic: %s)...', $label, implode(', ', $families), implode(', ', $weights), $includeItalic ? 'yes' : 'no')); @@ -206,6 +211,204 @@ class SyncGoogleFonts extends Command return self::SUCCESS; } + private function syncFromDisk(string $basePath, bool $dryRun): int + { + if (! File::isDirectory($basePath)) { + $this->error(sprintf('Font directory not found: %s', $basePath)); + + return self::FAILURE; + } + + if ($this->option('prune')) { + $this->warn('Ignoring --prune when rebuilding from disk.'); + } + + $fonts = $this->buildManifestFromDisk($basePath); + + if (! count($fonts)) { + $this->warn('No fonts found on disk.'); + } + + if ($dryRun) { + $this->info(sprintf('Dry run complete: %d font families would be written to %s', count($fonts), $basePath)); + + return self::SUCCESS; + } + + $this->writeManifest($basePath, $fonts); + $this->writeCss($basePath, $fonts); + Cache::forget('fonts:manifest'); + + $this->info(sprintf('Rebuilt manifest for %d font families from %s', count($fonts), $basePath)); + + return self::SUCCESS; + } + + /** + * @return array> + */ + private function buildManifestFromDisk(string $basePath): array + { + $directories = File::directories($basePath); + $fonts = []; + + foreach ($directories as $dir) { + $slug = basename($dir); + $files = collect(File::files($dir)) + ->filter(function (\SplFileInfo $file) { + $extension = strtolower($file->getExtension()); + + return in_array($extension, ['woff2', 'woff', 'otf', 'ttf'], true); + }) + ->values(); + + if (! $files->count()) { + continue; + } + + $variantsByKey = []; + foreach ($files as $file) { + $filename = $file->getFilename(); + $extension = strtolower($file->getExtension()); + $style = $this->extractStyleFromFilename($filename); + $weight = $this->extractWeightFromFilename($filename); + $variantKey = $this->buildVariantKey($weight, $style); + $priority = $this->extensionPriority($extension); + $relativePath = sprintf('/fonts/google/%s/%s', $slug, $filename); + + $existing = $variantsByKey[$variantKey] ?? null; + if ($existing && ($existing['priority'] ?? 0) >= $priority) { + continue; + } + + $variantsByKey[$variantKey] = [ + 'variant' => $variantKey, + 'weight' => $weight, + 'style' => $style, + 'url' => $relativePath, + 'priority' => $priority, + ]; + } + + if (! count($variantsByKey)) { + continue; + } + + $variants = array_values(array_map(function (array $variant) { + unset($variant['priority']); + + return $variant; + }, $variantsByKey)); + + usort($variants, function (array $left, array $right) { + $weightCompare = ($left['weight'] ?? 400) <=> ($right['weight'] ?? 400); + if ($weightCompare !== 0) { + return $weightCompare; + } + + return strcmp((string) ($left['style'] ?? 'normal'), (string) ($right['style'] ?? 'normal')); + }); + + $fonts[] = [ + 'family' => $this->familyFromSlug($slug), + 'slug' => $slug, + 'category' => null, + 'variants' => $variants, + ]; + } + + usort($fonts, fn (array $left, array $right) => strcmp((string) $left['family'], (string) $right['family'])); + + return $fonts; + } + + private function familyFromSlug(string $slug): string + { + $parts = array_filter(explode('-', $slug), fn ($part) => $part !== ''); + + $words = array_map(function (string $part) { + if (is_numeric($part)) { + return $part; + } + + if (strlen($part) <= 3) { + return strtoupper($part); + } + + return ucfirst(strtolower($part)); + }, $parts); + + return trim(implode(' ', $words)); + } + + private function extractStyleFromFilename(string $filename): string + { + $lower = strtolower($filename); + + return str_contains($lower, 'italic') || str_contains($lower, 'oblique') ? 'italic' : 'normal'; + } + + private function extractWeightFromFilename(string $filename): int + { + if (preg_match('/(?:^|[^0-9])(100|200|300|400|500|600|700|800|900)(?:[^0-9]|$)/', $filename, $matches)) { + return (int) $matches[1]; + } + + $lower = strtolower($filename); + $weightMap = [ + 'thin' => 100, + 'extralight' => 200, + 'ultralight' => 200, + 'light' => 300, + 'regular' => 400, + 'book' => 400, + 'medium' => 500, + 'semibold' => 600, + 'demibold' => 600, + 'bold' => 700, + 'extrabold' => 800, + 'ultrabold' => 800, + 'black' => 900, + 'heavy' => 900, + ]; + + foreach ($weightMap as $label => $weight) { + if (str_contains($lower, $label)) { + return $weight; + } + } + + return 400; + } + + private function buildVariantKey(int $weight, string $style): string + { + if ($weight === 400 && $style === 'normal') { + return 'regular'; + } + + if ($weight === 400 && $style === 'italic') { + return 'italic'; + } + + if ($style === 'italic') { + return $weight.'italic'; + } + + return (string) $weight; + } + + private function extensionPriority(string $extension): int + { + return match ($extension) { + 'woff2' => 4, + 'woff' => 3, + 'otf' => 2, + 'ttf' => 1, + default => 0, + }; + } + /** * @return array */ diff --git a/public/fonts/google/fonts.css b/public/fonts/google/fonts.css index 5bb41e3..41741a5 100644 --- a/public/fonts/google/fonts.css +++ b/public/fonts/google/fonts.css @@ -1,106 +1,322 @@ /* Auto-generated by fonts:sync-google */ @font-face { - font-family: 'Manifest Font'; + font-family: 'Archivo'; font-style: normal; font-weight: 400; font-display: swap; - src: url('/fonts/google/manifest-font/regular.woff2') format('woff2'); + src: url('/fonts/google/archivo/Archivo-400-normal.ttf') format('truetype'); } @font-face { - font-family: 'Plus Jakarta Sans'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url('/fonts/google/plus-jakarta-sans/PlusJakartaSans-400-normal.ttf') format('truetype'); -} - -@font-face { - font-family: 'Plus Jakarta Sans'; - font-style: italic; - font-weight: 400; - font-display: swap; - src: url('/fonts/google/plus-jakarta-sans/PlusJakartaSans-400-italic.ttf') format('truetype'); -} - -@font-face { - font-family: 'Plus Jakarta Sans'; - font-style: normal; - font-weight: 500; - font-display: swap; - src: url('/fonts/google/plus-jakarta-sans/PlusJakartaSans-500-normal.ttf') format('truetype'); -} - -@font-face { - font-family: 'Plus Jakarta Sans'; - font-style: italic; - font-weight: 500; - font-display: swap; - src: url('/fonts/google/plus-jakarta-sans/PlusJakartaSans-500-italic.ttf') format('truetype'); -} - -@font-face { - font-family: 'Plus Jakarta Sans'; - font-style: normal; - font-weight: 600; - font-display: swap; - src: url('/fonts/google/plus-jakarta-sans/PlusJakartaSans-600-normal.ttf') format('truetype'); -} - -@font-face { - font-family: 'Plus Jakarta Sans'; - font-style: italic; - font-weight: 600; - font-display: swap; - src: url('/fonts/google/plus-jakarta-sans/PlusJakartaSans-600-italic.ttf') format('truetype'); -} - -@font-face { - font-family: 'Plus Jakarta Sans'; + font-family: 'Archivo'; font-style: normal; font-weight: 700; font-display: swap; - src: url('/fonts/google/plus-jakarta-sans/PlusJakartaSans-700-normal.ttf') format('truetype'); + src: url('/fonts/google/archivo/Archivo-700-normal.ttf') format('truetype'); } @font-face { - font-family: 'Plus Jakarta Sans'; - font-style: italic; - font-weight: 700; - font-display: swap; - src: url('/fonts/google/plus-jakarta-sans/PlusJakartaSans-700-italic.ttf') format('truetype'); -} - -@font-face { - font-family: 'Space Grotesk'; + font-family: 'Archivo Black'; font-style: normal; font-weight: 400; font-display: swap; - src: url('/fonts/google/space-grotesk/SpaceGrotesk-400-normal.ttf') format('truetype'); + src: url('/fonts/google/archivo-black/ArchivoBlack-400-normal.ttf') format('truetype'); } @font-face { - font-family: 'Space Grotesk'; + font-family: 'Arimo'; font-style: normal; - font-weight: 500; + font-weight: 400; font-display: swap; - src: url('/fonts/google/space-grotesk/SpaceGrotesk-500-normal.ttf') format('truetype'); + src: url('/fonts/google/arimo/Arimo-400-normal.ttf') format('truetype'); } @font-face { - font-family: 'Space Grotesk'; - font-style: normal; - font-weight: 600; - font-display: swap; - src: url('/fonts/google/space-grotesk/SpaceGrotesk-600-normal.ttf') format('truetype'); -} - -@font-face { - font-family: 'Space Grotesk'; + font-family: 'Arimo'; font-style: normal; font-weight: 700; font-display: swap; - src: url('/fonts/google/space-grotesk/SpaceGrotesk-700-normal.ttf') format('truetype'); + src: url('/fonts/google/arimo/Arimo-700-normal.ttf') format('truetype'); +} + +@font-face { + font-family: 'Barlow'; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url('/fonts/google/barlow/Barlow-400-normal.ttf') format('truetype'); +} + +@font-face { + font-family: 'Barlow'; + font-style: normal; + font-weight: 700; + font-display: swap; + src: url('/fonts/google/barlow/Barlow-700-normal.ttf') format('truetype'); +} + +@font-face { + font-family: 'Bebas Neue'; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url('/fonts/google/bebas-neue/BebasNeue-400-normal.ttf') format('truetype'); +} + +@font-face { + font-family: 'DM Sans'; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url('/fonts/google/dm-sans/DmSans-400-normal.ttf') format('truetype'); +} + +@font-face { + font-family: 'DM Sans'; + font-style: normal; + font-weight: 700; + font-display: swap; + src: url('/fonts/google/dm-sans/DmSans-700-normal.ttf') format('truetype'); +} + +@font-face { + font-family: 'Figtree'; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url('/fonts/google/figtree/Figtree-400-normal.ttf') format('truetype'); +} + +@font-face { + font-family: 'Figtree'; + font-style: normal; + font-weight: 700; + font-display: swap; + src: url('/fonts/google/figtree/Figtree-700-normal.ttf') format('truetype'); +} + +@font-face { + font-family: 'Fira Sans'; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url('/fonts/google/fira-sans/FiraSans-400-normal.ttf') format('truetype'); +} + +@font-face { + font-family: 'Fira Sans'; + font-style: normal; + font-weight: 700; + font-display: swap; + src: url('/fonts/google/fira-sans/FiraSans-700-normal.ttf') format('truetype'); +} + +@font-face { + font-family: 'Fraunces'; + font-style: italic; + font-weight: 400; + font-display: swap; + src: url('/fonts/google/fraunces/Fraunces-400-italic.ttf') format('truetype'); +} + +@font-face { + font-family: 'Fraunces'; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url('/fonts/google/fraunces/Fraunces-400-normal.ttf') format('truetype'); +} + +@font-face { + font-family: 'Fraunces'; + font-style: italic; + font-weight: 500; + font-display: swap; + src: url('/fonts/google/fraunces/Fraunces-500-italic.ttf') format('truetype'); +} + +@font-face { + font-family: 'Fraunces'; + font-style: normal; + font-weight: 500; + font-display: swap; + src: url('/fonts/google/fraunces/Fraunces-500-normal.ttf') format('truetype'); +} + +@font-face { + font-family: 'Fraunces'; + font-style: italic; + font-weight: 600; + font-display: swap; + src: url('/fonts/google/fraunces/Fraunces-600-italic.ttf') format('truetype'); +} + +@font-face { + font-family: 'Fraunces'; + font-style: normal; + font-weight: 600; + font-display: swap; + src: url('/fonts/google/fraunces/Fraunces-600-normal.ttf') format('truetype'); +} + +@font-face { + font-family: 'Fraunces'; + font-style: italic; + font-weight: 700; + font-display: swap; + src: url('/fonts/google/fraunces/Fraunces-700-italic.ttf') format('truetype'); +} + +@font-face { + font-family: 'Fraunces'; + font-style: normal; + font-weight: 700; + font-display: swap; + src: url('/fonts/google/fraunces/Fraunces-700-normal.ttf') format('truetype'); +} + +@font-face { + font-family: 'Heebo'; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url('/fonts/google/heebo/Heebo-400-normal.ttf') format('truetype'); +} + +@font-face { + font-family: 'Heebo'; + font-style: normal; + font-weight: 700; + font-display: swap; + src: url('/fonts/google/heebo/Heebo-700-normal.ttf') format('truetype'); +} + +@font-face { + font-family: 'IBM Plex Sans'; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url('/fonts/google/ibm-plex-sans/IbmPlexSans-400-normal.ttf') format('truetype'); +} + +@font-face { + font-family: 'IBM Plex Sans'; + font-style: normal; + font-weight: 700; + font-display: swap; + src: url('/fonts/google/ibm-plex-sans/IbmPlexSans-700-normal.ttf') format('truetype'); +} + +@font-face { + font-family: 'Inconsolata'; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url('/fonts/google/inconsolata/Inconsolata-400-normal.ttf') format('truetype'); +} + +@font-face { + font-family: 'Inconsolata'; + font-style: normal; + font-weight: 700; + font-display: swap; + src: url('/fonts/google/inconsolata/Inconsolata-700-normal.ttf') format('truetype'); +} + +@font-face { + font-family: 'Inter'; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url('/fonts/google/inter/Inter-400-normal.ttf') format('truetype'); +} + +@font-face { + font-family: 'Inter'; + font-style: normal; + font-weight: 700; + font-display: swap; + src: url('/fonts/google/inter/Inter-700-normal.ttf') format('truetype'); +} + +@font-face { + font-family: 'Jost'; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url('/fonts/google/jost/Jost-400-normal.ttf') format('truetype'); +} + +@font-face { + font-family: 'Jost'; + font-style: normal; + font-weight: 700; + font-display: swap; + src: url('/fonts/google/jost/Jost-700-normal.ttf') format('truetype'); +} + +@font-face { + font-family: 'Kanit'; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url('/fonts/google/kanit/Kanit-400-normal.ttf') format('truetype'); +} + +@font-face { + font-family: 'Kanit'; + font-style: normal; + font-weight: 700; + font-display: swap; + src: url('/fonts/google/kanit/Kanit-700-normal.ttf') format('truetype'); +} + +@font-face { + font-family: 'Karla'; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url('/fonts/google/karla/Karla-400-normal.ttf') format('truetype'); +} + +@font-face { + font-family: 'Karla'; + font-style: normal; + font-weight: 700; + font-display: swap; + src: url('/fonts/google/karla/Karla-700-normal.ttf') format('truetype'); +} + +@font-face { + font-family: 'Lato'; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url('/fonts/google/lato/Lato-400-normal.ttf') format('truetype'); +} + +@font-face { + font-family: 'Lato'; + font-style: normal; + font-weight: 700; + font-display: swap; + src: url('/fonts/google/lato/Lato-700-normal.ttf') format('truetype'); +} + +@font-face { + font-family: 'Lora'; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url('/fonts/google/lora/Lora-400-normal.ttf') format('truetype'); +} + +@font-face { + font-family: 'Lora'; + font-style: normal; + font-weight: 700; + font-display: swap; + src: url('/fonts/google/lora/Lora-700-normal.ttf') format('truetype'); } @font-face { @@ -136,9 +352,609 @@ } @font-face { - font-family: 'Archivo Black'; + font-family: 'Material Icons'; font-style: normal; font-weight: 400; font-display: swap; - src: url('/fonts/google/archivo-black/ArchivoBlack-400-normal.ttf') format('truetype'); + src: url('/fonts/google/material-icons/MaterialIcons-400-normal.ttf') format('truetype'); +} + +@font-face { + font-family: 'Material Icons Outlined'; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url('/fonts/google/material-icons-outlined/MaterialIconsOutlined-400-normal.otf') format('opentype'); +} + +@font-face { + font-family: 'Material Symbols Outlined'; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url('/fonts/google/material-symbols-outlined/MaterialSymbolsOutlined-400-normal.ttf') format('truetype'); +} + +@font-face { + font-family: 'Material Symbols Outlined'; + font-style: normal; + font-weight: 700; + font-display: swap; + src: url('/fonts/google/material-symbols-outlined/MaterialSymbolsOutlined-700-normal.ttf') format('truetype'); +} + +@font-face { + font-family: 'Merriweather'; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url('/fonts/google/merriweather/Merriweather-400-normal.ttf') format('truetype'); +} + +@font-face { + font-family: 'Merriweather'; + font-style: normal; + font-weight: 700; + font-display: swap; + src: url('/fonts/google/merriweather/Merriweather-700-normal.ttf') format('truetype'); +} + +@font-face { + font-family: 'Montserrat'; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url('/fonts/google/montserrat/Montserrat-400-normal.ttf') format('truetype'); +} + +@font-face { + font-family: 'Montserrat'; + font-style: normal; + font-weight: 700; + font-display: swap; + src: url('/fonts/google/montserrat/Montserrat-700-normal.ttf') format('truetype'); +} + +@font-face { + font-family: 'Mulish'; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url('/fonts/google/mulish/Mulish-400-normal.ttf') format('truetype'); +} + +@font-face { + font-family: 'Mulish'; + font-style: normal; + font-weight: 700; + font-display: swap; + src: url('/fonts/google/mulish/Mulish-700-normal.ttf') format('truetype'); +} + +@font-face { + font-family: 'Noto Sans'; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url('/fonts/google/noto-sans/NotoSans-400-normal.ttf') format('truetype'); +} + +@font-face { + font-family: 'Noto Sans'; + font-style: normal; + font-weight: 700; + font-display: swap; + src: url('/fonts/google/noto-sans/NotoSans-700-normal.ttf') format('truetype'); +} + +@font-face { + font-family: 'Noto Sans JP'; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url('/fonts/google/noto-sans-jp/NotoSansJp-400-normal.ttf') format('truetype'); +} + +@font-face { + font-family: 'Noto Sans JP'; + font-style: normal; + font-weight: 700; + font-display: swap; + src: url('/fonts/google/noto-sans-jp/NotoSansJp-700-normal.ttf') format('truetype'); +} + +@font-face { + font-family: 'Noto Sans KR'; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url('/fonts/google/noto-sans-kr/NotoSansKr-400-normal.ttf') format('truetype'); +} + +@font-face { + font-family: 'Noto Sans KR'; + font-style: normal; + font-weight: 700; + font-display: swap; + src: url('/fonts/google/noto-sans-kr/NotoSansKr-700-normal.ttf') format('truetype'); +} + +@font-face { + font-family: 'Noto Sans TC'; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url('/fonts/google/noto-sans-tc/NotoSansTc-400-normal.ttf') format('truetype'); +} + +@font-face { + font-family: 'Noto Sans TC'; + font-style: normal; + font-weight: 700; + font-display: swap; + src: url('/fonts/google/noto-sans-tc/NotoSansTc-700-normal.ttf') format('truetype'); +} + +@font-face { + font-family: 'Noto Serif'; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url('/fonts/google/noto-serif/NotoSerif-400-normal.ttf') format('truetype'); +} + +@font-face { + font-family: 'Noto Serif'; + font-style: normal; + font-weight: 700; + font-display: swap; + src: url('/fonts/google/noto-serif/NotoSerif-700-normal.ttf') format('truetype'); +} + +@font-face { + font-family: 'Nunito'; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url('/fonts/google/nunito/Nunito-400-normal.ttf') format('truetype'); +} + +@font-face { + font-family: 'Nunito'; + font-style: normal; + font-weight: 700; + font-display: swap; + src: url('/fonts/google/nunito/Nunito-700-normal.ttf') format('truetype'); +} + +@font-face { + font-family: 'Nunito Sans'; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url('/fonts/google/nunito-sans/NunitoSans-400-normal.ttf') format('truetype'); +} + +@font-face { + font-family: 'Nunito Sans'; + font-style: normal; + font-weight: 700; + font-display: swap; + src: url('/fonts/google/nunito-sans/NunitoSans-700-normal.ttf') format('truetype'); +} + +@font-face { + font-family: 'Open Sans'; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url('/fonts/google/open-sans/OpenSans-400-normal.ttf') format('truetype'); +} + +@font-face { + font-family: 'Open Sans'; + font-style: normal; + font-weight: 700; + font-display: swap; + src: url('/fonts/google/open-sans/OpenSans-700-normal.ttf') format('truetype'); +} + +@font-face { + font-family: 'Oswald'; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url('/fonts/google/oswald/Oswald-400-normal.ttf') format('truetype'); +} + +@font-face { + font-family: 'Oswald'; + font-style: normal; + font-weight: 700; + font-display: swap; + src: url('/fonts/google/oswald/Oswald-700-normal.ttf') format('truetype'); +} + +@font-face { + font-family: 'Outfit'; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url('/fonts/google/outfit/Outfit-400-normal.ttf') format('truetype'); +} + +@font-face { + font-family: 'Outfit'; + font-style: normal; + font-weight: 700; + font-display: swap; + src: url('/fonts/google/outfit/Outfit-700-normal.ttf') format('truetype'); +} + +@font-face { + font-family: 'PT Sans'; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url('/fonts/google/pt-sans/PtSans-400-normal.ttf') format('truetype'); +} + +@font-face { + font-family: 'PT Sans'; + font-style: normal; + font-weight: 700; + font-display: swap; + src: url('/fonts/google/pt-sans/PtSans-700-normal.ttf') format('truetype'); +} + +@font-face { + font-family: 'PT Serif'; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url('/fonts/google/pt-serif/PtSerif-400-normal.ttf') format('truetype'); +} + +@font-face { + font-family: 'PT Serif'; + font-style: normal; + font-weight: 700; + font-display: swap; + src: url('/fonts/google/pt-serif/PtSerif-700-normal.ttf') format('truetype'); +} + +@font-face { + font-family: 'Playfair Display'; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url('/fonts/google/playfair-display/PlayfairDisplay-400-normal.ttf') format('truetype'); +} + +@font-face { + font-family: 'Playfair Display'; + font-style: normal; + font-weight: 700; + font-display: swap; + src: url('/fonts/google/playfair-display/PlayfairDisplay-700-normal.ttf') format('truetype'); +} + +@font-face { + font-family: 'Plus Jakarta Sans'; + font-style: italic; + font-weight: 400; + font-display: swap; + src: url('/fonts/google/plus-jakarta-sans/PlusJakartaSans-400-italic.ttf') format('truetype'); +} + +@font-face { + font-family: 'Plus Jakarta Sans'; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url('/fonts/google/plus-jakarta-sans/PlusJakartaSans-400-normal.ttf') format('truetype'); +} + +@font-face { + font-family: 'Plus Jakarta Sans'; + font-style: italic; + font-weight: 500; + font-display: swap; + src: url('/fonts/google/plus-jakarta-sans/PlusJakartaSans-500-italic.ttf') format('truetype'); +} + +@font-face { + font-family: 'Plus Jakarta Sans'; + font-style: normal; + font-weight: 500; + font-display: swap; + src: url('/fonts/google/plus-jakarta-sans/PlusJakartaSans-500-normal.ttf') format('truetype'); +} + +@font-face { + font-family: 'Plus Jakarta Sans'; + font-style: italic; + font-weight: 600; + font-display: swap; + src: url('/fonts/google/plus-jakarta-sans/PlusJakartaSans-600-italic.ttf') format('truetype'); +} + +@font-face { + font-family: 'Plus Jakarta Sans'; + font-style: normal; + font-weight: 600; + font-display: swap; + src: url('/fonts/google/plus-jakarta-sans/PlusJakartaSans-600-normal.ttf') format('truetype'); +} + +@font-face { + font-family: 'Plus Jakarta Sans'; + font-style: italic; + font-weight: 700; + font-display: swap; + src: url('/fonts/google/plus-jakarta-sans/PlusJakartaSans-700-italic.ttf') format('truetype'); +} + +@font-face { + font-family: 'Plus Jakarta Sans'; + font-style: normal; + font-weight: 700; + font-display: swap; + src: url('/fonts/google/plus-jakarta-sans/PlusJakartaSans-700-normal.ttf') format('truetype'); +} + +@font-face { + font-family: 'Poppins'; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url('/fonts/google/poppins/Poppins-400-normal.ttf') format('truetype'); +} + +@font-face { + font-family: 'Poppins'; + font-style: normal; + font-weight: 700; + font-display: swap; + src: url('/fonts/google/poppins/Poppins-700-normal.ttf') format('truetype'); +} + +@font-face { + font-family: 'Prompt'; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url('/fonts/google/prompt/Prompt-400-normal.ttf') format('truetype'); +} + +@font-face { + font-family: 'Prompt'; + font-style: normal; + font-weight: 700; + font-display: swap; + src: url('/fonts/google/prompt/Prompt-700-normal.ttf') format('truetype'); +} + +@font-face { + font-family: 'Quicksand'; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url('/fonts/google/quicksand/Quicksand-400-normal.ttf') format('truetype'); +} + +@font-face { + font-family: 'Quicksand'; + font-style: normal; + font-weight: 700; + font-display: swap; + src: url('/fonts/google/quicksand/Quicksand-700-normal.ttf') format('truetype'); +} + +@font-face { + font-family: 'Raleway'; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url('/fonts/google/raleway/Raleway-400-normal.ttf') format('truetype'); +} + +@font-face { + font-family: 'Raleway'; + font-style: normal; + font-weight: 700; + font-display: swap; + src: url('/fonts/google/raleway/Raleway-700-normal.ttf') format('truetype'); +} + +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url('/fonts/google/roboto/Roboto-400-normal.ttf') format('truetype'); +} + +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 700; + font-display: swap; + src: url('/fonts/google/roboto/Roboto-700-normal.ttf') format('truetype'); +} + +@font-face { + font-family: 'Roboto Condensed'; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url('/fonts/google/roboto-condensed/RobotoCondensed-400-normal.ttf') format('truetype'); +} + +@font-face { + font-family: 'Roboto Condensed'; + font-style: normal; + font-weight: 700; + font-display: swap; + src: url('/fonts/google/roboto-condensed/RobotoCondensed-700-normal.ttf') format('truetype'); +} + +@font-face { + font-family: 'Roboto Mono'; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url('/fonts/google/roboto-mono/RobotoMono-400-normal.ttf') format('truetype'); +} + +@font-face { + font-family: 'Roboto Mono'; + font-style: normal; + font-weight: 700; + font-display: swap; + src: url('/fonts/google/roboto-mono/RobotoMono-700-normal.ttf') format('truetype'); +} + +@font-face { + font-family: 'Roboto Slab'; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url('/fonts/google/roboto-slab/RobotoSlab-400-normal.ttf') format('truetype'); +} + +@font-face { + font-family: 'Roboto Slab'; + font-style: normal; + font-weight: 700; + font-display: swap; + src: url('/fonts/google/roboto-slab/RobotoSlab-700-normal.ttf') format('truetype'); +} + +@font-face { + font-family: 'Rubik'; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url('/fonts/google/rubik/Rubik-400-normal.ttf') format('truetype'); +} + +@font-face { + font-family: 'Rubik'; + font-style: normal; + font-weight: 700; + font-display: swap; + src: url('/fonts/google/rubik/Rubik-700-normal.ttf') format('truetype'); +} + +@font-face { + font-family: 'Saira'; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url('/fonts/google/saira/Saira-400-normal.ttf') format('truetype'); +} + +@font-face { + font-family: 'Saira'; + font-style: normal; + font-weight: 700; + font-display: swap; + src: url('/fonts/google/saira/Saira-700-normal.ttf') format('truetype'); +} + +@font-face { + font-family: 'Source Sans 3'; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url('/fonts/google/source-sans-3/SourceSans3-400-normal.ttf') format('truetype'); +} + +@font-face { + font-family: 'Source Sans 3'; + font-style: normal; + font-weight: 700; + font-display: swap; + src: url('/fonts/google/source-sans-3/SourceSans3-700-normal.ttf') format('truetype'); +} + +@font-face { + font-family: 'Space Grotesk'; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url('/fonts/google/space-grotesk/SpaceGrotesk-400-normal.ttf') format('truetype'); +} + +@font-face { + font-family: 'Space Grotesk'; + font-style: normal; + font-weight: 500; + font-display: swap; + src: url('/fonts/google/space-grotesk/SpaceGrotesk-500-normal.ttf') format('truetype'); +} + +@font-face { + font-family: 'Space Grotesk'; + font-style: normal; + font-weight: 600; + font-display: swap; + src: url('/fonts/google/space-grotesk/SpaceGrotesk-600-normal.ttf') format('truetype'); +} + +@font-face { + font-family: 'Space Grotesk'; + font-style: normal; + font-weight: 700; + font-display: swap; + src: url('/fonts/google/space-grotesk/SpaceGrotesk-700-normal.ttf') format('truetype'); +} + +@font-face { + font-family: 'Titillium WEB'; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url('/fonts/google/titillium-web/TitilliumWeb-400-normal.ttf') format('truetype'); +} + +@font-face { + font-family: 'Titillium WEB'; + font-style: normal; + font-weight: 700; + font-display: swap; + src: url('/fonts/google/titillium-web/TitilliumWeb-700-normal.ttf') format('truetype'); +} + +@font-face { + font-family: 'Ubuntu'; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url('/fonts/google/ubuntu/Ubuntu-400-normal.ttf') format('truetype'); +} + +@font-face { + font-family: 'Ubuntu'; + font-style: normal; + font-weight: 700; + font-display: swap; + src: url('/fonts/google/ubuntu/Ubuntu-700-normal.ttf') format('truetype'); +} + +@font-face { + font-family: 'Work Sans'; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url('/fonts/google/work-sans/WorkSans-400-normal.ttf') format('truetype'); +} + +@font-face { + font-family: 'Work Sans'; + font-style: normal; + font-weight: 700; + font-display: swap; + src: url('/fonts/google/work-sans/WorkSans-700-normal.ttf') format('truetype'); } diff --git a/public/fonts/google/manifest.json b/public/fonts/google/manifest.json index e3e19ad..b5b07c5 100644 --- a/public/fonts/google/manifest.json +++ b/public/fonts/google/manifest.json @@ -1,141 +1,31 @@ { - "generated_at": "2026-01-15T21:06:13+01:00", + "generated_at": "2026-01-23T21:42:36+01:00", "source": "google-webfonts", - "count": 5, + "count": 54, "fonts": [ { - "family": "Manifest Font", - "category": "sans-serif", + "family": "Archivo", + "slug": "archivo", + "category": null, "variants": [ { "variant": "regular", "weight": 400, "style": "normal", - "url": "/fonts/google/manifest-font/regular.woff2" - } - ] - }, - { - "family": "Plus Jakarta Sans", - "slug": "plus-jakarta-sans", - "category": "sans-serif", - "variants": [ - { - "variant": "regular", - "weight": 400, - "style": "normal", - "url": "/fonts/google/plus-jakarta-sans/PlusJakartaSans-400-normal.ttf" + "url": "/fonts/google/archivo/Archivo-400-normal.ttf" }, { - "variant": "italic", - "weight": 400, - "style": "italic", - "url": "/fonts/google/plus-jakarta-sans/PlusJakartaSans-400-italic.ttf" - }, - { - "variant": 500, - "weight": 500, - "style": "normal", - "url": "/fonts/google/plus-jakarta-sans/PlusJakartaSans-500-normal.ttf" - }, - { - "variant": "500italic", - "weight": 500, - "style": "italic", - "url": "/fonts/google/plus-jakarta-sans/PlusJakartaSans-500-italic.ttf" - }, - { - "variant": 600, - "weight": 600, - "style": "normal", - "url": "/fonts/google/plus-jakarta-sans/PlusJakartaSans-600-normal.ttf" - }, - { - "variant": "600italic", - "weight": 600, - "style": "italic", - "url": "/fonts/google/plus-jakarta-sans/PlusJakartaSans-600-italic.ttf" - }, - { - "variant": 700, + "variant": "700", "weight": 700, "style": "normal", - "url": "/fonts/google/plus-jakarta-sans/PlusJakartaSans-700-normal.ttf" - }, - { - "variant": "700italic", - "weight": 700, - "style": "italic", - "url": "/fonts/google/plus-jakarta-sans/PlusJakartaSans-700-italic.ttf" - } - ] - }, - { - "family": "Space Grotesk", - "slug": "space-grotesk", - "category": "sans-serif", - "variants": [ - { - "variant": "regular", - "weight": 400, - "style": "normal", - "url": "/fonts/google/space-grotesk/SpaceGrotesk-400-normal.ttf" - }, - { - "variant": 500, - "weight": 500, - "style": "normal", - "url": "/fonts/google/space-grotesk/SpaceGrotesk-500-normal.ttf" - }, - { - "variant": 600, - "weight": 600, - "style": "normal", - "url": "/fonts/google/space-grotesk/SpaceGrotesk-600-normal.ttf" - }, - { - "variant": 700, - "weight": 700, - "style": "normal", - "url": "/fonts/google/space-grotesk/SpaceGrotesk-700-normal.ttf" - } - ] - }, - { - "family": "Manrope", - "slug": "manrope", - "category": "sans-serif", - "variants": [ - { - "variant": "regular", - "weight": 400, - "style": "normal", - "url": "/fonts/google/manrope/Manrope-400-normal.ttf" - }, - { - "variant": 500, - "weight": 500, - "style": "normal", - "url": "/fonts/google/manrope/Manrope-500-normal.ttf" - }, - { - "variant": 600, - "weight": 600, - "style": "normal", - "url": "/fonts/google/manrope/Manrope-600-normal.ttf" - }, - { - "variant": 700, - "weight": 700, - "style": "normal", - "url": "/fonts/google/manrope/Manrope-700-normal.ttf" + "url": "/fonts/google/archivo/Archivo-700-normal.ttf" } ] }, { "family": "Archivo Black", "slug": "archivo-black", - "category": "sans-serif", + "category": null, "variants": [ { "variant": "regular", @@ -144,6 +34,1072 @@ "url": "/fonts/google/archivo-black/ArchivoBlack-400-normal.ttf" } ] + }, + { + "family": "Arimo", + "slug": "arimo", + "category": null, + "variants": [ + { + "variant": "regular", + "weight": 400, + "style": "normal", + "url": "/fonts/google/arimo/Arimo-400-normal.ttf" + }, + { + "variant": "700", + "weight": 700, + "style": "normal", + "url": "/fonts/google/arimo/Arimo-700-normal.ttf" + } + ] + }, + { + "family": "Barlow", + "slug": "barlow", + "category": null, + "variants": [ + { + "variant": "regular", + "weight": 400, + "style": "normal", + "url": "/fonts/google/barlow/Barlow-400-normal.ttf" + }, + { + "variant": "700", + "weight": 700, + "style": "normal", + "url": "/fonts/google/barlow/Barlow-700-normal.ttf" + } + ] + }, + { + "family": "Bebas Neue", + "slug": "bebas-neue", + "category": null, + "variants": [ + { + "variant": "regular", + "weight": 400, + "style": "normal", + "url": "/fonts/google/bebas-neue/BebasNeue-400-normal.ttf" + } + ] + }, + { + "family": "DM Sans", + "slug": "dm-sans", + "category": null, + "variants": [ + { + "variant": "regular", + "weight": 400, + "style": "normal", + "url": "/fonts/google/dm-sans/DmSans-400-normal.ttf" + }, + { + "variant": "700", + "weight": 700, + "style": "normal", + "url": "/fonts/google/dm-sans/DmSans-700-normal.ttf" + } + ] + }, + { + "family": "Figtree", + "slug": "figtree", + "category": null, + "variants": [ + { + "variant": "regular", + "weight": 400, + "style": "normal", + "url": "/fonts/google/figtree/Figtree-400-normal.ttf" + }, + { + "variant": "700", + "weight": 700, + "style": "normal", + "url": "/fonts/google/figtree/Figtree-700-normal.ttf" + } + ] + }, + { + "family": "Fira Sans", + "slug": "fira-sans", + "category": null, + "variants": [ + { + "variant": "regular", + "weight": 400, + "style": "normal", + "url": "/fonts/google/fira-sans/FiraSans-400-normal.ttf" + }, + { + "variant": "700", + "weight": 700, + "style": "normal", + "url": "/fonts/google/fira-sans/FiraSans-700-normal.ttf" + } + ] + }, + { + "family": "Fraunces", + "slug": "fraunces", + "category": null, + "variants": [ + { + "variant": "italic", + "weight": 400, + "style": "italic", + "url": "/fonts/google/fraunces/Fraunces-400-italic.ttf" + }, + { + "variant": "regular", + "weight": 400, + "style": "normal", + "url": "/fonts/google/fraunces/Fraunces-400-normal.ttf" + }, + { + "variant": "500italic", + "weight": 500, + "style": "italic", + "url": "/fonts/google/fraunces/Fraunces-500-italic.ttf" + }, + { + "variant": "500", + "weight": 500, + "style": "normal", + "url": "/fonts/google/fraunces/Fraunces-500-normal.ttf" + }, + { + "variant": "600italic", + "weight": 600, + "style": "italic", + "url": "/fonts/google/fraunces/Fraunces-600-italic.ttf" + }, + { + "variant": "600", + "weight": 600, + "style": "normal", + "url": "/fonts/google/fraunces/Fraunces-600-normal.ttf" + }, + { + "variant": "700italic", + "weight": 700, + "style": "italic", + "url": "/fonts/google/fraunces/Fraunces-700-italic.ttf" + }, + { + "variant": "700", + "weight": 700, + "style": "normal", + "url": "/fonts/google/fraunces/Fraunces-700-normal.ttf" + } + ] + }, + { + "family": "Heebo", + "slug": "heebo", + "category": null, + "variants": [ + { + "variant": "regular", + "weight": 400, + "style": "normal", + "url": "/fonts/google/heebo/Heebo-400-normal.ttf" + }, + { + "variant": "700", + "weight": 700, + "style": "normal", + "url": "/fonts/google/heebo/Heebo-700-normal.ttf" + } + ] + }, + { + "family": "IBM Plex Sans", + "slug": "ibm-plex-sans", + "category": null, + "variants": [ + { + "variant": "regular", + "weight": 400, + "style": "normal", + "url": "/fonts/google/ibm-plex-sans/IbmPlexSans-400-normal.ttf" + }, + { + "variant": "700", + "weight": 700, + "style": "normal", + "url": "/fonts/google/ibm-plex-sans/IbmPlexSans-700-normal.ttf" + } + ] + }, + { + "family": "Inconsolata", + "slug": "inconsolata", + "category": null, + "variants": [ + { + "variant": "regular", + "weight": 400, + "style": "normal", + "url": "/fonts/google/inconsolata/Inconsolata-400-normal.ttf" + }, + { + "variant": "700", + "weight": 700, + "style": "normal", + "url": "/fonts/google/inconsolata/Inconsolata-700-normal.ttf" + } + ] + }, + { + "family": "Inter", + "slug": "inter", + "category": null, + "variants": [ + { + "variant": "regular", + "weight": 400, + "style": "normal", + "url": "/fonts/google/inter/Inter-400-normal.ttf" + }, + { + "variant": "700", + "weight": 700, + "style": "normal", + "url": "/fonts/google/inter/Inter-700-normal.ttf" + } + ] + }, + { + "family": "Jost", + "slug": "jost", + "category": null, + "variants": [ + { + "variant": "regular", + "weight": 400, + "style": "normal", + "url": "/fonts/google/jost/Jost-400-normal.ttf" + }, + { + "variant": "700", + "weight": 700, + "style": "normal", + "url": "/fonts/google/jost/Jost-700-normal.ttf" + } + ] + }, + { + "family": "Kanit", + "slug": "kanit", + "category": null, + "variants": [ + { + "variant": "regular", + "weight": 400, + "style": "normal", + "url": "/fonts/google/kanit/Kanit-400-normal.ttf" + }, + { + "variant": "700", + "weight": 700, + "style": "normal", + "url": "/fonts/google/kanit/Kanit-700-normal.ttf" + } + ] + }, + { + "family": "Karla", + "slug": "karla", + "category": null, + "variants": [ + { + "variant": "regular", + "weight": 400, + "style": "normal", + "url": "/fonts/google/karla/Karla-400-normal.ttf" + }, + { + "variant": "700", + "weight": 700, + "style": "normal", + "url": "/fonts/google/karla/Karla-700-normal.ttf" + } + ] + }, + { + "family": "Lato", + "slug": "lato", + "category": null, + "variants": [ + { + "variant": "regular", + "weight": 400, + "style": "normal", + "url": "/fonts/google/lato/Lato-400-normal.ttf" + }, + { + "variant": "700", + "weight": 700, + "style": "normal", + "url": "/fonts/google/lato/Lato-700-normal.ttf" + } + ] + }, + { + "family": "Lora", + "slug": "lora", + "category": null, + "variants": [ + { + "variant": "regular", + "weight": 400, + "style": "normal", + "url": "/fonts/google/lora/Lora-400-normal.ttf" + }, + { + "variant": "700", + "weight": 700, + "style": "normal", + "url": "/fonts/google/lora/Lora-700-normal.ttf" + } + ] + }, + { + "family": "Manrope", + "slug": "manrope", + "category": null, + "variants": [ + { + "variant": "regular", + "weight": 400, + "style": "normal", + "url": "/fonts/google/manrope/Manrope-400-normal.ttf" + }, + { + "variant": "500", + "weight": 500, + "style": "normal", + "url": "/fonts/google/manrope/Manrope-500-normal.ttf" + }, + { + "variant": "600", + "weight": 600, + "style": "normal", + "url": "/fonts/google/manrope/Manrope-600-normal.ttf" + }, + { + "variant": "700", + "weight": 700, + "style": "normal", + "url": "/fonts/google/manrope/Manrope-700-normal.ttf" + } + ] + }, + { + "family": "Material Icons", + "slug": "material-icons", + "category": null, + "variants": [ + { + "variant": "regular", + "weight": 400, + "style": "normal", + "url": "/fonts/google/material-icons/MaterialIcons-400-normal.ttf" + } + ] + }, + { + "family": "Material Icons Outlined", + "slug": "material-icons-outlined", + "category": null, + "variants": [ + { + "variant": "regular", + "weight": 400, + "style": "normal", + "url": "/fonts/google/material-icons-outlined/MaterialIconsOutlined-400-normal.otf" + } + ] + }, + { + "family": "Material Symbols Outlined", + "slug": "material-symbols-outlined", + "category": null, + "variants": [ + { + "variant": "regular", + "weight": 400, + "style": "normal", + "url": "/fonts/google/material-symbols-outlined/MaterialSymbolsOutlined-400-normal.ttf" + }, + { + "variant": "700", + "weight": 700, + "style": "normal", + "url": "/fonts/google/material-symbols-outlined/MaterialSymbolsOutlined-700-normal.ttf" + } + ] + }, + { + "family": "Merriweather", + "slug": "merriweather", + "category": null, + "variants": [ + { + "variant": "regular", + "weight": 400, + "style": "normal", + "url": "/fonts/google/merriweather/Merriweather-400-normal.ttf" + }, + { + "variant": "700", + "weight": 700, + "style": "normal", + "url": "/fonts/google/merriweather/Merriweather-700-normal.ttf" + } + ] + }, + { + "family": "Montserrat", + "slug": "montserrat", + "category": null, + "variants": [ + { + "variant": "regular", + "weight": 400, + "style": "normal", + "url": "/fonts/google/montserrat/Montserrat-400-normal.ttf" + }, + { + "variant": "700", + "weight": 700, + "style": "normal", + "url": "/fonts/google/montserrat/Montserrat-700-normal.ttf" + } + ] + }, + { + "family": "Mulish", + "slug": "mulish", + "category": null, + "variants": [ + { + "variant": "regular", + "weight": 400, + "style": "normal", + "url": "/fonts/google/mulish/Mulish-400-normal.ttf" + }, + { + "variant": "700", + "weight": 700, + "style": "normal", + "url": "/fonts/google/mulish/Mulish-700-normal.ttf" + } + ] + }, + { + "family": "Noto Sans", + "slug": "noto-sans", + "category": null, + "variants": [ + { + "variant": "regular", + "weight": 400, + "style": "normal", + "url": "/fonts/google/noto-sans/NotoSans-400-normal.ttf" + }, + { + "variant": "700", + "weight": 700, + "style": "normal", + "url": "/fonts/google/noto-sans/NotoSans-700-normal.ttf" + } + ] + }, + { + "family": "Noto Sans JP", + "slug": "noto-sans-jp", + "category": null, + "variants": [ + { + "variant": "regular", + "weight": 400, + "style": "normal", + "url": "/fonts/google/noto-sans-jp/NotoSansJp-400-normal.ttf" + }, + { + "variant": "700", + "weight": 700, + "style": "normal", + "url": "/fonts/google/noto-sans-jp/NotoSansJp-700-normal.ttf" + } + ] + }, + { + "family": "Noto Sans KR", + "slug": "noto-sans-kr", + "category": null, + "variants": [ + { + "variant": "regular", + "weight": 400, + "style": "normal", + "url": "/fonts/google/noto-sans-kr/NotoSansKr-400-normal.ttf" + }, + { + "variant": "700", + "weight": 700, + "style": "normal", + "url": "/fonts/google/noto-sans-kr/NotoSansKr-700-normal.ttf" + } + ] + }, + { + "family": "Noto Sans TC", + "slug": "noto-sans-tc", + "category": null, + "variants": [ + { + "variant": "regular", + "weight": 400, + "style": "normal", + "url": "/fonts/google/noto-sans-tc/NotoSansTc-400-normal.ttf" + }, + { + "variant": "700", + "weight": 700, + "style": "normal", + "url": "/fonts/google/noto-sans-tc/NotoSansTc-700-normal.ttf" + } + ] + }, + { + "family": "Noto Serif", + "slug": "noto-serif", + "category": null, + "variants": [ + { + "variant": "regular", + "weight": 400, + "style": "normal", + "url": "/fonts/google/noto-serif/NotoSerif-400-normal.ttf" + }, + { + "variant": "700", + "weight": 700, + "style": "normal", + "url": "/fonts/google/noto-serif/NotoSerif-700-normal.ttf" + } + ] + }, + { + "family": "Nunito", + "slug": "nunito", + "category": null, + "variants": [ + { + "variant": "regular", + "weight": 400, + "style": "normal", + "url": "/fonts/google/nunito/Nunito-400-normal.ttf" + }, + { + "variant": "700", + "weight": 700, + "style": "normal", + "url": "/fonts/google/nunito/Nunito-700-normal.ttf" + } + ] + }, + { + "family": "Nunito Sans", + "slug": "nunito-sans", + "category": null, + "variants": [ + { + "variant": "regular", + "weight": 400, + "style": "normal", + "url": "/fonts/google/nunito-sans/NunitoSans-400-normal.ttf" + }, + { + "variant": "700", + "weight": 700, + "style": "normal", + "url": "/fonts/google/nunito-sans/NunitoSans-700-normal.ttf" + } + ] + }, + { + "family": "Open Sans", + "slug": "open-sans", + "category": null, + "variants": [ + { + "variant": "regular", + "weight": 400, + "style": "normal", + "url": "/fonts/google/open-sans/OpenSans-400-normal.ttf" + }, + { + "variant": "700", + "weight": 700, + "style": "normal", + "url": "/fonts/google/open-sans/OpenSans-700-normal.ttf" + } + ] + }, + { + "family": "Oswald", + "slug": "oswald", + "category": null, + "variants": [ + { + "variant": "regular", + "weight": 400, + "style": "normal", + "url": "/fonts/google/oswald/Oswald-400-normal.ttf" + }, + { + "variant": "700", + "weight": 700, + "style": "normal", + "url": "/fonts/google/oswald/Oswald-700-normal.ttf" + } + ] + }, + { + "family": "Outfit", + "slug": "outfit", + "category": null, + "variants": [ + { + "variant": "regular", + "weight": 400, + "style": "normal", + "url": "/fonts/google/outfit/Outfit-400-normal.ttf" + }, + { + "variant": "700", + "weight": 700, + "style": "normal", + "url": "/fonts/google/outfit/Outfit-700-normal.ttf" + } + ] + }, + { + "family": "PT Sans", + "slug": "pt-sans", + "category": null, + "variants": [ + { + "variant": "regular", + "weight": 400, + "style": "normal", + "url": "/fonts/google/pt-sans/PtSans-400-normal.ttf" + }, + { + "variant": "700", + "weight": 700, + "style": "normal", + "url": "/fonts/google/pt-sans/PtSans-700-normal.ttf" + } + ] + }, + { + "family": "PT Serif", + "slug": "pt-serif", + "category": null, + "variants": [ + { + "variant": "regular", + "weight": 400, + "style": "normal", + "url": "/fonts/google/pt-serif/PtSerif-400-normal.ttf" + }, + { + "variant": "700", + "weight": 700, + "style": "normal", + "url": "/fonts/google/pt-serif/PtSerif-700-normal.ttf" + } + ] + }, + { + "family": "Playfair Display", + "slug": "playfair-display", + "category": null, + "variants": [ + { + "variant": "regular", + "weight": 400, + "style": "normal", + "url": "/fonts/google/playfair-display/PlayfairDisplay-400-normal.ttf" + }, + { + "variant": "700", + "weight": 700, + "style": "normal", + "url": "/fonts/google/playfair-display/PlayfairDisplay-700-normal.ttf" + } + ] + }, + { + "family": "Plus Jakarta Sans", + "slug": "plus-jakarta-sans", + "category": null, + "variants": [ + { + "variant": "italic", + "weight": 400, + "style": "italic", + "url": "/fonts/google/plus-jakarta-sans/PlusJakartaSans-400-italic.ttf" + }, + { + "variant": "regular", + "weight": 400, + "style": "normal", + "url": "/fonts/google/plus-jakarta-sans/PlusJakartaSans-400-normal.ttf" + }, + { + "variant": "500italic", + "weight": 500, + "style": "italic", + "url": "/fonts/google/plus-jakarta-sans/PlusJakartaSans-500-italic.ttf" + }, + { + "variant": "500", + "weight": 500, + "style": "normal", + "url": "/fonts/google/plus-jakarta-sans/PlusJakartaSans-500-normal.ttf" + }, + { + "variant": "600italic", + "weight": 600, + "style": "italic", + "url": "/fonts/google/plus-jakarta-sans/PlusJakartaSans-600-italic.ttf" + }, + { + "variant": "600", + "weight": 600, + "style": "normal", + "url": "/fonts/google/plus-jakarta-sans/PlusJakartaSans-600-normal.ttf" + }, + { + "variant": "700italic", + "weight": 700, + "style": "italic", + "url": "/fonts/google/plus-jakarta-sans/PlusJakartaSans-700-italic.ttf" + }, + { + "variant": "700", + "weight": 700, + "style": "normal", + "url": "/fonts/google/plus-jakarta-sans/PlusJakartaSans-700-normal.ttf" + } + ] + }, + { + "family": "Poppins", + "slug": "poppins", + "category": null, + "variants": [ + { + "variant": "regular", + "weight": 400, + "style": "normal", + "url": "/fonts/google/poppins/Poppins-400-normal.ttf" + }, + { + "variant": "700", + "weight": 700, + "style": "normal", + "url": "/fonts/google/poppins/Poppins-700-normal.ttf" + } + ] + }, + { + "family": "Prompt", + "slug": "prompt", + "category": null, + "variants": [ + { + "variant": "regular", + "weight": 400, + "style": "normal", + "url": "/fonts/google/prompt/Prompt-400-normal.ttf" + }, + { + "variant": "700", + "weight": 700, + "style": "normal", + "url": "/fonts/google/prompt/Prompt-700-normal.ttf" + } + ] + }, + { + "family": "Quicksand", + "slug": "quicksand", + "category": null, + "variants": [ + { + "variant": "regular", + "weight": 400, + "style": "normal", + "url": "/fonts/google/quicksand/Quicksand-400-normal.ttf" + }, + { + "variant": "700", + "weight": 700, + "style": "normal", + "url": "/fonts/google/quicksand/Quicksand-700-normal.ttf" + } + ] + }, + { + "family": "Raleway", + "slug": "raleway", + "category": null, + "variants": [ + { + "variant": "regular", + "weight": 400, + "style": "normal", + "url": "/fonts/google/raleway/Raleway-400-normal.ttf" + }, + { + "variant": "700", + "weight": 700, + "style": "normal", + "url": "/fonts/google/raleway/Raleway-700-normal.ttf" + } + ] + }, + { + "family": "Roboto", + "slug": "roboto", + "category": null, + "variants": [ + { + "variant": "regular", + "weight": 400, + "style": "normal", + "url": "/fonts/google/roboto/Roboto-400-normal.ttf" + }, + { + "variant": "700", + "weight": 700, + "style": "normal", + "url": "/fonts/google/roboto/Roboto-700-normal.ttf" + } + ] + }, + { + "family": "Roboto Condensed", + "slug": "roboto-condensed", + "category": null, + "variants": [ + { + "variant": "regular", + "weight": 400, + "style": "normal", + "url": "/fonts/google/roboto-condensed/RobotoCondensed-400-normal.ttf" + }, + { + "variant": "700", + "weight": 700, + "style": "normal", + "url": "/fonts/google/roboto-condensed/RobotoCondensed-700-normal.ttf" + } + ] + }, + { + "family": "Roboto Mono", + "slug": "roboto-mono", + "category": null, + "variants": [ + { + "variant": "regular", + "weight": 400, + "style": "normal", + "url": "/fonts/google/roboto-mono/RobotoMono-400-normal.ttf" + }, + { + "variant": "700", + "weight": 700, + "style": "normal", + "url": "/fonts/google/roboto-mono/RobotoMono-700-normal.ttf" + } + ] + }, + { + "family": "Roboto Slab", + "slug": "roboto-slab", + "category": null, + "variants": [ + { + "variant": "regular", + "weight": 400, + "style": "normal", + "url": "/fonts/google/roboto-slab/RobotoSlab-400-normal.ttf" + }, + { + "variant": "700", + "weight": 700, + "style": "normal", + "url": "/fonts/google/roboto-slab/RobotoSlab-700-normal.ttf" + } + ] + }, + { + "family": "Rubik", + "slug": "rubik", + "category": null, + "variants": [ + { + "variant": "regular", + "weight": 400, + "style": "normal", + "url": "/fonts/google/rubik/Rubik-400-normal.ttf" + }, + { + "variant": "700", + "weight": 700, + "style": "normal", + "url": "/fonts/google/rubik/Rubik-700-normal.ttf" + } + ] + }, + { + "family": "Saira", + "slug": "saira", + "category": null, + "variants": [ + { + "variant": "regular", + "weight": 400, + "style": "normal", + "url": "/fonts/google/saira/Saira-400-normal.ttf" + }, + { + "variant": "700", + "weight": 700, + "style": "normal", + "url": "/fonts/google/saira/Saira-700-normal.ttf" + } + ] + }, + { + "family": "Source Sans 3", + "slug": "source-sans-3", + "category": null, + "variants": [ + { + "variant": "regular", + "weight": 400, + "style": "normal", + "url": "/fonts/google/source-sans-3/SourceSans3-400-normal.ttf" + }, + { + "variant": "700", + "weight": 700, + "style": "normal", + "url": "/fonts/google/source-sans-3/SourceSans3-700-normal.ttf" + } + ] + }, + { + "family": "Space Grotesk", + "slug": "space-grotesk", + "category": null, + "variants": [ + { + "variant": "regular", + "weight": 400, + "style": "normal", + "url": "/fonts/google/space-grotesk/SpaceGrotesk-400-normal.ttf" + }, + { + "variant": "500", + "weight": 500, + "style": "normal", + "url": "/fonts/google/space-grotesk/SpaceGrotesk-500-normal.ttf" + }, + { + "variant": "600", + "weight": 600, + "style": "normal", + "url": "/fonts/google/space-grotesk/SpaceGrotesk-600-normal.ttf" + }, + { + "variant": "700", + "weight": 700, + "style": "normal", + "url": "/fonts/google/space-grotesk/SpaceGrotesk-700-normal.ttf" + } + ] + }, + { + "family": "Titillium WEB", + "slug": "titillium-web", + "category": null, + "variants": [ + { + "variant": "regular", + "weight": 400, + "style": "normal", + "url": "/fonts/google/titillium-web/TitilliumWeb-400-normal.ttf" + }, + { + "variant": "700", + "weight": 700, + "style": "normal", + "url": "/fonts/google/titillium-web/TitilliumWeb-700-normal.ttf" + } + ] + }, + { + "family": "Ubuntu", + "slug": "ubuntu", + "category": null, + "variants": [ + { + "variant": "regular", + "weight": 400, + "style": "normal", + "url": "/fonts/google/ubuntu/Ubuntu-400-normal.ttf" + }, + { + "variant": "700", + "weight": 700, + "style": "normal", + "url": "/fonts/google/ubuntu/Ubuntu-700-normal.ttf" + } + ] + }, + { + "family": "Work Sans", + "slug": "work-sans", + "category": null, + "variants": [ + { + "variant": "regular", + "weight": 400, + "style": "normal", + "url": "/fonts/google/work-sans/WorkSans-400-normal.ttf" + }, + { + "variant": "700", + "weight": 700, + "style": "normal", + "url": "/fonts/google/work-sans/WorkSans-700-normal.ttf" + } + ] } ] } \ No newline at end of file diff --git a/tests/Feature/Console/SyncGoogleFontsTest.php b/tests/Feature/Console/SyncGoogleFontsTest.php index 3572d25..5d7d3ab 100644 --- a/tests/Feature/Console/SyncGoogleFontsTest.php +++ b/tests/Feature/Console/SyncGoogleFontsTest.php @@ -305,4 +305,50 @@ class SyncGoogleFontsTest extends TestCase File::deleteDirectory($targetPath); } + + public function test_it_rebuilds_manifest_from_disk_without_downloading(): void + { + $targetPath = storage_path('app/test-fonts'); + File::deleteDirectory($targetPath); + + File::ensureDirectoryExists($targetPath.'/alpha-sans'); + File::put($targetPath.'/alpha-sans/AlphaSans-400-normal.ttf', 'font-binary'); + File::put($targetPath.'/alpha-sans/AlphaSans-700-italic.woff2', 'font-binary'); + + File::ensureDirectoryExists($targetPath.'/beta-serif'); + File::put($targetPath.'/beta-serif/BetaSerif-Regular.ttf', 'font-binary'); + + File::ensureDirectoryExists($targetPath.'/ibm-plex-sans'); + File::put($targetPath.'/ibm-plex-sans/IBMPlexSans-Bold.ttf', 'font-binary'); + + Http::fake(); + + Artisan::call('fonts:sync-google', [ + '--from-disk' => true, + '--path' => 'storage/app/test-fonts', + ]); + + Http::assertNothingSent(); + + $manifestPath = $targetPath.'/manifest.json'; + $cssPath = $targetPath.'/fonts.css'; + + $this->assertFileExists($manifestPath); + $this->assertFileExists($cssPath); + + $manifest = json_decode(File::get($manifestPath), true); + $this->assertSame(3, $manifest['count']); + $this->assertTrue(collect($manifest['fonts'])->pluck('family')->contains('Alpha Sans')); + $this->assertTrue(collect($manifest['fonts'])->pluck('family')->contains('Beta Serif')); + $this->assertTrue(collect($manifest['fonts'])->pluck('family')->contains('IBM Plex Sans')); + + $this->assertTrue(str_contains(File::get($cssPath), "font-family: 'IBM Plex Sans';")); + + $alpha = collect($manifest['fonts'])->firstWhere('family', 'Alpha Sans'); + $alphaVariants = collect($alpha['variants'] ?? [])->pluck('variant')->all(); + $this->assertContains('regular', $alphaVariants); + $this->assertContains('700italic', $alphaVariants); + + File::deleteDirectory($targetPath); + } }