クライアントサイドでGoogle fontからttf,otf, woffファイルを取得できなかった時の備忘録
実現したいこと
Section titled “実現したいこと”- ブラウザ上でvercel/satoriを使って画像生成したい
- フォントファイルをセルフホストすれば、問題なくsatoriを利用できるが、できればセルフホストしたくない(対応フォントが増えたら面倒)
- Google Font経由でフォントファイルをダウンロードして、satoriで利用できないか
satoriがwoff2に対応していれば実現できるが、対応していない現状フロントエンド(クライアントサイド)のみでは実現できない。
サーバサイドがあれば実現できる。
- サーバサイドなし
- ユーザがGoogle Fontsから使いたいフォントの名前とスタイルを選んで、入力欄に入力する想定
想定した処理フロー
Section titled “想定した処理フロー”CSSのFetch
Section titled “CSSのFetch”Google Fontはユーザーエージェントに合わせて、返却するフォントファイルの形式が変わるのでUser-Agentヘッダーを書き換えることで解決を試みました。 が、よくよく考えれば禁止ヘッダーなので上書きができず、目論見はここで頓挫します。
export const USER_AGENTS_BY_FONT_TYPE = { // IE11 as woff woff: "Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko", // Googlebot as truetype ttf: "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)",};
export type FontType = keyof typeof USER_AGENTS_BY_FONT_TYPE;
export async function fetchGoogleFontCSS( url: string, fontType: FontType = "ttf") { const response = await fetch(url, { headers: { "User-Agent": USER_AGENTS_BY_FONT_TYPE[fontType], }, });
if (!response.ok) { throw new Error( `Failed to load font: ${response.status} ${response.statusText}` ); }
return response.text();}
CSSからデータを抽出する
Section titled “CSSからデータを抽出する”const subsetRegex = /\/\* (.+?) \*\//;const fontFileRegex = /src: url\((.+?)\)/;
type ExtractResult = { subsets: Record<string, string>; hasItems: boolean;};
const extract = (regex: RegExp, line: string) => regex.exec(line)?.[1];
export function extractFontFromCSS(css: string): ExtractResult { const subsets: Record<string, string> = {};
let currentSubset = ""; for (const line of css.split("\n")) { const subset = extract(subsetRegex, line); if (subset) { currentSubset = subset; continue; }
const url = extract(fontFileRegex, line); if (url && !subsets[currentSubset]) { subsets[currentSubset] = url; } }
return { subsets, hasItems: Object.keys(subsets).length > 0, };}
というわけで、結局サーバサイドないしプロキシが必要になるという結論で終わりです。