diff options
-rw-r--r-- | Master/texmf-dist/doc/latex/robust-externalize/robust-externalize.pdf | bin | 766861 -> 1596591 bytes | |||
-rw-r--r-- | Master/texmf-dist/doc/latex/robust-externalize/robust-externalize.tex | 903 | ||||
-rw-r--r-- | Master/texmf-dist/tex/latex/robust-externalize/robust-externalize.sty | 1071 |
3 files changed, 1814 insertions, 160 deletions
diff --git a/Master/texmf-dist/doc/latex/robust-externalize/robust-externalize.pdf b/Master/texmf-dist/doc/latex/robust-externalize/robust-externalize.pdf Binary files differindex cb3a4071e7e..58ea1ff7146 100644 --- a/Master/texmf-dist/doc/latex/robust-externalize/robust-externalize.pdf +++ b/Master/texmf-dist/doc/latex/robust-externalize/robust-externalize.pdf diff --git a/Master/texmf-dist/doc/latex/robust-externalize/robust-externalize.tex b/Master/texmf-dist/doc/latex/robust-externalize/robust-externalize.tex index 58d64342c5d..f06333b9fea 100644 --- a/Master/texmf-dist/doc/latex/robust-externalize/robust-externalize.tex +++ b/Master/texmf-dist/doc/latex/robust-externalize/robust-externalize.tex @@ -1,3 +1,4 @@ +%\pdfoutput=1% Do not remove, needed for arxiv \documentclass[a4paper,doc2]{ltxdoc} % doc2 is needed to force the old version, or links get colored in a weird red way even with hidelinks. https://github.com/latex3/latex2e/issues/822 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -10,15 +11,24 @@ \usepackage{amsmath} \usepackage[margin=3cm]{geometry} \usepackage{calc} +\usepackage{siunitx} \usepackage{tikz} +\usepackage{tikzit} +\input{new_zxstyle.tikzstyles} \usetikzlibrary{shadows,fit} % \usetikzlibrary fails because file is not in current directory, lazy to setup TEXINPUTS \makeatletter \usepackage{robust-externalize} \makeatother \robExtConfigure{ - enable optimizations -} + enable optimizations, + copy file to cache={zx-calculus.sty}, + copy file to cache={tikzlibraryzx-calculus.code.tex}, + % You need to uncomment that line to rename the .tex files into .tex-backup + % backup source for arxiv, + rename backup files for arxiv, + print source when saving, % debug +} \usepackage{zx-calculus} \usepackage{forest} \setlength{\marginparwidth}{2cm} @@ -26,6 +36,7 @@ % Loads the great package that produces tikz-like manual (see also tikzcd for examples) \input{pgfmanual-en-macros.tex} % Is supposed to be included in recent TeX distributions, but I get errors... +\usepackage{gnuplot-lua-tikz} %% Generate with gnuplot -e "set terminal tikz createstyle" %% For verbatim environments: @@ -94,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.2}\\[3mm] + {Léo Colisson \quad Version 2.5}\\[3mm] {\href{https://github.com/leo-colisson/robust-externalize}{\texttt{github.com/leo-colisson/robust-externalize}}} \end{center} @@ -102,10 +113,11 @@ \bigskip -\textbf{WARNING: This library is young and might lack some testing (and is an important rewrite of a previous version), but v2.0 is definitely usable. Even if we try to stay backward compatible, the only guaranteed way to be immune to changes is to copy/paste the library in your main project folder. Please report any bug to \url{https://github.com/leo-colisson/robust-externalize}!} +\textbf{WARNING: Even if we try to stay backward compatible, the only guaranteed way to be 100\% immune to changes is to copy/paste the\ .sty file of the library in your main project folder. Please report any bug to \url{https://github.com/leo-colisson/robust-externalize}!} -\textbf{WARNING 2: the coming version 2.1 improves significantly the compilation time compared with 2.0 that was surprisingly slow. Now, it is fairly well optimized (but the code needs a bit of cleaning and documentation before doing an official release)} +\textbf{WARNING 2: the recent versions 2.1 and 2.2 improves significantly the compilation time compared with 2.0 that was surprisingly slow. Now, it is fairly well optimized, and version 2.3 is even more optimized.} +\textbf{WARNING 3: If you read this documentation from the arXiv or CTAN, you might prefer to read it from \mylink{https://github.com/leo-colisson/robust-externalize}{Github} to get the latest version.} \section{A taste of this library} @@ -118,7 +130,7 @@ This library allows you to cache any language: not only \LaTeX{} documents and \ enable fallback to manual mode, } \end{verbatim} -Then type something like this (note the |C| for |cached| at the end of |tikzpictureC|, see below to override the original command), and compile with |pdflatex -shell-escape yourfile.tex| (or \textcolor{red}{\textbf{read below if you do not want to use }}|-shell-escape|\textcolor{red}{\textbf{ (note that overleaf enables shell-escape by default, and it not needed once all pictures are cached)}}): +Then type something like this (note the |C| for |cached| at the end of |tikzpictureC|, see below to override the original tikz commands), and compile with |pdflatex -shell-escape yourfile.tex| (or \textcolor{red}{\textbf{read below if you do not want to use }}|-shell-escape|\textcolor{red}{\textbf{ (note that overleaf enables shell-escape by default, and it is not needed once all pictures are cached)}}): \begin{codeexample}[width=0pt,vbox] \robExtConfigure{ add to preset={tikz}{ @@ -128,11 +140,16 @@ Then type something like this (note the |C| for |cached| at the end of |tikzpict } The next picture is cached % \begin{tikzpictureC}[baseline=(A.base)] - \node[fill=red, rounded corners](A){My node that respects baseline \ding{164}.}; + \node[fill=red, rounded corners](A){My node respecting baseline \ding{164}.}; \node[fill=red, rounded corners, opacity=.3,overlay] at (A.north east){I am an overlay text}; \end{tikzpictureC} and you can see that overlay and depth works. \end{codeexample} +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}. +\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: \begin{codeAndResult} @@ -145,7 +162,7 @@ for name in ["Alice", "Bob"]: Actually, we also provide this style by default (and explain how to write it yourself), just make sure to load: \begin{verbatim} \usepackage{pythonhighlight} -\usepackage{tcolorbox} +\usepackage[most]{tcolorbox} \end{verbatim} You can also cache any environment and command using something like: @@ -208,6 +225,17 @@ A ZX figure % } \end{codeAndResult} \end{itemize} + +We also provide methods to cache automatically \mylink{https://tikzit.github.io/}{TikZit} pictures (|\cacheCommand| alone would not be sufficient as the source relies on an external file): + +\begin{codeexample}[vbox] + %%%%%%% In your preamble: + \cacheTikzitWithStyle{new_zxstyle.tikzstyles} + + %%%%%%% In your document: + \tikzfig{demo} %figures/ can be omitted +\end{codeexample} + You can also cache all tikz pictures by default, except those that are run from some commands (as remember picture does not work yet, this is important). For instance, this code will allow you to freely use the |todonotes| package (that uses internally tikz but is not cachable with this library since it uses remember picture) while caching all other elements: \begin{codeexample}[code only] %% this will cache \tikz and tikzpictures by default (using the tikz preset) @@ -341,6 +369,13 @@ We also provide a number of other functionalities, among others: \item And more\dots \end{itemize} +\section{Acknowledgments} + +I am deeply indebted to many users on \url{tex.stackexchange.com} that made the writing of this library possible. I can't list you all, but thank you so much! A big thanks also to the project \url{https://github.com/sasozivanovic/memoize/} from which I borrowed most of the code to automatically wrap commands. Thanks to kiryph and dflvunoooooo for providing very useful feedbacks. + +\section{Support} + +If you find this library useful, please consider citing the arXiv version of this documentation, possibly using |\nocite|. If you want to support me financially and offer me a virtual coffee, you can use my \mylink{https://github.com/sponsors/tobiasBora}{GitHub sponsor page}. \section{Introduction} @@ -378,10 +413,10 @@ I tested the library on Linux and Windows, but the library should work on all OS \paragraph{Do I need to compile using -shell-escape?} -Since we need to compile the images via an external command, the simpler option is to add the argument |-shell-escape| to let the library run the compilation command automatically (this is also the case of \tikzname's externalize library). However, people worried by security issues of |-shell-escape| (that allows arbitrary code execution if you don't trust the \LaTeX{} code) might be interested by these facts: +Since we need to compile the images via an external command the first time, the simpler option is to add the argument |-shell-escape| to let the library run the compilation command automatically (this is also the case of \tikzname's externalize library). Note that overleaf already defines this option by default, so you don't need to do anything special in overleaf. Moreover, this is only needed \textbf{during the first compilation} since once the image is cached, |-shell-escape| is not needed anymore (therefore, on the arXiv website, you can upload the |robustExternalize| folder containing the cached images without any issue, see \cref{sec:arxiv} for details). However, people worried by security issues of |-shell-escape| (that allows arbitrary code execution if you don't trust the \LaTeX{} code) might be interested by these facts: \begin{itemize} -\item You can choose to display a dummy content explaining how to manually compile the pictures until they are compiled, see |enable fallback to manual mode|. -\item You can compile manually the images: all the commands that are left to be executed are also listed in \texttt{robExt-compile-missing-figures.sh} and you can just inspect and run them, either with \texttt{bash robExt-compile-missing-figures.sh} or by typing them manually (most of the time it's only a matter of running \texttt{pdflatex somefile.tex}). +\item You can choose to display a dummy content explaining how to manually compile the pictures instead of giving an error until they are compiled, see |enable fallback to manual mode|. +\item You can compile manually the images: all the commands that are left to be executed are also listed in \texttt{JOBNAME-robExt-compile-missing-figures.sh} and you can just inspect and run them, either with \texttt{bash robExt-compile-missing-figures.sh} or by typing them manually (most of the time it's only a matter of running \texttt{pdflatex somefile.tex} from the \texttt{robustExternalize} folder). \item If you allowed: \begin{itemize} \item |pdflatex| (needed to compile latex documents) @@ -396,11 +431,29 @@ Since we need to compile the images via an external command, the simpler option \paragraph{Is it working on overleaf?} -Yes: overleaf automatically compiles documents with |-shell-escape|, so nothing special needs to be done there (of course, if you use this library to run some code, the programming language might not be available, but I heard that python is installed on overleaf servers for instance, even if this needs to be doubled checked). If the first compilation of the document to cache images times out, you can just repeat this operation multiple times until all images are cached. +Yes: overleaf automatically compiles documents with |-shell-escape|, so nothing special needs to be done there (of course, if you use this library to run some code, the programming language might not be available, but I heard that python is installed on overleaf servers for instance, even if this needs to be doubled checked). If the first compilation of the document to cache images times out, you can just repeat this operation multiple times until all images are cached. In case of doubt, you can look at the log file to see which file is getting cached. If overleaf still times out, read the next paragraph. + +\paragraph{Overleaf still times-out, why?} + +During the first compilation, the pictures are not compiled, so you may obtain a time out. However, at each compilation, new pictures should be added to the cache, so you should eventually be able to compile (check the log file, you should read ``No need to recompile XX.pdf'' if the picture is already in the cache). If you want this to be faster, you can try to enable |\robExtConfigure{compile in parallel}| in order to compile the pictures in parallel (need 2 compilations). If you do not often change all pictures, you can also locally download the project (it might be more practical to use the git bridge if you have it enabled), compile them locally, and copy back the |robustExternalize| folder containing all cached pictures. + +If all pictures are still compiled, but you still experience timeouts, you can try to use compiled presets, with something like: +\begin{verbatim} +\robExtConfigure{ + new compiled preset={compiled ZX}{templateZX, compile latex template}{} +} +\end{verbatim} +to create a new, more efficient (we call it compiled) preset called |compiled ZX| from a preset |templateZX|. + +You can also try to reduce the compilation time by reducing the number of compilation passes. Indeed, overleaf will run |pdflatex| multiple times to make it converge to the final version, increasing mechanically the compilation time (for instance a file that takes 6s to compile locally was not able to compile online). You can force overleaf to compile a single time the document by creating a |latexmkrc| file at the root of the project, containing: +\begin{verbatim} +$max_repeat=1 +\end{verbatim} +Of course, you might need to recompile more than once the document to reach the final version. \paragraph{Do you have some benchmarks?} -We completely rewrote the \href{https://github.com/leo-colisson/robust-externalize/commit/09a05a86ec9c04eb6687b75831ebb84185786ac5}{original library} to introduce the notion of placeholders (before, the template was fixed forever), allowing for greater flexibility. The original rewrite (1.0) had really poor performance compared to the original library (only x3 improvement compared to no externalization, while the old library could be 20x faster), but we made some changes (v2.0) that correct this issue. Here is a benchmark we made on an article with 159 small pictures (ZX diagrams from the zx-calculus library): +We completely rewrote the \href{https://github.com/leo-colisson/robust-externalize/commit/09a05a86ec9c04eb6687b75831ebb84185786ac5}{original library} to introduce the notion of placeholders (before, the template was fixed forever), allowing for greater flexibility. The original rewrite (1.0) had really poor performance compared to the original library (only x3 improvement compared to no externalization, while the old library could be 20x faster), but we made some changes (v2.0) that correct this issue to recover the original speed (at least when using compiled templates). Here is a benchmark we made on an article with 159 small pictures (ZX diagrams from the zx-calculus library): \begin{itemize} \item without externalization: 1mn25 \item with externalization, v2.0 (first run, not in parallel): 3mn05, so takes twice the time; but (first run in parallel) takes 60s (so faster by a factor of 1.5). @@ -453,6 +506,45 @@ Cons of \url{https://github.com/sasozivanovic/memoize}: Note that |remember picture| is not supported in both libraries, but we can provide multiple methods to disable externalizations when |remember picture| is used. +\subsection{How to use with arXiv}\label{sec:arxiv} + +You can completely use this library with the arXiv's website (this documentation is also compiled in arXiv), but you need to follow a few steps due to arXiv's particularities: +\begin{itemize} +\item First, arXiv does NOT allow |-shell-escape|. So you either need to disable externalization (not recommended since this will of course increase the compilation time and will not work when working with non-latex code like python or external images), or you need to compile locally the document to cache all images locally\footnote{Make sure to recompile from scratch, by removing the .aux file, if your cached pictures can depend on counters (like the current page) that can change across compilations.}, and upload the |robustExternalize/| folder containing all cached images online in a zip file. +\item Moreover, if you choose to upload the |robustExternalize/| folder, the arXiv website will automatically remove all |.pdf| files if a |.tex| file is present (which will break this library, since it puts all sources in the |.tex| file and the output in a |.pdf| file). The trick is to rename all the source |.tex| files into |.tex-backup| (this library will automatically recreate the |.tex| from the |.tex-backup| starting from v2.4). We provide two approaches for that: + \begin{itemize} + \item \textbf{Solution 1}: if you have python installed the most robust solution is to run +\begin{verbatim} +$ python robExt-prepare-for-arxiv.py +\end{verbatim} + before sending the document to the arXiv. This will rename all the |.tex| files in the |robust-externalize| folder that have a matching |*.pdf| file into |.tex-backup|, and list these files in |robExt-arxiv-files-to-rename.txt|. Then, add in the preamble of your document: + \begin{codeexample}[code only] + \robExtConfigure{ + rename backup files for arxiv, + } + \end{codeexample} + This will automatically rename back the |.tex-backup| files into |.tex| (without needing shell-escape), based on the |robExt-arxiv-files-to-rename.txt| file (if this file exists and if the files have not already been renamed: it is therefore safe to keep this option on even without running the python code), allowing the rest of the document to compile normally. + \item \textbf{Solution 2}: if you do \textbf{not} have python installed, you can simply run: + \begin{codeexample}[code only] + \robExtConfigure{ + backup source for arxiv, + } + \end{codeexample} + This will automatically rename the source files |.tex| into |.tex-backup| (make sure to comment this line on the final pdf sent to the arXiv). Note however that this is less robust than solution 1 since the |.tex-backup| will be renamed into |.tex| only if it is in the source, but sometimes other |.tex| files might need to be renamed as well: for instance, |gnuplot| with |cairolatex| terminal creates two files |-gnuplot-.tex| and |-gnuplot-.pdf|, and this file will not be renamed with this method, so the |-gnuplot-.pdf| file will be lost. + \end{itemize} +\item This should be enough to compile most documents. But note that since the arXiv uses a different Texlive version and might try to compile other document formats, it might be the case that the number of page varies from your original document: if you uses things like |forward counter=page|, it might cause you some troubles since your local cache might contain the cache for |page=42| while the arXiv would ask for the cached value for |page=45|, producing an error like |enable -shell-escape|. There are a few steps you can follow to fix these issues: + \begin{itemize} + \item First, make sure to have the same margins/paper on your local file and online (by default arXiv will create a document in |letterpaper|). To that end, you can force the paper format with |\documentclass[a4paper]{article}|. + \item ArXiv might try to compile with various output formats like eps, so it might help to force a pdf output by starting your document with |\pdfoutput=1| (not sure how helpful this is, it might already be forced when we insert a pdf?). + \item ArXiv will automatically remove all empty files. While for some files it is not an issue (aux files and compilation logs), if this file is the |.pdf| it will be an issue. So make sure to always generate non-empty output files. + \item If despite these options, arXiv still tries to fetch a cached value that is not present in your local cache, you can enable +\begin{verbatim} +\robExtConfigure{print source when saving} +\end{verbatim} + This will print in the log file the content of the |.deps| file, of the |.tex| file, and the compilation command that would have been run without the error. You can then recreate these files in the cache and compile it locally. If you do not want to copy/paste the content of the file, it might be simpler to simply see the value that differs from your own local cache and just temporarily change this value in your local document to automatically generate the correct cache entry. If the value is obtained via |forward counter=page|, you can use |forward counter force value={page}{45}| to force the value of the counter. + \end{itemize} +\end{itemize} + \section{Tutorial} \subsection{Installation} @@ -473,7 +565,7 @@ If you want to display a message in the pdf on how to manually compile the file \noindent (otherwise, it will give you an error if |-shell-escape| is disabled and if some pictures are not yet cached) -If you forget/do not want to enable |-shell-escape|, this will give you this kind of message: +If you forget/do not want to enable |-shell-escape|, this will give you this kind of message with instructions to follow to compile without shell escape: \noindent { \robExtConfigure{ @@ -486,6 +578,32 @@ If you forget/do not want to enable |-shell-escape|, this will give you this kin } +If you use an editor, you can usually configure |-shell-escape| directly inside the editor, even on a per-document basis. Shell-escape is enabled by default on overleaf. In emacs/auctex, just add at the end of your document: +\begin{verbatim} +% Local Variables: +% TeX-command-extra-options: "--shell-escape" +% End: +\end{verbatim} +save, and run |M-x revert-buffer| (Note that it seems that |--shell-escape| can be used with one or two dashes on Linux, I am not sure about the number of dashes needed in Windows.) + +For TeXstudio, you can enable shell-escape on the current document by adding in your document: +\begin{verbatim} +% !TeX TXS-program:compile = txs:///pdflatex/[--shell-escape] +\end{verbatim} +and you might also want to enable: +\begin{verbatim} +\robExtConfigure{textstudio} +\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|). + +If you use |latexmk| (with any editor), you can create a file called |.latexmkrc| containing: +\begin{verbatim} +$pdf_mode = 1; +$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. + \subsection{Caching a tikz picture} If you only care about \tikzname's picture, you have 3 options: @@ -610,11 +728,48 @@ Note that if you use the |tikzpictureC| or |tikzpicture| syntax, you want to add \textbf{Important}: Note that the preset options can be specified in a number of places: in |\robExtConfigure| (possibly in a group, or inside a preset), in the options of the picture, in the default options of |cacheEnvironment| and |cacheCommand| etc. Most options can be used in all these places, the only difference being the scope of the options. +If you find yourself duplicating code between the main document and the preamble of the cached file, we provide multiple solutions with different properties: +\begin{itemize} +\item Recommended: |\newcommandAutoForward| etc, to automatically forward specific macros/colors etc: see \cref{sec:forwardIntro}. Very useful if you want maximum purity without annoying recompilation. +\item |\runHereAndInPreambleOfCachedFiles|: mostly useful if you want to always add a text both in the current file and in the cached files, like a font etc. +\item Share a common input file with or without dependencies (see \cref{sec:dependencies}), useful if you need to work with external files or if you do NOT want purity (i.e.\ you can choose not to recompile the file even if its content changes). +\end{itemize} + +|\runHereAndInPreambleOfCachedFiles{XXX}| is very simple (need at least version 2.3), as it is simply a shortcut for typing |XXX| in the current file and in the preamble of the cached files (default to latex preset). For instance if you want the current file and all cached files to use the Times New Roman font, you can do: + +\begin{codeexample}[code only] +% By default adds +\runHereAndInPreambleOfCachedFiles{ + \usepackage{fontspec} + % need lualatex or xelatex to compile + \setmainfont{Times New Roman} +} + +\robExtConfigure{ + add to preset={latex}{ + use lualatex, % Make sure to compile with lualatex the cached images. + }, +} +\end{codeexample} + +You can also define your common macros (even if we recommend |\newcommandAutoForward| to avoid frequent recompilations when the definitions changes): +\begin{codeexample}[width=0pt,vbox] +\runHereAndInPreambleOfCachedFiles{ + \def\name#1{The great lady #1 and her friends.} +} + +\cacheTikz +This is the picture of \name{Alice}: +\begin{tikzpicture} + \node[fill=orange]{\name{Alice}}; +\end{tikzpicture} +\end{codeexample} + \subsection{Dependencies}\label{sec:dependencies} -\textbf{Note: dependencies can sometimes be advantageously replaced with auto-forwarding of arguments, cf.\ \cref{sec:forwardIntro}.} +\textbf{Note: dependencies can sometimes be advantageously replaced with auto-forwarding of arguments, cf.\ \cref{sec:forwardIntro}, or |\runHereAndInPreambleOfCachedFiles|.} -It might be handy to have a file that is loaded in both the main document and in the cached pictures. For instance, if you have a file |common_inputs.tex| that you want to input in both the main file and in the cached files, that contains, say:\\ +It might be handy to have a file that is loaded in both the main document and in the cached pictures. For instance, if you have a logo that might be included in the cached files, or if you have a file |common_inputs.tex| that you want to input in both the main file and in the cached files, that contains, say:\\ |\def\myValueDefinedInCommonInputs{42}|\\ then you can add it as a dependency this way (here we use the |latex| preset that does not wrap the code inside a |tikzpicture| only to illustrate that we can also cache things that are not generated by tikz): \begin{codeexample}[width=0pt,vbox] @@ -628,6 +783,13 @@ Note that the placeholder |__ROBEXT_WAY_BACK__| contains the path from the cache You can note that we used |add dependencies={common_inputs.tex}|: this allows us to recompile the files if |common_inputs.tex| changes. If you do not want this behavior (e.g. |common_inputs.tex| changes too often and you do not want to recompile everything at every change), you can remove this line, but beware: if you do a breaking changes in |common_inputs.tex| (e.g.\ redefine |42| to |43|), then the previously cached picture will not be recompiled! (So you will still read 42 instead of 43.) +Sometimes, it can also be useful to copy a file in the cache folder. This can be done via: +\begin{codeexample}[code only] +\robExtConfigure{ + copy file to cache={zx-calculus.sty}, +} +\end{codeexample} + \subsection{Wrap arbitrary environments} You can wrap arbitrary environments using the already presented |cacheEnvironment| and |cacheCommand|, where the first mandatory argument is the name of the environment/macro, and the second mandatory argument contains the default preset. |cacheEnvironment| works for any environment while |cacheCommand| might need a bit of help to determine the signature of the macro if the function is defined via xparse. Long story short, |O{foo}| is an optional argument with default value foo, |m| is a mandatory argument. By default, you can pass an optional arguments via the first argument enclosed in |<>|: @@ -725,6 +887,7 @@ Some text % You can feed data from the main document to the cached file using placeholders, since |set placeholder eval={__foo__}{\bar}| will evaluate |\bar| and put the result in |__foo__|. For instance, if the picture depends on the current page, you can do: +\message{We will export the page: \thepage} \begin{codeexample}[width=0pt,vbox] \begin{tikzpictureC}<set placeholder eval={__thepage__}{\thepage}> \node[rounded corners, fill=red]{The current page is __thepage__.}; @@ -820,6 +983,105 @@ We compute this data that is long to compute: We computed the cached value \myLongResult. \end{codeexample} +\subsection{A note on font handling} + +Cached pictures are basically simple latex files that are compiled in a separate |standalone| document. Therefore, if the main document loads a different font, there is no reason for the cached document to also use that font. Fully-automatically forwarding the current font by default is not so easy because there are so many ways to load a font in LaTeX (by loading a package like |\usepackage{times}|, by using xelatex or lualatex, by calling locally |\rmfamily|, by loading a different class… and here I'm not even mentioning math font where each symbol \mylink{https://tex.stackexchange.com/questions/603475/is-there-any-command-to-find-out-the-current-math-font-used)}{can use a different font}, and this is even more complicated if the main file is compiled with a different engine (e.g.\ what should I do if the main document is compiled with lualatex but the inner cached images are compiled with pdflatex, that supports very few fonts by default?). For these reasons, it is simpler to simply let the user load fonts as they want, for instance using something like: +\begin{codeexample}[width=0pt] +\robExtConfigure{ + add to preset={latex}{ + add to preamble={\usepackage{times}}, + }, +} + +\begin{tikzpictureC} +\node[fill=green]{See I am using times font}; +\end{tikzpictureC} +\end{codeexample} +or: +\begin{codeexample}[width=0pt] +\robExtConfigure{ + add to preset={latex}{ + add to latex options={12pt}, + }, +} + +\begin{tikzpictureC} +\node[fill=green]{See I am using a 12pt font}; +\end{tikzpictureC} +\end{codeexample} + +But if you want to use the same font as the current document, you might want to avoid code duplication (this might also be useful for any arbitrary code). Since v2.3, you can do: +\begin{codeexample}[code only] +\runHereAndInPreambleOfCachedFiles{ + \usepackage{fontspec} + % need lualatex or xelatex to compile + \setmainfont{Times New Roman} +} + +\robExtConfigure{ + add to preset={latex}{ + use lualatex, % Make sure to compile with lualatex the cached images. + }, +} +\end{codeexample} +and it will automatically compile the cached images with lualatex, and configure the font of the current document and of the cached documents to follow |Times New Roman|. + +If you like to often change the font in the document, you can also do something like that to always compile the document with the font currently in use if you use lualatex (this code fails with \mylink{https://tex.stackexchange.com/questions/705208/get-current-font}{xelatex}, currently trying to understand why): +\begin{codeexample}[code only] +\usepackage{fontspec} +\setmainfont{Times New Roman} + +%% needed since \f@family contains a @ in its name: +\makeatletter +\def\myCurrentFont{\f@family} +\makeatother + +\robExtConfigure{ + add to preset={latex}{ + use lualatex, + % create a new placeholder containing the name of the current font: + set placeholder eval={__MY_FONT__}{\myCurrentFont}, + add to preamble={ + \usepackage{fontspec} + \setmainfont{__MY_FONT__} + }, + }, +} +\end{codeexample} +If you want this to work on xelatex, you can either manually attribute a |NFSSFamily| name like: +\begin{codeexample}[code only] +\runHereAndInPreambleOfCachedFiles{ + \usepackage{fontspec} + % need lualatex or xelatex to compile, make sure to use a different NFSSFamily for each new font you add here + \setmainfont[NFSSFamily=tmsnr]{Times New Roman} +} +\end{codeexample} +and use the above solution for lualatex (you can change the font with |\fontfamily{tmsnr}\selectfont|), or do instead something like this: +\begin{codeexample}[code only] +% Create a new command \mysetmainfont which forwards the font to the picture. +\NewDocumentCommand{\mysetmainfont}{m}{% + \def\myCurrentFont{#1}% Stores the name of the current font in \myCurrentFont + \setmainfont{#1}% +} + +% Use mysetmainfont from now to change font: +\mysetmainfont{Times New Roman} + +\usepackage{robust-externalize} +\robExtConfigure{ + add to preset={latex}{ + use xelatex, + set placeholder eval={__MY_FONT__}{\myCurrentFont}, + add to preamble={ + \usepackage{fontspec} + \setmainfont{__MY_FONT__} + }, + }, +} +\end{codeexample} + + + \subsection{Compile in parallel} Make sure to have xargs installed (installed by default on most linux, on Windows you need to install the lightweight GNU On Windows (Gow) \url{https://github.com/bmatzelle/gow}), and type in your preamble: \begin{codeexample}[code only] @@ -1162,7 +1424,8 @@ We also provide support for other languages, notably |bash|, but it is relativel set -e __ROBEXT_MAIN_CONTENT__ # Create the pdf file to certify that no compilation error occured -touch "__ROBEXT_OUTPUT_PDF__" +# (non empty file to avoid arXiv error) +echo "ok" > "__ROBEXT_OUTPUT_PDF__" \end{PlaceholderFromCode} \robExtConfigure{ @@ -1216,7 +1479,9 @@ plt.savefig("__ROBEXT_OUTPUT_PDF__") If you want to recompile a file (e.g. an untracked dependency was changed\dots{} and you do not want to track it to avoid recompilation when you change a single line in this file), you can use the |recompile| style since v2.1: -\begin{codeexample}[vbox] +\begin{codeexample}[vbox, + %TURN_INTO_CODE_ONLY_ARXIV + ] \cacheEnvironment{tikzcd}{tikz, add to preamble={\usepackage{tikz-cd}}} \begin{tikzcd}<recompile> A \rar & B @@ -1248,7 +1513,7 @@ Note also that we usually define two names for each function, one normal and one \subsection{Placeholders}\label{sec:placeholders} -Placeholders are the main concept allowing this library to generate the content of a source file based on a template (a template will itself be a placeholder containing other placeholders). A placeholder is a special strings like |__COLOR_IMAGE__| that should start and end with two underscores, ideally containing no space or double underscores inside the name directly. Placeholders are inserted for instance in a string and will be given a value later. This value will be used to replace (recursively) the placeholder in the template. For instance, if a placeholder |__LIKES__| contains |I like __FRUIT__ and __VEGETABLE__|, if the placeholder |__FRUIT__| contains |oranges| and if the placeholder |__VEGETABLE__| contains |salad|, then evaluating |__LIKES__| will output |I like oranges and salad.| +Placeholders are the main concept allowing this library to generate the content of a source file based on a template (a template will itself be a placeholder containing other placeholders). A placeholder is a special strings like |__COLOR_IMAGE__| that should start and end with two underscores, ideally containing no space or double underscores inside the name directly (we might use this property even more in a future release to improve further the efficiency). Placeholders are inserted for instance in a string and will be given a value later. This value will be used to replace (recursively) the placeholder in the template. For instance, if a placeholder |__LIKES__| contains |I like __FRUIT__ and __VEGETABLE__|, if the placeholder |__FRUIT__| contains |oranges| and if the placeholder |__VEGETABLE__| contains |salad|, then evaluating |__LIKES__| will output |I like oranges and salad.| Note that you are not strictly forced to follow the above convention (it allows us to optimize the code to find and replace placeholders), but in that case you should enable |not all placeholders have underscores|. @@ -1405,6 +1670,18 @@ Compare with: \placeholderFromContent{__NAME__}{Alice} The (unexpanded) template contains \texttt{\getPlaceholderNoReplacement{__LIKES__}} \end{codeexample} +Note that this macro is expandable, which can be helpful for instance if you use |siunitx| that expects an expandable content: +\begin{codeexample}[width=0pt,vbox] +\setPlaceholder{__TEST__}{4.42345} +\num[round-mode=figures,round-precision=3]{\getPlaceholderNoReplacement{__TEST__}} +\end{codeexample} +This is contrary to |\getPlaceholder| that is not expandable, so if you do want expandable |\getPlaceholder| with replacements, you should rather first evaluate the placeholder with |\getPlaceholderInResult{__TEST_VARIABLE__}|: +\begin{codeexample}[width=0pt,vbox] +\setPlaceholder{__TEST_A__}{4.42345} +\setPlaceholder{__TEST__}{__TEST_A__} +\getPlaceholderInResult{__TEST_A__} +\num[round-mode=figures,round-precision=3]{\robExtResult} +\end{codeexample} \end{pgfmanualentry} \subsubsection{Setting a value to a placeholder} @@ -2297,9 +2574,16 @@ plt.ylabel('Number of futurestud.io Tutorials') \makeatletter% \def\extrakeytext{style, } \extractkey/robExt/set compilation command=\marg{compilation command}\@nil + \extractkey/robExt/set compilation command create if no error=\marg{compilation command}\@nil + \extractkey/robExt/set compilation command move if no error=\marg{compilation command}\@nil \makeatother% \pgfmanualbody - Style that alias to |set placeholder={__ROBEXT_COMPILATION_COMMAND__}{#1}|, in order to define the placeholder that will hold the compilation command. + |set compilation command| is equivalent to |set placeholder={__ROBEXT_COMPILATION_COMMAND__}{#1}|, in order to define the placeholder that will hold the compilation command. + + Then, |set compilation command move if no error| (new in v2.4) + is like |set compilation command| except that it will copy the file located in |__ROBEXT_OUTPUT_PDF__-tmp| into |__ROBEXT_OUTPUT_PDF__| if the command succeeds (returns a status code of 0), and otherwise it will not create the |__ROBEXT_OUTPUT_PDF__| at all, creating an error message. This is needed by some programs that will create an output file even if an error occurs, for instance wget will still create an empty file if there is no internet connection, producing no error during the compilation, which can be confusing. + + |set compilation command create if no error| (new in v2.4) will, on the other hand, create an empty |__ROBEXT_OUTPUT_PDF__| file if the command succeeds (only useful if you use a custom include command). \end{pgfmanualentry} \begin{pgfmanualentry} @@ -2353,7 +2637,9 @@ plt.ylabel('Number of futurestud.io Tutorials') \pgfmanualbody (new to v2.1) |recompile| will force a compilation even if the image is already cached (mostly useful if you made a change to a non-tracked dependency). Note that since this library does not want to remove any file on the hard drive (we do not want to risk to remove important files in case of a bug), this command will NOT clear the auxiliary files already present, so for maximum purity, ensure that your compiling command is idempotent (so that compiling twice gives the same outcome as compiling once). -\begin{codeexample}[vbox] +\begin{codeexample}[vbox, + %TURN_INTO_CODE_ONLY_ARXIV + ] \cacheEnvironment{tikzcd}{tikz, add to preamble={\usepackage{tikz-cd}}} \begin{tikzcd}<recompile> A \rar & B @@ -2393,6 +2679,15 @@ You want to delete the file \texttt{\filetodeleteInCache.pdf}. % \end{pgfmanualentry} +\begin{pgfmanualentry} + \extractcommand\robExtMv\@@ + \extractcommand\robExtCp\@@ + \pgfmanualbody + (new in v2.4) Respectively expands to |mv| and |cp| in linux/unix and to |move| and |copy| in Windows (needed to create a compilation command that works in both linux and windows). Note that |set compilation command| does NOT expand its argument by default and spaces after macros are removed by latex, so you need to use |/.expanded| and |\space| like in: + \begin{codeexample}[code only] + set compilation command/.expanded={\robExtCp\space"__ROBEXT_SOURCE_FILE__" "__ROBEXT_OUTPUT_PDF__"} + \end{codeexample} +\end{pgfmanualentry} \subsubsection{Options to configure the inclusion command} @@ -2413,19 +2708,26 @@ The inclusion command is the command that is run to include the cached file back \makeatletter% \def\extrakeytext{style, } \extractkey/robExt/custom include command=\marg{include command}\@nil + \extractkey/robExt/include command is input=\opt{\oarg{additional code to run}}\@nil \makeatother% \pgfmanualbody - Sets the command to run to include the compiled file, after checking if the file has been correctly compiled and loading |*-out.tex| (useful to pass information back to the pdf). + Sets the command to run to include the compiled file, after checking if the file has been correctly compiled and loading |*-out.tex| (useful to pass information back to the pdf). |include command is input={SOME CODE}| is a shortcut for: +\begin{verbatim} +custom include command={SOME CODE\input{\robExtAddCachePathAndName{\robExtFinalHash.pdf}}}, +\end{verbatim} \end{pgfmanualentry} \begin{pgfmanualentry} \makeatletter% \def\extrakeytext{style, } \extractkey/robExt/do not include pdf\@nil + \extractkey/robExt/include command is input=\opt{additional command}\@nil \makeatother% \pgfmanualbody - Do not include the pdf. Useful if you only want to compile the file but use it later (note that you should still generate a |.pdf| file, possibly empty, to indicate that the compilation runs smootly). Equivalent to:\\ + |do not include pdf| do not include the pdf. Useful if you only want to compile the file but use it later (note that you should still generate a |.pdf| file, possibly empty, to indicate that the compilation runs smootly). Equivalent to:\\ |custom include command={}| + Use |include command is input| (v2.3 and after) in order to specify that you should |\input| the |.pdf| file (note that despite the extension, it should still contain a LaTeX code). You can also specify an optional command to run before. Equivalent to: + |custom include command={#1\input{\robExtAddCachePathAndName{\robExtFinalHash.pdf}}}| \end{pgfmanualentry} \begin{pgfmanualentry} @@ -2465,6 +2767,16 @@ The inclusion command is the command that is run to include the cached file back By default, the include commands runs |\includegraphics| on the pdf, and possibly raises it if needed. You can customize the arguments passed to |\includegraphics| here. \end{pgfmanualentry} +\begin{pgfmanualentry} + \makeatletter% + \def\extrakeytext{style, } + \extractkey/robExt/add cache to graphicspath\@nil + \makeatother% + \pgfmanualbody + This will add the folder containing the cache in the |\graphicspath{}|. Mostly useful if you need to input a file from the cache that does |\includegraphics{foo}| where |foo| is an image from the cache itself (looking at you gnuplot). +\end{pgfmanualentry} + + \subsubsection{Configuration of the cache} If needed, you can configure the cache: @@ -2497,6 +2809,24 @@ If needed, you can configure the cache: \begin{pgfmanualentry} \makeatletter% \def\extrakeytext{style, } + \extractkey/robExt/set output directory=\marg{output directory}\@nil + \makeatother% + \pgfmanualbody + If you compile with |--output-directory build|, you also need to tell this library that you are compiling with |--output-directory build|. As I can't find a reliable way to get this value, even using |TEXMF_OUTPUT_DIRECTORY|, you need to specify manually the compilation directory. To do that, you can either compile with: + \begin{codeexample}[code only] + pdflatex -shell-escape -output-directory=build "\def\robExtOutputDirectory{build}\input{test.tex}" + \end{codeexample} + or add in your file: + \begin{codeexample}[code only] + \robExtConfigure{ + set output directory=build, + } + \end{codeexample} +\end{pgfmanualentry} + +\begin{pgfmanualentry} + \makeatletter% + \def\extrakeytext{style, } \extractkey/robExt/no cache folder\@nil \makeatother% \pgfmanualbody @@ -2580,6 +2910,7 @@ Some text % \def\extrakeytext{style, } \extractkey/robExt/command if no externalization\@nil \extractkey/robExt/command if externalization\@nil + \extractkey/robExt/run command if externalization=\marg{code}\@nil \makeatother% \pgfmanualbody You can easily change the command to run if externalization is disabled using by setting the \textbf{.code} of this key. By default, it is configured as:\\ @@ -2589,7 +2920,7 @@ command if no externalization/.code={% } \end{verbatim} Unless you know what you are doing, you should include |\robExtDisableTikzpictureOverwrite| as it is often necessary to avoid infinite recursion when externalization is disabled and the original command has been replaced with a cached version (for instance done by |\cacheTikz|). Note that if you write your own style, try to modify |__ROBEXT_MAIN_CONTENT__| so that it can be included as-it in a document: this way you do not need to change this command. - You can also use |command if externalization/.append code={...}| if you want to run a code only if externalization is enabled (this is used for instance by the |forward| functionality). + You can also use |command if externalization/.append code={...}| if you want to run a code only if externalization is enabled (this is used for instance by the |forward| functionality), but we advise you rather to use |run command if externalization={...}| as this will also work if the style is executed while |command if externalization| is already running (this might occur when calling the style inside |if matches word|, for instance when forwarding colors). \end{pgfmanualentry} \begin{pgfmanualentry} @@ -2665,6 +2996,23 @@ In order to enforce reproducibility, you should tell what are the files that you Note that the placeholder |__ROBEXT_WAY_BACK__| contains the path from the cache folder (containing the |.tex| that will be cached) to the root folder.\\ This way, you can easily input files contained in the root folder. \end{pgfmanualentry} +\begin{pgfmanualentry} + \extractcommand\copyFileToCache\@@ + \extractcommand\forceCopyFileToCache\@@ + \makeatletter% + \def\extrakeytext{style, } + \extractkey/robExt/copy file to cache=\marg{file}\@nil + \makeatother% + \pgfmanualbody + It can sometimes be handy to copy a file to the cache folder to avoid using |__ROBEXT_WAY_BACK__| for instance to refer to another file. This can be done with |copy file to cache|, either in a preset or in |\robExtConfigure| like: +\begin{codeexample}[code only] +\robExtConfigure{ + copy file to cache={zx-calculus.sty}, +} +\end{codeexample} +Note that |copy file to cache| will always compare the md5 sum to check if a copy must be done or not. +\end{pgfmanualentry} + \subsubsection{Forward macros}\label{sec:forward} (since v2.1) @@ -2762,6 +3110,7 @@ Really involved macros (e.g.\ defined inside libraries) might call many other ma \makeatletter% \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 \makeatother% \pgfmanualbody Forward a counter: @@ -2770,6 +3119,12 @@ Really involved macros (e.g.\ defined inside libraries) might call many other ma \node[rounded corners, fill=red]{The current page is \thepage.}; \end{tikzpictureC} \end{codeexample} +The force value also assigns a value to the counter irrespective of the current value (might be useful for instance to generate a cached value for the arXiv). +\begin{codeexample}[width=0pt] +\begin{tikzpictureC}<forward counter force value={page}{65}> + \node[rounded corners, fill=red]{The current page is \thepage.}; +\end{tikzpictureC} +\end{codeexample} \end{pgfmanualentry} \begin{pgfmanualentry} @@ -2794,13 +3149,25 @@ but also with colors defined with |\colorlet|: \node[fill=myviolet,yshift=-1cm]{B}; \end{tikzpictureC} \end{codeexample} -Note that using |if matches word|, you can also automatically forward colors: +Note that using |if matches word|, you can also automatically forward colors. This command also scales better than |if matches|: if |if matches| is used to register $N$ words, the compilation time will increase for each new |if matches| (since we need to check each time if the string contains the expression). |if matches word|, on the other hand, is more clever, and extracts in a single loop all the words of the string: the running time is therefore independent of the number of times |if matches word| was called. You can call |if matches word| outside of any preset like in: \begin{codeexample}[width=0pt] \definecolor{myred}{HTML}{F01122} \colorlet{myviolet}{blue!50!myred} \colorlet{mypink}{pink!90!orange} \robExtConfigure{ - add to preset={tikz}{ + if matches word={mypink}{forward color=mypink}, +} +\begin{tikzpictureC} + \node[fill=mypink,yshift=-1cm]{B}; +\end{tikzpictureC} +\end{codeexample} +And this will automatically allow all presets based on |latex| to forward this color (see also |\definecolorAutoForward| to do that even quicker). You can also simply call it on any preset of your choice like: +\begin{codeexample}[width=0pt] +\definecolor{myred}{HTML}{F01122} +\colorlet{myviolet}{blue!50!myred} +\colorlet{mypink}{pink!90!orange} +\robExtConfigure{ + add to preset={latex}{ if matches word={mypink}{forward color=mypink}, }, } @@ -2808,7 +3175,7 @@ Note that using |if matches word|, you can also automatically forward colors: \node[fill=mypink,yshift=-1cm]{B}; \end{tikzpictureC} \end{codeexample} -but since this is a bit long to type, we provide a shortcut: +or, equivalently: \begin{codeexample}[width=0pt] \definecolor{myred}{HTML}{F01122} \colorlet{myviolet}{blue!50!myred} @@ -2826,9 +3193,11 @@ but since this is a bit long to type, we provide a shortcut: \makeatletter% \def\extrakeytext{style, } \extractkey/robExt/auto forward\@nil + \extractkey/robExt/auto forward only macros\@nil + \extractkey/robExt/auto forward words\@nil \makeatother% \pgfmanualbody - Enable automatic forwarding of macros marked with |*AutoForward| commands or |\configIfMacroPresent|. This command naively extracts all used macro via a simple regex match and forwards them/execute the style configured in |\configIfMacroPresent| if it exists. + |auto forward| enables automatic forwarding of macros marked with |*AutoForward| commands or |\configIfMacroPresent| or |if matches word|. This command naively extracts all used macros via a simple regex match and forwards them/execute the style configured in |\configIfMacroPresent| if it exists. It does a similar thing for words registered via |if matches word| or |register word with namespace|. If you want to only forward macros or words, you can use |auto forward only macros| or |auto forward words|. \begin{codeexample}[vbox] \cacheTikz \robExtConfigure{add to preset={tikz}{auto forward}} @@ -2878,8 +3247,8 @@ but since this is a bit long to type, we provide a shortcut: \extractcommand\ProvideExpandableDocumentCommandAutoForward\marg{macro}\marg{arg spec}\opt{\oarg{additional style}}\marg{definition}\@@ \extractcommand\DeclareExpandableDocumentCommandAutoForward\marg{macro}\marg{arg spec}\opt{\oarg{additional style}}\marg{definition}\@@ \extractcommand\defAutoForward\marg{macro}\opt{\oarg{def-style arg spec}}\marg{macro}\opt{\oarg{additional style}}\@@ - \extractcommand\genericAutoForward\opt{*}\opt{\oarg{preset}}\marg{string to match}\opt{\oarg{additional style}}\marg{code to execute}\@@ - \extractcommand\genericAutoForwardStringMatch\opt{*}\opt{\oarg{preset}}\marg{string to match}\opt{\oarg{additional style in preset}}\opt{\oarg{additional style outside preset}}\marg{code to execute}\@@ + \extractcommand\genericAutoForward\opt{*}\opt{\oarg{preset}}\opt{\oarg{namespace}}\marg{word}\opt{\oarg{additional style}}\marg{code to run}\@@ + \extractcommand\genericAutoForwardStringMatch\opt{*}\opt{\oarg{preset}}\marg{string}\opt{\oarg{additional style}}\marg{code to run}\@@ \pgfmanualbody \begin{verbatim} \newcommandAutoForward{\macro}[nb args][default value]{def}[STYLE] @@ -2908,7 +3277,7 @@ but since this is a bit long to type, we provide a shortcut: \end{tikzpicture} \end{codeexample} - This works similarly for other commands (note that for |\defAutoForward|, you need to put the arguments in a bracket, like |\defAutoForward\myMacro[#1 and #2]{Hey #1, #2}| for |\def\myMacro#1 and #2{Hey #1, #2}|). |\genericAutoForward| and |\genericAutoForwardStringMatch| are a bit special (for now these two commands are alias, but only use the first one if the string is surrounded by non-letter characters as we plan to make that special case more efficient later as it uses internally |if matches word|), as it can be used to run any code assuming that |string to match| is present. For instance, you can use it to automatically forward the code that defines a new tikz style assuming |mystyle| is preset: + This works similarly for other commands (note that for |\defAutoForward|, you need to put the arguments in a bracket, like |\defAutoForward\myMacro[#1 and #2]{Hey #1, #2}| for |\def\myMacro#1 and #2{Hey #1, #2}|). |\genericAutoForward| and |\genericAutoForwardStringMatch| are a bit special, as it can be used to run any code assuming that |word|/|string to match| is present (|\genericAutoForward| is more efficient as it does not scale with the number of words, but do not allow spaces etc in the name). For instance, you can use it to automatically forward the code that defines a new tikz style assuming |mystyle| is preset: \begin{codeexample}[width=0pt] \cacheTikz \genericAutoForward{mystyle}{ @@ -2943,6 +3312,68 @@ This will also run the code to define it in the current document (practical for }; \end{tikzpicture} \end{codeexample} + \begin{codeexample}[vbox] + % space is not allowed in the name with this more efficient version: + \robExtGenericAutoForward{mystyle}{% + \tikzset{% + mystyle/.style={fill=#1!30!white,text=#1!40!black}, + }% + } + \begin{tikzpictureC} + \node[mystyle=green]{My style automatically forwarded}; + \end{tikzpictureC} + \end{codeexample} + \begin{codeexample}[vbox] + % space is allowed in the name with this slightly less efficient version: + \robExtGenericAutoForwardStringMatch{my style}{% + \tikzset{% + my style/.style={fill=#1!30!white,text=#1!40!black}, + }% + } + \begin{tikzpictureC} + \node[my style=green]{My style with a space in the name automatically forwarded}; + \end{tikzpictureC} + \end{codeexample} + Note that by default, this forwards elements in the |latex| preset, but you can change it via |preset|. You can also configure additional style to run, and choose another namespace for the word version (practical only if you want to forward different codes to different presets: note that a single namespace is allowed per preset). +\end{pgfmanualentry} + +\begin{pgfmanualentry} + \extractcommand\runHereAndInPreambleOfCachedFiles\opt{\oarg{preset}}\marg{code}\@@ + \pgfmanualbody + (from v2.3) This will run |code| right now and it will add it in the preamble of the files that use the preset |preset| (defaults to |latex|). This is practical to avoid duplicating the code into |add to preamble|. For instance, if want the configure the font of the current document and of all latex documents at the same time, use: +\begin{codeexample}[code only] +\runHereAndInPreambleOfCachedFiles{ + \usepackage{fontspec} + \setmainfont{Times New Roman} +} + +\robExtConfigure{ + add to preset={latex}{ + use lualatex, % Make sure to compile with lualatex the cached images. + }, +} +\end{codeexample} +\end{pgfmanualentry} + +\begin{pgfmanualentry} + \extractcommand\definecolorAutoForward\marg{color}\marg{model}\marg{value}\opt{\oarg{additional style}}\@@ + \extractcommand\colorletAutoForward\marg{color}\marg{value}\opt{\oarg{additional style}}\@@ + \pgfmanualbody + Define colors like |\definecolor| and |\colorlet|, but also runs +\begin{verbatim} +if matches word={COLOR}{forward color=COLOR} +\end{verbatim} + to automatically forward the color (this will forward it only in latex-based presets). You can provide additional style to run as well in option. + \begin{codeexample}[vbox] + \definecolorAutoForward{myred}{HTML}{F01122} + \colorletAutoForward{myviolet}{blue!50!myred} + \begin{tikzpictureC} + \node[fill=myred,yshift=-1cm]{Uses myred}; + \end{tikzpictureC} + \begin{tikzpictureC} + \node[fill=myviolet,yshift=-1cm]{Myviolet}; + \end{tikzpictureC} + \end{codeexample} \end{pgfmanualentry} \begin{pgfmanualentry} @@ -2974,9 +3405,12 @@ This will also run the code to define it in the current document (practical for \extractkey/robExt/if matches=\marg{string}\marg{style to apply}\@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 + \extractkey/robExt/auto forward words\@nil + \extractkey/robExt/auto forward words namespace=\marg{namespace}\@nil \makeatother% \pgfmanualbody - Apply the corresponding style if the string (resp.\ regex in \LaTeX{}3 format) matches is matched in the content. As of today, |if matches word| and |if matches| are equivalent, but I plan to make |if watches word| more efficient\footnote{Scaling with $O(s)$ instead of $O(sn)$, where $s$ is the size of the string typed in |cacheMe| and $n$ is the number of times |if matches| is used, for instance when replacing multiple strings.}, and make it only match words that are made of consecutive letters |[A-Za-z]|. The regex version can be more expressive, but is significantly slower (it can easily multiply by 2 the compilation time). + |if matches *| applies the corresponding style if the string (resp.\ word or regex in \LaTeX{}3 format) matches is matched in the content. |if matches word| is more efficient that |if matches string| since the running time is independent of the number of times |if matches word| is called\footnote{Scaling with $O(s)$ instead of $O(sn)$, where $s$ is the size of the string typed in |cacheMe| and $n$ is the number of times |if matches| is used, for instance when replacing multiple strings.}. |if matches word| make it only match words that are mostly made of consecutive letters and numbers |[A-Za-z0-9]| (more precisely separated by elements in |\robExtWordSeparators|). The regex version can be more expressive, but is significantly slower (it can easily multiply by 2 the compilation time). \begin{codeexample}[vbox] \robExtConfigure{ add to preset={my python}{ @@ -3021,8 +3455,10 @@ Some text % \draw[->] (0,0) to[bend left] (my original node); \end{tikzpicture} \end{codeexample} +Note that to make |if matches word| even more efficient, you can call it before the preset. It will "register" the corresponding word with |register word| (itself a shortcut for |register word with namespace| with an empty namespace), and call |auto forward words| (shortcut for |auto forward words namespace={}|) in the |latex| preset (or in the current preset if we are already in a preset) to forward all words registered in the corresponding namespace (empty by default). Unless you want to forward different words to different presets, these more advanced functions involving namespaces should not be really useful for the end user. \end{pgfmanualentry} + \subsubsection{Pass compiled file to another template}\label{sec:passOutputToOtherTemplate} It can sometimes be handy to use the result of a previous cached file to cache another file, or to do anything else (e.g.\ it can also be practical to debug an issue). |name output| can be used to do that @@ -3490,7 +3926,7 @@ outputTxt="__ROBEXT_OUTPUT_PREFIX__-out.txt" outputTex="__ROBEXT_OUTPUT_PREFIX__-out.tex" outputPdf="__ROBEXT_OUTPUT_PDF__" \end{verbatim} -in order to quit when an error occurs, and to define two variables containing the path to the |pdf| file and to the file that is read by the |verbatim output| setting (that just apply a |\verbatiminput| on that file). Finally, it also creates the file |outputPdf| with |touch| in order to notify that the compilation succeeded. +in order to quit when an error occurs, and to define two variables containing the path to the |pdf| file and to the file that is read by the |verbatim output| setting (that just apply a |\verbatiminput| on that file). Finally, it also creates the file |outputPdf| with |echo "ok"| in order to notify that the compilation succeeded. In practice: \begin{codeAndResult} @@ -3530,6 +3966,230 @@ This file contains: You might also like to use |name output=yourfile| that will create two macros |\yourfile| and |\yourfileInCache|, equal respectively to the prefix |robExt-somehash| and |pathOfCache/robExt-somehash|. +\subsubsection{Online images} + +We also provide support for online images since v2.3. +\begin{pgfmanualentry} + \extractcommand\includegraphicsWeb\opt{<robExt options>}\opt{\oarg{includegraphics options}}\marg{url image}\@@ + \makeatletter% + \def\extrakeytext{style, } + \extractkey/robExt/web image\@nil + \extractkey/robExt/web image/wget\@nil + \extractkey/robExt/web image/curl\@nil + \makeatother% + \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}. +\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] +\robExtConfigure{ + add to preset={web image}{ + curl + }, +} + +Here is a cat downloaded online: \includegraphicsWeb[width=3cm]{http://placekitten.com/400/303}. +\end{codeexample} +By default, the |wget| and |curl| presets are defined as: +\begin{verbatim} +wget/.style={ + set compilation command={wget "__ROBEXT_MAIN_CONTENT__" -O "__ROBEXT_OUTPUT_PDF__"}, +}, +curl/.style={ + set compilation command={curl "__ROBEXT_MAIN_CONTENT__" -L -o "__ROBEXT_OUTPUT_PDF__"}, +}, +\end{verbatim} +\end{pgfmanualentry} + +\subsubsection{Gnuplot} + +We also provide support for gnuplot since v2.3. + +\begin{pgfmanualentry} + \makeatletter% + \def\extrakeytext{style, } + \extractkey/robExt/gnuplot\@nil + \extractkey/robExt/gnuplot/set terminal=\marg{terminal and options}\@nil + \extractkey/robExt/gnuplot/pdf terminal=\opt{pdf terminal options}\@nil + \extractkey/robExt/gnuplot/tikz terminal=\opt{tikz terminal options}\@nil + \makeatother% + \pgfmanualbody + Loads the |gnuplot| preset. By default, it uses the |pdf| terminal: +\begin{codeAndResult} +\begin{CacheMeCode}{gnuplot} +plot sin(x) +\end{CacheMeCode} +\end{codeAndResult} + +You can also specify the gnuplot options to the pdf terminal, like the size, using: +\begin{codeAndResult} +\begin{CacheMeCode}{gnuplot,pdf terminal={size 5cm,4cm}} +plot sin(x) +\end{CacheMeCode} +\end{codeAndResult} +You can also change the options of the |includegraphics| command used to include the image, using as usual: +\begin{codeAndResult} +\begin{CacheMeCode}{gnuplot,set includegraphics options={width=5cm,height=4cm}} +plot sin(x) +\end{CacheMeCode} +\end{codeAndResult} +You can also use other terminals using the |set terminal| option. For instance, you can use the png terminal and configure its size using: +\begin{codeAndResult} +\begin{CacheMeCode}{gnuplot,set terminal={png size 400,300}} +plot sin(x) +\end{CacheMeCode} +\end{codeAndResult} +The tikz terminal is a bit special since we do not want to use |\includegraphics| but |\input| to include the file. You can use it like this, just make sure: +\begin{itemize} +\item to have gnuplot installed with lua support. With nix you can get it with:\\ + |nix-shell -p '(gnuplot.override { withLua = true; })'| +\item Add |\usepackage{gnuplot-lua-tikz}| after making sure to run\\ + |gnuplot -e "set terminal tikz createstyle"| once to create the |.sty| file. +\end{itemize} +\begin{codeAndResult} +% \usepackage{gnuplot-lua-tikz} %% Generate with gnuplot -e "set terminal tikz createstyle" +\begin{CacheMeCode}{gnuplot,tikz terminal={size 5cm,4cm}} +plot sin(x) +\end{CacheMeCode} +\end{codeAndResult} +(Note that if you do not provide any option to |tikz terminal|, it will just use the default value in gnuplot, you can also add any other option to the terminal here.) By default, we only cache the gnuplot compilation, but the |\input| is not cached (so gnuplot will not be run again the next time, but tikz will run every time). But it can also be cached, just add |cache tikz| for that: +\begin{codeAndResult} +\robExtConfigure{ + add to preset={tikz}{ + % Run gnuplot -e "set terminal tikz createstyle" in the robustExternalize folder first + % to create this file, or, better, run this command in your local folder and use + % "copy file to cache" to copy the 3 generated files.: + add to preamble={\usepackage{gnuplot-lua-tikz}}, + }, + add to preset={gnuplot}{ + tikz terminal/.append style={ + cache tikz, + }, + }, +} +\begin{CacheMeCode}{gnuplot, tikz terminal={size 8cm,5cm}} +plot cos(x) +\end{CacheMeCode} +\end{codeAndResult} +We also automatically add |__ROBEXT_WAY_BACK__| to the |loadpath|, this way you do NOT need to type |plot "__ROBEXT_WAY_BACK__yourfile.dat"`| if the data is in the main folder. Note however that you need to add this file to the list of dependencies, otherwise it will not be recompiled if the file changes: +\begin{codeAndResult} +\begin{CacheMeCode}{gnuplot, dependencies={gnuplot_data.dat}} +plot "gnuplot_data.dat" +\end{CacheMeCode} +\end{codeAndResult} + +You can also use the current text width when using the tikz terminal using something like:\\ +|tikz terminal/.expanded={size \lenToCm{\textwidth},\lenToCm{.3\textwidth}}|\\ +Full example: +\begin{codeAndResult} +% Just to check size: +\noindent\rule{\textwidth}{1mm}\\% +\begin{CacheMeCode}{gnuplot, tikz terminal/.expanded={size \lenToCm{\textwidth},\lenToCm{.3\textwidth}}} +plot cos(x) +\end{CacheMeCode} +\end{codeAndResult} + +Similar to the tikz terminal, you can also use the |cairolatex| terminal like this: +\begin{codeAndResult} +\begin{CacheMeCode}{gnuplot,cairolatex terminal} +plot sin(x) +\end{CacheMeCode} +\end{codeAndResult} +As before, you can also specify the size, for instance it term of the current textwidth: +\begin{codeAndResult} +\begin{CacheMeCode}{gnuplot, cairolatex terminal/.expanded={size \lenToCm{\textwidth},\lenToCm{.3\textwidth}}} +plot cos(x) +\end{CacheMeCode} +\end{codeAndResult} +\end{pgfmanualentry} + +\begin{pgfmanualentry} + \extractcommand\robExtGpgetvar\marg{variable name}\@@ + \extractcommand\robExtGpgetvarNb\opt{*}\opt{\oarg{text, default 404}}\marg{variable name}\@@ + \pgfmanualbody + Like |\gpgetvar{}| from |gnuplot-lua-tikz| but does not produce an error if the variable does not exist yet (mostly useful if you compile in parallel as shown \mylink{https://github.com/leo-colisson/robust-externalize/issues/17}{here}). The first time, you will get a message like: + + \robExtGpgetvar{test_variable} + + and later this will compile fine: +\begin{codeAndResult} +\begin{CacheMeCode}{gnuplot, tikz terminal={providevars test_variable}} +test_variable = 5 +plot cos(test_variable * x) title "Test2" +\end{CacheMeCode} +Variable is \robExtGpgetvar{test_variable}. +\end{codeAndResult} +|\robExtGpgetvarNb| is similar, except that it prints a number 404 by default (useful if you use it in siunit) and is expandable (also \mylink{https://github.com/leo-colisson/robust-externalize/issues/17}{needed for siunit}). You can choose another text using the optional argument. The star version prints a warning in the log if the variable does not exist, but can only be used in LuaLaTeX: +\begin{codeAndResult} +Variable is \num[round-mode=figures,round-precision=3]{\robExtGpgetvarNb{non_existing_variable}}. +\end{codeAndResult} +Note that you will get a warning if the variable does not exist (yet?) only in LuaLaTeX as in other engines there is no \mylink{https://tex.stackexchange.com/questions/705299/print-message-in-expandable-macro/705304}{way to write in the log with an expandable function}. +\end{pgfmanualentry} + +\subsubsection{TikZit} + +\mylink{https://tikzit.github.io/}{TikZit} is a software to produce \tikzname{} images via a graphical interface. + +\begin{pgfmanualentry} + \extractcommand\cacheTikzit\opt{\oarg{preset}}\@@ + \extractcommand\cacheTikzitWithStyle\opt{\oarg{preset}}\marg{name of style file}\@@ + \pgfmanualbody + (since v2.5) These command will automatically cache the |\tikzfig| command (and therefore also |\ctikzfig|). While |\cacheTikzit| will let you create the preset (named |tikzit| preset by default), |\cacheTikzitWithStyle| will automatically create the preset style for you, loading your style given as parameter: + \begin{codeexample}[vbox] + %%%%%%% In your preamble: + \cacheTikzitWithStyle{new_zxstyle.tikzstyles} + + %%%%%%% In your document: + \tikzfig{demo} % figures/ is optional + \end{codeexample} + If you prefer to do it manually, you can do instead: + \begin{codeexample}[vbox] + %%%%%%% In your preamble: + \robExtConfigure{ + % First, we copy the tikzit-related files to the cache: + copy file to cache={tikzit.sty}, + copy file to cache={new_zxstyle.tikzstyles}, + % We create the tikzit preset loaded by default: + new preset={tikzit}{ + latex, + add to preamble={ + \usepackage{tikzit} + \input{new_zxstyle.tikzstyles} + }, + }, + } + % Cache all tikzit figures: + \cacheTikzit + + %%%%%%% In your document: + \tikzfig{figures/demo} + \end{codeexample} + \textbf{WARNING:} since the tikz picture uses |baseline| that relies on the current font size to vertically align the figure, and since we do not yet propagate the current font size to the cached picture (there are so many ways to configure the font in \LaTeX\dots{}), you should make sure that the |tikzit| preset sets the font size like in your main document, for instance via: + \begin{codeexample}[vbox] + %%%%%%% In your preamble: + \cacheTikzitWithStyle{new_zxstyle.tikzstyles} + \robExtConfigure{ + add to preset={tikzit}{ + add to latex options={12pt}, + }, + } + %%%%%%% In your document: + \tikzfig{demo} % figures/ is optional + \end{codeexample} + Note that |\tikzfig| accepts an additional optional argument for further options, for instance to forward locally a macro or to disable externalization: + \begin{codeexample}[vbox] + %%%%%%% In your preamble, make sure to load: + %% \usepackage{tikzit} + %% \input{new_zxstyle.tikzstyles} + %% if you want "disable externalization" to work. + \cacheTikzitWithStyle{new_zxstyle.tikzstyles} + %%%%%%% In your document: + \tikzfig<disable externalization>{demo} % figures/ is optional + \end{codeexample} +\end{pgfmanualentry} + \subsubsection{Custom languages} To add support to new languages, see \cref{sec:otherLanguages} for an example. @@ -3632,10 +4292,19 @@ It might be handy to cache automatically a given environment: we already provide to cache all tikz pictures (unless externalization is disabled), but we also provide tools to handle arbitrary environments. \begin{pgfmanualentry} - \extractcommand\robExtExternalizeAllTikzpicture\opt{\oarg{preset for tikz}}\opt{\oarg{preset for tikzpicture}}\opt{\oarg{delimiters}}\@@ + \extractcommand\robExtExternalizeAllTikzpictures\opt{\oarg{preset for tikz}}\opt{\oarg{preset for tikzpicture}}\opt{\oarg{delimiters}}\@@ \extractcommand\cacheTikz\opt{\oarg{preset for tikz}}\opt{\oarg{preset for tikzpicture}}\opt{\oarg{delimiters}}\@@ + \extractcommand\robExtDoNotExternalizeAllTikzpictures\@@ + \extractcommand\doNotCacheTikz\@@ + \makeatletter% + \def\extrakeytext{style, } + \extractkey/robExt/cache tikz\@nil + \extractkey/robExt/cache tikz\@nil + \extractkey/robExt/cache tikz 2 args=\marg{tikz preset}\marg{tikzpicture preset}\@nil + \extractkey/robExt/cache tikz 3 args=\marg{tikz preset}\marg{tikzpicture preset}\marg{delimiters cacheMe options}\@nil + \makeatother% \pgfmanualbody - This will automatically cache all tikz pictures (both are alias, |\cacheTizk| is available from v2.0). Since v2.0, we added the delimiters options and we allow to specify custom presets for |tikz| and |tikzpicture|, and we parse |\tikz| as well (the |tikz| preset is now used by |\tikz| by default while the |tikzpicture| preset is used by |tikzpicture|). Note that we add an additional optional argument to the tikz picture via its first argument delimited by |delimiters| (defaults to |<>|) to specify preset options, which can for instance be practical to disable externalization on individual pictures. Cf.\ \cref{sec:disableExternalization} to see an example of use. + (|cache tikz*| from 2.3, simply style alias) This will automatically cache all tikz pictures (the |*ExternalizeAllTikzpicturers| are alias of the |*cacheTikz| which is available from v2.0). Since v2.0, we added the delimiters options and we allow to specify custom presets for |tikz| and |tikzpicture|, and we parse |\tikz| as well (the |tikz| preset is now used by |\tikz| by default while the |tikzpicture| preset is used by |tikzpicture|). Note that we add an additional optional argument to the tikz picture via its first argument delimited by |delimiters| (defaults to |<>|) to specify preset options, which can for instance be practical to disable externalization on individual pictures. Cf.\ \cref{sec:disableExternalization} to see an example of use. \begin{codeexample}[width=0pt,vbox] \cacheTikz @@ -3687,7 +4356,27 @@ You can also change the default style loaded for tikz and tikzpicture (note that \node(A){In tikzpicture \hello}; \end{tikzpicture} \end{codeexample} +Note that the |cache tikz| styles (the variation just provide default values) is useful for instance if you only want to locally enable |cache tikz| for the |\input| of a style (this way you can cache the output of a cached thing\dots{}). For instance, |gnuplot| with a tikz terminal will compile into a tex file that will be inputted. But unless |\cacheTikz| is enabled, the inputted picture will not be cached (i.e. tikz will need to run). If you still want to cache it without enabling |\cacheTikz| globally, you can load this style in the |gnuplot| style, or do something like: + +|\doNotCacheTikz| just undo |\cacheTikz|: +\begin{codeexample}[width=0pt,vbox] +\def\whereIAm{(not cached)} +\robExtConfigure{ + add to preset={tikz}{ + add to preamble={ + \def\whereIAm{(cached)} + }, + }, +} +\cacheTikz +\doNotCacheTikz + +\tikz \node[fill=pink, rounded corners]{\whereIAm}; +\begin{tikzpicture} + \node[fill=pink, rounded corners]{\whereIAm}; +\end{tikzpicture} +\end{codeexample} \end{pgfmanualentry} \begin{pgfmanualentry} @@ -3809,7 +4498,7 @@ Let us say that you want to cache all elements of a given environment, like |min \end{pgfmanualentry} \paragraph{Defining its own macro without repeating its definition.} -Note that when you define yourself a macro function, the above structure might not be optimal as you need to define the macro in the main document (in case you disable the externalization) and in the template. One option to avoid repeated code is to write the definition in a common input file (see \cref{sec:dependencies}), but you might prefer to keep everything in a single file. In that case, you can simply wrap your function into |cacheMe| yourself. Say you want your function to draw a circle like, |\tikz \draw[fill=#2] (0,0) circle [radius=#1];|, then wrap in inside |CacheMe|: +Note that when you define yourself a macro function, the above structure might not be optimal as you need to define the macro in the main document (in case you disable the externalization) and in the template. One option to avoid repeated code is to write the definition in a common input file (see \cref{sec:dependencies}), use |\runHereAndInPreambleOfCachedFiles| or |forward=\macroToExport|. But sometimes, you might need to do more involved operations, for instance to add a file as a dependency etc. So another solution it to wrap yourself your function into |cacheMe| yourself. Say you want your function to draw a circle like, |\tikz \draw[fill=#2] (0,0) circle [radius=#1];|, then wrap in inside |CacheMe|: \begin{codeexample}[width=0mm,vbox] % D<>{} is an optional argument enclosed in <>, we use this to pass arguments to CacheMe @@ -3899,9 +4588,77 @@ bash JOBNAME-robExt-compile-missing-figures.sh \subsection{How to debug}\label{sec:debug} -If for some reasons you are unable to understand why a build fails, first check if you compiled your document with |-shell-escape| (not that this must appear \textbf{before} the filename). Then, you can look at the log file to get more advices: when a cached document is compiled, we always write the full compilation command before compiling the file in the log file. This way, you can easily check the content of the file and see why it fails to compile. The compilation errors are also displayed directly in the output, and you can read the log file for details. +If for some reasons you are unable to understand why a build fails, first check if you compiled your document with |-shell-escape| (not that this must appear \textbf{before} the filename). + +Since v2.3, we provide a more advanced error handling system in order to provide more meaningful errors to the user, and to write the full compilation output in the log in case of an error. We do so by wrapping commands around something like |(my command) > somelog.tex 2>&1|; therefore nothing is printed on screen during the compilation of the cached picture, you only get a message if there is an error. +\begin{pgfmanualentry} + \makeatletter% + \def\extrakeytext{style, } + \extractkey/robExt/do not redirect compilation output\@nil + \makeatother + If you want to come back to the old behavior, that just runs the command is it either set this option, or set |\def\robExtDoNotRedirectOutput{}| and |\def\robExtDoNotRedirectOutputInManuallyCompileMissingFigures{}| (the last one will only concern commands written to the |robExt-compile-missing-figures.sh|, which is used in manual mode and in parallel mode). +\end{pgfmanualentry} + +Starting from 2.3, we also automatically print an actual error message like: +\begin{verbatim} +! Package robExt Error: On line 20: the pdf file +(robExt) +robustExternalize/robExt-F27CD43D192AA2668A16DEAA616591FF.pdf +(robExt) is not present. The compilation command "cd +(robExt) robustExternalize/ && xelatex -halt-on-error +(robExt) "robExt-F27CD43D192AA2668A16DEAA616591FF.tex"" used +(robExt) to compile the environment on line 20 certainly +(robExt) failed with errors: +(robExt) vvvvvv +(robExt) ! Package tikz Error: Giving up on this path. Did +(robExt) you forget a semicolon?. +(robExt) +(robExt) ^^^^^^ +(robExt) See full logs below (you might need to press ENTER +(robExt) to go to the next error) or in +(robExt) +robustExternalize/robExt-F27CD43D192AA2668A16DEAA616591FF-compilation.log. +\end{verbatim} +You can see inside the location in the main tex file of the block containing the error, we provide also the compilation command that lead to this error, and the lines of the log containing the word ``error'' (case insensitive), as well as the path to the full log of the compilation of the inner command, that you can also see if you continue the compilation by pressing Enter or by looking at the full log file, for instance in TeXstudio. This smaller message is often good enough in LaTeX, but usually not very good for languages that do not print the word ``error''. In that case, add to your preset: -You might often get an error |! Missing $ inserted.|: this is typically when a placeholder was left unreplaced (e.g.\ you forgot to define it, import it, or you forgot to wrap a command in |\evalPlaceholder{}|): since \LaTeX{} is asked to typeset |__something__|, it thinks that you are trying to write a subscript, and asks you to start first the math mode. +\begin{pgfmanualentry} + \makeatletter% + \def\extrakeytext{style, } + \extractkey/robExt/print whole file in error message\@nil + \extractkey/robExt/do not print whole file in error message\@nil + \makeatother + (Add this only in preset, do NOT set it globally) + If you want to print the whole file in the small error message because you do not want to inspect the full log file, you can use the style |print whole file in error message|. This is enabled by default on the gnuplot, python and bash presets for instance, but disabled in \LaTeX{} has it makes little sense to print thousands of log lines in the error. +\end{pgfmanualentry} + +\begin{pgfmanualentry} + \makeatletter% + \def\extrakeytext{style, } + \extractkey/robExt/texstudio\@nil + \extractkey/robExt/logs should show as errors\@nil + \extractkey/robExt/prefix log message with=\marg{string to prepend to log message}\@nil + \extractkey/robExt/spacing between lines in log=\marg{content to separate lines in log}\@nil + \makeatother + Some IDE like TeXstudio will only print lines starting with an exclamation mark, assuming that they are spaced enough, so to make the compilation errors more visible in the log, we can start every line in the log with a |!| (you can set it globally via |logs should show as errors| that internally calls |prefix log message with|) and separate all lines with enough empty lines using |spacing between lines in log|. For simplicity, we create |texstudio| to automatically configure these values so that errors print nicely in texstudio via: +\begin{verbatim} +\robExtConfigure{texstudio} +\end{verbatim} +This way you will get errors like: +\begin{verbatim} +-------- We print now the full log -------- +! sh: ligne 1: gnuplot : commande introuvable +-------- End of the full log -------- +\end{verbatim} +and the line |sh: ligne 1: gnuplot : commande introuvable| will appear in your IDE without going in the log file. Note that this will only be printed for items that enable |print whole file in error message| not to pollute the errors space when the log contains many lines (e.g. \LaTeX{}), but this is automatically enabled on most default presets. +\end{pgfmanualentry} + +Then, you can look at the log files\footnote{Either the latex one or the |-compilation.log| file, and sometimes try in a terminal if you really can't see any useful error as it might be the case that the error is not printed in the log file, which occurs for instance if xparse is not installed on Windows and you use the compilation command in parallel.} to get more advices, and run yourself the compilation command printed in the above error message. This way, you can easily check the content of the file and see why it fails to compile. In version before 2.3, or if you use |do not redirect compilation output|, the compilation errors are also displayed directly in the output, and you can read the log file for details. + +You might often get an error: +\begin{verbatim} +! Missing $ inserted. +\end{verbatim} +This is typically when a placeholder was left unreplaced (e.g.\ you forgot to define it, import it, or you forgot to wrap a command in |\evalPlaceholder{}|): since \LaTeX{} is asked to typeset |__something__|, it thinks that you are trying to write a subscript, and asks you to start first the math mode. To get more advanced info during the compilation command, set the style |more logs| (that does |\def\robExtDebugMessage#1{\message{^^J[robExt] #1}}|) in order to print some messages on the replacement process (there is also |\robExtDebugWarning| for more important messages, but it prints the logs by default. @@ -3948,6 +4705,15 @@ You can also use |debug| (or its alias |print command and source|) in order to d Show all imported placeholders and their content. Use the start not to pause. \end{pgfmanualentry} +\begin{pgfmanualentry} + \makeatletter% + \def\extrakeytext{style, } + \extractkey/robExt/nb lines after error to show=\marg{nb lines to keep}\@nil + \extractkey/robExt/remove line number\@nil + \extractkey/robExt/do not remove line number\@nil + \makeatother + In order to make the small error message more meaningful (to avoid getting to the full log), we try to extract some errors automatically, reading lines staring with |!|, or containing |error| or |invalid|. But this line alone is often not enough, so you can use |nb lines after error to show| to determine the number of lines to show after (defaults to 3). |remove line number| will remove in the short error message any |l.| at the beginning of the line, otherwise some editors (at least emacs), will interpret it as a line number in the current file and will jump to a bad position (this is enabled by default). These options would work best if set globally when you use |compile in parallel|. +\end{pgfmanualentry} \subsection{Advanced optimizations}\label{sec:advancedOperations} @@ -4006,36 +4772,58 @@ In order to know when to stop replacing placeholders earlier (i.e.\ without runn If you disable the import mechanism (enabled by default), it will do an |\importAllPlaceholders| while evaluating the template. This is not recommended as it is quite inefficient, it is better to selectively import the group you need. \end{pgfmanualentry} -\begin{pgfmanualentry} - \makeatletter% - \def\extrakeytext{style, } - \extractkey/robExt/disable optimizations\@nil - \extractkey/robExt/enable optimizations\@nil - \makeatother% - \pgfmanualbody - -\end{pgfmanualentry} +\section{Frequent errors} + +\subsection{Xelatex and tabs} +It seems that xelatex (I never had issues with pdflatex and lualatex, but if you get this error on other engines you can try the same fix) \mylink{https://tex.stackexchange.com/a/14776/116348}{has a hard time dealing with tabs} if you do not compile with: +\begin{verbatim} +xelatex -8bit yourfile.tex +\end{verbatim} +If you see that tabs in your input file are turned into |^^I| in the source code, you should either add the |-8bit| option\dots{} or simply follow egreg's advice: +\begin{center} + \emph{The strategy is very simple: never use TAB when dealing with TeX. Reserve the TAB key to trigger autocompletion and your life will be happier.} +\end{center} +If you use TeXstudio that uses Tabs by default, you can disable this feature by setting these two options: +\begin{verbatim} +Options > Configure TeXstudio > Editor > Replace indentation tabs with spaces +Options > Configure TeXstudio > Editor > Replace tabs in text with spaces +\end{verbatim} \section{TODO and known bugs:} \begin{itemize} \item See how to deal with label, links and cite inside pictures (without disabling externalization). For label and links, I have a proof of concept, I ``just'' need to write it down. \url{https://tex.stackexchange.com/questions/695277/clickable-includegraphics-for-cross-reference-data} \item Deal with remember picture -\item Since externalizing is all about speed, it would be nice to do more benchmarks. But overall, it seems fairly optimized, at least when using the compiled presets. -\item Add a way to forward tikz styles and/or arbitrary code. -\item Create a more efficient/precise way to check if a word is present than string ``is in''. Indeed, for now for each color/... to export, we check if the string contains this word, which scales like $O(sn)$ where $s$ is the size of the string and $n$ is the number of colors etc to export. Instead, I'd like to run it in $O(s)$ by extracting all words a single time, and then checking if this word exists or not. This is not an issue for |\newcommandAutoforward|, but could be an issue when exporting many colors or |\genericAutoForward|. +\item Since externalizing is all about speed, it would be nice to do more benchmarks. But overall, it seems fairly optimized, at least when using the compiled presets. One option to gain in term of efficiency and simplicity might be to directly use the fact that the placeholders start and end with two dashes: a more efficient implementation might be doable using something like |\def\findPlaceholders#1__#2__{}| instead of string replacement. I will try to investigate in future releases. \end{itemize} -\section{Acknowledgments} - -I am deeply indebted to many users on \url{tex.stackexchange.com} that made the writing of this library possible. I can't list you all, but thank you so much! A big thanks also to the project \url{https://github.com/sasozivanovic/memoize/} from which I borrowed most of the code to automatically wrap commands. Thanks to kiryph for providing useful feedbacks. - \section{Changelog} \begin{itemize} -\item Future v2.3 (master on github, not released yet on CTAN): +\item v2.5: + \begin{itemize} + \item Add tikzit support + \item Add support for |--output-directory| + \end{itemize} +\item v2.4: + \begin{itemize} + \item Better error message in wget if file not found + \item Add support for arXiv with |rename backup files for arxiv|, |print source when saving|, |backup source for arxiv|, |forward counter force value| + \item Add |\robExtMv|, |\robExtCp|, |set compilation command create if no error| and |set compilation command move if no error|. + \item Add |copy file to cache| + \end{itemize} +\item v2.3: + \begin{itemize} + \item Fix bug in |more logs| and |less logs| + \item Make |if matches word| more efficient, independent on the number of runs, allowing registration, namespaces, etc. + \item Added |\definecolorAutoForward| and |\colorletAutoForward| + \item Add support for gnuplot + \item Added |\runHereAndInPreambleOfCachedFiles| + \item Clearer and more advanced error handling + \item Add support for online images with |\includegraphicsWeb| + \end{itemize} \item v2.2 \begin{itemize} \item v2.1 introduced two important bugs where hashes could not be used in a |\cacheMe| environment, and |auto forward| was not working if the string was containing an environment. This is fixed now. @@ -4078,6 +4866,9 @@ I am deeply indebted to many users on \url{tex.stackexchange.com} that made the \printindex \end{document} +% Luatex is needed, otherwise quotes ' are turned into another symbol in documentation +% https://github.com/leo-colisson/robust-externalize/issues/11 % Local Variables: -% TeX-command-extra-options: "-shell-escape -halt-on-error" +% TeX-command-extra-options: "--shell-escape -halt-on-error" +% TeX-engine: luatex % End:
\ No newline at end of file diff --git a/Master/texmf-dist/tex/latex/robust-externalize/robust-externalize.sty b/Master/texmf-dist/tex/latex/robust-externalize/robust-externalize.sty index 3bf14a9f955..0a36647946d 100644 --- a/Master/texmf-dist/tex/latex/robust-externalize/robust-externalize.sty +++ b/Master/texmf-dist/tex/latex/robust-externalize/robust-externalize.sty @@ -1,4 +1,4 @@ -\ProvidesPackage{robust-externalize}[2.2 Cache anything (tikz, latex, python) in a robust, efficient and pure way.] +\ProvidesPackage{robust-externalize}[2.5 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 @@ -11,7 +11,7 @@ \RequirePackage{iftex} % computing md5 sum differs on lualatex/pdflatex/xelatex \RequirePackage{xparse} % solve https://github.com/leo-colisson/robust-externalize/issues/6 \RequirePackage{atveryend} % to compile in parallel all images -\usepackage{etl} % For auto forward https://tex.stackexchange.com/a/700844/116348 +%\usepackage{etl} % For auto forward https://tex.stackexchange.com/a/700844/116348 %% TODO list: % - provide an easy way to use cross-ref, bibtex etc (we just need to add them when writing the file) without recompiling the whole document (we don't want to lose the cache everytime a new bib entry is added) but while preserving. @@ -20,14 +20,127 @@ %%%%%%%%%%% Utils %%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% If the file contains a space, the jobname gets quotes around... +% https://tex.stackexchange.com/questions/418670/avoid-quotation-marks-when-using-jobname-or-currfilename +\newcommand*{\jobnameNoQuotes}{} +\newcommand*{\setjobnameNoQuotes}{% + \edef\jobnameNoQuotes{\jobname}% + \expandafter\expandafter\expandafter\setjobnameNoQuotesaux + \expandafter\jobnameNoQuotes\expandafter"\jobnameNoQuotes"\relax +} +\newcommand*{\setjobnameNoQuotesaux}{} +\def\setjobnameNoQuotesaux#1"#2"#3\relax{\def\jobnameNoQuotes{#2}} +\setjobnameNoQuotes + \ExplSyntaxOn \NewDocumentCommand{\robExtIfWindowsTF}{mm}{% \sys_if_platform_windows:TF {#1}{#2}% } + +% This will be printed in from of the error message, mostly useful in texstudio that only print lines +% containing errors +\def\robExtPrefixLogMessage{} + +% Prints the next 4 lines after an error: +\def\robExtLinesAfterError{3} +\def\robExtRemoveLineNumber{} + +%% Needed for studio to prepend ! in front of all lines, including big ones. +%% https://tex.stackexchange.com/questions/705502/message-add-symbol-in-front-of-new-lines-even-if-there-is-a-line-break +\def\robExtMessageWithPrefixNumberLines{^^J}% +\cs_new:Nn \robExt__message_with_prefix:n { + \str_set:Nn \l_tmpa_str {#1} + \int_compare:nNnTF {\str_count:N \l_tmpa_str + \str_count:N \robExtPrefixLogMessage} > {78} { + \typeout{\robExtPrefixLogMessage \str_range:Nnn \l_tmpa_str {1} {78 - \str_count:N \robExtPrefixLogMessage}\robExtMessageWithPrefixNumberLines} + \robExt__message_with_prefix:x {\str_range:Nnn \l_tmpa_str {78 - \str_count:N \robExtPrefixLogMessage + 1} {-1}} + }{ + \typeout{\robExtPrefixLogMessage \l_tmpa_str \robExtMessageWithPrefixNumberLines} + } +} +\cs_generate_variant:Nn \robExt__message_with_prefix:n { x } + +% We might want to remove l.XX in front of lines as emacs interpret this as a line of error and goes to a wrong place. +\cs_new:Nn \robExt__clean_line_if_needed:n { + \ifdefined\robExtRemoveLineNumber% + \str_compare:eNeTF {\str_range:nnn {#1} {1} {2}} = {l.} { + \str_range:nnn {#1} {3} {-1}% + } { + #1 + } + \else + #1 + \fi +} +\cs_generate_variant:Nn \robExt__clean_line_if_needed:n { V } + +\cs_new:Nn \robExt_get_errors_from_file:n { + \ior_open:Nn \g__robExt_read_ior {#1}% + \cs_undefine:N \l__robExt_I_saw_an_error: + \str_clear_new:N \l__robExt_tmp_str% + \int_set:Nn \l_tmpa_int {0} + \ior_str_map_inline:Nn \g__robExt_read_ior {% + \ifdefined\robExtPrintWholeFile + \str_gput_right:Nx \l__robExt_tmp_str {\tl_to_str:n{##1}^^J}% + \cs_set:Nn \l__robExt_I_saw_an_error: {} + \int_set:Nn \l_tmpa_int {\robExtLinesAfterError} + \else + \str_if_in:xnTF {\str_lowercase:n{##1}} {error} { + \str_gput_right:Nx \l__robExt_tmp_str {\robExt__clean_line_if_needed:V{\tl_to_str:n{##1}^^J}}% + \cs_set:Nn \l__robExt_I_saw_an_error: {} + \int_set:Nn \l_tmpa_int {\robExtLinesAfterError} + } { + \str_if_in:xnTF {\str_lowercase:n{##1}} {invalid} { + \str_gput_right:Nx \l__robExt_tmp_str {\robExt__clean_line_if_needed:V{\tl_to_str:n{##1}^^J}}% + \cs_set:Nn \l__robExt_I_saw_an_error: {} + \int_set:Nn \l_tmpa_int {\robExtLinesAfterError} + } { + % Check if it starts with a ! (usually latex errors start with this symbol) + \str_compare:eNeTF {\str_range:nnn {##1} {1} {1}} = {!} { + \str_gput_right:Nx \l__robExt_tmp_str {\robExt__clean_line_if_needed:V{\tl_to_str:n{##1}^^J}}% + \cs_set:Nn \l__robExt_I_saw_an_error: {} + \int_set:Nn \l_tmpa_int {\robExtLinesAfterError} + }{ + % Check if we saw an error not too long ago: + \int_compare:nNnTF {\l_tmpa_int} > {0} { + \str_gput_right:Nx \l__robExt_tmp_str {\robExt__clean_line_if_needed:V{\tl_to_str:n{##1}^^J}}% + } { + \str_set:Nn \l_tmp_str {##1} + } + \int_set:Nn \l_tmpa_int {\l_tmpa_int - 1} + } + } + } + \fi + }% + \cs_if_exist:NTF \l__robExt_I_saw_an_error: {} { + % We print the last line if we found no error. Might be useful for errors like: + % sh: ligne 1: gnuplot : commande introuvable. + \str_set_eq:NN \l__robExt_tmp_str \l_tmp_str + } +} + +\cs_new:Nn \robExt_write_file_in_log:n { + \ior_open:Nn \g__robExt_read_ior {#1}% + \ior_str_map_inline:Nn \g__robExt_read_ior {% + \ifdefined\robExtPrintWholeFile + \ifdefempty{\robExtPrefixLogMessage}{ + \typeout{\tl_to_str:n{##1}}% + }{ + \robExt__message_with_prefix:x {\tl_to_str:n{##1}} + } + \else + \typeout{\tl_to_str:n{##1}}% + \fi + }% +} + +\cs_generate_variant:Nn \robExt_write_file_in_log:n { x } + \ExplSyntaxOff % Debug function: by default, do nothing \def\robExtDebugMessage#1{} +\def\robExtDebugInfo#1{\message{#1}} \def\robExtDebugWarning#1{\message{^^J[robExt warning] #1}} \NewDocumentCommand{\robExtConfigure}{m}{% @@ -53,6 +166,7 @@ \cs_generate_variant:Nn \str_replace_all:Nnn { Nnx, Nnv, cnx, cxx, NnV, Nnx, Nnv } \cs_generate_variant:Nn \str_set:Nn { NV } +\cs_generate_variant:Nn \str_if_in:nnTF { xnTF } \cs_generate_variant:Nn \tl_rescan:nn { nv } \cs_generate_variant:Nn \tl_set_rescan:Nnn { Nnv, NnV } @@ -65,6 +179,7 @@ \cs_generate_variant:Nn \ior_str_get:NN { Nc } \cs_generate_variant:Nn \file_if_exist:nTF { xTF } +\cs_generate_variant:Nn \file_mdfive_hash:n { x } \cs_generate_variant:Nn \cs_replacement_spec:N { c } \cs_generate_variant:Nn \cs_argument_spec:N { c } @@ -110,25 +225,33 @@ %% In LaTeX3 error messages are defined here, this allows people to translate them, filter them, change them, %% make them more verbose etc. \msg_new:nnn {robExt}{underscore in name}{Error:~all~the~placeholders~should~contain~two~consecutive~underscores~in~their~name.} -\msg_new:nnn {robExt}{placeholder not existing}{[robExtGetPlaceholderInResultFromSinglePlaceholder] ~ The ~ placeholder ~ #3 ~ does ~ not ~ exists.} +\msg_new:nnn {robExt}{placeholder not existing}{[robExtGetPlaceholderInResultFromSinglePlaceholder] ~ The ~ placeholder ~ #1 ~ does ~ not ~ exists.} \msg_new:nnn {robExt}{forgot template}{You ~ are ~ writing ~ __ROBEXT_TEMPLATE__ ~ to ~ your ~ file: ~ seems ~ like ~ you ~ forgot ~ to ~ define ~ your ~ template ~ or ~ set ~ a ~ preset} \msg_new:nnn {robExt}{need shell escape}{You ~ need ~ to ~ compile ~ with ~ shell-escape ~ as ~ in: ~ "pdflatex ~ -shell-escape ~ yourfile.tex" ~ to ~ be ~ able ~ to ~ compile ~ automatically ~ the ~ figures} +\msg_new:nnn {robExt}{need shell escape with}{You ~ need ~ to ~ compile ~ with ~ shell-escape ~ as ~ in: ~ "pdflatex ~ -shell-escape ~ yourfile.tex" ~ to ~ be ~ able ~ to ~ #1} \msg_new:nnn {robExt}{need shell escape parallel}{You ~ need ~ to ~ compile ~ with ~ shell-escape ~ as ~ in: ~ "pdflatex ~ -shell-escape ~ yourfile.tex" ~ to ~ be ~ able ~ to ~ compile ~ automatically ~ the ~ figures ~ in ~ parallel} -\msg_new:nnn {robExt}{missing compiled pdf}{The ~ pdf ~ file ~ #1.pdf ~ is ~ not ~ present. ~ The ~ compilation ~ command ~ certainly ~ failed, ~ see ~ logs ~ above ~ or ~ in ~ #1.log.} -\msg_new:nnn {robExt}{missing compiled pdf parallel}{The ~ pdf ~ file ~ #1.pdf ~ is ~ not ~ present. ~ The ~ compilation ~ command ~ "#3"~used~to~compile~the~environment~on~line~#2~ certainly ~ failed, ~ see ~ logs ~ above ~ or ~ in ~ #1.log.} +\msg_new:nnn {robExt}{missing compiled pdf parallel}{The~compilation~of~block~at~line~#2~failed~as~the~following ~ file~is~missing: ~ #1.pdf. ~ The ~ compilation ~ command ~ "#3"~used~to~compile~the~environment~on~line~#2~ certainly ~ failed, ~ see ~ logs ~ above ~ or ~ in ~ #1.log.} +\msg_new:nnn {robExt}{dependency does not exist}{The~dependency~#1~does~not~exist.} + +\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}{auto forward not in cachemecode}{Auto~forward~is~less~efficient~in~cacheMeCode.} +\msg_new:nnn {robExt}{file does not exist}{The~file~#1~does~not~exist.} + % warnings \msg_new:nnn {robExt}{recommend two underscores in placeholders}{We~recommend~to~use~only~placeholders~containing~two~consecutive~underscores~in~their~name} \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.} % dummy placeholders added if image is not present \def\robExtImagePlaceholderIfManualMode{ \framebox[\linewidth] { \begin{minipage}{\linewidth} - \textbf{Manual ~ mode: ~ either ~ compile ~ with ~ \texttt{-shell-escape} ~ or ~ compile:\newline \texttt{\robExtAddCachePathAndName{\robExtFinalHash.tex}}\newline via ~ \newline \texttt{\l__robExt_current_compilation_command_str}\newline or ~ call ~ \newline\texttt{bash ~ \jobname-\robExtAddPrefixName{compile-missing-figures.sh}}\newline to ~ compile ~ all ~ missing ~ figures.} + \textbf{Manual ~ mode: ~ either ~ compile ~ with ~ \texttt{-shell-escape} ~ or ~ compile:\newline \texttt{\robExtAddCachePathAndName{\robExtFinalHash.tex}}\newline via ~ \newline \texttt{\l__robExt_current_compilation_command_str}\newline or ~ call ~ \newline\texttt{bash ~ \jobnameNoQuotes-\robExtAddPrefixName{compile-missing-figures.sh}}\newline to ~ compile ~ all ~ missing ~ figures.} \end{minipage} } } @@ -137,7 +260,7 @@ \framebox[\linewidth] { \begin{minipage}{\linewidth} - \textbf{Falling ~ back ~ to ~ draft ~ mode: ~ either ~ compile ~ with ~ \texttt{-shell-escape} ~ or ~ compile:\newline \texttt{\robExtAddCachePathAndName{\robExtFinalHash.tex}}\newline via ~ \newline \texttt{\l__robExt_current_compilation_command_str}\newline or ~ call ~ \newline\texttt{bash ~ \jobname-\robExtAddPrefixName{compile-missing-figures.sh}}\newline to ~ compile ~ all ~ missing ~ figures.} + \textbf{Falling ~ back ~ to ~ draft ~ mode: ~ either ~ compile ~ with ~ \texttt{-shell-escape} ~ or ~ compile:\newline \texttt{\robExtAddCachePathAndName{\robExtFinalHash.tex}}\newline via ~ \newline \texttt{\l__robExt_current_compilation_command_str}\newline or ~ call ~ \newline\texttt{bash ~ \jobnameNoQuotes-\robExtAddPrefixName{compile-missing-figures.sh}}\newline to ~ compile ~ all ~ missing ~ figures.} \end{minipage} } } @@ -163,9 +286,10 @@ %%% Important: this script is NOT executed by this file, it is only created so that users can remove unused previously cached files by running this script %%% outside of LaTeX. %%% Create scripts to remove useless files: -%%% Note that we don't override the script if it exists on purpose (the user might have changed it to fits their needs) -\begin{filecontents}[noheader]{robExt-remove-old-figures.py} +\begin{filecontents}[noheader,overwrite]{robExt-remove-old-figures.py} #!/usr/bin/env python3 +## This file is automatically generated, so do not change that file directly +## Instead, if you need to change it, copy it with a different name and edit the copy import os import re import glob @@ -214,6 +338,63 @@ if __name__ == '__main__': main() \end{filecontents} +\begin{filecontents}[noheader,overwrite]{robExt-prepare-for-arxiv.py} +#!/usr/bin/env python3 +## This file is automatically generated, so do not change that file directly +## Instead, if you need to change it, copy it with a different name and edit the copy + +## This file must be called when sending stuff to the arxiv website, that would otherwise remove the .pdf +## in presence of a .tex. +## This script will simply rename the .tex files in robustExternalize into .tex-backup. +## Then, you should call "rename backup files for arxiv" to rename back the files. +import os +import re +import glob +# Just run this script in order to remove all old figures not listed in robExt-all-figures.txt. + +# Note that this part is not extracted from the pdf file since it might be different on a previous run. You can however hardcode +# it here, your updated script will not be overriden unless you remove it yourself. +prefixes = [ "robExt-" ] +folders = [ "robustExternalize" ] + +def main(): + listOfFilesToMove = [] + listOfFilesAlreadyAdded = set() + for folder in folders: + for root, dirs, files in os.walk(folder): + for f in files: + if f.endswith(".tex"): + pdf = os.path.splitext(f)[0] + ".pdf" + if os.path.exists(os.path.join(root,pdf)): + listOfFilesToMove.append((os.path.join(root,f), os.path.join(root,f + "-backup"))) + if f.endswith(".tex-backup"): + tex = os.path.splitext(f)[0] + ".tex" + listOfFilesAlreadyAdded.add(os.path.join(root,tex)) + for (f,f2) in listOfFilesToMove: + print(f"-- {f} moved to {f2}") + listOfFilesAlreadyAdded.discard(f) + for f in listOfFilesAlreadyAdded: + print(f"-- {f} already moved") + print(f"Above are the files to move or already moved, are you sure you want to proceed? [y/N] (based on prefixes {prefixes})") + x = input().strip() + if x not in ["y", "Y"]: + print("All right, we abort.") + exit(1) + for (f,f2) in listOfFilesToMove: + os.replace(f, f2) + print(f"Moved {f} to {f2}") + with open('robExt-arxiv-files-to-rename.txt', 'w') as fd: + for (f,f2) in listOfFilesToMove: + fd.write(f'{f}\n') + for f in listOfFilesAlreadyAdded: + fd.write(f'{f}\n') + print('The files have been written to robExt-arxiv-files-to-rename.txt.') + print('Add \\robExtConfigure{rename backup files for arxiv} in your tex file.') + +if __name__ == '__main__': + main() +\end{filecontents} + \ExplSyntaxOn %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -258,6 +439,9 @@ if __name__ == '__main__': %%%%%%%%%%% Paths %%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% For support of --output-directory +\def\robExtPrefixAllCompilationCommands{\ifdefined\robExtOutputDirectory cd~"\robExtOutputDirectory"~&&~\fi} + \def\robExtPrefixFilename{robExt-} \NewExpandableDocumentCommand{\robExtAddCachePathAndName}{m}{% @@ -272,6 +456,48 @@ if __name__ == '__main__': \fi#1% } +% mv in unix and move in windows: +\robExtIfWindowsTF{ + \def\robExtMv{move} + \def\robExtCp{copy} +}{ + \def\robExtMv{mv} + \def\robExtCp{cp} +} + +\NewDocumentCommand{\robExtBackupSource}{m}{% + % arXiv will remove the .pdf file if the .tex is present. Solution: we move the .tex into .tex-backup + % and restore it during compilation. We could also backup the pdf but usually this file is bigger and will contain + % binary data that might not be so easy to copy in pure tex (in arxiv shell escape is disabled so we cannot + % use cp). + % \show\inBackupSource + \robExtRunCmdIfPossible{% + \robExtMv\space"\robExtAddCachePath{#1}"~% + "\robExtAddCachePath{#1-backup}"% + }% +} + +\NewDocumentCommand{\robExtRunCmdIfPossible}{m}{% + % Check if the output directory exists + \bool_if:nTF { \sys_if_shell_unrestricted_p: || \cs_if_exist_p:N \robExtForceCompilation} + { + \ifdefined\robExtManualMode + \message{Warning:~running~in~manual~mode~so~we~cannot~run~the~command~``#1''.} + \iow_now:Nx \g__robExt_write_manually_compile_all_missing_figures_iow {#1} + \else + \message{Running:~\robExtPrefixAllCompilationCommands #1}% + \sys_shell_now:x {\robExtPrefixAllCompilationCommands #1} + \fi + }{ + \ifdefined\robExtFallbackManualMode + \message{Warning:~shell~escape~fallback~to~manual~mode~so~cannot~run~#1.} + \iow_now:Nx \g__robExt_write_manually_compile_all_missing_figures_iow {#1} + \else + \msg_error:nnx{robExt}{need shell escape with}{run~#1} + \fi + } +} + \NewDocumentCommand{\robExtCheckIfPrefixFolderExists}{}{ % Check if the output directory exists \ifdefined\robExtCacheFolder @@ -279,17 +505,74 @@ if __name__ == '__main__': { \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.} + \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 {mkdir ~ -p ~ \robExtCacheFolder} + \sys_shell_now:x {\robExtPrefixAllCompilationCommands mkdir ~ -p ~ \robExtCacheFolder} \fi \fi }{ - \message{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 } +\NewDocumentCommand{\robExtCopyFileToCache}{m}{ + \robExtCheckIfPrefixFolderExists% + \file_if_exist:xTF {#1}{}{ + \msg_error:nnx{robExt}{file does not exist}{#1~(to~copy~to~cache)} + } + \file_if_exist:xTF {\robExtAddCachePath{#1}}{ + \robExtDebugInfo{The~file~\robExtAddCachePath{#1}~already~exists,~let~us~compare~md5~sum.} + \str_set:Nx \l_tmpa_str {\file_mdfive_hash:n{#1}} + \str_set:Nx \l_tmpb_str {\file_mdfive_hash:n{\robExtAddCachePath{#1}}} + \str_compare:eNeTF {\l_tmpa_str} = {\l_tmpb_str} { + \robExtDebugInfo{The~file~\robExtAddCachePath{#1}~has~the~good~md5~hash.} + }{ + \robExtDebugInfo{The~md5~hashes~of~#1~and~\robExtAddCachePath{#1}~are~different(\l_tmpa_str \space vs \space \l_tmpb_str),~let~us~copy~the~file.}% + \robExtRunCmdIfPossible{\robExtCp \space "#1"~"\robExtAddCachePath{#1}"}% + } + }{ + \robExtDebugInfo{The~file~\robExtAddCachePath{#1}~does~not~exist,~let~us~copy~it:} + \robExtRunCmdIfPossible{\robExtCp \space "#1"~"\robExtAddCachePath{#1}"}% + } +} +\let\copyFileToCache\robExtCopyFileToCache + +% \robExtRenameBackupFilesForArxiv{robExt-arxiv-files-to-rename.txt} takes all elements listed in the .tex file +% (like robustExternalize/robExt-F83AB245D8043E653A89489C2E25AA6A.tex) and rename the +% robustExternalize/robExt-F83AB245D8043E653A89489C2E25AA6A.tex-backup into +% robustExternalize/robExt-F83AB245D8043E653A89489C2E25AA6A.tex +% (needed for arxiv) +% This is done, importantly, without shell escape as arxiv does not have shell escape. +\NewDocumentCommand{\robExtRenameBackupFilesForArxiv}{O{-backup}m}{ + \file_if_exist:xTF{#2} { + \robExtDebugInfo{We~will~read~the~file~#2.} + \ior_open:Nn \g__robExt_read_ior {#2}% + %% We avoid creating a new ior since the number of available files is limited in tex + \seq_clear:N \l_tmpa_seq + \ior_str_map_inline:Nn \g__robExt_read_ior {% + \file_if_exist:xTF{##1#1} { + \seq_put_right:Nn \l_tmpa_seq {##1} + }{ + \robExtDebugInfo{Warning:~##1#1~does~not~exist,~so~we~will~not~rename~back~this~file~for~arXiv.} + } + }% + \ior_close:N \g__robExt_read_ior + \seq_map_inline:Nn \l_tmpa_seq { + \robExtDebugInfo{We~copy~the~source~##1#1~to~##1^^J}% + % First we read the file: + \ior_open:Nn \g__robExt_read_ior {##1#1}% + \iow_open:Nx \g__robExt_write_iow {##1} + \ior_str_map_inline:Nn \g__robExt_read_ior {% + \iow_now:Nx \g__robExt_write_iow {\tl_to_str:n{####1}}% + }% + \iow_close:N \g__robExt_write_iow + \ior_close:N \g__robExt_read_ior + } + }{ + \robExtDebugInfo{Warning:~#2~does~not~exist,~so~we~will~not~rename~back~elements~for~arXiv.} + } +} \NewExpandableDocumentCommand{\robExtGetPrefixPath}{}{% \ifdefined\robExtCacheFolder% @@ -317,9 +600,9 @@ if __name__ == '__main__': %% TODO: create a makefile. \iow_new:N \g__robExt_write_list_all_figures_iow %% Create a file robExt-all-figures.txt with the list of .tex files -\iow_open:Nx \g__robExt_write_list_all_figures_iow {\jobname-\robExtAddPrefixName{all-figures.txt}} +\iow_open:Nx \g__robExt_write_list_all_figures_iow {\jobnameNoQuotes-\robExtAddPrefixName{all-figures.txt}} \iow_new:N \g__robExt_write_manually_compile_all_missing_figures_iow -\iow_open:Nx \g__robExt_write_manually_compile_all_missing_figures_iow {\jobname-\robExtAddPrefixName{compile-missing-figures.sh}} +\iow_open:Nx \g__robExt_write_manually_compile_all_missing_figures_iow {\jobnameNoQuotes-\robExtAddPrefixName{compile-missing-figures.sh}} % Contains the list of dependency files (useful to compute the final md5sum) \seq_new:N \l__robExt_dependencies_str @@ -450,7 +733,7 @@ if __name__ == '__main__': } \let\evalPlaceholderNoReplacement\robExtEvalPlaceholderNoReplacement -\NewDocumentCommand{\robExtGetPlaceholderNoReplacement}{m}{ +\NewExpandableDocumentCommand{\robExtGetPlaceholderNoReplacement}{m}{ \str_use:c { l__robExt_placeholder_#1_str } } \let\getPlaceholderNoReplacement\robExtGetPlaceholderNoReplacement @@ -545,7 +828,7 @@ if __name__ == '__main__': %% Warning: only LaTeX-friendly code should be placed here, as LaTeX does not preserve some symbols and adds spaces %% Tested! %% The star version does NOT import the placeholder (useful for efficiency reasons) -\NewDocumentCommand{\robExtPlaceholderFromContent}{smm}{ +\NewDocumentCommand{\robExtPlaceholderFromContent}{sm+m}{ \robExt_set_hash_robust:cn { l__robExt_placeholder_#2_str } {#3} \checkIfPlaceholderNameIsLegal{#2} \ifdefined\robExtCompletelyDisableWholeImportSystem\else% Mostly for time optimizations... @@ -662,10 +945,10 @@ if __name__ == '__main__': %% https://tex.stackexchange.com/a/680259/116348 might work and be more efficient, but it might be less reliable %% and definitely more complicated and error prone. Instead, we write to a file and read the result. %% TODO: try to do it using verbatim, might be trivial or complicated, not sure, maybe see https://tex.stackexchange.com/questions/555359/reading-lines-verbatim-into-a-sequence-variable - \XSIMfilewritestart{\jobname-robExt-tmp-file-you-can-remove.tmp}% + \XSIMfilewritestart{\jobnameNoQuotes-robExt-tmp-file-you-can-remove.tmp}% }{% \XSIMfilewritestop% - \ior_open:Nn \g__robExt_read_ior {\jobname-robExt-tmp-file-you-can-remove.tmp}% + \ior_open:Nn \g__robExt_read_ior {\jobnameNoQuotes-robExt-tmp-file-you-can-remove.tmp}% %% Put the file in l__robExt_tmp_contain_file_str \str_clear_new:N \l__robExt_tmp_str% \ior_str_map_inline:Nn \g__robExt_read_ior {% @@ -762,10 +1045,10 @@ if __name__ == '__main__': %% This will copy "some code" in the cache, and set MYLIBPATH to the name of the file in the cache like %% MYLIBPATH = robExt-abc.py \NewDocumentEnvironment{RobExtPlaceholderPathFromCode}{sO{}m}{ - \XSIMfilewritestart*{\jobname-robExt-tmp-file-you-can-remove.tmp} + \XSIMfilewritestart*{\jobnameNoQuotes-robExt-tmp-file-you-can-remove.tmp} }{ \XSIMfilewritestop - \ior_open:Nn \g__robExt_read_ior {\jobname-robExt-tmp-file-you-can-remove.tmp} + \ior_open:Nn \g__robExt_read_ior {\jobnameNoQuotes-robExt-tmp-file-you-can-remove.tmp} %% Put the file in \l__robExt_tmp_contain_file_str \str_clear_new:N \l__robExt_tmp_contain_file_str \ior_str_map_inline:Nn \g__robExt_read_ior { @@ -856,7 +1139,7 @@ if __name__ == '__main__': \cs_if_exist:cTF { l__robExt_placeholder_#3_str }{ \str_set_eq:Nc { \l_robExt_result_str }{ l__robExt_placeholder_#3_str } }{ - \msg_error:nn{robExt}{placeholder not existing} + \msg_error:nnx{robExt}{placeholder not existing}{#3} } \robExtDebugMessage{Starting~to~replace~placeholder~#3~to~get~\l_robExt_result_str} \__robExt_replace_until_impossible: @@ -1039,7 +1322,7 @@ if __name__ == '__main__': % First we remove existing occurrences (useful to avoid listing the same macro more than once % if we redefine a macro): \seq_map_inline:Nn \l__robExt_tmp_seq { - \message{Adding ##1 to #1} + \robExtDebugMessage{Adding ##1 to #1} \seq_remove_all:cn {l__robExt_placeholder_group_#1_seq} {##1} \seq_put_right:cn {l__robExt_placeholder_group_#1_seq} {##1} } @@ -1126,6 +1409,7 @@ if __name__ == '__main__': \seq_new:N \l__robExt_list_groups_seq % contains the list of all groups +%%%% \robExtRegisterGroupPlaceholders{bash} adds "bash" to the list of existing groups \NewDocumentCommand{\robExtRegisterGroupPlaceholders}{m}{ \seq_if_exist:cTF {l__robExt_placeholder_group_#1_seq}{}{ \seq_clear_new:c {l__robExt_placeholder_group_#1_seq} @@ -1187,7 +1471,11 @@ if __name__ == '__main__': } \NewDocumentCommand{\robExtAddDependency}{m}{ - \seq_put_left:Nx \l__robExt_dependencies_str {#1} + \file_if_exist:nTF {#1} { + \seq_put_left:Nx \l__robExt_dependencies_str {#1} + } { + \msg_error:nnx{robExt}{dependency does not exist}{#1} + } } \NewDocumentCommand{\robExtDebugDependency}{}{ @@ -1356,7 +1644,12 @@ if __name__ == '__main__': % not want to recompile when the file changes \robExtUseHookJustBeforeWritingFiles% %% We add the figure in the list of files. - \iow_now:Nx \g__robExt_write_list_all_figures_iow {\robExtAddPrefixName{\robExtFinalHash.tex}} + % Avoid writing the same command multiple times (might occur in environments that execute the + % same command multiple times like align etc) + \cs_if_exist:cTF {l__robExt_write_list_all_figures_\robExtAddPrefixName{\robExtFinalHash.tex}:}{}{ + \iow_now:Nx \g__robExt_write_list_all_figures_iow {\robExtAddPrefixName{\robExtFinalHash.tex}} + \cs_gset:cn {l__robExt_write_list_all_figures_\robExtAddPrefixName{\robExtFinalHash.tex}:} {} + } %% We can now set the placeholders, and recompute the final value of the file: %% I was doing before \robExtPlaceholderFromContent + \robExtEvalPlaceholderInplace, but it is too slow \str_set:Nx \l_tmp_output_prefix_str {\robExtAddPrefixName{\robExtFinalHash}} @@ -1367,21 +1660,45 @@ if __name__ == '__main__': \str_replace_all:Nnx \l_robExt_result_str {__ROBEXT_WAY_BACK__}{\robExtCacheFolderWayBack} \str_replace_all:Nnx \l_robExt_result_str {__ROBEXT_CACHE_FOLDER__}{\robExtCacheFolder} \file_if_exist:xTF{\robExtAddCachePathAndName{\robExtFinalHash.tex}}{ - \message{The\space file\space \robExtAddCachePathAndName{\robExtFinalHash.tex} \space already\space exists.^^J} + \robExtDebugInfo{The\space file\space \robExtAddCachePathAndName{\robExtFinalHash.tex} \space already\space exists.^^J} }{ - \str_if_eq:VnTF { \l_robExt_result_str }{__ROBEXT_TEMPLATE__} { - \msg_error:nn{robExt}{forgot template} + % arXiv removes the .pdf if the .tex is present... so we need to manually remove the .tex file. + \file_if_exist:xTF{\robExtAddCachePathAndName{\robExtFinalHash.pdf}}{ + \robExtDebugInfo{The\space file\space \robExtAddCachePathAndName{\robExtFinalHash.pdf} \space already\space exists.^^J} + % Sometimes we still want to refer to the original tex file, for instance to input the source. + % So if a file .tex-backup exists, we copy it back to .tex: + \file_if_exist:xTF{\robExtAddCachePathAndName{\robExtFinalHash.tex-backup}} { + \robExtDebugInfo{We~copy~the~source~\robExtFinalHash.tex-backup~to~\robExtFinalHash.tex^^J}% + % First we read the file: + \ior_open:Nn \g__robExt_read_ior {\robExtAddCachePathAndName{\robExtFinalHash.tex-backup}}% + \iow_open:Nx \g__robExt_write_iow {\robExtAddCachePathAndName{\robExtFinalHash.tex}} + \ior_str_map_inline:Nn \g__robExt_read_ior {% + \iow_now:Nx \g__robExt_write_iow {\tl_to_str:n{##1}}% + }% + \iow_close:N \g__robExt_write_iow + \ior_close:N \g__robExt_read_ior + }{} }{ - % Check if the output directory exists - \robExtCheckIfPrefixFolderExists - \iow_open:Nx \g__robExt_write_iow {\robExtAddCachePathAndName{\robExtFinalHash.deps}} - \iow_now:NV \g__robExt_write_iow \l__robExt_dependencies_mdfive_str - \iow_close:N \g__robExt_write_iow - %% Save the final file: - \iow_open:Nx \g__robExt_write_iow {\robExtAddCachePathAndName{\robExtFinalHash.tex}} - \iow_now:NV \g__robExt_write_iow \l_robExt_result_str - \iow_close:N \g__robExt_write_iow - \message{Source ~ saved ~ in ~ \robExtAddCachePathAndName{\robExtFinalHash.tex}.} + \str_if_eq:VnTF { \l_robExt_result_str }{__ROBEXT_TEMPLATE__} { + \msg_error:nn{robExt}{forgot template} + }{ + % Check if the output directory exists + \robExtCheckIfPrefixFolderExists + \iow_open:Nx \g__robExt_write_iow {\robExtAddCachePathAndName{\robExtFinalHash.deps}} + \iow_now:NV \g__robExt_write_iow \l__robExt_dependencies_mdfive_str + \iow_close:N \g__robExt_write_iow + %% Save the final file: + \iow_open:Nx \g__robExt_write_iow {\robExtAddCachePathAndName{\robExtFinalHash.tex}} + \iow_now:NV \g__robExt_write_iow \l_robExt_result_str + \iow_close:N \g__robExt_write_iow + \robExtDebugInfo{Source ~ saved ~ in ~ \robExtAddCachePathAndName{\robExtFinalHash.tex}.} + \ifdefined\robExtPrintSourceWhenSaving + \typeout{Content~of~the~deps~file~\robExtAddCachePathAndName{\robExtFinalHash.deps}:^^J} + \robExt_write_file_in_log:x {\robExtAddCachePathAndName{\robExtFinalHash.deps}} + \typeout{Content~of~the~source~file~\robExtAddCachePathAndName{\robExtFinalHash.tex}:^^J} + \robExt_write_file_in_log:x {\robExtAddCachePathAndName{\robExtFinalHash.tex}} + \fi + } } } } @@ -1401,7 +1718,7 @@ if __name__ == '__main__': \let\l__robExt_do_not_compile\undefined } \cs_if_exist:NTF {\l__robExt_do_not_compile}{ - \message{No ~ need ~ to ~ recompile ~ \robExtAddCachePathAndName{\robExtFinalHash.pdf}^^J} + \robExtDebugInfo{No ~ need ~ to ~ recompile ~ \robExtAddCachePathAndName{\robExtFinalHash.pdf}^^J} }{ % Used to know later if we have run a compilation command at that step or not \cs_undefine:N \l__robExt_I_just_ran_a_compilation_command: @@ -1429,25 +1746,37 @@ if __name__ == '__main__': } {} \fi \ifdefined\robExtManualMode - \message{[robExt] Manual mode enabled: please, manually compile the images using \l__robExt_current_compilation_command_str or run 'bash \jobname-\robExtAddPrefixName{compile-missing-figures.sh}'.} + \message{[robExt] Manual mode enabled: please, manually compile the images using \l__robExt_current_compilation_command_str or run 'bash \jobnameNoQuotes-\robExtAddPrefixName{compile-missing-figures.sh}'.} % Avoid writing the same command multiple times (might occur in environments that execute the % same command multiple times like align etc) \cs_if_exist:cTF {l__robExt_missing_figure_\l__robExt_current_compilation_command_str :}{}{ - \iow_now:Nx \g__robExt_write_manually_compile_all_missing_figures_iow {\l__robExt_current_compilation_command_str}% a new line is automatically added + \ifdefined\robExtDoNotRedirectOutputInManuallyCompileMissingFigures% + \iow_now:Nx \g__robExt_write_manually_compile_all_missing_figures_iow {\l__robExt_current_compilation_command_str}% a new line is automatically added + \else + \iow_now:Nx \g__robExt_write_manually_compile_all_missing_figures_iow {(~\l__robExt_current_compilation_command_str~)~>~\robExtAddCachePathAndName{\robExtFinalHash-compilation.log}~2>&1}% a new line is automatically added + \fi \cs_gset:cn {l__robExt_missing_figure_\l__robExt_current_compilation_command_str :} {} \ifdefined\robExt@compile@parallel@after \gdef\robExt@compile@parallel@must@compile{} \seq_gput_right:Nx \g__robExt_files_compiled_in_parallel {\robExtAddCachePathAndName{\robExtFinalHash}} % Gets the line with the error for easier debugging \cs_gset:cx {g__robExt_lines_error_\robExtAddCachePathAndName{\robExtFinalHash}:} {\msg_line_number:} + \ifdefined\robExtPrintWholeFile + \cs_gset:cx {g__robExt_print_whole_file_\robExtAddCachePathAndName{\robExtFinalHash}:} {} + \fi \cs_gset:cx {g__robExt_compilation_command_\robExtAddCachePathAndName{\robExtFinalHash}:} {\l__robExt_current_compilation_command_str} \fi } - \else + \else% \bool_if:nTF { \sys_if_shell_unrestricted_p: || \cs_if_exist_p:N \robExtForceCompilation } { - \message{[robExt] We ~ will ~ start ~ the ~ compilation using: ~ \l__robExt_current_compilation_command_str.} - \sys_shell_now:x {\l__robExt_current_compilation_command_str} % The ~ are used in ExplSyntaxOn to add space + % For a better debugging, we create a new file: + \ifdefined\robExtDoNotRedirectOutput% + \sys_shell_now:x {\robExtPrefixAllCompilationCommands \l__robExt_current_compilation_command_str}% + \else + \sys_shell_now:x {\robExtPrefixAllCompilationCommands (~\l__robExt_current_compilation_command_str~)~>~\robExtAddCachePathAndName{\robExtFinalHash-compilation.log}~2>&1}% + \fi + \message{[robExt] We ~ will ~ start ~ the ~ compilation ~ using: ~ \l__robExt_current_compilation_command_str.}% %% We notify that we are running the compilation command, this way it is possible to check later %% if the file is not present because of some compilation error or because we are in manual mode %% or fallback @@ -1457,8 +1786,12 @@ if __name__ == '__main__': % Avoid writing the same command multiple times (might occur in environments that execute the % same command multiple times like align etc) \cs_if_exist:cTF {l__robExt_missing_figure_\l__robExt_current_compilation_command_str :}{}{ - \message{[robExt] Fallback to manual mode: please, manually compile the images using \l__robExt_current_compilation_command_str or run 'bash \jobname-\robExtAddPrefixName{compile-missing-figures.sh}'.} - \iow_now:Nx \g__robExt_write_manually_compile_all_missing_figures_iow {\l__robExt_current_compilation_command_str}% a new line is automatically added + \message{[robExt] Fallback to manual mode: please, manually compile the images using \l__robExt_current_compilation_command_str or run 'bash \jobnameNoQuotes-\robExtAddPrefixName{compile-missing-figures.sh}'.} + \ifdefined\robExtDoNotRedirectOutputInManuallyCompileMissingFigures% + \iow_now:Nx \g__robExt_write_manually_compile_all_missing_figures_iow {\l__robExt_current_compilation_command_str}% a new line is automatically added + \else + \iow_now:Nx \g__robExt_write_manually_compile_all_missing_figures_iow {(~\l__robExt_current_compilation_command_str~)~>~\robExtAddCachePathAndName{\robExtFinalHash-compilation.log}~2>&1}% a new line is automatically added + \fi \cs_gset:cn {l__robExt_missing_figure_\l__robExt_current_compilation_command_str :} {} \ifdefined\robExt@compile@parallel@after \gdef\robExt@compile@parallel@must@compile{} @@ -1466,6 +1799,9 @@ if __name__ == '__main__': \fi } \else + \ifdefined\robExtPrintSourceWhenSaving + \typeout{Compilation~command~that~we~would~have~run:^^J\l__robExt_current_compilation_command_str^^J} + \fi \msg_error:nn{robExt}{need shell escape} \fi } @@ -1523,10 +1859,29 @@ if __name__ == '__main__': % Check if we tried to run a compilation command at the previous step: \cs_if_exist:NTF \l__robExt_I_just_ran_a_compilation_command: { % We ran a compilation command but it failed: we print an error message - \msg_error:nnxxx{robExt}{missing compiled pdf parallel}{\robExtAddCachePathAndName{\robExtFinalHash}}{\msg_line_number:}{\l__robExt_current_compilation_command_str} + % We first check if an error file has been produced: + \file_if_exist:nTF {\robExtAddCachePathAndName{\robExtFinalHash-compilation.log}} { + % We print the error twice as some editor might display only the first error while in others + % it might be easier to see the last error. For instance, in emacs if we do not do that + % then it first prints the error message in a file in the cache (but it forgets the + % robustExternalize prefix) + % But first we get the lines containing the word error: + \robExt_get_errors_from_file:n {\robExtAddCachePathAndName{\robExtFinalHash-compilation.log}}% + % We print the message before the log because otherwise emacs is confused and tries to open the + % auxiliary cached file, and I don't want that. + \ifdefined\robExtHideFirstErrorMessage\else + \msg_error:nnxxxx{robExt}{missing compiled pdf parallel with log}{\robExtAddCachePathAndName{\robExtFinalHash}}{\msg_line_number:}{\l__robExt_current_compilation_command_str}{below~(you~might~need~to~press~ENTER~to~go~to~the~next~error)} + \fi + \message{--------~We~print~now~the~full~log~--------^^J} + \robExt_write_file_in_log:n {\robExtAddCachePathAndName{\robExtFinalHash-compilation.log}}% + \message{--------~End~of~the~full~log~--------^^J} + \msg_error:nnxxxx{robExt}{missing compiled pdf parallel with log}{\robExtAddCachePathAndName{\robExtFinalHash}}{\msg_line_number:}{\l__robExt_current_compilation_command_str}{above} + }{ + \msg_error:nnxxx{robExt}{missing compiled pdf parallel}{\robExtAddCachePathAndName{\robExtFinalHash}}{\msg_line_number:}{\l__robExt_current_compilation_command_str} + }% } { % We ran no compilation command: we print instead a placeholder depending on the context: \cs_if_exist:NTF \robExt@compile@parallel@must@compile { - % we are supposed to compile the current picture in parallel, let'us check if it will be possible: + % we are supposed to compile the current picture in parallel, let us check if it will be possible: \bool_if:nTF { \sys_if_shell_unrestricted_p: || \cs_if_exist_p:N \robExtForceCompilation} { \robExtImagePlaceholderIfParallelCompilation }{ @@ -1535,10 +1890,10 @@ if __name__ == '__main__': }{ \cs_if_exist:NTF \robExtManualMode { \robExtImagePlaceholderIfManualMode - \message{[robExt] ~ You ~ are ~ in ~ manual ~ mode: ~ please ~ compile ~ yourself ~ \robExtAddCachePathAndName{\robExtFinalHash.tex} ~ or ~ use ~ the ~ bash ~ \jobname-\robExtAddPrefixName{compile-missing-figures.sh}} + \message{[robExt] ~ You ~ are ~ in ~ manual ~ mode: ~ please ~ compile ~ yourself ~ \robExtAddCachePathAndName{\robExtFinalHash.tex} ~ or ~ use ~ the ~ bash ~ \jobnameNoQuotes-\robExtAddPrefixName{compile-missing-figures.sh}} }{ \robExtImagePlaceholderIfFallbackMode - \message{[robExt] ~ You ~ are ~ falling ~ back ~ to ~ manual ~ mode: ~ please ~ compile ~ yourself ~ \robExtAddCachePathAndName{\robExtFinalHash.tex} ~ or ~ use ~ the ~ bash ~ \jobname-\robExtAddPrefixName{compile-missing-figures.sh}} + \message{[robExt] ~ You ~ are ~ falling ~ back ~ to ~ manual ~ mode: ~ please ~ compile ~ yourself ~ \robExtAddCachePathAndName{\robExtFinalHash.tex} ~ or ~ use ~ the ~ bash ~ \jobnameNoQuotes-\robExtAddPrefixName{compile-missing-figures.sh}} } } } @@ -1634,7 +1989,7 @@ if __name__ == '__main__': } \let\autoForwardMacro\robExtAutoForwardMacro -\NewDocumentCommand{\robExtLoadAutoForwardMacroConfig}{m}{ +\NewDocumentCommand{\robExtLoadAutoForwardMacroConfig}{+m}{ %\str_set:Nx \l__robExt_tmp_str {\cs_to_str:N #1} \cs_if_exist:cTF {l__robExt_execute_if_macro_present\string#1}{ % if the macro is present twice, we want to run \forward only once @@ -1660,16 +2015,21 @@ if __name__ == '__main__': %% "auto forward" \NewDocumentCommand{\robExtAutoForward}{}{ - \ifdefined\robExtUserInputCacheMe\else - \msg_warning:nn{robExt}{auto forward not in cachemecode} - \tl_set_rescan:NnV \robExtUserInputCacheMe {} \l__robExt_placeholder___ROBEXT_MAIN_CONTENT_ORIG___str + % some code will automatically add auto forward, but we don't want to run it twice. + \robExtDebugInfo{Running auto forward.} + \ifdefined\robExt@already@ran@autoforward\robExtDebugInfo{Auto forward already ran}\else + \ifdefined\robExtUserInputCacheMe\else + \msg_warning:nn{robExt}{auto forward not in cachemecode} + \tl_set_rescan:NnV \robExtUserInputCacheMe {} \l__robExt_placeholder___ROBEXT_MAIN_CONTENT_ORIG___str + \fi + \robExt@normalBraces\robExtUserInputCacheMe + \expandafter\robExt@getfromstringA\robExtUserInputCacheMe\robExt@getfromstringA%\robExt@getfromstringA is added to know when to stop + \def\robExt@already@ran@autoforward{}% \fi - \robExt@normalBraces\robExtUserInputCacheMe - \expandafter\robExt@getfromstringA\robExtUserInputCacheMe\robExt@getfromstringA%\robExt@getfromstringA is added to know when to stop } % https://tex.stackexchange.com/questions/700834/efficiently-program-search-of-macro-in-string/700844 -\def\robExt@getfromstringA#1{\ifx#1\robExt@getfromstringA \else +\long\def\robExt@getfromstringA#1{\ifx#1\robExt@getfromstringA \else %\ifcsname L:\string#1\endcsname \addto\listmacros{#1}\fi \robExtLoadAutoForwardMacroConfig{#1}% \expandafter\robExt@getfromstringA\fi @@ -1677,7 +2037,7 @@ if __name__ == '__main__': \def\robExt@normalBraces#1{{\catcode`{=12 \catcode`}=12 \everyeof={\endfile}% \expandafter\robExt@normalBracesA\expandafter#1\scantokens\expandafter{#1}}} -\def\robExt@normalBracesA#1#2\endfile{\gdef#1{#2}} +\long\def\robExt@normalBracesA#1#2\endfile{\gdef#1{#2}} %%% To automatically define and forward \NewDocumentCommand{\newcommandAutoForward}{moomO{}}{ @@ -1774,33 +2134,57 @@ if __name__ == '__main__': } \NewDocumentCommand{\defAutoForward}{mO{}mO{}}{ - \def#1#2{#3} - \robExtAutoForwardMacro{#1}[#4] + \def#1#2{#3}% + \robExtAutoForwardMacro{#1}[#4]% } -% \robExtGenericAutoForward[tikz]{string to match}[additional style to run]{ +% \robExtGenericAutoForward[tikz][namespace]{string to match}[additional style to run]{ % code to run in cached file if string matches, and to always run in current folder if not using % the star version % } \ExplSyntaxOff -\NewDocumentCommand{\robExtGenericAutoForward}{sO{latex}mO{}m}{% - \IfBooleanTF{#1}{}{#5}% We run the code right now - \robExt@set@hash@robust\zxTmpMacro{#5}% + +\NewDocumentCommand{\definecolorAutoForward}{mmmO{}}{% + \definecolor{#1}{#2}{#3}% + \robExtConfigure{if matches word={#1}{forward color=#1,#4}}% +} + +\NewDocumentCommand{\colorletAutoForward}{mmO{}}{% + \colorlet{#1}{#2}% + \robExtConfigure{if matches word={#1}{forward color=#1,#3}}% +} + + +\NewDocumentCommand{\robExtRunHereAndInPreambleOfCachedFiles}{O{latex}m}{% + #2% \robExtConfigure{% - add to preset={#2}{% - if matches word={#3}{ - command if externalization/.append code={ - \expanded{% - \noexpand\robExtAddBeforePlaceholder*{__ROBEXT_MAIN_CONTENT__}{% - \expandonce{\zxTmpMacro}% - % #1%% - }% + add to preset={#1}{% + add to preamble={#2}, + },% + }% +} +\let\runHereAndInPreambleOfCachedFiles\robExtRunHereAndInPreambleOfCachedFiles + +\NewDocumentCommand{\robExtGenericAutoForward}{sO{latex}O{}mO{}m}{% + \IfBooleanTF{#1}{}{#6}% We run the code right now + \robExt@set@hash@robust\zxTmpMacro{#6}% + \expanded{% + \noexpand\robExtConfigure{% + register word with namespace={#3}{#4}{% + /utils/exec={% + \noexpand\robExtAddBeforePlaceholder*{__ROBEXT_MAIN_CONTENT__}{% + \expandonce{\zxTmpMacro}% }% - },#4% + },% + #5% + },% + add to preset={#2}{% + auto forward words namespace={#3},% }% }% - }% + }% } + \let\genericAutoForward\robExtGenericAutoForward % uses "if matches" instead of "if matches word" @@ -1810,7 +2194,7 @@ if __name__ == '__main__': \robExtConfigure{% add to preset={#2}{% if matches={#3}{ - command if externalization/.append code={ + run command if externalization={ \expanded{% \noexpand\robExtAddBeforePlaceholder*{__ROBEXT_MAIN_CONTENT__}{% \expandonce{\zxTmpMacro}% @@ -1840,8 +2224,120 @@ if __name__ == '__main__': \str_if_in:NnTF \l__robExt_placeholder___ROBEXT_MAIN_CONTENT_ORIG___str {#1} {\pgfkeysalso{#2}} {} } +% \robExtRegisterWord {namespace} {word} {style} +\NewDocumentCommand{\robExtRegisterWord}{mmm}{ + % \robExt_register_match_word:nnn {#1} {#2} {\pgfkeysalso{#3}} + \robExtRegisterWordCode{#1}{#2}{\pgfkeysalso{#3}}% +} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%% Automatically forward (word-based) +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% For efficiency reasons, we first extract in a single pass all words in the text (a word is basically [A-Za-z]+) +% and run a function on these words + +%%% I tried this, but it is really slow (500x slower than if_string_matches, overall 20% increase of +%%% compilation time) +% % \__robExt_auto_forward_words:N \commandToRunOnEachWord \stringToSearchOn +% \cs_set:Nn \__robExt_auto_forward_words:NN { +% % \l_tmpa_str will contain the current word read so far +% \str_set:Nn \l_tmpa_str {}% +% \str_map_inline:Nn #2 { +% % \token_case_charcode:NnTF ##1 {} {} {} +% \__robExt_if_letter:nTF {##1} { +% \str_put_right:Nn \l_tmpa_str {##1} +% }{ +% \str_if_empty:NTF \l_tmpa_str { } { +% % if the string is empty, we run the command on the string +% #1 \l_tmpa_str% +% \str_set:Nn \l_tmpa_str {}% we reset its value +% } +% } +% } +% } + +% %% \__robExt_if_letter:nTF {char} {true} {false} tests if an element is a letter +% %% https://tex.stackexchange.com/a/700864/116348 +% \prg_new_conditional:Npnn \__robExt_if_letter:n #1 { TF } +% { +% \bool_lazy_or:nnTF +% { +% \bool_lazy_and_p:nn +% { \int_compare_p:nNn { `#1 } > { `a - 1 } } +% { \int_compare_p:nNn { `#1 } < { `z + 1 } } +% } +% { +% \bool_lazy_and_p:nn +% { \int_compare_p:nNn { `#1 } > { `A - 1 } } +% { \int_compare_p:nNn { `#1 } < { `Z + 1 } } +% } +% \prg_return_true: +% \prg_return_false: +% } + +% % \robExt_register_match_word {namespace that defaults to empty} {word} {code to run if word is present} +% \cs_set:Nn \robExt_register_match_word:nnn { +% \cs_set:cn {l__robExt_execute_if_word_present_#1_#2:} {#3} +% } + +% % \robExt_try_to_execute_if_match_word:nn {namespace} {word} +% \cs_set:Nn \robExt_try_to_execute_if_match_word:nn { +% \cs_if_exist:cTF {l__robExt_execute_if_word_present_#1_#2:} {% +% \cs_if_exist:cTF {l__robExt_execute_if_word_present_#1_#2__already_forwarded:}{\message{Already forwarded}}{ +% \use:c {l__robExt_execute_if_word_present_#1_#2:}% +% % define it so that we do not import twice next time +% \cs_set:cx {l__robExt_execute_if_word_present_#1_#2__already_forwarded:} {} +% } +% } { } +% } +% \cs_generate_variant:Nn \robExt_try_to_execute_if_match_word:nn { nV } + +% \NewDocumentCommand{\robExtAutoForwardWords}{O{}}{ +% \cs_set:Nn \__robExt_tmp_fct:N { +% \robExt_try_to_execute_if_match_word:nV {#1} ##1 +% } +% \__robExt_auto_forward_words:NN \__robExt_tmp_fct:N \l__robExt_placeholder___ROBEXT_MAIN_CONTENT_ORIG___str +% } + +% \robExtAutoForwardWords[namespace] +\NewDocumentCommand{\robExtAutoForwardWords}{m}{% + % needed or the original string will be changed + \let\robExt@tmpString\l__robExt_placeholder___ROBEXT_MAIN_CONTENT_ORIG___str% + \robExt@scanmacro@find@word{#1}\robExt@tmpString% +} + \ExplSyntaxOff +% Thanks a lot wipet for this optimized version!! +% https://tex.stackexchange.com/questions/701351/more-efficient-string-extraction-of-words/701441#701441 +\def\robExtWordSeparators{{ };:."?!@+-/*,=\{\}[]\\'()&|~_^<>} + +% Not for user, use \robExtAutoForwardWords +% \robExt@scanmacro@find@word{namespace}\stringToParse +\def\robExt@scanmacro@find@word#1#2{% + \def\robExt@namespace{#1}% + % we will change the lccode (basically ascii code) of some characters like +- to delimit what a word is + % supposed to be. Since we do not want to affect other stuff, we put it in a bgroup. + \bgroup \expandafter\robExt@set@to@comma\robExtWordSeparators\relax% + \ifdefined\robExtAdditionalCodeInScanMacroFindWord\robExtAdditionalCodeInScanMacroFindWord\fi% + % This seems to set the string in lower case, so fill or Fill or get triggered. But it is not what we want here. + % \lowercase\expandafter{\expandafter\gdef\expandafter#2\expandafter{#2}}% + \lowercase\expandafter{\expandafter\gdef\expandafter#2\expandafter{#2}}% + \edef#2{\detokenize\expandafter{#2}}% + % \message{\string#2: \meaning#2} % prints the modified format of the scanned macro + \expandafter\egroup% + \expandafter\robExt@wordscan@aux#2,\relax,% +} +\def\robExt@set@to@comma #1{\ifx\relax#1\else \lccode`#1=`, \expandafter\robExt@set@to@comma\fi} +\def\robExt@wordscan@aux#1,{\ifx\relax#1\empty\else% + %\message{{#1}} % prints each scanned "word" + \ifcsname robExt@action@to@run@on@word:\robExt@namespace:#1\endcsname \csname robExt@action@to@run@on@word:\robExt@namespace:#1\endcsname \fi% + \expandafter\robExt@wordscan@aux\fi% +} +%\robExtRegisterWordCode{namespace}{word}{code} +\def\robExtRegisterWordCode#1#2#3{\expandafter\gdef\csname robExt@action@to@run@on@word:\string#1:\string#2\endcsname{#3}} + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%% Interface %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -1864,10 +2360,16 @@ if __name__ == '__main__': \robExtEvalPlaceholderReplaceFromList{__ROBEXT_TMP__}{% \pgfkeys{% /robExt/.cd, - #1/.append style={__ROBEXT_TMP__},% + #1/.append style={% + % Some styles run differently if inside a preset or not, + % like if matches word. This macro helps with detecting it. + /utils/exec={\def\robExtCurrentlyDefiningPreset{}},% + __ROBEXT_TMP__% + },% }% }% \robExtRemovePlaceholder{__ROBEXT_TMP__}% let us clean our variables + \let\robExtCurrentlyDefiningPreset\undefined% }, new preset/.code 2 args={% \robExtStrSetDoubleHash{\robExtTmpStr}{#2}% @@ -1878,10 +2380,16 @@ if __name__ == '__main__': \robExtEvalPlaceholderReplaceFromList{__ROBEXT_TMP__}{% \pgfkeys{% /robExt/.cd,% - #1/.style={__ROBEXT_TMP__},% + #1/.style={% + % Some styles run differently if inside a preset or not, + % like if matches word. This macro helps with detecting it. + /utils/exec={\def\robExtCurrentlyDefiningPreset{}},% + __ROBEXT_TMP__% + },% }% }% \robExtRemovePlaceholder{__ROBEXT_TMP__}% let us clean our variables + \let\robExtCurrentlyDefiningPreset\undefined% }, % new compiled preset={latex compiled}{ % code to compile in order to produce the expected strings @@ -2025,8 +2533,8 @@ if __name__ == '__main__': %%%%%%%%%%%%% %%% Debug %%% %%%%%%%%%%%%% - more logs/.code={\def\robExtDebugMessage#1{\message{^^J[robExt] #1}}}, - less logs/.code={\def\robExtDebugMessage#1{}}, + more logs/.code={\def\robExtDebugMessage##1{\message{^^J[robExt] ##1}}\def\robExtDebugInfo##1{^^J[robExt] ##1}}, + less logs/.code={\def\robExtDebugMessage##1{}\def\robExtDebugInfo##1{}}, show placeholder/.code={\robExtShowPlaceholder{#1}}, show placeholders/.code={\robExtShowPlaceholders}, show placeholders contents/.code={\robExtShowPlaceholdersContents}, @@ -2116,6 +2624,27 @@ if __name__ == '__main__': recompile/.code={\def\robExtForceRecompilation{}}, do not recompile/.code={\let\robExtForceRecompilation\undefined}, set compilation command/.code={\robExtSetCompilationCommand{#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={ + set compilation command/.expanded={#1 && \robExtMv\space"__ROBEXT_OUTPUT_PDF__-tmp" "__ROBEXT_OUTPUT_PDF__"} + }, + set compilation command create if no error/.code={\robExtSetCompilationCommand{#1 && echo "" > __ROBEXT_OUTPUT_PDF__}}, + do not redirect compilation output/.code={\def\robExtDoNotRedirectOutput{}\def\robExtDoNotRedirectOutputInManuallyCompileMissingFigures{}}, + print whole file in error message/.code={\def\robExtPrintWholeFile{}}, + do not print whole file in error message/.code={\let\robExtPrintWholeFile\undefined}, + prefix log message with/.code={\def\robExtPrefixLogMessage{#1}}, + spacing between lines in log/.code={\def\robExtMessageWithPrefixNumberLines{#1}}, + % This adds a ! in front of the code if \robExtPrintWholeFile is defined + logs should show as errors/.style={prefix log message with={!\space}}, + texstudio/.style={ + logs should show as errors, + spacing between lines in log={^^J^^J(sorry, these 4 empty lines are ugly but needed for texstudio to consider them as separate errors, customize me "with spacing between lines in log")^^J}, + }, + remove line number/.code={\def\robExtRemoveLineNumber{}}, % Removes the l. if the log error line starts with l.42 or it will disturb emacs. + do not remove line number/.code={\let\robExtRemoveLineNumber\undefined}, + remove line number, + nb lines after error to show/.code={\def\robExtLinesAfterError{#1}}, add argument to compilation command/.code={\robExtAddArgumentToCompilationCommand{#1}}, add arguments to compilation command/.style={ add argument to compilation command/.list={#1} @@ -2143,6 +2672,24 @@ if __name__ == '__main__': % The default include command includes the pdf, making sure it is raised depending on its depth, % but you can override it: custom include command/.code={\def\robExtIncludeCommand{#1}}, + include command is input/.style={ + custom include command={#1\input{\robExtAddCachePathAndName{\robExtFinalHash.pdf}}}, + }, + include command is input/.default={}, + % Sometimes (gnuplot), we need to include an image located in the cache folder: + % https://tex.stackexchange.com/questions/171587/append-entries-to-an-existing-graphicspath + add cache to graphicspath/.code={% + \appto\Ginput@path{\robExtCacheFolder}% + },% + %% 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/.code={% + \let\robExtIncludeCommandAdvanced\undefined% + \let\robExtIncludeCommand\undefined% + \let\robExtPrintWholeFile\undefined% + \setPlaceholder{__ROBEXT_INCLUDEGRAPHICS_FILE__}{\robExtAddCachePathAndName{\robExtFinalHash.pdf}}% + }, %% Use this when we do not want to include anything (e.g. the video will be processed later in the chain): do not include pdf/.style={ custom include command={}% @@ -2153,12 +2700,22 @@ if __name__ == '__main__': disable manual mode/.code={\let\robExtManualMode\undefined}, enable fallback to manual mode/.code={\def\robExtFallbackManualMode{}}, disable fallback to manual mode/.code={\let\robExtFallbackManualMode\undefined}, + % For the arxiv, we need to rename the source .tex into .tex-backup or it will be removed + backup source for arxiv/.code={\def\robExtEnableBackupSource{}}, + do not backup source for arxiv/.code={\let\robExtEnableBackupSource\undefined}, + rename backup files for arxiv/.code={\robExtRenameBackupFilesForArxiv{#1}}, + rename backup files for arxiv/.default={robExt-arxiv-files-to-rename.txt}, + % print in the log the file. Useful to debug in arxiv where we do not have any access to the logs + print source when saving/.code={\def\robExtPrintSourceWhenSaving{}}, + copy file to cache/.code={\robExtCopyFileToCache{#1}}, %% Arguments to include graphics include graphics args/.code={\def\robExtIncludeGraphicsArgs{#1}}, %% The role of this command is to set \l_robExt_result_str, that will contain the final string. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%% Configuration of the cache %%% - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + % For support of --output-directory + set output directory/.code={\def\robExtOutputDirectory{#1}}, %% Configure the prefix (default to "robExt-") set filename prefix/.code={\def\robExtPrefixFilename{#1}}, % first argument is subfolder, second is how to get from subfolder to the folder containing the source: @@ -2171,6 +2728,13 @@ if __name__ == '__main__': % 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. set subfolder and way back={robustExternalize/}{../}, + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + %%% Enable externalization for tikz %%% + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + cache tikz/.code={\robExtExternalizeAllTikzpictures{}}, + do not cache tikz/.code={\robExtDoNotExternalizeAllTikzpictures{}}, + cache tikz 2 args/.code 2 args={\robExtExternalizeAllTikzpictures[#1][#2]{}}, + cache tikz 3 args/.code n args={3}{\robExtExternalizeAllTikzpictures[#1][#2][3]{}}, %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%% Disable externalization %%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -2191,9 +2755,20 @@ if __name__ == '__main__': %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%% Forward macros if externalization is enabled %%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - command if externalization/.code={}, + command if externalization/.code={\def\robExt@we@are@already@in@command@if@externalization{}}, + % Doing "command if externalization/.append code={}" is not always good since we don't always know if the style + % will always be run before running "command if externalization". In particular this arrives when using + % something like "if matches word={mypink}{forward color=mypink}" + run command if externalization/.code={ + \ifdefined\robExt@we@are@already@in@command@if@externalization% + % we are already running command if externalization + #1% + \else% + \pgfkeysalso{command if externalization/.append code={#1}}% + \fi% + }, fw/.style={forward=#1}, - run code before main content if externalization enabled/.code={\message{#1}}, + run code before main content if externalization enabled/.code={}, % run code before main content if externalization enabled/.code={ % \message{aaa #1} % \def\zx@tmp{#1}% @@ -2213,7 +2788,7 @@ if __name__ == '__main__': % }, % }, forward/.style={% - command if externalization/.append code={% + run command if externalization={% \robExtGetCommandDefinitionInMacro{#1}% \expanded{% \noexpand\robExtAddBeforePlaceholder*{__ROBEXT_MAIN_CONTENT__}{% @@ -2223,7 +2798,7 @@ if __name__ == '__main__': }, }, forward at letter/.style={% - command if externalization/.append code={% + run command if externalization={% \robExtGetCommandDefinitionInMacro{#1}% \expanded{% \noexpand\robExtAddBeforePlaceholder*{__ROBEXT_MAIN_CONTENT__}{% @@ -2235,7 +2810,7 @@ if __name__ == '__main__': }, }, forward eval/.style={% - command if externalization/.append code={% + run command if externalization={% \expanded{% \noexpand\robExtAddBeforePlaceholder*{__ROBEXT_MAIN_CONTENT__}{% \noexpand\def\noexpand#1{#1}% @@ -2244,7 +2819,7 @@ if __name__ == '__main__': },% }, forward counter/.style={% - command if externalization/.append code={% + run command if externalization={% \expanded{% \noexpand\robExtAddBeforePlaceholder*{__ROBEXT_MAIN_CONTENT__}{% \noexpand\makeatletter% @@ -2256,8 +2831,23 @@ if __name__ == '__main__': }% },% },% + % Useful when we want to force the value of a forwarded counter. Useful for arXiv, e.g. + % if the page number is not the good one. + forward counter force value/.style 2 args={% + run command if externalization={% + \expanded{% + \noexpand\robExtAddBeforePlaceholder*{__ROBEXT_MAIN_CONTENT__}{% + \noexpand\makeatletter% + % Make sure the counter exists: + \noexpand\ifcsname c@#1\noexpand\endcsname\noexpand\else\noexpand\newcounter{#1}\noexpand\fi% + \noexpand\setcounter{#1}{#2}% + \noexpand\makeatother + }% + }% + },% + },% forward color/.style={ - command if externalization/.append code={% + run command if externalization={% \extractcolorspecs{#1}{\zx@tmp@model}{\zx@tmp@cmd}% \expanded{% \noexpand\robExtAddBeforePlaceholder*{__ROBEXT_MAIN_CONTENT__}{% @@ -2268,19 +2858,47 @@ if __name__ == '__main__': },% %% This will try to automatically forward some macros. For this, you must first define %% - auto forward/.code={\robExtAutoForward}, + auto forward only macros/.code={\robExtAutoForward}, + auto forward/.code={\robExtAutoForward\pgfkeysalso{auto forward words}}, auto forward color/.style 2 args={% - add to preset={#1}{ - % if matches={\b #2\b}{forward color=#2}, - % the Regex is maybe more precise (avoid unnecessary forwards) but is way faster - if matches={#2}{forward color=#2}, + add to preset={#1}{% + if matches word={#2}{forward color=#2}, }, }, load auto forward macro config/.code={\robExtLoadAutoForwardMacroConfig{#1}}, + %% Auto forward words + auto forward words namespace/.code={% + \ifdefined\robExt@autoforward@enabled\else% + \pgfkeysalso{command if externalization/.append code={% + \ifdefined\robExt@autoforward@enabled% + \expanded{\noexpand\robExtAutoForwardWords{\robExt@autoforward@enabled}}% + \fi% + }% + }% + \fi% + \def\robExt@autoforward@enabled{#1}% + },%%% todo: finish + auto forward words/.style={auto forward words namespace={}}, %% This will if matches regex/.code 2 args={\robExtIfMatchesRegex{#1}{#2}}, - if matches word/.code 2 args={\robExtIfMatchesString{#1}{#2}}, if matches/.code 2 args={\robExtIfMatchesString{#1}{#2}}, + 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 + \ifdefined\robExtCurrentlyDefiningPreset% + \pgfkeysalso{auto forward words}% + \else% + \pgfkeysalso{/robExt/latex/.append style={auto forward words}}% + \fi% + }, + % more efficient since we can register the words before the preset, but make sure to call `auto forward words namespace`. + % You can use an empty namespace. + register word with namespace/.code n args={3}{% + \robExtRegisterWord{#1}{#2}{#3}% + }, + register word/.style 2 args={% + register word with namespace={}{#1}{#2},% + }, %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%% Run code before/after inclusion %%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -2320,12 +2938,12 @@ if __name__ == '__main__': compile in parallel command/.store in=\robExt@compile@parallel@command, % Different backends to compile in parallel compile in parallel with gnu parallel/.style={ - compile in parallel command={parallel --jobs #1 :::: '\jobname-\robExtAddPrefixName{compile-missing-figures.sh}'}, + compile in parallel command={parallel --jobs #1 :::: '\jobnameNoQuotes-\robExtAddPrefixName{compile-missing-figures.sh}'}, }, compile in parallel with gnu parallel/.default={200\%}, compile in parallel with xargs/.style={ % Use " instead of ' as Windows does not consider ' as a valid surrounding for strings - compile in parallel command={xargs -t -I "{}" -P #1 \robExtParallelShell\space "{}" < "\jobname-\robExtAddPrefixName{compile-missing-figures.sh}"}, + compile in parallel command={xargs -t -I "{}" -P #1 \robExtParallelShell\space "{}" < "\jobnameNoQuotes-\robExtAddPrefixName{compile-missing-figures.sh}"}, }, % With 0 it spawns as many shells at possible, i.e. it compiles all 200 pictures at the same time, making the % system a laggy, and it is actually *slower* (2:05mn vs 1:15mn) than when using 16 threads. @@ -2361,12 +2979,36 @@ if __name__ == '__main__': \iow_close:N \g__robExt_write_manually_compile_all_missing_figures_iow \msg_warning:nnx{robExt}{rerun because parallel}{\robExt@compile@parallel@command} \message{\robExt@compile@parallel@command} - \sys_shell_now:x {\robExt@compile@parallel@command} + % Note that we do not save this into a file because it might help to see in real time where we are in + % the compilation process. This also means that we get poorer error message if this specific + % command fails in texstudio since it only prints the log file and not the whole terminal... + % Since this should anyway be quite rare (user has not xargs installed basically), this should be fine. + \sys_shell_now:x {\robExtPrefixAllCompilationCommands\robExt@compile@parallel@command} % We check if some files failed to compile \seq_map_inline:Nn \g__robExt_files_compiled_in_parallel { - \message{#1} \file_if_exist:xTF{#1.pdf}{}{ - \msg_error:nnxxx{robExt}{missing compiled pdf parallel}{#1}{\use:c {g__robExt_lines_error_#1:}}{\use:c {g__robExt_compilation_command_#1:}} + \file_if_exist:nTF {#1-compilation.log} { + % We print the error twice as some editor might display only the first error while in others + % it might be easier to see the last error. For instance, in emacs if we do not do that + % then it first prints the error message in a file in the cache (but it forgets the + % robustExternalize prefix) + % But first we get the lines containing the word error: + \let\robExtPrintWholeFile\undefined + \cs_if_exist:cTF {g__robExt_print_whole_file_#1:} {\def\robExtPrintWholeFile{}} {} + \robExt_get_errors_from_file:n {#1-compilation.log}% + % We print the message before the log because otherwise emacs is confused and tries to open the + % auxiliary cached file, and I don't want that. + \ifdefined\robExtHideFirstErrorMessage\else + \msg_error:nnxxxx{robExt}{missing compiled pdf parallel with log}{#1}{\use:c {g__robExt_lines_error_#1:}}{\use:c {g__robExt_compilation_command_#1:}}{below~(you~might~need~to~press~ENTER~to~go~to~the~next~error)} + \fi + \message{--------~We~print~now~the~full~log~--------^^J} + \robExt_write_file_in_log:n {#1-compilation.log}% + \message{--------~End~of~the~full~log~--------^^J} + \msg_error:nnxxxx{robExt}{missing compiled pdf parallel with log}{#1}{\use:c {g__robExt_lines_error_#1:}}{\use:c {g__robExt_compilation_command_#1:}}{above} + \let\robExtPrintWholeFile\undefined + }{ + \msg_error:nnxxx{robExt}{missing compiled pdf parallel}{#1}{\use:c {g__robExt_lines_error_#1:}}{\use:c {g__robExt_compilation_command_#1:}} + } } } } { @@ -2378,6 +3020,18 @@ if __name__ == '__main__': } \fi \fi + % \robExtBackupSource moves the source .tex into .tex-backup, needed for the arXiv website that removes the + % .pdf if there is a .tex file. + % We need to run it at the very end, as if we move it right after a cacheMe, we cannot refer anymore to the + % input file of this block later. + \iow_close:N \g__robExt_write_list_all_figures_iow + \ifdefined\robExtEnableBackupSource% + \ior_open:Nn \g__robExt_read_ior {\jobnameNoQuotes-\robExtAddPrefixName{all-figures.txt}}% + \ior_str_map_inline:Nn \g__robExt_read_ior {% + \robExtBackupSource{#1}% + }% + \ior_close:N \g__robExt_read_ior + \fi% } \ExplSyntaxOff @@ -2401,17 +3055,22 @@ if __name__ == '__main__': \ifdefined\robExtExecuteNamedOutput\robExtExecuteNamedOutput\fi% \ifdefined\robExtExecuteAfter\robExtExecuteAfter\fi% \fi% - \message{Finished to include the file.}% + \robExtDebugInfo{Finished to include the file.}% } %% #1: Arguments, #2: content to externalize -\NewDocumentCommand{\robExtCacheMe}{O{}m}{% +\NewDocumentCommand{\robExtCacheMe}{O{}+m}{% {% Group %% We store the input in a non-string element for efficiently implementing "auto forward" \edef\robExtUserInputCacheMe{\unexpanded{#2}}% \unexpanded is needed if the macro contains a #1 \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, set placeholder={__ROBEXT_MAIN_CONTENT_ORIG__}{#2},% default style,% #1, @@ -2637,12 +3296,16 @@ __ROBEXT_LATEX_WRITE_DEPTH_TO_OUT_FILE__% enable placeholders, % This way it appears before orig in the list: more efficient import placeholders from group={latex}, + %/utils/exec={\message{I am in the latex style!!!!!!}}, set placeholder={__ROBEXT_MAIN_CONTENT__}{__ROBEXT_MAIN_CONTENT_ORIG__}, import placeholder={__ROBEXT_MAIN_CONTENT_ORIG__}, first placeholders latex, set template={__ROBEXT_LATEX__}, 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}, + }, use latexmk/.style={ set placeholder={__ROBEXT_LATEX_ENGINE__}{latexmk}, }, @@ -2787,9 +3450,9 @@ def finished_with_no_error(): created (otherwise robust-externalize will think that the compilation failed) ''' if not os.path.exists(get_filename_from_extension(".pdf")): - # we create an empty path + # we create a nearly empty pdf (not empty or arxiv will remove it) with open(get_filename_from_extension(".pdf"), 'w') as f: - pass + f.write("ok") ### Starting main content __ROBEXT_MAIN_CONTENT__ @@ -2810,6 +3473,7 @@ finished_with_no_error() \robExtConfigure{ python/.style={ enable placeholders, + print whole file in error message, import placeholders={__ROBEXT_PYTHON__,__ROBEXT_PYTHON_IMPORT__,__ROBEXT_PYTHON_MAIN_CONTENT_WRAPPED__,__ROBEXT_PYTHON_FINISHED_WITH_NO_ERROR__,__ROBEXT_PYTHON_EXEC__}, set compilation command={__ROBEXT_PYTHON_EXEC__ "__ROBEXT_SOURCE_FILE__"}, set template={__ROBEXT_PYTHON__}, @@ -2906,13 +3570,24 @@ print_file.close() \robExtClearGroupPlaceholders{main} +%% Inspired by +%% https://tex.stackexchange.com/questions/259247/rescaling-gnuplottex-to-fit-in-subfigure/259271 +\ExplSyntaxOn +\DeclareExpandableDocumentCommand{\robExtLenToCm}{ O{cm} m } +{ + \dim_to_decimal_in_unit:nn { #2 } { 1 #1 } #1 +} +\ExplSyntaxOff +\let\lenToCm\robExtLenToCm + \robExtConfigure{ verbatim text/.style={ enable placeholders, set template={__ROBEXT_MAIN_CONTENT__}, custom include command={\evalPlaceholder{\verbatiminput{\robExtAddCachePathAndName{\robExtFinalHash.tex}}}}, %% Apparently this works on windows as well https://stackoverflow.com/questions/1702762/how-can-i-create-an-empty-file-at-the-command-line-in-windows - set compilation command={echo "" > __ROBEXT_OUTPUT_PDF__}, + %% We do not want it to be empty or arXiv will remove the file. + set compilation command={echo "ok" > __ROBEXT_OUTPUT_PDF__}, }, verbatim text no include/.style={ verbatim text, @@ -2928,6 +3603,94 @@ print_file.close() %%%%%% +%%%%%% Group "gnuplot" +%%%%%% + +% We clean the "main" group, that we will copy later to gnuplot. +\robExtClearGroupPlaceholders{main} + +\begin{PlaceholderFromCode}*{__GNUPLOT_TEMPLATE__} +set terminal __ROBEXT_GNUPLOT_TERMINAL__ +set output "__ROBEXT_GNUPLOT_OUTPUTFILE__" +set loadpath "__ROBEXT_WAY_BACK__" +__ROBEXT_GNUPLOT_PRELUDE__ +__ROBEXT_MAIN_CONTENT__ +\end{PlaceholderFromCode} + +\begin{PlaceholderFromCode}{__ROBEXT_GNUPLOT_PRELUDE__} +\end{PlaceholderFromCode} + +\robExtCopyGroupPlaceholders{gnuplot}{main} +\robExtRegisterGroupPlaceholders{gnuplot} + +\robExtConfigure{ + new preset={gnuplot}{ + enable placeholders, + print whole file in error message, + import placeholders from group={gnuplot}, + set compilation command={gnuplot -c "__ROBEXT_SOURCE_FILE__" && echo "ok" > "__ROBEXT_OUTPUT_PDF__"}, + set terminal/.style={ + set placeholder={__ROBEXT_GNUPLOT_TERMINAL__}{#1}, + }, + % The extension is important. Notably, the cairolatex expects a .tex. + set filename/.style={ + set placeholder={__ROBEXT_GNUPLOT_OUTPUTFILE__}{__ROBEXT_OUTPUT_PREFIX__-gnuplot-#1}, + set placeholder={__ROBEXT_INCLUDEGRAPHICS_FILE__}{\robExtAddCachePathAndName{\robExtFinalHash-gnuplot-#1}}, + }, + pdf terminal/.style={ + set terminal={pdf #1}, + set filename={.pdf}, + }, + pdf terminal/.default={}, + pdf terminal, + is tex output/.style={ + set filename={.tex}, + add cache to graphicspath, % At least in cairolatex, we first need to input a file that includegraphics an image in the cache. + custom include command={\message{We will input the file \robExtAddCachePathAndName{\robExtFinalHash-gnuplot-.tex}}\input{\robExtAddCachePathAndName{\robExtFinalHash-gnuplot-.tex}}}, + }, + tikz terminal/.style={ + set terminal={tikz #1}, + is tex output, + }, + tikz terminal/.default={}, + % Warning: you cannot define this option with the name "tikz" if you try to nest elements, for instance + % if the include command calls an \input that calls a tikz picture, that is cached by \cacheTikz and therefore + % call the "tikz" preset that is now changed. + cairolatex terminal/.style={ + set terminal={cairolatex #1}, + is tex output, + }, + cairolatex terminal/.default={}, + set template={__GNUPLOT_TEMPLATE__}, + print verbatim if no externalization, + }, +} + + +% Like \gpgetvar but do not produce an error if the variable does not exist yet +% Fix https://github.com/leo-colisson/robust-externalize/issues/17 +\ExplSyntaxOn +\NewDocumentCommand{\robExtGpgetvar}{m}{% + \ifcsname gp@var@#1\endcsname% + \gpgetvar{#1}% + \else% + \msg_warning:nnx{robExt}{gpgetvar recompilation needed}{#1}% + \emph{Please~recompile~to~load~ variable:~}% + \texttt{\tl_to_str:n {#1}}% + \fi% +} +% Is useful if you want a number even if there is an error https://github.com/leo-colisson/robust-externalize/issues/17#issuecomment-1862556771 +% It must be expandable. +\NewExpandableDocumentCommand{\robExtGpgetvarNb}{sO{404}m}{% + \ifcsname gp@var@#3\endcsname% + \gpgetvar{#3}% + \else% We cannot print a waring or siunit will complain. + \sys_if_engine_luatex:TF{\directlua{texio.write_nl('Warning:~the~variable~#3~used~in~robExtGpgetvarNb~does~not~exist~yet.~Please~recompile.')}}{}#2% + \fi% +} +\ExplSyntaxOff + +%%%%%% %%%%%% Group "bash" %%%%%% @@ -2941,7 +3704,7 @@ outputTex="__ROBEXT_OUTPUT_PREFIX__-out.tex" outputPdf="__ROBEXT_OUTPUT_PDF__" __ROBEXT_MAIN_CONTENT__ # Create the pdf file to certify that no compilation error occured -touch "${outputPdf}" +echo "ok" > "${outputPdf}" \end{RobExtPlaceholderFromCode} \robExtSetPlaceholder{__ROBEXT_BASH_SHELL__}{bash} @@ -2949,6 +3712,7 @@ touch "${outputPdf}" \robExtConfigure{ bash/.style={ enable placeholders, + print whole file in error message, import placeholders={__ROBEXT_BASH_TEMPLATE__,__ROBEXT_BASH_SHELL__}, set compilation command={__ROBEXT_BASH_SHELL__ "__ROBEXT_SOURCE_FILE__"}, set template={__ROBEXT_BASH_TEMPLATE__}, @@ -2960,6 +3724,50 @@ touch "${outputPdf}" \robExtRegisterGroupPlaceholders{bash} %%%%%% +%%%%%% Group "web image", to download images automatically +%%%%%% + +\robExtClearGroupPlaceholders{main} + +% \begin{RobExtPlaceholderFromCode}{__ROBEXT_BASH_TEMPLATE__} +% # Quit if there is an error +% set -e +% outputTxt="__ROBEXT_OUTPUT_PREFIX__-out.txt" +% outputTex="__ROBEXT_OUTPUT_PREFIX__-out.tex" +% outputPdf="__ROBEXT_OUTPUT_PDF__" +% __ROBEXT_MAIN_CONTENT__ +% # Create the pdf file to certify that no compilation error occured +% echo "ok" > "${outputPdf}" +% \end{RobExtPlaceholderFromCode} + +\robExtConfigure{ + web image/.style={ + enable placeholders, + print whole file in error message, + wget/.style={ + set compilation command move if no error={wget "__ROBEXT_MAIN_CONTENT__" -O "__ROBEXT_OUTPUT_PDF__-tmp"}, + }, + curl/.style={ + set compilation command move if no error={curl "__ROBEXT_MAIN_CONTENT__" -L -o "__ROBEXT_OUTPUT_PDF__-tmp"}, + }, + if unix={ + wget + }, + if windows={ + curl + }, + set template={}, + } +} + +\NewDocumentCommand{\robExtIncludegraphicsWeb}{D<>{}O{}m}{\robExtCacheMe[web image,set includegraphics options={#2},#1]{#3}} +\let\includegraphicsWeb\robExtIncludegraphicsWeb + +\robExtCopyGroupPlaceholders{web image}{main} +\robExtRegisterGroupPlaceholders{web image} + + +%%%%%% %%%%%% Group "default", available in all styles %%%%%% @@ -3063,6 +3871,7 @@ touch "${outputPdf}" } \NewDocumentCommand{\robExtExternalizeAllTikzpictures}{O{tikz}O{tikzpicture}O{<>}}{% + \robExtDoNotExternalizeAllTikzpictures% \robExtCacheEnvironment[#3]{tikzpicture}{#2}% % Tikz has a more complicated parsing system \DeclareCommandCopy{\robExtCommandOrigtikz}{\tikz}% we save the function for later reset @@ -3077,12 +3886,65 @@ touch "${outputPdf}" } \let\cacheTikz\robExtExternalizeAllTikzpictures +\NewDocumentCommand{\robExtDoNotExternalizeAllTikzpictures}{}{% + \ifdefined\robExtCommandOrigtikz% + \DeclareCommandCopy{\tikz}{\robExtCommandOrigtikz}% + \let\robExtCommandOrigtikz\undefined + \fi% + \ifdefined\robExtEnvironmentOrigtikzpicture% + \DeclareEnvironmentCopy{tikzpicture}{robExtEnvironmentOrigtikzpicture}% + \let\robExtEnvironmentOrigtikzpicture\undefined + \fi% +} +\let\doNotCacheTikz\robExtDoNotExternalizeAllTikzpictures + + %% The cached version \DeclareDocumentEnvironment{tikzpictureC}{D<>{}O{}O{}}{% \begin{CacheMe}{tikzpicture,#1}[#2]% }{\end{CacheMe}}% %%%%%% +%%%%%% Cache tikzit +%%%%%% + +\NewDocumentCommand{\robExtCacheTikzit}{O{tikzit}}{% + \ifdefined\robExtCommandOrigtikzfig\else% + \DeclareCommandCopy{\robExtCommandOrigtikzfig}{\tikzfig}% + \fi% + \robExtAddToCommandResetList{tikzfig}% + \DeclareDocumentCommand{\tikzfig}{D<>{}m}{% + % tikzit tries first ##2.tikz and fallsback to figures/##2.tikz otherwise. + \IfFileExists{##2.tikz}{\def\robExtTikzfigFile{##2}}{\def\robExtTikzfigFile{figures/##2}}% + \expanded{\noexpand\cacheMe[ + #1, + add dependencies/.expanded={\robExtTikzfigFile.tikz}, + ##1, + command if no externalization/.code={\noexpand\robExtDisableTikzpictureOverwrite\noexpand\robExtCommandOrigtikzfig{##2}}, + ]{\noexpand\tikzfig{__ROBEXT_WAY_BACK__\robExtTikzfigFile}}}% + }% +} +\let\cacheTikzit\robExtCacheTikzit + +\NewDocumentCommand{\robExtCacheTikzitWithStyle}{O{tikzit}m}{% + \robExtCacheTikzit[#1]% + \robExtConfigure{ + % First, we copy the tikzit-related files to the cache: + copy file to cache={tikzit.sty}, + copy file to cache={#2}, + % We create the tikzit preset loaded by default: + new preset={#1}{ + latex, + add to preamble={ + \usepackage{tikzit} + \input{#2} + }, + }, + }% +} +\let\cacheTikzitWithStyle\robExtCacheTikzitWithStyle + +%%%%%% %%%%%% The generic functions %%%%%% @@ -3090,7 +3952,8 @@ touch "${outputPdf}" \NewDocumentCommand{\robExtCacheEnvironment}{O{<>}mm}{% %% We need to save the original environment to avoid infinite recursion if we disable externalization %% https://tex.stackexchange.com/questions/695391/why-isnt-my-environment-restored/695398 - \NewEnvironmentCopy{robExtEnvironmentOrig#2}{#2}% + \ifcsname robExtEnvironmentOrig#2\endcsname\PackageWarning{Your are trying to cache an environment #2 that seems to be already cached... Expect weird things to happen.}{}\fi + \DeclareEnvironmentCopy{robExtEnvironmentOrig#2}{#2}% \robExtAddToEnvironmentResetList{#2}% \DeclareDocumentEnvironment{#2}{D#1{}}{% \def\robExtEnvironmentOrigName{#2}% |