最近在找一些教材的PDF版本,有时候找到了PDF版本却没有目录,对于教材这种需要经常查阅的电子书来说,没有书签目录会导致效率大大降低。之前一直将就着用了,正好暑假小学期结束了,有一些空闲时间,这次我决定给PDF加上目录。
以我下学期的课程计算机网络为例,教材为《计算机网络(第五版)》清华大学出版社。百度搜索一下,在脚本之家找到了这本书的电子版。下载后发现虽然是扫描版的,质量还不错,但是缺点就是没有目录,不方便查阅。
首先使用PDF Password Remover去除PDF内置的密码,否则无法编辑。接下来就可以使用常规的PDF编辑软件对PDF进行编辑了,我使用Adobe Acrobat DC删去了一些多余的页面。虽然也可以添加目录,不过需要一个一个手动添加,对于有七百多页的大部头来说效率实在太低,于是我借助了其他一些工具批量添加。
首先需要获取书籍的完整目录,不需要从PDF版本的目录页进行识别,一般来说书籍的目录页信息都是公开的,在网上书店或者出版社的网站一般可以找到,例如《计算机网络》的目录即可在清华大学出版社官网找到。将它复制下来,存放在一个文本文件中,我命名为data.txt。
结构类似下面这样:
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 
 | 第1章  引言 1
 1.1  使用计算机网络 2
 
 1.1.1  商业应用 2
 
 1.1.2  家庭应用 4
 
 1.1.3  移动用户 8
 
 1.1.4  社会问题 10
 (省略余下部分)
 
 | 
接着使用PDF Patcher,打开PDF文件,先添加几个书签,例如封面、前言等页,方便查看书签文件的结构。接着保存书签文件,即得到了类似以下格式的文件:
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 
 | <?xml version="1.0" encoding="gb2312"?><PDF信息 程序名称="PDFPatcher" 程序版本="0.3.3" 导出时间="2020年08月30日 11:33:38" PDF文件位置="C:\Output\[计算机网络(第5版)].(美)特南鲍姆.扫描版_Password_Removed.pdf">
 <度量单位 单位="点" />
 <文档书签>
 <书签 文本="封面" 动作="转到页面" 页码="1" 显示方式="适合页宽"/>
 <书签 文本="书名" 动作="转到页面" 页码="2" 显示方式="适合页宽"/>
 <书签 文本="版权" 动作="转到页面" 页码="3" 显示方式="适合页宽"/>
 <书签 文本="前言" 动作="转到页面" 页码="4" 显示方式="适合页宽"/>
 <书签 文本="目录" 动作="转到页面" 页码="6" 显示方式="适合页宽"/>
 <书签 文本="第1章  引言" 动作="转到页面" 页码="15" 显示方式="适合页宽">
 </文档书签>
 <页码样式 />
 </PDF信息>
 
 | 
注意文件的编码格式是GB2312,使用文本编辑器打开和保存的时候均需要注意格式问题。
根据这两个信息我们就可以使用脚本来处理目录数据,自动生成书签文件了。
我用的是Python脚本:
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 
 | def wrap(info, page):return '<书签 文本="{}" 动作="转到页面" 页码="{}" 显示方式="适合页宽">\n'.format(info, int(page)+14)
 def handle(line):
 line = line.strip()
 p = line.rfind(' ')
 return line[:p], line[p + 1:]
 
 with open('data.txt', 'r', encoding='utf-8') as fin:
 with open('out.xml', 'w', encoding='utf-8') as fout:
 lines = list(filter(lambda x: len(x.strip()) > 0, fin.readlines()))
 i = 0
 while i < len(lines) and '第' in lines[i] and '章' in lines[i]:
 info, page = handle(lines[i])
 fout.write(wrap(info, page))
 i += 1
 while i < len(lines) and (lines[i].split(' ')[0].count('.') == 1 or '习题' in lines[i]):
 info, page = handle(lines[i])
 fout.write(wrap(info, page))
 i += 1
 while i < len(lines) and lines[i].split(' ')[0].count('.') == 2:
 info, page = handle(lines[i])
 fout.write(wrap(info, page))
 fout.write('</书签>\n')
 i += 1
 fout.write('</书签>\n')
 fout.write('</书签>\n')
 
 
 | 
得到输出文件out.xml后,手动将其复制到原来的书签文件对应位置即可。
然后使用PDF Patcher导入书签文件,注意把之前的书签删除。

此外,如果觉得扫描版的PDF文件大小太大了,可以使用Acrobat DC中的优化PDF功能,优化文件大小。