読者です 読者をやめる 読者になる 読者になる

PDFの不遇なアクセシビリティのお話

ピクシブ株式会社 Advent Calendar 2016、23日目の記事です。

こんにちは、新卒エンジニアのrokurokuです。 普段はpixivコミックのアプリを開発しています。
今日は、去年リリースされた小説PDF化機能について、PDF内のテキストへのアクセシビリティを中心にお話しします。

小説PDF化機能ってなに?

小説PDF化機能は、pixiv小説に投稿した自分の作品をPDFに変換する機能です(非公開作品でも変換できます)。 投稿した小説を手軽にいい感じのPDFに変換できて、それを印刷できるというコンセプトをもって開発されました。 詳しくは小説PDF化機能をリリースしたときの記事をご覧いただければと思います。 なお現在のPDF化はAmazon Lambdaを利用して行われています。 詳細は弊社CTOの記事をどうぞ。

f:id:devpixiv:20161223021454p:plain 「吾輩は猫である」の一部抜粋

私が作ったのは、入力として(テキスト,設定,フォント)を受け取り、上のようなPDFを出力するプログラムです。 FreeTypeとICUを利用してC++で作成しました。 特別難しい技術を使っているわけではないので割愛して、PDFの話をしていきたいと思います。

そもそもPDFってなに?

Portable Document Formatのことを指し、どこでも同じ見た目で文書を参照できるように作られたファイルの仕様^1です。 PDFファイルの中には、線を引く、指定範囲を指定した色で塗りつぶす、グラデーションをかける、テキストを表示する、画像を表示するなどの命令が書かれています。 命令通りに描画命令を実行していくと環境に依存せず、同じレイアウトの文書を読むことができます。 最近はPDF.jsなどの台頭によって、ある程度のレベルまではブラウザ上でもPDFを描画するということが可能になってきました。

PDFの読み解き方

PDFの中身は、基本的にはASCII文字列ですので、テキストエディタで読むことができます。
——というのは半分嘘で、部分的にデータ圧縮が施されることが一般的なので、読むことは困難です。 圧縮を解除されたPDFを読みたい場合にはqpdfが便利です。インストールしてqpdf --stream-data=uncompress inputFileName outputFileNameとすると解凍することができます。 ただし、この場合でも画像のRGBデータやアルファチャンネルは単なるバイナリ列で保存されますし、JPEGファイルがそのまま埋め込まれることもあります。 またフォント・動画・音声などは、その仕様がPDFの外で定義されていて、それらはバイナリデータです。 そのため圧縮を解除しても読むことは難しい場合がありますが、大部分は仕様を片手に読むことができます。

PDFの中で文字を表現するたくさんの方法

PDFの基本的なファイル構造については数多くのサイトで解説されていることなので、この記事では「テキスト」にフォーカスを当てていきます。

普段はあまり気にされませんが、テキストは非常に表示の再現性が低いメディアです。 テキストの描画が表示されるそのときに実行されるからです。 このためテキストの表示は、環境の差異によって結果が変わってきます。

  • 文字の形が格納されているフォントのインストール状況
  • そのフォントを描画するOSのAPIの実装
  • そのAPIを利用するアプリケーションの実装

こうした問題を解決して、どの環境でも同じ見た目を得ることができるファイル形式の一つがPDFです。 だからこそ印刷の入稿にPDFが使われているのです。

印刷結果だけを気にするのであればどんな方法でテキストを描画しても良いのですが、PDFビューワーなどで開いた際にはテキストがどう描画されているのかは重要な意味を持ちます。 具体的には、開かれたPDF内のテキストを選択できるか、コピーできるか、検索できるかという、人とコンピュータの間のインターフェースに違いが出ます。

