texlive[74882] Master/texmf-dist: robust-externalize (8apr25)
commits+karl at tug.org
commits+karl at tug.org
Tue Apr 8 22:13:54 CEST 2025
Revision: 74882
https://tug.org/svn/texlive?view=revision&revision=74882
Author: karl
Date: 2025-04-08 22:13:54 +0200 (Tue, 08 Apr 2025)
Log Message:
-----------
robust-externalize (8apr25)
Modified Paths:
--------------
trunk/Master/texmf-dist/doc/latex/robust-externalize/robust-externalize.pdf
trunk/Master/texmf-dist/doc/latex/robust-externalize/robust-externalize.tex
trunk/Master/texmf-dist/tex/latex/robust-externalize/robust-externalize.sty
Modified: trunk/Master/texmf-dist/doc/latex/robust-externalize/robust-externalize.pdf
===================================================================
(Binary files differ)
Modified: trunk/Master/texmf-dist/doc/latex/robust-externalize/robust-externalize.tex
===================================================================
--- trunk/Master/texmf-dist/doc/latex/robust-externalize/robust-externalize.tex 2025-04-08 20:13:44 UTC (rev 74881)
+++ trunk/Master/texmf-dist/doc/latex/robust-externalize/robust-externalize.tex 2025-04-08 20:13:54 UTC (rev 74882)
@@ -105,7 +105,7 @@
{\Large\bfseries Cache anything (\tikzname, tikz-cd, python…),\\in a robust, efficient and pure way.}
\vspace{1em}
- {Léo Colisson \quad Version 2.9}\\[3mm]
+ {Léo Colisson \quad Version 3.0}\\[3mm]
{\href{https://github.com/leo-colisson/robust-externalize}{\texttt{github.com/leo-colisson/robust-externalize}}}
\end{center}
@@ -147,15 +147,15 @@
Since v2.3 we also provide a way to include online pictures with |\includegraphicsWeb|:
\begin{codeexample}[width=0pt,vbox]
-Here is a picture of cat downloaded online: \includegraphicsWeb[width=3cm]{http://placekitten.com/400/300}.
+Here is a picture of bear downloaded online: \includegraphicsWeb[width=3cm]{https://placebear.com/400/300}.
\end{codeexample}
-You can also cache arbitrary code (e.g.\ python). You can also define arbitrary compilation commands, inclusion commands, and presets to fit you need. For instance, you can create a preset to obtain:
+You can also cache arbitrary code (e.g.\ python). Note that if you use a version before 2.7, you cannot indent the python code. You can also define arbitrary compilation commands, inclusion commands, and presets to fit you need. For instance, you can create a preset to obtain:
\begin{codeAndResult}
\begin{CacheMeCode}{python print code and result, set title={The for loop}}
-for name in ["Alice", "Bob"]:
- print(f"Hello {name}")
+ for name in ["Alice", "Bob"]:
+ print(f"Hello {name}")
\end{CacheMeCode}
\end{codeAndResult}
@@ -298,17 +298,17 @@
\end{codeexample}
(see that |CacheMe| can be used to cache arbitrary pictures)
-Or include images generated in python:
+Or include images generated in python, note that if you use a version before 2.7, you cannot indent the python code:
\begin{codeexample}[code only]
\begin{CacheMeCode}{python, set includegraphics options={width=.8\linewidth}}
-import matplotlib.pyplot as plt
-year = [2014, 2015, 2016, 2017, 2018, 2019]
-tutorial_count = [39, 117, 111, 110, 67, 29]
-plt.plot(year, tutorial_count, color="#6c3376", linewidth=3)
-plt.xlabel('Year')
-plt.ylabel('Number of futurestud.io Tutorials')
-plt.savefig("__ROBEXT_OUTPUT_PDF__")
+ import matplotlib.pyplot as plt
+ year = [2014, 2015, 2016, 2017, 2018, 2019]
+ tutorial_count = [39, 117, 111, 110, 67, 29]
+ plt.plot(year, tutorial_count, color="#6c3376", linewidth=3)
+ plt.xlabel('Year')
+ plt.ylabel('Number of futurestud.io Tutorials')
+ plt.savefig("__ROBEXT_OUTPUT_PDF__")
\end{CacheMeCode}
\end{codeexample}
@@ -592,7 +592,7 @@
\end{verbatim}
and you might also want to enable:
\begin{verbatim}
-\robExtConfigure{textstudio}
+\robExtConfigure{texstudio}
\end{verbatim}
to get clearer error messages as TeXstudio is \mylink{https://github.com/texstudio-org/texstudio/issues/1410}{fairly bad at parsing error messages} (see also |print whole file in error message|).
@@ -602,11 +602,11 @@
$pdflatex = 'pdflatex --shell-escape';
\end{verbatim}
-For other editors, \mylink{https://tex.stackexchange.com/questions/598818/how-can-i-enable-shell-escape}{this answer} describe pretty accurately the solutions you have depending on your software.
+For other editors, \mylink{https://tex.stackexchange.com/questions/598818/how-can-i-enable-shell-escape}{this answer} describes pretty accurately the solutions you have depending on your software.
\subsection{Caching a tikz picture}
-If you only care about \tikzname's picture, you have 3 options:
+If you only care about \tikzname's picture, you have 4 options:
\begin{enumerate}
\item Call once |\cacheTikz| that will redefine |tikzpicture| and |\tikz| to use our library (if you use this solution, make sure to read how to disable externalization (\cref{sec:disableExternalization}) as we do not support for instance |remember picture|). Then, configure the default preamble for cached files as explained below by extending the |tikz| or |tikzpicture| presets (that first loads |tikz|).
\item Use the generic commands that allows you to wrap an arbitrary environment:
@@ -618,7 +618,7 @@
|\cacheCommand{yourcommand}[O{default val}m]{your preset options}|\\
(|O{default val}m| means that the command accepts one optional argument with default value |default val| and one mandatory argument) but \textbf{we do recommend} to use |\cacheCommand| to cache |\tikz| since |\tikz| has a quite complicated parsing strategy (e.g.\ you can write |\tikz[options] \node{foo};| which has no mandatory argument enclosed in |{}|). |\cacheTikz| takes care of this already and caches both the environment |\begin{tikzpicture}| and the macro |\tikz|.
\item Use |tikzpictureC| instead of |tikzpicture| (this is mostly done to easily convert existing code to this library, but works only for |tikz| pictures).
-\item Use the more general |CacheMe| environment, that can cache \tikzname, \LaTeX{}, python, and much more.
+\item Use the more general |CacheMe| environment, or |CacheMeCode| if you use non-\LaTeX{} code\footnote{The later involves a more advanced parsing that allows it to contain special symbols like percent, but on the other hand it cannot be used inside macros and some environments directly. On the other hand, the former will remove some characters like percent (that are considered as comments) or newlines (which is harmless for \LaTeX{} code), but can be used anywhere.}, to cache \tikzname, \LaTeX{}, python and much more.
\end{enumerate}
These 4 options are illustrated below (note that all commands accept a first optional argument enclosed in |<...>| that contains the options to pass to |CacheMe| after loading the |tikzpicture| preset, that loads itself the |tikz| preset first):
@@ -1136,6 +1136,7 @@
The environment |CacheMeCode| can be used for this purpose.
\subsubsection{Python code}
+Note that if you use a version before 2.7, you cannot indent the python code (i.e. the first line must not start with any space).
\paragraph{Generate an image}
@@ -1144,27 +1145,27 @@
\begin{codeexample}[code only]
\begin{CacheMeCode}{python, set includegraphics options={width=.8\linewidth}}
-import matplotlib.pyplot as plt
-year = [2014, 2015, 2016, 2017, 2018, 2019]
-tutorial_count = [39, 117, 111, 110, 67, 29]
-plt.plot(year, tutorial_count, color="#6c3376", linewidth=3)
-plt.xlabel('Year')
-plt.ylabel('Number of futurestud.io Tutorials')
-plt.savefig("__ROBEXT_OUTPUT_PDF__")
+ import matplotlib.pyplot as plt
+ year = [2014, 2015, 2016, 2017, 2018, 2019]
+ tutorial_count = [39, 117, 111, 110, 67, 29]
+ plt.plot(year, tutorial_count, color="#6c3376", linewidth=3)
+ plt.xlabel('Year')
+ plt.ylabel('Number of futurestud.io Tutorials')
+ plt.savefig("__ROBEXT_OUTPUT_PDF__")
\end{CacheMeCode}
\end{codeexample}
-will produce the image visible in \cref{fig:pythonGeneratedImage}. \textbf{Importantly: you do not want to indent the content of CacheMeCode, or the space will also appear in the final code.}
+will produce the image visible in \cref{fig:pythonGeneratedImage}.
\begin{figure}
\centering
\begin{CacheMeCode}{python, set includegraphics options={width=.8\linewidth}}
-import matplotlib.pyplot as plt
-year = [2014, 2015, 2016, 2017, 2018, 2019]
-tutorial_count = [39, 117, 111, 110, 67, 29]
-plt.plot(year, tutorial_count, color="#6c3376", linewidth=3)
-plt.xlabel('Year')
-plt.ylabel('Number of futurestud.io Tutorials')
-plt.savefig("__ROBEXT_OUTPUT_PDF__")
+ import matplotlib.pyplot as plt
+ year = [2014, 2015, 2016, 2017, 2018, 2019]
+ tutorial_count = [39, 117, 111, 110, 67, 29]
+ plt.plot(year, tutorial_count, color="#6c3376", linewidth=3)
+ plt.xlabel('Year')
+ plt.ylabel('Number of futurestud.io Tutorials')
+ plt.savefig("__ROBEXT_OUTPUT_PDF__")
\end{CacheMeCode}
\caption{Image generated with python.}
\label{fig:pythonGeneratedImage}
@@ -1177,8 +1178,8 @@
For instance:
\begin{codeAndResult}
\begin{CacheMeCode}{python, do not include pdf}
-import math
-write_to_out(r"\gdef\cosComputedInPython{" + str(math.cos(1)) + r"}")
+ import math
+ write_to_out(r"\gdef\cosComputedInPython{" + str(math.cos(1)) + r"}")
\end{CacheMeCode}
$\rightarrow$ The cosinus of 1 is \cosComputedInPython.
@@ -1330,7 +1331,8 @@
breakindent=.5\textwidth,
frame=single,
breaklines=true,
- style=mypython]{\robExtAddCachePathAndName{\robExtFinalHash-code.txt}}
+ % On old distribution, this is called mypython
+ style=pythonhighlight-style]{\robExtAddCachePathAndName{\robExtFinalHash-code.txt}}
Output:
\verbatiminput{\robExtAddCachePathAndName{\robExtFinalHash-print.txt}}
\end{tcolorbox}
@@ -1397,7 +1399,7 @@
\evalPlaceholder{
\begin{tcolorbox}[colback=red!5!white,colframe=red!75!black,title=__MY_TITLE__]
% \noindent Code:
- \lstinputlisting[frame=single,breakindent=.5\textwidth,frame=single,breaklines=true,style=mypython]{\robExtAddCachePathAndName{\robExtFinalHash-code.txt}}
+ \lstinputlisting[frame=single,breakindent=.5\textwidth,frame=single,breaklines=true,style=pythonhighlight-style]{\robExtAddCachePathAndName{\robExtFinalHash-code.txt}}
Output:
\verbatiminput{\robExtAddCachePathAndName{\robExtFinalHash-print.txt}}
\end{tcolorbox}
@@ -2097,8 +2099,8 @@
Since |remove leading spaces if not disabled| is enabled by default on python, it is therefore fine to indent your python code like in:
\begin{codeAndResult}
\begin{CacheMeCode}{python print code and result}
- for i in range(3):
- print(f"Hello {i}")
+ for i in range(3):
+ print(f"Hello {i}")
\end{CacheMeCode}
\end{codeAndResult}
\end{pgfmanualentry}
@@ -2185,19 +2187,19 @@
|\lenToCm{.75\linewidth}| will output the length of |.75\linewidth| in cm by default, like |10cm|. If you want to change the unit, you can use the optional argument to specify a different unit (like |in| for inches, see |\dim_to_decimal_in_unit| for details). You can remove the unit at the end using |\lenToCmNoUnit{.75\linewidth}| (needs v2.6) to obtain something like |10| instead of |10cm|. These commands are helpful with |set placeholder eval| to send length from \LaTeX{} to your environment:
\begin{codeAndResult}
\begin{CacheMeCode}{python, set placeholder eval={__LINEWIDTH__}{\lenToCmNoUnit[in]{.75\linewidth}}}
-import matplotlib.pyplot as plt
-import matplotlib
-from matplotlib.pyplot import figure
-figure(figsize=(__LINEWIDTH__, __LINEWIDTH__))
-year = [2014, 2015, 2016, 2017, 2018, 2019]
-tutorial_count = [39, 117, 111, 110, 67, 29]
-plt.plot(year, tutorial_count, color="#6c3376", linewidth=2)
-plt.title("Simple plot")
-plt.xlabel('Year')
-plt.ylabel('Number of futurestud.io Tutorials')
-print(get_filename_from_extension(".pgf"))
-# https://stackoverflow.com/a/52587591/4987648
-plt.savefig("__ROBEXT_OUTPUT_PDF__", bbox_inches="tight")
+ import matplotlib.pyplot as plt
+ import matplotlib
+ from matplotlib.pyplot import figure
+ figure(figsize=(__LINEWIDTH__, __LINEWIDTH__))
+ year = [2014, 2015, 2016, 2017, 2018, 2019]
+ tutorial_count = [39, 117, 111, 110, 67, 29]
+ plt.plot(year, tutorial_count, color="#6c3376", linewidth=2)
+ plt.title("Simple plot")
+ plt.xlabel('Year')
+ plt.ylabel('Number of futurestud.io Tutorials')
+ print(get_filename_from_extension(".pgf"))
+ # https://stackoverflow.com/a/52587591/4987648
+ plt.savefig("__ROBEXT_OUTPUT_PDF__", bbox_inches="tight")
\end{CacheMeCode}
\end{codeAndResult}
\end{pgfmanualentry}
@@ -3069,9 +3071,9 @@
set placeholder eval={__thepage__}{\thepage},
%% We disable externalization
disable externalization}
-with open("__ROBEXT_OUTPUT_PREFIX__-out.txt", "w") as f:
- for i in range(5):
- f.write(f"Hello {i}, we are on page __thepage__\n")
+ with open("__ROBEXT_OUTPUT_PREFIX__-out.txt", "w") as f:
+ for i in range(5):
+ f.write(f"Hello {i}, we are on page __thepage__\n")
\end{CacheMeCode}
\end{codeAndResult}
@@ -3241,9 +3243,11 @@
\def\extrakeytext{style, }
\extractkey/robExt/forward counter=counter to forward\@nil
\extractkey/robExt/forward counter force value=\marg{counter to forward}\marg{value of counter}\@nil
+ \extractkey/robExt/forward counter back=counter to forward\@nil
+ \extractkey/robExt/forward counter and back=counter to forward\@nil
\makeatother%
\pgfmanualbody
- Forward a counter:
+ |forward counter| forwards a counter to the cached picture:
\begin{codeexample}[width=0pt]
\begin{tikzpictureC}<forward counter=page>
\node[rounded corners, fill=red]{The current page is \thepage.};
@@ -3255,6 +3259,17 @@
\node[rounded corners, fill=red]{The current page is \thepage.};
\end{tikzpictureC}
\end{codeexample}
+|forward counter back| does the opposite, it gets the value of the counter at the end of the cached file, and redefines it in the main file, while |forward counter and back| does both |forward counter| and |forward counter back|:
+\begin{codeexample}[width=0pt,vbox]
+\newcounter{mycounter}
+\setcounter{mycounter}{12}
+\begin{CacheMe}{latex, forward counter and back=mycounter}
+ Inside the cached picture, the value of mycounter is \the\value{mycounter}.
+ Let's change it to 42! \setcounter{mycounter}{42}
+\end{CacheMe}\\
+Outside the cached picture, the new value of the counter is \the\value{mycounter}.
+\end{codeexample}
+
\end{pgfmanualentry}
\begin{pgfmanualentry}
@@ -3533,6 +3548,7 @@
\makeatletter%
\def\extrakeytext{style, }
\extractkey/robExt/if matches=\marg{string}\marg{style to apply}\@nil
+ \extractkey/robExt/if matches else=\marg{string}\marg{style to apply if match}\marg{style to apply if no match}\@nil
\extractkey/robExt/if matches word=\marg{string}\marg{style to apply}\@nil
\extractkey/robExt/if matches regex=\marg{latex3 regex}\marg{style to apply}\@nil
\extractkey/robExt/register word with namespace=\marg{namespace}\marg{word}\marg{style}\@nil
@@ -3798,9 +3814,9 @@
i.e.\ instead of printing the pdf we print the content of the file |__ROBEXT_OUTPUT_PREFIX__-out.txt| using the command in |__ROBEXT_VERBATIM_COMMAND__|, that defaults to |\verbatiminput|:
\begin{codeAndResult}
\begin{CacheMeCode}{python, verbatim output}
-with open("__ROBEXT_OUTPUT_PREFIX__-out.txt", "w") as f:
- for i in range(5):
- f.write(f"Hello {i}\n")
+ with open("__ROBEXT_OUTPUT_PREFIX__-out.txt", "w") as f:
+ for i in range(5):
+ f.write(f"Hello {i}\n")
\end{CacheMeCode}
\end{codeAndResult}
\end{pgfmanualentry}
@@ -3859,10 +3875,24 @@
\extractkey/robExt/latex/use latexmk\@nil
\extractkey/robExt/latex/use lualatex\@nil
\extractkey/robExt/latex/use xelatex\@nil
+ \extractkey/robExt/latex/use latex and dvi\@nil
+ \extractkey/robExt/dvi to ps\@nil
\makeatother%
\pgfmanualbody
- Use latexmk/lualatex/xelatex to compile. It is a shortcut for:\\
- |set placeholder={__ROBEXT_LATEX_ENGINE__}{yourfavoriteengine}|
+ The first three styles use latexmk/lualatex/xelatex to compile. It is a shortcut for:\\
+ |set placeholder={__ROBEXT_LATEX_ENGINE__}{yourfavoriteengine}|\\
+ |dvi to ps| is called internally in |use latex and dvi|, converts the |.dvi| file into a |.ps| file, and renames it into |.pdf| so that it is automatically included. |use latex and dvi| sets the compiler to |latex|, producing |dvi| files, and call |dvi to ps| to convert them to ps files that can be included. So if your workflow involves dvi, you can call directly:
+ \begin{codeexample}[code only]
+ \robExtConfigure{
+ add to preset={latex}{use latex and dvi},
+ }
+ \end{codeexample}
+ and you can then compile your file as usual with:
+\begin{verbatim}
+# --shell-escape only needed the first time you compile, see above for alternatives
+$ latex --shell-escape yourfile.tex
+$ dvips yourfile.dvi
+\end{verbatim}
\end{pgfmanualentry}
\begin{pgfmanualentry}
@@ -3961,9 +3991,9 @@
We demonstrate its usage on a few examples:
\begin{codeAndResult}
\begin{CacheMeCode}{python, verbatim output}
-with open(get_verbatim_output(), "w") as f:
- for i in range(5):
- f.write(f"Hello {i}\n")
+ with open(get_verbatim_output(), "w") as f:
+ for i in range(5):
+ f.write(f"Hello {i}\n")
\end{CacheMeCode}
\end{codeAndResult}
@@ -3972,25 +4002,25 @@
You can also generate some images. This code will produce the image in \cref{fig:pythonGeneratedImage2}:
\begin{codeexample}[code only]
\begin{CacheMeCode}{python, set includegraphics options={width=.8\linewidth}}
-import matplotlib.pyplot as plt
-year = [2014, 2015, 2016, 2017, 2018, 2019]
-tutorial_count = [39, 117, 111, 110, 67, 29]
-plt.plot(year, tutorial_count, color="#6c3376", linewidth=3)
-plt.xlabel('Year')
-plt.ylabel('Number of futurestud.io Tutorials')
-plt.savefig("__ROBEXT_OUTPUT_PDF__")
+ import matplotlib.pyplot as plt
+ year = [2014, 2015, 2016, 2017, 2018, 2019]
+ tutorial_count = [39, 117, 111, 110, 67, 29]
+ plt.plot(year, tutorial_count, color="#6c3376", linewidth=3)
+ plt.xlabel('Year')
+ plt.ylabel('Number of futurestud.io Tutorials')
+ plt.savefig("__ROBEXT_OUTPUT_PDF__")
\end{CacheMeCode}
\end{codeexample}
{\begin{figure}
\centering
\begin{CacheMeCode}{python, set includegraphics options={width=.8\linewidth}}
-import matplotlib.pyplot as plt
-year = [2014, 2015, 2016, 2017, 2018, 2019]
-tutorial_count = [39, 117, 111, 110, 67, 29]
-plt.plot(year, tutorial_count, color="#6c3376", linewidth=3)
-plt.xlabel('Year')
-plt.ylabel('Number of futurestud.io Tutorials')
-plt.savefig("__ROBEXT_OUTPUT_PDF__")
+ import matplotlib.pyplot as plt
+ year = [2014, 2015, 2016, 2017, 2018, 2019]
+ tutorial_count = [39, 117, 111, 110, 67, 29]
+ plt.plot(year, tutorial_count, color="#6c3376", linewidth=3)
+ plt.xlabel('Year')
+ plt.ylabel('Number of futurestud.io Tutorials')
+ plt.savefig("__ROBEXT_OUTPUT_PDF__")
\end{CacheMeCode}
\caption{Image generated with python.}
\label{fig:pythonGeneratedImage2}
@@ -4014,8 +4044,8 @@
(Warning: in 2.7 a bug was introduced (fixed in v2.8) where an empty line was automatically added in front of the code) This is a demo style that can print a python code and its result.
\begin{codeAndResult}
\begin{CacheMeCode}{python print code and result, set title={The for loop}}
-for name in ["Alice", "Bob"]:
- print(f"Hello {name}")
+ for name in ["Alice", "Bob"]:
+ print(f"Hello {name}")
\end{CacheMeCode}
\end{codeAndResult}
You can set |__ROBEXT_PYTHON_TCOLORBOX_PROPS__| the options of the tcolorbox,\\ |__ROBEXT_PYTHON_CODE_MESSAGE__| and |__ROBEXT_PYTHON_RESULT_MESSAGE__| which are displayed before the corresponding block, |__ROBEXT_PYTHON_LSTINPUT_STYLE__| which contains the default lstinput style and |__MY_TITLE__| (cf |set title|) that contains the title of the box. Make sure to have the following packages to use the default styling:
@@ -4110,7 +4140,7 @@
\pgfmanualbody
|\includegraphicsWeb| is used in place of |\includegraphics| to download online images:
\begin{codeexample}[width=0pt,vbox]
-Here is a cat downloaded online: \includegraphicsWeb[width=3cm]{http://placekitten.com/400/300}.
+Here is a bear downloaded online: \includegraphicsWeb[width=3cm]{https://placebear.com/400/300}.
\end{codeexample}
By default, the engine uses |wget| in linux (since it is installed by default) and |curl| on windows (should also be installed by default on recent windows). But you can change it by modifying the |web image| preset (or using the optional robExt options that are loaded after |web image|):
\begin{codeexample}[width=0pt,vbox]
@@ -4120,7 +4150,7 @@
},
}
-Here is a cat downloaded online: \includegraphicsWeb[width=3cm]{http://placekitten.com/400/303}.
+Here is a bear downloaded online: \includegraphicsWeb[width=3cm]{https://placebear.com/400/303}.
\end{codeexample}
By default, the |wget| and |curl| presets are defined as:
\begin{verbatim}
@@ -4932,11 +4962,20 @@
\section{Changelog}
\begin{itemize}
+\item v3.0:
+ \begin{itemize}
+ \item Added |dvi to ps| and |use latex and dvi|
+ \item Fix bug with |no cache folder|
+ \item Fix bug with |add to compilation command options| not working
+ \item Added |forward counter and back| and |forward counter back|
+ \item Fix infinite compilation time when input does not exist (WARNING:, it will invalidate all previously cached LaTeX documents since this adds a compilation option to LaTeX by default! If you want to use the old buggy behavior to preserve your cache, define |\robExtSetPlaceholder{__ROBEXT_LATEX_COMPILATION_COMMAND_OPTIONS__}{-halt-on-error}|. Anyway, if you want truly reproducible caching, just copy the .sty file in your project)
+ \item Support |if matches else|
+ \end{itemize}
\item v2.9 (2024/03/15)
\begin{itemize}
\item Bug that may forbid the package to load has been fixed (you might encounter |ERROR: Missing = inserted for \ifnum|)
\end{itemize}
-\item v2.8 (current master, not yet released):
+\item v2.8:
\begin{itemize}
\item Fix a regression bug introduced in 2.7: |python print code and result| was adding a new line above the code printed. This is fixed now.
\end{itemize}
@@ -5021,4 +5060,4 @@
% TeX-command-extra-options: "--shell-escape -halt-on-error"
% TeX-engine: luatex
% jinx-languages: "en"
-% End:
\ No newline at end of file
+% End:
Modified: trunk/Master/texmf-dist/tex/latex/robust-externalize/robust-externalize.sty
===================================================================
--- trunk/Master/texmf-dist/tex/latex/robust-externalize/robust-externalize.sty 2025-04-08 20:13:44 UTC (rev 74881)
+++ trunk/Master/texmf-dist/tex/latex/robust-externalize/robust-externalize.sty 2025-04-08 20:13:54 UTC (rev 74882)
@@ -1,4 +1,4 @@
-\ProvidesPackage{robust-externalize}[2024/03/15 v2.9 Cache anything (tikz, latex, python) in a robust, efficient and pure way.]
+\ProvidesPackage{robust-externalize}[2025/04/08 v3.0 Cache anything (tikz, latex, python) in a robust, efficient and pure way.]
% todo:
% change order argument replace from list, it is hard to read this way
@@ -33,6 +33,11 @@
\setjobnameNoQuotes
\ExplSyntaxOn
+% in pgf it is not possible to use explsyntax
+\NewDocumentCommand{\robExtMsgError}{m}{%
+ \msg_error:nn{robExt}{#1}
+}
+
\NewDocumentCommand{\robExtIfWindowsTF}{mm}{%
\sys_if_platform_windows:TF {#1}{#2}%
}
@@ -240,6 +245,7 @@
\msg_new:nnn {robExt}{missing compiled pdf parallel with log}{The~compilation~of~the~code~block~at~line~#2~failed:~the~following ~ file~is~indeed~missing: ~ #1.pdf. ~ The ~ compilation ~ command ~ "#3"~used~to~compile~the~environment~on~line~#2~ certainly ~ failed~with~errors:^^Jvvvvvv^^J\l__robExt_tmp_str^^J\string^\string^\string^\string^\string^\string^ ^^JSee~full~logs~#4~ or ~ in ~ #1-compilation.log.}
\msg_new:nnn {robExt}{remove spaces until non spaces characters}{The~placeholder~#1~contains~characters~other~than~spaces~(#2)~before~the~separator~#3.}
+\msg_new:nnn {robExt}{need to load pythonhighlight}{You~need~to~load~the~package~pythonhighlight~to~use~the~lstlisting~style~pythonhighlight-style.}
\msg_new:nnn {robExt}{auto forward not in cachemecode}{Auto~forward~is~less~efficient~in~cacheMeCode.}
@@ -251,7 +257,6 @@
\msg_new:nnn {robExt}{enabled parallel no shell escape}{Warning:~you~enabled~parallel~compilation~but~shell-escape~is~disabled.}
\msg_new:nnn {robExt}{rerun because parallel}{Warning:~Compiling~all~missing~figures~in~parallel~with~"#1".~You~need~to~rerun~LaTeX~to~include~them.}
\msg_new:nnn {robExt}{gpgetvar recompilation needed}{Warning:~you~need~to~recompile~as~the~gpgetvar~variable~"#1"~does~not~exist~yet.}
-\msg_new:nnn {robExt}{warning res not defined}{Warning:~the~result~\\res{#1}~is~not~defined,~maybe~try~to~recompile..}
% dummy placeholders added if image is not present
\def\robExtImagePlaceholderIfManualMode{
@@ -506,20 +511,22 @@
}
\NewDocumentCommand{\robExtCheckIfPrefixFolderExists}{}{
- % Check if the output directory exists
+ % Check if the output directory exists and is not empty
\ifdefined\robExtCacheFolder
- \bool_if:nTF { \sys_if_shell_unrestricted_p: || \cs_if_exist_p:N \robExtForceCompilation}
- {
- \ifdefined\robExtDoNotMkdirFolder\else
- \ifdefined\robExtManualMode
- \message{If ~ you ~ get~ an~ error,~ make ~ sure ~ to ~ enable ~ pdflatex ~ -shell-escape ~ or ~ to ~ MANUALLY ~ CREATE ~ THE ~ FOLDER ~ \robExtCacheFolder.}
- \else
- \sys_shell_now:x {\robExtPrefixAllCompilationCommands mkdir ~ -p ~ \robExtCacheFolder}
+ \ifx\robExtCacheFolder\empty\else
+ \bool_if:nTF { \sys_if_shell_unrestricted_p: || \cs_if_exist_p:N \robExtForceCompilation}
+ {
+ \ifdefined\robExtDoNotMkdirFolder\else
+ \ifdefined\robExtManualMode
+ \message{If ~ you ~ get~ an~ error,~ make ~ sure ~ to ~ enable ~ pdflatex ~ -shell-escape ~ or ~ to ~ MANUALLY ~ CREATE ~ THE ~ FOLDER ~ \robExtCacheFolder.}
+ \else
+ \sys_shell_now:x {\robExtPrefixAllCompilationCommands mkdir ~ -p ~ \robExtCacheFolder}
+ \fi
\fi
- \fi
- }{
- \message{Warning: If ~ you ~ get~ an~ error,~ make ~ sure ~ to ~ enable ~ pdflatex ~ -shell-escape ~ or ~ to ~ manually ~ CREATE ~ THE ~ FOLDER ~ \robExtCacheFolder.}
- }
+ }{
+ \message{Warning: If ~ you ~ get~ an~ error,~ make ~ sure ~ to ~ enable ~ pdflatex ~ -shell-escape ~ or ~ to ~ manually ~ CREATE ~ THE ~ FOLDER ~ \robExtCacheFolder.}
+ }
+ \fi
\fi
}
@@ -1881,7 +1888,9 @@
\str_replace_all:Nnx \l__robExt_current_compilation_command_str {__ROBEXT_CACHE_FOLDER__}{\robExtCacheFolder}
% Make sure this command is run from the cache folder
\ifdefined\robExtCacheFolder
- \str_put_left:Nx \l__robExt_current_compilation_command_str {cd ~ \robExtCacheFolder \space && ~ }
+ \ifx\robExtCacheFolder\empty\else
+ \str_put_left:Nx \l__robExt_current_compilation_command_str {cd ~ \robExtCacheFolder \space && ~ }
+ \fi
\fi%
%%% We enable manual mode if we enabled "compile in parallel after=N" and we compiled more than N elements
% Check if we want to run stuff in parallel
@@ -2370,10 +2379,15 @@
\regex_match:nVTF {#1} \l__robExt_placeholder___ROBEXT_MAIN_CONTENT_ORIG___str {\pgfkeysalso{#2}} {}
}
-\NewDocumentCommand{\robExtIfMatchesString}{mm}{
- \str_if_in:NnTF \l__robExt_placeholder___ROBEXT_MAIN_CONTENT_ORIG___str {#1} {\pgfkeysalso{#2}} {}
+\NewDocumentCommand{\robExtIfMatchesString}{mmm}{
+ \str_if_in:NnTF \l__robExt_placeholder___ROBEXT_MAIN_CONTENT_ORIG___str {#1} {\pgfkeysalso{#2}} {\pgfkeysalso{#3}}
}
+\NewDocumentCommand{\robExtIfPlaceholderMatchesString}{mmmm}{
+ \str_if_in:cnTF {l__robExt_placeholder_#1_str} {#2} {#3} {#4}
+}
+\let\ifPlaceholderMatchesString\robExtIfPlaceholderMatchesString
+
% \robExtRegisterWord {namespace} {word} {style}
\NewDocumentCommand{\robExtRegisterWord}{mmm}{
% \robExt_register_match_word:nnn {#1} {#2} {\pgfkeysalso{#3}}
@@ -2809,9 +2823,6 @@
recompile/.code={\def\robExtForceRecompilation{}},
do not recompile/.code={\let\robExtForceRecompilation\undefined},
set compilation command/.code={\robExtSetCompilationCommand{#1}},
- change source extension/.style={
- add before placeholder/.expanded={__ROBEXT_COMPILATION_COMMAND__}{\robExtCp\space "__ROBEXT_SOURCE_FILE__" "__ROBEXT_OUTPUT_PREFIX__.#1" &&},
- },
% like "set compilation command" but moves the "__ROBEXT_OUTPUT_PDF__-tmp" to "__ROBEXT_OUTPUT_PDF__" if
% there is no error.
set compilation command move if no error/.style={
@@ -2911,7 +2922,7 @@
% synonyme, "cache folder" is prefered over ""
set subfolder and way back/.code 2 args={\def\robExtCacheFolder{#1}\def\robExtCacheFolderWayBack{#2}},
set cache folder and way back/.code 2 args={\def\robExtCacheFolder{#1}\def\robExtCacheFolderWayBack{#2}},
- no cache folder/.code={\let\robExtCacheFolder\undefined\def\robExtCacheFolderWayBack{}},
+ no cache folder/.style={set cache folder and way back={}{}},
% By default we put everything in robustExternalize
% Change this before starting to cache any library, and if you change it mid-document, be aware
% that you will not be able to refer to elements in the old folder.
@@ -3034,6 +3045,17 @@
}%
},%
},%
+ forward counter back/.style={
+ add to preamble={
+ \AtEndDocument{% Execute at end of file: we propagate back the value of the counter
+ \immediate\write\writeRobExt{\string\setcounter{#1}{\the\value{#1}}}%
+ }
+ },
+ },
+ forward counter and back/.style={
+ forward counter=#1,
+ forward counter back=#1,
+ },
forward color/.style={
run command if externalization={%
\extractcolorspecs{#1}{\zx at tmp@model}{\zx at tmp@cmd}%
@@ -3069,7 +3091,8 @@
auto forward words/.style={auto forward words namespace={}},
%% This will
if matches regex/.code 2 args={\robExtIfMatchesRegex{#1}{#2}},
- if matches/.code 2 args={\robExtIfMatchesString{#1}{#2}},
+ if matches/.code 2 args={\robExtIfMatchesString{#1}{#2}{}},
+ if matches else/.code n args={3}{\robExtIfMatchesString{#1}{#2}{#3}},
if matches word/.code 2 args={%
\robExtRegisterWord{}{#1}{#2}%
% If ran inside a preset, we want to enable it, otherwise we enable it on the latex preset
@@ -3268,39 +3291,7 @@
}
\let\cacheMe\robExtCacheMe
-\ExplSyntaxOn
%% #1: Arguments, #2: content to externalize
-\str_new:N \__robExt_tmp_contain_code_str
-\tl_new:N \__robExt_tmp_contain_code_tl
-\NewDocumentCommand{\robExtCacheMeCode}{O{}+v}{%
- {% Group
- %% We store the input in a non-string element for efficiently implementing "auto forward"
- \edef\robExtUserInputCacheMe{\unexpanded{#2}}%
- \tl_set:Nn \__robExt_tmp_contain_code_tl {#2}
- \tl_replace_all:Nen \__robExt_tmp_contain_code_tl {\char_generate:nn{13}{12}} {^^J}
- \str_set:Ne \__robExt_tmp_contain_code_str {\tl_to_str:e {\__robExt_tmp_contain_code_tl}}
- %\str_show:N \__robExt_tmp_contain_code_str
- %\tl_replace_all:Nnn \__robExt_tmp_contain_code_str {^^M} {b}
- %\str_show:N \__robExt_tmp_contain_code_str
- \pgfkeys{%
- /robExt/.cd,%
- %% This is needed notably if the cached elements are nested, like the include command uses itself a tikz
- %% picture etc cached via \cacheTikz... It it hard to reset everything efficiently (like we might not
- %% want to reset all compilation commands etc), so you can add here stuff that might need to be restored
- %% later.
- reset,
- /utils/exec={\robExtPlaceholderFromString{__ROBEXT_MAIN_CONTENT_ORIG__}{\__robExt_tmp_contain_code_str}},
- default~style,%
- defaultPlaceholderFromCodeStyle,
- #1,
- }%
- \robExtEvaluateCompileAndInclude%
- }%
-}
-\let\cacheMeCode\robExtCacheMeCode
-\ExplSyntaxOff
-
-%% #1: Arguments, #2: content to externalize
\NewDocumentEnvironment{RobExtCacheMe}{m+b}{%
\robExtCacheMe[#1]{#2}%
}{}
@@ -3497,7 +3488,7 @@
%% Compilation commands
\robExtSetPlaceholder{__ROBEXT_LATEX_COMPILATION_COMMAND__}{__ROBEXT_LATEX_ENGINE__ __ROBEXT_LATEX_COMPILATION_COMMAND_OPTIONS__ "__ROBEXT_SOURCE_FILE__"}
-\robExtSetPlaceholder{__ROBEXT_LATEX_COMPILATION_COMMAND_OPTIONS__}{-halt-on-error}
+\robExtSetPlaceholder{__ROBEXT_LATEX_COMPILATION_COMMAND_OPTIONS__}{-halt-on-error -interaction=nonstopmode}
\robExtSetPlaceholder{__ROBEXT_LATEX_ENGINE__}{pdflatex}
\robExtConfigure{
@@ -3511,6 +3502,9 @@
only placeholders in compilation command={__ROBEXT_LATEX_COMPILATION_COMMAND__,__ROBEXT_LATEX_ENGINE__,__ROBEXT_LATEX_COMPILATION_COMMAND_OPTIONS__},
only placeholders in template={__ROBEXT_LATEX__,__ROBEXT_LATEX_OPTIONS__,__ROBEXT_LATEX_DOCUMENT_CLASS__,__ROBEXT_LATEX_PREAMBLE__,__ROBEXT_LATEX_PREAMBLE_HYPERREF__,__ROBEXT_LATEX_PREAMBLE_AFTER_HYPERREF__,__ROBEXT_LATEX_MAIN_CONTENT_WRAPPED__,__ROBEXT_LATEX_TRIM_LENGTH__,__ROBEXT_LATEX_CREATE_OUT_FILE__,__ROBEXT_LATEX_WRITE_DEPTH_TO_OUT_FILE__,__ROBEXT_MAIN_CONTENT__,__ROBEXT_MAIN_CONTENT_ORIG__},
},
+ dvi to ps/.style={
+ add to placeholder/.expanded={__ROBEXT_COMPILATION_COMMAND__}{ && dvips "__ROBEXT_OUTPUT_PREFIX__.dvi" && \robExtMv\space"__ROBEXT_OUTPUT_PREFIX__.ps" "__ROBEXT_OUTPUT_PDF__"},
+ },
% some useful presets
latex/.style={
enable placeholders,
@@ -3524,11 +3518,15 @@
set compilation command={__ROBEXT_LATEX_COMPILATION_COMMAND__},
%% Configure the latex compilation engine
add to compilation command options/.style={
- add to placeholder={__ROBEXT_LATEX_COMPILATION_COMMAND_OPTIONS__}{#1},
+ add to placeholder={__ROBEXT_LATEX_COMPILATION_COMMAND_OPTIONS__}{##1},
},
use latexmk/.style={
set placeholder={__ROBEXT_LATEX_ENGINE__}{latexmk},
},
+ use latex and dvi/.style={
+ set placeholder={__ROBEXT_LATEX_ENGINE__}{latex},
+ dvi to ps,
+ },
use lualatex/.style={
set placeholder={__ROBEXT_LATEX_ENGINE__}{lualatex},
},
@@ -3755,7 +3753,7 @@
\robExtSetPlaceholder{__ROBEXT_PYTHON_TCOLORBOX_PROPS__}{colback=red!5!white,colframe=red!75!black}
\robExtSetPlaceholder{__ROBEXT_PYTHON_CODE_MESSAGE__}{}
\robExtSetPlaceholder{__ROBEXT_PYTHON_RESULT_MESSAGE__}{Output:}
-\robExtSetPlaceholder{__ROBEXT_PYTHON_LSTINPUT_STYLE__}{frame=single, breakindent=.5\textwidth, frame=single, breaklines=true, style=mypython}
+\robExtSetPlaceholder{__ROBEXT_PYTHON_LSTINPUT_STYLE__}{breakindent=.5\textwidth, frame=single, breaklines=true, style=pythonhighlight-style}
\robExtConfigure{
python print code and result/.style={
@@ -3769,6 +3767,17 @@
set title={Python code},
custom include command={
% Useful to replace __MY_TITLE__:
+ %% To print a nicer error message, we check if the user uses the default style
+ \robExtIfPlaceholderMatchesString{__ROBEXT_PYTHON_LSTINPUT_STYLE__}{pythonhighlight-style}{%
+ \ifcsname lststy at pythonhighlight-style$\endcsname%
+ \else%
+ \ifcsname lststy at mypython$\endcsname%
+ \lstdefinestyle{pythonhighlight-style}{style=mypython}%
+ \else%
+ \robExtMsgError{needtoloadpythonhighlight}%
+ \fi%
+ \fi%
+ }{}%
\evalPlaceholder{
\begin{tcolorbox}[title=__MY_TITLE__,__ROBEXT_PYTHON_TCOLORBOX_PROPS__]
__ROBEXT_PYTHON_CODE_MESSAGE__%
@@ -3784,140 +3793,8 @@
\robExtCopyGroupPlaceholders{python print code result}{main}
\robExtRegisterGroupPlaceholders{python print code result}
-%%%%%%
-%%%%%% Group "python exec"
-%%%%%%
-\robExtClearGroupPlaceholders{main}
-
-% if we set res = XXX or res[""] = XXX it will print it by default, if we do instead res[42] = XXX
-% it will not load it. To make this easy to program, we create an element for the empty string,
-% this way we can always \res{} in the include command.
-
-\begin{RobExtPlaceholderFromCode}[remove spaces until=>]{__ROBEXT_PYTHON_EXEC_TEMPLATE__}
- > def toMacro(x):
- > if hasattr(x, '__toMacro'):
- > return x.__toMacro()
- > else:
- > return str(x)
- >
- > __ROBEXT_PYTHON_EXEC_LIBRARY_CHANGES__
- > __ROBEXT_PYTHON_EXEC_CUSTOM_TO_MACRO_DEF__
- > # We create a special class extending dict to check if the user just did res[42] = xxx
- > # or res = foo, as in that case we will just print the result directly.
- > class DictToExportToMacros(dict):
- > pass
- > res = DictToExportToMacros()
- > res[""] = ""
- > __ROBEXT_PYTHON_EXEC_RES_EQUALITY____ROBEXT_MAIN_CONTENT_ORIG__
- > if not isinstance(res, DictToExportToMacros):
- > write_to_out(r"\gdef\robExtResMacro{" + toMacro(res) + r"}")
- > else:
- > for k in res:
- > # We create a macro with csname to allow numbers in the name etc and parallel compilation
- > write_to_out(r"\expandafter\gdef\csname robExtResMacro" + str(k) + r"\endcsname{" + toMacro(res[k]) + r"}")
-\end{RobExtPlaceholderFromCode}
-
-\robExtSetPlaceholder{__ROBEXT_PYTHON_EXEC_RES_EQUALITY__}{}
-\robExtSetPlaceholder{__ROBEXT_PYTHON_EXEC_LIBRARY_CHANGES__}{}
-\begin{RobExtPlaceholderFromCode}{__ROBEXT_PYTHON_EXEC_CUSTOM_TO_MACRO_DEF__}
-\end{RobExtPlaceholderFromCode}
-
-% Some macro always expect a number to work, and must be expandable
-\NewDocumentCommand{\robExtResForceNumber}{m}{%
- \ifcsname robExtResMacro#1\endcsname%
- \csname robExtResMacro#1\endcsname%
- \else%
- 404%
- \fi%
-}
-
-\def\robExtNoResult#1{%
- \textbf{??}%
-}
-\NewDocumentCommand{\robExtRes}{m}{%
- \ifcsname robExtResMacro#1\endcsname%
- \csname robExtResMacro#1\endcsname%
- \else%
- \msg_warning:nnx{robExt}{warning res not defined}{#1}%%
- \robExtNoRes{#1}%
- \fi%
-}
-\let\res\robExtRes
-
-\robExtConfigure{
- new preset={python exec res}{
- python,
- % we do that so that custom include command does not pick the result from previous runs locally
- /utils/exec={\let\robExtResMacro\undefined},
- import placeholders={__ROBEXT_PYTHON_EXEC_CUSTOM_TO_MACRO_DEF__,__ROBEXT_PYTHON_EXEC_RES_EQUALITY__,__ROBEXT_PYTHON_EXEC_TEMPLATE__,__ROBEXT_PYTHON_EXEC_LIBRARY_CHANGES__},
- custom include command={\robExtRes{}},
- set placeholder={__ROBEXT_MAIN_CONTENT__}{__ROBEXT_PYTHON_EXEC_TEMPLATE__},
- },
- new preset={python exec}{
- python exec res,
- set placeholder={__ROBEXT_PYTHON_EXEC_RES_EQUALITY__}{res =},
- },
-}
-
-\robExtCopyGroupPlaceholders{python exec}{main}
-\robExtRegisterGroupPlaceholders{python exec}
-
%%%%%%
-%%%%%% Group "sage" and "sage res"
-%%%%%%
-
-\robExtClearGroupPlaceholders{main}
-
-\robExtSetPlaceholder{__ROBEXT_SAGE_EXEC__}{sage}
-
-% We need to overwrite some definitions as it adds a .sage in front automatically by default
-\begin{RobExtPlaceholderFromCode}[]{__ROBEXT_SAGE_EXEC_LIBRARY_CHANGES__}
-def get_filename_from_extension(extension):
- '''
- If you want to create a file with extension 'extension' (with the appropriate base name), this command
- is for you. For instance get_filename_from_extension(".mp4") would return something like
- robExt-somehash.mp4
- the extension can also be like get_filename_from_extension("-out.tex") etc.
- '''
- return os.path.join(get_cache_folder(), "__ROBEXT_OUTPUT_PREFIX__" + extension)
-
-ROBEXT_PLOT_SAVE_FORMAT = "png"
-ROBEXT_PLOT_SAVE_OPTIONS = {}
-ROBEXT_PLOT_ID = 0
-
-def __plot_graphics_to_macro(self):
- global ROBEXT_PLOT_SAVE_FORMAT
- global ROBEXT_PLOT_ID
- global ROBEXT_PLOT_SAVE_OPTIONS
- filename = get_file_base() + f"plot-{ROBEXT_PLOT_ID}.{ROBEXT_PLOT_SAVE_FORMAT}"
- self.save(filename, **ROBEXT_PLOT_SAVE_OPTIONS)
- ROBEXT_PLOT_ID += 1
- return filename
-
-sage.plot.graphics.Graphics.__toMacro = __plot_graphics_to_macro
-
-\end{RobExtPlaceholderFromCode}
-
-\robExtConfigure{
- new preset={sage res}{
- python exec res,
- import placeholders={__ROBEXT_SAGE_EXEC__,__ROBEXT_SAGE_EXEC_LIBRARY_CHANGES__},
- set placeholder={__ROBEXT_PYTHON_EXEC_LIBRARY_CHANGES__}{__ROBEXT_SAGE_EXEC_LIBRARY_CHANGES__},
- % Sage expects the extension .sage
- set compilation command={__ROBEXT_SAGE_EXEC__ "__ROBEXT_OUTPUT_PREFIX__.sage"},
- change source extension=sage,
- },
- new preset={sage}{
- sage res,
- set placeholder={__ROBEXT_PYTHON_EXEC_RES_EQUALITY__}{res =},
- },
-}
-
-\robExtCopyGroupPlaceholders{python exec}{main}
-\robExtRegisterGroupPlaceholders{python exec}
-
-%%%%%%
%%%%%% Group "verbatim"
%%%%%%
More information about the tex-live-commits
mailing list.