(12日目) blockdiag の class 機能を使ってみる

今日は blockdiag の class 機能を紹介します。

class 機能も最近できたばかりの機能で、シンプルでわかりやすい機能が多い blockdiag の中で
少し変わった位置にある機能です。
blockdiag ではノードやエッジ(矢印)、グループなどを書く際に、
label や color などの属性を設定しながら図を作成しています。

class 機能はあらかじめ属性をクラスとしてまとめておいて、
それを各要素に適用するための仕組みです。
CSS の class 定義と似た考え方を持っています。


例として次のような図を考えてみます。今日はサンプルとして nwdiag を利用しています。
ホスト数が 8台あるネットワークを模した図です。

nwdiag {
  network dmz {
    router [shape = cisco.router];
    server01 [shape = cisco.www_server, textcolor = red, address = 192.168.0.1];
    server02 [shape = cisco.www_server, textcolor = red, address = 192.168.0.2];
    server03 [shape = cisco.www_server, textcolor = red, address = 192.168.0.3];
  }
  network internal {
    router [shape = cisco.router];
    client01 [shape = cisco.pc, textcolor = gray, address = 192.168.1.1];
    client02 [shape = cisco.pc, textcolor = gray, address = 192.168.1.2];
    client03 [shape = cisco.pc, textcolor = gray, address = 192.168.1.3];
    client04 [shape = cisco.pc, textcolor = gray, address = 192.168.1.4];
    client05 [shape = cisco.pc, textcolor = gray, address = 192.168.1.5];
    client06 [shape = cisco.pc, textcolor = gray, address = 192.168.1.6];
    client07 [shape = cisco.pc, textcolor = gray, address = 192.168.1.7];
    client08 [shape = cisco.pc, textcolor = gray, address = 192.168.1.8];
  }
}

サーバーを 3台、クライアントホストを 8台用意しましたが、
それぞれを同じように描画するために定義が少し煩雑になっています。

ここで便利なのが class 機能です。
共通の定義(ここでは shape, textcolor) を抜き出しクラスとして定義すると、
図の定義がかなりシンプルになります。

nwdiag {
  class server [shape = cisco.www_server, textcolor = red];
  class client [shape = cisco.pc, textcolor = gray];

  network dmz {
    router [shape = cisco.router];
    server01 [class = server, address = 192.168.0.1];
    server02 [class = server, address = 192.168.0.2];
    server03 [class = server, address = 192.168.0.3];
  }
  network internal {
    router [shape = cisco.router];
    client01 [class = client, address = 192.168.1.1];
    client02 [class = client, address = 192.168.1.2];
    client03 [class = client, address = 192.168.1.3];
    client04 [class = client, address = 192.168.1.4];
    client05 [class = client, address = 192.168.1.5];
    client06 [class = client, address = 192.168.1.6];
    client07 [class = client, address = 192.168.1.7];
    client08 [class = client, address = 192.168.1.8];
  }
}

どうでしょうか、すこしは図の定義はすっきりしたのではないでしょうか。
class をうまく使うことで、サーバーやクライアントの表現を変更したくなった場合に
一箇所の変更でまとめて変更するようなこともできます。

生成された結果の図は最初と変わりません。

autoclass プラグイン

blockdiag ではもうひとつクラスを適用する仕組みを提供しています。
その仕組みとは autoclass プラグインです。

autoclass プラグインを使うとノード名に応じて自動的にクラスを適用します。
先ほどの例では server と client というふたつのクラスを定義していました。

  class server [shape = cisco.www_server, textcolor = red];
  class client [shape = cisco.pc, textcolor = gray];

autoclass を利用すると 〜_server, 〜_client というノードを定義すると
自動的にこれらのクラスが適用されます。

autoclass を利用して同じ図をもう一度書きなおすと以下のようになります。

nwdiag {
  plugin autoclass;
  class server [shape = cisco.www_server, textcolor = red];
  class client [shape = cisco.pc, textcolor = gray];

  network dmz {
    router [shape = cisco.router];
    s01_server [address = 192.168.0.1];
    s02_server [address = 192.168.0.2];
    s03_server [address = 192.168.0.3];
  }
  network internal {
    router [shape = cisco.router];
    c01_client [address = 192.168.1.1];
    c02_client [address = 192.168.1.2];
    c03_client [address = 192.168.1.3];
    c04_client [address = 192.168.1.4];
    c05_client [address = 192.168.1.5];
    c06_client [address = 192.168.1.6];
    c07_client [address = 192.168.1.7];
    c08_client [address = 192.168.1.8];
  }
}

もちろん出力された図は同じものが出力されます。


class 機能や autoclass プラグインは大きめの図を作る際の手助けとなる機能です。
今回の例では shape, textcolor 属性を例として取り上げましたが、
blockdiag で扱える属性であればすべてをクラスとして定義することができます。

図を書く際に共通なスタイルを見つけたらクラスにして、シンプルに書けないか試してみてください。

