Skip to content

Commit 906ab3e

Browse files
committedNov 28, 2023
fix: prerender fonts
1 parent f8c0751 commit 906ab3e

File tree

3 files changed

+83
-0
lines changed

3 files changed

+83
-0
lines changed
 

‎src/runtime/core/font/cache.ts

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export const fontCache: Record<string, any> = {}

‎src/runtime/core/font/fetch.ts

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import { Buffer } from 'node:buffer'
2+
import type { H3Event } from 'h3'
3+
import type { FontConfig } from '../../types'
4+
import { base64ToArrayBuffer } from '../env/assets'
5+
import { fontCache } from './cache'
6+
import { useStorage } from '#imports'
7+
8+
export async function loadFont(e: H3Event, font: FontConfig) {
9+
const fontKey = `${font.name}:${font.weight}`
10+
const storageKey = `assets:nuxt-og-image:font:${fontKey}`
11+
if (fontCache[fontKey])
12+
return fontCache[fontKey]
13+
14+
// fetch local inter
15+
const [name, weight] = fontKey.split(':')
16+
17+
let data: ArrayBuffer | undefined
18+
// check cache first
19+
if (await useStorage().hasItem(storageKey))
20+
data = base64ToArrayBuffer(await useStorage().getItem<ArrayBuffer>(storageKey))
21+
// fetch local fonts
22+
if (!data) {
23+
if (font.path) {
24+
data = await e.$fetch(font.path, {
25+
baseURL: useNitroOrigin(e).replace('https', 'http'),
26+
responseType: 'arrayBuffer',
27+
})
28+
}
29+
else {
30+
data = await e.$fetch(`/__og-image__/font/${name}/${weight}.ttf`, {
31+
responseType: 'arrayBuffer',
32+
})
33+
}
34+
}
35+
36+
fontCache[fontKey] = { name, weight: Number(weight), data, style: 'normal' }
37+
await useStorage().setItem(storageKey, Buffer.from(data).toString('base64'))
38+
// convert data to string
39+
return fontCache[fontKey]
40+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import { createError, defineEventHandler, proxyRequest, sendRedirect } from 'h3'
2+
import { parseURL } from 'ufo'
3+
4+
// copied from vercel/satori
5+
export default defineEventHandler(async (e) => {
6+
const path = parseURL(e.path).pathname
7+
8+
// path will be like this: /__og-image__/font/<name>/<weight>.ttf
9+
const [name, weight] = path.split('/font/')[1].split('.')[0].split('/')
10+
11+
if (!name || !weight)
12+
return 'Provide a font name and weight'
13+
14+
// using H3Event $fetch will cause the request headers not to be sent
15+
const css = await globalThis.$fetch(`https://fonts.googleapis.com/css2?family=${name}:wght@${weight}`, {
16+
headers: {
17+
// Make sure it returns TTF.
18+
'User-Agent':
19+
'Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_8; de-at) AppleWebKit/533.21.1 (KHTML, like Gecko) Version/5.0.5 Safari/533.21.1',
20+
},
21+
})
22+
if (!css) {
23+
return createError({
24+
statusCode: 500,
25+
statusMessage: `Invalid Google Font ${name}:${weight}`,
26+
})
27+
}
28+
29+
const ttfResource = css.match(/src: url\((.+)\) format\('(opentype|truetype)'\)/)
30+
if (ttfResource?.[1])
31+
return proxyRequest(e, ttfResource[1], {})
32+
33+
// try woff2
34+
const woff2Resource = css.match(/src: url\((.+)\) format\('woff2'\)/)
35+
if (woff2Resource?.[1])
36+
return sendRedirect(e, woff2Resource[1])
37+
38+
return createError({
39+
statusCode: 500,
40+
statusMessage: `Malformed Google Font CSS ${css}`,
41+
})
42+
})

0 commit comments

Comments
 (0)
Please sign in to comment.