🐕

PDFMakeで日本語フォントを使用する

2022/07/21に公開

背景

JavaScriptでPDFを出力する場合、PDFMakeを使用する方法が一般的ですが、デフォルトのフォントがRobotoで日本語非対応になっています。vfs_fonts.jsという設定ファイルでフォントを変更することが出来ますが、フォントのバイナリをbase64で指定しなければならず、巨大なソースファイルが生まれてしまいます。

ビルド時、デバッグ時やSPAの初回ロード時に動作が著しく遅くなるため、解決策として行ったことをここに記します。

やること

バイナリをbase64で直打ちする方法をやめて、フォントファイルを別途ダウンロードさせることにします。PDFMakeを最初に使用するとき、フォントをダウンロード→base64化して変数にセットします。

PDFMakeを使用するまでフォントファイルは読み込まれないため、ビルド時、デバッグ時やSPAの初回ロード時の読み込みが遅くなりません。

具体的な方法

以下にReactのソースを示します。フォントファイルをダウンロード→base64化→pdfMakeにセットしています。pdfMakeのフォント設定は初回のみ行えばいいため、Reduxなどグローバル変数を管理するライブラリにpdfMakeのインスタンスを入れておいたほうがいいかもしれません。

サンプルプロジェクトは以下のGithubリポジトリにあります。
https://github.com/MatalStone/react-pdfmake-sample

Vue.jsやAngularでもフォントファイルをダウンロード→base64化→pdfMakeにセットという方法は有効ですので試してみてください。

import logo from './logo.svg';
import './App.css';
import pdfMake from 'pdfmake';
import axiosBase from 'axios'

const axios = axiosBase.create({
  baseURL: './',
  headers: {
    'Content-Type': 'application/x-font-ttf',
  },
  responseType: 'blob',
})

async function blobToBase64(blob) {
  return new Promise((resolve, reject) => {
    const reader = new FileReader()
    reader.onload = function() {
      const result = this.result.split('base64,')[1]
      resolve(result)
    };
  
    reader.readAsDataURL(blob)
  })
}

async function onClick() {
  if(pdfMake.vfs == null) {
    const response = await axios.get('ipaexg.ttf')
    if(response.status !== 200) {
      return
    }
    const font = await blobToBase64(response.data)
    pdfMake.vfs = {"ipaexg.ttf": font}
    pdfMake.fonts = {
      IPAEXGothic: {
        normal: 'ipaexg.ttf',
      },
    };
  }
  
  const docDefinition = {
    content: [`テスト出力`],
    defaultStyle: {
      font: 'IPAEXGothic',
    },
  };

  pdfMake.createPdf(docDefinition).download();
}

function App() {
  return (
    <div className="App">
      <header className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
        <p>
          PDFMake sample.
        </p>
        <p
          className="App-link"
          onClick={onClick}
        >
          Export PDF
        </p>
      </header>
    </div>
  );
}

export default App;

Discussion