(13日目) Jenkins + bitbucket.org で Sphinx で作られた Web サイトを自動公開する

12日目の記事をかいてからまだそんなに時間が経っていませんが、
勢いで 13日目の記事も作ってしまいました。
今日は Jenkins + bitbucket.org + Sphinx の組み合わせで、Web サイトを自動的に公開する仕組みをご紹介します。
この仕組みは実際に blockdiag.com の更新に使われているもので、僕自身もこの仕組みによってかなり楽できているものです。

一応用語説明しておきます。

Jenkins のインストール

もちろんみなさん Debian を使われていると思いますので、Debian ユーザー向けの手順を書いていきます。
Jenkins プロジェクトでは、Debian パッケージを公開してくれているため、
他のパッケージのインストールと同様にかんたんにインストールできます。

Debian Repository for Jenkins の指示に従い、Jenkins をインストールして行きましょう。

まずは Jenkins リポジトリのキーを登録します。

wget -q -O - http://pkg.jenkins-ci.org/debian/jenkins-ci.org.key | sudo apt-key add -

つぎに /etc/apt/sources.list に Debian Repository for Jenkins のエントリーを登録します。

# Debian Repository for Jenkins
deb http://pkg.jenkins-ci.org/debian binary/

あとはいつもどおり apt で jenkins パッケージをインストールするだけです。

$ sudo apt-get update
$ sudo apt-get install jenkins

Jenkins のアクセス制御を行う

apt でインストールした直後、Jenkins は 8080 ポートでアクセス制御のない状態で動作します。
これはセキュリティ的に望ましい状態ではないので、アクセス制御をかけるようにしましょう。

アクセス制御をかける方法はすでに Web 上に記事がたくさんあるので、
ここでは私が実施している方法をかなりおおざっぱに紹介します。
なお、何をやっているのかよくわからない人は、真似してコピペしないでください。大抵失敗して困ります。

Jenkins 設定の基本戦略としてはこんな感じです。

  • 外部から Jenkins に直接アクセスさせない
  • Apache 経由で公開する(mod_proxy)
  • Basic 認証をかける
  • URL のパスは / ではなく /jenkins にする (既存のサイトにぶら下げるため)

というものです。

まずは外部から Jenkins に直接アクセスできないよう、ファイアウォールの設定を行います。

$ sudo ufw enable
$ sudo ufw allow ssh
$ sudo ufw allow http
$ sudo ufw default DROP

そして Jenkins の設定(/etc/default/jenkins)を変更します。
変更点は次の二箇所です。

  • ポートを 9000 に変更する
  • URL のパスを / ではなく /jenkins に変更
# port for HTTP connector (default 8080; disable with -1)
HTTP_PORT=9000

JENKINS_ARGS="--webroot=/var/run/jenkins/war --httpPort=$HTTP_PORT --ajp13Port=$AJP_PORT --prefix=/jenkins"

つぎに Apache の設定を行いましょう。まずは mod_proxy を有効にします。

$ sudo a2enmod proxy proxy_http

そして、mod_proxy の設定および Basic 認証の設定を行います。
具体的には /etc/apache2/sites-available/default などに以下の記述を行います。

    ProxyRequests Off # Disable forward proxy
    ProxyPass /jenkins http://localhost:9000/jenkins
    ProxyPassReverse /jenkins http://localhost:9000/jenkins

    <Location /jenkins>
        Order deny,allow
        Allow from all

        AuthUserFile /etc/apache2/sites-available/jenkins.passwd
        AuthName realm
        AuthType basic
        Require valid-user
    </Location>

最後に Basic 認証用のパスワードファイルを作成して Apache と Jenkins を再起動します。
ユーザー名は適当に決めてください。

$ sudo htpasswd -c /etc/apache2/sites-available/jenkins.passwd username
$ sudo /etc/init.d/jenkins restart
$ sudo /etc/init.d/apache2 restart

最後に設定した URL にアクセスして Jenkins が表示されていれば成功です。
そうでなければ設定を再度見なおしてみてください。

Jenkins 自体の設定をする

Jenkins は最初から基本機能が揃っているので、手元の環境ではほとんどカスタマイズしていません。
bitbucket.org との連携をするために Jenkins Mercurial Plugin を入れています。

プラグインのインストールは Jenkins のトップページから
[Jenkins の管理] - [プラグインの管理] とメニューをたどったところにあります。
[利用可能] タブを選択し、リストの中から Mercurial Plugin をインストールしてください。

Sphinx プロジェクトを登録する

ようやく基本的な設定ができたので、さっそく Sphinx プロジェクトのビルドを設定しましょう。
トップページの [新規ジョブ作成] メニューを選択します。
ジョブ名を入力し、[フリースタイル・プロジェクトのビルド] を選択して下さい。

