我创建了自己的环境mytikz
,它基本上看起来像这样(完整的 MWE):
\documentclass[a4paper]{article}
\usepackage{tikz}
\usetikzlibrary{external}
\newenvironment{mytikz}{%begin code
\begin{figure}[htp]
\centering
\begin{tikzpicture}%
}%
{%end code
\end{tikzpicture}
\caption{}
\end{figure}%
}
\tikzexternalize
\begin{document}
\begin{mytikz}
\draw(0,0) circle (1cm);
\end{mytikz}
\end{document}
问题是我想使用 TikZ 的external
库,这样我就可以加快文档的构建时间。这是不可能的,因为环境的结束代码不会及时扩展,以便 TikZ 找到它\end{tikzpicture}
正在寻找的内容。因此我收到以下错误:
! File ended while scanning use of \tikzexternal@laTeX@collect@until@end@tikzpicture.
有没有办法让我的结束代码更早地展开,这样 TikZ 就可以找到它?我试过使用这个environ
包,但这降低了命令的可用性newenvironment
(我的实际新环境有点复杂,使用\newenvironmentx
这个xargs
包)。
答案1
困难在于,外部化库要\end{tikzpicture}
确定它正在“外部化”的图片的结尾。它这样做无扩展所以\end{tikzpicture}
必须出现同时作为\begin{tikzpicture}
。这就是为什么:
\newenvironment{mytikz}{\begin{tikzpicture}}{\end{tikzpicture}}
不起作用。当输入
\begin{mytikz}
\node {hello world};
\end{mytikz}
然后\begin{mytikz}
一直扩展到\begin{tikzpicture}
(甚至超出),此时 TeX 开始吞噬东西。它遇到\end{mytikz}
而不扩展它,因此永远不会意识到它就是\end{tikzpicture}
它所寻找的。
这环境软件包通过将环境转换为命令来解决这个问题。因此当 TeX\begin{mytikz}
看到前它会将其展开(完全展开,也就是说,它必须展开到足以知道它是使用包定义的程度environ
),然后开始寻找\end{mytikz}
。然后它将内容放入内部宏中,并将相应的代码转储到流中。这意味着当 TeX 最终看到时\begin{tikzpicture}
,正确的内容\end{tikzpicture}
也已插入到流中的正确位置。
因此,我们希望保留此功能,但同时添加xargs
包的参数处理功能。这里的技巧是意识到参数处理部分并不实际上必须与环境相连 - 它只是对用户来说看起来像它。(这在 TeX/LaTeX 命令中很常见。许多命令似乎带有参数,但实际上不执行任何操作。)如果我们使用 声明一个环境,那么在 之后\NewEnviron
出现的任何内容\begin{myenv}
都会被放入一个名为 的特殊宏中\BODY
。也就是说,如果我们有:
\NewEnviron{myenv}{something first \BODY\ something last}
然后
\begin{myenv}
in the middle
\end{myenv}
在流中被替换something first in the middle something last
(几乎……,我并不担心\par
这里)。重点是这个还效劳于
\begin{myenv}[some][optional]{or}{mandatory}[arguments]
in the middle
\end{myenv}
因为这样会产生something first [some][optional]{or}{mandatory}[arguments] in the middle something last
。因此,如果something first
恰好是宏,它将“看到”[some][optional]{or}{mandatory}[arguments]
并认为它们是用于它。
这里的“几乎”实际上是放入流中的东西,something first \BODY\ something last
所以我们需要\BODY
先扩展才能得到参数。
综合以上所有,我们得到:
\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{external}
\tikzexternalize
\usepackage{environ}
\usepackage{xargs}
\newcommandx{\startmytikz}[1][1=]{%
\begin{figure}[htp]
\centering
\begin{tikzpicture}[#1]}
\NewEnviron{mytikz}{\expandafter\startmytikz\BODY
\end{tikzpicture}
\end{figure}}
\begin{document}
\tikzset{external/force remake=true}
\begin{mytikz}[every path/.style={red}]
\draw(0,0) circle (1cm);
\node {hello world};
\end{mytikz}
\end{document}
当然,如果愿意的话,可以将所有这些包含在一个定义中:
\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{external}
\tikzexternalize
\usepackage{environ}
\usepackage{xargs}
\newcommandx{\NewEnvironx}[5][2,3]{%
\expandafter\newcommandx\csname start#1\endcsname[#2][#3]{#4}%
\NewEnviron{#1}{\csname start#1\expandafter\endcsname\BODY #5}}
\NewEnvironx{mytikz}[1][1=]{%
\begin{figure}[htp]
\centering
\begin{tikzpicture}[#1]}
{\end{tikzpicture}
\end{figure}}
\begin{document}
\tikzset{external/force remake=true}
\begin{mytikz}[every path/.style={red}]
\draw(0,0) circle (1cm);
\node {hello world};
\end{mytikz}
\end{document}