本文已加入专栏文章目录,归入「进阶使用」文章系列。

本文涉及的宏包

  • hyperref
  • bookmark,生成 PDF 书签,依赖 hyperref
  • tocbibind,为目录、参考文献和索引生成目录项

默认情况

章节命令(以 section 为例)

section [<short title>]{<long title>}
section*[<short title>]{<long title>}

分别生成编号和不编号的章节标题。默认情况下,

  • 编号的章节标题,产生目录项,即在 ableofcontents 生成的目录中有对应的条目。
  • 不编号的章节标题,不产生目录项。

如果使用 hyperref/bookmark 宏包来生成 PDF 书签,那么

  • 编号的章节标题,还产生 PDF 书签。点击标题对应的书签和目录项,将跳转到正文中章节标题出现的位置。
  • 不编号的章节标题,不产生 PDF 书签。

不编号章节举例

以书籍举例,序言、前言、参考文献和索引部分的标题,是常见的不编号章节标题。特别地,由 ableofcontents 产生的目录部分的标题,也是不编号的章节标题。

这些不编号章节,按生成方式,又有两类

  • 手动生成的,例如 section*{前言}
  • 自动生成的,例如命令 ableofcontentsprintindex就会自动生成目录和索引部分的标题。这些命令的内部,也使用了带星号的章节命令,如 section*{...}

把不编号章节同时加入目录和 PDF 书签

手动生成的

section*{前言}
addcontentsline{toc}{section}{前言}

addcontentsline 必须出现在章节标题命令之后。

自动生成的

  • 理论上,需要用户了解并修改 ableofcontents 命令的定义,往里加入 addcontentsline 或类似命令。
  • 实际上,已有宏包替用户做了这类事,例如 tocbibind 宏包就提供了把目录(toc)、参考文献(bib)和索引(ind)的标题加入目录的功能。载入宏包,功能即生效。宏包文档介绍了一些可配置项,例如只把索引加入目录,而另两项不动。
    • 注意,tocbibind 宏包重定义了相关命令(如 ableofcontents),如有其他宏包和文档类修改了相同命令,可能遇到兼容性问题。

注意,把不编号章节加入目录时,对应的 PDF 书签会自动添加。每一条目录项都有对应的 PDF 书签,这是合理的默认行为。

把不编号章节仅加入 PDF 书签

手动生成的

% preamble
% usepackage{bookmark}

section*{title 2}
% see definition of Hy@MakeCurrentHref in hyperref.sty
ookmark[dest=HyperLocalCurrentHref, level=1]{title 2}

说明

  • 类似地,ookmark 需要在章节标题命令之后使用。
  • dest 选项接受一个由 hyperref 定义的超链接目标(destination)
  • 带星号的章节标题命令(如section*)会创建一个新的超链接目标,并将其储存在一系列命令中,HyperLocalCurrentHref 是其中的一个。(暂不清楚 HyperGlobalCurrentHrefHyperLocalCurrentHref@currentHref 在应用上的差异。)
  • level 选项接受一个整数,用于表示书签的(绝对)层级。类似的还有接受相对层级的 rellevel 选项,详见 bookmark 宏包文档 1.2.5 节。

自动生成的

  • 理论上,同样需要用户了解并修改相应命令的定义。
  • 实际上,笔者还没发现提供相关功能的宏包。

不推荐的用法

只用 hyperref宏包实现「把手动生成的不编号章节仅加入 PDF 书签」,生成的书签跳转目标「不准」,需要做额外的调整。见本专栏之前的文章《(旧文)[LaTeX] - 使手动添加的 PDF 章节书签跳转到准确位置》。使用bookmark 宏包则没有这个问题。

hyperref 的用户文档中,针对 PDF 书签的功能,也推荐用户使用 bookmark 宏包(见 hyperref 文档 4.1.1 节末尾)。

所以,涉及手动生成 PDF 书签时,推荐 bookmark 宏包。

比较完整的例子

下面的例子,集中展示了前文提到的几种用法。

documentclass{article}
usepackage{lipsum}
usepackage{tocbibind}
usepackage[bookmarksopen=true]{hyperref}
usepackage{bookmark}

egin{document}
ableofcontents

section{abc}
lipsum

section{bcd}
lipsum

section*{title}
addcontentsline{toc}{section}{title}

subsection*{sub title}
addcontentsline{toc}{subsection}{sub title}

subsection*{sub title 2}
addcontentsline{toc}{subsection}{sub title 2}
lipsum

section*{title 2}
% see definition of Hy@MakeCurrentHref in hyperref.sty
ookmark[dest=HyperLocalCurrentHref, level=1]{title 2}
lipsum

end{document}

附记

一条不带有链接跳转的目录项,它所需的只有三项信息(详见本专栏文章《探索 LaTeX2e 格式:目录项(外一篇:怎么改 numwidth)》),

  • 层级
  • 内容(包括编号和文本)
  • 页码

所以,更一般的情况下,addcontentsline 只需和对应的无编号章节目录出现在同一页,保证两者的页码相同即可。

一条带有链接跳转的目录项,它需要额外获取跳转目标的信息。PDF 格式支持的跳转目标,可以储存三项内容,

  • 目标所在页面(以绝对页码的形式)
  • 目标在那一页的横纵相对位置
  • 跳转后的显示状态(如适应宽度、适应高度等)

详见 PDF Ref v1.7, Sec. 8.2.1。在 hyperref 里,跳转目标储存在 @currentHref。 所以,更一般的情况下,只需保证在无编号章节目录和 ookmark 命令之间,@currentHref 储存的信息不被刷新即可。

@currentHref 这个宏的命名方式,和 LaTeX2e 中储存标签信息的内部宏 @currentlabel (本专栏文章《探索 LaTeX2ε 格式:计数器》有所提及)有相似之处。


推荐阅读:
相关文章