Take the 2-minute tour ×
TeX - LaTeX Stack Exchange is a question and answer site for users of TeX, LaTeX, ConTeXt, and related typesetting systems. It's 100% free, no registration required.

I would like to produce a document where I can display the first and last items of each two page spread in the headers of both the left and right pages. I know I can use \markboth{}{} to collect the usual form of marks that feed \leftmark and \rightmark. But that is limited to the text of one page at a time.

There doesn't appear to be a natural way to use the forward reference mechanism to do this, but there must be a trick, right?

share|improve this question
add comment

2 Answers

The fancyhdr manual (Section 10 — Dictionary style headers, p.11) introduces exactly such a page style.

share|improve this answer
    
That is close to what I want, but for a single page at a time not a two-page spread. I've started there, but I want to know the first and last entries of the right-hand page to include some part of them in the header or footer of the left-hand page, and vice-versa. –  RBerteig Feb 4 '11 at 11:21
    
In case any one is interested in the page style described by Thorsten, the fancyhdr manual suggests the use of fix2col.sty with marks in two-column tables, but the package is a little hard to find. CTan currently has distributions for two installations at ctan.org/pkg/fix2col . I found more general information, including the actual package manual, at ftp.math.purdue.edu/mirrors/ctan.org/macros/latex ./contrib/fix2col/ . –  brannerchinese Apr 25 '11 at 21:20
add comment
up vote 2 down vote accepted

An approach that seems to be working for me is to switch to LuaTeX, specifically the LuaLaTeX package, both from the current MiKTeX distribution for Windows.

This has allowed me to write a Lua module that I load with \directlua{require"dictfun"} in my document's preamble. When the dictfun module loads, it tries to initialize a table of the headers for every page from a file named after the \jobname, found in the global tex.jobname. I used \makeevenhead and \makeoddhead to hook into the page header and call a Lua function from dictfun to update the index of header info and construct the text that belongs on this page's header. The text of the header is placed from the Lua side with a call to tex.print().

Finally, after the main body is finished, I make one more call into the Lua module to write out the current content of the index table.

This solution requires that the text be processed by lualatex at least twice with the pagination stable, but that isn't an unreasonable requirement.

I'm sure it could be done in pure TeX, but building data structures is something that native Lua does very well.

Update: Since I've been asked, here are the "interesting" bits. I have not attempted to package this in any rational way, these fragments have simply been lifted out of my book project with some light editing.

First, here are fragments out of book.text, which holds most of the prologue code and which includes all of the book's content by reference. The Makefile builds the final PDF file by compiling this file....

% Start with memoir and tweak.
\documentclass[10pt]{memoir}

% ... 
% Stuff related to fonts, sizes, page dimensions and similar cruft left out
% as not interesting here
% ... 

% Allow for more than two column layouts. My dictionary ended up being only 
% two column, but other page designs were tried, and there may be a reason 
% why I kept the multicol package anyway but I've forgotten and didn't 
% document that detail.
\usepackage{multicol}

% Make a new environment for the dictionary body. This makes it easier to 
% constrain the dictionary page headers to just the definitions pages and 
% keep them out of the front matter and appendices.
\copypagestyle{dict}{plain}

% Load our Lua code to handle a database of page marker information. It will
% read its state from a file named after the TeX job, with ".headmarks" appended.
\directlua{require"dictfun"}

% Define even and odd page headers and footers that invoke the Lua module
% to both record information about the words on the pages as well as construct 
% the TeX source string that actually becomes the content of the page header
% or footer. For my editorial sanity, the headers include the page numbers, but
% located next to the binding not the page's outer edge.
\makeevenhead{dict}{%
    \directlua{dictfun.headmarks(\thepage,"\leftmark","\rightmark")}}{}{\thepage}
\makeoddhead{dict}{\thepage}{}{%
    \directlua{dictfun.headmarks(\thepage,"\leftmark","\rightmark")}}
\makeevenfoot{dict}{\footnotesize \directlua{dictfun.footmarks(\thepage)}}{}{}
\makeoddfoot{dict}{}{}{\footnotesize \directlua{dictfun.footmarks(\thepage)}}

\pagestyle{dict}

% More setup cruft was here...

