16

For a given book which is a daily devotional, I have no page numbers at all, but each section corresponds to a day in the year, with one day per page. I generate an index of Bible references in the end of the book, and I link the Bible references to the page where they are quoted. But since there are no page numbers in the book, the index must contain the day (i.e. the secion, e.g. "13 Mai" instead of 151) instead of the page number.

So far, my (ugly) solution was to output the section name and page in the logs with:

 \typeout{Date on page \thepage:\thesection:\chaphead}%

and then use a shell script to parse the logs and modify the generated index (.ind file) with sed:

#!/bin/bash                                                                     

NAME="$1"                                                                       

while read -r page day month; do                                                
   bsmonth=$(sed -e 's@\\@\\\\@g' <<<$month)                                    
   sed -i "s@textrm{\([^,]*, \)\?$page\(, [^,]*\)\?}@textrm{\1\\\\hyperlink{page.$page}{\\\\mbox{\\\\scshape\\\\makedate{$day}~$bsmonth}}\2}@g" $NAME.ind
done < <(sed -n 's@Date on page \([0-9]\+\):\([0-9]\+\):\(.*\)@\1 \2 \3@p' $NAME.log)

I would call LaTeX this way:

%.pdf: %.tex            
    TEXINPUTS=$(TEXINPUTS) pdflatex -shell-escape -interaction=batchmode $< 
    # Modify index to use dates instead of pages                            
    ./index_dates.sh $*                                                     
    # No -shell-escape to prevent re-creation of index                      
    TEXINPUTS=$(TEXINPUTS) pdflatex -interaction=batchmode $<   

I'm using imakeidx so the index is automatically regenerated when I compile the document. In TeXLive 2009, not passing -shell-escape prevented imakeidx from rebuilding the index, and thus from erasing the changes made by index_dates.sh, so it worked fine (although still ugly).

Now in TeXLive 2010, the -shell-escape trick doesn't work, and before I switch to another ugly trick, I'd like to think of rewriting this piece of code in proper TeX. Can you give me some hint as to how to do it properly?

  • Should I generate dynamic macros such as:

    \def\pagemonth@\csname \thepage{\thesection~\chaphead}
    

and then modify the index? If so, how do I modify the index?

  • Should I write these macros to a separate file and import it later on (and how to do that)?

  • Should I hook into imakeidx (and how)?

  • Should I replace imakeidx with another package that would allow this more easily?

  • Should I switch to another index builder (such as xindy) and what parameters would I pass it?

  • Should I add a "style" for page numbers in the index which would actually be a call to a macro replacing with my content?

4
  • 3
    I would title this question something like: "Can index entries (from makeindex) refer to something other than the page numbers?" to make it clearer what you are trying to achieve (the shell script is incidental to the problem, it seems). Jun 16, 2011 at 9:40
  • @Andrew: ack, done.
    – raphink
    Jun 16, 2011 at 9:43
  • it's a glossaries job, I believe.
    – egreg
    Jun 16, 2011 at 11:00
  • @egreg: the thing is, I'm not generating the index myself, the bibleref package with the ibibleverse command calls index. Can glossaries interface with that?
    – raphink
    Jun 16, 2011 at 12:17

2 Answers 2

10

Here is a possible solution. I show it with sections, it should be easy to adapt to your setting.

(1) I define a counter and step it each time a new section is started with the \Section command, which defines a unique command holding the section's title and whose name depends on the value of the counter.

(2) I get copies of the relevant indexing commands and patch them: \Index requires \@Index which in turn requires \@Wrindex; \@Wrindex is the heart of the process, because it writes the entry in the .idx file. So I patch it in such a way that it immediately writes the current value of the counter. Here \protected@iwrite is the same as \protected@write, but does an \immediate\write instead of the usual delayed \write, since we don't need the page number which is known only during the output routine.

(3) The real \Index command is a wrapper around a useful function of makeindex, usually employed for "See…".

(4) We pass makeindex the -s raphink option, where raphink.ist is a style file containing only

delim_0 ""

that will not add a comma after an entry.

Now a command such as \Index{Veni Sancte Spiritus} in the "January 1st" section will write

\indexentry{Veni Sancte Spiritus|transform}{1}

in the .idx file (assuming that 1 is the current value of the counter). If we write also \Index{Veni Sancte Spiritus} on Pentecost, we'll have an entry such as

