# Hyperref shifts location of “see” and “see also” in index. How to restore?

Unlike the very similar Place “see also” after page numbers in index question, this question differs in that it (and any solution) relates to indexing in conjunction with hyperref.

With the following MWE:

\documentclass{report}

\usepackage{makeidx}
\makeindex

\usepackage{hyperref}

\begin{document}
foo\index{foo}\index{foo|seealso{bar}}
foo\index{foo}
bar\index{bar}
\printindex
\end{document}


Index page output without the \usepackage{hyperref} line is:

Index
bar, 1


The last output line correctly sees the pageno located before the see also reference.

However, index page output including the \usepackage{hyperref} line incorrectly / undesirably relocates the see also reference before the pageno:

Index
bar, 1


Question: how can I have my cake and eat it too, i.e., include hyperref but locate pageno references before see and see also index entry text as in the output without hyperref?

[Not a xindy question]

There's a clue to what's happening in the makeindex log file, which contains the warning:

## Warning (input = test.idx, line = 2; output = test.ind, line = 7):
-- Conflicting entries: multiple encaps for the same page under same key.


This occurs when an entry is indexed multiple times on the same page with different encaps (the command that encapsulates the page number). Here's an example that illustrates this:

\documentclass{report}

\usepackage{makeidx}
\makeindex

