diff --git a/quartz/components/Head.tsx b/quartz/components/Head.tsx index dd4ef25bd..4bca972f7 100644 --- a/quartz/components/Head.tsx +++ b/quartz/components/Head.tsx @@ -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(
-
{title} -
-
+

{description} -

+

{ let font: Promise 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", ) diff --git a/quartz/util/fonts.ts b/quartz/util/fonts.ts index 77e9df73a..bf5859e18 100644 --- a/quartz/util/fonts.ts +++ b/quartz/util/fonts.ts @@ -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> { + 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 (infer U)[] ? U : never