1

I'd like to do something that is very similar to what is asked for (and answered) here:

Hiding an environment but keeping equation labels and their numbers inside,

but I'd like to go one step further.

I have a number of custom environments that I can show or hide (in the PDF output after compiling) by toggling an ON/OFF option.

I might have a labelled equation inside a certain environment, which I will label with \label{marker} and refer to in another part of the document with \eqref{marker}, outside of that type of environment. When I choose to hide that type of environment, I still want each of these now-hidden equations to be counted by the equation counter, and I can do this, thanks to the above-linked post and response. However, I also want to still be able to reference these hidden equations in other (unhidden) parts of the document, and this is the part I am having trouble with. (At first I thought that the solution with \setbox0\vbox in the above-linked post would work perfectly, but when I copy-paste the code from that post and compile, \eqref{marker} outputs (??) when \label{marker} is inside the hidden environment.)

At the moment, I just have equations inside the optionally-hidden environments, but at some point I may want to do the same with figures and other things one can label and cross-reference with \label{marker} and \ref{marker} or \eqref{marker}. So, an all-in-one (or many-in-one) solution is preferable to one that just deals with equations.

Effectively, I want to hide the visual output, but keep all the "behind the scenes" stuff in the .aux file etc., as if the visual output were not hidden.

I can imagine that my problem may not be well-defined, since changing the visual output will change page numbers and locations of things within the document. I am using hyperref but I don't really care what happens with the links to hidden equations. I don't envisage ever needing \pageref{key} with \label{key} inside a hidden environment.

Perhaps another way of thinking of my goal is this: I essentially want to shrink the output of a type of environment to a point so small that it is invisible to the naked eye, but not to LaTeX.

In fact, in the MWE provided, if I first compile with everything unhidden, then compile with one or both environments hidden, everything looks just as I want it and the data is all in the .aux file. When I compile a second time, the data is removed from the .aux file and the equation references appear as (??).

I tried to research solutions involving \immediate\write somehow, but unfortunately this seems too advanced for me to understand at present. I've also tried to understand

Hide output, but maintain the cross-references

and

hide specific table, keep cross-references and caption in \listoftables,

which seem related, among other posts, but to no avail.

Any help or advice greatly appreciated.

\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}

2 Answers 2

1

Declare \let\oriwrite=\write and replace

\setbox0\vbox{\BODY}

by

\setbox0=\vbox{\def\write{\immediate\oriwrite}\BODY}

The explanation: the labels must be written to the aux file. They are processed by asynchronous \write and these \write nodes are not used in \shipout, so, they are not finalized. If you set these \write commands temporarily as \immediate then the result is really written to the aux file.

1
  • Works perfectly. Thank-you for the elegant solution and lucid explanation.
    – larivee
    Jul 10, 2020 at 21:05
0

Problem 1:

Usually the \label-command triggers writing referencing-data (\newlabel-entry) to the .aux-file in terms of \protected@write. This means things that are not prefixed by \protect get expanded immediately but all things get written in delayed fashion, i.e., when the page whereon they occur is shipped out to the .pdf-file/.dvi-file.

With desertEnvironmentOFF and/or forestEnvironmentOFF things are put into \box0 while \box0 is never used. As the box is never used, its material never ends up on a page that is shipped out. Thus referencing-data (\newlabel-entries) belonging to \label-commands that end up inside \box0 will never be written to the .aux-file. Thus the corresponding referencing-labels (which will be defined as \r@...-macros from the \newlabel-entries when reading the .aux-file at the beginning of the LaTeX-run) will never exist. Referencing not-existing referencing-labels yields ?? in the text of the document and messages in the .log-file and on the terminal.

Problem 2:

