LaTeX 的页面是作为一个纵向列表拼装起来、再作为一个完成的盒子整体输出(ship out)的,也就是说,事后根本没有缝隙可以把背景塞到它下面。页面背景——整页底色、出血的照片、斜穿而过的 DRAFT 水印——之所以还能实现,是因为所有提供背景的宏包都用同一个手法:在输出前插一脚,把自己的墨先放下去。正文随后才印上来,盖在它上面,看起来就成了背景。本页就沿着这一条线索,依次走过 \pagecolor、eso-pic、background 宏包和 draftwatermark,最后落到多数人翻车的地方:打印和文件体积。
\pagecolor 与真正的背景层有什么不同
如果只需要整页一个纯色底,答案就是一行 \pagecolor{颜色},完全用不上任何背景宏包。不过这个命令来自 color/xcolor 而非 LaTeX 内核,所以不写 \usepackage{xcolor} 就用 \pagecolor{yellow} 会得到 ! Undefined control sequence.。想撤销则用 \nopagecolor。它有两点常让人意外。第一,它是全局的,会无视分组:写在环境里或写在 {...} 里,底色照样从那一页一直铺到文末(颜色本身怎么写,属于颜色那一页的内容)。第二,它只能涂一个平坦的矩形——放不下照片、logo,也放不下旋转的文字。本页余下的内容,几乎都是从第二条限制里长出来的。
\documentclass{article}
\usepackage{xcolor} % \pagecolor lives here, not in the kernel
\begin{document}
\pagecolor{yellow!10} % tints this page and every page after it
Text on tinted paper.
\clearpage
\nopagecolor % back to plain paper
Text on plain paper.
\end{document}那么,当想在正文下面放比颜色更复杂的东西时,究竟发生了什么?等到 LaTeX 知道一页里有什么的时候,这一页其实已经完工了:输出例程已经把一个大纵向盒子拼好,剩下的只是写出去而已,根本没有可以塞进去的「层」。内核给出的替代方案是一对钩子——shipout/background 和 shipout/foreground,其机制直白得近乎好笑。shipout/background 会在输出盒子的最前面插入一个零尺寸的 \hbox,于是它的内容最先被印出来,页面其余部分直接盖印在上面;shipout/foreground 则在最末尾追加一个,并抬回到角上,于是最后才印,从而盖住正文。PDF 里没有 z 坐标——落墨的先后顺序就是唯一的「深度」。两个钩子都会开一个 picture 环境,其原点 (0,0) 位于页面左上角,\unitlength 为 1 pt,所以往里面 \put 的纵坐标通常是负数。
有趣的是,这两个钩子比使用它们的宏包年轻得多。它们随 2020-10-01 版 LaTeX 才进入内核;在那之前,每个做背景的宏包都自行给输出例程打补丁,atbegshi、everyshi、everypage 这一串正是那段历史的遗存。它们如今仍能用,但已经变成薄薄的兼容层——everypage 的 \AddEverypageHook{...} 现在字面上就是 \AddToHook{shipout/background}{\put(1in,-1in){...}},其中 1in,-1in 正好把原点从纸角向内、向下各移一英寸,也就是 TeX 传统的参考点。实际含义很简单:eso-pic、background 和 draftwatermark 最终都把材料放进同一个内核钩子。所以它们可以混用——也因此会为「谁先被印出来」而互相争位。
用 eso-pic 给每页铺图 — \AddToShipoutPictureBG
通用工具是 eso-pic。\AddToShipoutPictureBG{...} 会把参数内容画在此后每一页正文的下面,\AddToShipoutPictureFG{...} 则画在正文上面。Rolf Niepraschk 的这个宏包比内核钩子还老,在第 3 版里改用了这些钩子;它之所以长盛不衰,是因为它对「你想画什么」不作任何假设。带星号的 \AddToShipoutPictureBG* 和 \AddToShipoutPictureFG* 只作用于当前页——内部实现是:该页一输出,带星号的材料就被清空。想在中途停掉背景,调用 \ClearShipoutPictureBG(前景则是 \ClearShipoutPictureFG)。\AddToShipoutPicture 和 \AddToShipoutPicture* 是 BG 版的旧别名,行为完全一致。
钩子内部就是 picture 环境,所以用 \put(x,y){...} 来定位。eso-pic 默认把原点移到纸张左下角(默认开启的 pscoord 选项);改用 \usepackage[texcoord]{eso-pic} 载入,原点就移到左上角,y 向下增长。每次都从角上数实在难受,所以宏包备了一批辅助命令:以纸张为基准的 \AtPageLowerLeft、\AtPageUpperLeft、\AtPageCenter,以正文区为基准的 \AtTextUpperLeft、\AtTextLowerLeft、\AtTextCenter,以及在 memoir 文档类下才起作用的 \AtStockLowerLeft 等。当前版本的 LaTeX 允许 \put 直接接受长度,所以可以写 \put(0,-\paperheight);为此而生的旧包装 \LenToUnit 如今实际上只是个透传,仅为兼容而保留。如果坐标全靠猜,\usepackage[grid]{eso-pic} 很管用——它会在每页画出带标注的网格,单位默认为 gridunit=mm,也可选 bp、in、pt。
\documentclass{article}
\usepackage{eso-pic}
\usepackage{graphicx}
% one image stretched over the whole sheet, on every page
\AddToShipoutPictureBG{%
\AtPageLowerLeft{%
\includegraphics[width=\paperwidth,height=\paperheight]{background.jpg}}%
}
\begin{document}
The text sits on top of the full-bleed image.
\end{document}如果想要的是画出来的底纹而不是照片,就把 tikzpicture 放进钩子里。这里方便的锚点是 \AtPageCenter,因为这样 TikZ 自身的坐标就从纸张中心向四周辐射,一切都可以相对 (0,0) 来推算。实际的工作流程是:定位期间一直挂着 grid 选项,定好之后再去掉。
\documentclass{article}
\usepackage[grid,gridunit=mm]{eso-pic} % drop "grid" once the position is right
\usepackage{tikz}
\AddToShipoutPictureBG{%
\AtPageCenter{%
\begin{tikzpicture}
\fill[blue!8] (0,0) circle (6cm);
\end{tikzpicture}}%
}
\begin{document}
A soft tinted disc sits behind the text on every page.
\end{document}如何只让背景出现在一页或封面上
答案是:用带星号的形式,并且写在那一页开始的位置。\AddToShipoutPictureBG*{...} 只对 LaTeX 读到这一行时正在拼装的那一页生效。所以封面背景要写在导言区或紧挨 \maketitle 之前;写得更靠后,它就落到当时恰好打开的那一页上去了。同样的道理揭示了 \ClearShipoutPictureBG 的一个陷阱:它是全局的且立即生效,因此在一页中途调用,当前这一页的背景就已经没了,而不只是下一页。如果当前页还要保留背景,请先用 \clearpage 把这页送出去,再清除。若背景要覆盖连续几页,标准写法就是:先用不带星号的形式开启,然后 \clearpage 加 \ClearShipoutPictureBG。
\documentclass{article}
\usepackage{eso-pic}
\usepackage{graphicx}
\title{Annual Report}
\author{A. Author}
\begin{document}
% starred form: the cover art lands on this page only
\AddToShipoutPictureBG*{%
\AtPageLowerLeft{%
\includegraphics[width=\paperwidth,height=\paperheight]{cover.pdf}}%
}
\maketitle
\clearpage
No background from here on.
\end{document}专用宏包也各自提供了同样用途的捷径。background 用 firstpage=true 只作用于首页,用 pages=some 配合 \BgThispage 则可任选页面。draftwatermark 用 \usepackage[firstpageonly]{draftwatermark} 限定在第一页,并且可以在文档中途用 \DraftwatermarkOptions{stamp=false} 与 {stamp=true} 关闭和恢复水印。如果只是铺一张图,eso-pic 之上的薄封装 wallpaper 宏包很顺手:\CenterWallPaper{倍率}{文件} 居中放图,\TileWallPaper{宽}{高}{文件} 平铺,\ULCornerWallPaper 一类命令则贴到四角。更契合本节的是,这些命令都有名字里带 This 的「仅当前页」孪生版本,例如 \ThisCenterWallPaper。
background 宏包 —— 用 \backgroundsetup 声明式做水印
如果不想自己算坐标,Gonzalo Medina 的 background 宏包就是答案。所有设置都汇总到 \backgroundsetup{键=值, ...},绘制交给 TikZ。只写 \usepackage{background} 就会启用默认值:一个红色系(color=red!45)的大号 Draft(contents=Draft),以 opacity=0.5 的不透明度、60 度角斜穿每一页中央。主要的键有 contents=(内容,可以是文字、\includegraphics 或图形)、color=(xcolor 的全部色域)、opacity=(0 为完全透明,1 为完全不透明)、angle=(逆时针,−360 到 360)、scale=、position=(TikZ 的节点定位语法,如 current page.center;传裸坐标时不要加括号)、placement=(center/top/bottom 三个预设)、nodeanchor= 与 anchor=,以及用于微调的 hshift=、vshift=。\backgroundsetup 在导言区和正文里都能调用,次数不限,因此可以在文中途换设计。
\documentclass{article}
\usepackage{background}
\backgroundsetup{
contents={DRAFT},
color=red,
opacity=0.25,
angle=45,
scale=8,
position=current page.center,
}
\begin{document}
Every page carries a diagonal DRAFT watermark. Compile twice.
\end{document}有三个坑。第一,务必编译两次。background 的材料是一个带 remember picture, overlay 的 tikzpicture,锚定在 current page 节点上,所以它在纸面上的绝对位置依赖上一次运行写进 .aux 的信息。第一次运行会给出 LaTeX Warning: Label(s) may have changed. Rerun to get cross-references right.,水印要么错位要么不出现。第二,如果 contents= 的素材含有 LaTeX 命令,请通过 \backgroundsetup 指定,而不要写成 \usepackage 的选项——选项字符串的解析方式会让它出错。第三,pages=some 配合 \BgThispage 没有问题,但相反的命令 \NoBgThispage 不能用在双栏文档中:它的实现依赖 afterpage,而 afterpage 与双栏模式不协调。在双栏下,稳妥的做法是反过来——改用 pages=some,在需要背景的页面上调用 \BgThispage。
draftwatermark —— 一行做出 DRAFT 水印
如果只需要文字水印,Sergio Callegari 的 draftwatermark 是最短路径。写一句 \usepackage{draftwatermark},每页中央就会出现一个大号灰色 DRAFT——默认值为 text=DRAFT、angle=45、scale=1、fontsize=0.25\paperwidth,颜色是 colormodel=gray 下的 colorspec=0.8。设置集中在 \DraftwatermarkOptions{键=值}:text=、angle=、scale=、fontsize=、color=(可带颜色模型,如 {[gray]{0.85}})、hpos=/vpos=(或用 pos={x,y} 一次给全)、hanchor=/vanchor=(也可以像 anchor=lt 那样用字母连写),以及多行文字用的 alignment=。只有一个例外:firstpageonly 只能作为宏包选项使用,写成 \DraftwatermarkOptions{firstpageonly} 会得到 ! Package keyval Error: firstpageonly undefined.。只想标记首页时,请以 \usepackage[firstpageonly]{draftwatermark} 载入。
\documentclass{article}
% firstpageonly works only here, as a package option
\usepackage[firstpageonly]{draftwatermark}
\DraftwatermarkOptions{
text=CONFIDENTIAL,
color={[gray]{0.85}},
angle=45,
fontsize=1.5cm,
}
\begin{document}
Only the first page is marked CONFIDENTIAL.
\end{document}这个宏包之所以存在,本身就是一段小历史。它的前身是 Jürgen Vollmer 的 draftcopy,用 PostScript 的 \special 来画水印,而 pdfLaTeX 无法解释这类指令;按手册的说法,与直接生成 PDF 的引擎不兼容,正是写 draftwatermark 的最初动机。因此现行版本完全不用依赖驱动的把戏,在 pdfLaTeX、XeLaTeX、LuaLaTeX 下表现一致。旧命令 \SetWatermarkText{...}、\SetWatermarkScale{...}、\SetWatermarkColor{...}、\SetWatermarkAngle{...}、\SetWatermarkLightness{...}(0 为黑,1 为白)为兼容而保留,但它们只是转发到 \DraftwatermarkOptions,新文档应当直接用键值形式。收尾阶段真正好用的是 stamp 一族:\usepackage[final]{draftwatermark}(nostamp 同义)可以在完全不动配置的前提下去掉水印;而 final 又是常见的全局文档类选项,所以 \documentclass[final]{article} 也能触发它。这个宏包的限制同样明确:draftwatermark 只往 shipout/background 里放材料,因此做不出压在正文上的印章。要叠在前景,或者要严格控制层叠次序,就得用 eso-pic 的 \AddToShipoutPictureFG、KOMA-Script 的 scrlayer,或直接使用 shipout/foreground 钩子。draftwatermark 甚至声明了自己在钩子内的次序:它要求排在 eso-pic 之前绘制,所以两者同时使用时,eso-pic 的图会压在水印上面。
该选哪个宏包,以及常见的卡点
实务上的顺序就是「从朴素的开始」:底色用 \pagecolor,文字水印用 draftwatermark,想要声明式的整洁配置就用 background;只有这些都不听使唤时,才下探到 eso-pic,用 \put 直接放置。而一旦需要把东西压在正文上方,可选项就只剩 eso-pic(或者 scrlayer、或者裸钩子)。
| 工具 | 适合用途 | 能放到正文前面吗? | 注意点 |
|---|---|---|---|
\pagecolor | 整页的纯色底 | 不能(它就是纸的颜色) | 需要 xcolor;全局生效且无视分组 |
eso-pic | 把任意图形放到绝对位置;最自由的基础 | 可以(\AddToShipoutPictureFG) | 坐标要自己定;带星号的只作用于当前页 |
background | 声明式斜向水印;角度和不透明度好调 | 不能(仅背景) | 需编译两次;\NoBgThispage 在双栏下失效 |
draftwatermark | 一行做出 DRAFT / CONFIDENTIAL 文字水印 | 不能(仅背景) | firstpageonly 只能作为宏包选项 |
wallpaper | 居中、平铺或贴角的图片背景 | 不能(仅背景) | eso-pic 之上的薄封装;\This... 版本只作用于当前页 |
shipout/background | 完全不用宏包,自己掌控一切 | 可以(用 shipout/foreground) | 原点在页面左上角,y 为负;用 \AddToHook 注册 |
- 背景不出现,或位置不对 →
background要编译两次。用eso-pic时,先怀疑 y 坐标的正负(默认原点在左下)以及\put外面混入的多余空白。 - 本想只放在封面,却每页都有 → 用成了不带星号的形式。改用
\AddToShipoutPictureBG*,并写在\maketitle紧前面。 - 一清除,连当前这页也没了 →
\ClearShipoutPictureBG立即生效;先写一个\clearpage。 - 想把水印压在正文上面 → 用
\AddToShipoutPictureFG。draftwatermark和background都只做背景,做不到。 - 提交版想去掉水印 →
\usepackage[final]{draftwatermark}或\DraftwatermarkOptions{stamp=false};配置保留,水印消失。 - 走 DVI 路线(dvipdfmx / dvips) → 都能用,但颜色、不透明度和图片载入依赖驱动;
eso-pic另有dvips选项。
打印与文件体积:整页背景真正的代价
在屏幕上漂亮的整页背景,落到纸上往往是另一回事。先说物理约束:办公用激光和喷墨打印机没法把墨打到纸张最边缘。以为拉到 \paperwidth 就能出血的图片,拿回来时四边都带着几毫米宽的白框。如果确实需要出血印刷,就得用大于成品尺寸的纸张印刷再裁切——也就是说,正确的做法是让文档类或 geometry 提供带出血的纸张,而不是把图片继续拉大。第二项代价更朴素:在背光屏幕上看着温柔的淡色底,印在纸上就是整页的墨。慢、贵,而且通常比预想的更深。可读性同样要付账:背景越重,正文的对比度越低,所以水印的 opacity 稳妥的目标是「刚好能察觉」。
文件体积的表现,取决于背景是用什么做的,差别相当明显。用 \includegraphics 引入的图片在 PDF 里只嵌入一次,各页只是引用它;所以一份一百页的报告加上照片背景,体积也不过是「原报告 + 一张照片」。用 TikZ 画的背景则不然:绘图命令会为每一页重新执行一遍,图形直接写进每一页的内容流,于是文件体积大致与页数成正比地膨胀。如果精细的矢量背景把 PDF 撑到了几 MB,解决办法很简单——把那张图单独编译成一个 PDF,再用 \includegraphics 引入。矢量质量照旧,而嵌入只发生一次。