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 文档了。