\indexentry{Veni Sancte Spiritus|transform}{150}

Processing with makeindex -s raphink (done automatically by imakeidx) will give the following in the .ind file:

\item Veni Sancte Spiritus \transform{1, 150}

The macro \transform reads the comma separated list and splits it using then the commands \csname cursec1\endcsname and \csname cursec150\endcsname that contain the correct section titles.

The implementation

Here are the macros in a sample document.

\begin{filecontents*}{raphink.ist}
delim_0 ""
\end{filecontents*}
\documentclass[a4paper]{report}
\usepackage{etoolbox}
\newcounter{indexcnt}
\newcommand\Section[1]{%
  \stepcounter{indexcnt}%
  \expandafter\gdef\csname cursec\theindexcnt\endcsname{#1}%
  \section{#1}}

\usepackage{imakeidx}
\makeindex[options=-s raphink]

\makeatletter
\let\@Index\@index
\patchcmd{\@Index}{\@wrindex}{\@Wrindex}{}{}
\let\@Wrindex\@wrindex
\patchcmd{\@Wrindex}{\thepage}{\theindexcnt}{}{}
\let\xIndex\index
\patchcmd{\xIndex}{\@index}{\@Index}{}{}
\patchcmd{\xIndex}{\@index}{\@Index}{}{}
\let\protected@iwrite\protected@write
\patchcmd{\protected@iwrite}{\write}{\immediate\write}{}{}
\patchcmd{\@Wrindex}{\protected@write}{\protected@iwrite}{}{}
\makeatother

\newcommand{\Index}[1]{\xIndex{#1|transform}}
\newcommand{\transform}[1]{\forcsvlist\decodesec{#1}}
\newcommand{\decodesec}[1]{, \csname cursec#1\endcsname}

\begin{document}

\chapter{Christmas}

\Section{January 1st}

...
Veni Sancte Spiritus\Index{Veni Sancte Spiritus}
...

\chapter{Easter}

\Section{Pentecost}

...
Veni Sancte Spiritus\Index{Veni Sancte Spiritus}
...


\printindex

\end{document}
12
  • @Raphink A line was missing, try now.
    – egreg
    Jun 16, 2011 at 16:50
  • @egreg: ok the example works now, but I can't adapt it to my needs somehow... I'm trying \renewcommand*{\biblerefindex}[1]{\xIndex{#1|transform}} since bibleref.sty has \newcommand*{\biblerefindex}{\index}...
    – raphink
    Jun 16, 2011 at 17:20
  • My bad, patching a command before defining it is a bad idea ;-)
    – raphink
    Jun 16, 2011 at 18:07
  • @Raphink: definitely. :-) Let me know about the result.
    – egreg
    Jun 16, 2011 at 18:16
  • There is something I don't understand... I have \expandafter\gdef\csname pagedate\thepage\endcsname{\pagedate}% when a section is triggered. I expect the value of \pagedate to be stored in the \pagedate234 macro (as an example), but it seems \pagedate is executed when the macro is called. How can I store the value of \pagedate instead of the definition of the macro?
    – raphink
    Jun 16, 2011 at 21:07
3

Here is my final implementation of @egreg's answer. It uses \thepage in spite of his idea to use a counter. The reason for that is that I want to generate hyperlinks in the index, which is not possible if I don't know the page to link to, and I don't want to play with hundreds of dynamic labels. All in all, this works and I'm happy with it:

\newcommand{\getpagedate}[1]{%                                                  
  \csname pagedate#1\endcsname                                                  
}                                                                               

\usepackage{etoolbox}                                                           
\newcommand{\transform}[1]{                                                     
  \def\secondparam{0}%                                                          
  \forcsvlist\decodesec{#1}}                                                    
\newcommand{\decodesec}[1]{%                                                    
  \ifthenelse{\secondparam=1}{, }{}%                                            
  \hyperlink{page.#1}{\mbox{\scshape\getpagedate{#1}}}%                         
  \def\secondparam{1}}                                                          
\renewcommand*{\bvidxpgformat}{transform} 

\newcommand{\dvday}[1]{\newpage\section{#1}%                                   
  \expandafter\xdef\csname pagedate\thepage\endcsname{\noexpand\makedate{\thesection}~\chaphead}%                                                                         
}

And here is a picture of the result:

index with dates

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .