コンテンツにスキップ

クライアントサイドでGoogle fontからttf,otf, woffファイルを取得できなかった時の備忘録

  • ブラウザ上でvercel/satoriを使って画像生成したい
  • フォントファイルをセルフホストすれば、問題なくsatoriを利用できるが、できればセルフホストしたくない(対応フォントが増えたら面倒)
  • Google Font経由でフォントファイルをダウンロードして、satoriで利用できないか

satoriがwoff2に対応していれば実現できるが、対応していない現状フロントエンド(クライアントサイド)のみでは実現できない。

サーバサイドがあれば実現できる。

  • サーバサイドなし
  • ユーザがGoogle Fontsから使いたいフォントの名前とスタイルを選んで、入力欄に入力する想定

Google Fontはユーザーエージェントに合わせて、返却するフォントファイルの形式が変わるのでUser-Agentヘッダーを書き換えることで解決を試みました。 が、よくよく考えれば禁止ヘッダーなので上書きができず、目論見はここで頓挫します。

fetch-css.ts
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();
}
extract-from-css.ts
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,
};
}

というわけで、結局サーバサイドないしプロキシが必要になるという結論で終わりです。