With desertEnvironmentOFF and/or forestEnvironmentOFF you wish to use \ref for printing numbers of items of sectioning that do not occur in the document.

  • You use the hyperref-package. When the hyperref-package is in use, then \ref does not only deliver a textual phrase representing the number of the referenced item of sectioning. Then \ref delivers that textual phrase as a hyperlink whose destination is the referenced item of sectioning.

    Thus: When using the hyperref-package, then desertEnvironmentOFF and/or forestEnvironmentOFF implies that \ref "attempts" to deliver a hyperlink to a destination which does not exist/which does not occur in the document.

  • If an item of sectioning does not occur in the document, then pages whereon that item of sectioning occurs do not exist. Thus trying to reference such an item of sectioning via \pageref does not make sense.

An approach to the matter...

...could be based on the distinction between \immediate\write and delayed \write:

  • Make sure that with desertEnvironmentOFF and/or forestEnvironmentOFF \immediate\write is in use instead of just \write when referencing-data/\newlabel-entry is written to .aux-file. This way values of sectioning-counters might be correct but page-numbers associated to the \newlabel-entries in question will definitely be wrong. That doesn't matter as page-numbers are obsolete in this case.

  • Internally \refstepcounter is used all the time for stepping counters of items of sectioning and - as hyperref is in use - for creating destination-anchors via \hyper@anchorstart.

    Thus you can patch \hyper@anchorstart to write in delayed fashion, i.e., without \immediate an entry to the .aux-file for each destination-anchor that gets created. (In the example below this is done by the \destinationlabel-macro.)
    In case material does not end up on a page of the document/is not shipped out to a page of the document, that entry will not be written to the .aux-file.
    Thus in consecutive LaTeX-runs you can take the existence of that entry for an indicator for detecting whether the destination in question exists.
    The name of the destination whose existence is to be detected can be obtained from the data belonging to a referencing-label via \getrefbykeydefault from the refcount-package.

    In the example below the macro \InCaseDestinationInReferenceLabelExists extracts the name of the destination from a referencing-label and checks whether the associated \destinationlabel-entry exists/whether the associated \destination@-macro is defined.

    You can use it, e.g., as

    \InCaseDestinationInReferenceLabelExists{⟨label⟩}{\ref}{\ref*}{{⟨label⟩}
    in order to prevent the attempt of creating hyperlinks in situations where the destination associated to the referencing-label does not exist.

    You can probably also use it, e.g., as

    \InCaseDestinationInReferenceLabelExists{label}{%
      \pageref{label}%
    }{%
      ⟨Error-message: \pageref does not make sense as no corresponding page exists⟩%
    }
    .

Thanks and Credits:

Many thanks to Ulrike Fischer for her answer to the question How to check if hypertarget label exists.

This answer gave me inspiration for the idea of ​​adding a verifiable entry in the .aux file for each destination.

\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}

enter image description here

3
  • This works perfectly, too. Thank-you for the comprehensive response. I wish I could accept both answers. I'm not really qualified to judge which is "better". All I can say is that, for my present purposes, both solutions appear to do what I want to do. While wipet's solution is short and sweet, Ulrich Diez's solution seems like it has anticipated and addressed some potential issues that may come up in the future, or that other people trying to do similar things may come across.
    – larivee
    Jul 11, 2020 at 13:17
  • @larivee Regarding making referencing-labels of things that do not occur in the document available by turning \write into \immediate\write wipet's answer and mine are basically the same. But one problem when using hyperref is that this strategy also may cause writing names of hyperlink-destinations to .aux-file's \newlabel-entries which don't exist as the things where they are to be placed do not occur in the .pdf-file. When (with hyperref) \ref attempts to produce the hyperlink, this yields... Jul 11, 2020 at 14:05
  • @larivee When (with hyperref) \ref attempts to produce the hyperlink, this yields these pdfTeX warning (dest): name{equation.0.2} has been referenced but does not exist, replaced by a fixed one-messages in the .log-file and clicking the hyperlink will navigate you to the very first page of the document. I addressed this issue in my answer by providing \InCaseDestinationInReferenceLabelExists as a means for deciding whether \ref (the hyperlink-producing-variant) or \ref* (the variant without hyperlink) should be used. Jul 11, 2020 at 14:06

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .