EPUB/PDFへの変換を自前で行う
今回は、電書の原稿フォーマットとその中心になるマークアップ言語YDMLの概要、そしてそのパースについて説明します。
電書部では当初から、原稿からEPUB/PDFへの変換を自前で行う方針でした。したがって、原稿フォーマットの要件も自分たちで決めなくてはなりません。一つの原稿からPDF/EPUBの両方を自動的に生成できることの他に、何が必要でしょうか。
飲み会やSkypeチャットで検討や議論を行い、原稿フォーマット案を作り、実際に変換してみるなかで、だんだん見えてきました。そうして決まった、主な要件は次のとおりです。
- ルールが簡単で、誰にでも書ける。
- 技術班の助けをかりなくても、著者が見栄えを直接コントロールできる。
- 見栄えは新書や文庫程度のレベルで十分で、雑誌のようなリッチなものは不要。
電書部の原稿フォーマット
これらの要件を満たす、原稿フォーマットの概要を説明します。
ディレクトリ構造
ディレクトリを一つ用意し、ルールに沿った名前とフォーマットのファイルをフラットに並べます。画像ファイルがあればそれも同じ場所におきます。このディレクトリが、一冊の電書に対応する原稿になります。
電書情報
著者・書名などの情報を、okuduke.txtというファイルに書きます。
short_nameは、サーバ内で電書を識別するために使われます。
本文ファイルの名前
本文になるファイルは先頭に連番をつけます。拡張子はtxtです。
表紙にしたいページがあれば、そのページはcover.txtと名付けます。本文や表紙の内容はプレーンテキストでもかまいませんし、後述のマークアップを含んでもかまいません。
目次情報
EPUBリーダで表示する目次は、各テキストの1行目から自動的につくります。でも、それでは困る場合もあります。そういうときは、目次情報を持つファイルtoc.txtを用意します。
一行にファイル名と対応する見出しをカンマで区切って並べます。対応する見出しがないファイルには、目次が作られません。
ファイルへのマークアップ
本文ファイルおよびcover.txtの中では、簡単なマークアップ言語を使うことができます。以下で説明します。
電書マークアップ言語「YDML」
例
電書マークアップの例を次に示します。
電書部技術班ではこのマークアップを「YDML(Yonemitsu Densho Markup Language)」と呼んでいます。
タグ一覧
YDMLのタグは<>で囲まれた文字列です。閉じタグはXMLやHTMLと同じように </ からはじまります。
YDMLのタグ一覧
タグ | 閉じタグ | 説明 |
<left> | あり | 左寄せになる。 |
<center> | あり | センタリングされる。 |
<right> | あり | 左寄せになる。 |
<large> | あり | 大きなフォントになる。 |
<small> | あり | 小さなフォントになる。 |
<quote> | あり | 引用扱いになる。 |
<bold> | あり | 太字になる。 |
<sonomama> | あり | 囲まれたテキスト内のYDMLタグが、そのまま表示される。 |
<ruby> | あり | ルビをふる。たとえば、<ruby>蜻蛉/とんぼ</ruby>のように使う。 |
<bouten> | あり | 傍点をふる。 |
<img> | なし | イメージを表示する。ファイル名は<img="1.jpg">のように指定。また、<img="1.jpg" size="80%">としたときは、リーダの表示幅に対して、指定した比率の幅でイメージを表示する。sizeを指定しない場合は、30%で表示する。 |
<hasen> | なし | 破線を引く。 |
YDMLの考え方
YDMLはレイアウト指向で、文章の論理構造をマークアップするものではありません。そうなったのは、次のことが理由です。
- レイアウトは、著者がみずから行いたい。
- 著者の意に反したレイアウトが自動的に決まるようにはしたくない、という考えです。かといって、cssのようなものを採用すると「誰にでも書ける」という目標から遠ざかります。
- 文章の要素は、すべて論理的に意味が決まるとは限らない。
- 「この章見出しっぽいものは、実は本文でもある」という表現だって、ありえます。
実は<quote>タグだけはレイアウト指向ではないマークアップになってしまっています。「字下げ」タグにすべきだったかもしれません。
YDMLのパース
YDMLは簡単に書けますが、パースしづらいフォーマットです。プレーンテキストに時々タグが混じる形なので、XMLパーサではパースできません。Wiki記法のような行指向でもないため、改行を利用したパースもできません。
現在の電書変換では、正規表現をつかってターゲットのフォーマットに直接置換する方法を使っています。EPUBに変換しているコードの一部を以下に示します。
このやりかたには、いろいろと問題があります。例えば、次のような事柄が挙げられます。
- 閉じタグ忘れなどのエラーが検出できない
- EPUB変換とPDF変換で別々のコードになってしまうため、同じ原稿から別のトラブルが発生することがある
- YDMLの拡張に手間がかかる
あたらしいパーサを作ろう
これらの問題を解決するために、YDMLの新しいパーサを実装中です。まずパーサジェネレータをつくり、その上でYDMLを定義するアプローチをとっています。
パーサジェネレータ(Noratextという名前をつけました)は、次のことを目指しています。
- テキストに所々混じるタイプのマークアップを簡単に定義・パースできること。
- パース結果から、複数のフォーマットに簡単に変換できること。
現在のNoratextには3つの機能があります。
- 語彙の定義からレキサを生成する
- 文法の定義からパーサを生成する
- 生成規則の定義から変換コードを生成する
最新版は http://github.com/skoji/Noratext にあります。執筆時点で完成しておらず仕様も確定していないので、ここではNoratextの仕様や実装には立ち入らず、YDML定義の概要を説明をしていきます。
語彙定義
Noratextを使ったYDMLの語彙定義を次に示します。
symbolsに定義した名前で、<>で囲まれた文字がタグとして認識できるlexerが定義されます(現バージョンのNoratextは<>で囲まれたタグの定義のみに対応しています)。定義した後は次のように使います。
Lexer#processを呼ぶことで、ソースのテキストをタグと通常のテキストに分解した配列が返ってきます。この時点では閉じタグなどのチェックはしていません。
文法定義
次に、NoratextによるYDMLの文法定義の一部を示します。
これにより、YDML文書の構造を読み取れるパーサが定義されます。使い方は、次のとおりです。
documentを根とした、木構造ができているのがわかると思います。閉じタグ忘れなど構造に問題があれば、ここでチェックされます。
XHTMLを生成する規則の定義
そして、最終的なフォーマットを生成する規則の定義です。YDMLからXHTMLを生成する規則定義の一部を次に示します。
Processorというオブジェクトをつくり、そこにparserのパース結果(ParsedDataオブジェクト)を読ませる構造になっています。パース結果の要素に一対一対応で出力を生成します。
今後の予定
Noratextはひととおりの機能は実装していますが、まだまだ問題があります。特に「簡単にマークアップを定義できる」とは言い難いのが、一番大きな課題です。例えばparserのtext要素定義はずっとシンプルにできるはずです。do/endが多すぎるparser定義の書き方も気にかかります。まずはNoratextを利用したYDMLパーサを完成させますが、その後も「簡単に定義」を目指して大幅に変更していこう、と考えています。
なお、YDMLパーサそのものは、執筆時点ではまだ公開していません。EPUB変換とあわせて動作するようになり次第、githubで公開する予定です。
Noratextという名前
最後に、パーサジェネレータの名前について説明します。
「だれかによって権威づけされたテキストではなく、あたりを自由にうろつく"野良テキスト"が電書になる助けをする」というイメージを持って、Noratextという名前を選びました。新パーサを作ろうと考えてはじめた2010年7月ごろです。
同じ頃に初代電書部長・米光一成は、「ぼくにとっては、電書は「黒船」なんかじゃなくて、街を自由に巡る「野良猫」のような存在」と言っています。ここからNoratextという名前をとったようにみえるかもしれませんが、違います。Noratextをはじめたとき、私はこの言葉を知りませんでした。
とはいえ、「野良テキスト」という発想は、電書部活動を通して知った米光さんの考え方に明らかに影響を受けています。ですから、「電書は街を自由に巡る野良猫のような存在」って言葉からNoratextと名付けた、といってしまってもいいかもしれません。