%% florilegium.sty -- a procedurally generated floral page margin, drawn by Lua %% https://github.com/kevinraymond/florilegium (MIT) %% %% LuaLaTeX only. The geometry of every petal, leaf and stem is computed in Lua %% (ink.lua, flora.lua) and written straight into the page as PDF path %% operators; TeX does no arithmetic. A page takes a second or two. %% %% Usage %% \usepackage{florilegium} authored composition (hand-placed, authored.lua) %% \usepackage[grown]{florilegium} composition grown to fit the page (grow.lua) %% \usepackage[nomargin]{florilegium} no margin, only \florilegiumdraw %% \usepackage[grown,novisitors]{florilegium} no butterflies, bees, snails or birds %% \usepackage[grown,sections]{florilegium} a flower beside every numbered \section %% and \subsection (standard classes) %% \usepackage[grown,aging]{florilegium} the garden ages from spring on the first %% page to autumn on the last (two runs: the page %% count is read from the .aux of the run before) %% \florilegiumbloom[motif] (grown) a flower, or any motif of grow.lua, in %% the margin beside the current line %% \florilegiumage{a} (grown) the age of the following pages: 0 spring, %% 0.5 summer, 1 autumn %% \florilegiuminitial[lines]{L} at the start of a paragraph: the letter L as a %% decorated initial, a tile `lines' lines tall %% (default 4), the text wrapped around it %% \florilegiuminitialfont{file.otf} the OpenType font the initial's outline is read %% from (default lmroman10-bold.otf) %% \def\florilegiumseed{7} before \usepackage: a fixed seed (any positive %% integer) instead of the page-number seed; on the %% command line: lualatex "\def\florilegiumseed{7}\input{doc}" %% \florilegiumtextend at the very end of the text (grown only): the %% garden then also fills the space below it %% \florilegiumframe[explain]{k}{n} grown only: the following pages show frame k of %% n of their garden's growth (animation.tex) %% \definecolor{ink}{HTML}{...} after loading: the hue of the whole palette %% \definecolor{accent}{HTML}{...} the copper of the hairline accents %% \florilegiumdraw{flower}{1.2} any primitive at the origin of the current TikZ %% scope; see flora.lua for the list and options, %% e.g. \florilegiumdraw{leaf}{2.0, {depth=0.6}} %% \NeedsTeXFormat{LaTeX2e} \ProvidesPackage{florilegium}[2026/10/30 v1.1 procedural floral margin, Lua-drawn] \ifdefined\directlua\else \PackageError{florilegium}{florilegium needs LuaLaTeX}{Compile with lualatex.}\fi \RequirePackage{tikz} \RequirePackage{eso-pic} \newif\ifflorilegium@grown \newif\ifflorilegium@margin \florilegium@margintrue \newif\ifflorilegium@visitors \florilegium@visitorstrue \newif\ifflorilegium@sections \newif\ifflorilegium@aging \DeclareOption{authored}{\florilegium@grownfalse} \DeclareOption{grown}{\florilegium@growntrue} \DeclareOption{nomargin}{\florilegium@marginfalse} \DeclareOption{novisitors}{\florilegium@visitorsfalse} \DeclareOption{sections}{\florilegium@sectionstrue} \DeclareOption{aging}{\florilegium@agingtrue} \ProcessOptions\relax \ifdefined\florilegiumseed\else\def\florilegiumseed{}\fi %% ---------------------------------------------------------------- palette %% `ink` gives the hue and saturation; the ramp from navy to mist is derived %% from it in OKLCH (ink.lua). `accent` is used for the fine copper lines. \definecolor{ink}{HTML}{2456C4} \definecolor{accent}{HTML}{B4662F} \directlua{florilegium = require("florilegium")} \ifflorilegium@visitors\else\directlua{florilegium.visitors = false}\fi %% pass the current colors to Lua \def\florilegium@colors{% \extractcolorspecs{ink}{\flo@m}{\flo@s}\convertcolorspec{\flo@m}{\flo@s}{rgb}\flo@ink \extractcolorspecs{accent}{\flo@m}{\flo@s}\convertcolorspec{\flo@m}{\flo@s}{rgb}\flo@acc \directlua{florilegium.colors(\flo@ink, \flo@acc)}} %% one primitive inside a tikzpicture, under the current transformation \newcommand\florilegiumdraw[2]{\florilegium@colors\directlua{florilegium.draw("#1", #2)}} %% ---------------------------------------------------------------- page seed \def\florilegium@seed{% \directlua{tex.sprint(florilegium.seed(\number\value{page}\ifx\florilegiumseed\@empty\else, \florilegiumseed\fi))}} %% ---------------------------------------------------------------- compositions \newcommand\margingarden{% \begin{tikzpicture}[overlay] \florilegium@colors \edef\flo@seed{\florilegium@seed}% \directlua{florilegium.authored(\flo@seed)}% \end{tikzpicture}} %% where the text ends, measured in the same pass: \pagetotal is the height of %% the material already on the current page \def\flo@endpage{-1}\def\flo@endtotal{0pt} \newcommand\margingardengrown{% \begin{tikzpicture}[overlay] \florilegium@colors \edef\flo@seed{\florilegium@seed}% \ifflorilegium@aging\ifdefined\flo@lastpage \directlua{florilegium.auto_age(\number\value{page}, \flo@lastpage)}\fi\fi \ifnum\value{page}=\flo@endpage\relax \directlua{florilegium.grown(\flo@seed, 1, florilegium.geometry().ytop - \number\dimexpr\flo@endtotal\relax * 2.54 / (72.27 * 65536), \number\ShipoutBox)}% \else \directlua{florilegium.grown(\flo@seed, 0, 0, \number\ShipoutBox)}% \fi \end{tikzpicture}} \newcommand\florilegiumtextend{\par \xdef\flo@endpage{\number\value{page}}% \xdef\flo@endtotal{\the\dimexpr\pagetotal+\pagedepth\relax}} %% the margin follows the text (grown only): a whatsit marks the line, and the %% shipout hook finds it in the page box (florilegium.lua) \newcommand\florilegiumbloom[1][]{\directlua{florilegium.mark("bloom", "\luaescapestring{#1}")}} \protected\def\florilegium@headmark#1{\ifnum#1<3 \directlua{florilegium.mark("heading", #1)}\fi} \ifflorilegium@sections % standard classes build every heading with \@sect; its last argument is the % text that is typeset (the TOC and marks get the optional one), so the mark % goes there, after the number \AtBeginDocument{% \let\flo@sect\@sect \def\@sect#1#2#3#4#5#6[#7]#8{\flo@sect{#1}{#2}{#3}{#4}{#5}{#6}[{#7}]{\florilegium@headmark{#2}#8}}} \fi \newcommand\florilegiumage[1]{\directlua{florilegium.age = #1}} %% a decorated initial: a square tile from the cap height of the first line to %% the baseline of line #1, the paragraph's first #1 lines indented beside it \newcommand\florilegiuminitial[2][4]{\par \begingroup \dimen@=\fontcharht\font`\H \global\dimen1=\dimexpr\baselineskip*\numexpr#1-1\relax+\dimen@\relax \global\dimen3=\dimen@ \endgroup \hangindent=\dimexpr\dimen1+0.6em\relax \hangafter=-#1\relax \noindent\florilegium@colors \directlua{florilegium.initial("\luaescapestring{#2}", \number\dimen1, \number\hangindent, \number\dimen3)}% \llap{\directlua{florilegium.hidden(true)}#2\directlua{florilegium.hidden(false)}}% \ignorespaces} \newcommand\florilegiuminitialfont[1]{\directlua{florilegium.initial_font = "\luaescapestring{#1}"}} \ifflorilegium@aging % the page count goes to the .aux; the next run reads it at \begin{document} \AtEndDocument{% \immediate\write\@auxout{\gdef\string\flo@lastpage{\number\value{page}}}% \ifdefined\flo@lastpage\ifnum\flo@lastpage=\value{page}\else\flo@rerun\fi\else\flo@rerun\fi} \def\flo@rerun{\PackageWarningNoLine{florilegium}{The page count changed. Rerun to get the aging right}} \fi %% growth animation (grown only; animation.tex, tools/animate.py): the pages %% that follow show frame #2 of #3 of their garden's growth; the option %% [explain] also marks the free disc that each branch grows to \newcommand\florilegiumframe[3][]{\directlua{florilegium.frame(#2, #3, "#1")}} %% ---------------------------------------------------------------- plumbing \ifflorilegium@margin \ifflorilegium@grown \AddToShipoutPictureBG{\AtPageLowerLeft{\margingardengrown}} \else \AddToShipoutPictureBG{\AtPageLowerLeft{\margingarden}} \fi \fi \endinput