Skip to main content

PDFKit や CGPDF で読み込めない PDF ページがある

現象

  • アプリ内で PDFKit や CGPDF を使用して描画すると、真っ白になるページがある
    • 通常の描画 (PDFPage.draw())だと出ないが、サムネイル表示 (PDFPage.thumbnail()) だと出る場合がある
  • Mac の「プレビュー」でも同様に読み込めない
    • 本文とサムネイルについても同じような動作になる
  • Acrobat Reader では読み込める
info

素直に Acrobat 使うのが良さそうな気もするが、いろいろ調べた過程でコマンド類をまとめた (コマンドいろいろある)

問題判別

JPEG 2000 (JPXDecode) で埋め込まれた画像が含まれていないかを確認する
→ 含まれていたら、これが原因で PDFKit で開けない場合がある

Acrobat Pro を使用する場合

ツール → 印刷工程 → Preflight メニューから「JPEG2000」を検索

コマンドを使用の場合

MuPDF (mutool) を使用

bash
brew install MuPDF
mutool show -e NG.pdf grep | grep -i JPXDecode

→ 「JPXDecode」を含む行が出たら該当

txt
13 0 obj <</Type/XObject/Subtype/Image/BitsPerComponent 8/ColorSpace/DeviceRGB/Filter/JPXDecode/Height 1649/Length 94301/Name/X/Width 1120>> stream

※ NG のページのみ指定して調べたいなら、qpdf で部分取り出ししてから

txt
brew install qpdf
qpdf all.pdf NG.pdf --pages all.pdf 2-2 --

※ 他の形式と並べると

  • JPEG2000 ( /JPXDecode ) → NG の場合がある ・・・ JPX + SMask(透明)/ 16bit の組み合わせで、そうなりがちらしい (OCRの透明テキストとの組み合わせで?)
  • JPEG ( /DCTDecode ) → OK
  • Flate ( /FlateDecode ) → OK

対応

  • JPEG2000 をやめて、JPEG で PDF ファイルを保存し直せば良い

Acrobat Pro を使用の場合

  • 方法1. ファイル → 別名で保存 → 最適化された PDF ... → 圧縮=JPEG (JPEG2000 でなく) を選ぶ。互換性 = Acrobat 5 (PDF 1.4) にする
  • 方法2. ツール → 印刷工程 → Preflight → Fixups (修正) → Convert JPEG2000 images to JPEG

CLI コマンドを仕様の場合

bash
mutool convert -o OK.pdf -O compress-images=true,imagemode=jpg NG.pdf

※ 画質優先なら imagemode=lossless にすると可逆(Flate)と出来るが、元ファイルよりサイズが大きくなる可能性がある

→ 以下のような警告が出る場合、変換によって OCR やフォントまわりで何か崩れてゴミ文字が出るようになるかもしれない

txt
warning: FT_Get_Advance(HiddenHorzOCR,12434): invalid glyph index

→ その場合は テキストを全部消してしまう

bash
brew install ghostscript
gs -o OK-without-text.pdf -sDEVICE=pdfwrite -dAutoRotatePages=/None -dFILTERTEXT OK.pdf

→ (テキストを消したあとで、) ついでにコマンドで OCR し直したいなら、 ocrmypdf を使用

bash
brew install ocrmypdf tesseract tesseract-lang
ocrmypdf --redo-ocr -l jpn+eng --optimize 1 --output-type pdf OK-without-text.pdf OK-redo-ocr.pdf

tesseract-lang を入れて、 -l jpn+eng を指定することで日本語も判定されるようになる。精度よさそう

参考: iOS アプリでの実装メモ

以下のような判定をして、描画出来なかったページにプレースホルダを置くことにする。

  • 「コンテンツあるのに真っ白」状態でエラー判別
  • 「JPEG2000が含まれているか」を CGPDF.. で判別