Florilegium - LaTeX の余白に花を咲かせる
Lua で計算しながら LaTeX の余白にデルフトブルーの花草を育てるパッケージ。ページごとに違う庭が生まれる。
日本語
コピー

由来
Reddit の投稿は、こうした花を LaTeX でどう作るかを問いかけていた。元の作品がどう作られたのかは知らないし、このプロジェクトもそれを再現しようとはしていない。これはひとつのやり方であり、私が実際にやったやり方だ。パラメータをランダム化したプリミティブを手で書き、Lua で彩色し、手続き的なレイアウトで配置する。そのレイアウトの「余った空間を埋める」部分は、1998 年に装飾文様の研究で示されたアイデアを借りている。見た目は近くなる。
これは何か
LaTeX 向けに手続き生成したデルフトブルーの花のページ余白。画像も、読み込んだパスも、外部アセットもない。花びら、葉、茎のひとつひとつが、ページの書き出し中に Lua で計算されるパラメトリック曲線で、水彩のように彩色され、そのまま PDF に書き込まれる。ページごとに違う庭が生まれる。

育てた構図の種を 6 つ。同じコードで種だけを変えると、テキストが残した余白を庭が埋めていく。
2 つの文書が 1 つのパッケージを共有し、さらに 2 つ(showcase.tex、spread.tex)がほかに何ができるかを示す:
florilegium.tex | florilegium-grow.tex | |
|---|---|---|
| 構図 | 手書き: 茎、葉、花を手で配置し、すべてのパラメータをサンプリング | 生成: 貪欲な空間充填ループでページに合わせて育てた植物 |
| 適応する対象 | なし(A4、余白 68 mm 向けに調整) | ページレイアウト、および最終ページでテキストの下に残る余白 |
どちらの PDF も自己記述的で、本文は余白がどう描かれるかを記した解説になっている。


300 dpi で切り出した図版。斑状の淡い色面に白い筋を抜き、白い葉脈の入った裂片状の葉、ワスレナグサの集散花序、茎の後ろに淡く浮かぶ花の影。
成長の過程を見る


シード 6、空白の余白から完成したページまで。まず茎が伸び、葉は先端が節を過ぎたところで開き、それぞれの茎は花で終わる。次に貪欲ループの出番だ。右側では、破線の円がそれぞれ、その時点で見つかった最大の空き円盤を表す。直後に枝がそこへ伸びる。大きな穴が先に、ゆっくりと。小さな穴は最後に、速く。蝶と蜂が飛んできて、鳥は小枝に滑り込み、カタツムリは茎を這い上がる。最後のフレームまで来ると、それはこのシードがいつも生み出すページそのものだ。
成長の過程は、ページをレイアウトするときに一度だけ記録しておく。各茎は長さを段階的に伸ばした接頭辞としても描き、各モチーフはいくつかの小さめのスケールでも描く。同じ乱数の抽選を再生しているだけだ。あとはフレームごとに、その時点が来たパーツを元の描画順で書き出す。make grow-6 はこの方法で 1 つのシードを描画する(build/grow-6.gif と .mp4、約 45 秒)。破線の円を出すには EXPLAIN=1 を付ける。
余白がテキストを読む

showcase.tex「余白の一年」:最初のページが春、次が夏、最後のページが秋。各節はその見出しが示す機能を説明する。
育った余白は、周囲の本文に反応する:
- 頭文字。
\florilegiuminitial[5]{E}は段落の最初の一文字を、5 行分の高さのタイルに組む。文字の輪郭は OpenType フォント(既定では Latin Modern Bold)から読み取り、花びらのように描く。つるが文字の線の上と下を交互に通り抜け、収まる場所には葉を、隙間には花を置く。花は文字のカウンター(閉じた空間)に入ることが多い。文字自体は見えない形でも組まれるので、段落をコピーすれば今まで通り「Every」になる。 - 見出しの花。 オプション
sectionsを付けると、番号付きの節と小節すべての見出しの横の余白に花が描かれる。色はアクセントの銅色で、かつて見出しを赤で際立たせたのと同じやり方だ。 - 本文が求めた場所に花を。
\florilegiumbloomはそれが置かれた行の横に花を描く。\florilegiumbloom[lily]には庭のモチーフなら何でも指定できる。この印はページを出力するときにページボックスの中から拾われるので、1 回の実行で機能する。 - 季節。 オプション
agingを付けると、庭は最初のページの春(ほとんどがつぼみで、小さく淡い葉)から最後のページの秋(種子の頭、散る花びら、銅色の葉が数枚)へと移り変わる。ページ数を数えるため 2 回の実行が必要になる。代わりに\florilegiumage{0.8}で年齢を手動で設定することもできる。


