【JavaScript】リモートにある .ttf ファイルを読み込んで jsPDF に日本語フォントを適用する

# 概要

　jsPDF を使って日本語で PDF ファイルを生成したかったが、日本語対応はしていないため、.ttf ファイルを使用する。

https://github.com/parallax/jsPDF

> PDF の 14 の標準フォントは、ASCII コード ページに限定されています。UTF-8 を使用する場合は、必要なグリフを提供するカスタム フォントを統合する必要があります。jsPDF は .ttf ファイルをサポートしています。

.ttf ファイルは Google Fonts からダウンロードする。今回使用する `Noto Serif Japanese` の .ttf ファイルは以下からダウンロードできる（Download all(1)をクリックする）。

 https://fonts.google.com/selection?subset=japanese&script=Hira

　README にある通り以下に .ttf ファイルをアップロードする。

https://rawgit.com/MrRio/jsPDF/master/fontconverter/fontconverter.html

> jsPDF にフォントを追加するには、 /fontconverter/fontconverter.htmlのフォントコンバーターを使用します。フォントコンバーターは、提供された ttf ファイルの内容を base64 でエンコードされた文字列として含み、jsPDF 用の追加コードを含む js ファイルを作成します。生成された js ファイルをプロジェクトに追加するだけです。これで、コードで setFont メソッドを使用して、UTF-8 でエンコードされたテキストを書き込む準備が整います。

アップロード後、以下のようなファイルがダウンロードされる。

```js
import { jsPDF } from "jspdf"
var font = 'AAEAAAAXAQAABABwQkF...'
var callAddFont = function () {
this.addFileToVFS('NotoSerifJP-VariableFont_wght-normal.ttf', font);
this.addFont('NotoSerifJP-VariableFont_wght-normal.ttf', 'NotoSerifJP-VariableFont_wght', 'normal');
};
jsPDF.API.events.push(['addFonts', callAddFont])
```

しかしサイズが大きいのでこれを GitHub で管理したくない。

# リモートにある .ttf ファイル を読み込む

- サンプルコード

```react
  const handleExportPdf = async () => {
    const doc = new jsPdf();

    // リモートにある .ttf ファイル を読み込む
    const res = await fetch(
      'https://sample-bucket.s3.ap-northeast-1.amazonaws.com/fonts/NotoSerifJP-VariableFont_wght.ttf'
    );
    const fontBlob = await res.blob();

    // Blob の内容を読み込む
    const reader = new FileReader();
    reader.readAsDataURL(fontBlob);

    // Blob 読み込み完了後の処理
    reader.onloadend = function () {
      const result = reader?.result;
      if (result && typeof result === 'string') {
        // Base64 部分を取得
        const base64data = result.split(',')[1];

        // フォント設定
        doc.addFileToVFS('NotoSerifJP.ttf', base64data);
        doc.addFont('NotoSerifJP.ttf', 'NotoSerifJP', 'normal');
        doc.setFont('NotoSerifJP');

        // PDFにテキストを書き込む
        doc.text(
          'リモートの .ttf ファイルから jsPDF にフォントを設定します。',
          20,
          40
        );

        // PDFを保存
        doc.save('sample.pdf');
      }
    };
```

- 実行後

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621414/2f4a9328-571c-da3c-b62d-55916eff7dca.png)

# 解説

- リモートファイルの読み込み
  - S3 バケットに保存した .ttf ファイルを fetch で取得し、レスポンスから Blog オブジェクトを取得する

```react
// リモートにある .ttf ファイル を読み込む
const res = await fetch(
  'https://sample-bucket.s3.ap-northeast-1.amazonaws.com/fonts/NotoSerifJP-VariableFont_wght.ttf'
);
const fontBlob = await res.blob();
```

https://developer.mozilla.org/ja/docs/Web/API/Response/blob

- Blog オブジェクトから Base64 コードを読み込む

```react
// Blob の内容を読み込む
const reader = new FileReader();
reader.readAsDataURL(fontBlob);
```

https://developer.mozilla.org/ja/docs/Web/API/FileReader/readAsDataURL

- Base64 コードを jsPDF に渡す

```react
// Blob 読み込み完了後の処理
reader.onloadend = function () {
  const result = reader?.result;

  if (result && typeof result === 'string') {
    // Base64部分を取得
    const base64data = result.split(',')[1];

    // フォント設定
    doc.addFileToVFS('NotoSerifJP.ttf', base64data);
    doc.addFont('NotoSerifJP.ttf', 'NotoSerifJP', 'normal');
    doc.setFont('NotoSerifJP');
    ...
```

# 参考

https://github.com/parallax/jsPDF

https://developer.mozilla.org/ja/docs/Web/API/Response/blob

https://developer.mozilla.org/ja/docs/Web/API/FileReader/readAsDataURL