26 Jun 2025 - tsp
Last update 26 Jun 2025
5 mins
We have all been there - writing up a paper or a thesis in our favorite typesetting system (i.e. the only sane one to use for any serious publication - LaTeX). We build up our text via appropriate annotations, apply our layout classes, write our math just as easy as it gets - but then we have to include figures that we plot our own. We would like to use consistent styling (same fonts, same font sizes, same styles, etc.) but somehow just rendering our figures in an external program and inserting them using the traditional includegraphics
does not do the trick - the labels are off, the fonts not the same, any change in scaling destroys consistency. And we have to manually rebuild our graphics whenever something changes. Would it not be nice to have an automated and simple way of generating our graphics? There is a pretty nice one when already utilizing a Makefile
to build your document and using Pythonโs matplotlib
to generate your graphics indeed. Itโs using the pgf
file format.
For simplicity letโs assume we want to draw a simple sine- and cosine wave in our document. So letโs first write a simple matplotlib
script that does that - but use the pgf
backend and file format to store our result. We call this sinecosine.py
(note that our Python file should have the same name as the pgf output - just a different file ending. We will not enforce this in this example though).
# Before importing the plt object we have to set the PGF backend
import matplotlib as mpl
mpl.use('pgf')
# Then do imports as usual
import matplotlib.pyplot as plt
import numpy as np
# Our parameters
f = 10 # We do calculation at 10 Hz
omega = 2 * np.pi * f
t = np.linspace(0, 1, 1000) # One second period with 1000 sampling steps
A = 1.0 # Amplitude
f1 = A * np.sin(omega * t)
f2 = A * np.cos(omega * t)
fig,ax = plt.subplots()
ax.plot(t, f1, label = "Sine")
ax.plot(t, f2, label = "Cosine")
ax.grid()
ax.set_xlabel("Time [s]")
ax.set_ylabel("Function value [arb.]")
ax.legend()
plt.savefig('sinecosine.pgf', format = 'pgf')
This will generate the following graph for us:
The next step is the Makefile
that we utilize to build our document. A very simple Makefile
for GNU make
to build a document could look like the following:
LATEX_FLAGS = -halt-on-error -interaction=nonstopmode
LATEX = pdflatex
BIBTEX = bibtex
FIGURES_DIR := figures
FIGURES_SCRIPTS := $(wildcard $(FIGURES_DIR)/*.py)
PGF_FILES := $(FIGURES_SCRIPTS:.py=.pgf)
all: main.pdf clean
.phony: clean cleanall graphics
graphics: $(PGF_FILES)
$(FIGURES_DIR)/%.pgf: $(FIGURES_DIR)/%.py
@echo "Generating $@ from $<"
cd $(FIGURES_DIR) && python3.11 $(<F)
main.pdf: main.tex main.bib graphics
$(LATEX) $(LATEX_FLAGS) main.tex
-$(BIBTEX) main
$(LATEX) $(LATEX_FLAGS) main.tex
$(LATEX) $(LATEX_FLAGS) main.tex
clean:
rm -rf *.aux *.bbl *.blg *.log *.out *.toc *.lof *.lot *.ist *.glo *.acn
cleanall: clean
rm -rf main.pdf
rm -rf $(FIGURES_DIR)/*.pgf
As one can see in this Makefile
Iโve defined an environment variable that describes where figures are stored (FIGURES_DIR
). From this I derive a list of Python scripts called FIGURES_SCRIPTS
as well as the associated pgf
filenames in PGF_FILES
FIGURES_DIR := figures
FIGURES_SCRIPTS := $(wildcard $(FIGURES_DIR)/*.py)
PGF_FILES := $(FIGURES_SCRIPTS:.py=.pgf)
A simple wildcard rule describes how to build the pgf
files by executing the Python script - in my case with python3.11
. As soon as a pgf
file from the FIGURES_DIR
is occuring as a dependency in any target the following rule is executed to build the pgf
file:
$(FIGURES_DIR)/%.pgf: $(FIGURES_DIR)/%.py
@echo "Generating $@ from $<"
cd $(FIGURES_DIR) && python3.11 $(<F)
As a last step Iโve added a simple rule graphics
that depends on all known pgf
files that are generateable from the Python files in the figures directory. This allows for a more readable Makefile
and our main rule for main.pdf
depends on it:
graphics: $(PGF_FILES)
This way the Makefile only invokes the Python scripts whenever one of the script files has changed to save compilation time which is rather crucial for larger documents.
The remaining part of the Makefile
is just standard way to build a TeX document. Multiple invocations of pdflatex
and a matching bibtex
call are used to generate the indices and the resulting document. An accompanying clean
rule (that is .PHONY
is it gets executes even in case nothing has changed) allows to delete all intermediate files. We do not delete our generated pgf
files of course though we do not store them in out git
repository.
Now to the last part is to utilize the graphics inside our LaTeX document. This is very simple. We utilize the pgf
libraries:
\usepackage{pgf}
\usepackage{pgfkeys}
\usepackage{pgfmath}
At the appropriate position we just include the figure file:
\begin{figure}
\input{figures/sinecosine.pgf}
\caption{Our sine and cosing PGF rendering}
\label{fig:sinecosine}
\end{figure}
To sum up here is a complete sample available as a GitHub GIST:
The above example has shown a very simple and scaleable way on how to automatically generate consistent plots for your LaTeX document in a very simple and easy way. It again shows the power and flexibility of toolchains like LaTeX
, GNU make
and Pythons matplotlib
.
This article is tagged:
Dipl.-Ing. Thomas Spielauer, Wien (webcomplains389t48957@tspi.at)
This webpage is also available via TOR at http://rh6v563nt2dnxd5h2vhhqkudmyvjaevgiv77c62xflas52d5omtkxuid.onion/