PDFの描画結果として画面に表示される文字には、大きく分けると以下のようなものがあります。

  • フォントを描画した文字
    • OSなどで使用されるフォントなどを使用します
    • テキストを選択したりコピーしたり検索したりできます
  • PDFの描画命令で描画された文字
    • 文字のアウトライン化と呼ばれる処理がこれにあたります
    • フォントの文字のアウトラインをそのままPDFの描画命令に変換したものです
    • テキスト情報にアクセスすることはできません
  • 画像としての文字
    • スキャナで書類を取り込んだ場合などはこれにあたります
    • JPEGが埋め込まれることが多いです
    • テキスト情報にアクセスすることはできません
  • 画像としての文字(OCR処理などが入ってる場合)
    • OCRは光学文字認識のことです
    • テキストを選択したりコピーしたり検索したりできます
      • あっているかどうかはOCRソフトの精度に依存します
    • 文字情報は画像の上に不可視のテキストを描画することで実現されています
      • PDFのレンダリング命令で3 Trと指定すると輪郭も塗りつぶしもなくなります
      • それ以外は通常のフォント描画と同じことをします
  • Type 3フォントを描画したもの(特殊なフォント)
    • 外部のフォントファイルの仕様には依存しません
    • 各文字がPDFの描画命令になっています

このことからわかるように、見た目さえ小説らしくなればいいというだけであれば、PDF出力には様々な手段があります。 そしてその実装コストは手段によって大きく変わってきます。 ただ、個人的な思いとして、デジタルデータとしても残す価値のあるものになればと思い、小説PDF化機能のPDFではその内部構造にも気を配りました。

アクセシビリティの高そうなPDF

当たり前のことですが、pixiv小説はテキストがメインのコンテンツです。 このことからテキストとしての情報が破壊されることは避けたいと考えていました。 そこで採用したのがPDFの仕様にある、タグ付きPDF(TaggedPDF)というものです。

通常のPDFのアクセシビリティは極めて低く、コンピュータで適切にPDF文書から情報を取り出すのは非常に難しいです。 この問題を解決してくれそうなのがタグ付きPDFです。 タグ付きPDFはドキュメント内での描画要素全てに役割をタグ付けして、各要素が文書全体でどういう役割を持つのかを構造化した情報を含んでいます。

f:id:devpixiv:20161222221400p:plain タグ付きPDFの概要

例えば、通常のPDFでは行の途中で折り返しが起こると、見た目上の行と意味上の行を完全に区別することができなくなりますなってしまいます。 一方、タグ付きPDFでは複数のテキスト描画をまとめて一つの論理的な行として扱えます。 つまりこの情報を参照することで、オリジナルのテキスト情報を復元することができるのです。

もっとわかりやすいところでいうと、タグ付きPDFではPDF読み上げツールなどにおける読み上げ順序にも利用されます。

PDFを出力するライブラリは世にいくつもあるのですが、それらを使わなかった(使えなかった)理由がこのタグ付きPDFにあります。 タグ付きPDFを出力できて、かつUnicodeを適切に扱うことのできるライブラリは当時見当たりませんでした。 そのため、PDF出力エンジンを1から実装することにしました。

結果として、pixiv小説の小説PDF化機能で出力されるPDFにはルビや改ページのためのpixiv小説の特殊タグを除いて、入力されたUnicode列が正しく保存されるようになっています。

タグ付きPDFへの各種ビューワーの対応

対応するPDFリーダーではタグ付きPDFの情報を解釈して、適切に文書構造を取り扱ってくれます。 ただし、実際の挙動は以下のように不完全な感じです。

  • Windows 10の「リーダー」
    • 折り返し関係なく入力された文字列が復元される
    • 折り返しをまたぐ文字列が検索でき、結果を選択してくれる(ページをまたぐと検索してくれない)
  • macOSの「プレビュー」
    • 折り返し、改行関係なく半角スペースがコピーされる
    • 折り返しやページをまたぐ文字列が検索できる(文字列は選択されず結果のページしか出してくれない)
  • Adobe Acrobat Reader DC
    • 折り返しでも改行がコピーされる
    • 折り返しをまたぐ文字列が検索でき、結果を選択してくれる(ページをまたぐと検索してくれない)

どうにも個性の強いPDFビューワーが多いようです。 Adobe Reader時代はもう少し良く動いていたのですが、アクセシビリティは難しいですね。

まとめ

今回は、小説PDFをタグ付きPDFとして出力することで論理構造を持たせた話をしました。 タグ付きPDFはPDF文書にアクセシビリティという概念を持ち込むことができる素晴らしい仕様なのですが、利用者側の需要がないせいか、PDFビューワーの実装もまちまちなのが残念です。