% Hijack some section commands to collect information. In particular, each word
% entry begins with a paragraphmark command naming the word. We redefine chaptermark
% and sectionmark to do nothing just in case. The markboth directive sets up the 
% information that will be passed to Lua in the page header via leftmark and 
% rightmark. 
\renewcommand{\chaptermark}[1]{}
\renewcommand{\sectionmark}[1]{}
\renewcommand{\paragraphmark}[1]{\markboth{#1}{#1}}

% Actually set the page dimensions to match a specific binding at Lulu.

%%%% PAGE DIMENSIONS
%% Set up the paper for lulu trade paperback
\setstocksize{9in}{6in}
\settrimmedsize{\stockheight}{\stockwidth}{*}
\settrims{0pt}{0pt}
\setulmarginsandblock{0.75in}{.5in}{*}
\setlrmarginsandblock{.65in}{.5in}{*}
\setheadfoot{\baselineskip}{2ex}
\setheaderspaces{*}{*}{.618}
\checkandfixthelayout


% Memoir's title page details... changed from my project, obviously
\title{Sample Dictionary}
\author{Various}
%\date{}


\begin{document}
\frontmatter

% We'll emit a title page so the PDF page block has a dust cover.
% Front.tex has fairly obvious content, including a call to maketitle to get
% that part out of the way, and also including some statistics generated
% by the project's build process. 
\input{front}

% Here's the actual dictionary content. Each word's entry is a memoir 
% paragraph that includes the plain text entry name in all caps, as well as
% the whole definition in multiple TeX paragraphs. Naturally, the content 
% is extracted from other sources as part of the build process which actually
% writes the file "block.tex" before LuaLaTeX is called on to compile the
% book.

% Now move to the next recto page and include our actual body text
% block in a suitable column count environment.
\cleardoublepage
\mainmatter
\begin{multicols}{2}
\footnotesize
\input{block}
\normalsize
\end{multicols}

\backmatter

% One final call to our Lua module gives it a chance to update its page 
% database. This is the same data flow used by the native TeX forward 
% reference system, and does require multiple passes to get all references
% placed so that even page headers can know what will be on the following 
% odd page.
\directlua{dictfun.writemarks()}
\end{document}

The LaTeX code did all the plumbing, but the Lua code actually stores and retrieves the data needed by each page header and footer. Here is an abridged version of dictfun.lua which is placed in the same folder as book.tex:

-- module to load from luatex
module(...,package.seeall)

-- Storage for the database of what words begin and end each page    
headtab = {}

-- Called from the page header with the page number, left word (first on page) 
-- and right word (last on page). Update the page database, and format a string
-- containing the first and last words over each two page spread to return.
function headmarks(pg,left,right)
    if not pg then return end
    pg = tonumber(pg)
    -- remember the index terms for this page, but strip off
    -- anything after a semicolon in each term
    headtab[pg]={left:gsub(";.*$",""),right:gsub(";.*$","")}
    local lpage, rpage
    if pg % 2 == 0 then
        -- left page
        lpage = headtab[pg]
        rpage = headtab[pg+1] or {"",""}
    else
        -- right page
        lpage = headtab[pg-1] or {"",""}
        rpage = headtab[pg]
    end
    tex.print(lpage[1].."---"..rpage[2])
end

-- Called from the page footer with the page number. I've demonstrated that
-- it can tell left from right. Other applications are left as an exercise.
function footmarks(pg)
    if not pg then return end
    pg = tonumber(pg)
    local lpage, rpage
    if pg % 2 == 0 then
        -- left page
        tex.print("left")
    else
        tex.print("right")
    end
end

-- Called when the module loads to attempt to open and read the table from
-- a file related to the project name.    
function readmarks()
    local f = assert(loadfile(tex.jobname..".headmarks"))
    if not f then headtab = {} return end
    headtab = f()
end

-- Called to write the page database back out to a file.    
function writemarks()
    local f = assert(io.open(tex.jobname..".headmarks","w"))
    if not f then return end
    f:write"return {\n"
    for _,t in ipairs(headtab) do
    f:write(string.format("  {%q,%q},\n", t[1], t[2]))
    end
    f:write"}\n"
end

-- Finally, load the page database.
readmarks()

I hope I haven't left out anything too critical. I've had to chop out a bunch of proprietary details, but I believe I've left enough behind to show how I did it.

share|improve this answer
    
So is dictfun available anywhere? –  cfr Feb 6 at 4:44
    
@cfr I haven't published it, but I don't think there is any reason why I can't do that. It will be a few days before I have a chance to dig through the project and make sure I didn't leak any proprietary information accidentally. I'll update the answer with a link "soon". –  RBerteig Feb 11 at 0:25
    
That sounds really promising. I look forward to seeing it. I'm assuming it is not MiKTeX or Windows specific? –  cfr Feb 11 at 1:30
    
My project was built on Windows with LuaLaTeX as distributed in MiKTeX, but nothing I did was Windows specific on purpose. Of course, I haven't attempted to use it on any other platform, or really even for any other project. –  RBerteig Feb 11 at 2:10
    
@cfr Here you go... –  RBerteig Feb 11 at 2:50
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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