クライアントサイドだけで日本語PDFを出力したときの文字化け・改行不具合をpdfmake最新版で再ビルドして直した話


はじめに

  • 常用文字にサブセット化を行ったものを id:naoa_yさんが作ってくれているのでそれを使っても良い
    • ビルド後のファイル(vfs_fonts.js)で 5MB -> 2.3MBぐらいに減ってる
    • ただしbuildが5年前なのでpdfmakeのバージョンが0.1.20と古い
    • 最新は0.1.63 (released this on 11 Dec 2019)
  • 上記の[email protected]だと日本語化したときの文字改行(word wrap)が適切にうごかない&改行されたとき文字化けする不具合があった
    • 私の場合、可変長の文字を動的に組み込む必要があり、自動改行はどうしても必要だった。1行に収まる短文のみであれば問題ないかと
  • 最新だとなおるかもしれないと、pdfmakeを最新にして使いたかったので、buildし直した。結論、治った!
  • Vueと書いてるけど、フォント差し替えるまでは共通だとおもう
  • @watameさんの記事を参考にしました。ありがとうございます。

TL;DR

  • このリポジトリにある、build/pdfmake.min.js, build/vfs_fonts.jsを使えばよいです。2つ合わせると10MB程度になります
  • https://github.com/yazashin/pdfmake/tree/0.1.63-ja
  • フォントサイズを減らしたい・違うフォントにしたいって人は下記を読みましょう

日本語対応pdfmakeをビルドする

fork&cloneする

cloneした後branchを0.1系に切り替える

  • "This is unstable master branch for version 0.2.x, for stable version 0.1.x see branch 0.1."とのこと

日本語フォントを入手する

  • ここは好み・用途によるとおもいますが、私のケースではこのフォントがマッチしました
  • https://ipafont.ipa.go.jp/#jp
  • IPAexゴシックのみ利用(等幅フォント)
  • メリデメを考えた時、メリットが勝ったのでこれを選択
    • 用途として、珍しい漢字もつかわれるのでサブセット化したとき文字化けの発生が怖い
    • pdfmakeとvfs_fontsで合計して10MBぐらいになる
      • pdfmake.min.js 1.2MB / vfs_fonts.js 8.1MB
      • 印刷ページごとに都度読み込みタイプだと辛い印象がある
      • vueのようなSPAでindex.htmlにscriptタグでglobalに登録してしまえば、大きな問題にはならないって考えて進めた
      • ぶっちゃけ最近はネット回線高速なので、SPAで初回ロード時に1回発生する10MB程度ならほとんど気にならないと思う
        • 安定したらServiceWorkerのキャッシュ対象にしてしまうのもありだとおもう
      • SEO気にする必要がない業務系サービスなのでこの判断ができている。っていう前提があります。
        • むしろSEO気にするサービスで、SPA & PDF生成が必要って、かなりニッチだとはおもうけどw

フォントを入れ替える

  • before

  • after

vfs_fonts.jsの更新

  • buildFontsを実行
$ yarn install
  ....
success Saved lockfile.
✨  Done in 216.37s.

$ ./node_modules/.bin/gulp buildFonts
[10:45:48] Using gulpfile ~/workspace/pdfmake/gulpfile.js
[10:45:48] Starting 'buildFonts'...
[10:45:48] Finished 'buildFonts' after 189 ms
  • pdfmakeへフォントを反映
$ yarn run build
...
WARNING in webpack performance recommendations:
You can limit the size of your bundles by using import() or require.ensure to lazy load some parts of your application.
For more info visit https://webpack.js.org/guides/code-splitting/
[10:50:16] Finished 'build' after 23 s
✨  Done in 24.49s.
  • warning出てるけど無視して進める

vfs_fonts.jsの確認

  • buildフォルダにあるvfs_fonts.jsがこうなっていれば大丈夫

Vue(typescript)で確認

  • 今回つくったpdfmake & vfs_fonts は、npmとして登録はせず、publicなフォルダに静的jsとして利用する
    • npmにするとdeploy時にciでビルドするとき、フォントのビルドから必要になるとか、そこらへん面倒でこうした
  • vue-cliのversionによって構成かわりますが、VUE_PROJECT_DIR/public/static-js/配下においた

index.htmlに登録

  • ポイントして、 deferを忘れずにつけましょう。10MB近いjsの読み込みでHTMLのパースが止まってしまいUXに影響がでます
index.html
 ... ~~~ ...
    <div id="app"></div>
    <!-- built files will be auto injected -->
  </body>
  <script defer src="/static-js/pdfmake.min.js"></script>
  <script defer src="/static-js/vfs_fonts.js"></script>
</html>

Vueの実装

  • Vueで使うときはこんなかんじ。手打ちなので間違ってるかもしれません。
pdf.vue
declare var pdfMake: any;
@Component({})
export default class PrintPdf extends Vue {
    createPdf() {
        pdfMake.fonts = {
            IPA_gothic: {
                normal: 'ipaexg.ttf',
                bold: 'ipaexg.ttf',
                italics: 'ipaexg.ttf',
                bolditalics: 'ipaexg.ttf',
            }
        };
        const defaultStyle = 'IPA_gothic';
        const docDefinition = {
            content: 'Hello 日本語!',
            defaultStyle: {
                font: defaultStyle
            }
        }
        pdfMake.createPdf(docDefinition).print();
    }
}
  • pdfmake 0.1.63 & IPAゴシック で生成したPDF
  • なお、文章はジェネレータでつくったので意味をなしてません。

  • 文字改行時の文字化け不具合がなおって本当に良かった..

Thanks