隐藏环境但保留标签以供交叉引用

隐藏环境但保留标签以供交叉引用

我想做一些与这里要求(和回答)的事情非常相似的事情:

隐藏环境但保留方程标签及其数字

但我想更进一步。

我有许多自定义环境,可以通过切换开/关选项来显示或隐藏它们(在编译后的 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 文件时。

desertEnvironmentOFFand/orforestEnvironmentOFF放入东西,而\box0while\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}

在此处输入图片描述

相关内容