2

I would like to force the expansion of a macro in the \index command. This is a minimal example:

\documentclass{book}
\usepackage{makeidx}
\makeindex

\begin{document}
Text. Text. Text. Text. Text. Text. \index{page number \arabic{page}} Text. Text. Text.
\printindex
\end{document}

After compilation, the index is on page 2, and the index entry reads as “page number 2, 1”.

What I would want is that it reads “page number 1, 1”, since the command was issued on page 1.

Thank you for your help.

  • accessing the page number as \arabic{page} gives a very unreliable number even without an index, it should always be accessed via \pageref. within a given paragraph all values of \arabic{page} will be the same, even if the paragraph spans multiple pages. – David Carlisle May 30 at 14:46
  • Isn't the page number provided by the indexing? That's its purpose! I guess your case is not about the page number, is it? – egreg May 30 at 15:07
3

I usually really don't need an index entry which tells me that the page whose number is 1 can be found on/as page 1. ;-)

Why do you want something like that?

Be that as it may.

Let's take the issue for a moot challenge.

You will face several problems:

  1. You cannot generally predict whether material processed currently will end up on the page which LaTeX is currently about to construct or will end up on the next page.
    You can rely on \arabic{page} yielding the number of the page which LaTeX is currently about to construct, but you cannot predict whether the material which you are attempting to denote via this expression will end up on the page which LaTeX is currently about to construct or on the page following that page.

    Therefore you usually need to use the \label..\pageref-mechanism or one of its derivates for denoting things by naming the page numbers where the things in question appear.

  2. The \index-macro does some attempt at preventing material which goes into index-entries/which goes into the .idx-file and by makeindex is used for sorting and creating the .ind-file from being expanded.
    If you look at its definition via \show\index you will find that it does (via \@sanitize) some catcode-trickery for ensuring that things which usually are taken for single control sequence tokens will be taken for ordinary character token squences.
    E.g., \macro shall not be taken for the control sequence token \macro but it shall be taken for a sequence of character tokens \, m, a, c, r, o.

    You need to override these attempts at expansion-prevention because

    • you don't want all these index-entries sorted as page number \arabic{page} but you want them sorted as page number 1, page number 2, ...
    • you don't want their expansion delayed until due to \printindex having processed the .ind-file because then all these entries would yield the number which the page counter has at the time of printing that portion of the index where the corresponding index entry does occur in the index instead of having the number of that page where the corresponding material can be found within the main text of the document.
  3. You need to take care of the \@bsphack..\@esphack-thingie done both by \label and by \index when placing \label and \index right behind each other.

I can offer a umbersome approach where the \getrefbykeydefault-macro of the refcount package by Heiko Oberdiek is used for referencing page numbers (that macro requires two expansions/two "hits" with \expandafter for delivering the desired value) and where a combination of \expandafter and a selfmade macro \romannumeral\ExchangeWithFirstExpanded is used both for overriding \index's expansion-prevention and for obtaining the numerical value which comes from \label..\pageref/\getrefbykeydefault-referencing immediately at the time of processing the \index-command. (\romannumeral is used only for triggering expansion. It is used in a way where it does not deliver any token.)

You need to make sure to compile the code as many times as necessary for not obtaining the warning LaTeX Warning: Label(s) may have changed. Rerun to get cross-references right any more before for the first time calling makeindex as otherwise index entries will not be formed by the correct phrases.

I decided to place the commands for creating the referencing label and the index-entry into a macro of its own:

\documentclass{book}

