プログラマがあまりやりたがらない作業のひとつとしてドキュメント作成がよく挙げられるかと思います。 〇〇仕様書とか〇〇設計書とか〇〇手順書などの作成作業ですね。 特に成果物がdocxやxlsxのようなプレーンテキストでない場合、次のような理由で気が滅入る方も多いのではないでしょうか。

  • 手慣れた道具(エディタ)を使うことができない
  • 専用のソフトでよくわからない機能が裏で勝手に動いたりしてて気持ち悪い
  • 変更履歴をトラッキングしづらい、差分をマージしづらい

Markdownでさくっと書いてそれっぽいものができれば、GitHubのWikiなどにぼちぼち書き溜めておいて最後にコンパイルするみたいなやり方ができるなぁとか、そうすればわざわざドキュメント専用リポジトリを作ったりする必要もないなぁとかモヤモヤしながらいろいろ調査・検討したので以下の4構成でまとめたいと思います。

  1. Pandocについて
  2. Pandocを使って Markdown -> HTML, PDF を作成する
  3. Pandocを使って Markdown -> docx を作成する
  4. Pandocで作られたdocxをRubyで加工する

Pandocについて

PandocはHaskellで書かれたドキュメント変換ツールで、入力・出力ともに多彩なフォーマットに対応しているのが特徴です。
また、実行時に自作のフィルタをかませることが可能なので、独自の処理を施すこともできます。(例えば、特定文字列の一括置換など。)フィルタはRubyやPythonなどでも作成することができます。

詳細はUser’s Guide(日本語版)を読んでいただくとして、以降ではこのPandocを使う前提で記述します。

ちなみにPandocのMarkdownには多少の方言(拡張)があり、例えば以下のようなかたちで文書のタイトル、著者、日付を付けることができたり、

% これはタイトルです
% 株式会社ネットワーク応用通信研究所
% 2016年4月1日

...文書の開始...

また以下のように表のキャプションを付けることができたりします。

Table: これは表のキャプションです

| りんご | ぶどう | みかん |
|--------|--------|--------|
|    3個 |    1個 |    6個 |

このあたりもUser’s Guideに詳細が載っていますので是非ご一読を。

Pandocを使って Markdown -> HTML, PDF を作成する

まずはドキュメントの形式にあまりこだわりのない依頼主のケース。
ある程度整形された資料で十分なのであれば、HTMLにしてしまうのはどうでしょう。
WordのスタイルをポチポチいじるよりはまだCSSを書いたほうがいい、という方もおられるのではないでしょうか。

  $ pandoc origin.md -s --self-contained -t html5 \
    -c /path/to/doc.css -o output.html

--css (-c)オプションで外部ファイルのCSSを指定できるため、一旦いい感じのCSSを作ってしまえば使い回しができます。
--self-containedオプションを付けることにより、画像やスタイルシートなどはHTMLファイル内に埋め込まれるため、出力されたファイル単体で外部から参照していたコンテンツなども表示することができます。

入力:

% これはタイトルです
% 株式会社ネットワーク応用通信研究所
% 2016年4月1日

# これは見出し1です

これは本文1です。

![それっぽい画像です](./image.png)

上の画像は「いらすとや」様より以下の素材を利用させていただきました。

* サーバーのイラスト
* コンピューターを使いこなす子供のイラスト
* インターネットのイラスト

# これは見出し2です

これは本文2です。

Table: これは表1です

| t1h1 | t1h2 | t1h3 |
|------|------|------|
| t1c1 | t1c2 | t1c3 |

出力:

出力されたHTML

そうは言ってもやっぱり「HTMLはちょっと…少なくともPDFは欲しい…」みたいな話にはなりそうなので、PDFを作ります。 PandocはPDF出力もサポートしていますが、LaTeXを経由するのでそれっぽい見た目になります。
プリアンブルは--include-in-header= (-H)オプションを使って別ファイルで指定できるので、標準的な何かを作ってしまえばこれまた使い回すことができます。 また、--tocオプションを付けると自動で目次も作成してくれます。

  $ pandoc origin.md -o output.pdf -H preamble.tex \
    -V documentclass=ltjreport --latex-engine=lualatex

