shelffのアプリアイコン

iPadむけPDF読書アプリ、shelffのバージョン1.5をリリースした。

今回はハイライトなどのアノテーション改善という地味目の機能改善だ。地味だけど重要で、「Acrobat OCRのテキストを壊す可能性がある」という問題を修正したのだ。

これはAppleのPDFKitの問題で、PDFKitを使う限り避けられない。iPadのPreviewアプリでも再現する。サードパーティアプリでも、PDFKitを使っていれば再現するはずだ。

今回は、アノテーション保存にPDFKitを使わず、Rust製のlopdfというライブラリを使うように変更した。PDFライブラリは有償のものがおおく、しかし買い切りな上にあまり本数も出ていないshelffで採用できるような価格ではない。lopdfはMITライセンスのOSSなので採用できた。lopdfに修正をいれる必要があり、本体にpull requestを送ってマージしてもらった。

まず、PDFKitの問題についてすこし詳しく書く。

PDFKitによるアノテーション書き込みは、PDF全体を保存する。このとき、Adobe AcrobatのOCR文字をただしく解釈できずに、壊してしまう場合がある。日本語OCRだとほぼ確実に壊れる。欧文文字だけなら、壊れないようだ。

PDFにはincremental updateという仕様がある。変化した部分だけを末尾に追記していく方式だ。これを使うと、既存のデータを壊す可能性は低くなるが、PDFKitにはincremental updateのAPIが存在しない。これがlopdfを採用した理由だ。実はPDFKitというかQuartz PDFには、内部的にはincremental update機能があるようで、macOSのプレビューアプリでアノテーションを書いた場合はincrementalだ。そしてiPadでも、なぜかQuickLook経由でアノテーションを書きこむとincremental updateになるのだ!プレビューアプリではやってないのに…

そして、lopdfにいれた修正について。

いくつかの候補からlopdfにしぼり、incremental updateを試作したところうまくうごく。しかし手元のPDF書籍をいくつかためしてみると、アノテーションの保存後に全体が白紙になってしまうものがあった。これはパスワードなしでも開けるが暗号化されているPDFで、lopdfのincremental updateが暗号化に対応していなかったのが原因だった。incremental部分が暗号化無しとして書きこまれてしまい、しかし全体としては暗号化されたままなので、復号できず白紙になっていた。データ自体が消えたわけではない。

最初は「暗号化されていたらPDFKitにフォールバックする」という実装をいれて避けようとしていたが、そうするとAcrobat OCR + 暗号化のPDFは変わらず救えない。そもそもlopdfのincremental updateが、暗号化PDFにたいして正しく動かないというバグなので、lopdf自体を修正することにした。修正するからには、本体に採用してもらったほうが楽だ。ということでPRを送ったら数時間でマージされた。lopdfはPRを放置しない方針のようで、これは助かった。

今回の作業のなかで、Acrobat OCRを壊す以外にも、PDFKit特有の問題にいくつか遭遇した。その一部について簡単に書いておく。

まず、テスト用に探してきた特定のPDFで、アノテーションを書いて保存しようとすると利用メモリが増えつづけてメモリ不足で落ちる、という現象が起きた。これはPreviewアプリでも起きる。何が起きているのか詳しくはみていない。これも今回の修正で問題なく書きこめるようになった。

それから、縦書きのアノテーションについて。PDFKitでは縦書きを「一文字ずつの横書き」とみなしてアノテーションをつける。ハイライトは一見ちゃんと縦にみえるが、Acrobatなどで開くと、一文字ずつ個別にハイライトされていることがわかる。shelffの今回のアップデートでは、縦書きのアノテーションをちゃんと縦書きになるようにした。この時の座標指定順序などで色々あった。

PDFKitでは縦書きの傍線を一文字ずつ横に書いてしまうのでかなり残念なことになる。ちゃんと傍線も縦にするように最初はshelffを修正した…のだが、残念ながらそれはPDFKitでは正しく表示できないことがわかった。Acrobatやpopplerでは表示できるのだが。Acrobatで縦書きPDFに付与した傍線も、PDFKitでは同じように表示できない。そしてなぜか、QuickLookや、Previewのサムネイルでは縦書き傍線が正しく表示できるのだ、なんだよそれ! PDFKit本体、Previewのサムネイル、そしてQuickLookはそれぞれレンダラが異なるようで、「サムネイルだけおかしい」というパターンもいくつかあった。このあたりはよくわからないので追求していない。

だいぶ苦労したので、lopdfをつかったアノテーション書きこむライブラリは独立したOSSとして公開するつもりでのんびり準備している。