I'm using matplotlib
to generate pgf
files.
Based on those, I use standalone tex
files which only include necessary settings and the afore-built pgf
s.
In this scenario, I'm getting errors when using custom tex-macros for descriptions in my plot files.
Here an example pgf
generator:
import matplotlib as mpl
mpl.use("pgf")
mpl.rcParams.update({
"pgf.texsystem": "pdflatex",
"pgf.preamble": [
#r"\newcommand{\foo}{foo}",
r"\usepackage{import}",
r'\subimport{./}{foo.tex}'
]
})
import matplotlib.pyplot as plt
plt.figure(figsize=(4.5,2.5))
plt.plot(range(5))
plt.xlabel(r'\foo{}')
plt.savefig('foo.pgf')
that can be used in a dir with the following foo.tex
file:
\newcommand{\foo}{foo}
Running this results in the following error:
ValueError: Error processing '\foo{}'
LaTeX Output:
! Undefined control sequence.
<argument> ....000000}{12.000000}\selectfont \foo
{}
<*> ...ze{10.000000}{12.000000}\selectfont \foo{}}
! ==> Fatal error occurred, no output PDF file produced!
Transcript written on texput.log.
Please note, that this is an error generated by matplotlib
and not of compiling my standalone files.
Also note, that the error goes away when the \foo
macro is provided as part of the pgf.preamble
(the line commented out) instead.
I checked the pgf
produced by this variant and indeed it uses \foo{}
.
I'm having trouble narrowing the problem further down. Here my concrete questions:
matplotlib
invoke pdflatex
at all?
I'm generating pgf
output and thus pdflatex
should not be necessary.
(For the reference: I strace
d the script above and indeed know that pdflatex
is being called.)matplotlib
tries to compile?
The error references texput.log
by (of course) that file doesn't exist afterwards.tl;dr Including tex
-files in the pgf.preamble
of matplotlib
requires absolute paths.
For the future, I recommend the following pdflatex
"replacement script" for purposes of debugging:
#!/usr/bin/env bash
MAIN=/usr/bin/pdflatex
cat /dev/stdin | tee /some/abs/path/to/dbg.tex | "${MAIN}" "${@}"
Make sure to save it as pdflatex
, make sure it's executable, make sure /usr/bin/pdflatex
is your actual pdflatex
and ensure this wrapper is found first in yout PATH
(cf. which pdflatex
).
When running the python
generator, we preserve the final tex
input in the dbg.tex
.
That answers (2).
Considering the output, we see:
\documentclass{minimal}
\usepackage{import}
\subimport{./}{foo.tex}
\begin{document}
text $math \mu$
\typeout{pgf_backend_query_start}
\sbox0{\sffamily\fontsize{10.000000}{12.000000}\selectfont lp}
\typeout{\the\wd0,\the\ht0,\the\dp0}
\sbox0{\sffamily\fontsize{10.000000}{12.000000}\selectfont 0}
\typeout{\the\wd0,\the\ht0,\the\dp0}
\sbox0{\sffamily\fontsize{10.000000}{12.000000}\selectfont 1}
\typeout{\the\wd0,\the\ht0,\the\dp0}
\sbox0{\sffamily\fontsize{10.000000}{12.000000}\selectfont 2}
\typeout{\the\wd0,\the\ht0,\the\dp0}
\sbox0{\sffamily\fontsize{10.000000}{12.000000}\selectfont 3}
\typeout{\the\wd0,\the\ht0,\the\dp0}
\sbox0{\sffamily\fontsize{10.000000}{12.000000}\selectfont 4}
\typeout{\the\wd0,\the\ht0,\the\dp0}
\sbox0{\sffamily\fontsize{10.000000}{12.000000}\selectfont \foo{}}
\typeout{\the\wd0,\the\ht0,\the\dp0}
I don't know what that should be useful for.
But I'm guessing matplotlib
is trying to adjust the font-setup for which it tries compiling this "test" document.
That (sort of) answers (1).
Now the conclusion (obvious in hindsight):
matplotlib
compiles this sample document in a temporary directory.
Clearly there is no foo.tex
available in this directory, so the subimport
fails.
From that point onwards it is obvious that \foo
will not be available.
Though not the cleanest solution,
this can be fixed by including foo.tex
via an absolute path.
Working python generator to finally answer (3):
import matplotlib as mpl
import pathlib
mpl.use("pgf")
mpl.rcParams.update({
"pgf.texsystem": "pdflatex",
"pgf.preamble": [
r"\usepackage{import}",
f'\subimport{{{pathlib.Path.cwd().resolve()}/}}{{foo.tex}}']
})
import matplotlib.pyplot as plt
plt.figure(figsize=(4.5,2.5))
plt.plot(range(5))
plt.xlabel(r'\foo{}')
plt.savefig('foo.pgf')
(I use python3
and pathlib
.
For python2
we would rather fall back to os.getcwd.)