How to get the cases brace to be a link
One can try and adapt your solution How to color delimiters, which is based on collecting the material and using it in a \vphantom
. The amsmath
package has the convenient \collect@body
to help with this, but you also need to use the \ifnum0
trick of Knuth as in https://tex.stackexchange.com/a/319031/15925 to avoid grouping problems when this is used inside an align
, gather
, array
or similar constructs built on halign
s:
\documentclass{article}
\usepackage{amsmath}
\usepackage{hyperref}
\usepackage{xcolor}
\newcommand{\BracKern}{\kern-\nulldelimiterspace}
\makeatletter
\newcommand{\@Brach}[2]{%
%left bracket with hyperlink
\mathopen{\hyperlink{sec:cases}{\color{red}\left#1\vphantom{#2}\BracKern\right.}}
#2% content
}
\renewenvironment{cases}{% From amsmath.sty
{\ifnum0=`}\fi\matrix@check\cases\collect@body\env@cases}%
{\ifnum0=`{\fi}}
\def\env@cases#1{%
\@Brach{\lbrace}%
{\let\@ifnextchar\new@ifnextchar
\def\arraystretch{1.2}%
\array{@{}l@{\quad}l@{}}%
#1
\endarray}%
}
\makeatother
\begin{document}
\section{Cases Environment}\label{sec:cases}
The \verb|cases| environment is used for \dots.
\clearpage
\section{Piecewise Defined Function}
\begin{equation*}
f(x) =
\begin{cases}
1 & x < 0, \\
0 & x = 0, \\
-1 & x > 0.
\end{cases}
\end{equation*}
\begin{align*}
g(x) &= e^x,\\
f(x) &=
\begin{cases}
1 & x < 0, \\
0 & x = 0, \\
-1 & x > 0,
\end{cases}\\
h(x) &= x^2.
\end{align*}
\end{document}
Andrew Swann’s solution was the second thing I too had vaguely thought of (but, of course, it is one thing to have a vague intuition, and another to turn it into working code!), My first idea, however, was to try to implement the desired behavior at the level of TeX primitive commands. The purpose of this answer is simply to provide a sort of proof of concept of how this can be achieved: in my opinion, Andrew’s strategy remains the best one. Of course, the actual implementation depends on the typesetting engine being used, but the principle (i.e., proper placement of “whatsit” nodes within a math list) should remain the same across all engines. What follows refers specifically to pdfTeX, however.
As it is well known, after building a math list TeX converts it into a
horizontal one according to the rules detailed in Appendix G of The
TeXbook. In this conversion, a boundary item (the kind of node that
\left\{
generates) eventually gets converted into a simple box, so
what we actually want to attain is that this box be properly bracketed
between appropriate \pdfstartlink
and \pdfendlink
nodes. The idea
is that, knowing both the exact rules that govern the construction of
math lists, explained in Chapter 26, and the details of how how TeX
shuffles things around when converting a math list into a horizontal one,
one can devise the proper sequence of commands that eventually yield
the right nodes in the right places.
There is a slight complication, however: a \left<delim><math mode material>\right<delim>
construction always produces an Inner atom (p. 292) whose nucleus contains the <math mode material>
, which, during the conversion from math to horizontal list, is always enclosed within an additional level of boxing (p. 443, Rule 7, and p. 445, Rule 17, first sentence). This means that just putting a \pdfstartlink
node before \left\{
and a \pdfendlink
node after it won’t work, because the two nodes end up at different boxing levels. A simple experiment confirms this:
% My standard header for TeX.SX answers:
\documentclass[a4paper]{article} % To avoid confusion, let us explicitly
% declare the paper format.
\usepackage[T1]{fontenc} % Not always necessary, but recommended.
% End of standard header. What follows pertains to the problem at hand.
\newcommand*{\ShowLists}{%
\begingroup
\showboxbreadth = 1000
\showboxdepth = 10
\tracingonline = 1
\showlists
\endgroup
}
\begin{document}
Very simple:
\(
\special{Comment: B}
\left(
\special{Comment: E}
xyz
\right)
\ShowLists
\).\ShowLists
\end{document}
The first \ShowLists
displays the math list, as follows:
### math mode entered at line 22
\special{Comment: B}
\mathinner
.\left"28300
.\special{Comment: E}
.\mathord
..\fam1 x
.\mathord
..\fam1 y
.\mathord
..\fam1 z
.\right"29301
The second one shows the horizontal list that results from the above:
### horizontal mode entered at line 21
\hbox(0.0+0.0)x15.0
\T1/cmr/m/n/10 V
\kern-0.83313
\T1/cmr/m/n/10 e
\T1/cmr/m/n/10 r
\T1/cmr/m/n/10 y
\glue 3.33252 plus 1.66626 minus 1.11084
\T1/cmr/m/n/10 s
\T1/cmr/m/n/10 i
\T1/cmr/m/n/10 m
\T1/cmr/m/n/10 p
\T1/cmr/m/n/10 l
\T1/cmr/m/n/10 e
\T1/cmr/m/n/10 :
\glue 4.44336 plus 3.33252 minus 0.55542
\mathon
\special{Comment: B}
\hbox(7.5+2.5)x23.84497
.\hbox(7.5+2.5)x3.8889
..\OT1/cmr/m/n/10 (
.\special{Comment: E}
.\OML/cmm/m/it/10 x
.\OML/cmm/m/it/10 y
.\kern0.35878
.\OML/cmm/m/it/10 z
.\kern0.4398
.\hbox(7.5+2.5)x3.8889
..\OT1/cmr/m/n/10 )
\mathoff
\T1/cmr/m/n/10 .
spacefactor 3000
We see that, as said above, the two \special
s wind up at different boxing levels, which is not good.
But (pdf-)e-TeX has got \middle
, too. Now, I must admit that I am not aware of any documentation source which is as a precise reference for the details of math list conversion in e-TeX as Appendix G of The TeXbook is for TeX (suggestion are most welcome, of course); but we can always experiment. An idea that almost suggests itself is the following:
% My standard header for TeX.SX answers:
\documentclass[a4paper]{article} % To avoid confusion, let us explicitly
% declare the paper format.
\usepackage[T1]{fontenc} % Not always necessary, but recommended.
% End of standard header. What follows pertains to the problem at hand.
\newcommand*{\ShowLists}{%
\begingroup
\showboxbreadth = 1000
\showboxdepth = 10
\tracingonline = 1
\showlists
\endgroup
}
\begin{document}
Still pretty simple:
\(
\left. \kern -\nulldelimiterspace
\special{Comment: B}
\middle(
\special{Comment: E}
xyz
\right)
\ShowLists
\).\ShowLists
\end{document}
The final horizontal list is
### horizontal mode entered at line 21
\hbox(0.0+0.0)x15.0
\T1/cmr/m/n/10 S
\T1/cmr/m/n/10 t
\T1/cmr/m/n/10 i
\T1/cmr/m/n/10 l
\T1/cmr/m/n/10 l
\glue 3.33252 plus 1.66626 minus 1.11084
\T1/cmr/m/n/10 p
\T1/cmr/m/n/10 r
\T1/cmr/m/n/10 e
\T1/cmr/m/n/10 t
\T1/cmr/m/n/10 t
\kern-0.27771
\T1/cmr/m/n/10 y
\glue 3.33252 plus 1.66626 minus 1.11084
\T1/cmr/m/n/10 s
\T1/cmr/m/n/10 i
\T1/cmr/m/n/10 m
\T1/cmr/m/n/10 p
\T1/cmr/m/n/10 l
\T1/cmr/m/n/10 e
\T1/cmr/m/n/10 :
\glue 4.44336 plus 3.33252 minus 0.55542
\mathon
\hbox(7.5+2.5)x23.84497
.\hbox(0.0+0.0)x1.2, shifted -2.5
.\kern -1.2
.\special{Comment: B}
.\hbox(7.5+2.5)x3.8889
..\OT1/cmr/m/n/10 (
.\special{Comment: E}
.\OML/cmm/m/it/10 x
.\OML/cmm/m/it/10 y
.\kern0.35878
.\OML/cmm/m/it/10 z
.\kern0.4398
.\hbox(7.5+2.5)x3.8889
..\OT1/cmr/m/n/10 )
\mathoff
\T1/cmr/m/n/10 .
spacefactor 3000
This time we did succeed: the two “whatsit” nodes are at the same level, and bracket the box containing the parenthesis.
We are now ready to provide the proof of concept we had promised:
\documentclass[a4paper]{article}
\usepackage[T1]{fontenc}
\usepackage{amsmath}
\makeatletter
\newcommand*\hyperleft[3]{%
\left. \kern -\nulldelimiterspace
\pdfstartlink attr{#2} user{#3}
\middle #1%
\pdfendlink
}
% We prefer to keep the original "cases":
\newenvironment*{hypercases}{% From amsmath.sty
% \matrix@check\cases % obviously not needed in our case
\env@hyper@cases
}{%
\endarray\right.%
}
\newcommand*\env@hyper@cases{%
\let\@ifnextchar\new@ifnextchar
\hyperleft\lbrace
% the following two lines are obviously just a stub:
{/Border[0 0 1]/H/I/C[0 1 1]}%
{/Subtype/Link/A<</Type/Action/S/URI/URI(http://www.tug.org/)>>}%
\def\arraystretch{1.2}%
\array{@{}l@{\quad}l@{}}%
}
\makeatother
\begin{document}
\section{Piecewise Defined Function}
Two examples follow, so that you can compare results.
The first one uses the \texttt{cases} environment:
\[
f(x) =
\begin{cases}
1 & x < 0 \\
0 & x = 0 \\
-1 & x > 0.
\end{cases}
\]
The second one uses the \texttt{hypercases} environment:
\[
f(x) =
\begin{hypercases}
1 & x < 0 \\
0 & x = 0 \\
-1 & x > 0.
\end{hypercases}
\]
\end{document}
Of course, further work is needed to put this into a usable form.
Addition
I’ve had a glance to the code of the hperref
package, and it seems that the «crude hack» I mention in a comment should work as expected, albeit with a refinement.
The idea is use the method shown in the first part of this answer, but using the normal commands that hyperref
provides for link creation to deliver the \pdfstartlink
and \pdfendlink
nodes at the right place, putting all the burden of the generation of the low-level PDF syntax on the hyperref
package itself. As a first approximation, one could try with
\left. \kern -\nulldelimiterspace
\href{http://www.tug.org/}{\middle\lbrace}
(for a link to a web page) and hope that this will enclose the \middle
node between appropriate “whatsit” nodes (whose exact type depends on the driver) which, as seen above, will wind up at the right place when the math list is converted into a horizontal one. Unfortunately, this doesn’t work.
The reason is that the hyperref
package encloses the text of the link within several groups, which obviously interfere with the \left. ... \middle\lbrace ... \right.
construction. Here is where our «crude hack» steps in: we prepend to \middle\lbrace
as many \endgroup
tokens as it is required to return to the \left
group level, and append an equal number of \begingroup
tokens after it. Inspection of the code of the hyperref
package and of the hpdftex
driver suggests that this should not disrupt the proper placement of \pdfstartlink
and \pdfendlink
nodes, and indeed it seems to work. Note, however, that it will disrupt any mechanism that relies on \aftergroup
to deliver “whatsit” nodes at the end of the link; in particular, the colorlinks
option will not work with our links (see below).
The number of groups that must be exited depends not only on the command used to generate the link (e.g., \href{...}{...}
vs. \hyperref[...]{...}
), but also on other factors and, in principle, on the driver used; for example, in the case of the \href
command, it might depend on the type of the referred-to URL (e.g., http:
vs. file:
vs. run:
…). The best strategy is therefore to determine this number dynamically, either by means of \currentgrouplevel
or of \currentgrouptype
: in the code shown below we chose the second test. It is also possible to perform both test and issue an error message if they do not agree.
Actually, the ideas outlined above can be applied to a more general situation. In the code below, we define the following four commands:
\leftLinkToLabel<delim>{<label>}
typesets<delim>
as a variable-size left delimiter that is a link to the spot in the document marked with\label{<label>}
.\rightLinkToLabel<delim>{<label>}
does the same, but with a right delimiter.\leftLinkToURL<delim>{<URL>}
typesets<delim>
as a variable-size left delimiter that is a link to<URL>
.\rightLinkToURL<delim>{<URL>}
does the same, but with a right delimiter.
Of course, this set is not exhaustive, but it’s easy to extend it (for example, providing the \middle...
variants of the above commands). After doing this, we use \leftLinkTo...
to implement two \cases
-like environments that, we hope, will exhibit the required behavior. Unfortunately, we cannot use \newcases
to do so, because our new environments take an argument.
Here is the code:
\documentclass[a4paper]{article}
\usepackage[T1]{fontenc}
\usepackage{amsmath}
\usepackage{hyperref}
\makeatletter
% Common syntax: #1 = delimiter, #2 = link.
\newcommand*\leftLinkToLabel[2]{%
\@left@delimiter@wrapper{#1}{\hyperref[#2]}%
}
\newcommand*\rightLinkToLabel[2]{%
\@right@delimiter@wrapper{#1}{\hyperref[#2]}%
}
\newcommand*\leftLinkToURL[2]{%
\@left@delimiter@wrapper{#1}{\href{#2}}%
}
\newcommand*\rightLinkToURL[2]{%
\@right@delimiter@wrapper{#1}{\href{#2}}%
}
\newcommand*\@left@delimiter@wrapper[2]{%
\left. \kern -\nulldelimiterspace
\@general@link@helper{#1}{#2}%
}
\newcommand*\@right@delimiter@wrapper[2]{%
\@general@link@helper{#1}{#2}%
\kern -\nulldelimiterspace \right.
}
\@ifdefinable\@restore@groups@at@end@of@link{} % just check that we can use it
\newcommand*\@exit@to@leftright@group{%
\ifnum \currentgrouptype=\sixt@@n \else
% \typeout{Exiting group level \number\currentgrouplevel.}%
\endgroup
\xdef\@restore@groups@at@end@of@link{%
\@restore@groups@at@end@of@link
\begingroup
}%
\expandafter\@exit@to@leftright@group
\fi
}
\newcommand*\@general@link@helper[2]{%
\global\let \@restore@groups@at@end@of@link \@empty
#2{% #2 is either "\hyperref[...]" or "\href{...}"
\@exit@to@leftright@group
\middle #1%
\@restore@groups@at@end@of@link
}%
}
\newenvironment*{linkToLabelCases}{% From amsmath.sty
% \matrix@check\cases % obviously not needed in our case
\env@hyper@cases\leftLinkToLabel
}{%
\endarray\right.%
}
\newenvironment*{linkToURLCases}{% From amsmath.sty
% \matrix@check\cases % obviously not needed in our case
\env@hyper@cases\leftLinkToURL
}{%
\endarray\right.%
}
\newcommand*\env@hyper@cases[2]{%
\let\@ifnextchar\new@ifnextchar
#1\lbrace{#2}%
\def\arraystretch{1.2}%
\array{@{}l@{\quad}l@{}}%
}
\makeatother
\begin{document}
\section{Target}
\label{S:Target}
This is the target of our links.
\newpage
\section{Links}
\label{S:Links}
Some examples of links follow.
A simple bracket:
\begin{align*}
\left[xyz\right] &= \left[xyz\right] \\
\leftLinkToLabel[{S:Target}xyz\right]
&= \left[xyz\rightLinkToLabel]{S:Target}
\end{align*}
Now with fractions:
\begin{align*}
\left\{\frac{x}{y}\right\} &= \left\{\frac{x}{y}\right\} \\
\leftLinkToLabel\{{S:Target}\frac{x}{y}\right\}
&= \left\{\frac{x}{y}\rightLinkToLabel\}{S:Target}
\end{align*}
Now with matrices:
\begin{align*}
\left(
\begin{matrix}
1 & 0 & 0 \\
0 & 1 & 0 \\
0 & 0 & 1
\end{matrix}
\right) &= \left(
\begin{matrix}
1 & 0 & 0 \\
0 & 1 & 0 \\
0 & 0 & 1
\end{matrix}
\right) \\
\leftLinkToLabel({S:Target}
\begin{matrix}
1 & 0 & 0 \\
0 & 1 & 0 \\
0 & 0 & 1
\end{matrix}
\right) &= \left(
\begin{matrix}
1 & 0 & 0 \\
0 & 1 & 0 \\
0 & 0 & 1
\end{matrix}
\rightLinkToLabel){S:Target}
\end{align*}
Again with matrices, but this time linking to a URL\@:
\begin{align*}
\left(
\begin{matrix}
1 & 0 & 0 & 0 \\
0 & 1 & 0 & 0 \\
0 & 0 & 1 & 0 \\
0 & 0 & 0 & 1
\end{matrix}
\right) &= \left(
\begin{matrix}
1 & 0 & 0 & 0 \\
0 & 1 & 0 & 0 \\
0 & 0 & 1 & 0 \\
0 & 0 & 0 & 1
\end{matrix}
\right) \\
\leftLinkToURL({http://www.tug.org/}
\begin{matrix}
1 & 0 & 0 & 0 \\
0 & 1 & 0 & 0 \\
0 & 0 & 1 & 0 \\
0 & 0 & 0 & 1
\end{matrix}
\right) &= \left(
\begin{matrix}
1 & 0 & 0 & 0 \\
0 & 1 & 0 & 0 \\
0 & 0 & 1 & 0 \\
0 & 0 & 0 & 1
\end{matrix}
\rightLinkToURL){http://www.tug.org/}
\end{align*}
\section{Piecewise defined function}
\label{S:Piece}
No link at all:
\[
f(x) = \begin{cases}
1 & x < 0 \\
0 & x = 0 \\
-1 & x > 0
\end{cases}
\]
Linking to a label:
\[
f(x) = \begin{linkToLabelCases}{S:Target}
1 & x < 0 \\
0 & x = 0 \\
-1 & x > 0
\end{linkToLabelCases}
\]
Linking to a URL\@:
\[
f(x) = \begin{linkToURLCases}{http://www.tug.org/}
1 & x < 0 \\
0 & x = 0 \\
-1 & x > 0
\end{linkToURLCases}
\]
% \showboxbreadth = 1000
% \showboxdepth = 10
% \showlists
\end{document}
You can uncomment the diagnostic commands near the end to check that the nodes are properly arranged. For example, here is the excerpt of the list that pertains to the brace of the linkToURLCases
environment (the last one):
\hbox(24.09993+19.09991)x89.71133, shifted 127.64433, display
.\OML/cmm/m/it/10 f
.\kern1.0764
.\OT1/cmr/m/n/10 (
.\OML/cmm/m/it/10 x
.\OT1/cmr/m/n/10 )
.\glue(\thickmuskip) 2.77771 plus 2.77771
.\OT1/cmr/m/n/10 =
.\glue(\thickmuskip) 2.77771 plus 2.77771
.\hbox(24.09993+19.09991)x56.91278
..\hbox(0.0+0.0)x1.2, shifted -2.5
..\kern -1.2
..\pdfstartlink(*+*)x* attr{/Border[0 0 1]/H/I/C[0 1 1]} action user{/Subtype/L
ink/A<</Type/Action/S/URI/URI(http://www.tug.org/)>>}
..\vbox(0.0+42.00043)x8.8889, shifted -23.50021
...\hbox(0.0+9.00009)x8.8889
....\OMX/cmex/m/n/10 8
...\hbox(0.0+3.00003)x8.8889
....\OMX/cmex/m/n/10 >
...\hbox(0.0+18.00018)x8.8889
....\OMX/cmex/m/n/10 <
...\hbox(0.0+3.00003)x8.8889
....\OMX/cmex/m/n/10 >
...\hbox(0.0+9.00009)x8.8889
....\OMX/cmex/m/n/10 :
..\pdfendlink
..\vbox(24.09993+19.09991)x46.82388
If you want DVI output, you can check that our solution works (more or less) with the hypertex
driver as well. This is the corresponding trace that you obtain if you specify that driver and compile with latex
instead of with pdflatex
:
\hbox(24.09993+19.09991)x89.71133, shifted 127.64433, display
.\OML/cmm/m/it/10 f
.\kern1.0764
.\OT1/cmr/m/n/10 (
.\OML/cmm/m/it/10 x
.\OT1/cmr/m/n/10 )
.\glue(\thickmuskip) 2.77771 plus 2.77771
.\OT1/cmr/m/n/10 =
.\glue(\thickmuskip) 2.77771 plus 2.77771
.\hbox(24.09993+19.09991)x56.91278
..\hbox(0.0+0.0)x1.2, shifted -2.5
..\kern -1.2
..\special{html:<a href="http://www.tug.org/">}
..\vbox(0.0+42.00043)x8.8889, shifted -23.50021
...\hbox(0.0+9.00009)x8.8889
....\OMX/cmex/m/n/10 8
...\hbox(0.0+3.00003)x8.8889
....\OMX/cmex/m/n/10 >
...\hbox(0.0+18.00018)x8.8889
....\OMX/cmex/m/n/10 <
...\hbox(0.0+3.00003)x8.8889
....\OMX/cmex/m/n/10 >
...\hbox(0.0+9.00009)x8.8889
....\OMX/cmex/m/n/10 :
..\special{html:</a>}
..\vbox(24.09993+19.09991)x46.82388
On the other hand, as said above, the colorlinks
option will not work as expected, and the following trace (obtained with pdflatex
) shows what happens:
\hbox(24.09993+19.09991)x89.71133, shifted 127.64433, display
.\OML/cmm/m/it/10 f
.\kern1.0764
.\OT1/cmr/m/n/10 (
.\OML/cmm/m/it/10 x
.\OT1/cmr/m/n/10 )
.\glue(\thickmuskip) 2.77771 plus 2.77771
.\OT1/cmr/m/n/10 =
.\glue(\thickmuskip) 2.77771 plus 2.77771
.\hbox(24.09993+19.09991)x56.91278
..\hbox(0.0+0.0)x1.2, shifted -2.5
..\kern -1.2
..\pdfstartlink(*+*)x* attr{/Border[0 0 0]/H/I/C[0 1 1]} action user{/Subtype/L
ink/A<</Type/Action/S/URI/URI(http://www.tug.org/)>>}
..\pdfcolorstack 0 push {0 1 0 0 k 0 1 0 0 K}
..\pdfcolorstack 0 pop
..\vbox(0.0+42.00043)x8.8889, shifted -23.50021
...\hbox(0.0+9.00009)x8.8889
....\OMX/cmex/m/n/10 8
...\hbox(0.0+3.00003)x8.8889
....\OMX/cmex/m/n/10 >
...\hbox(0.0+18.00018)x8.8889
....\OMX/cmex/m/n/10 <
...\hbox(0.0+3.00003)x8.8889
....\OMX/cmex/m/n/10 >
...\hbox(0.0+9.00009)x8.8889
....\OMX/cmex/m/n/10 :
..\pdfendlink
..\vbox(24.09993+19.09991)x46.82388
As you see, the \pdfcolorstack 0 pop
node is delivered prematurely, so the link doesn’t get any color; but since the \pdfendlink
node, on the other hand, is in the right place, the link will nonetheless be active.
I use the orignal command, add color to the brace \color{red}\left\lbrace\normalcolor
, move back one space, make a hyperlink on that space \hyperlink{sec:cases}{\hspace{-1em}\ }
, then move forward \hspace{1em}
. It seems to work.
\expandafter\newif\csname ifGin@setpagesize\endcsname
\documentclass{article}
\usepackage{amsmath}
\usepackage{hyperref}
\usepackage{xcolor}
%% http://tex.stackexchange.com/questions/85033/colored-symbols
\newcommand*{\mathcolor}{}
\def\mathcolor#1#{\mathcoloraux{#1}}
\newcommand*{\mathcoloraux}[3]{%
\protect\leavevmode
\begingroup
\color#1{#2}#3%
\endgroup
}
\makeatletter%
\renewenvironment{cases}{% From amsmath.sty
\matrix@check\cases\env@cases
}{%
\endarray\right.%
}
\def\env@cases{%
\let\@ifnextchar\new@ifnextchar
\color{red}\left\lbrace% <--- How do I replace this,
\normalcolor
\hyperlink{sec:cases}{\hspace{-1em}\ }\hspace{1em}% <-- with something like this, but get the right size
\def\arraystretch{1.2}%
\array{@{}l@{\quad}l@{}}%
}
\makeatother
\begin{document}
\section{Cases Environment}\label{sec:cases}
The \verb|cases| environment is used for \dots.
\clearpage
\section{Piecewise Defined Function}
\begin{align*}
\exp(x) &= e^x\\
f(x) &=
{\begin{cases}
1 & x < 0, \\
-1 & x > 0,
\end{cases}} \\
g(x) &= x^2.
\end{align*}
\begin{align*}
\exp(x) &= e^x\\
f(x) &=
{\begin{cases}
1 & x < 0, \\
0 & x = 0, \\
0 & x = 0, \\
0 & x = 0, \\
-1 & x > 0,
\end{cases}} \\
g(x) &= x^2.
\end{align*}
\end{document}