ブラウザ上でvercel/satoriを使って画像生成したかった。
フォントファイルをセルフホストすれば問題なくsatoriを利用できるが、できればセルフホストしたくない(対応フォントが増えたら面倒)。
Google Font経由でフォントファイルをダウンロードして、satoriで利用できないか試したが、結論から言うと失敗した。
結論
satoriがwoff2に対応していれば実現できるが、対応していない現状フロントエンド(クライアントサイド)のみでは実現できない。
サーバサイドがあれば実現できる。
要件
- サーバサイドなし
- ユーザがGoogle Fontsから使いたいフォントの名前とスタイルを選んで、入力欄に入力する想定
想定した処理フロー
- ユーザーがフォント名とスタイルを入力
- Google Fonts APIからCSSを取得
- CSSからフォントファイルのURLを抽出
- フォントファイルをダウンロード
- satoriで画像生成
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();}User-Agentは禁止ヘッダーのため、fetchで上書きできない。
CSSからデータを抽出する
仮に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, };}終わりに
というわけで、結局サーバサイドないしプロキシが必要になるという結論で終わり。
ブラウザからの制約(禁止ヘッダー、woff2のみ配信)により、クライアントサイドだけでの実現は断念した。
参考資料
まとめ
ブラウザでGoogle Fontsからwoff/ttfを取得しようとしたが、User-Agent制限とsatoriのwoff2非対応により失敗。
サーバサイドまたはプロキシを経由する必要がある。失敗から学んだ知見として記録を残しておく。
hsb.horse