春、初夏、晩夏、秋にそれぞれ一粒の種を蒔く。季節が変えるのは実るものだけで、場所は変わらない。
訪問者

ほとんどのページには訪問者がいる。開けた場所にはチョウが一、二頭とハチ、主茎の一本を登ってくるカタツムリ、そして五ページに一ページほどの割合で、自分の小枝にとまった鳥が一羽。訪問者はそれぞれ独立したランダムストリームから来るので、訪問者のいないページは、訪問者がいた場合とまったく同じページになる。オプションnovisitorsを指定すると、訪問者は描かれない。
任意のインク

パレット全体はひとつの色、inkから生まれる。デルフトブルー、セピア、緑、クリムゾン、紫、ティール。最も濃い線から最も淡いにじみまでの段階は、その色相から OKLCH で計算するため、明るい側が灰色に転ぶことはない。
Themes

成長ループは、自分が何を描いているのかを知らない。モチーフの表(各モチーフが取る円盤の大きさ、重み、それを描く関数)を読み、すべてのプリミティブを名前で呼び出す。テーマは 1 つの Lua ファイル theme-<name>.lua で、プリミティブの一部を置き換え、表に行を追加することもあれば、独自のコンポジションを持ち込むこともある。
theme=geometric は同じ植物を平面図形から切り出したものだ(theme-geometric.lua)。成長の仕方は花とまったく同じで、変わるのは描画だけ。茎はノード間をまっすぐに走り、葉は凧形と鋸歯状の多角形を明るい面と暗い面に折り、まっすぐな白い葉脈を入れたもの。花は菱形のロゼットか、層を重ねた星形多角形。巻きひげは四角い螺旋、鳥と蝶はローポリ。色はインクのフラットな濃淡で、ぼかしはない。花のあいだにはクリスタル(プラトン立体を、カットストーンのようにフラットシェーディングしたもの)が育つ。1 ページのサイズは flora のページのおよそ 10 分の 1 だ。


theme=math は植物ではない(theme-math.lua、figures.lua から描画)。余白を伝ってページ下端へと数式が流れ落ち、少し重なりながら溶け合う。段組みの下端と交わる角には大きな図版を置く(アポロニアの詰め込み、深いコッホ雪片、Vogel の円盤、多面体)。段組みに沿って一つか二つのファミリーが並ぶ。図が連なるにつれて形を変え、小さな銅色の矢印でつながっていく。コッホ雪片が一段階深くなり、多角形が辺を増やして円に近づき、立方体が切り詰められて八面体になり、弦 k → mk mod n がカージオイドからネフロイドへと変わり、Vogel の円盤は黄金角のときだけ均等に詰まる。成り立つ等式(e^{iπ} + 1 = 0、V − E + F = 2、……)は下端と最終ページの本文下に沿って流れる。それ以外のすべては、大きな穴から順に、図(プラトン立体、結び目、ヒルベルト曲線、リサージュ曲線、ハイポトロコイド、Maurer のバラ、ペテルセングラフ、……)、TeX で組んださまざまな大きさとわずかな傾きの文字・数字・記号、そして点である。庭と同じように、ページが残した空間を埋めていく。どれだけ入るかは density が決める。

\usepackage[grown,theme=geometric]{florilegium} % or theme=math; the whole document
\florilegiumtheme{math} % or from the page this stands on
テーマを指定しない文書は、テーマが存在する前とバイト単位で同じ出力になる。
余白を減らす

