![隐藏环境但保留标签以供交叉引用](https://linux22.com/image/417445/%E9%9A%90%E8%97%8F%E7%8E%AF%E5%A2%83%E4%BD%86%E4%BF%9D%E7%95%99%E6%A0%87%E7%AD%BE%E4%BB%A5%E4%BE%9B%E4%BA%A4%E5%8F%89%E5%BC%95%E7%94%A8.png)
我想做一些与这里要求(和回答)的事情非常相似的事情:
但我想更进一步。
我有许多自定义环境,可以通过切换开/关选项来显示或隐藏它们(在编译后的 PDF 输出中)。
我可能在某个环境中有一个带标签的方程式,我将用 \label{marker} 标记该方程式,并在该环境类型之外的文档另一部分用 \eqref{marker} 引用该方程式。当我选择隐藏该环境类型时,我仍然希望方程式计数器能够计数这些现在隐藏的方程式,我可以做到这一点,这要感谢上面链接的帖子和回复。但是,我还希望仍然能够在文档的其他(未隐藏)部分引用这些隐藏的方程式,而这正是我遇到麻烦的部分。(起初,我认为上面链接的帖子中使用 \setbox0\vbox 的解决方案可以完美运行,但当我从该帖子复制粘贴代码并进行编译时,如果 \label{marker} 位于隐藏环境中,则 \eqref{marker} 会输出 (??)。)
目前,我仅在可选隐藏环境中有方程式,但在某些时候,我可能想对图形和其他可以使用 \label{marker} 和 \ref{marker} 或 \eqref{marker} 标记和交叉引用的东西做同样的事情。因此,一体化(或多合一)解决方案比仅处理方程式的解决方案更可取。
实际上,我想隐藏视觉输出,但将所有“幕后”内容保留在 .aux 文件等中,就好像视觉输出没有被隐藏一样。
我可以想象我的问题可能定义不明确,因为更改视觉输出将更改文档中页码和内容的位置。我正在使用 hyperref,但我并不关心隐藏方程的链接会发生什么。我不认为在隐藏环境中需要 \pageref{key} 和 \label{key}。
也许我的目标的另一种思维方式是这样的:我本质上想将某种环境的输出缩小到肉眼看不见的程度,但对 LaTeX 来说却不是。
事实上,在提供的 MWE 中,如果我首先在未隐藏所有内容的情况下进行编译,然后在隐藏一个或两个环境的情况下进行编译,则所有内容看起来都如我所愿,并且数据全部位于 .aux 文件中。当我第二次编译时,数据将从 .aux 文件中删除,并且公式引用显示为 (??)。
我尝试以某种方式研究涉及 \immediate\write 的解决方案,但不幸的是,这对我来说似乎太高级了,目前无法理解。我也试图理解
和
隐藏特定表格,保留 \listoftables 中的交叉引用和标题,
这些帖子看起来与此相关,但无济于事。
非常感谢任何帮助或建议。
\documentclass[desertEnvironmentOFF, forestEnvironmentON]{article}
\usepackage{amsmath}
\usepackage{hyperref}
\usepackage{ifthen}
\usepackage{environ}
\newif\ifdesertEnvironment
\DeclareOption{desertEnvironmentON}{\desertEnvironmenttrue}
\DeclareOption{desertEnvironmentOFF}{\desertEnvironmentfalse}
\newif\ifforestEnvironment
\DeclareOption{forestEnvironmentON}{\forestEnvironmenttrue}
\DeclareOption{forestEnvironmentOFF}{\forestEnvironmentfalse}
\ProcessOptions\relax
\newcounter{environments}
\numberwithin{environments}{section}
\numberwithin{equation}{section}
\ifthenelse{\boolean{desertEnvironment}}
{\NewEnviron{desertEnvironment}[1][]
{\refstepcounter{environments}\vspace*{1em}
{{\bfseries Desert \theenvironments.}}
{\itshape \BODY}\vspace*{1em}}}
{\NewEnviron{desertEnvironment}[1][]
{\refstepcounter{environments}
\setbox0\vbox{\BODY}
}}
\ifthenelse{\boolean{forestEnvironment}}
{\NewEnviron{forestEnvironment}[1][]
{\refstepcounter{environments}\vspace*{1em}
{{\bfseries Forest \theenvironments.}}
{\itshape \BODY}\vspace*{1em}}}
{\NewEnviron{forestEnvironment}[1][]
{\refstepcounter{environments}
\setbox0\vbox{\BODY}
}}
\usepackage{setspace}
\setlength{\parindent}{0pt}
\setlength{\parskip}{1em}
\begin{document}
Here is some text that is not inside any custom environment. Here is a labelled equation that is not inside any custom environment [should be (0.1)]:
\begin{equation}
\label{eq:fermat}
x^n + y^n = z^n.
\end{equation}
\begin{desertEnvironment}
\label{desert:01}
This is a desert environment. It is labelled. Here is a labelled equation within the environment [should be (0.2)]:
\begin{equation}
\label{eq:desertEinstein}
e = mc^2
\end{equation}
The desert environment ends with this sentence.
\end{desertEnvironment}
\begin{forestEnvironment}
\label{forest:01}
This is a desert environment. It is labelled. Here is a labelled equation within the environment [should be (0.3)]:
\begin{equation}
\label{eq:forestPythagoras}
a^2 + b^2 = c^2.
\end{equation}
The desert environment ends with this sentence.
\end{forestEnvironment}
Another labelled equation that is not inside any environment [should be (0.4)]:
\begin{equation}
\label{eq:euler}
e^{i\pi} + 1 = 0.
\end{equation}
Reference to desert environment: Desert Environment \ref{desert:01} [should be 0.1].
Reference to equation inside it: Einstein \eqref{eq:desertEinstein} [should be (0.2)].
Reference to forest environment: Forest Environment \ref{forest:01} [should be 0.2].
Reference to equation inside it: Pythagoras \eqref{eq:forestPythagoras} [should be (0.3)].
Reference to outside eq'ns: Fermat \eqref{eq:fermat}, Euler \eqref{eq:euler} [should be (0.1), (0.4)].
\end{document}
答案1
声明\let\oriwrite=\write
和替换
\setbox0\vbox{\BODY}
经过
\setbox0=\vbox{\def\write{\immediate\oriwrite}\BODY}
解释:标签必须写入aux
文件。它们是异步处理的\write
,并且这些\write
节点未在中使用\shipout
,因此它们尚未最终确定。如果您\write
暂时将这些命令设置为,则\immediate
结果将真正写入辅助文件。
答案2
问题 1:
通常,\label
-command 会触发将引用数据 ( \newlabel
-entry) 写入 .aux 文件\protected@write
。这意味着没有前缀的内容\protect
会立即扩展,但所有内容都会以延迟方式写入,即当它们所在的页面被发送到 .pdf 文件/.dvi 文件时。
用desertEnvironmentOFF
and/orforestEnvironmentOFF
放入东西,而\box0
while\box0
永远不会被使用。由于盒子从未被使用,其材料永远不会出现在运出的页面上。因此,\newlabel
属于\label
最终进入其中的命令的引用数据(条目)\box0
永远不会被写入 .aux 文件。因此,相应的引用标签(将在 LaTeX 运行开始时读取 .aux 文件时从条目中定义为\r@...
宏)永远不会存在。引用不存在的引用标签会在文档文本和 .log 文件以及终端中的消息中\newlabel
产生。??
问题2:
使用desertEnvironmentOFF
和/或forestEnvironmentOFF
您希望用于\ref
打印文档中未出现的分段项目数量。
您使用 hyperref-package。使用 hyperref-package 时,它
\ref
不仅会提供表示所引用节项编号的文本短语,还会将该\ref
文本短语作为超链接提供,其目标是所引用节项。因此:当使用 hyperref 包时,则
desertEnvironmentOFF
和/或forestEnvironmentOFF
暗示\ref
“尝试”将超链接传送到文档中不存在/未出现的目的地。如果文档中没有出现某个分段项,则出现该分段项的页面不存在。因此尝试通过该分段项引用该分段项
\pageref
是没有意义的。
处理此事的方法...
...可以基于\immediate\write
和延迟之间的区别\write
:
确保使用
desertEnvironmentOFF
和/或,而不是仅在 referencing-data/ -entry 写入 .aux-file时forestEnvironmentOFF
\immediate\write
使用。这样,sectioning-counters 的值可能是正确的,但\write
\newlabel
与相关条目相关的页码\newlabel
肯定是错误的。这并不重要,因为在这种情况下页码已经过时了。内部
\refstepcounter
一直用于对分段项进行步进计数器,并且 - 由于 hyperref 正在使用 - 用于通过 创建目标锚点\hyper@anchorstart
。因此你可以修补
\hyper@anchorstart
以写入以延迟的方式,即没有\immediate
.aux 文件中为每个创建的目标锚点添加一个条目。(在下面的示例中,这是由 -macro 完成的\destinationlabel
。)
如果材料最终没有出现在文档的页面上/没有发送到文档的页面上,则该条目将不会写入 .aux 文件。
因此,在连续的 LaTeX 运行中,您可以将该条目的存在作为检测相关目标是否存在的指标。可以通过refcount 包
从属于引用标签的数据中获得要检测其存在的目标的名称。\getrefbykeydefault
在下面的例子中,宏
\InCaseDestinationInReferenceLabelExists
从引用标签中提取目标的名称,并检查相关\destinationlabel
条目是否存在/相关\destination@
宏是否定义。你可以使用它,例如
以防止在与引用标签相关联的目标不存在的情况下尝试创建超链接。\InCaseDestinationInReferenceLabelExists{⟨label⟩}{\ref}{\ref*}{{⟨label⟩}
你也可以使用它,例如
。\InCaseDestinationInReferenceLabelExists{label}{% \pageref{label}% }{% ⟨Error-message: \pageref does not make sense as no corresponding page exists⟩% }
致谢:
非常感谢Ulrike Fischer 的回答针对这个问题如何检查超目标标签是否存在。
这个答案给了我启发,让我想到在每个目的地的 .aux 文件中添加一个可验证的条目。
\documentclass[desertEnvironmentOFF, forestEnvironmentON]{article}
\usepackage{amsmath}
\usepackage{refcount}
\usepackage{hyperref}
\newif\ifdesertEnvironment
\DeclareOption{desertEnvironmentON}{\desertEnvironmenttrue}
\DeclareOption{desertEnvironmentOFF}{\desertEnvironmentfalse}
\newif\ifforestEnvironment
\DeclareOption{forestEnvironmentON}{\forestEnvironmenttrue}
\DeclareOption{forestEnvironmentOFF}{\forestEnvironmentfalse}
\ProcessOptions\relax
\makeatletter
\@ifdefinable\CopyOfWritePrimitive{%
\let\CopyOfWritePrimitive=\write
}%
\AtBeginDocument{%
\@ifdefinable\savedhyper@anchorstart{%
\let\savedhyper@anchorstart=\hyper@anchorstart
}%
\def\hyper@anchorstart#1{%
\destinationlabel{#1}%
\savedhyper@anchorstart{#1}%
}%
}%
\newcommand\destinationlabel[1]{%
\@bsphack\protected@write\@auxout{\let\write=\CopyOfWritePrimitive}{%
\string\newdestinationlabel{#1}%
}\@esphack
}%
\newcommand\newdestinationlabel[1]{%
\global\@namedef{Destination@#1}{defined}%
}%
\newcommand\InCaseDestinationInReferenceLabelExists[1]{%
\@ifundefined{Destination@\getrefbykeydefault{#1}{anchor}{\string"\string?\string?\string?\string"}}%
{\@secondoftwo}%
{\@firstoftwo}%
}%
\makeatother
\usepackage{ifthen}
\usepackage{environ}
\newcounter{environments}
\numberwithin{environments}{section}
\numberwithin{equation}{section}
\makeatletter
\ifthenelse{\boolean{desertEnvironment}}%
{\NewEnviron{desertEnvironment}[1][]%
{\vspace*{1em}\refstepcounter{environments}%
{{\bfseries Desert \theenvironments.}}%
{\itshape \BODY}\vspace*{1em}}}%
{\NewEnviron{desertEnvironment}[1][]%
{\begingroup
\def\write{\immediate\CopyOfWritePrimitive}%
\setbox0 =\vbox{\refstepcounter{environments}\BODY}%
\endgroup
}}
\ifthenelse{\boolean{forestEnvironment}}%
{\NewEnviron{forestEnvironment}[1][]%
{\vspace*{1em}\refstepcounter{environments}%
{{\bfseries Forest \theenvironments.}}%
{\itshape \BODY}\vspace*{1em}}}%
{\NewEnviron{forestEnvironment}[1][]%
{\begingroup
\def\write{\immediate\CopyOfWritePrimitive}%
\setbox0 =\vbox{\refstepcounter{environments}\BODY}%
\endgroup
}}
\renewcommand\eqref[1]{%
\textup{\tagform@{\InCaseDestinationInReferenceLabelExists{#1}{\ref}{\ref*}{#1}}}%
}%
\makeatother
\usepackage{setspace}
\setlength{\parindent}{0pt}
\setlength{\parskip}{1em}
\begin{document}
Here is some text that is not inside any custom environment. Here is a labelled equation that is not inside any custom environment [should be (0.1)]:
\begin{equation}
\label{eq:fermat}
x^n + y^n = z^n.
\end{equation}
\begin{desertEnvironment}
\label{desert:01}
This is a desert environment. It is labelled. Here is a labelled equation within the environment [should be (0.2)]:
\begin{equation}
\label{eq:desertEinstein}
e = mc^2
\end{equation}
The desert environment ends with this sentence.
\end{desertEnvironment}
\begin{forestEnvironment}
\label{forest:01}
This is a forest environment. It is labelled. Here is a labelled equation within the environment [should be (0.3)]:
\begin{equation}
\label{eq:forestPythagoras}
a^2 + b^2 = c^2.
\end{equation}
The forest environment ends with this sentence.
\end{forestEnvironment}
Another labelled equation that is not inside any environment [should be (0.4)]:
\begin{equation}
\label{eq:euler}
e^{i\pi} + 1 = 0.
\end{equation}
Reference to desert environment:
Desert Environment \InCaseDestinationInReferenceLabelExists{desert:01}{\ref}{\ref*}{desert:01}
[should be 0.1].
Reference to equation inside it: Einstein \eqref{eq:desertEinstein} [should be (0.2)].
Reference to forest environment:
Forest Environment \InCaseDestinationInReferenceLabelExists{forest:01}{\ref}{\ref*}{forest:01}
[should be 0.2].
Reference to equation inside it: Pythagoras \eqref{eq:forestPythagoras} [should be (0.3)].
Reference to outside eq'ns: Fermat \eqref{eq:fermat}, Euler \eqref{eq:euler} [should be (0.1), (0.4)].
\end{document}