Samuel Ireson

Conditional rendering in TeX

Sat Sep 14 2024
5 min read

A technique for conditional inclusion of parts of a TeX document.

Contents

Introduction

Let me describe to you the situation which I found myself in. I wanted to create some maths problems sheets, and needed a way to maintain a single document while rendering both an output which included the solutions and an output which didn’t. There is an obvious way to do this; just comment and uncomment the solutions each time you want to compile the relevant document, but this is a bit of a pain, especially when the document needs to go through many iterations. There is another solution; maintain each of the problem and solution sheets separately, but this is equally painful, since every single change will require another change to prevent differences between the documents.

I recently found a better way, and I’ll outline this method now.

The method I’m now using involves symlinks. A symlink, short for symbolic link, is a file which points to another file. Strictly speaking, there are two kinds of symlink, hard and soft, with the former pointing exactly to the point in memory, and the latter pointing to the file location.

The idea of the method is to create a single master.tex file, then make symlinks to this file, with specifically chosen names which we will use to conditionally render parts of the document. For example, we can create the master file, and two symlinked files with,

touch master.tex
ln -s master.tex problems.tex
ln -s master.tex solutions.tex

where the -s specifies that the symlink should be soft.

Conditional rendering

From now, the only document which we need to alter is master.tex, as the other files are symlinked to this file, and will β€˜change’ accordingly.

As an example, consider the following master.tex

\documentclass{article}

\begin{document}
Q: What is the best way to learn mathematics?
A: Do loads of problems!
\end{document}

So, in the problems.pdf which will be created on compilation of problems.tex, we want only to see the question, and in solutions.pdf, similarly created, we want to see everything. In order to achieve this result, we will use lualatex to access, and condition on the filename.

In order to access the filename in lualatex, we can make a \directlua call, and access the arg object, which contains a list of all the arguments passed to lualatex. For example,

\documentclass{article}

\directlua{
	if (arg[1] == 'solutions.tex') then
		% Condition to go here.
	end
}

\begin{document}
Q: What is the best way to learn mathematics?
A: Do loads of problems!
\end{document}

Now, we can conditionally include a specific statement if the filename is solutions.tex. This specific statement should relate to an \if which we have defined elsewhere in the document.

\documentclass{article}

\newif\ifsolution

\directlua{
	if (arg[1] == 'solutions.tex') then
		tex.print("\string\\solutiontrue")
	end
}

\begin{document}
Q: What is the best way to learn mathematics?
\ifsolution
A: Do loads of problems!
\fi
\end{document}

Compilation

Now, to obtain both of the .pdf files which we were after, we can simply compile each of the symlinked files with lualatex.

lualatex problems.tex
lualatex solutions.tex

I think this is pretty cool! We don’t need any external packages or dependencies; we’re only relying on the power of lualatex, which you’re probably using anyway.

Example use case: CV management

Maybe it’s not a common situation, but there are a wide variety of jobs which I find interesting and want to apply for. As a result of this, I can’t often use the same CV for every application. It wouldn’t really make sense to highlight the same things when applying for data analyst roles and linux engineer roles for example.

So the question becomes, how can one effectively maintain multiple CVs simultaneously. There are obviously many similarities between the CVs which I want to submit, and as such when I make a change to a common area, I would like this change to be seen in every CV. This is a perfect use case for the conditional rendering which I have outlined above.

I use the following directory structure,

β”œβ”€β”€ academic
β”‚Β Β  └── Samuel Ireson CV.pdf
β”œβ”€β”€ data
β”‚Β Β  └── Samuel Ireson CV.pdf
β”œβ”€β”€ general
β”‚Β Β  └── Samuel Ireson CV.pdf
β”œβ”€β”€ internship
β”‚Β Β  └── Samuel Ireson CV.pdf
β”œβ”€β”€ quant
β”‚Β Β  └── Samuel Ireson CV.pdf
β”œβ”€β”€ software
β”‚Β Β  └── Samuel Ireson CV.pdf
β”œβ”€β”€ Samuel Ireson CV.tex
└── sections
 Β Β  β”œβ”€β”€ academic_skills.tex
    .
    .
    .
 Β Β  └── societies.tex

In this case, instead of conditioning on the filename, I pass an additional argument to lualatex, and condition on that β€” like this.

% IFS
\newif\ifacademic
\newif\ifdata
\newif\ifgeneral
\newif\ifinternship
\newif\ifquant
\newif\ifsoftware

\directlua{
	if (arg[2] == 'academic') then
	tex.print("\string\\academictrue")
	elseif (arg[2] == 'data') then
	tex.print("\string\\datatrue")
	elseif (arg[2] == 'general') then
	tex.print("\string\\generaltrue")
	elseif (arg[2] == 'internship') then
	tex.print("\string\\internshiptrue")
	elseif (arg[2] == 'quant') then
	tex.print("\string\\quanttrue")
	elseif (arg[2] == 'software') then
	tex.print("\string\\softwaretrue")
	end
}

Then, I use a Makefile to compile the CV multiple times, each time passing a difference argument to lualatex, and moving the .pdf into a well-named directory to avoid over-writing. The exact Makefile is,

INPUTS = Samuel\ Ireson\ CV.tex sections/*.tex
COMPILER = lualatex
MASTER = Samuel\ Ireson\ CV.tex
CVS = academic data general internship quant software

Samuel\ Ireson\ CV.pdf: $(INPUTS)
	for dir in $(CVS); do \
		mkdir -p $$dir; \
		$(COMPILER) $(MASTER) $$dir; \
		mv Samuel\ Ireson\ CV.pdf $$dir; \
	done

I can run this whenever I want to re-compile all of the CVs.

Conclusion

Conditional rendering in tex turns out to be more straightforward than I initially thought, and has loads of potential uses. Hope you enjoyed :)

Related posts