方程式のそばに庭を丸ごと置くのは重すぎる。テーマを問わず、控えめにする方法は 2 つある。
density=0.4は育つ量を減らす。ループがより大きな穴で止まり、各モチーフの周りに残る紙が増え、葉やフィラーは少なくなる。ダイヤルを最小にすると植物そのものが痩せていく。主茎が 2 本、次に 1 本、フッターなし。0.05では花を付けた 1 本の蔓に、葉が 2、3 枚と房が 1、2 つだけ。math テーマなら要素が減るということだ。intensity=0.5は余白全体を淡く塗る。庭の背後にあるゴーストフラワーと同じ塗り方だ。変わるのは色だけで、ディテールは変わらない。
\usepackage[grown,density=0.4,intensity=0.6]{florilegium}
デフォルト(1 と 1)では、どのページも以前とまったく同じページになる。
テキスト中の装飾ひとつ

余白をまったく取らないこともできる。\florilegiumornament{leaf} は行の中に 1 つのプリミティブを、文字 1 つ分の大きさの枠に入れて配置する。たとえば段落の冒頭を飾るのに使う。文書内の各オーナメントはそれぞれ固有の形を持ち、ビルドを重ねてもその形を保つ。seed=random は PDF をビルドするたびに新しい形を描き、seed=7 は固定の形を描く。
\usepackage[nomargin]{florilegium} % ornaments only, no garden in the margin
\florilegiumornament{leaf} A paragraph that opens with a leaf.
\florilegiumornament[size=2.4em, ink=5B2A1F, angle=30, seed=random]{flower}
どのプリミティブでも使える(leaf、leaflet、flower、blossom、bud、bell、lily、cyme、umbel、butterfly、bee、bird、hedera、sparkle、...)。現在のテーマのプリミティブも同様だ(crystal と geometric、knot、polyhedron、koch、... と math)。
ページの両側

spread.tex、2 ページと 3 ページ:外側の余白を広く取った両面ドキュメント。ガーデンは左ページの左側に育ち、鏡映されて右ページの右側にも育つ。最終ページでは幾何学テーマにも切り替わる。
ガーデンは余白が広いほうの側に育つので、設定するものは何もない。両面ドキュメント(twoside、outer余白を広く取る)ではページに応じて左右が入れ替わる。右余白を広く取った片面ドキュメントなら、どのページでも右側に育つ。鏡映ページは鏡に映したようにレイアウトされ、そのうえで反射を通して描画される。すべての点がすでに経由している最後の変換、それが反射だ。同じシードが左側に育てるガーデンの、正確な反射になる。
PostScript の場合

postscript/florilegium.ps:純粋な PostScript で描く、成長する余白。ファイルは一つで完結する。Ghostscript かプリンタがページを解釈しながら花びらを一枚ずつ計算し、テキスト 2 ページがその仕組みを説明する。
描画の中核、すべてのプリミティブ、成長ループは ink.lua、flora.lua、grow.lua から手続き単位で移植した。TeX も他のファイルも要らない:
gs -dALLOWPSTRANSPARENCY -sDEVICE=pdfwrite -dSeed=7 -o garden.pdf postscript/florilegium.ps
make postscript # the same -> build/postscript-7.pdf; SEED=12 for another garden
-dSeed=N で庭を選ぶ。指定しなければ時計が選ぶので、印刷するたびに違う庭になる。-dAge=0 から 1 で季節を決め、-dNoVisitors を付けると蝶、蜂、蝸牛、鳥を省く。2 ページで約 2.5 秒。Lua の多くはそのまま対応する。PostScript 自身の変換行列が各プリミティブを配置し(translate、rotate、scale)、パスと Bézier 曲線はネイティブで、実行時に組み立てた手続きが Lua のクロージャの代わりになる(花びらの半幅は輪郭ビルダーに渡す手続きだ)。対応しないものが四つある:
- レイヤー。 庭は一つの順序で成長するが、描画は奥から手前へ行う。そこで各 fill と stroke を
pathforallでレイヤーごとのリストに取り込み、最後にリストを順に再生する。 - 透明。 PostScript には透明がない。Ghostscript なら
-dALLOWPSTRANSPARENCYで使える。このフラグなしの場合、あるいはプリンタ上では、半透明の fill は着地しそうな色と混ぜて不透明で塗る。結果は平坦になるが、庭は同じだ。 - 乱数。 生成器は 32 ビット整数演算の Park-Miller で、PostScript の実数は単精度なので、ここでの seed 7 は LaTeX での seed 7 とは別の庭になる。
- テキスト。 ファイルは自前の 2 ページを Times で組む。見出しがどこにあり、本文がどこで終わるかを把握しているので、見出しの銅色の花も最終行の下の庭も LaTeX と同じように働く。イニシャル、アニメーション、テーマ、鏡像ページ、密度や強度、本文中の装飾、著者による構図は持たない。

