LaTeX教程(016)- LaTeX \LaTeX LATEX文档结构(16)
接上一讲
我们前面知道,\vref
是对\ref
的升级,而varioref
包也提供了一个对\pageref
升级的命令\vpageref
。它和\vref
的原理很相似,内置了一些判断。
\vpageref[same-page][other-page]{key}
在不指定选项的前提下,它所输出的内容就是\vref
字符串去掉其中的\ref
字符串。例如,如果\ref
的输出是3.3
,而\vref
输出(我们假设引用和引用对象在彼此的对面)就是3.3 on the facing page
,那么\vpageref
的输出就是on the facing page
,没有前面\ref
生成的索引字符串。其它情形依此类推,分别是on the preceding page,on the following page以及on page x。只有引用和引用对象在同一页的时候和\vref
不同,这种情况下,\vpageref
会输出on the current page。该情形下的字符串被存储在命令\reftextcurrent
中,重定义这个命令可以将"on the current page"改成其他你想要的字符串。
另外,\vpageref
生成的字符串中,只有on page x中的x
可以被自动转换为超链接。
注意,不管使用\vref
还是\vpageref
,最好在命令前后分别放置一个空格,否则它们生成的字符串很可能会和前后文本连在一起。当然,如果是在一个段落的开头,那么前面的空格就不需要了。
\vpageref
比\vref
多了一个选项。其中same-page
选项和\vref
的same-page
选项作用相同,用来代替引用和引用对象在同一页时的输出字符串。而other-page
用于其他情况,也就是引用和引用对象不在同一页的情况。这种情况下,other-page
中内容会放置在原本的输出内容的前面。如"other-page
+on the preceding page"等。我们可以做个测试:
\documentclass{book}
\usepackage[a5paper,margin=1.5in]{geometry}
\usepackage[nospace]{varioref}
\usepackage{kantlipsum}
\usepackage[colorlinks=true,linkcolor=blue]{hyperref}
\begin{document}\chapter{A chapter}
\vpageref[same][other]{test}\kant[1]\vpageref[same][other]{test}\kant[2]\label{test}\kant[3]\vpageref[same][other]{test}
\chapter{A chapter two}
\vpageref[same][other]{test}
\end{document}
这里不截图了,自行编译一下。因为有交叉引用,记得要编译两遍。
实际上,像"on the preceding page","on the following page"等文本,也有存储它们的命令,它们分别是
\reftextafter
\reftextafter
\reftextfacebefore
\reftextfaceafter
我们将在下文中讲解这些命令。
\vpageref
的星号变体\vpageref*
和其原型不同的是它不会生成超链接(注意,经笔者本人测试,该机制并不总是有效的)。
\vpagerefrange
命令,其形式如下:
\vpagerefrange[same-page]{first}{last}
\vpagerefrange*[same-page]{first}{last}
它和\vpageref
很像,不同的地方是它有两个必选参数,而只有一个选项。该命令用两个索引来确定一个范围,它可以生成如"on page 15-18"这样的索引字符串。我们用一个例子演示一下:
\documentclass{book}
\usepackage[a5paper,margin=1.5in]{geometry}
\usepackage[nospace]{varioref}
\usepackage{kantlipsum}
\usepackage[colorlinks=true,linkcolor=blue]{hyperref}
\begin{document}\chapter{A chapter}
\label{chap:one}\label{chap:oone}
\kant[1]\kant[2]\chapter{A chapter two}\label{chap:two}\label{chap:twoo}\dots some text \dots\vpagerefrange{chap:one}{chap:oone}\vpagerefrange{chap:two}{chap:twoo}\vpagerefrange{chap:one}{chap:two}\vpagerefrange[Customized text]{chap:two}{chap:twoo}\end{document}
编译(只截取部分,记得编译两遍):
如上面的例子所呈现的那样,如果两标签在同一页上,则只会显示一个页码。更特别地,如果两个标签都在当前页上,并且没有指定same-page
选项时,则会显示"on the current page"。这句文本被存储在命令\reftextcurrent
中,可以重定义它。而如果指定了该选项,那么此种情况下就会显示我们在选项中自定义的文本(注意,选项只对两个标签都在当前文件时有效)。其它的情况(两个标签在不同页时)则会显示"on pages x-xx"。如果你用了hyperref
,那么两个页码都会生成单独的链接,可以跳转至各自的页码。
和前面讲过的命令一样,星号形式的命令可以抑制hyperref
包生成链接(注意,该机制也并不总是有效的)。
\vrefrange
,它的命令形或如下:
\vrefrange[same-page]{first}{last}
它可以简单地表示成一个由代码:
\ref{first} to \ref{last} \vrefpagerange[same-page]{first}{last}
定义而成的命令。我们在期望表示"chapter 2 to chapter 4 on pages 11-28"这样的索引字符串时,才需要使用它。我们用一个例子演示:
\documentclass{book}
\usepackage[a5paper,margin=1.5in]{geometry}
\usepackage[nospace]{varioref}
\usepackage{kantlipsum}
\usepackage[colorlinks=true,linkcolor=blue]{hyperref}
\begin{document}
% 回忆一下上一篇,\labelformat是用来定制索引字符串的
\labelformat{chapter}{chapter~#1}\chapter{A chapter}
\label{chap:one}\kant[1]\kant[2]\chapter{A chapter two}\label{chap:two}\dots some text \dots\vrefrange{chap:one}{chap:two}\end{document}
编译:
定义你自己的索引命令
有时候你可能会想用varioref
包的内部特性来定义你自己的索引命令,该包为此提供了三个辅助命令。
\vpagerefcompare{key1}{key2}{true-code}{false-code}
该命令比较key1
和key2
处的页码,并且根据比较结果,决定执行true-code
还是false-code
。如果两个标签的页码相等,就执行true-code
,否则执行false-code
。
\vpagerefnearby{key}{true-code}{false-code}
该命令会判断标签\labek{key}
是不是在附近,通过判断它生成的页索引是文本的(如previous, current, next等)还是一个带有数字页码的字符串。如果是前者,执行true-code
,否则(后者)执行false-code
。
\vrefpagenum{cmd}{key}
该命令允许我们定义自己的小命令,以实现类似于前面两个命令所提供的功能。第二个命令是一个标签,也就是我们用在\label
中的东西。第一个参数是任意一个命令(注意必须是自定义的,不能和已有的命令重复),在使用了上述命令之后,cmd
就会存储key
的页码。我们用一个例子演示一下:
\documentclass{book}
\usepackage[a5paper,margin=1.5in]{geometry}
\usepackage[nospace]{varioref}
\begin{document}\chapter{A chapter}
\label{chap:one}\vrefpagenum{\mycmd}{chap:one} %使用\mycmd存储chap:one的页码\mycmd\end{document}
编译:
这些命令几乎不会用到,这里不详细展开。
语言选项
该包支持由babel系统(第13章会讲到)定义的选项,因此,像`\usepackage[ngerman]{varioref},这样正文中的标签就会生成符合德语的索引字符串。如果你的文档是多语言的,那么就要指定所有的语言。如果你所用的语言不在选项中,那么你就需要定制自己的索引字符串,定制的方法我们后面讲。
个性化的定制
为了更进一步的定制,生成的索引字符串都是由宏来定义的,也就是说,它们存储在一些命令中。在默认情况下(没有设置其他语言的情况下),如果标签在索引的前一页,但不在对面,索引就会生成字符串"on the preceding page"。该文本存储在命令\reftextbefore
。而如果标签在索引的前一页,并且它们相互在对方的对面一页,即,标签在左手页,索引在右手页,那么索引就会生成字符串"on the facing page"。该文本存储在命令\reftextfacebefore
中。
类似的,如果如果标签在索引的后面一页,但不在对面,索引就会生成字符串"on the following page"。这段文本存储在命令\reftextafter
中。而如果标签在索引后面一页,并且在对面,那么就会生成"on the facing page"。该文本存储在命令\reftextfaceafter
中。
如上一篇所说,\reftextfacebefore
和\reftextfaceafter
只在双页模式的文档类中才会生效。
而标签和索引在同一页时,生成的索引字符串被存储在命令\reftextcurrent
中。
这些命令都可以被重定义,用你想用的语言。
我们作个演示:
\documentclass{ctexbook} % 注意我们要使用到中文
% 因此将文档类改为ctexbook,并且记得使用xelatex引擎编译
\usepackage[a5paper,margin=1.5in]{geometry}
\usepackage[nospace]{varioref}
\usepackage{kantlipsum}
\usepackage[colorlinks=true,linkcolor=blue]{hyperref}
\begin{document}
\chapter{Chapter one}\renewcommand{\reftextbefore}{在上一页}
\renewcommand{\reftextafter}{在下一页}
\renewcommand{\reftextfacebefore}{在左手页}
\renewcommand{\reftextfaceafter}{在右手页}
\renewcommand{\reftextcurrent}{在当前页}\label{test01}\vpageref{test01}\vpageref{test02}\kant[1]
\label{test02}\vpageref{test01}\vpageref{test03}\kant[2]
\label{test03}\vpageref{test02}\end{document}
使用中文时记得使用xelatex引擎编译,并且编译两遍。这里不截图了,读者自行编译。
当标签和索引相距较远时,索引就会调用\reftextfaraway
命令。该命令和前面那些命令不同,它有一个参数。默认情况下,它存储的是"on page x"这样的字符串,我们在重定义它的时候可以使用\pageref
来生成页码。例如,我们在前面的例子中加上一行重定义命令:
\renewcommand\reftextfaraway[1]{在第~\pageref{#1}~页}
然后在最后的\vpageref{test02}
代码后面加上一行\vpageref{test01}
,再编译就会生成索引字符串"在第 1 页"。
\reftextpagerange
和\reftextlabelrange
的内部定义都非常简单,它们分别通过两个\pageref
或\ref
来表示从一个标签到另一个标签的范围。
如果前面的例子你都自己编译过,那么你可能会发现这样的现象: 不会连续出现两个相同的文本型索引(像on the current page, on the previous page这种)。我们用一个例子演示一下:
\documentclass{book}
\usepackage[a5paper,margin=1.5in]{geometry}
\usepackage[nospace]{varioref}
\begin{document}
\chapter{Chapter one}\label{test01}sometext\dots\vpageref{test01}\vpageref{test01}\vpageref{test01}\vpageref{test01}\end{document}
编译:
哪怕相邻索引对应的是两个不同的标签的key
,只要调用了同一个\reftext...
命令,就会显示不同的文本,中间隔着一些内容也不影响:
\documentclass{book}
\usepackage[a5paper,margin=1.5in]{geometry}
\usepackage[nospace]{varioref}
\usepackage{kantlipsum}
\begin{document}\chapter{Chapter one}\label{test01}sometext\dots\vpageref{test01}\kant[1]
\label{test02}\vpageref{test02}\end{document}
内容较多,请读者自行编译。
如果同一个\reftext...
命令连续出现多次,则会有两个字符串轮流出现的现象,如前面的例子所示。
但是,如果两个相同的\reftext...
命令之间,隔着一个不同的\reftext...
命令,就会打破这种限制,也就是说,这两个相同的\reftext...
命令生成的文本也可能是相同的。我们演示一下:
\documentclass{book}
\usepackage[a5paper,margin=1.5in]{geometry}
\usepackage[nospace]{varioref}
\usepackage{kantlipsum}
\begin{document}\chapter{Chapter one}\label{test01}sometext\dots\vpageref{test01}\vpageref{test02}\vpageref{test01}\kant[1]
\label{test02}\vpageref{test01}\vpageref{test02}\end{document}
请读者自行编译。
会出现这种现象的不仅仅是\reftextcurrent
,文本型索引的那几个命令都有这样的特性。
这是因为varioref
包想要将索引文本插入到日常用语中,而为了避免语言上的重复,给每一个\reftext...
命令准备了两个不同的字符串,使它们轮流出现。varioref
包通过调用\reftextvario
命令实现这一特性。该命令有两个参数,它使得这两个参数中的内容轮流出现。我们用一个例子演示:
\documentclass{book}
\usepackage[a5paper,margin=1.5in]{geometry}
\usepackage[nospace]{varioref}
\usepackage{kantlipsum}
\begin{document}\chapter{Chapter one}sometext\dots\reftextvario{test01}{test02}\reftextvario{test01}{test02}\reftextvario{test01}{test02}\end{document}
编译:
而默认情况下,varioref
是像如下这种方式定义这些索引命令的:
\newcommand\reftextfaceafter{on the \reftextvario{facing}{next} page}
\newcommand\reftextfacebefore{on the \reftextvario{facing}{preceding} page}
\newcommand\reftextafter {on the \reftextvario{following}{next} page}
\newcommand\reftextbefore{on the \reftextvario{preceding page}{page before}}
\newcommand\reftextcurrent {on \reftextvario{this}{the current} page}
\newcommand\reftextfaraway [1]{on page~\pageref{#1}}
\newcommand\reftextpagerange [2]{on pages~\pageref{#1}--\pageref{#2}}
\newcommand\reftextlabelrange[2]{\ref{#1} to~\ref{#2}}
这些命令你都可以根据自己的需求自定义它们。
有些用户可能不喜欢文本型的索引,那么我们可以将这些\reftext...
命令定义为on page~\thevpagerefnum
,\thevpagerefnum
会获取到当前索引的页码。例如:
\documentclass{book}
\usepackage[a5paper,margin=1.5in]{geometry}
\usepackage[nospace]{varioref}
\usepackage{kantlipsum}
\begin{document}\renewcommand\reftextcurrent {on page~\thevpagerefnum}
\renewcommand\reftextbefore {on page~\thevpagerefnum}\chapter{Chapter one}sometext\dots
\label{test01}\vpageref{test01}\kant[1]\vpageref{test01}\end{document}
自行编译一下,两处索引都会显示为"on page 1"。
如果你想让所有的文本型索引都不显示(被忽略,而不是变成页码),只显示那些页码索引,那么就把\reftext...
命令定义成{\unskip}
。例如:
\documentclass{book}
\usepackage[a5paper,margin=1.5in]{geometry}
\usepackage[nospace]{varioref}
\usepackage{kantlipsum}\begin{document}\renewcommand\reftextcurrent {\unskip}
\renewcommand\reftextbefore {\unskip}\chapter{Chapter one}sometext\dots
\label{test01}\vpageref{test01}\vpageref{test02}\kant[1]\vpageref{test01}\label{test02}\end{document}
编译完你会发现,只有一个\vpageref{test02}
生成的"on the next page",其他的都被忽略了。这是因为\reftextbefore
没有被重定义。
注意,这里的{\unskip}
是必要的,否则会生成一段空白间距。
有些语言具有完全不同的句子结构,以至于仅调整个别的字词和短语是不够的。为解决这样的问题,varioref
包还提供了四个命令,它们分别是\vrefformat
,\Vrefformat
, \vrefrangeformat
和\fullrefformat
。我们很少用到,这里不再详述。感兴趣的读者可以查阅包文档。
一些要小心的问题
考虑这样一种情形,如果文本型索引在换页时从中间断开,会怎么样呢?例如:
table 5 on the current
<换页>
page
这表示"表格5"在哪一页呢?varioref
以索引的末尾为参考点,也就是说,这种情形下,"表格5"在"page"那一页,而不在"table 5 on the current"那一页。如果table 5在上面一页,则索引则会这样显示:
table 5 on the following<换页>page
LaTeX \LaTeX LATEX有时候会因为索引的断页产生一些错误,例如,考虑这样一种情形:
table 5 on the following<换页>page"表格5"
此时 LaTeX \LaTeX LATEX发现标签文本是错的,因为"表格5"和page在同一页,系统会将标签文本换成"table 5 on this page"。不幸的是,该文本比"table 5 on the following page"要短,情形就变成了这样:
table 5 on this page<换页>"表格5"
现在标签和索引又不在同一页了,标签文本又是错的了,而前一种却是对的。 LaTeX \LaTeX LATEX会因此进入死循环。无论使用哪种算法,都很难令人满意。出现这样的问题时,需要用户手动调整标签的索引的位置。
在编辑文档时,可以在导言区放置一个\vrefwarning
命令,它可以将这类错误转换为警告,而在调用varioref
包时,指定draft
选项,也有同样的效果。
在导言区放置\vrefshowerrors
可以让varioref
包在检测到可能的循环时停止,而在调用varioref
包时,指定final
选项,也有同样的效果。这实际上也是默认设置。
如果你希望在文档的某些地方仅禁用错误,这些命令也可以在文档内部使用。
另外,如果你不想要我们前面演示的那种轮流切换的文本型索引,又不想重定义每个\reftext...
命令,那么就可以重定义\reftextvario
命令,使得它只显示第一个参数或者第二个参数(两个备用文本中的一个):
\renewcommand\reftextvario[2]{#1}
不指定nospace
选项时,该包会有何表现
varioref
包设计之初,它有一个特殊的行为: 它的命令会移除前面的空格,并且会增加一个自己的空格。因此,你可以就算不在\vpageref
前面放置空格,包也会将索引放置合适的位置。但是这样有一个问题,如果我不想要索引前面的空格呢? 例如,我们想要将索引放在一个括号里(如(\vpageref{xxx})),多一个空格就不合适了。宏包提供了星号形式的命令来取消前面的空格,也就是说,如果我们不想在前面加一个空格,就使用\vpageref*{xxx}
。
但这种方法和其他包的配合不太好,因此我们总是建议指定nospace
选项。如果指定了nospace
选项,那么常规的索引命令(\vpageref{xxx}
)前面都不会自动增加空格,需要的时候,要手动添加(一般情况下都需要)。