feat: use config header + body fonts for satori

This commit is contained in:
Ben Schlegel 2023-09-22 16:43:43 +02:00
parent 5ea283ac1a
commit 346dbfe3e3
No known key found for this signature in database
GPG Key ID: 8BDB8891C1575E22
2 changed files with 52 additions and 36 deletions

View File

@ -3,7 +3,7 @@ import { JSResourceToScriptElement } from "../util/resources"
import { QuartzComponentConstructor, QuartzComponentProps } from "./types"
import satori from "satori"
import * as fs from "fs"
import { getTtfFromGfont } from "../util/fonts"
import { getSatoriFont } from "../util/fonts"
import { GlobalConfiguration } from "../cfg"
import sharp from "sharp"
@ -12,20 +12,22 @@ import sharp from "sharp"
* @param title what title to use
* @param description what description to use
* @param fileName what fileName to use when writing to disk
* @param fontName name of font to use (must be google font)
* @param headerFontName name of font to use for header (must be google font)
* @param bodyFontName name of font to use for body (must be google font)
* @param cfg `GlobalConfiguration` of quartz
*/
async function generateSocialImage(
title: string,
description: string,
fileName: string,
fontName: string,
headerFontName: string,
bodyFontName: string,
cfg: GlobalConfiguration,
colorScheme: "lightMode" | "darkMode",
) {
const font = (await getTtfFromGfont(fontName)) as ArrayBuffer
const fonts = await getSatoriFont(headerFontName, bodyFontName)
// How many characters are allowed before switching to smaller font
const fontBreakPoint = 26
const fontBreakPoint = 22
const useSmallerFont = title.length > fontBreakPoint
const svg = await satori(
<div
@ -52,28 +54,30 @@ async function generateSocialImage(
paddingBottom: "2rem",
}}
>
<div
<p
style={{
color: cfg.theme.colors[colorScheme].dark,
fontSize: useSmallerFont ? 70 : 80,
fontSize: useSmallerFont ? 70 : 82,
marginLeft: "4rem",
textAlign: "center",
marginRight: "4rem",
fontFamily: fonts[0].name,
}}
>
{title}
</div>
<div
</p>
<p
style={{
color: cfg.theme.colors[colorScheme].dark,
fontSize: 44,
marginLeft: "8rem",
marginRight: "8rem",
lineClamp: 3,
fontFamily: fonts[1].name,
}}
>
{description}
</div>
</p>
</div>
<div
style={{
@ -88,20 +92,7 @@ async function generateSocialImage(
{
width: ogHeight,
height: ogWidth,
fonts: [
{
name: fontName,
data: font,
weight: 800,
style: "normal",
},
{
name: fontName,
data: font,
weight: 400,
style: "normal",
},
],
fonts: fonts,
},
)
@ -121,7 +112,7 @@ export default (() => {
let font: Promise<ArrayBuffer | undefined>
function Head({ cfg, fileData, externalResources }: QuartzComponentProps) {
if (!font) {
font = getTtfFromGfont(cfg.theme.typography.header)
// font = getSatoriFont(cfg.theme.typography.header)
}
const slug = fileData.filePath
const filePath = slug?.replaceAll("/", "-")
@ -133,6 +124,7 @@ export default (() => {
description,
filePath as string,
cfg.theme.typography.header,
cfg.theme.typography.body,
cfg,
"lightMode",
)

View File

@ -1,14 +1,38 @@
import { FontWeight, SatoriOptions } from "satori/wasm"
/**
* Get the `.ttf` file (as ArrayBuffer) of a font by google font name
* @param font a valid google font
* Get an array of `FontOptions` (for satori) given google font names
* @param headerFontName name of google font used for header
* @param bodyFontName name of google font used for body
* @returns FontOptions for header and body
*/
export async function getTtfFromGfont(font: string) {
const css = await (await fetch(`https://fonts.googleapis.com/css?family=${font}`)).text()
const urlRegex = /url\((https:\/\/fonts.gstatic.com\/s\/.*?.ttf)\)/g
const match = urlRegex.exec(css)
if (match) {
// fontData is an ArrayBuffer containing the .ttf file data
const fontTtf = await (await fetch(match[1])).arrayBuffer()
return fontTtf
}
export async function getSatoriFont(headerFontName: string, bodyFontName: string) {
const headerFont = await fetchTtf(headerFontName, 700)
const bodyFont = await fetchTtf(bodyFontName, 400)
const fonts: SatoriOptions["fonts"] = [headerFont, bodyFont]
return fonts
}
function fetchTtf(
fontName: string,
weight: FontWeight,
): Promise<GetElementType<SatoriOptions["fonts"]>> {
return new Promise(async (resolve, reject) => {
const css = await (
await fetch(`https://fonts.googleapis.com/css?family=${fontName}:${weight}`)
).text()
const urlRegex = /url\((https:\/\/fonts.gstatic.com\/s\/.*?.ttf)\)/g
const match = urlRegex.exec(css)
if (match) {
// fontData is an ArrayBuffer containing the .ttf file data
const fontTtf = await (await fetch(match[1])).arrayBuffer()
resolve({ name: fontName, data: fontTtf, weight: weight as FontWeight, style: "normal" })
} else {
reject("Could not fetch font")
}
})
}
// Type helper
type GetElementType<T extends any[]> = T extends (infer U)[] ? U : never