lp postscript/florilegium.psを使い、シードを指定せずに Epson のインクジェットプリンタへ 2 回送ったファイル。コンピュータの印刷システムはジョブごとにプログラムを再実行するため、印刷のたびにシードを時計から取り、異なる庭が育つ。プリンタには不透明版が渡される。透明を持たないからだ。
ビルド
LuaLaTeX、1 パス。1 ページおよそ 1 秒(showcase.texは 2 パス。季節にページ数が必要なため)。
make # the four PDFs
make seed-7 # one page from seed 7 -> build/seed-7.pdf
make gallery # gallery/seeds.png + seeds.gif, inks.png, seasons.png, themes.png, dials.png (python3, Pillow, pdftoppm)
make grow-6 # growth animation of seed 6 -> build/grow-6.gif + .mp4 (ffmpeg)
make grow-6 EXPLAIN=1 # the same with the free discs marked -> build/grow-6-explain.*
TeX が入っていない場合は、イメージを一度ビルドし、その中で全部実行する:
docker build -t florilegium-tex tools
make RUN=tools/in-docker
あるいはflorilegium.sty、9 つの.luaファイル、.texファイルのいずれかをOverleafのプロジェクトに貼り付け、コンパイラを LuaLaTeX に設定する。
自分のドキュメントで使う:
\usepackage[grown]{florilegium} % or plain \usepackage{florilegium} for the authored one
% options for the grown one: sections (flowers at the headings), aging (two runs),
% novisitors (no butterflies, bees, snails or birds); for both: theme=geometric or theme=math,
% density=0.4 (grown only), intensity=0.6
% two-sided? nothing to set: the garden grows in the wider side margin
\definecolor{ink}{HTML}{5B2A1F} % optional: the hue of the whole palette (this is sepia)
\definecolor{accent}{HTML}{B4662F} % optional: the copper of the hairlines
...
\florilegiuminitial[4]{T}his paragraph starts with a decorated T.
... a flower in the margin beside this line\florilegiumbloom ...
\florilegiumage{0.8} % grown only: autumn from here on
\florilegiumtheme{geometric} % another theme, from the page this stands on
\florilegiumornament{leaf} % one leaf in the text, the size of a letter
\florilegiumtextend % grown only: at the very end of the text
ページ番号の代わりに固定シードを使う: lualatex "\def\florilegiumseed{7}\input{florilegium-grow.tex}"。
どのプリミティブも、現在のスコープの移動・回転・拡大の下で、TikZ ピクチャの中に単独で描ける:
\begin{tikzpicture}
\begin{scope}[shift={(2,2)},rotate=20]\florilegiumdraw{flower}{1.3}\end{scope}
\begin{scope}[shift={(5,0)}]\florilegiumdraw{leaf}{3.0, {depth=0.6}}\end{scope}
\begin{scope}[shift={(8,1)}]\florilegiumdraw{ghost}{"lily", {s=1.4}}\end{scope}
\begin{scope}[shift={(11,1)}]\florilegiumdraw{butterfly}{1.5}\end{scope}
\end{tikzpicture}
テーマを使えば、プリミティブを名前で描くこともできる: theme=geometricの下で\florilegiumdraw{crystal}{1.0}(specimen-geometric.texを参照)、theme=mathの下で\florilegiumdraw{knot}{1.2}。
仕組み
描画(ink.lua)。ジオメトリはすべて Lua で計算し、PDF のパス演算子としてページごとに 1 つのリテラルノードへ書き出す。TeX は一切計算しない。プリミティブは局所座標系でアフィン変換の下に描かれ、その変換はスコープ内でプリミティブが呼ばれたときに TikZ から読み取られる。色は、文書で唯一の色inkから導かれるトーンランプによる。inkの色相でネイビーからミストへ、OKLCH で補間するので明るい側は灰色にならず青のままになる。先細りのストロークは 2 本のオフセット Bézier で描き、サンプリングしたリボンではない。
プリミティブ(flora.lua)。水彩画家が塗る順に描く。平塗り、その上に乗算で濃い斑、縁に溜まる顔料、白で抜く筋、細い葉脈をいくつか、輪郭はほとんど描かない。幅広の花びらと花、傾けて見たスターチス、花と小花、つぼみとベルフラワー。切れ込みのある葉と全縁の葉。サソリの尾のように巻くワスレナグサの集散花序、枝分かれする小枝、散形花序、巻きひげ、種子頭。蝶、蜂、カタツムリ、鳥。銅色のきらめき、ロゼット、そして茎が見出し罫になるアイビー葉のフルーロン。どれも淡いゴーストとして描ける。
手で組んだ構図(authored.lua)。植物は左下隅の根から育つ。3 本の茎が手で選んだ制御点を通り、4 本目がフッターとしてページ下端に沿う。葉は茎の決めた割合の位置に付き、その他のモチーフはすべて、茎から接線方向に出てモチーフの軸に沿って届く枝の上にある。だからどの花も根までたどれる。
育つ構図(grow.lua)。Wong、Zongker、Salesin のComputer-Generated Floral Ornament(SIGGRAPH 1998)の精神で。茎は高さの滑らかなノイズとして柱をなして揺らぎ、葉は余地のある節に吊るされる。あとは繰り返し。茎の届く範囲で最大の空き円盤を取り、それに収まるモチーフを選び、最寄りの茎の点からそこへ枝を伸ばす。枝は今度は茎になる。大きな穴から埋まるので、花やユリは早くに収まり、房と葉が中ほどを満たし、ループの終わりには小葉、蕾、小花が茎に押し込まれる。テキストの終端は同じパスで\pagetotalを使って測るので、最後のページが短ければ、その下の空間へ枝が伸びる。コンパイルは 1 回で済む。
ページを読む(florilegium.lua)。見出しや\florilegiumbloomは、テキスト中に目に見えないマーカーノードを残す。ページを出力するとき、そのボックスをノードごとにたどり、組んだとおりの高さ、深さ、グルーを足し上げて、各マーカーのページ上の高さを求める。庭はまずそれらの行に花を与え、そのあとで葉を吊るす。年齢はモチーフの重みを変える。春は蕾と蕾の房、秋は種子の頭。秋には一部の葉を、インクではなくアクセントから組んだランプで塗る。
頭文字(initial.lua)。文字の輪郭は luaotfload の OpenType リーダーからベジェ曲線として得る。多角形に平坦化すれば偶奇の内外判定ができ、文字と枠への距離のグリッドから花を置く最大の隙間が見つかる。蔓はタイルを横切る Catmull-Rom 曲線。文字と交わるところでは表と裏を交互に通り、表に出る側の糸は文字でクリップして描き直し、紙色の細いハローを付ける。
手続き的生成 ≠ ランダム。 シードはページ番号から導出されるため、ページ n には常に庭 n が割り当てられる。再現性があるからこそ、この文書は組版できる程度に安定する。
コードの引き継ぎ用マップ、調整可能なパラメータ、未着手のアイデアについては CLAUDE.md を参照。
クレジット
- この問いを投げかけた Reddit の投稿(Origin 参照)。
- 成長ループは Wong, Zongker & Salesin の Computer-Generated Floral Ornament(SIGGRAPH 1998)に基づく。
- トーンランプは Björn Ottosson の知覚色空間 Oklab で計算している。
ink.luaにある変換行列は氏のもので、パブリックドメインとして公開されている。 - 乱数は Tommy Ettinger のパブリックドメイン生成器 mulberry32 による。
\florilegiumornament、そのinkキーとseed=randomキー、そしてdensityオプションは、rubyFeedback による issue #4 の回答に基づく。- 数学テーマの図には、由来となる人物の名前がそのまま付いている。Maurer のバラ、Vogel のヒマワリ模型、Temple Fay の蝶曲線、Petersen グラフ、Koch の雪片、Hilbert 曲線、Lissajous 図形、Apollonius のギャスケット。
ライセンス
MIT。LICENSE を参照。Copyright (c) 2026 Kevin Raymond.