Skip to content

Commit

Permalink
feat: googleicons provider (#133)
Browse files Browse the repository at this point in the history
  • Loading branch information
qwerzl authored Sep 11, 2024
1 parent ef74666 commit f4cf7e8
Show file tree
Hide file tree
Showing 4 changed files with 150 additions and 0 deletions.
4 changes: 4 additions & 0 deletions docs/content/1.get-started/4.providers.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ Then, when you use a `font-family` in your CSS, we check to see whether it match

[Google Fonts](https://fonts.google.com/) is one of the best known public font APIs.

### `googleicons`

[Google Fonts Icons](https://fonts.google.com/icons) adds support for Material Symbols and Material Icons.

### `bunny`

[Bunny Fonts](https://fonts.bunny.net/) is provided by [bunny.net](https://bunny.net/) and is a drop-in Google Fonts compatible API, focusing on privacy.
Expand Down
46 changes: 46 additions & 0 deletions playground/pages/providers/googleicons.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<template>
<div>
Material Symbols Outlined:
<div class="material-symbols-outlined">
search
</div>
Material Icons Outlined:
<div class="material-icons-outlined">
search
</div>
</div>
</template>

<style scoped>
.material-symbols-outlined {
font-family: 'Material Symbols Outlined';
font-weight: normal;
font-style: normal;
font-size: 24px;
line-height: 1;
letter-spacing: normal;
text-transform: none;
display: inline-block;
white-space: nowrap;
word-wrap: normal;
direction: ltr;
-webkit-font-feature-settings: 'liga';
-webkit-font-smoothing: antialiased;
}
.material-icons-outlined {
font-family: 'Material Icons Outlined';
font-weight: normal;
font-style: normal;
font-size: 24px;
line-height: 1;
letter-spacing: normal;
text-transform: none;
display: inline-block;
white-space: nowrap;
word-wrap: normal;
direction: ltr;
-webkit-font-feature-settings: 'liga';
-webkit-font-smoothing: antialiased;
}
</style>
2 changes: 2 additions & 0 deletions src/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { join, relative } from 'pathe'
import { withoutLeadingSlash } from 'ufo'
import local from './providers/local'
import google from './providers/google'
import googleicons from './providers/googleicons'
import bunny from './providers/bunny'
import fontshare from './providers/fontshare'
import adobe from './providers/adobe'
Expand Down Expand Up @@ -93,6 +94,7 @@ export default defineNuxtModule<ModuleOptions>({
local,
adobe,
google,
googleicons,
bunny,
fontshare,
fontsource,
Expand Down
98 changes: 98 additions & 0 deletions src/providers/googleicons.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import { hash } from 'ohash'

import type { FontProvider } from '../types'
import { extractFontFaceData, addLocalFallbacks } from '../css/parse'
import { cachedData } from '../cache'
import { $fetch } from '../fetch'
import { logger } from '../logger'

export default {
async setup() {
await initialiseFontMeta()
},
async resolveFontFaces(fontFamily, defaults) {
if (!isGoogleIcon(fontFamily)) {
return
}

return {
fonts: await cachedData(`googleicons:${fontFamily}-${hash(defaults)}-data.json`, () => getFontDetails(fontFamily), {
onError(err) {
logger.error(`Could not fetch metadata for \`${fontFamily}\` from \`googleicons\`.`, err)
return []
},
}),
}
},
} satisfies FontProvider

/** internal */

let fonts: string[]

async function fetchFontMetadata() {
const response: { families: string[] } = JSON.parse((await $fetch<string>(
'https://fonts.google.com/metadata/icons?key=material_symbols&incomplete=true',
)).split('\n').slice(1).join('\n')) // remove the first line which makes it an invalid JSON

return response.families
}

async function initialiseFontMeta() {
fonts = await cachedData('googleicons:meta.json', fetchFontMetadata, {
onError() {
logger.error('Could not download `googleicons` font metadata. `@nuxt/fonts` will not be able to inject `@font-face` rules for googleicons.')
return []
},
})
}

function isGoogleIcon(family: string) {
return fonts.includes(family)
}

async function getFontDetails(family: string) {
let css = ''

if (family.includes('Icons')) {
css += await $fetch<string>('/css2', {
baseURL: 'https://fonts.googleapis.com/icon',
query: {
family: family,
},
})
}

for (const extension in userAgents) {
// Legacy Material Icons
if (family.includes('Icons')) {
css += await $fetch<string>('/icon', {
baseURL: 'https://fonts.googleapis.com',
headers: { 'user-agent': userAgents[extension as keyof typeof userAgents] },
query: {
family: family,
},
})
}
// New Material Symbols
else {
css += await $fetch<string>('/css2', {
baseURL: 'https://fonts.googleapis.com',
headers: { 'user-agent': userAgents[extension as keyof typeof userAgents] },
query: {
family: family + ':' + 'opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200',
},
})
}
}

return addLocalFallbacks(family, extractFontFaceData(css))
}

const userAgents = {
woff2: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36',
ttf: 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/534.54.16 (KHTML, like Gecko) Version/5.1.4 Safari/534.54.16',
// eot: 'Mozilla/5.0 (compatible; MSIE 8.0; Windows NT 6.1; Trident/4.0)',
// woff: 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:27.0) Gecko/20100101 Firefox/27.0',
// svg: 'Mozilla/4.0 (iPad; CPU OS 4_0_1 like Mac OS X) AppleWebKit/534.46 (KHTML, like Gecko) Version/4.1 Mobile/9A405 Safari/7534.48.3',
}

0 comments on commit f4cf7e8

Please sign in to comment.