Astroでフォントデータをローカルファイルから読み込めるようにする

はじめに

Astroでsatoriを使ってOGP画像用のパスを作成しようとしたところ、フォントデータを読み込む際に以下のエラーが出た。

[ERROR] Unsupported OpenType signature /src

# または、`fs.readFileSync`で読み込もうとすると
ENOENT: no such file or directory, open '../assets/fonts/NotoSansJP-Regular.ttf'

対応を調べてみたが、ほとんどの人がネットワーク経由でGoogleFontsからフォントデータをフェッチして利用しているらしかった。しかし、できることならネットワークに依存せずローカルのファイルを読み込めるようにしたかった。さらに調査をした結果、なんとか対応できたので残しておく。

対応方法

astro.config.mjsの中で、以下のようにフォントを読み込むためのプラグインを用意することでフォントを読み込めるようになった。このプラグインでは、指定した拡張子(.ttf, .woff)のフォントファイルをバイナリデータとして読み込み、それをエクスポート可能な形に変換している。

export default defineConfig({
  vite: {
    plugins: [rawFonts([".ttf", ".woff"])],
  },
});

const rawFonts = (ext) => {
  return {
    name: "vite-plugin-raw-fonts",
    transform(_, id) {
      if (ext.some((e) => id.endsWith(e))) {
        const buffer = fs.readFileSync(id);
        return {
          code: `export default ${JSON.stringify(buffer)}`,
          map: null,
        };
      }
    },
  };
}

フォントデータを読み込んで使用する際はこうする。

import React from 'react'
import satori from 'satori';
import RegularFont from "../assets/fonts/NotoSansJP-Regular.ttf";
import BoldFont from "../assets/fonts/NotoSansJP-Bold.ttf";

const generateOgpImage = async (element: React.ReactNode) => {
  const svg = await satori(
    element,
    {
      width: 1200,
      height: 630,
      fonts: [
        {
          name: "Noto Sans JP",
          data: Buffer.from(RegularFont),
          style: "normal",
          weight: 400,
        },
        {
          name: "Noto Sans JP",
          data: Buffer.from(BoldFont),
          style: "normal",
          weight: 600,
        },
      ],
    }
  );

  // 略
}

おわり

これでフォントデータをローカルファイルから読み込めるようになった。AstroでOGP画像用のパスを作成した対応も後日ブログに書こうと思う。

関連記事