Web技術ではじめるレポート・論文制作

Category: dev

この記事はKobe University Advent Calendar 2017の6日の記事です。 なお私は当該大学の学部3年(2017年12月現在)です。


Vivliostyleやその他のWeb技術を用いて、HTMLとCSSで課題のレポートを書くための環境を作る話です。 あと「CSSではじめる同人誌制作」を勝手に販促する記事でもあります。

HTML+CSSによる組版

個人的にはWordもTeXもあまり得意ではない、というか、HTML+CSSで似たようなことができそうなのにわざわざWordやTeXを使うのが大変だと感じています(InDesignはよく知りません)。 文書構造の明確化という観点においては、構造と見た目が明確に分離しているHTML+CSSに勝るものはありません(筆者個人の主観です)。

そこでWeb技術を用いてレポートや論文を書きたいなあと思っていたのですが、ちょうどセキュキャンの事前準備で行けなかったときのコミケで、pentapodさんより「CSSではじめる同人誌制作」が頒布されていたので、友人に頼んで確保しておいてもらいました。

内容については同人誌制作だけをターゲットに絞らず, できるだけ色々な場面で使えるような説明を心がけています.
というあとがきの記述通り、CSS組版全般、また単にCSSの解説本としても読めるようになっており、本書を参考にしてレポートを書くための環境を整えることができました。 同人誌でもレポートでも論文でも、やりたいことは結局同じ組版であるため、整えるべき環境は変わりません。

この記事で書くこと

CSSを用いた組版については、「CSSではじめる同人誌制作」を直接読んでいただくのが一番早いため、今回はあまり詳しくは書きません。 300円というお手頃価格で電子版も出ているので買ってください。 これは販促です。

そこでこの記事では、本書を参考にしながら行った環境構築を中心に書いていきます。 構築した環境は https://github.com/Tatamo/htmldtp に上げていますが、「そもそもボイラープレートという単語を数日前にはじめて知った」レベルのためディレクトリ構成ひとつとっても正直褒められたものではないと感じています。 なのでまたちゃんと体裁を整えたいと思います。

Vivliostyle

とはいえ、Vivliostyleについては触れておかないと話が始まりません。 これはWeb技術による組版を目的としたプロジェクトで、CSSの将来的な仕様を先取りすることで、より多彩なレイアウトを実現できるようにしているものです。 サンプルが充実しており、実際にCSSによってデザインされた本と、その生のHTMLを閲覧することができます。

こういうレベルのことがCSSで既に可能となっているのです。

Vivliostyleを用いた印刷を行う方法は何種類かありますが、Chrome拡張を用いるのが最も簡単だと思われます。 目的のHTMLファイルを表示した状態で拡張機能を有効化するとレイアウトが紙面向けのものに変換されるので、これをChromeの印刷機能を用いて紙面やPDFに印刷します。

そのため、Vivliostyleに対応したCSSファイルと、それを適用するための原稿となるHTMLファイルを用意することが目的となります。 HTMLとCSSの状態からPDFを生成する作業を自動化する方法についても「CSSではじめる同人誌制作」では解説されていますが、今回は省略するので本書を購入してください。

恥ずかしい話ですが、最近のCSS3の仕様をしっかり追いかけているわけではないため、どこからどこまでがVivliostyleによって実現されている機能で、どこまでがブラウザの標準機能で既にサポートされている部分なのかがよくわかっていません。 このあたりは書けば書くほどボロが出るに違いないので適当にごまかします。

フレームワーク選定

書籍ではPug+marked+Stylus+Prism.js+MathJax+Browsersync+Gulpの構成が紹介されていましたが、このあたりは好みだと思うので適当に選んでいきました。

結論としては、Nunjucks+nunjucks-markdown+PostCSS(cssnext+stylelint)+highlight.js+MathJax+Browsersync+Gulpという構成になりました。

Nunjucks

まずはHTMLテンプレートエンジンを選びます。 変数を用いた操作や別のHTMLの継承などの機能を取り入れることで、HTMLをより書きやすくして開発の高速化・再利用の促進を図ります。

