Linux下使用pdftk对pdf文件添加或修改目录

通常Linux自带的pdf阅读器既轻便又能够满足绝大多数pdf阅读需求。但是如Ubuntu 18.04自带的evince这样的轻量阅读器是没有添加、修改目录的功能的。本文对这个问题提供一个基本的解决方案。

安装pdftk

pdftk被绝大多数linux操作系统支持(见pdf安装说明)。然而自Ubuntu 18.04 LTS发布以来,apt官方源中一直没有包含pdftk及其依赖组件,所以无法通过apt安装。

https://bugs.launchpad.net/ubuntu/+source/pdftk/+bug/1764450的4楼有解决方案,直接复制并在shell中执行以下四行命令即可完成pdftk安装。

1
2
3
4
wget http://archive.ubuntu.com/ubuntu/pool/universe/p/pdftk/pdftk_2.02-4build1_amd64.deb
wget http://archive.ubuntu.com/ubuntu/pool/main/g/gcc-6/libgcj17_6.4.0-8ubuntu1_amd64.deb
wget http://archive.ubuntu.com/ubuntu/pool/main/g/gcc-defaults/libgcj-common_6.4-3ubuntu1_all.deb
sudo dpkg -i pdftk_2.02-4build1_amd64.deb libgcj17_6.4.0-8ubuntu1_amd64.deb libgcj-common_6.4-3ubuntu1_all.deb

之后直接在shell中运行pdftk以检验安装是否成功。这个版本不保证最新,但是保证在Ubuntu 18.04 LTS中可用。当然之前的Ubuntu版本(17.10及之前的)都可以直接用apt安装。

获得pdf文件结构

在shell中运行:

1
pdftk in.pdf dump_data output info

其中in.pdf是希望修改的pdf文件,info是自由指定的pdf文件结构输出文件,dump_data是pdftk的获取结构的指令,output是pdftk的输出到文件的指令。运行成功则生成info文件。

添加或修改pdf目录

使用文本编辑器打开info文件。假如pdf文件原来存在目录,则可找到

1
2
3
4
BookmarkBegin
BookmarkTitle: 第一章
BookmarkLevel: 1
BookmarkPageNumber: 11

这样的四行。该四行表示一个书签,也就是我们需要的一个“目录”。类似的四行文本重复出现,表示多个书签。假如pdf文件原来没有目录,可以在第一个PageMediaBegin之前增加这样的四行。

这四行的意义已经写得很清楚:开始标志、标题、层级和页码。注意标题如果是英文,可以直接写;而如果是中文,则需要将中文转化成类似上面例子中的十进制Unicode编码才能正常显示。

最后要记得保存info文件。

将中文转化为十进制Unicode编码

转化方法有很多,可以直接在http://www.ip138.com/utf8/等网站上在线转换。大部分网站不区分UTF8和Unicode,因为UTF8也兼容Unicode所以问题不大。而重点在于大部分在线转换器不提供十进制的编码(只有十六进制,格式为&#xXXXX;形式,小写的x表示十六进制,后四位X表示十六进制数字,最后是半角分号),并且pdftk似乎只支持十进制的编码(格式为&#XXXXX;形式,五位X表示十进制数字,最后是半角分号)。所以道理上可以将十六进制手动转换成十进制再放到info文件中。

在Linux系统中我们可以利用shell直接获得十进制Unicode编码:

1
echo -n 第一章|iconv -t unicode|od -x -d

命令中-n表示不换行;echo输出的文字传递给iconv,-t unicode表示转换为Unicode编码;之后传递给od,-x表示输出2比特十六进制格式,-d表示输出2比特十进制格式。输出如下:

1
2
3
0000000  feff  7b2c  4e00  7ae0
65279 31532 19968 31456
0000010

这里feff是标准Unicode的字节顺序标记(BOM),表示Unicode文本流的开始。所以与“第一章”对应的三个数是后面的三个。十六进制应当与在线转换工具给出的结果一致,十进制则是我们需要的编码。

最后复制第二个到最后一个5位十进制数,粘贴到info文件中,并修改成上面说过的十进制格式(注意必须有分号,分号和下一个编码之间不需要空格)即可。

输出添加目录后的pdf文件

准备好info文件后,shell中执行

1
pdftk in.pdf update_info info output out.pdf

命令中in.pdf是待修改的pdf源文件,info是我们准备好的pdf结构文件,out.pdf则是希望输出的pdf的文件名;update_info是pdftk更新pdf结构的指令。注意输出文件名不能和输入文件名重名,若希望原位修改则需要自己手动重命名了。

自动化添加的建议

在添加目录工作量很大的时候,我们希望能够自动获得修改好的info文件。思路是:

  • 获得目录
    直接复制pdf中的目录文本(若可以复制)或者使用OCR将图片形式的pdf目录识别为文本,再加以修改得到正确的目录条目与页码。参考http://www.codebelief.com/article/2018/01/generate-pdf-bookmark-entry-in-five-minutes/
  • 修改目录info格式
    可以使用python、perl脚本等读取上面获得的目录的文本文件并解析,自动生成满足pdftk要求的info格式(四行格式)。同时,通过解释器自带的文本编码转换函数或者调用系统指令(如上),自动转换中文为满足格式的十进制Unicode编码。