3

Suppose you are writing a book that contains problems and you want to be able to create two documents as output: one containing the text of the book and the problems, and one containing the problems and solutions. That seems like a fairly common situation. A sensible format for your file seems to be

\documentclass{book}

\newif\ifsolutions
\newif\iftext

\texttrue
%\solutionstrue

\newtheorem{theorem}{Theorem}
\newenvironment{problem}[1]{\item{\textit{#1}.}}{}

\begin{document}

\chapter{Theory}

\iftext
   \begin{theorem}
     If \(x\) and \(y\) then \(z\).
   \end{theorem}
\fi

\section*{Problems}

\begin{enumerate}

\begin{problem}{Generalization}
  Show that Theorem 1 can be generalized by dropping the requirement \(y\).

  \ifsolutions
     Solution: 
     First note that
     \begin{equation}
       a = b.
     \end{equation}
     The result follows from (1).
  \fi

\end{problem}

\end{enumerate}

\end{document}

(For a real book you would probably put the chapters in separate files and use the \include mechanism.)

If you want to include the text and problems, you set \texttrue and if you want to include the problems and solutions you set \solutionstrue.

Now suppose you want to include hyperrefs. All references in the text-and-problems file can be hyperlinks. (Let's assume there are no references in the text to objects in the solutions.) But references in the problems-and-solutions file to objects in the text cannot be hyperlinks --- they have to be plain text --- while references in that file to objects in the problems and solutions should be hyperlinks.

One way to proceed is to (1) use two macros for the references in the problems and solutions, one for references within the problems and solutions and one for references to object in the text, and (2) make available the relevant entries from the aux file for the text-and-problems in the problems-and-solutions file. So something like this:

 \documentclass{book}
 \usepackage{hyperref}
 \def\theoremautorefname{Theorem}

 \newif\ifsolutions
 \newif\iftext

 %\texttrue
 \solutionstrue

 \iftext
   \def\xautoref#1{\autoref{#1}}
 \else
   \def\xautoref#1{\autoref*{#1}}
   \newlabel{theorem}{{1}{1}{Theory}{theorem.1}{}}
 \fi

 \newtheorem{theorem}{Theorem}
 \newenvironment{problem}[1]{\item{\textit{#1}.}}{}

 \begin{document}

 \chapter{Theory}

 \iftext
   \begin{theorem}\label{theorem}
     If \(x\) and \(y\) then \(z\).
   \end{theorem}
 \fi

 \section*{Problems}

 \begin{enumerate}

 \begin{problem}{Generalization}
   Show that \xautoref{theorem} can be generalized by dropping the requirement \(y\).

   \ifsolutions
     Solution: 
     First note that
     \begin{equation}\label{equation}
       a = b.
     \end{equation}
     The result follows from \autoref{equation}.
   \fi

 \end{problem}

 \end{enumerate}

 \end{document}

That works, but requires the links in the problems and solutions to be coded according to whether they are internal to the problems and solutions or point to the text, and also requires the manual inclusion of the relevant \newlabels from the aux file for the text-and-problems. A better way to proceed would appear be to use a single macro for all hyperlinks and have the computer figure out whether to use plain text or a hyperref, and if plain text, what plain text to put, without manually copying the \newlabels from the aux file for the text-and-problems file.

That appears to require the system, when creating the problems-and-solutions file, to first create an aux file for the text-and-problems file, and then to check, for each hyperlink, whether to get the content from that file and display it as plain text or to get it from the aux file for the problems-and-solutions file and display it as a hyperlink. Implementing that in LaTeX seems a little challenging. Is it doable? Or is there a better way?

1 Answer 1

1

My proposal would be to be more verbose in your specifications of the text and solution components. To that end, I define environments textpart and solnpart below that should contain the text and solution parts of your document. Additionally, references to any components in your textpart should use \textref. It's only these type of references that should receive special treatment.

\documentclass{book}

\usepackage{amsmath,environ,etoolbox,hyperref}

\newif\ifsolutions
\newif\iftext

\newcommand{\textref}[1]{\iftext\ref{#1}\else\ref*{#1}\fi}

\textfalse
%\solutionstrue

\newtheorem{theorem}{Theorem}
\newenvironment{problem}[1]{\item \textit{#1}.}{}

\makeatletter
\newcommand{\textlabel}[1]{%
  \listxadd{\labellist}{{#1}{\@currentlabel}}%
}
\newcommand{\setcurrentlabel}[2]{\def\@currentlabel{#2}}%
\newcommand{\setlabel}[2]{\label{#1}}%

\newsavebox{\gobblebox}
\NewEnviron{textpart}{%
  \iftext
    \BODY % Set the text part as-is
  \else
    \def\labellist{}% Clear the list of labels
    \savebox{\gobblebox}{%
      \begin{minipage}{\textwidth}
        \let\label\textlabel% All \labels will be stored
        \BODY % Store the text part in a box (and never set it)
      \end{minipage}}
    \renewcommand*{\do}[1]{% How each store \label will be processed
      \setcurrentlabel##1%
      \setlabel##1
    }%
    \expandafter\dolistloop\expandafter{\labellist}%
  \fi
}
\makeatother

\NewEnviron{solnpart}{%
  \ifsolutions
    \BODY % Set the solution part as-is
  \else
    \begin{lrbox}{\gobblebox}
      \begin{minipage}{\textwidth}
        \BODY % Store the solution part in a box (and never set it)
      \end{minipage}
    \end{lrbox}
  \fi
}

\begin{document}

\chapter{Theory}

\begin{textpart}
  \begin{theorem}
  If \(x\) and \(y\) then \(z\). \label{thm:theorem}
  \end{theorem}
\end{textpart}

\section*{Problems}

\begin{enumerate}

  \begin{problem}{Generalization}
    Show that Theorem~\textref{thm:theorem} can be generalized by dropping the requirement \(y\).
    \label{prb:problem}

    \ifsolutions
      Solution: 
      First note that, based on Theorem~\textref{thm:theorem} and Problem~\ref{prb:problem},
      \begin{equation}
       a = b. \label{eqn:equation}
      \end{equation}
      The result follows from~\eqref{eqn:equation}.
    \fi

  \end{problem}

\end{enumerate}

\end{document}

The idea behind the textpart environment is to capture all uses of \label{<label>} and store both the <label> and current value of \@currentlabel inside a list. Then, at the end of textpart, process the list of items and issue separate \labels for each, setting \@currentlabel as appropriate. This would yield the correct reference, but not the correct name or page number. However, the definitions could be expanded to incorporate that.

Additionally, \textref sets either a \ref or a \ref*, depending on whether \texttrue or \textfalse.

The code could be simplified to only use one conditional (\iftext) since you want only two document options: (1) text-and-problems or (2) problems-and-solutions.


Here is an update that stores all the components related to a \label, allowing you to use \autoref (say):

\documentclass{book}

\usepackage{amsmath,environ,etoolbox,hyperref}

\newif\ifsolutions
\newif\iftext

\newcommand{\textref}[1]{\iftext\autoref{#1}\else\autoref*{#1}\fi}

\textfalse
%\solutionstrue

\newtheorem{theorem}{Theorem}
\newenvironment{problem}[1]{\item \textit{#1}.}{}

\makeatletter
\newcommand{\textlabel}[1]{%
  \listxadd{\labellist}{{#1}{\@currentlabel}{\thepage}{\@currentlabelname}{\@currentHref}}%
}
\newcommand{\setcurrentlabels}[5]{%
  \def\@currentlabel{#2}%
  \def\thepage{#3}%
  \def\@currentlabelname{#4}%
  \def\@currentHref{#5}%
}%
\newcommand{\setlabel}[5]{\label{#1}}%

\newsavebox{\gobblebox}
\NewEnviron{textpart}{%
  \iftext
    \BODY % Set the text part as-is
  \else
    \def\labellist{}% Clear the list of labels
    \savebox{\gobblebox}{%
      \begin{minipage}{\textwidth}
        \let\label\textlabel% All \labels will be stored
        \BODY % Store the text part in a box (and never set it)
      \end{minipage}}
    \renewcommand*{\do}[1]{% How each store \label will be processed
      \setcurrentlabels##1%
      \setlabel##1
    }%
    \dolistloop{\labellist}%
  \fi
}
\makeatother

\NewEnviron{solnpart}{%
  \ifsolutions
    \BODY % Set the solution part as-is
  \else
    \begin{lrbox}{\gobblebox}
      \begin{minipage}{\textwidth}
        \BODY % Store the solution part in a box (and never set it)
      \end{minipage}
    \end{lrbox}
  \fi
}

\begin{document}

\chapter{Theory}

\begin{textpart}
  \begin{theorem}
  If \(x\) and \(y\) then \(z\). \label{thm:theorem}
  \end{theorem}
\end{textpart}

\section*{Problems}

\begin{enumerate}

  \begin{problem}{Generalization}
    Show that \textref{thm:theorem} can be generalized by dropping the requirement \(y\).
    \label{prb:problem}

    \ifsolutions
      Solution: 
      First note that, based on \textref{thm:theorem} and \autoref{prb:problem},
      \begin{equation}
       a = b. \label{eqn:equation}
      \end{equation}
      The result follows from~\eqref{eqn:equation}.
    \fi

  \end{problem}

\end{enumerate}

\end{document}
4
  • Beautiful! I would like to use \autoref rather than \ref, so I have been trying to understand how to follow your suggestion that "the definitions could be expanded to incorporate that." (I don't care about the page number, but I'd like the labels to capture the right name.) But I'm stuck --- can you give me a hint? Commented Dec 21, 2019 at 17:36
  • @MartinJ.Osborne: I've added an update to the bottom of the answer. It allows for using \autoref. Compare the two code blocks to see what the difference is. :)
    – Werner
    Commented Dec 21, 2019 at 19:29
  • Thanks! When using the code for my book I encountered a few issues. I believe I have solved them, although I'm not sure about the last one, which I discuss in the next comment. 1. I need both \autoref and \ref, so I define \textautoref and \textref. 2. If, instead of \begin{theorem}, you say \begin{theorem}[Property \(\alpha\)], you get an error if \textfalse. But \begin{theorem}[Property $\alpha$] works fine. That seems to me to be a bug in hyperref. 3. \caption{The value of \(x\)} also causes a problem. I worked around that by using \caption[]{The value of \(x\)}. Commented Dec 22, 2019 at 17:19
  • The issue I'm not sure about is that figures in the text cause errors when \textfalse and \solutionstrue ("Not in outer par mode"). To address this issue, I have done two things. (1) After the \else in the definition of the textpart environment I have redefined the figure environment using \renewenvironment{figure}[1]{}{}. (2) That redefinition causes an error with \caption. I have worked around that by using the caption package and putting \captionsetup{type=figure} at the start of each figure in the text; I cannot see any undesirable side effects. Is there a better way? Commented Dec 22, 2019 at 17:21

You must log in to answer this question.

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