Vivliostyleが拓くCSS組版の可能性

Vivliostyleでなにができるの

Vivliostyle(ビブリオスタイル)はCSS組版を実現するオープンソースのライブラリ/アプリケーション群です。これから始まるこの連載は、そんなVivliostyleの魅力や可能性にさまざまな方向から迫ってみようという試み。まず第1回としてCSS組版とはなにか、そしてVivliostyleでどんなことが、どのようにしてできるのかをご紹介します。

CSS組版ってなに?

そもそも組版とはなんでしょう。一言でいえば文字・図版・写真などをページ上に配置することです。この記事では、とりわけCSSやHTMLといったWeb技術をつかうCSS組版を取り上げます。その最大の特徴はページ区切り(pagination⁠⁠。ブラウザーではコンテンツをスクロールして読み進みますが、CSS組版ではページをめくることで読み進みます。結果として、CSS組版は印刷用データ(PDF)を作成する方法でもあります。現在CSS組版のユースケースの多くはこれです。

また、意外に思われるかもしれませんが、Vivliostyle以外にもCSS組版のアプリケーションはたくさんあります(★はオープンソース⁠⁠。

上記にないVivliostyleの魅力は、縦書きを始めとする日本語組版に強いこと、そしてオープンソースであることです。

Vivliostyleの特徴と基本的な仕組み

Vivliostyleのアプリケーションは層状に積み重なったライブラリ/アプリケーションの組み合わせです。その構成を図で説明しましょう。まずもっともベーシックな、Vivliostyle Viewerをみてみましょう図1⁠。

図1 Vivliostyle Viewerの構成
図1

図を見ると、2つの層に分かれていることが分かります。上層にあってブラウザーとともにHTMLとCSSを処理し、ページに組み立てるのが、組版エンジンであるVivliostyle.jsです。楕円をブラウザーと半分ずつに分割しているのは、Vivliostyle.jsはブラウザーのポリフィル(機能を補完するコード)として動作するからです。他方、Vivliostyle ViewerはVivliostyle.jsをライブラリとして使いながら、その組版結果を表示するのです。

この後ご紹介する2つのアプリケーションでも、最上位にVivliostyle.jsとVivliostyle Viewerの組み合わせがある構成は変わりません。つまり、どのアプリケーションも組版エンジンとプレビュアーは共通というわけです。そこで次に、Vivliostyle CLIをみてみましょう図2⁠。

図2 Vivliostyle CLIの構成
図2

前述したVivliostyle Viewerはそのままに、その下に新たな層が2つ追加されています(なお、⁠ブラウザー」の部分はChromiumを利用します⁠⁠。1つはMarkdownをHTMLに変換するVFM(Vivliostyle Flavored Markdown)とスタイルシートのテンプレートであるThemes[1]の層、その下の層がPDFとEPUBを出力するVivliostyle CLIです。

Vivliostyle CLIは以下のような機能をもっていますが、それらは丸括弧内のVivliostyleプロダクトの機能を利用しています。

  1. MarkdownをHTMLに変換する(VFM)
  2. スタイルを切り替える(Themes)
  3. 複数の原稿から本の形にまとめる
  4. HTMLとCSSを処理し、ページに組み立てる(Vivliostyle.js)
  5. CSS組版の結果をプレビューする(Vivliostyle Viewer)
  6. PDFとEPUBを出力する

このことから、Vivliostyle CLIにおいて、Vivliostyleの各プロダクトが1つに統合されているとも言えるでしょう。では最後に、Vivliostyle Pubをみてみましょう図3⁠。

図3 Vivliostyle Pubの構成
図3

Vivliostyle CLIはそのままに、下に1つ新たな層が追加されています。この層の実体はクラウドです。つまりVivliostyle Pubとは、Vivliostyle CLIをクラウド上にそのままデプロイし、ユーザー管理その他必要な機構を加えてWebアプリとして動作させたものなのです。

ここでもう一度、組版エンジンであるVivliostyle.jsが、ブラウザーのポリフィルであることを考えてみたいと思います。このことは、もう1つのVivliostyleのメリットを導き出します。詳しくは後述しますが、元来CSS組版では、ブラウザーがまだ実装していないCSS仕様にもとづいて組版します。これらの処理は組版エンジンであるVivliostyle.jsが担当します。加えてVivliostyle.jsは、ブラウザーの組版エンジンによるCSSの機能を利用できるように作られています。これにより、Vivliostyle.jsが独自には実装していないCSS機能であっても、ブラウザーさえ対応していれば組版に利用できるのです。

結果として、Vivliostyleは日進月歩の勢いですすむブラウザのCSS対応を、そのまま自分のものにできるという訳です。ただし、これについてはブラウザが変わると組版結果も変わりかねないデメリットがあることも忘れてはいけません。

実際に使ってみよう

Vivliostyle CLIを使って実際にCSS組版にチャレンジしてみましょう。前提としてNode.js v16以上のインストールが必要ですので、まだの方は準備をお願いします。なお、筆者の環境はmacOSですが、Windowsでも同様の結果が得られます。

では、ターミナルでサンプルファイルのあるディレクトリに移動した上で、Vivliostyle CLIをインストールしましょう。

% npm install -g @vivliostyle/cli

この記事のために青空文庫からサンプルを作ってみました。以下からローカルにクローンしてください。

% git clone https://github.com/MurakamiShinyu/shokubutsu_ichinichi.git
% cd shokubutsu_ichinichi

うまくいったら、さっそくCSS組版でプレビューしてみましょう。

% vivliostyle preview shokubutsu_ichinichi.md

自動的にChromiumが起動して、下記のような画面が表示されれば成功です図4⁠。

図4 Vivliostyle CLIのプレビュー表示
図4

画面中央右端の「>」をクリックするとページがめくれます。この他ページのナビゲーションは画面上端左のアイコンからでも可能です。すべて紹介することはできませんが、さまざまな機能があるので試してみください。

ページをめくったところでウィンドウを横長に広げてみてください。下記のような見開き表示になります図5⁠。

図5 プレビューにおける見開き表示
図5

最後にPDFファイルを出力してみましょう。ウィンドウを閉じてプレビューを終了させてから、以下のように入力してください。

% vivliostyle build shokubutsu_ichinichi.md -o shokubutsu_ichinichi.pdf

今いるディレクトリに、下記のようなPDFファイルが生成されているはずです図6⁠。

図6 Vivliostyle CLIから出力したPDFファイル
図6

なお、Vivliostyle CLIは前述したようにEPUBが出力可能です。これについても本連載の中でご紹介していくつもりです。

Markdownファイルの中身

では、サンプルデータを詳しく見ていきましょう。まずは肝心の本文ファイル、shokubutsu_ichinichi.mdからです。

冒頭1行から8行まで、---で上下を囲んだ部分がフロントマター。

---
title: 植物一日一題
author: 牧野富太郎
lang: ja
link:
  - rel: stylesheet
    href: css/style.css
---

この部分はそのままHTMLではhead要素に変換されます。では、実際にどのようなHTMLファイルに変換されるか見てみましょう。まず以下のコマンドでVFMをインストールします。

% npm install -g @vivliostyle/vfm

インストールが成功したら、以下のコマンドで、MarkdownファイルをHTMLファイルに変換します。

% vfm ./shokubutsu_ichinichi.md > shokubutsu_ichinichi.html

生成されたshokubutsu_ichinichi.htmlを確認すると、先頭部分は以下のようになっています。

<html lang="ja">
  <head>
    <meta charset="utf-8">
    <title>植物一日一題</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <meta name="author" content="牧野富太郎">
    <link rel="stylesheet" href="css/style.css">
  </head>
  <body>

フロントマターで記述されていたtitle:author:lang:などのプロパティが、どのように変換されたか確認できます。とくに注目していただきたいのは、スタイルシートの読み込みもフロントマターのlinkでおこなっていることです。この方法以外にもvivliostyle.config.jsでスタイルシートを指定する方法がありますが、この記事では分かりやすさを優先してフロントマターで指定しています。なお、今回は使用していませんが、一部のプロパティは<body>の属性に変換されます。その他フロントマターの詳細は公式ドキュメントを参照してください。

さて、他にもVFMでお伝えすべきことはたくさんあるのですが、文字量の関係であと1つだけ、改行ルールについて書いておきましょう。shokubutsu_ichinichi.mdの、たとえば142-146行目を見てください。段落と段落の間に空行が入っています。これがHTMLではどのように変換されたか、先ほどのshokubutsu_ichinichi.htmlを見てみましょう。

        <p> 昔といっても文化五年(1808)の徳川時代に<ruby>小野蘭山<rt>おのらんざん</rt></ruby>という本草学者がいて、ジャガタライモを馬鈴薯であるといいはじめてから以来、今日にいたるまでほとんど誰もこれを否定する者がなく、ジャガタライモは馬鈴薯、馬鈴薯はジャガタライモだとしてこれを口にし、また書物や雑誌などに書き、これをそう肯定しているのが常識となっているが、それは大きな間違いであって、馬鈴薯はけっしてジャガタライモではないぞと今日大声で疾呼し喝破したのは私であったが、しかし蘭山がジャガタライモを馬鈴薯だといった後五年しての文化十年(1813)に<ruby>大槻玄沢<rt>おおつきげんたく</rt></ruby>は、この蘭山の考えている馬鈴薯をジャガタライモの漢名とするの説を疑い、これを<ruby>栗本丹洲<rt>くりもとたんしゅう</rt></ruby>に質問したが丹洲もまたその説を疑ったということが<ruby>白井光太郎<rt>しらいみつたろう</rt></ruby>博士の『改訂増補日本博物学年表』に出ている。</p>
        <p> 元来馬鈴薯というものは中国の福建省中の一地方に産する一植物の名で、それが『<ruby>松溪県志<rt>しょうけいけんし</rt></ruby>』(松溪県は福建省地方の地名)と題する書物に僅かに載っているが、それがどんな植物であるのかは中国人でさえもこれを知らず、またかろうじての右の県志のほか、ありとあらゆる中国の文献には敢て一つもこれが出ていない。すなわち得体の分らぬ一辺境の中国土産の品で、中国人でさえも一向に知らないオブスキュアの植物である。</p>
        <p> しかるにジャガタライモは元来外国産、すなわち南アメリカのアンデス地方の原産のもので、四三三年前の西暦一五六五年に初めて欧州に入り、後ち欧州から東洋に持ち来たされ、ついに我が日本におけると同様に中国にも入りこんだものである。この事実からみても、それが元来の中国植物である馬鈴薯ではあり得ない理屈ではないか。そして中国人はこの外来植物に対して適切な新命名の洋芋(洋とは海外から来た渡り者を意味する)あるいは荷蘭薯(オランダイモの意)などと称えていて、けっしてこれを馬鈴薯などと間違った名では呼んでいない。その間違いを敢てしているものはひとり日本人だけである。これはちょうど馬を指して鹿だといい、人を指して猿だといっているようなものであるから、この馬鈴薯をジャガイモと呼ぶことは躊躇なく早速に廃すべく、したがって馬鈴薯の名は即刻放遂すべきものだ。</p>

お分かりでしょうか。一般にMarkdownでは、改行2つでHTMLのp要素の区切りになります。改行が1つだけ(段落間の空行なし)の場合は無視されて、ひとつながりのp要素、つまり段落ではなくなってしまいます[2]

CSS組版のエッセンス⁠スタイルシート

スタイルシートを見ていく前に、CSS組版で使われるCSS仕様について確認しておきましょう。

  • CSS Paged Media Module Level 3
    • ページメディア用 CSS 基本仕様。ページ余白、ページサイズと向き、ページヘッダー/フッター、ページ番号など
  • CSS Generated Content for Paged Media Module
    • ページメディア用の生成コンテンツ仕様。柱(ページヘッダーに表示する章タイトルなど⁠⁠、脚注、クロスリファレンス(ページ番号参照)など
  • CSS Page Floats
    • ページフロート仕様。図版などを本のページの上部や下部に配置する

実際にリンク先を読むと分かりますが、これらはすべて勧告前のドラフトです。だからでしょうか、ブラウザーでは実装されていません。こうしてVivliostyleのようなプロダクトが生まれる余地ができたのですが、同時に現在のCSS組版の残念な状況も表しています。もっとユーザーを広げて、これらの仕様を勧告にまで改訂していくことが望まれます。

ではスタイルシートに移りましょう。前述した以下のコマンドで、再度プレビューを立ち上げてください。

% vivliostyle preview shokubutsu_ichinichi.md

その上でサンプルのスタイルシート、cssフォルダの中にあるstyle.cssを開きましょう。:rootセレクタで定義された中の6行目に、以下のようなコメントアウトされた記述があります。

:root {
  /* writing-mode: vertical-rl; */
  /* ... */
}

writing-modeは、CSS Writing Modes Level 3で規定された書字方向を指定するプロパティです。ここでは:rootセレクタで定義されたことにより、コンテンツ全体に効力が及ぶので「本文の基本設定」の1つということになります。

ただし、コメントアウトで無効化されたことで、現在は初期値のhorizontal-tb、つまり横書きでプレビューされています。では、このコメントアウトを外して保存してみましょう。自動的にChromiumがリロードされて、縦組に変わりました図7⁠。

図7 コメントアウトを外すだけで横書きから縦書きに変わる
図7

この他にもスタイルシートを上から下まで1つずつ説明していきたいのですが、文字量の関係でそうもいきません。最後にあと1つだけ、となるとやはりCSS組版の基本であるページネーションの指定をご説明します。ずっと後ろの方、266行-297行目を見てください。

@page {
  size: A4;
  margin: 25mm;
}

@page :left { /* 左ページ */
  @top-left {
    content: string(title); /* 柱:タイトル */
    writing-mode: horizontal-tb;
    font-size: 0.8rem;
  }

  @bottom-left {
    content: counter(page); /* ぺージ番号 */
    writing-mode: horizontal-tb;
    font-size: 0.8rem;
  }
}

@page :right { /* 右ページ */
  @top-right {
    content: string(chapter-title); /* 柱:章タイトル */
    writing-mode: horizontal-tb;
    font-size: 0.8rem;
  }

  @bottom-right {
    content: counter(page); /* ぺージ番号 */
    writing-mode: horizontal-tb;
    font-size: 0.8rem;
  }
}

最初の@pageは左と右ページの共通指定、2番目の@page :leftは左ページだけ、3番目の@page :rightは右ページだけの指定です。たとえば最初の@pageでは判型とページの余白サイズを設定していますが、これは左右ページ共通の指定です。margin: 25mmだと、左右ページの天地左右すべてのマージンが25ミリになる訳です(もしもノドと小口でマージンを変えたい場合は、左右ページごとに個別指定します⁠⁠。この辺りの@pageルールについては、前述 ⁠CSS Paged Media Module Level 3⁠⁠ の4.1. The @page Ruleを参照してください。

2番目の@page :leftでは柱としてタイトルが、3番目の@page :rightでは章タイトルが別々に指定されています。ここでは異なる箇所で定義されている変数titlechapter-titleを取得しています。それが下記の箇所、337行-343行です。

h1 {
  string-set: title content();
}

h2 {
  string-set: chapter-title content();
}

上記のようにtitleとしてh1(見出し1)の文字列が、chapter-titleとしてh2(見出し2)の文字列が定義されており、これを前述のcontent: string(…)で、柱として取得し、規定のスタイルで配置するよう指定されている訳です。

柱というのは書籍特有のナビゲーションの1つです。そこでは決まった文字列や番号を、ページごとに特定の場所に配置することで、読者に今読んでいる場所をナビゲートしているのですが、こうした昔からの仕組みが巧みに仕様化されていて、それをスタイルシートで利用していることが分かると思います。より詳細は前述 ⁠CSS Generated Content for Paged Media Module⁠⁠ の1.1 Named stringsを参照してください。

同様に、@page :leftの子要素である@bottom-leftにおいて、また@page :rightの子要素である@bottom-rightにおいて、それぞれ取得するノンブル(ページ番号)counter(page)として指定されています。

また、ここで記述されている@top-left@top-right、あるいは@bottom-left@bottom-rightというのは、⁠CSS Paged Media Module Level 3⁠⁠ で定義されている版面外の余白スペースの位置の名称です。@top-left「天-左⁠⁠、@top-right「天-右⁠⁠、@bottom-left「地-左⁠⁠、@bottom-right「地-右」になります。詳細は5. Page-Margin Boxesを参照してください。

まだまだCSS組版特有のCSS機能をご紹介すべきなのですが、連載初回のミッションは概説です。くどくなる前に切り上げることにしましょう。積み残した分は、この連載の後の方で再登場させていただく予定ですので、そちらで補えればと思います。スタイルシートでのコメントや、READMEになるべく説明を入れておきましたので、それらを手がかりにしてください。分からないことがあれば、私達のSlackにjoinして#q-and-aチャンネルで質問してください。大歓迎です!

おすすめ記事

記事・ニュース一覧