今回はNunjucksを使うことにしました。 昔Djangoを触ったことがあるので見慣れたテンプレートであるという点などが採用理由です。 他には以前使ったことのあるEJS、紹介されていたPug(Jade)なども候補に上がりました。

<!-- template/_base.njk -->
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>{{ title }}</title>
</head>
<body>
{% block content %}
    <section>
        <p>default content</p>
    </section>
{% endblock %}
</body>
</html>
<!-- index.njk -->
{% extends "template/_base.njk" %}
{% set title = "awesome-report" %}
{% block content %}
<section>
    <!-- awesome-report-content -->
</section>
{% endblock %}

のような感じでHTMLにいろいろなものをくっつけていきながら書けるようになります。 Nunjucksで記述したHTMLは、.htmlのほかに.njkなどの拡張子を使ったりするらしいです。

nunjucks-markdown

ひたすら文章を書くのにいちいち<p>タグを書いたりしていられないので、Markdownを使えるようにすることが必要です。 Nunjucksを使うことにしたので、markedのNunjucks向けプラグインであるnunjucks-markdownを使うことにしました。

{% block content %}
<section>
    {% markdown %}
    ## PPAP
    I have a pen.
    I have an apple.
    ```js
    console.log("Apple pen!");
    ```
    {% endmarkdown %}
</section>
{% endblock %}

のように、{% markdown %}で囲った部分をMarkdownとして書くことができようになります。

PostCSS(cssnext+stylelint)

HTMLだけでなくCSSも次世代仕様を使用できるようにしておくと便利です。 SassやStylusという選択肢もありますが、この次世代CSS過渡期において独自記法マシマシのcoffeeとあくまでES2015をもとにしたtsならどちらを使うかという話です。 一通り必要な機能が入ったプラグイン集であるcssnextと、lintをしてくれるstylelintを入れていますが、この構成だとSassのように@importできない点で不便さがあるので、CSSをモジュール化できるようにするためにpostcss-importも入れたほうが良いかもしれません。

これは余談です。

highlight.js

プログラムリストを貼り付けることが求められることもあるので、シンタックスハイライターを用意します。 他の候補としてはPrism.jsなどがあります。 今回はこのブログで使ったりして使い慣れているhighlight.jsを用いました。

MathJax

TeX形式で書いた数式を表示するために必須です。 MathJax以外の選択肢は特にないと言っていいでしょう。 適当なスニペットを<head>タグ内に追加し、

<p>MathJax test: $E = mc^2$</p>

などと書くだけで数式部分が整形されて表示されます。

どうでもいい余談ですが、個人的にはMicrosoft Officeの数式エディタは見た目もきれいですし非常に使いやすいと思っています。 これについてはあまり同意されたことがない気がします。

Browsersync

ソースコードが更新され(、そしてそれを検知してビルドが走ってdist/ディレクトリが更新され)たことを検知し、Browsersyncが自動でブラウザを再読み込みしてくれるようにします。 わざわざF5を押さなくても変更が反映され、ビルド結果がすぐにブラウザ上で見れて便利…… なのですが、今回はブラウザを更新したあとVivliostyleのChrome拡張を有効化しなければならず、結局F5相当の動作を手で行わなければならないという難点があります。

Gulp

ビルドを自動化してくれるようにするためのタスクランナーです。 Gulpは使っていますがGruntは使ったことがないです。 タスクランナーとは、hogeというパッケージと一緒にgulp-hogeを入れなければならなくなってpackage.jsonの依存パッケージ数が2倍近くに膨れ上がるという、とても素敵なものです。

最近は何でもかんでもnpm-scriptsでやろうという話があって、確かに一理あるなあという感じなのでGulpじゃなくてもよかったかもしれません。

全部突っ込む

JavaScriptから利用するためのパッケージを一つも入れていない、というかJavaScriptを書くつもりがない(gulpfile除く)のに依存パッケージ数が10を越えるのはどういうことなんだ、という感じですが、とりあえず上記のツールを全部組み合わせます。 highlight.jsとMathJaxに関しては、<script>タグで読み込むだけでいいのでnpm installは不要です。 Nunjucksのテンプレート継承機能を用いて、これらの読み込み用のスニペットをモジュール化しておくのがいいでしょう。