ジョブ作成後の設定は次のようにしています。

  • ソースコード管理システム: Mercurial
  • Repository URL: bitbucket.org 上のリポジトリ URL
  • SCM をポーリング: */10 * * * *
    • 10分置きに更新をチェックしに行きます。
  • E-mail 通知: 自分のメールアドレス
  • 不安定ビルドも逐一メールを送信: 有効
  • ビルドを壊した個人にも別途メールを送信: 有効

また [ビルド手順の追加] ボタンを押して、以下のビルド用コマンドを登録します。

make clean gettext html
rsync -aru --delete ./ /var/www/blockdiag.com/

この欄に登録したコマンドは、ビルドが行われる際に実行される処理です。
ビルド時に

  • make clean gettext html を実行
  • rsync で公開ディレクトリにファイルをコピー

という処理が自動的に行われます。
この部分は各自の公開方法によってアレンジを行なってください。*1

まとめ

ここまでの設定を行うと、リポジトリが更新されるたびにドキュメントのビルド・公開が行われます。
だれかが Sphinx ドキュメントを更新して bitbucket.org に push すると
HTML のビルドが行われる仕掛けになっています。

仕組みを作るのにインストールや設定など一手間かかりますが、
一度設定してしまうとドキュメントを書いて push するだけでサイトが更新されるので、
とても楽にサイトを更新できるようになります。
Sphinx 自体がドキュメントの中身に集中できるようなツールなので、
他のツールでそれを加速できるともっとドキュメントを書くのが楽しくなります。

是非試してみてください :-)

*1:実際、blockdiag.com は英語サイトがあるのでもうちょっと複雑なビルド工程になってます。

(11日目) Sphinx から PDF を生成してみよう (LaTeX 編)

今日のアドベントカレンダーは再び PDF 生成の話です。

前回は rst2pdf を使って PDF 生成を行いましたが、今日は LaTeX 経由で PDF 生成する方法を紹介します。
どちらも PDF 形式のドキュメントを生成することができますが、
それぞれで使い方や仕上がりが異なっているので、どちらかお好きな方で試すのがよいと思います。

TeXLive 2011 のインストール

まずは LaTeX をインストールします。
Sphinx では日本語の文字コードとして UTF-8 を利用しているため、
利用する LaTeXUTF-8 に対応したものである必要があります。

ここでは TeX 系統合パッケージのひとつである TeXLive を利用します。
なお、TeXLive 2009 までは収録されている pLaTeX は EUC-JP ベースとのことなので、
ここでは最新版の 2011 を利用するようにします。

Debian に収録されている TeXLive は 2009 なので、TeXLive のインストーラを利用してインストールします。*1

$ wget http://mirror.ctan.org/systems/texlive/tlnet/install-tl-unx.tar.gz
$ tar xzvf install-tl-unx.tar.gz
$ cd install-tl-20111210
$ sudo ./install-tl
Loading http://ftp.jaist.ac.jp/pub/CTAN/systems/texlive/tlnet/tlpkg/texlive.tlpdb
Installing TeX Live 2011 from: http://ftp.jaist.ac.jp/pub/CTAN/systems/texlive/tlnet
Platform: i386-linux => 'Intel x86 with GNU/Linux'
Distribution: net  (downloading)
Using URL: http://ftp.jaist.ac.jp/pub/CTAN/systems/texlive/tlnet
Directory for temporary files: /tmp

======================> TeX Live installation procedure <=====================

=======> Note: Letters/digits in <angle brackets> indicate menu items <=======
=======>       for commands or configurable options                   <=======

 Detected platform: Intel x86 with GNU/Linux

 <B> platforms: 1 out of 19

 <S> installation scheme (scheme-full)
     84 collections out of 85, disk space required: 2932 MB

 Customizing installation scheme:
   <C> standard collections
   <L> language collections

 <D> directories:
   TEXDIR (the main TeX directory):
     /usr/local/texlive/2011
   TEXMFLOCAL (directory for site-wide local files):
     /usr/local/texlive/texmf-local
   TEXMFSYSVAR (directory for variable and automatically generated data):
     /usr/local/texlive/2011/texmf-var
   TEXMFSYSCONFIG (directory for local config):
     /usr/local/texlive/2011/texmf-config
   TEXMFVAR (personal directory for variable and automatically generated data):
     ~/.texlive2011/texmf-var
   TEXMFCONFIG (personal directory for local config):
     ~/.texlive2011/texmf-config
   TEXMFHOME (directory for user-specific files):
     ~/texmf

 <O> options:
   [ ] use letter size instead of A4 by default
   [X] allow execution of restricted list of programs via \write18
   [X] create all format files
   [X] install macro/font doc tree
   [X] install macro/font source tree

 <V> set up for portable installation

Actions:
 <I> start installation to hard disk
 <H> help
 <Q> quit

Enter command: i

"Enter command" が表示されたら I と入力してお茶を淹れにいきましょう。
TeXLive は TeX 関係のパッケージが大量に収録されている超巨大パッケージで、
約 2GB ほどのファイル群をダウンロードします。


アップデートが完了した後、TeXLive のコマンドがどこからでも利用できるよう、
環境変数 PATH に /usr/local/texlive/2011/bin/i386-linux を追加してください。