\makeatletter
% \romannumeral\ExchangeWithFirstExpanded{<k>}{<first>}{<second>}
% yields:
% <second>< <first> "hit" k times by \expandafter >
\newcommand\@exchange[2]{#2#1}%
\newcommand*\@innerdfork{}%
\def\@innerdfork#1d#2#3dd{#2}%
\newcommand*\@dfork[1]{\@innerdfork#1{\@firstoftwo}d{\@secondoftwo}dd}%
\newcommand\ExchangeWithFirstExpanded[3]{%
  0\expandafter\@exchange\expandafter{\romannumeral\number\number#1 000d}{\@innerExp{}{#2}{#3}}%
}%
\newcommand\@innerExp[4]{%
  \@dfork{#4}{#1\@exchange#1{#2}{ #3}}{\@innerExp{#1#1\expandafter}{#2}{#3}}%
}%

% 
%\PageNuberAlsoInIndexEntry{<name of to be placed referencing label>}
%
\newcommand\PageNuberAlsoInIndexEntry[1]{%
  \label{#1}% <-This will - due to \@esphack - switch \lastskip to 0, so we need a trick for restoring \lastskip:
  \ifhmode\nobreak\hskip-\@savsk\nobreak\hskip\@savsk\fi
  \expandafter\index
    \expandafter{\romannumeral\ExchangeWithFirstExpanded{2}{\getrefbykeydefault{#1}{page}{0}}{page number }%
  }%
}%
\makeatother

\usepackage{makeidx}
\usepackage{refcount}

\makeindex

\begin{document}
Text. Text. Text. Text. Text. Text. \PageNuberAlsoInIndexEntry{NicePage} Text. Text. Text.
\printindex
\end{document}

enter image description here

enter image description here



From your comment

The original problem was this one: We are translating a book from English to French which has maybe 100 pages of endnotes. Names in the endnotes are referred to in the index in this way: Pythagoras, 412n35, which means “endnote number 35, on page 412”.

I learned that the problem seems not to be about expanding material within an \index-command but about providing correct syntax within index-entries:

There is the |-thingie with index-entries for the makeindex-program:

\index{Pythagoras|bar} means that the page number belonging to the index-entry Pythagoras will be wrapped into \bar when due to \printindex printing the index entries. This means you will get something like:
Pythagoras, \bar{412}.

You can use this feature and

  1. define a macro \bar which will prepend the letter n and the value of the endnote-counter.

  2. define your own variant of the \index-command which does append the |bar thingie automatically.

In the example below I did this and I also tried to add some support for the hyperref-package.

The latter implied that I had to patch/redefine some of the internal macros of the endnotes-package.

When loading the hyperref-package (the decision whether to load it or not is up to you):

With \endnote you will get an endnote with the endnote-mark having a link to the endnote-text.

With \endnote* you will get an endnote without the endnote-mark having a link to the endnote-text.

With \IndexWithEndnotemark{index entry} inside the argument of \endnote you will, after (re-)running makeindex, get an index-entry where the letter n and the value of the endnote-counter is appended to the page number and where this page-number-construct is wrapped into a hyperlink leading to the corresponding endnote-text.

With \IndexWithEndnotemark*{index entry} inside the argument of \endnote you will, after (re-)running makeindex, get an index-entry where the letter n and the value of the endnote-counter is appended to the page number and where this page-number-construct is not wrapped into a hyperlink.

When not loading the hyperref-package you never get hyperlinks.

\documentclass{book}

\makeatletter
% This must take place before loading hyperref.
\newcommand\MySaved@Wrindex@NoHyperref{}%
\let\MySaved@Wrindex@NoHyperref=\@wrindex
\makeatother

\usepackage{makeidx}
\usepackage{endnotes}
\usepackage{hyperref}

\makeatletter
\newcounter{totalendnotes}%
\newcommand*\savedendnote{}%
\let\savedendnote\endnote
\newcommand*\savedendnotemark{}%
\let\savedendnotemark\endnotemark
\newcommand\savedbaselineskip{}%
\newcommand\numberofendnoteanchor{}%
\newcommand\hypercommandforendnote[2]{#2}%
\newcommand\IndexWithEndnotemark@index{}%
\let\IndexWithEndnotemark@index=\index
%%
%%  \IndexWithEndnotemark{index entry}
%%
\newcommand\IndexWithEndnotemark{%
  \@bsphack
  \begingroup\@sanitize
  \@ifstar\@IndexWithEndnotemarkAtStar\@IndexWithEndnotemarkAtNoStar
}%
\newcommand\@IndexWithEndnotemarkAtStar[1]{%
  \ifx\IndexWithEndnotemark@index\index
     \expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi
  \@index\MySaved@Wrindex@NoHyperref{#1|addtheenmarkAtStar{n}{\@theenmark}{\numberofendnoteanchor}}%
}%
\newcommand*\addtheenmarkAtStar[4]{#4#1#2}%
\newcommand\@IndexWithEndnotemarkAtNoStar[1]{%
  \ifx\IndexWithEndnotemark@index\index
     \expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi
  \@index\MySaved@Wrindex@NoHyperref{#1|addtheenmarkAtNoStar{n}{\@theenmark}{\numberofendnoteanchor}}%
}%
\newcommand*\addtheenmarkAtNoStar[4]{\sethypercommandforendnote{\hyperlink{endnote:#3}}{#4#1#2}}%
%
\AtBeginDocument{%
  \@ifpackageloaded{hyperref}{%
    \newcommand\sethypercommandforendnote[1]{#1}%
  }{%
    \newcommand\sethypercommandforendnote[1]{}%
  }%
}%
%
\def\endnote{%
  \stepcounter{totalendnotes}%
  \def\numberofendnoteanchor{\thetotalendnotes}%
  \@ifstar{%
    \savedendnote
  }{%
    \sethypercommandforendnote{\def\hypercommandforendnote{\hyperlink}}%
    \savedendnote
  }%
}%
\def\endnotemark{%
   \stepcounter{totalendnotes}%
   \def\numberofendnoteanchor{\thetotalendnotes}%
   \@ifstar{%
     \savedendnotemark
   }{%
     \sethypercommandforendnote{\def\hypercommandforendnote{\hyperlink}}%
     \savedendnotemark
   }%
}%
\def\@makeenmark{%
  \hbox{%
    \@textsuperscript{%
      \normalfont
      \hypercommandforendnote{endnote:\numberofendnoteanchor}{\@theenmark}%
     }%
   }%
}%
\long\def\@endnotetext#1{%
     \if@enotesopen \else \@openenotes \fi
     \immediate\write\@enotes{\@doanenote{\@theenmark}{\thetotalendnotes}}%
     \begingroup
        \def\next{#1}%
        \newlinechar='40
        \immediate\write\@enotes{\meaning\next}%
     \endgroup
     \immediate\write\@enotes{\@endanenote}%
}%
\def\theendnotes{\immediate\closeout\@enotes \global\@enotesopenfalse
  \begingroup
    \edef\savedbaselineskip{\the\baselineskip}%
    \makeatletter
    \edef\@tempa{`\string >}%
    \ifnum\catcode\@tempa=12%
      \let\@ResetGT\relax
    \else
      \edef\@ResetGT{\noexpand\catcode\@tempa=\the\catcode\@tempa}%
      \@makeother\>%
    \fi
    \def\@doanenote##1##2##3>{\def\@theenmark{##1}\par\begingroup
        \@ResetGT
        \def\numberofendnoteanchor{##2}%
        \sethypercommandforendnote{%
          \def\hypercommandforendnote####1####2{%
            {%
              \def\HyperRaiseLinkHook{%
                \setlength\HyperRaiseLinkLength{\savedbaselineskip}%
                \setlength\HyperRaiseLinkLength{.5\HyperRaiseLinkLength}%
              }%
              \Hy@raisedlink{\hypertarget{####1}{}}%
            }%
            ####2%
          }%
        }%
        \edef\@currentlabel{\csname p@endnote\endcsname\@theenmark}%
        \enoteformat}
    \def\@endanenote{\par\endgroup}%
    \enoteheading
    \enotesize
    \input{\jobname.ent}%
  \endgroup
}%
\makeatother

\makeindex

\begin{document}
Some drivel about Pythagoras.%
\endnote{\IndexWithEndnotemark{Pythagoras}The first endnote related to drivel about Pythagoras.}

Some more drivel about Pythagoras.%
\endnote{\IndexWithEndnotemark{Pythagoras}The second endnote related to drivel about Pythagoras.}

\theendnotes

\printindex
\end{document}

enter image description here

enter image description here

  • Ulrich and egreg, yes of course, my purpose was not to get the page number in two different ways in an index line. – André Bellaïche Jun 1 at 16:22
  • The original problem was this one: We are translating a book from English to French which has maybe 100 pages of endnotes. Names in the endnotes are referred to in the index in this way: Pythagoras, 412n35, which means “endnote number 35, on page 412”. How to get the 35 from the endnote counter, while the \index command is trying as much as it can not to expand macros, was what I could not succeed to do. Thank you Ulrich, I will try to adapt your solution. – André Bellaïche Jun 1 at 16:29
  • @AndréBellaïche I just edited my answer for providing a solution to the original problem. – Ulrich Diez Jun 2 at 0:10

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.