LaTeX でソースコードをいい感じに魅せる

誰に見せるわけでもないですが、僕は LaTeX で書き物をするのが好きです。

LaTeX を使うと、まるで出版物のように(自分が書いたものとは思えないくらい)美しいドキュメントが出来上がるので、使っていて楽しい道具の一つだったりします。

ただ、デフォルト設定のクラスをそのまま使うと、生成されるドキュメントはあまりにもシンプルすぎるため、体裁をリッチにするためにこちらの記事を大いに参考にさせて頂いています。

precure-3dprinter.hatenablog.jp

今日は、上記の設定からもう一歩進んで、ソースコードを示す際のちょっとした裏技(?)をご紹介したいと思います。

プレーンなソースコードの出力例

先ほどご紹介した記事では、tcolorbox環境とverbatim環境を組み合わせてソースコードを出力しています*1

\documentclass[11pt,dvipdfmx,b5paper,oneside]{jsbook}

\usepackage{color}
\usepackage{framed}
\usepackage{tcolorbox}
\usepackage{tikz}

\begin{document}

\begin{tcolorbox}[title=main.c] %%% タイトルを「main.c」とする
\setlength{\baselineskip}{12pt} %%% 行間を詰める
\begin{verbatim}
1  /* ここにはソースコードを書く */
2  #include<stdio.h>
3
4  int main(void)
5  {
6    printf("Hello, World!\n");
7    return 0;
8  }
\end{verbatim}
\end{tcolorbox}

\end{document}

いかにも教科書っぽい出力が得られます。

これはこれで十分なクオリティだと思いますが、時にはソースコードの一部に特殊記号を使いたいケース*2や、文字色を部分的に変えたいケース*3もあるでしょう。

verbatim環境では、文字列がそのままに出力されてしまいますので、直接入力できない特殊文字や部分装飾が効きません。

ソースコードの中でLaTeXコマンドを使う

このようなケースに対応するには、verbatim環境の代わりにalltt環境が効果的です。

\documentclass[11pt,dvipdfmx,b5paper,oneside]{jsbook}

\usepackage{color}
\usepackage{framed}
\usepackage{tcolorbox}
\usepackage{tikz}
\usepackage{alltt}       %%% 追加
\usepackage{lmodern}     %%% 追加
\usepackage[T1]{fontenc} %%% 追加

\begin{document}

\begin{tcolorbox}[title=main.c]
\setlength{\baselineskip}{12pt}
\setlength{\fboxsep}{1pt} %%% 網掛けの余白を1ptに設定
\begin{alltt}
1  \textcolor{teal}{/* ここにはソースコードを書く */}
2  #include<\colorbox[gray]{.8}{stdio.h}>
3
4  int main(void)
5  \{
6    printf("Hello, World!\textbackslash{}n");
7    return 0;
8  \}
\end{alltt}
\end{tcolorbox}

\end{document}

alltt環境は、

  • \」「{」「}」以外は入力したままに出力される
  • \」に続けてLaTeXのコマンドを入力できる

── という特徴があり、工夫次第でソースコードの視認性を上げることができるようになります。

ただし、ソースコードの中に「\」「{」「}」が出現する場合は、それぞれ「\textbackslash{}」「\{」「\}」に置換する必要がありますので注意しましょう。

Pretty Printもどき

少しやり過ぎですが、例えば、Pretty Print のような装飾つきの出力もやろうと思えば可能です。

ここでは、空白記号を明示的に出力するためのマクロ\sを定義しています。

