sphinx-contrib / tikz

A Sphinx extension to draw pictures with the TikZ/PGF LaTeX package
Other
19 stars 15 forks source link

tikz_latex_preamble does not escape backslash characters #19

Open doyougnu opened 6 months ago

doyougnu commented 6 months ago

behavior

consider this setting in conf.py where I'm just trying to define a macro

...
tikz_latex_preamble = "\newcommand\\Foo[1]{Z}"
...

Then using one of the examples:

.. tikz:: [>=latex',dotted,thick] \draw[->] (0,0) -- (1,1) -- (1,0)
   -- (2,0);
   :libs: arrows

note that I am not even using the macro, this yields:

System Message: WARNING/2 ([>=latex',dotted,thick] \draw[->] (0,0) -- (1,1) -- (1,0) -- (2,0);)

! LaTeX Error: Missing \begin{document}. See the LaTeX manual or LaTeX Companion for explanation. Type H <return> for immediate help. ... l.9 e wcommand\Foo[1]{Z} ! Undefined control sequence. l.9 ewcommand\Foo [1]{Z} (/nix/store/2kqi4kak6rgpzr5s2kh1fsv3ynz9s3xd-texlive-2022-env-texmfdist/tex/lat ex/l3backend/l3backend-pdftex.def) No file tikz-9623d8fec8222981fc4dda796d12608e10225c41.aux. (/nix/store/2kqi4kak6rgpzr5s2kh1fsv3ynz9s3xd-texlive-2022-env-texmfdist/tex/con text/base/mkii/supp-pdf.mkii [Loading MPS to PDF converter (version 2006.09.02).] ) (/nix/store/2kqi4kak6rgpzr5s2kh1fsv3ynz9s3xd-texlive-2022-env-

because the \n in \newcommand is interpreted as a newline, not as latex code.

Expected behavior

tikz_latex_preamble is interpreted as latex code or automatically escapes backslashes.

Workaround

manually escape the newline like:

tikz_latex_preamble = "\\newcommand\Foo[1]{Z}"

or use a raw string:

tikz_latex_preamble = r"\newcommand\Foo[1]{Z}"

A more complicated example

Consider this preamble:

tikz_latex_preamble = """
\\newcommand\MemoryLayout[1]{
  \begin{tikzpicture}[scale=0.3]
     \draw[thick](0,0)--++(0,3)node[above]{$0$};
     \foreach \pt/\col/\lab [remember=\pt as \tp (initially 0)] in {#1} {
       \foreach \a in {\tp,...,\pt-1} {
          \draw[fill=\col](-\a,0) rectangle ++(-1,2);
       }
       \draw[thick](-\pt,0)--++(0,3)node[above]{$\pt$};
       \if\lab\relax\relax\else
         \draw[thick,decorate, decoration={brace,amplitude=4mm}]
            (-\tp,-0.2)--node[below=4mm]{\lab} (-\pt,-0.2);
       \fi
     }
  \end{tikzpicture}
}
"""

note the \r in \relax and \t in \tp and the \f in \foreach and \fi. Each of these will need to be escaped as well

philexander commented 6 months ago

Thank you for opening this issue.

Rationale

Imho this is a question of correct usage of Python. In Python, a literal string "\newcommand" starts with a newline. A literal string r"\newcommand" starts with a backslash.

This leaves the choice of how to provide literal strings to tikz_latex_preamble up to the user.

Adding escaping magic to tikz_latex_preamble would be an interface change and would break code that uses escapes on purpose. See example 1 below.

Example 1: Deliberatly use newlines as "\n":

\tikz_latex_preamble = "\\newcommand\\Foo{First line\n\nSecond line}"

Example 2: Use raw multiline strings:


\tikz_latex_preamble = r"""
\newcommand\Foo{
  First line

  Second line
}
"""
doyougnu commented 6 months ago

Yes I agree. I didn't realize python had raw multiline strings until I did some searching. Perhaps just a documentation update is needed then?