pixiv insideは移転しました! ≫ http://inside.pixiv.blog/

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ビューワーの実装もまちまちなのが残念です。

ピクシブ株式会社 Advent Calendar 2016 まとめ

気になるけど全部読んでるヒマが無い!そんなあなたのために各記事をザックリとご紹介します。


ピクシブの新執行役員に直撃!CTO&CCO就任記念インタビュー

今月からCTO(最高技術責任者)になった高山と、CCO(最高文化責任者)になった川田がそれぞれ抱負を語っています。

こんな人にオススメ
CCOってなんじゃいと思ってる人
31歳の執行役員がどんな人達か気になる人
・いずれ経営者の視点で会社を支えたいと考えているエンジニア・学生さん

Railsのアップグレードでもう苦しまない

ある程度コストのかかるRailsアップグレードも、事前の心がけや普段からの工夫で簡単にしていけるんですね。

こんな人にオススメ
Railsのアップグレードでヒィヒィ言わされた経験がある人
YAGNIってなんだろうと思ってる人
日本酒が好きな人

Firebase Cloud Messaging (FCM) を利用してお手軽に通知を送る

Firebaseを使ったプッシュ通知の具体的な手順と、pixivアプリでの活用例について紹介しています。 通常なら一斉に送られるプッシュ通知ですが、各ユーザーに最適なコンテンツを届けるために個別にトピックを生成しているというのがスゴそう。

こんな人にオススメ
・Firebase Notificationは使ってるけどFirebase Cloud Messagingは使ったことがない人
・プッシュ通知によるABテストに興味がある人

LINE BOT: pixivコミックをおすすめしてくれる司書チャットボットを試作しました

発言の内容を理解して漫画のレコメンドを行うLINE BOTの作り方が紹介されています。 LINE BOTのカード表示って複数コンテンツのオススメに適してそうですね。

こんな人にオススメ
・LINE BOTの可能性が気になってる人
Microsoftの自然言語解析APIに興味がある人
・pixivの新卒エンジニアの力を見てやろうという人

Swift+CocoaPodsではじめてのOSS(ハンバーガーメニューを作ってみた)

iOS/Macアプリのライブラリ管理ツール「CocoaPods」向けに新しいライブラリを登録してみたという記事です。

こんな人にオススメ
・iOS/Macアプリ開発にもRubyのBundler的なものあったのか〜という人
・CocoaPodsを使っていてライブラリを作る方法が知りたい

CMSの編集ロックUI部分で元気に走り回るRiot.js

Riot.jsの基礎と、Fluxを実現するためにRiotControlを選んだ理由などについて書かれています。 技術的負債を解消し、機能追加も実現したというエピソードが語られていて良いですね。

こんな人にオススメ
・そもそもpixivisionってなんだろうという人
・Riot.jsに興味がある人
“jQueryによるDOM操作”や“複雑な状態遷移”に悩まされている人

BlenderとPythonで3Dモデルを動的生成してレンダリングする

pixivFACTORYサーバサイドでBlenderを利用している事に関連して、Blender上でPythonスクリプトから3Dモデルをいじる方法が紹介されています。Blenderでなにかしら自動化を行いたい時に読むと便利そうです。

こんな人にオススメ
・Blenderを日頃使っている人
Blenderで自動化したいと思っている人
・Generative Artに興味がある人(?)

割れ窓理論を導入してWebサービスのクオリティに直結した話

デザイン崩れの修正を、普段から書き溜めておいて週一で優先的に解消するようにしてみたという話です。 タスクが解消されるという点だけではなく、チームメンバーが普段からデザインについて意識しやすくなる点も良いですね。

こんな人にオススメ
・直したいと思ってるデザイン崩れを溜め込んでいる
・現場の人間に対して、小さいデザイン修正ってどう依頼すればいいんだろうと思ってる人
・割れ窓警察

AndroidのBottomNavigationってどうなのよ?pixivコミックのリニューアルを通じて考えてみた

“Androidアプリには珍しい「画面下部のナビゲーション」”を実現してみたという話です。 スクロール時に下部のナビゲーションが隠れるような動作は自前で作ろうとすると地味に手間になりそうですが、そこまで提供してくれているライブラリがあるんですね。

こんな人にオススメ
・Androidアプリのナビゲーション周りが気になってる人
無料で漫画読み放題「pixivコミック」ってなんだろうという人

pixiv開発を支えるVim (タグジャンプ編)

タグジャンプ(universal-ctags)が同名の定義に弱いので、良い感じにタグジャンプしてくれるVimプラグインを作ってみたという話です。 自分の道具を作れるエンジニアは強い

こんな人にオススメ
・Vim使い
・タグジャンプが同名の定義に弱くて困っている人

PHP開発のためのEmacs 2016 (pixiv <3 Emacs)

Emacsでの開発を効率化するために、どんな設定やプラグインを入れているか紹介しています。 補完、ファイルの絞り込み、ドキュメント参照などの汎用的なものはもちろん、最後にはpixivの開発に特化した設定も書かれています。

こんな人にオススメ
・EmacsでPHPやWebの開発をしたい人
・もっとEmacsを便利にしたい人

pixiv Sketchのフロントアーキテクチャ

ISUCON6本戦の元ネタにもなったサービスの構成について、具体的な利点・目的とともに紹介しています。 特に、WEBとモバイルアプリとでAPIを共通化できるのは魅力的ですね。

こんな人にオススメ
・ISUCON6本戦で悔しい思いをした人
・これからWEB&モバイルアプリの同時開発を控えている人

まだシングルスレッドでレンダリングしてるの? HTML5 CanvasとWeb Workerの最新技術