なお、TeXLive は定期的に構成パッケージの更新が行われているので、
定期的に以下のコマンドでアップデートを行なってください。

$ sudo tlmgr update --self
$ sudo tlmgr update --all

Sphinx の更新

現在の最新版である Sphinx 1.1.2 は日本語 TeX の出力に問題があり、
手順どおりに進めても日本語が含まれているとうまくビルドすることができません。

この問題に対して、現在打田さんの手によって日本語 LaTeX 周りの改良が進められています。
これらの改良はまとまり次第 Sphinx 本体に反映されることになると思いますが、
現時点では手動で Sphinx に修正を入れる必要があります。


修正はパッチとして提供されていないため、1ファイルずつ差し替えを行いました。

$ cd /usr/local/lib/python2.6/dist-packages/Sphinx-1.1.2-py2.6.egg/sphinx
$ sudo mv quickstart.py quickstart.py.orig
$ sudo wget https://bitbucket.org/uchida/sphinx/raw/f5de3b205a52/sphinx/quickstart.py
$ cd texinputs
$ sudo mv Makefile Makefile.orig
$ sudo mv fncychap.sty fncychap.sty.orig
$ sudo mv sphinx.sty sphinx.sty.orig
$ sudo wget https://bitbucket.org/uchida/sphinx/raw/f5de3b205a52/sphinx/texinputs/Makefile
$ sudo wget https://bitbucket.org/uchida/sphinx/raw/f5de3b205a52/sphinx/texinputs/fncychap.sty
$ sudo wget https://bitbucket.org/uchida/sphinx/raw/f5de3b205a52/sphinx/texinputs/sphinx.sty
$ cd ../writers
$ sudo mv latex.py latex.py.orig
$ sudo wget https://bitbucket.org/uchida/sphinx/raw/f5de3b205a52/sphinx/writers/latex.py

LaTeX 経由で PDF ファイルを生成する

では、Sphinx から PDF ファイルを出力してみましょう。

Sphinx プロジェクトの設定として、conf.py に以下の行を追加します。

# 言語の設定
language = 'ja'

# LaTeX の docclass 設定
latex_docclass = {'manual': 'jsbook'}

そして、以下のコマンドで PDF の生成を行います。

$ make latex
$ make -C _build/latex all-pdf-ja

生成された PDF はこんな形式になります。rst2pdf で生成されたものと見比べてみてください。



なお、打田さんの修正を適用してから Sphinx プロジェクトを作成した場合は
latexpdfja という make ターゲットが用意されているので

$ make latexpdfja

というコマンドだけで PDF を生成することができます。


以前はかなり LaTeX と格闘しながらビルドしていた印象があるのですが、
TeXLive 2011 を利用し、打田さんのまとめられた修正を適用することで
とても簡単に PDF 出力ができるようになっています。

ディストリビューションで TeXLive 2011 が利用されるようになることと、
修正が Sphinx 本体に取り込まれるともっともっとかんたんに使うことが出来るようになるでしょう。
その日が待ち遠しですね :-)

