golden-luckyの日記

ツイッターより長くなるやつ

XMLからEPUBを作る

昨日までの話をふりかえってみます。

  • 構造化文書というと、どうしてもXMLタグの書式で構造を示すあの世界観が想起されやすい
  • しかし、書式はあくまでも記法にすぎないと思うことにして、構造のほうだけ抽象データ型でかっちり用意するという世界観もありうる。これはPandocやDocutilsでいちおう成功を見ている

このアドベントカレンダーでは、後者の世界観を「ライトウェイト構造化文書」と呼んでみました。 特にPandocの成功については、代数的データ型、パーサコンビネータ、パターンマッチを兼ね備えたHaskellに依拠する部分が大きかったのではないかなと個人的には思っています。

今日は、そんなHaskellEPUBを作ろう、という話です。 PandocにはEPUB Writerもありますが、痒い所に手が届く感じではなかったので、自分で生成器を作ることにしました。

HaskellXMLを扱う

EPUBの中身はXMLです。したがって、EPUBを作るにはXMLを扱う仕組みが必要です。

ここで、Pandoc構造のような抽象データ型とXMLの関係にちょっと思いを馳せてみます。 抽象データ型をXML的に観れば、あるセマンティクスをひとつ固定してその構造を代数的データ型で表したもの、といえます。 ということは、XMLを抽象データ型の観点で見れば、抽象データ型をさらに抽象したもの、といえるでしょう。 そのような対象を扱う手段として、HaskellにはArrowという仕組みがあります。 そして、このArrowを利用してXMLを扱うライブラリとして、HXTというものが使えます。

HXTそのものについては、時間の都合上、この記事では説明を省略します。 かなり昔に書いた記事がいまでも役に立つと思うので、使ってみようと思う人はそちらを参考にしてください。

HXTを使ってXMLからEPUBを作るアプリケーションを作りました

PandocにはEPUB Writerがあるので、たとえばMarkdownの原稿からEPUBを作るなら、素直に pandoc コマンドを使えば十分です。 しかし、野良XMLからEPUBへの変換でPandocを使うのは、個人的にはあまりメリットがないと考えています。 入力もXMLであるような場面で、わざわざその構造をPandoc構造にいったん潰してしまうのは、あまりうまくないからです。 原稿がXMLっぽいもの(HTMLを含む)なら、その構造を目いっぱい使ってEPUBを作りたいものです。

っていうふうに書くと、「EPUBの中身はXMLなのだから、元のXMLをそのまま使うだけではないのか」と思う人がいるかもしれません。 しかし、それだけだとふつうはEPUBにはなりません。 確かに、データとして見れば、EPUBは「XMLをZIPしたもの」です。 しかし、コンテンツとして見ると、Webブラウザなどで閲覧する前提で書かれたXML(あるいはHTML)と書籍として公開する前提のEPUBとでは、いろいろ異なる点があります。 そのためちょこちょこ細工が必要になるのです。

実際にEPUBを自分で作ってみると、面倒なのはこの辺の細工だなというのがわかると思います。

  1. ヘッダとか図のキャプションに文字列や連番を追加する
  2. そのヘッダに付加した連番で、本文から章や図を参照する
  3. 論理目次と物理目次をはじめとするメタ情報を作成する

1つめの話は、たとえば<h1>要素の頭には「第1章」、その下の<h2>要素の頭には「1.1」、図には「図1.1」などの文字列を連接するということです。 そんなのCSSでやればいいじゃん、と思うかもしれませんが、CSScontentプロパティが使える広義のEPUBリーダー(Kindleも含む)は一部なので、これはコンテンツのHTMLのほうに埋め込む必要がある、というのがEPUB作成者の間では一般的な認識だと思います。 また、そうやって生成した連番のテキストは、場合によってはそれを参照している側にも付加する必要があり、これが上記でいうと2つめの項目です。 これらの作業には、当然、(テキストではなく)XMLに対する操作が必要になります。

3つめの話は、読者が目に見える部分だけじゃなくて、そもそもEPUBの仕様的に必要なメタ情報がいっぱいあります。 詳しくは他の資料などを参照してもらうとして、とくにめんどくさいのはOPFファイルと呼ばれるものの準備です。 このファイルに、中で使われている画像から何からすべての情報をきちんと登録しなければ、正しいEPUBファイルになりません。 そのための情報は元のXMLソースから抽出してくることになり、これにもXMLの操作が必要になります。

先にもちらっとふれましたが、元のXMLをいったんPandoc構造に落としてもかまわなければ、Pandocがこのへんの面倒をすべてみてくれます。 Pandoc構造にはHTMLを生で保持するデータ構造もあるので、それで十分なことも多いでしょう。

が、せっかくなのでその辺の処理もいちど自分で経験してみるかと思って、HXTでXMLからEPUBを生成するアプリケーションを開発しました。 仕事でEPUBを生成する必要があるとき、いまでは基本的にこれを使っています(中身がわかっているので手をいれやすい)。

ちなみに「qnda」という名前の由来は画面をひっくり返すとわかると思います。

このqndaを使って、「WikipediaのエントリをEPUB化するWebサーバ」をScottyというHaskellの軽量Webフレームワークを使って書いたこともあります。 HTMLではなくMediaWikiをソースにしているので、qndaのもともとのコンセプトは完全に失われていますが…

複数のエントリ(デフォルトでは3つまで)をフォームに指定してSubmitすると、それぞれを章とするEPUBファイルが返ってくるような仕掛けです。

f:id:golden-lucky:20191223182625p:plain

生成されたEPUBはリーダーでこんな感じに読めます。

f:id:golden-lucky:20191223182656p:plain

EPUB、売ってないじゃん

というわけでHTMLに変換可能な原稿からは技術的にEPUBを自力で生成できる当社ですが、いまのところEPUBの商品は販売していません。 Amazon Kindleでも販売していないんですが、これには次のような理由(というか言い訳)があります。

現状、EPUBが真につらいのは、生成することよりも、リーダーごとの動作検証だと思います。 PDFでさえ特定のビューワーが思いもよらない動作をする場合があるのですが(とくにApple系)、これにEPUBリーダーでの動作検証をして販売できるモノを準備する余裕がありません。 また、もしモノは用意できたとしても、管理する商品形態が1つ増えるのは当社の体力的につらく、そのために残念ながら見送っているのが実情です。 EPUBも欲しいという声があるのは承知しているのですが…。(そもそもEPUBに未来はあるのでしょうか…)

ちなみにKindleも事情は同じで、生成はできても、販売するために解決しないといけないバックオフィス上の課題がいくつかあります。

以上、ドキュメント屋さん的にはいろいろ面白いEPUBだけど出版社的にはちょっぴり難しい判断がある、という話でした。

明日の記事の予定はまだ決まっていません。