Showing posts with label amsmath. Show all posts
Showing posts with label amsmath. Show all posts

Thursday, October 16, 2008

Fixing bug in hyperref's autoref: amsmath-type equations under items get "item" label

Earlier this year, in a comp.text.tex posting, I discussed a bug I found in hyperref. That bug was then fixed in hyperref 6.77a, but there are still plenty of old packages hanging around that have this bug (for example, the hyperref from TeXLive 2007 on my system that I still haven't upgraded to TeXLive 2008).

The problem happens when
  • amsmath (or mathtools) is loaded.
  • An equation (or any other type of displayed-math environment) environment with labels occurs INSIDE an enumerated item.
  • You try to use \autoref to generate a reference to one of those equation labels later.
The \autoref macro will not properly return an "Equation" label (e.g., "Equation 1"). Instead, it will return an "item" label (e.g., "item 1").

To fix this problem, I use these lines of code just after I load hyperref in my preamble:
\makeatletter
\newcommand{\AMShreffix}[1]{%
    \expandafter\let\csname old#1\expandafter\endcsname%
        \csname #1\endcsname%
    \expandafter\renewcommand\csname #1\endcsname{%
        \@hyper@itemfalse\csname old#1\endcsname}}
\makeatother
\AMShreffix{equation}
\AMShreffix{align}
\AMShreffix{gather}
The \AMShreffix (i.e., AMS-href-fix) macro "fixes" the displayed-math environment that you pass it. This example fixes equation, align, and gather. If you want to fix alignat too, add a \AMShreffix{alignat} at the end of those lines. Make similar changes for your other displayed-math environments (e.g., flalign and multline too).

That fix changes all of the improper "item" labels back into proper "Equation" labels.

Tuesday, August 07, 2007

lineno and amsmath compatibility

UPDATE 2 (READ THIS!): As Ulrich Diez points out, this can be made more compact. Here's the result (place this in the preamble, probably anywhere):
\newcommand*\patchAmsMathEnvironmentForLineno[1]{%
\expandafter\let\csname old#1\expandafter\endcsname\csname #1\endcsname
\expandafter\let\csname oldend#1\expandafter\endcsname\csname end#1\endcsname
\renewenvironment{#1}%
{\linenomath\csname old#1\endcsname}%
{\csname oldend#1\endcsname\endlinenomath}}%
\newcommand*\patchBothAmsMathEnvironmentsForLineno[1]{%
\patchAmsMathEnvironmentForLineno{#1}%
\patchAmsMathEnvironmentForLineno{#1*}}%
\AtBeginDocument{%
\patchBothAmsMathEnvironmentsForLineno{equation}%
\patchBothAmsMathEnvironmentsForLineno{align}%
\patchBothAmsMathEnvironmentsForLineno{flalign}%
\patchBothAmsMathEnvironmentsForLineno{alignat}%
\patchBothAmsMathEnvironmentsForLineno{gather}%
\patchBothAmsMathEnvironmentsForLineno{multline}%
}
This is all that is needed for the standard AMS math environments. However, if you want to add environment BLAH, you can add
\patchAmsMathEnvironmentForLineno{BLAH}
before the closing curly brace. If you want to add environments BLAH and BLAH*, you can add
\patchBothAmsMathEnvironmentsForLineno{BLAH}
before the closing curly brace.

UPDATE 1: If you use the mathlines lineno option, you may notice that amsmath environments like align may create strange double numbers at the end of the environment. I haven't looked into why this is the case. If you really need mathlines loaded, you may want to consider ways of using equation or equation* exclusively. Keep in mind that you will still need to load at least the equation and equation* lines:
\AtBeginDocument{%
%
\let\oldequation\equation%
\let\endoldequation\endequation%
\renewenvironment{equation}%
{\linenomath\oldequation}{\endoldequation\endlinenomath}%
%
\expandafter\let\expandafter\oldequationstar\csname equation*\endcsname%
\expandafter\let\expandafter\endoldequationstar\csname endequation*\endcsname%
\renewenvironment{equation*}%
{\linenomath\oldequationstar}{\endoldequationstar\endlinenomath}%
}
You may think that the displaymath option of lineno will do this for you, but loading amsmath seems to prevent that. I haven't looked into why.

A comp.text.tex post gives some history behind this fix.

It's well-known that lineno and amsmath don't play well together. After loading amsmath, the paragraph that precedes an equation, equation*, align, align*, or any of the other amsmath environments will cease to get line numbers. The ugly fix to this is to wrap every math environment with a linenomath environment. That's pretty annoying. Here's a fix that redefines the equation environment and the amsmath environments so that they'll work with noalign. Yes, it even handles the starred versions of these.
% To deal with amsmath, must redefine math environments later
\AtBeginDocument{%
%
\let\oldequation\equation%
\let\endoldequation\endequation%
\renewenvironment{equation}%
{\linenomath\oldequation}{\endoldequation\endlinenomath}%
%
\expandafter\let\expandafter\oldequationstar\csname equation*\endcsname%
\expandafter\let\expandafter\endoldequationstar\csname endequation*\endcsname%
\renewenvironment{equation*}%
{\linenomath\oldequationstar}{\endoldequationstar\endlinenomath}%
%
\let\oldalign\align%
\let\endoldalign\endalign%
\renewenvironment{align}%
{\linenomath\oldalign}{\endoldalign\endlinenomath}%
%
\expandafter\let\expandafter\oldalignstar\csname align*\endcsname%
\expandafter\let\expandafter\endoldalignstar\csname endalign*\endcsname%
\renewenvironment{align*}%
{\linenomath\oldalignstar}{\endoldalignstar\endlinenomath}%
%
\let\oldflalign\flalign%
\let\endoldflalign\endflalign%
\renewenvironment{flalign}%
{\linenomath\oldflalign}{\endoldflalign\endlinenomath}%
%
\expandafter\let\expandafter\oldflalignstar\csname flalign*\endcsname%
\expandafter\let\expandafter\endoldflalignstar\csname endflalign*\endcsname%
\renewenvironment{flalign*}%
{\linenomath\oldflalignstar}{\endoldflalignstar\endlinenomath}%
%
\let\oldalignat\alignat%
\let\endoldalignat\endalignat%
\renewenvironment{alignat}%
{\linenomath\oldalignat}{\endoldalignat\endlinenomath}%
%
\expandafter\let\expandafter\oldalignatstar\csname alignat*\endcsname%
\expandafter\let\expandafter\endoldalignatstar\csname endalignat*\endcsname%
\renewenvironment{alignat*}%
{\linenomath\oldalignatstar}{\endoldalignatstar\endlinenomath}%
%
\let\oldgather\gather%
\let\endoldgather\endgather%
\renewenvironment{gather}%
{\linenomath\oldgather}{\endoldgather\endlinenomath}%
%
\expandafter\let\expandafter\oldgatherstar\csname gather*\endcsname%
\expandafter\let\expandafter\endoldgatherstar\csname endgather*\endcsname%
\renewenvironment{gather*}%
{\linenomath\oldgatherstar}{\endoldgatherstar\endlinenomath}%
%
\let\oldmultline\multline%
\let\endoldmultline\endmultline%
\renewenvironment{multline}%
{\linenomath\oldmultline}{\endoldmultline\endlinenomath}%
%
\expandafter\let\expandafter\oldmultlinestar\csname multline*\endcsname%
\expandafter\let\expandafter\endoldmultlinestar\csname endmultline*\endcsname%
\renewenvironment{multline*}%
{\linenomath\oldmultlinestar}{\endoldmultlinestar\endlinenomath}%
%
}
The pattern is pretty clear. You can use it along with any of your own environments. For example, add this at the end (before the closing curly brace) to fix your BLAH environment:
\let\oldBLAH\BLAH%
\let\endoldBLAH\endBLAH%
\renewenvironment{BLAH}%
{\linenomath\oldBLAH}{\endoldBLAH\endlinenomath}%
and drop this at the end (before the closing curly brace) to fix your BLAH* environment:
\expandafter\let\expandafter\oldBLAHstar\csname BLAH*\endcsname%
\expandafter\let\expandafter\endoldBLAHstar\csname endBLAH*\endcsname%
\renewenvironment{BLAH*}%
{\linenomath\oldBLAHstar}{\endoldBLAHstar\endlinenomath}%
I hope that's useful for someone.

Wednesday, June 27, 2007

paralist, varioref, and hyperref: a happy combination

Consider the following...
\usepackage{varioref}
\labelformat{equation}{\textup{(#1)}}
This uses the clever varioref package to alter how equation references are displayed. The reference will be displayed with parentheses around the reference and the whole thing will be forced upright (so things don't look stupid in an italicized theorem environment). For example, for an equation labeled "eq:1" and a figure labeled "fig:1", I get these results:
\ref{eq:1} produces "(1)"
\ref{fig:1} produces "1"
This removes the need for \eqref from amsmath; just use \ref instead. Now, let's add hyperref:
\usepackage{hyperref}
So,
\ref{eq:1} produces a linked "(1)"
\ref*{eq:1} produces an unlinked "(1)"
\ref{fig:1} produces a linked "1"
\ref*{fig:1} produces an unlinked "1"
\autoref{eq:1} produces a linked "Equation (1)"
\autoref*{eq:1} produces an unlinked "Equation (1)"
\autoref{fig:1} produces a linked "Figure 1"
\autoref*{fig:1} produces an unlinked "Figure 1"
So this gives us the equivalent of an unlinked "\eqref*" through \ref*. Now, let's add paralist...
\usepackage{paralist}
\labelformat{equation}{\textup{(#1)}}
and an enumerate environment with automatic bracketed lowercase roman numeral numbering...
\begin{enumerate}[(i)]
\item This is item one. \label{item:1}
\item This is item two. \label{item:2}
\end{enumerate}
The \label macros can be associated with any enumerate item, even without paralist. The package paralist allows us to easily customize the displayed labels (enumitem is nice alternative). So, we get an enumerated output like...
 (i) This is item one.
(ii) This is item two.
Now we get...
\ref{item:1} produces a linked "(i)"
\ref*{item:1} produces an unlinked "(i)"
\autoref{item:1} produces a linked "item (i)"
\autoref*{item:1} produces an unlinked "item (i)"

Isn't life sweet? Notice the similarities between these things and stuff provided by prettyref and typedref. I think hyperref's autoref with the flexibility provided by varioref is a nice way to replace those old goodies.

Now can someone convince the Journal of Mathematical Biology to start using a bibliography style compatible with natbib? That would truly make me happy...