Nunjucks(+nunjucks-markdown)とPostCSSを用いて、それぞれHTMLファイルとCSSファイルを生成する処理が行われるので、これをGulpのタスクとしてまとめてしまいます。 あとはソースコードの更新を検知して自動的にこのビルド処理が走るようにしたり、ビルド完了のタイミングでBrowsersyncに更新を伝えたりする処理をgulpfile.jsに書いて終わりです。

使ってみた

大学でパーセプトロンの出力を求めるだけのレポート課題が出たので、構築した環境を用いてレポートを書いてみました。

以下に、Vivliostyleを用いて出力されたPDF(のスクリーンショットにモザイクかけたただの画像)を示します。

PDFに出力されたレポート

図はLibreOffice Drawで用意しましたがそのあたりは適当です。

これに用いたCSSは、

@page {
  size: A4;
  margin: 20mm;
  @bottom-center {
    content: counter(page);
  }
}

figcaption {
  counter-increment: fig;
}

figcaption::before {
  content: "図" counter(fig) ". ";
}

/* うまく動かない(後述) */
a.figref::after {
  content: "図" target-counter(attr(href), fig);
}

img {
  max-width: 100%;
}

だけであり、非常に簡潔なCSSで十分に見た目の整ったレポートを書くことができます。

そういえば指定するのを忘れていましたが、当然font-sizeでフォントサイズをポイント指定することも可能です。

相互参照と問題点

図を示している部分のHTMLを抜粋すると、以下のようになります。

<section>
    {% markdown %}
    ## パーセプトロンの提示
    まず、パーセプトロンの修正後の重みを<a class="figref" href="#fig-nn"></a>に示す。
    <figure>
        <img src="perceptron.png">
        <figcaption id="fig-nn">パーセプトロン</figcaption>
    </figure>
    簡単のため、パーセプトロンのそれぞれのノードに番号を付与した。
    {% endmarkdown %}
</section>

figcaption::beforecontentcounter(fig)を用いることで、自動的に図表番号を振ることができます。

それに加えて、figcaptionの持つidに対するリンクのcontentにtarget-counter(attr(href), fig)を指定しています。 これはattr(href)—つまりhrefの属性値、"#fig-nn"—の地点でのcounter(fig)のカウンタの値を取得しており、つまりその図表に割り当てられた番号が得られます。

これによって相互参照が実現できます。

と言ってドヤ顔したいところなのですが、ここで致命的な問題が発生します。 なぜかChrome拡張のVivliostyleでは、このtarget-counter()がうまく動かず、図表番号が??で表示されます。

他の手段であるVivliostyle FormatterやVivliostyle Viewerを使えば動かせはするのですが、その場合highlight.jsによるシンタックスハイライトが効かなかったり、MathJaxは対応しているもののMathML以外の記法を使えない、などの別の問題が生じてきます。

なので現状、a.figref::afterに対するスタイル指定を削除し、<a class="figref" href="#fig-nn">図1</a>などと直接書くしかないという本末転倒感があります。

これについては今のところ解決策を見つけられていないので、もう少し調べてみて必要ならissueを飛ばすなりしようと思っています。

おわり

レポート・論文程度ならそこまで凝ったデザインである必要がないので、非常に短いCSSでそれなりの印刷が可能になります。 HTML+CSSを用いてレポートを書くというのは十分に実用の範囲内だと思ったので、これからも試していこうと思っています。

しかしながら、やはり現状で相互参照が実現できないというのはレポート・論文執筆には致命的なところがあります。 target-counter()は策定中の次期CSS仕様であることから、解決は時間の問題だとは思っているのですが、なんとかしたいところです。

今の時代のCSSは、もはやWebページだけでなく紙面上のレイアウトも自在に操れるようになってきています。「CSSではじめる同人誌制作」はCSSを使ったさまざまな紙面レイアウトについて書かれており、とてもおすすめなのでぜひ購入してみてください。