我想制作一个文档,其中可以在左页和右页的页眉中显示每两页展开的第一项和最后一项。我知道我可以使用 来\markboth{}{}
收集通常形式的标记,即进纸\leftmark
和\rightmark
。但这仅限于一次一页的文本。
似乎没有一种自然的方法可以使用前向引用机制来做到这一点,但一定有一个技巧,对吧?
答案1
这花式高清手册(第 10 节 - 词典样式标题,第 11 页)介绍了这样的页面样式。
答案2
对我来说似乎有效的方法是切换到 LuaTeX,特别是 LuaLaTeX 包,两者都来自当前的 Windows MiKTeX 发行版。
这使得我能够编写一个 Lua 模块,并将其加载\directlua{require"dictfun"}
到文档的序言中。当模块加载时,它会尝试从全局 中找到的dictfun
中以 命名的文件初始化每个页面的标题表。我使用和挂接到页面标题中,并调用 Lua 函数来更新标题信息的索引并构造属于此页面标题的文本。标题的文本从 Lua 端通过调用 放置。\jobname
tex.jobname
\makeevenhead
\makeoddhead
dictfun
tex.print()
最后,在主体完成后,我再调用一次 Lua 模块来写出索引表的当前内容。
该解决方案要求文本至少经过 lualatex 处理两次,并且分页稳定,但这并不是一个不合理的要求。
我确信它可以在纯 TeX 中完成,但构建数据结构是原生 Lua 非常擅长的事情。
更新:既然有人问我,下面就是“有趣”的部分。我并没有试图以任何理性的方式包装这些内容,这些片段只是从我的书项目中摘录出来,并进行了一些简单的编辑。
首先,以下是 的片段book.text
,其中包含了大部分序言代码,并引用了本书的所有内容。 Makefile 通过编译此文件来构建最终的 PDF 文件……
% 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}
LaTeX 代码完成了所有管道工作,但 Lua 代码实际上存储和检索每个页眉和页脚所需的数据。以下是其精简版本,dictfun.lua
放置在与 相同的文件夹中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()
我希望我没有遗漏任何太过关键的内容。我不得不删去一些专有细节,但我相信我留下了足够多的内容来展示我是如何做到的。