6月 292012
 

这里讨论一下合并 PDF 文档的几种方法。 合并多个 PDF 文档的动机来源于毕业设计论文: 我们最终提交的电子版论文中有三个部分需要是带亲笔签名的纸版页面的扫描件。

我首先想到的是 pdftk,这是一个开源的 PDF 编辑套件, 除了可以用来提取合并 PDF 页面外,还可以对 PDF 文档进行加密解密、填写表单、提取加入元信息等、 添加水印等。它的实现中主要的编程语言是 Java。 我们可以用类似下面的命令合并多个文档:

pdftk A=main.pdf B=auth.pdf C=sm.pdf D=form.pdf cat A1 B A3-59 C A61-end D output final.pdf

命令比较有趣:可以为多个输入文档指定句柄(handle), cat 同经典的Unix工具是“连接”的意思。后面指定要连接的文档的页码范围, 可以用 end 表示最后一页。

下面说合并效果。总体上我觉得还是不错的。 使用 pdftk 合并得到的最终文档里通常的超链接都保留下来且指向正确的位置。 不过如果最初超链接指向的内容不在合并后的文档里的话 (例如上面例子中的 A 文档的第 60 页), 这个超链接就消失了,虽然有时候你会希望链接能够依旧指向那个位置。 另外,pdftk 合并文档时不会保留 PDF 书签。有一些办法可以将书签重新加入, 参考 StackOverflow,不过我没有尝试。

我还尝试了 Adobe Acrobat,其具体版本是 Acrobat 9 Pro。 注意我所需的合并中其实有两个都是替换 (用扫描件替代原始页面),还有一个是追加到文档最后, 这两个操作分别对应 Acrobat 菜单中的替换页面和插入页面,操作相当简单。 在我的测试中 Acrobat 的合并效果更好, 保留了书签和目录中一个指向被替换页面的链接, 虽然替换后点击链接会跳转到对应页面的中央位置(而非顶部)。 我后来发现我的原始主文档 PDF 是 1.5 版本,而扫描 PDF 都是 1.6 版本, Acrobat 9 默认保存的 PDF 版本也是 1.6 版本。 这带来一个疑问:是否是因为 PDF 版本不匹配导致了链接目标位置不准确? 不过,预先将全部文档转换成1.5 和 1.6 版本 (利用 Acrobat 的 PDF 优化器功能)然后去替换合并, 得到的最终文档的链接仍然不够准确。 值得一提的是,Acrobat 是价格不菲的商业软件,且新版本 Acrobat X 没有 Linux 版本, 所以对于 Linux 用户来说不是一个很好的选择。

那么 Linux 用户是否可以有一个开源方案, 得到带有书签和正确的目录中链接的合并后电子版文档呢? 我觉得借助于 LaTeX 的 pdfpages 宏包是可以的。 注意我们拥有主体 PDF 文档的源文件(LaTeX 格式的), 另外我只需要目录和书签中有指向一个扫描页面的超链接。 (不能指望能链接到扫描页中的一个图片之类的, 因为扫描后的 PDF 基本就是一张图了。) 我经过一番尝试,发现如下可以得到想要的效果:

\clearpage
\phantomsection
\addcontentsline{toc}{chapter}{\thu@declarename}
\includepdf{scan/sm2.pdf}

可以看到目录中的链接是手工指定的。事实上这样得到的效果是最好的: 链接正确地指向了扫描页面顶部。你可能注意到一个细节: 我使用了另一个文件 sm2.pdf,而非前面的 sm.pdf。 这是因为我扫描得到的 PDF 是 1.6 版本的,而目前用到的 TeX Live 2011 系统只能生成 1.5 版本的 PDF。于是我不得不降低扫描文档的 PDF 版本,具体方法是用 Evince 打印扫描 PDF 到新文件。 虽然结果最好,但过程比较 hack:需要直接修改论文 LaTeX 模板(ThuThesis) 以插入 PDF 页面。(也许今后 ThuThesis 会加个选项让这件事情变得容易。)

以上介绍了我尝试了的几种合并 PDF 文档的方法,各自的优缺都有所提及。 我其实在想是否有一个开源 PDF 编辑工具可以“修复”或“新加”超链接, 这样配合 pdftk与 Ghostscript 就可以在不需要源文件的条件下合并得到保留全部链接的 PDF 文档了。

3月 132012
 

在 PDF 文档中声明许可证信息大致有两种方式。一是直接在许可证信息写在文档内(目录之前或文档末尾等位置),另一种方式则是嵌入 XMP 元数据信息。这里讲的是后面一种方式。

XMP 并不仅限于嵌入 PDF,事实上它是独立于目标媒体的标准。可以将 XMP 数据嵌入到文档、图片、音频等各种文件中。它本质上是一段 XML 文档。详情参看 wikipedia

在 LaTeX 下使用 XMP 在最终的 PDF 文档中嵌入许可证信息,我摸索出来的最好方法是使用 hyperxmp 宏包。这个宏包会和 hyperref 宏包配合,扩展 hyperref ,支持诸如 pdfcopyright、pdflicenseurl 等选项。这些选项的内容会最终添加到 DVI/PDF 文档的元信息中。

如下是一个简单的示例:

\documentclass{article}
% A Test to use hyperxmp package to include license info into the pdf file.
% The pdf file can be produced by pdflatex/xelatex.
% The license info can be seen in
% * Evince(File--Properties) (of a new version, v3.2.1 on Fedora 16 is ok).

% See also the hyperxmp package's doc by 'texdoc hyperxmp'.

% License of this tex file:
%  Copying and distribution of this file, with or without modification,
%  are permitted in any medium without royalty provided the copyright
%  notice and this notice are preserved.  This file is offered as-is,
%  without any warranty.

\title{Hyperxmp Tests}
\author{Foo Bar}
\usepackage{hyperref}
\usepackage{hyperxmp}
\hypersetup{
pdfauthor={Foo Bar},
pdfcopyright={Copyright (C) 2012 by Foo Bar.
Licensed under CC-BY-NC-SA 3.0. Some rights reserved.},
pdflicenseurl={http://creativecommons.org/licenses/by-nc-sa/3.0/},
}
\usepackage{lipsum}

\begin{document}
\maketitle
\lipsum
\end{document}

编译可以使用 pdflatex 或者 xelatex。之后可以使用 Evince 看到如图的结果。 图片是在 Fedora 16 上 Evince 3.2.1 的截图。高版本的 Adobe Reader 应该也能看到类似的效果。注意 hyperxmp 的文档中提及使用 xelatex 引擎编译时默认情况下会压缩元数据信息,可能导致其他的 PDF 阅读器无法识别元信息。不过我的测试表明 Evince 可以识别并正确处理压缩过的元信息。另外完整的测试文件可以在我的 TeXlab 仓库中得到。测试过程中发现一个有趣的现象:当删掉 pdflicenseurl 一行后, 编译得到的 PDF 文档里的许可证信息就无法在 Evince 中看到了。

Screenshot-pdf-license-info-shown-in-evince

另外一种方式是使用 xmpincl 宏包。它需要单独的一个 .xmp 文件,而且目前只兼容 pdflatex 引擎。所以我觉得不是很方便易用。注意到 Creative Commons 网站上提供生成 .xmp 格式的许可证文件,所以这也算是一个可行的选择。这篇博文有关于这种方式的更多介绍。