From 0956a40f7a19e3082d103be3f68a2733107fcf3a Mon Sep 17 00:00:00 2001 From: Eric Bailey Date: Mon, 16 Jun 2025 17:13:21 -0500 Subject: [PATCH] Fix fonts --- bskyogcard/scripts/install-fonts.ts | 52 ++++++++++++++++------------- bskyogcard/src/components/Text.tsx | 3 +- bskyogcard/src/context.ts | 6 ++-- bskyogcard/src/routes/post.tsx | 7 +--- bskyogcard/src/util/fonts.ts | 49 ++++++++++++++++++++++++--- bskyogcard/test.html | 8 +++++ 6 files changed, 88 insertions(+), 37 deletions(-) diff --git a/bskyogcard/scripts/install-fonts.ts b/bskyogcard/scripts/install-fonts.ts index 5c58fb7b9e..80eddf0877 100644 --- a/bskyogcard/scripts/install-fonts.ts +++ b/bskyogcard/scripts/install-fonts.ts @@ -2,36 +2,42 @@ import {writeFile} from 'node:fs/promises' import * as path from 'node:path' import {fileURLToPath} from 'node:url' -const __DIRNAME = path.dirname(fileURLToPath(import.meta.url)) +import { + ADDITIONAL_FONTS, + ADDITIONAL_FONTS_POSTSCRIPT_NAMES, +} from '../src/util/fonts.js' -const FONTS = [ - 'https://cdn.jsdelivr.net/fontsource/fonts/noto-sans-jp@5.0/japanese-700-normal.ttf', - 'https://cdn.jsdelivr.net/fontsource/fonts/noto-sans-tc@5.0/chinese-traditional-700-normal.ttf', - 'https://cdn.jsdelivr.net/fontsource/fonts/noto-sans-sc@5.0/chinese-simplified-700-normal.ttf', - 'https://cdn.jsdelivr.net/fontsource/fonts/noto-sans-hk@5.0/chinese-hongkong-700-normal.ttf', - 'https://cdn.jsdelivr.net/fontsource/fonts/noto-sans-kr@5.0/korean-700-normal.ttf', - 'https://cdn.jsdelivr.net/fontsource/fonts/noto-sans-thai@5.0/thai-700-normal.ttf', - 'https://cdn.jsdelivr.net/fontsource/fonts/noto-sans-arabic@5.0/arabic-700-normal.ttf', - 'https://cdn.jsdelivr.net/fontsource/fonts/noto-sans-hebrew@5.0/hebrew-700-normal.ttf', -] +const __DIRNAME = path.dirname(fileURLToPath(import.meta.url)) +const outDir = path.join(__DIRNAME, '..', 'src', 'assets', 'fonts') async function main() { await Promise.all( - FONTS.map(async urlStr => { - const url = new URL(urlStr) - const res = await fetch(url) - const font = await res.arrayBuffer() - const filename = url.pathname - .split('/') - .slice(-2) - .join('/') - .replace(/@[\d.]+\//, '-') + ADDITIONAL_FONTS.map(async urlStr => { + // get last part of the URL + const raw = urlStr.split('/').slice(-1).join('') + // style and weight are known parts of the path, rest is the font family + const [styleWithExtension, weight, ...fontFamilyParts] = raw + .split('-') + .reverse() + const style = styleWithExtension.split('.')[0] + // re-reverse the font family parts to get the original order + const rawFontFamily = fontFamilyParts.reverse().join('-') + // get the postscript name from the map + const postScriptName = ADDITIONAL_FONTS_POSTSCRIPT_NAMES[rawFontFamily] + // our filename format: `weight-postScriptName-style.ttf` + const filename = `${weight}-${postScriptName}-${style}.ttf` + // fetch the file + const res = await fetch(urlStr) + if (!res.ok) { - throw new Error(`HTTP ${res.status}: fetching failed for ${filename}`) + throw new Error(`HTTP ${res.status}: fetching failed for ${urlStr}`) } + + console.log(`Writing font ${filename}...`) + await writeFile( - path.join(__DIRNAME, '..', 'src', 'assets', 'fonts', filename), - Buffer.from(font), + path.join(outDir, filename), + Buffer.from(await res.arrayBuffer()), ) }), ) diff --git a/bskyogcard/src/components/Text.tsx b/bskyogcard/src/components/Text.tsx index 46078c8ddf..7370ee0913 100644 --- a/bskyogcard/src/components/Text.tsx +++ b/bskyogcard/src/components/Text.tsx @@ -1,4 +1,5 @@ import {atoms as a, style as s, theme as t} from '../theme/index.js' +import {FONT_FAMILY_DEF} from '../util/fonts.js' const SCALE_MULTIPLIER = 1 - 0.0625 @@ -20,7 +21,7 @@ export function Text({ a.tracking_wide, t.atoms.text, { - fontFamily: 'Inter', + fontFamily: FONT_FAMILY_DEF, fontWeight: 400, }, ...(cx ?? []), diff --git a/bskyogcard/src/context.ts b/bskyogcard/src/context.ts index 9ebf643e53..f877c3599b 100644 --- a/bskyogcard/src/context.ts +++ b/bskyogcard/src/context.ts @@ -1,7 +1,7 @@ import {AtpAgent} from '@atproto/api' import {Config} from './config.js' -import {getFontFiles, readFonts} from './util/fonts.js' +import {getFontDefinitions} from './util/fonts.js' export type AppContextOptions = { cfg: Config @@ -23,8 +23,8 @@ export class AppContext { static async fromConfig(cfg: Config, overrides?: Partial) { const appviewAgent = new AtpAgent({service: cfg.service.appviewUrl}) - const fontFiles = getFontFiles() - const fonts = readFonts(fontFiles) + const fonts = getFontDefinitions() + return new AppContext({ cfg, appviewAgent, diff --git a/bskyogcard/src/routes/post.tsx b/bskyogcard/src/routes/post.tsx index ce488f4fd4..a8648648db 100644 --- a/bskyogcard/src/routes/post.tsx +++ b/bskyogcard/src/routes/post.tsx @@ -10,7 +10,6 @@ import {getPost} from '../data/getPost.js' import {getPostData} from '../data/getPostData.js' import {httpLogger} from '../logger.js' import {loadEmojiAsSvg} from '../util.js' -import {getFontFiles} from '../util/fonts.js' import { getRenderOptions, parseDisplayOptionsFromQuery, @@ -72,15 +71,11 @@ export default function (ctx: AppContext, app: Express) { }, ) const output = await resvg.renderAsync(svg, { - font: { - fontFiles: getFontFiles(), - defaultFontFamily: 'Inter', - }, fitTo: { mode: 'width', value: width * 2, }, - logLevel: 'trace', + // logLevel: 'trace', }) res.statusCode = 200 diff --git a/bskyogcard/src/util/fonts.ts b/bskyogcard/src/util/fonts.ts index 2670f4a7f7..f997ced96f 100644 --- a/bskyogcard/src/util/fonts.ts +++ b/bskyogcard/src/util/fonts.ts @@ -3,6 +3,43 @@ import * as path from 'node:path' const __DIRNAME = path.join(process.cwd(), 'src') +export const ADDITIONAL_FONTS = [ + 'https://cdn.jsdelivr.net/fontsource/fonts/noto-sans-jp@5.0/japanese-700-normal.ttf', + 'https://cdn.jsdelivr.net/fontsource/fonts/noto-sans-tc@5.0/chinese-traditional-700-normal.ttf', + 'https://cdn.jsdelivr.net/fontsource/fonts/noto-sans-sc@5.0/chinese-simplified-700-normal.ttf', + 'https://cdn.jsdelivr.net/fontsource/fonts/noto-sans-hk@5.0/chinese-hongkong-700-normal.ttf', + 'https://cdn.jsdelivr.net/fontsource/fonts/noto-sans-kr@5.0/korean-700-normal.ttf', + 'https://cdn.jsdelivr.net/fontsource/fonts/noto-sans-thai@5.0/thai-700-normal.ttf', + 'https://cdn.jsdelivr.net/fontsource/fonts/noto-sans-arabic@5.0/arabic-700-normal.ttf', + 'https://cdn.jsdelivr.net/fontsource/fonts/noto-sans-hebrew@5.0/hebrew-700-normal.ttf', +] + +/** + * CSS `fontFamily` defs must use the PostScript names of the fonts. When + * adding additional fonts above, you'll need to figure out the PostScript name + * and add it to the map below. + */ +export const ADDITIONAL_FONTS_POSTSCRIPT_NAMES: Record = { + arabic: 'Noto Sans Arabic', + hebrew: 'Noto Sans Arabic', + japanese: 'Noto Sans JP Thin', + korean: 'Noto Sans KR', + thai: 'Noto Sans Thai', + 'chinese-hongkong': 'Noto Sans HK', + 'chinese-simplified': 'Noto Sans SC', + 'chinese-traditional': 'Noto Sans TC', +} + +/** + * Precomputed, for use in the runtime CSS defs + */ +export const FONT_FAMILY_DEF = `Inter, ${Object.values( + ADDITIONAL_FONTS_POSTSCRIPT_NAMES, +).join(', ')}, sans-serif` + +/** + * Get all TTF files from our fonts dir + */ export function getFontFiles() { const fontDirectory = path.join(__DIRNAME, 'assets', 'fonts') return readdirSync(fontDirectory) @@ -10,13 +47,17 @@ export function getFontFiles() { .map(file => path.join(fontDirectory, file)) } -export function readFonts(fontFiles: string[]) { - return fontFiles.map(file => { +/** + * Get all font files and format for use by Satori + */ +export function getFontDefinitions() { + return getFontFiles().map(file => { const basename = path.basename(file, path.extname(file)) const italic = basename.includes('Italic') - const [weight, name] = basename.split('-') + const [weight, postScriptName] = basename.split('-') + return { - name, + name: postScriptName, data: readFileSync(file), weight: parseInt(weight), style: italic ? 'italic' : 'normal', diff --git a/bskyogcard/test.html b/bskyogcard/test.html index 9d0fe72f44..328521c855 100644 --- a/bskyogcard/test.html +++ b/bskyogcard/test.html @@ -107,6 +107,14 @@ + +
+

Other languages

+
+
Japanese
+
+
+

Misc