hyperref icon indicating copy to clipboard operation
hyperref copied to clipboard

Hyperref shifts location of "see" and "see also" in index

Open CDChao opened this issue 2 years ago • 5 comments

First a minimal working example is as follows.

\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 as follows. The last output line correctly sees the pageno located before the see also reference.

Index bar, 1 foo, 1, see also bar

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

Index bar, 1 foo, see also 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?

Could hyperref fix the question?

CDChao avatar Jun 04 '22 03:06 CDChao

Similar: Hyperref shifts location of "see" and "see also" in index. How to restore? on TeX-SX

While the solution in the link doesn't work as before since it produces no hyperlinks on pagenoes any more. Also I think it should be a bug of hyperref.

CDChao avatar Jun 04 '22 06:06 CDChao

While the solution in the link doesn't work as before since it produces no hyperlinks on pagenoes any more.

No hyperlinks on page numbers, really? In some examples of that answer, although package option hyperindex=false is used, hyperlink commands like \hyperpage are added back through .ist files.

When two indices share the same entry are

  • on different pages, then they're ordered by page numbers;
  • on the same page, and only one has encap field, like \index{foo}\index{foo|seealso{bar}}, then the one without encap comes first (that's why see also comes last in your example, without hyperref);
  • on the same page, and both have encap field, then they're ordered by their encap fields/strings, see relevant lines in makeindex's source file.

The third case is the case when hyperref is loaded with default setting hyperindex=true. It converts \index{foo} and \index{foo|seealso{bar}} to \indexentry{foo|hyperpage}{1} and \indexentry{foo|hyperindexformat{\seealso{bar}}}{1}, respectively. Then since i < p, see also comes first.

Inn the latex step which generates .idx, it may be impossible to keep two group of index entries having the same order:

  • \indexentry{foo}{1} and \indexextry{foo|seealso{bar}}{1}
  • \indexentry{foo|<encap1>}{1} and \indexentry{foo|<encap2>{\seealso{bar}}}{1}

But it's feasible to solve the problem in the makeindex step, which generates .ind from .idx, with a .ist style file. hyperref package manual, sec. 13.2 Index with makeindex contains one such .ist example, but it seems it doesn't work for entries with encap. Here's my attempt:

\begin{filecontents}[noheader, force]{hyperpage.ist}
delim_0 ", \\hyperpageIdx{"
delim_1 ", \\hyperpageIdx{"
delim_2 ", \\hyperpageIdx{"
delim_n "}\\nil, \\hyperpageIdx{"
delim_t "}\\nil"
encap_prefix "\\"
encap_infix "}{{"
encap_suffix "}"
\end{filecontents}

\documentclass{article}
\usepackage{makeidx}
\makeindex

\usepackage{etoolbox} % for \ifstrempty
\usepackage[hyperindex=false]{hyperref}

% no encap,   e.g., \hyperpageIdx{1}\relax
% with encap, e.g., \hyperpageIdx{\seealso{bar}}{{2}}\relax
\def\hyperpageIdx#1#2\nil{%
  \ifstrempty{#2}
    {\hyperpage{#1}}
    {\ignorespaces#1{\hyperpage#2}}%
}

\def\seexalso#1#2{\emph{see xalso} #1} % "a" < "x" so "seealso" < "seexalso" 
\begin{document}
  text\index{bar}
  \index{diff pages I}
  \index{diff pages II|seealso{bar}}
  \index{same page, one encaped}
    \index{same page, one encaped|seealso{bar}}
  \index{same page, both encaped|seealso{bar}}
    \index{same page, both encaped|seexalso{bar}}
  \newpage

  text
  \index{diff pages I|seealso{bar}}
  \index{diff pages II}
  
  \printindex
\end{document}

image

muzimuzhi avatar Jun 05 '22 00:06 muzimuzhi

While the solution in the link doesn't work as before since it produces no hyperlinks on pagenoes any more.

No hyperlinks on page numbers, really? In some examples of that answer, although package option hyperindex=false is used, hyperlink commands like \hyperpage are added back through .ist files.

When two indices share the same entry are

  • on different pages, then they're ordered by page numbers;
  • on the same page, and only one has encap field, like \index{foo}\index{foo|seealso{bar}}, then the one without encap comes first (that's why see also comes last in your example, without hyperref);
  • on the same page, and both have encap field, then they're ordered by their encap fields/strings, see relevant lines in makeindex's source file.

The third case is the case when hyperref is loaded with default setting hyperindex=true. It converts \index{foo} and \index{foo|seealso{bar}} to \indexentry{foo|hyperpage}{1} and \indexentry{foo|hyperindexformat{\seealso{bar}}}{1}, respectively. Then since i < p, see also comes first.

Inn the latex step which generates .idx, it may be impossible to keep two group of index entries having the same order:

  • \indexentry{foo}{1} and \indexextry{foo|seealso{bar}}{1}
  • \indexentry{foo|<encap1>}{1} and \indexentry{foo|<encap2>{\seealso{bar}}}{1}

But it's feasible to solve the problem in the makeindex step, which generates .ind from .idx, with a .ist style file. hyperref package manual, sec. 13.2 Index with makeindex contains one such .ist example, but it seems it doesn't work for entries with encap. Here's my attempt:

\begin{filecontents}[noheader, force]{hyperpage.ist}
delim_0 ", \\hyperpageIdx{"
delim_1 ", \\hyperpageIdx{"
delim_2 ", \\hyperpageIdx{"
delim_n "}\\nil, \\hyperpageIdx{"
delim_t "}\\nil"
encap_prefix "\\"
encap_infix "}{{"
encap_suffix "}"
\end{filecontents}

\documentclass{article}
\usepackage{makeidx}
\makeindex

\usepackage{etoolbox} % for \ifstrempty
\usepackage[hyperindex=false]{hyperref}

\makeatletter
% no encap,   e.g., \hyperpageIdx{1}\relax
% with encap, e.g., \hyperpageIdx{\seealso{bar}}{{2}}\relax
\def\hyperpageIdx#1#2\nil{%
  \ifstrempty{#2}
    {\hyperpage{#1}}
    {\ignorespaces#1{\hyperpage#2}}%
}
\makeatother

\def\seexalso#1#2{\emph{see xalso} #1} % "a" < "x" so "seealso" < "seexalso" 
\begin{document}
  text\index{bar}
  \index{diff pages I}
  \index{diff pages II|seealso{bar}}
  \index{same page, one encaped}
    \index{same page, one encaped|seealso{bar}}
  \index{same page, both encaped|seealso{bar}}
    \index{same page, both encaped|seexalso{bar}}
  \newpage

  text
  \index{diff pages I|seealso{bar}}
  \index{diff pages II}
  
  \printindex
\end{document}

image

I don't know why this doesn't work in MacTeX 2022 which is used on my Mac.

CDChao avatar Jun 05 '22 04:06 CDChao

Did you specify makeindex -s hyperpage.ist <main>?

muzimuzhi avatar Jun 05 '22 15:06 muzimuzhi