PDF出力ボタン制作時のハマった話

行程さんというサービスを運営しています。共有したくなる旅のしおりを簡単に作成できるサービスです。

このサービスで、しおりを印刷してもらいたいために、前回、段組の話を書きました。今回は、どんな環境でもウェブページを見たままのPDFを出力するためにPDF出力ボタンの機能を作ったときの話をします。

ウェブページを見たままPDF出力する

問題

  • 印刷プレビューページがあるが、PCのChromeでしか再現できない
  • iPhoneからでもコンビニ印刷できるように、PCのChromeで再現できている見た目のままPDF出力をしたい

解決案

  • サーバーサイド側でライブラリを用いてPDFを出力する(easyPDF、TCPDF、mPDFなど)

しかしこれらのフレームワークは、見たままを出力できない

実際に取った方法

  • ヘッドレスChrome+Puppeteer+Node.js

これであれば、Chromeで見たままを出力できます。ヘッドレスChromeは画面のないChromeです。サーバーにインストールしたり、画面キャプチャの自動化を行う際に便利です。

気をつけることといえば、PCのChromeで表示しているときも同じものを出力しないといけないため、ウェブフォントを通常のページかえらも、サーバーサイドからも表示させられるようにしなければいけない。そうでなければ、文字間の違いで、意図したものからずれたものになってしまいます。

顔文字と絵文字を表示させたい

課題

  • 絵文字と顔文字が表示されない

絵文字もフォントですし、特殊な顔文字もフォントをインストールしていないといけない場合があります。

解決

  • いろいろフォントをインストール

かなり特殊(つまり特定の媒体上でしかインストールされていないよう)なものはあきらめました。また、絵文字も様々な種類があるため、決め打ちで設定することにしました。

アプリ内でページを開くとダウンロードできない

問題

  • LINE等のアプリないでウェブページを表示させているとき、ダウンロードボタンを押下するとダウンロードできない
  • (アプリは関係ないですが)iPhoneのSafariからダウンロードされず開いてしまう

方法1:リンク先を「xxx.php」

PDF出力バグ

結論としては拡張子「php」でプログラムに処理をさせることは不具合が多く利用に向きません。

ここで注目していただきたいのは、アプリ内からだとPOSTやリファラーの内容が消えるということです。これはリクエストが2回投げられていて1回目はPOSTの内容があるのに対して、特定のアプリケーションで開こうとするときに2回目のリクエストが投げられ、そのときに情報が削ぎ落とされているようです。

Android端末+LINE等のアプリ内+ダウンロード時のファイル名にも注目していただきたいですが、ファイル名の拡張子が「php」になるため、使い物になりません。

方法2:リンク先を「xxx.pdf」(プログラムに渡す)

PDF出力

結論をいうと、毎回生成する必要があって日本語の名前をつけたい場合はこの方法が向いています。

日本語のファイル名をつけることができます。しかし、ファイル名に絵文字があった場合、ファミリーマート・ローソン系のプリンターで印刷できません。そのため、絵文字を入れさせないようにする必要があります。

方法3:リンク先を「xxx.pdf」(通常のPDFファイル)

PDF出力

こちらは物理的なPDFファイルを配置している場合です。特に問題はありません。インジケーターの表示も変わってきます。

ここまですべての方法でもiPhoneのSafariはバックグラウンドで「ダウンロード」というものができないので、ダウンロードはあきらめましょう。

※上記3つの検証結果は参考程度に見てください。一応全部検証しましたが間違っているかもしれないですし、バージョンアップ等で変わることもありますし

結論

  • ダウンロード → あきらめる
  • 拡張子 → pdf
  • formでPOSTやリファラー → 使わない
  • ファイル名 → 絵文字を使わない(ファイル名に絵文字があるとファミマ・ローソン系のプリンターで印刷できない)

絵文字が表示されない、重い

問題

  • PDFに絵文字が入っていると、ファミリーマート・ローソン系のプリンターで印刷されない
  • 絵文字1種類につき1MBほど増える(絵文字が100個あれば100MB)

なぜ1種類1MBも増えるのかは謎です。1種類を100個使うのであればそれほど増えません。

解決案

  • 絵文字があった場合、画像に変換する→面倒
  • 絵文字を簡単なものにする→ユーザー体験激下がり
  • PDF出力後に変換→こちらを採用

使った技術

  • Ghostscript

ヘッドレスChromeで出力されたPDFを、Ghostscriptを使ってアウトラインや圧縮などの変換を行います。ダウンロードまでの時間は多少犠牲になってしまいます。PDFのページ数が多ければ多いほど時間がかかります。

同時に、ファミリーマート・ローソン系のプリンターでも絵文字の印刷が可能になります。

Ghostscriptのバージョンは9.25を使うと、絵文字の背景が真っ黒になってしまうので9.5以上であれば解決されました。