%%% \s: 空白記号を出力するマクロ
\newcommand\s{\textcolor{lightgray}{\char"20}}

\begin{tcolorbox}[title=main.c]
\setlength{\baselineskip}{12pt}
\setlength{\fboxsep}{1pt}
\begin{alltt}
1  \textcolor{teal}{/* ここにはソースコードを書く */}
2  \textcolor{blue}{#include}\textcolor{purple}{<stdio.h>}
3
4  \textcolor{blue}{int}\s{}main(\textcolor{blue}{void})
5  \{
6  \s\s{}printf(\textcolor{brown}{"Hello,\s{}World!\textbackslash{}n"});
7  \s\s\textcolor{blue}{return}\s{}0;
8  \}
\end{alltt}
\end{tcolorbox}


さらに自由に

さて、ここからが面白いところです。

tcolorbox環境は、座標を指定して文字や記号を重ねることができます。

座標の指定方法にはいくつかコツがありますが、ここではまず、座標にあたりをつけるための格子線グリッドを表示してみましょう。

\documentclass[11pt,dvipdfmx,b5paper,oneside]{jsbook}

\usepackage{color}
\usepackage{framed}
\usepackage{tcolorbox}
\usepackage{tikz}
\usepackage{alltt}
\usepackage{lmodern}
\usepackage[T1]{fontenc}
\tcbuselibrary{skins}    %%% 追加

\begin{document}

\begin{tcolorbox}[
  title=main.c,  %%% タイトルを「main.c」とする
  enhanced,      %%% タイトル付きのフレーム
  underlay={     %%% 背景を描画
    \begin{tcbclipinterior}
      \draw[step=0.2,lightgray] (0,0) grid (interior.north east);  %%% 目盛り線
      \draw[step=1,gray] (0,0) grid (interior.north east);         %%% 目盛り線
    \end{tcbclipinterior}
  }
]
\setlength{\baselineskip}{12pt}
\begin{alltt}
1  /* ここにはソースコードを書く */
2  #include<stdio.h>
3
4  int main(void)
5  \{
6    printf("Hello, World!\textbackslash{}n");
7    return 0;
8  \}
\end{alltt}
\end{tcolorbox}

\end{document}

これで、目盛りが見えるようになりました。

続いて、実際に座標を指定して線を引いてみましょう。

線を引く

線を引くには、以下のように始点と終点を指定し、さらに線を表す「--」を与えます。

座標の原点は左下端なので注意しましょう。つまり、(8,1)と指定した場合、左下端を起点にして右に8目盛り、上に1目盛り進んだ地点を意味します。

\begin{tcolorbox}[
  title=main.c,
  enhanced,
  underlay={
    \begin{tcbclipinterior}
      \draw[step=0.2,lightgray] (0,0) grid (interior.north east);  %%% 目盛り線
      \draw[step=1,gray] (0,0) grid (interior.north east);         %%% 目盛り線

      \draw[red] (8,1) -- (11,2);            %%% 直線を引く
      \draw[red] (8,1) node [left] {始点};    %%% 文字を出力
      \draw[red] (11,2) node [right] {終点};  %%% 文字を出力
    \end{tcbclipinterior}
  }
]
\setlength{\baselineskip}{12pt}
\setlength{\fboxsep}{1pt}
\begin{alltt}
1  /* ここにはソースコードを書く */
2  #include<stdio.h>
3
4  int main(void)
5  \{
6    printf("Hello, World!\textbackslash{}n");
7    return 0;
8  \}
\end{alltt}
\end{tcolorbox}


まとめ

これらの技巧を駆使すると、例えば、長大なYAMLファイルのうち、注目すべき箇所をハイライト表示したり──

はたまた、識別子を一致させなければならない箇所の対応関係を視覚化したり──

応用篇として、式変形の補助線を引いたりといったこともできます。

こうした一手間で、だいぶ、読み手の認知負荷を軽減できるのではというお話でした。

*1:ただし、これだけだと行間が空き過ぎてしまうため、本記事では意図的に行間を詰めている。

*2:例えば、紙面の都合で折り返しているが、実際には1行で入力せねばならない場合、当該行の行末に「↙︎」のような記号を付す場合などが考えられる。

*3:ソースコードの変更差分を強調したい場合などが考えられる。

Copyright (c) 2012 @tercel_s, @iTercel, @pi_cro_s.