OffscreenCanvasWeb Worker、さらにそれらを組み合わせてCanvasに関連した描画処理をマルチスレッドで行う例が紹介されています。 サンプルではシングルスレッドとのパフォーマンスの差がなかったようですが、いずれゲームやドローツールなどでCanvasをゴリゴリ使う時には使えるかもしれません。

こんな人にオススメ
Canvasのパフォーマンス限界を超えたい
・そもそもWeb Workerってなんじゃいという人

Slackを情報収集に使おう!SlackにRSS/Atomを簡単に追加できるChrome拡張機能の紹介

SlackをRSSリーダーとして使う時に、登録を楽にしてみたという話。 コードも短く、処理の流れについても説明されているので、これからChrome拡張を作ってみたい人にも良さそうな記事です。

こんな人にオススメ
・Chrome拡張に興味がある人
・SlackをRSSリーダーとして活用している人

ES2016+ on herokuでSlackにイラストを流してユーザ像を感じ取る話

自社サービスの機能がどう使われているかを知るために、新規投稿をSlackへ流すBotをNode.jsで作ってみたという記事です。 async/awaitを使った非同期処理の直列実行をメインに、sleepを挟む処理なども書かれていて、これからクローラーを作る人にもわかりやすい内容となってます。

こんな人にオススメ
・Callback地獄になりがちな人
・Node.jsでBotを作りたい人

GitLabの運用方法をドーンと公開!!

ピクシブではGitLabもGitHubも使っていますが、最も大きなリポジトリはGitLabで管理されています。 この記事ではGitLabについて、LDAPを利用した認証方法、前段にGoogle認証を挟む方法、バージョンアップ時の注意点などについて書かれています。

こんな人にオススメ
・GitLabを利用している人
・GitLabの認証とかどうすべきなんだろという人

pixivのイラストで機械学習したらどうなるのっと ~pix2pixで自動彩色編~

機械学習の分野で最近話題になったpix2pixを用いて、イラストの自動彩色を試してみたという記事です。 pixivに公開されているイラストの中でも、フォーマットの決まっている企画をデータセットとして利用させていただいた形ですね。

こんな人にオススメ
・機械学習に興味がある人
・自動で色塗りしてもらえたらいいのになという人

2017年はGitLabだけで開発のタスク管理を完結するのも夢じゃない

タスク管理のためにGitLabが使えるのでは、というのを実際の運用をしながら得られた所感とともに解説しています。 ピクシブでは各プロジェクト(チーム)ごとにタスク管理の方法も任せられているので、こんなふうに小さく試していけるのが良いです。

こんな人にオススメ
・GitLabを利用している人
・いろんなタスク管理方法を知っておきたい人

Google Apps Scriptで業務を効率化! 大切なものは全てGoogle先生が作ってくれていた

サーバーレスでちょっとしたことをする時にGoogle Apps Scriptは便利ですよね。 Google Apps Scriptの基本的な解説をはじめとして、実際にどんなスクリプトを業務効率化のために利用しているのかを紹介しています。

こんな人にオススメ
・GoogleSpreadSheetを活用している人
・業務効率化のためにがんばってJavaScript書いてみようかなという人

ピクシブとニジエで野球!? — コードを書いて学んだ草野球チームの運営

野球が大好きなエンジニアが、草野球チームでの活動を通して、使ってもらえるWebサイトを制作するために奮闘した話。 ちゃんとチームメンバーの要望を聞きつつ自分のこだわりも反映させていく制作のフローは、今後の仕事でも活きてきそうですね。

こんな人にオススメ
・野球好き
・スポーツチームも上手く運営したいエンジニア
ピクシブとニジエの絡み(意味深)が気になる人

社内で勝手にアプリを作ってみんなでワイワイした話

欲しいアプリが無いなら作ればいいじゃない」という話。 アプリでもWebサービスでもそうですが、たまに新しい物を一から作ってみんなに見せてみると、自分も含めてテンションが上がりますよね。
記事中にあるSlackで見せた時のテンションにもご注目。

こんな人にオススメ
・ピクシブのものづくりのワイワイ感に興味がある人
・作ったアプリ見せたがりマン

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

タグ付きPDFを利用することで、構造化されたテキスト情報をPDFに持たせる事ができる、という話をpixivの小説PDF化機能と絡めて解説しています。
オチが少し悲しいです。

こんな人にオススメ
・PDF出力に興味のある人
・そもそも小説PDF化機能ってなんじゃろという人

みなさんはSwift3ですか?

pixivコミックアプリのSwift3対応を実例として、移行時に注意すべき点について解説しています。 エラーにはならないが望んだ挙動をしなくなる例があるのは怖いですね。

こんな人にオススメ
・これからSwift3化をする人
・そういえば最近Swift3化をして、なんだかおかしい挙動があったような…という人

pixivの事例で考える、脆弱性報奨金制度との上手な付き合い方

ピクシブが利用しているバグ報奨金プラットフォーム「BugBounty.jp」について紹介しています。 導入した理由や、報告例と対応例、さらには、おおまかな報奨金額の基準も記載されているので、これからバグを見つけようという方も見てみてください。

こんな人にオススメ
・ホワイトハッカー
自分の所でもバグ報告を受け付けたいと思ってる人


ピクシブ株式会社 Advent Calendar 2016 まとめ、いかがでしたでしょうか。
数行ずつの紹介でも1ヶ月分となるとなかなかの文量ですね!

今後もpixiv insideではエンジニアリングに関わらず情報を発信していきますので、2017年以降もぜひご注目ください。

この記事は、ピクシブ株式会社 Advent Calendar 2016 の22日目。
執筆者は、@moyashipan でした。