2014/12/24

TeXの組版で1行ごとに色を変える

テキストからなるドキュメントのページというのは、行の集まりだと考えられます。 では、ページの行ごとに装飾を施すようなことは可能でしょうか? たとえば、本文を行ごとに色分けするようなことは可能でしょうか?

Webページでは、CSSを使って、表の各行の背景色をストライプにするような効果がよく使われています。 しかし、本文を行ごとに色分けするのは、CSSだけではかなり難しそうです。 ブラウザで本文の1行になるコンテンツをどう決めているのか知りませんが、それを制御しようと思ったら、ブラウザの描画処理に介入する方法が必要に思えます。

この事情は (La)TeX でも変わりません。 1行ごとに色を変えるといった処理を実現するには、行とページを組み立てる TeX の仕組みに介入する手立てが必要です。 TeXでは、その手立てが「出力ルーチン(アウトプットルーチン)」という形で提供されています。

ここでは、TeX & LaTeX Advent Calendar 2014 第24日のネタとして、行ストライプ問題を使って出力ルーチンで遊んでみます (第23日は tkz-fct パッケージがアレだった件、明日最終日はZRさんです)。

出力ルーチンとは

ものすごく端折って言うと、TeXは入力ソースを読み込んだら最適な改行位置を計算して各行を作ります。 その各行が溜まってきたら、そこから最適な改ページ位置を計算して1ページを作ります。 各行が溜まったものは「メイン垂直リスト」、そこからTeXが計算して作った1ページは「\box255」と呼ばれます。

しかし、\box255 がそのまま DVI ファイルや PDF ファイルの 1ページにはなるわけではありません。 \box255 を調理して DVI ファイルや PDF ファイルの 1ページにするのが「出力ルーチン」の仕事です。 ページノンブルを振ったり、柱を作ったりする仕事も、実は出力ルーチンで行われています。

この出力ルーチンにおける処理の内容は、ユーザが好きなように定義できます。 その定義では、\box255 をそのまま律儀に使う必要もありません。 たとえば、出力するページに残したい行だけを取り出して残りはメイン垂直リストに戻すことも可能です(改めて \box255 が組み立てられるので、戻した行は次ページ以降の材料として使われる)。 残したい行だけを取り出すときには、\vsplit というプリミティブが使えます。 「\vsplit ボックス to 高さ」のように実行すれば、指定したボックスから指定した高さぶんの行を取り出してきたボックスが手に入ります。

本文をクリスマスっぽいストライプにする

ここまでくれば、本文の行をストライプにする方法が見えてきましたね。 \box255 から \vsplit で1行ずつ取り出して交互に色を付け、それを集めたボックスを作り、そのボックスを出力ページとして吐き出せばよさそうです。

\documentclass[12pt]{article}

\usepackage{lipsum}
\usepackage[usenames,dvipsnames]{xcolor}

\fboxsep=0pt
\newcount\RGF
\RGF=0
\def\XmasStripe#1{
  \ifnum\RGF=0
    \colorbox{red}{\color{ForestGreen}#1}
    \global\RGF=1
  \else
    \colorbox{ForestGreen}{\color{Red}#1}
    \global\RGF=0
  \fi}

\output={ \loop \setbox1=\vsplit255 to\baselineskip \setbox0=\vbox{\copy0\XmasStripe{\copy1}\vskip-\dp1} \ifdim\ht255>0pt \repeat \shipout\box0}
\begin{document} \color{ForestGreen} % 最初の行のため。colorパッケージがおかしい? \lipsum[1-10] \end{document}
\output というのが、ストライプ柄のために定義した出力ルーチンです。 \box255 から \box1 へと1行ずつ取り出して交互に色を付け(\XmasStripe)、それらを次々に連結していった \box0 を作り(各行を連結するときにボックスの深さをつぶすために \vskip-\dp1 している)、最後にその \box0 をページとして吐き出しています。 ページとして吐き出すためのプリミティブは \shipout です。

実行結果はこんな感じ。もちろんドキュメントオプションでフォントサイズを変えても行ごとに色付けされます。スクリーンショットも貼っておきます。

注意

ここに載せたのはおもちゃのプログラムなので、ノンブル付けといった他の処理はすべて無効になってしまいます。 「Underfull/Overfull \vbox」もてんこ盛りです。

解説のほうも、いかにも出力ルーチンが簡単なものっぽい書き方をしてますが、正直なところ、書いている本人もそれほどちゃんとは理解していません。 なにせ、TeX が \box255 としてページを組み立てる処理と非同期に深く連動している処理です。 少なくとも TeX ブックの第14章、第15章を完全に理解している必要があります。 なので、本来はなるべくいじらないほうがいいでしょう。

参考文献

David Salomon "Output Routines: Examples and Techniques. Part I: Introduction and Examples."

No comments :