\newcommand{\zzz}[1]{#1}
\newcommand{\aaa}[1]{#1}

\begin{document}
foo\index{foo|textbf}
\index{foo|zzz}
\index{foo|emph}
\index{foo|aaa}
\index{foo}

\printindex
\end{document}


This results in four multiple encap warnings. The ind file contains:

\begin{theindex}

\item foo, 1, \aaa{1}, \emph{1}, \textbf{1}, \zzz{1}

\end{theindex}


makeindex has listed each of the five foo entries for page one, but has sorted them in the order: no encap (\index{foo}) followed by the remaining entries that have been ordered alphabetically according to the encap value.

Now back to your MWE without hyperref:

\documentclass{report}

\usepackage{makeidx}
\makeindex

\begin{document}
foo\index{foo}\index{foo|seealso{bar}}
foo\index{foo}
bar\index{bar}
\printindex
\end{document}


Here the first and third foo index don't have an encap (\index{foo}), so makeindex can combine them into an unencapsulated entry for page 1, but the second foo index has an encap (\index{foo|seealso{bar}}). The encap value being seealso{bar}, so the .ind file contains the line:

  \item foo, 1, \seealso{bar}{1}


Again, the entry without an encap is listed first. Now let's add hyperref:

\documentclass{report}

\usepackage{makeidx}
\makeindex

\usepackage{hyperref}

\begin{document}
foo\index{foo}\index{foo|seealso{bar}}
foo\index{foo}
bar\index{bar}
\printindex
\end{document}


When hyperref is added, hyperref adds an encap to \index{foo} behind the scenes. You can see this by inspecting the .idx file, which contains:

\indexentry{foo|hyperpage}{1}
\indexentry{foo|hyperindexformat{\seealso{bar}}}{1}
\indexentry{foo|hyperpage}{1}


Again there's a multiple encaps warning and makeindex again sorts the conflicting entries alphabetically according the encap, so the .ind file now contains:

\item foo, \hyperindexformat{\seealso{bar}}{1}, \hyperpage{1}


This is what's causing the change in order. The simplest solution is to avoid the multiple encap clash by moving the cross-referenced entries to another part of the document that has a different page number. (Since \seealso ignores its second argument, the actual page number is irrelevant.) For example:

\documentclass{report}

\usepackage{makeidx}
\makeindex

\usepackage{hyperref}

\begin{document}
foo\index{foo}
foo\index{foo}
bar\index{bar}

\clearpage
\index{foo|seealso{bar}}
\printindex
\end{document}


This gets rid of the warning and moves the cross-reference to the end of the location list.

Edit: Another possibility is to switch off hyperref's hyperindex option and create a makeindex style file to wrap the location list in a command that can add hyperlinks to the page numbers. For example:

\documentclass{report}

\usepackage{filecontents}
\usepackage{makeidx}
\usepackage[hyperindex=false]{hyperref}

\makeindex

\begin{filecontents}{\jobname.ist}
delim_0 ", \\locationlist\{"
delim_1 ", \\locationlist\{"
delim_2 ", \\locationlist\{"
delim_t "\}"
delim_n ","
encap_prefix "\\encap\{\\"
encap_infix "\{"
encap_suffix "\}\}"
\end{filecontents}

\makeatletter

\newcommand*{\encap}[1]{#1}

\def\@location#1#2\end@location{%
\ifx\encap#1\relax
#2%
\else
\hyperpage{#1#2}%
\fi
}

\newcommand*{\locationlist}[1]{%
\def\locationsep{}%
\@for\location:=#1\do{%
\locationsep
\expandafter\@location\location\end@location
\def\locationsep{, }%
}%
}
\makeatother

\begin{document}
foo\index{foo}\index{foo|seealso{bar}}
foo\index{foo}
bar\index{bar}

\clearpage
bar\index{bar}

\clearpage
bar\index{bar}

\printindex
\end{document}


If the file is called test.tex, then this needs:

pdflatex test
makeindex -s test.ist -o test.ind test.idx
pdflatex test


This applies \hyperpage to the plain locations but not to the encaps. If you have other encaps (for example, \index{foo|textbf}) you'll need a wrapper command, for example:

\documentclass{report}

\usepackage{filecontents}
\usepackage{makeidx}
\usepackage[hyperindex=false]{hyperref}

\makeindex

\begin{filecontents}{\jobname.ist}
delim_0 ", \\locationlist\{"
delim_1 ", \\locationlist\{"
delim_2 ", \\locationlist\{"
delim_t "\}"
delim_n ","
encap_prefix "\\encap\{\\"
encap_infix "\{"
encap_suffix "\}\}"
\end{filecontents}

\makeatletter

\newcommand*{\encap}[1]{#1}

\def\@location#1#2\end@location{%
\ifx\encap#1\relax
#2%
\else
\hyperpage{#1#2}%
\fi
}

\newcommand*{\locationlist}[1]{%
\def\locationsep{}%
\@for\location:=#1\do{%
\locationsep
\expandafter\@location\location\end@location
\def\locationsep{, }%
}%
}
\makeatother

\newcommand*{\primaryidx}[1]{\textbf{\hyperpage{#1}}}

\begin{document}
foo\index{foo}\index{foo|seealso{bar}}
foo\index{foo}
bar\index{bar}

\clearpage
bar\index{bar}

\clearpage
bar\index{bar}
foo\index{foo|primaryidx}

\printindex
\end{document}


This produces:

Note that the "see also" part isn't at the end of the list as it's associated with page 1. (The analogous see key in glossaries fakes page Z to counteract this issue.) This problem will also occur without hyperref.

One way to get around this (if you don't want to move your \index commands to the end of the document) is to modify the location parser so that it saves the cross-reference and moves it to the end of the list:

\documentclass{report}

\usepackage{filecontents}
\usepackage{makeidx}
\usepackage[hyperindex=false]{hyperref}

\makeindex

\begin{filecontents}{\jobname.ist}
delim_0 ", \\locationlist\{"
delim_1 ", \\locationlist\{"
delim_2 ", \\locationlist\{"
delim_t "\}"
delim_n ","
encap_prefix "\\encap\{\\"
encap_infix "\{"
encap_suffix "\}\}"
\end{filecontents}

\makeatletter

\newcommand*{\encap}[1]{#1}

\def\@ifseealso#1#2\end@ifseealso#3#4{%
\ifx\seealso#1\relax
#3%
\else
#4%
\fi
}

\def\@location#1#2\end@location{%
\ifx\encap#1\relax
\@ifseealso#2\relax\end@ifseealso
{\def\crossref{\locationsep #2}}
{\locationsep #2}%
\else
\locationsep
\hyperpage{#1#2}%
\fi
}

\newcommand*{\locationlist}[1]{%
\def\locationsep{}%
\def\crossref{}%
\@for\location:=#1\do{%
\expandafter\@location\location\end@location
\def\locationsep{, }%
}%
\crossref
}
\makeatother

\newcommand*{\primaryidx}[1]{\textbf{\hyperpage{#1}}}

\begin{document}
foo\index{foo}\index{foo|seealso{bar}}
foo\index{foo}
bar\index{bar}

\clearpage
bar\index{bar}

\clearpage
bar\index{bar}
foo\index{foo|primaryidx}

\printindex
\end{document}


• Nicola, great forensics -- I learned something new. Not certain that it answers my question, since rather than an over-and-again text-rearranging method I was looking for a programmatic solution to resolve the matter once and for all. Still, I'm marking the question answered since I suspect little better can be done (@Heiko, please add your advice if you think otherwise). – Kilgore Aug 18 '16 at 5:03
• @Kilgore I've updated my answer if you don't want to rearrange the \index commands. – Nicola Talbot Aug 18 '16 at 10:22