iOS用オープンソースPDFビューア「vfr/Reader」でPDFを開くとクラッシュする時の対処法


XCode

※当ブログではアフィリエイト広告を利用しています。

iOSにはGitHubで公開されていて無料で使える素晴らしいオープンソースPDFビューア「vfr/Reader」がありますが、特定のPDFを開いた時にアプリがクラッシュする問題が起きました。

GitHubのプロジェクトページには解決方法がありませんでしたが、自力でなんとか解決できたのでその方法をメモします。

「vfr/Reader」とは

iOSで使えるオープンソースのPDFビューアです。PDFビューアは自分で作ろうとするとかなり大変(私は挫折しました)ですが、「vfr/Reader」を使えば自分のアプリに手軽にPDFビューアを組み込むことができます。

vfr/Reader 画面サンプル

本エントリ執筆時点ではiOS6以降のiPhone、iPadに対応しており最新のiOS8でも使うことができます。

「vfr/Reader」はGitHubの以下のページからダウンロードできます。
GitHubプロジェクトページ:vfr/Reader · GitHub

「vfr/Reader」で発生した問題

ほとんどのPDFファイルを開くときは問題ありませんでしたが、以下のような条件のときにPDFの内容を描画している途中でアプリがクラッシュします。

  • iPad 2~iPad 4実機
  • iOS 7と8両方
  • スキャナでカラーの紙を高画質(300dpi)でスキャンして作成したPDF

XCodeよりiPad 2実機を指定してアプリをデバッグ実行すると、問題のPDFを開いた時点で以下のようにメモリ不足によりアプリが終了した旨のメッセージが出ます。

The App “アプリの名前” on “iPad・iPhoneの名前” quit unexpectedly.
Message from debugger: Terminated due to Memory Pressure

vfr/Reader メモリ不足によりアプリが終了

また出力を見るとReaderViewControllerdidReceiveMemoryWarningデリゲートメソッドが呼ばれているため、何らかの理由でメモリが足りなくなっていることは間違い無さそうです。

vfr/Reader  didreceivememorywarning

しかしiPad Airではメモリ搭載量の関係か、アプリがクラッシュすること無くPDFを開くことができます。

解決した方法

PDFの描画を行っている部分ReaderContentPage.mの547行目付近に、CGContextSetInterpolationQuality関数とCGContextSetRenderingIntent関数の2つを追加しました。

変更前

CGContextDrawPDFPage(context, _PDFPageRef); // Render the PDF page into the context

変更後

CGContextSetInterpolationQuality(context, kCGInterpolationHigh);
CGContextSetRenderingIntent(context, kCGRenderingIntentDefault);

CGContextDrawPDFPage(context, _PDFPageRef); // Render the PDF page into the context

CGContextDrawPDFPage関数でPDFを描画する前に、CGContextSetInterpolationQuality関数のパラメータで画質を明示的にkCGInterpolationHighとして指定することで、iPad2~4で問題のPDFを開いてもアプリをクラッシュさせずPDFが開くようになりました。

ちなみにCGContextSetInterpolationQuality関数のパラメータでデフォルトの画質kCGInterpolationDefaultを指定するとアプリがクラッシュするのは直らなかったため、明示的にクオリティを指定する必要があるようです。

おわりに

問題が発生した時、GitHubのPull Request等で有志が解決法を示してくれていれば良いのですが、そうでない場合は問題の報告をする、自分で解決する、代替手段を見つける等の必要が出てきます。

オープンソースソフトウェアは便利ですが、想定外のことが起きた時のリスクは厄介ですね。