出力:

出力されたPDF

巨大な表などがあるとデフォルトでは見た目が残念で、TeX辛い勢的には整形が面倒だったりします。 TeX/LaTeXを書き慣れている人には、一旦TeXに変換した方がいろいろ融通が効いて良いかもしれません。
TeX辛い勢の最終手段としては上で作成したHTMLをブラウザで開いてPDFで保存という手も使えなくはないです。 (ブラウザで表示される通りの見た目でPDFに出力されるかどうかはブラウザ依存になってしまいますが。)

Pandocを使って Markdown -> docx を作成する

次に成果物としてMicrosoft Office文書を求められる依頼主のケース。

大体上のスライドに全部書かれているのですが、まとめると、以下でスタイル参照用のファイルを出力し、

  $ pandoc --print-default-data-file reference.docx > reference.docx

reference.docxのスタイルをいじって自分用の書式を作成します。(reference.docxはスタイルだけが使用され、本文は無視されます。)

Pandocユーザーズガイド日本語版を翻訳した方のQiitaの記事より、使用できるスタイルは以下とのことです。

使用できるスタイルは以下の通り:【段落】 標準(Normal), Compact, 表題(Title), Authors, 日付(Date), Heading 1, Heading 2, Heading 3, Heading 4, Heading 5, Block Quote, Definition Term, Definition, 本文(Body Text), Table Caption, Image Caption; 【文字】 Default Paragraph Font, Body Text Char, Verbatim Char, Footnote Ref, Link.

以下のコマンドでreference.docxを参照しつつ、Markdownをdocxに変換できます。

  $ pandoc origin.md --reference-docx=reference.docx -o output.docx

出力:

出力されたdocx

docxなのでWordで開ける環境さえあればPDFへの変換も余裕ですね。
各種フォントサイズや色などはデフォルトでも充分使えそうな感じですが、それぞれお好みに調整すれば良いし、特に以下などは予めスタイルを設定しておくと良さそうな感じでした。

  • ヘッダ、フッタの設定(社名、ページ数表示など)
  • 見出しに番号を振る

一方で以下のように痒いところに手が届かない部分もあります。

  • 図や表のキャプションに番号が振られない
  • 表にTableNormalという標準スタイルが使われており事前にスタイルの編集ができない
    • そもそも日本語環境のWordにはTableNormalというidを持ったスタイルが存在しない?
    • いずれにせよbuilt-inのスタイルを編集することは非推奨っぽい

Pandocで作られたdocxをRubyで加工する

上記のスライドにもありましたが、docxはXMLで構成されているため、構造がわかればWordを直接触らなくても加工することが可能です。先に挙げた「痒いところ」をなんとかできないかと以下のようなツールを作ってみました。

pandocxtyle

docxというgemがありますが、それを少し拡張して目的のアクション毎にタスク化したものです。 製作途中であり、業務の中で使ったこともないのですが、例えば以下のようなことができます。

図表のキャプションに番号を振る

ただのテキストではなく、Word上で自動でインクリメント、デクリメントされる番号を振ります。

連番挿入直後

キャプションに番号を振れたのはよいのですが、実際のところ本文からの相互参照とかもできないと今ひとつですね。 そのあたりもカバーしようと思うともっと複雑なことをする必要があります。

表のスタイルを置換する

先述したTableNormalではなく、別のスタイルを一括で適用できます。

表スタイル変更直後

その他

目次を作る、Heading 1の直前に改ページを入れる、などの機能も作ったのですが、これらはそれぞれ、Pandocでの変換時に--tocオプションを付ける、Heading 1のスタイルに設定する、という作業で解決する話だったので無用の長物になりました。

まとめ

とまぁ、結局痒いところに手が届くようになったかというと現状怪しい感じではありますが、こんな具合に属人的なタスクを減らしていくことができたらなぁと日々悶々としています。

参考