※当ブログではアフィリエイト広告を利用しています。
iOSにはGitHubで公開されていて無料で使える素晴らしいオープンソースPDFビューア「vfr/Reader」がありますが、特定のPDFを開いた時にアプリがクラッシュする問題が起きました。
GitHubのプロジェクトページには解決方法がありませんでしたが、自力でなんとか解決できたのでその方法をメモします。
「vfr/Reader」とは
iOSで使えるオープンソースのPDFビューアです。PDFビューアは自分で作ろうとするとかなり大変(私は挫折しました)ですが、「vfr/Reader」を使えば自分のアプリに手軽にPDFビューアを組み込むことができます。
本エントリ執筆時点では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
また出力を見るとReaderViewController
で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等で有志が解決法を示してくれていれば良いのですが、そうでない場合は問題の報告をする、自分で解決する、代替手段を見つける等の必要が出てきます。
オープンソースソフトウェアは便利ですが、想定外のことが起きた時のリスクは厄介ですね。