*1:ディストリビューションの対応状況は[http://code.google.com/p/python-doc-ja/wiki/LaTeXBuild:title=こちらのページ]から確認できます…が、VineLinux 以外は対応していないようです

(10日目) blockdiag のフォントを切り替える

今日は最新の blockdiag で搭載されたフォント切り替え機能について紹介します。

かんたんなフォント指定

blockdiag は文字を描画する際に TrueType フォントを利用しています。

blockdiag は起動時にオプションを指定しない場合、
TrueType フォントがインストールされていないか探索します。*1

よく利用されるフォント指定方法は -f (--font)オプションを利用して TrueType フォントを指定する方法です。

$ blockdiag -f /path/to/font.ttf input.diag

この指定を行うと指定したフォントで文字の描画を行います。

実際に同じ図を複数のフォントでレンダリングしてみましょう。
サンプルには以下のソースを利用しました。

blockdiag {
  default_fontsize = 24;

  ABC -> def -> あいうえお -> 文章;
}
  • IPA フォント (ゴシック)

  • IPA フォント (明朝)

  • さざなみフォント (ゴシック)

  • AppleGothic

それぞれのフォントによって出力される文字がそれぞれ異なっていることがわかるかと思います。
世の中にはフリーのもの、有償のものを含め本当にたくさんのフォントがあるので、
興味が有る方はタイポグラフィやレタリングについて調べてみるととても楽しいと思います。

複雑なフォント指定 (複数のフォントを切り替える)

-f オプションを利用したフォントを切り替え方についてご紹介しましたが、
図の表現として一部の要素だけフォントを切り替えたいケースがあります。

blockdiag 1.1.0 では複数のフォントを切り替える方法として fontmap という考え方が導入されました。
fontmap は TrueType フォントと名前(フォントファミリー名)の対をリストにしたものです。

fontmap ファイル

fontmap を理解するには、実際の fontmap 定義ファイルを見てもらうのがわかりやすいと思います。
fontmap ファイルは以下のようなファイルです。

[fontmap]
serif = /usr/share/fonts/truetype/ipafont/ipamp.ttf
serif-bold = /usr/share/fonts/truetype/ipafont/ipamp.ttf
sansserif = /usr/share/fonts/truetype/ipafont/ipagp.ttf
monospace = /usr/share/fonts/truetype/ipafont/ipam.ttf
cursive = /usr/share/fonts/truetype/ipafont/ipam.ttf
cursive-italic = /usr/share/fonts/truetype/ipafont/ipam.ttf
fantasy = /usr/share/fonts/truetype/ipafont/ipam.ttf

fontmap ファイルは一般的な ini 形式のファイルで、fontmap セクション([fontmap]) の中に
複数のフォントをリストアップしたものです。

フォントをリストアップする際、左辺にはフォントファミリー(font-family)名を記載します。
フォントファミリーは 1〜3つのパートからなる文字列です。

[フォント名称]-[総称ファミリ]-[スタイル]

それぞれのパートは以下の役割を持っています。

  • フォント名称 (省略可)
    • フォントの名称を指定する。自由に指定することができる。
  • 総称ファミリ (必須)
    • フォントの形状を指定する。CSS の総称ファミリ (generic-family) と同じもの。
    • sansserif, serif, monospace, fantasy, cursive のいずれかの値を取る
    • それぞれの意味は以下のとおり。
      • sansserif: ゴシック体、ヒゲのないフォント
      • serif: 明朝体、ヒゲのあるフォント
      • monospace: 固定幅フォント
      • fantasy: 装飾系フォント
      • cursive: 筆記体フォント
  • スタイル (省略可)
    • フォントの装飾を指定する
    • normal(装飾なし), bold(太字), italic(斜体), oblique(斜体) のいずれかの値を取る
    • 省略時は normal として扱われる


例えば AppleGothic, ヒラギノ, IPAフォント(ゴシック/明朝)を利用する場合は
次のような fontmap ファイルを定義します。*2

[fontmap]
apple-sansserif: /System/Library/Fonts/AppleGothic.ttf
hiragino-sansserif: /Library/Fonts/Hiragino Sans GB W3.otf
ipa-sansserif: /Users/tkomiya/fonts/ipagp.otf
ipa-serif: /Users/tkomiya/Fonts/ipamp.otf
フォントの切り替え (fontfamily)

さて、fontmap を定義してしまえば、あとは図の定義でフォントを切り替えながら利用するだけです。
blockdiag では 2つの方法でフォントを切り替えることができます。

  • default_fontfamily
    • 図全体に反映されるフォント設定です。
    • fontmap のフォントファミリーを指定します。
    • 初期値は sansserif-normal です
  • 要素毎の fontfamily 設定
    • 要素ごとに設定されるフォント設定です。
    • defualt_fontfamily より優先されて適用されます。

この2つを組み合わせると、特定の箇所だけ文字を大きくすることができます。

blockdiag {
  default_fontsize = 16;
  default_fontfamily = "ipa-sanserif";  // 標準のフォントを IPA ゴシックに設定

  AppleGothic [fontfamily = "apple-sansserif"];
  ヒラギノゴシック [fontfamily = "hiragino-sansserif"];
  IPAゴシック;
  IPA明朝 [fontfamily = "ipa-serif"];
}

blockdiag で変換する際、--fontmap ファイルを fontmap ファイルを指定し忘れないようにします。

$ blockdiag --fontmap fontmaprc input.diag

フォントサイズの調整

ここまではフォント(書体)の話をしていましたが、文字描画の構成要素の一つに大きさ、つまりフォントサイズもがあります。
blockdiag では2つの方法でフォントサイズを変更することができます。

  • default_fontsize 設定
    • 図全体に反映されるフォントサイズ設定です。
    • 初期値は 11 ポイントです。
  • 要素毎の fontsize 設定*3
    • 要素ごとに設定されるフォントサイズ設定です。
    • default_fontsize より優先されて適用されます。

この2つを組みわせると、全体の文字を大きくしたり、特定の箇所だけ文字を大きくまたは小さくすることができます。

blockdiag {
  default_fontsize = 16;       // 標準のフォントサイズを 16 ポイントに設定

  小さい文字 [fontsize = 12];  // フォントサイズを小さくする
  普通の文字;                  // 設定しない = 標準のフォント(16ポイント)を使用
  大きい文字 [fontsize = 24];  // フォントサイズを大きくする
}

まとめ

blockdiag では -f オプションと fontmap の二種類のフォント指定方法があります。
このふたつを使い分けて、ドキュメンテーションに役立ててください。

fontmap の仕組みが複雑すぎてすみません orz *4

*1:特定のパスを見に行くだけなので、よりよい探索ロジックがあれば教えて欲しいです。

*2:この例は手元の MacOS Lion 環境に、~/Fonts に IPA フォントを設置した環境です。

*3:ノードだけでなく、エッジ、グループなどにも利用できます

*4:言い訳:PDF/PNG と SVG を両立させるためにこんな複雑な仕組みになっています。XLFD と CSS、TrueType というそれぞれのいいとこどりをしているつもりですが、最初に予想されたとおり複雑で残念な感じです。だれか助けて。

(10日目/番外編) SVG とフォントについて愚痴る。

blockidag で SVG 形式を使っている人はよく文字がはみ出る場面に遭遇します。

これは SVG が HTML のように「表示時にフォントが決定する」仕組みであるためです。
つまり、SVG ファイルを出力したときはどのようなフォントが用いられるかわからないのです。

これには表示する端末に指定したフォントがインストールされているかわからないという理由があるのですが、
blockdiag のような図形を生成するソフトウェアにとっては致命的です。
また、とても残念なことにテキストの折り返し機能も SVG にはありません。
描画時に「ローカルのフォントを元に」折り返し位置を予想してレンダリングしています。
そのため、表示するフォントによってずれが発生するのです。*1

CSS では描画時のヒント情報として font-family 属性を埋め込んで対処しますが、
この font-family 情報の生成は面倒な上に、あまり当てにはなりません。
ということで、blockdiag では generic-family だけを利用しています。

この部分についてはいい落とし所がよくわからなかったので、
もし解決策やいいアイディアをお持ちの方がいらっしゃれば是非教えて下さい。


あと PIL で Bold や Italic のレンダリングを行う方法も教えてもらえると大喜びします。

未熟すぎるんで吊ってきます… orz

*1:さらに補足すると Interactive Shell では PIL が利用できないため、超適当な文字幅予測をしています。そのため、幅広の文字(w など)が特にずれます

(9日目) sphinxcontrib-blockdiag の desctable を使ってみる

今日は sphinxcontrib-blockdiag の強力な機能、desctable を紹介します。

blockdiag シリーズはテキストから図を生成するツールですが、
ドキュメントというものは図だけで説明が完結するということは少ないです。
図に対する説明、各構成要素の説明などが図とペアになって必要とされることが多いと思われます。

sphinxcontrib-blockdiag の desctable 機能は、
ドキュメントに図を埋め込む際に補足情報として説明表を一緒に出力します。
desctable は description table を意味しており、図の各構成要素の説明を表形式にすることを指しています。

また、desctable はひとつの入力から図と表をセットで生成することで、
ドキュメントの不整合を防ぐことも目的としています。

desctable を使ってみる

では、desctable 機能を使ってみましょう。

desctable 機能を利用するためには、図に説明情報が埋め込まれている必要があります。
blockdiag シリーズでは、各ノードに description という属性が用意されていますので、
この description 属性に説明テキストを設定します。

{
  会員登録画面 -> 確認メール -> 入力画面 -> 確認画面 -> 完了画面, 完了メール;

  会員登録画面 [description = "会員登録のトップです。"];
  確認メール [shape = mail, description = "到達性の確認メールです。"];
  入力画面 [description = "会員情報を入力します。10フォーム程度を予定。"];
  確認画面 [description = "入力情報の確認。"];
  完了画面 [description = "完了後はログイン状態になっている。"];
  完了メール [shape = mail, description = "サンキューメール"];
}

では、この図を Sphinx 文書に埋め込みましょう。
通常通り blockdiag ディレクティブを利用しますが、その際に desctable オプションを指定します。

.. blockdiag::
   :desctable:

   {
     会員登録画面 -> 確認メール -> 入力画面 -> 確認画面 -> 完了画面, 完了メール;

     会員登録画面 [description = "会員登録のトップです。"];
     確認メール [shape = mail, description = "到達性の確認メールです。"];
     入力画面 [description = "会員情報を入力します。10フォーム程度を予定。"];
     確認画面 [description = "入力情報の確認。"];
     完了画面 [description = "完了後はログイン状態になっている。"];
     完了メール [shape = mail, description = "サンキューメール"];
   }

desctable オプションを指定すると図と共に図の説明表が生成されます。
説明表は description 属性の内容を含んだものです。

desctable を拡張する (numbered 属性)

desctable が表を作成する際に参照する属性の一つに numbered 属性があります。
numbered 属性はノードに番号をつけるためのものですが、
desctable はこの番号を表にも反映します。

.. blockdiag::
   :desctable:

   {
     会員登録画面 -> 確認メール -> 入力画面 -> 確認画面 -> 完了画面, 完了メール;

     会員登録画面 [numbered = 1, description = "会員登録のトップです。"];
     確認メール [shape = mail, numbered = 2, description = "到達性の確認メールです。"];
     入力画面 [numbered = 3, description = "会員情報を入力します。10フォーム程度を予定。"];
     確認画面 [numbered = 4, description = "入力情報の確認。"];
     完了画面 [numbered = 5, description = "完了後はログイン状態になっている。"];
     完了メール [shape = mail, numbered = 6, description = "サンキューメール"];
   }

numbered 属性を利用すると図と説明表を見比べやすくなります。

desctable を拡張する (attributes プラグイン)

blockdiag 1.0.3 から導入された attributes プラグインは desctable の説明表を拡張します。
attributes プラグインを利用すると、ノードの属性を新たに追加するとともに追加された属性を説明表に掲載します。

.. blockdiag::
   :desctable:

   {
     // ノードの属性 server, redundant, software を追加
     plugin attributes [server = 稼働サーバー, redundancy = 冗長性, software = 利用ソフトウェア];

     アプリ -> データベース, メッセージキュー, メールサーバ;

     アプリ [server = "web01,web02,web03", redundancy = "o", software = "Apache 2.2, PHP 5.3"];
     データベース [server = "db01", redundancy = x, software = "MySQL 5.5"];
     メッセージキュー [server = "db01" redundancy = x, software = "RabbitMQ"];
     メールサーバ [server = "mail01,mail02", redundancy = o, software = "Postfix"];
   }

この例のように各ノードに説明用の属性を追加することで、
ドキュメントとしての完成度があがりわかりやすいドキュメントが作れるようになります。

利用される例としては blockdiag の機能構成の説明や、
nwdiag や rackdiag における機器構成を記述するのに使われます。

(8日目) Sphinx から PDF を生成してみよう (rst2pdf 編)

今日のアドベントカレンダーでは Sphinx で PDF 出力する方法をご紹介します。

Sphinx から PDF を出力するには 2種類の方法があります。
ひとつは Sphinx 標準で提供されている LaTeX 経由で PDF を出力する方法です。
もうひとつは rst2pdf をインストールし、rst2pdf の Sphinx 拡張機能を用いる方法です。

二通りある PDF 出力について、僕は次のような印象を持っています。*1

  • LaTeX 経由
    • LaTeX による美しい PDF 出力が期待できる
    • TeX のスタイルを書き換えることで、デザインを変えることができそう
    • .tex ファイルから PDF への変換は Sphinx の他に TeXLive (など)が必要
      • TeXLive は巨大パッケージであるため、容量がとても大きい(約 2GB)
    • TeXLive 2010 以降でないと日本語の扱いがいまいち*2
  • rst2pdf 経由
    • rst2pdf のインストールがかんたん(TeXLive と比べると)
    • Sphinx 経由で rst2pdf を利用するとデザインの自由がほとんど無い
    • rst2pdf の作者は Sphinx を利用しておらず、質問に対してシブめの返事が来る
    • 使えない Sphinx 拡張がたまにある

なお、第三勢力の座を sphinxcontrib-docxbuilder が狙っています。
こちらの特徴はこんな感じです。

  • sphinxcontrib-docxbuilder
    • MS-Word の機能を用いて PDF 出力する
    • Word のデザインテンプレートを使うのでデザイン調整がしやすい
    • でもバージョンがまだ 0.0.1alpha 。

今日はこれらのうち、rst2pdf を使った PDF 出力を取り上げてみようと思います。

rst2pdf とは

rst2pdf は Roberto Alsina さんが開発したツールで、reST 形式のテキストから PDF を生成するツールです。
Sphinx は標準の reST に対して独自の拡張(ディレクティブやロールなど)を加えていますが、rst2pdf は標準の reST が対象になります。

本来は rst2pdf は単体で利用するツールですが、PDF 出力部分を Sphinx から利用できるよう Sphinx 拡張を提供しているため、
Sphinx から利用することも出来ます。

rst2pdf を使ってみる

Debian squeeze では rst2pdf パッケージが存在しますが、少しバージョンが古いので easy_install を使います。
依存パッケージである reportlab と PIL はビルドに時間がかかるので、こちらは apt でインストールします。

$ sudo apt-get install python-reportlab pythong-imaging
$ sudo easy_install rst2pdf

次に日本語用の設定ファイルを作成します。
デフォルトでは rst2pdf は日本語が利用できないようになっているので、
フォントの設定をする必要があります。
ja.json というファイル名で以下の内容を保存します。

{
    "fontsAlias" : {
        "stdFont": "VL-PGothic-Regular",
        "stdBold": "VL-PGothic-Regular",
        "stdItalic": "VL-PGothic-Regular",
        "stdBoldItalic": "VL-PGothic-Regular",
        "stdMono": "VL-Gothic-Regular",
        "stdMonoBold": "VL-Gothic-Regular",
        "stdMonoItalic": "VL-Gothic-Regular",
        "stdMonoBoldItalic": "VL-Gothic-Regular",
        "stdSans": "VL-Gothic-Regular",
        "stdSansBold": "VL-Gothic-Regular",
        "stdSansItalic": "VL-Gothic-Regular",
        "stdSansBoldItalic": "VL-Gothic-Regular"
    },
    "styles" : [
        ["base" , {
            "wordWrap": "CJK"
        }],
        ["literal" , {
            "wordWrap": "None"
        }]
     ]
}

それでは試しに rst2pdf を単体で利用してみます。
サンプルとして次のような reST ファイルを用意しました。

=============================
rst2pdf sample documentation
=============================

これは rst2pdf を利用するサンプルです。

blockdiag の紹介
=================

blockdiag シリーズは以下のパッケージから構成されています。

* blockdiag
* seqdiag
* actdiag
* nwdiag
   * nwdiag
   * rackdiag

blockdiag パッケージとコマンド群
=================================

もちろん表も書けます。

============  =========
パッケージ名  コマンド
============  =========
blockdiag     blockdiag
seqdiag       seqdiag
actdag        actdiag
nwdiag        nwdiag
              rackdiag
============  =========

ではこのファイル(blockdiag.rst)を PDF に変換してみます。

$ rst2pdf -s ja blockdiag.rst

生成された PDF (blockdiag.pdf) はこのような表示になります。

Sphinx から使ってみよう

rst2pdf を Sphinx から利用する際はいくつか設定が必要です。

  • conf.py の書き換え
  • Makefile の書き換え
  • ja.json の作成

それぞれについて順番に説明していきます。

conf.py の書き換え

まずは rst2pdf を有効にするため conf.py を編集します。
ここで、かならず pdf_stylesheets の設定を忘れないようにしてください。
rst2pdf 単体と同じように日本語を有効にする設定('ja')が含まれています。

extensions += ['rst2pdf.pdfbuilder']

pdf_documents = [
    ('index', u'MyProject', u'My Project', u'Author Name'),
]

# A comma-separated list of custom stylesheets.
pdf_stylesheets = ['sphinx', 'kerning', 'a4', 'ja']

# Language to be used for hyphenation support
pdf_language = "ja"

また、前述の ja.json を Sphinx プロジェクトのディレクトリにコピーしておきます。

Makefile の書き換え

Makefile には rst2pdf 経由でビルドする make ターゲットが記述されていないので、
Makefile に pdf ターゲットを追加します。

pdf:
    $(SPHINXBUILD) -b pdf $(ALLSPHINXOPTS) $(BUILDDIR)/pdf
    @echo
    @echo "Build finished. The PDF files are in _build/pdf."

引数なしで make コマンドを実行した時に表示されるヘルプに
pdf がリストアップされないのが気になる人は help ターゲットに

        @echo "  pdf        to make pdf file"

を足しておくとよいかもしれません。

ここまで設定すると make pdf すると Sphinx ドキュメントを PDF 出力することができます。

$ make pdf
bin/sphinx-build -b pdf -d _build/doctrees   . _build/pdf
Running Sphinx v1.1.2
loading translations [ja]... done
loading pickled environment... done
building [pdf]: targets for 1 source files that are out of date
updating environment: 0 added, 1 changed, 1 removed
reading sources... [100%] index

looking for now-outdated files... none found
pickling environment... done
checking consistency... done
processing MyProject... index
resolving references...
done
writing MyProject... done
build succeeded.

Build finished. The PDF files are in _build/pdf.

$ ls _build/pdf
MyProject.pdf


Sphinx 経由で出力すると表紙や目次が付きます。
これらが不要な場合は conf.py の設定で調整することができます。

# 表紙を出力しない
pdf_use_coverpage = False

# 目次を出力しない
pdf_use_toc = False
エラーが出る人へ

rst2pdf と docutils-0.8 以降の組み合わせでは、以下のようなエラーが発生します。

$ make pdf
bin/sphinx-build -b pdf -d _build/doctrees   . _build/pdf
Running Sphinx v1.1.2
loading translations [ja]... done
loading pickled environment... done
building [pdf]: targets for 2 source files that are out of date
updating environment: 0 added, 0 changed, 0 removed
looking for now-outdated files... none found
processing MyProject... index [ERROR] pdfbuilder.py:129 get_language() takes exactly 2 arguments (1 given)
Traceback (most recent call last):
  File "/home/katsuwo/.buildout/buildout-eggs/rst2pdf-0.16-py2.6.egg/rst2pdf/pdfbuilder.py", line 121, in write
    appendices=opts.get('pdf_appendices', self.config.pdf_appendices) or [])
  File "/home/katsuwo/.buildout/buildout-eggs/rst2pdf-0.16-py2.6.egg/rst2pdf/pdfbuilder.py", line 188, in assemble_doctree
    self.docutils_languages[lang] = get_language(lang)
TypeError: get_language() takes exactly 2 arguments (1 given)
FAILED
build succeeded.

この問題に対する修正が提供されているので、docutils に回避用のパッチを適用するとよいでしょう。


さて、今日は rst2pdf を使って PDF 出力する方法をご紹介しました。
標準で利用できる HTML や ePub とあわせて、よく利用する方法だと思います。
ぜひ利用してみてください。

*1:個人の感想です。

*2:Debian には texlive 2009 がパッケージングされているので、UTF8 がうまく扱えません