乐于分享
好东西不私藏

用PDFPatcher为没有书签的PDF制作书签

用PDFPatcher为没有书签的PDF制作书签
创建: 2024-02-27 19:51
更新: 2026-07-02 21:28

有些不是用Pdg2Pic制作的PDF,比如从Z站下载的,只有页面,没有书签,Chrome打开后,左侧只有缩略图,没有电子目录结构(书签)。这种,Chrome显示的页码与纸版页码并不对应,前者是PDF层面的绝对页码,后者是印刷时自定义的页码。比如纸版页码1,对应正文第1页,前面一般还有照片、序言、目录页等,不包含在纸版页码计数器内。

我分享PDF摘录时,用的是Chrome看到的绝对页码,不是纸版页码,因为我主要在PC上看电子版。正经引用书籍内容时,应给纸版页码。

书签的作用,是在纸版页码、绝对页码之间建立映射,书签显示纸版页码,点击后跳至相应的绝对页码。若无书签,就得自己找出纸版页码1所对应的绝对页码,算出差值(delta),对纸版目录中的纸版页码(增加delta)心算出绝对页码,在Chrome中手工跳转。有些人受不了这个麻烦,我倒是无所谓,内容为王,其它不重要。

另一种书签是在章节标题、绝对页码之间建立映射,相比前一种书签,二者各有利弊。

本小节介绍前一种书签的制作过程。有很多方案,只介绍一种自用方案。

用PDFPatcher打开无书签PDF。在右侧PDF页面移动鼠标至左上角,右键"在此插入书签","书签文本"处随便输点啥,比如"foo","方位"那里显示的数字实际对应刚才鼠标所在点的y坐标,坐标系的原点(0,0)是PDF页面左下角。

菜单上选,文件->换名保存书签文件->some.xml。这个xml长这样

<?xml version="1.0" encoding="gb2312"?>
<PDF信息 程序名称="PDFPatcher" 程序版本="..." 导出时间="..." PDF文件位置="some.pdf">

    <文档书签>

        <书签 文本="foo" 页码="1" 动作="转到页面" 显示方式="坐标缩放" 上="800" />

    </文档书签>

</PDF信息>

手工编辑xml,改成(只是个示例)

<?xml version="1.0" encoding="gb2312"?>
<PDF信息 程序名称="PDFPatcher" 程序版本="..." 导出时间="..." PDF文件位置="some.pdf">

    <文档书签>

<书签 文本="照片" 动作="转到页面" 页码="4" 显示方式="坐标缩放" 左="0" 上="800" 比例="0" />

<书签 文本="自序" 动作="转到页面" 页码="15" 显示方式="坐标缩放" 左="0" 上="800" 比例="0" />

<书签 文本="目录" 动作="转到页面" 页码="19" 显示方式="坐标缩放" 左="0" 上="800" 比例="0" />

<书签 文本="1" 动作="转到页面" 页码="28" 显示方式="坐标缩放" 左="0" 上="800" 比例="0" />

<书签 文本="2" 动作="转到页面" 页码="29" 显示方式="坐标缩放" 左="0" 上="800" 比例="0" />

...
<书签 文本="384" 动作="转到页面" 页码="411" 显示方式="坐标缩放" 左="0" 上="800" 比例="0" />

    </文档书签>

</PDF信息>

解释一下<书签>中的某几个字段

文本  将来显示的电子目录结构(书签)
页码  与"文本"字段关联的绝对页码,即跳转目标的绝对页码
左    跳转目标点的x坐标,一般对应目标页的左上角,可调整至目标页任意位置
上    跳转目标点的y坐标,一般对应目标页的左上角,可调整至目标页任意位置

其他字段保持上例展示的那样。xml决定书签效果,"文本"想写什么都可以;"页码"要与"文本"匹配,否则跳转目标非预期;跳转后展示时自动滚动到(x,y)处,比如想跳至页面中部某处,每个目标页的(x,y)是独立的;有多少条<书签>行都成。

假设已编辑好some.xml,回到PDFPatcher,先删除之前搞的foo书签,菜单上选,文件->导入书签(Ctrl+R),文件->保存PDF文件(Ctrl+S),指定新PDF文件名。

Chrome中打开some_new.pdf,检验书签效果。

一般<书签>行很多,上例纸版页码从1到384递增,每行只有"文本"、"页码"两字段递增,手工编辑不理智,可用程序自动生成。

#!/usr/bin/env python
# -*- encoding: utf-8 -*-


#

# python3 bookmark.py <param0> <param1> <param2> [count]

# python3 bookmark.py 1 28 800 10

# python3 bookmark.py 1 28 800 384 > some.txt

#

# param0 书签中看到的数字,对应纸版页码

# param1 Chrome打开PDF时的绝对页码,不对应纸版页码

# param2 用PDFPatcher获取目标页面左上角y坐标

# count  缺省为10

#


import
 sys

def
 main ( argv ) :
    ret = False
    while
 True :
        if
 len( argv ) < 4 :
            print
( f"Usage: python3 {argv[0]} <param0> <param1> <param2> [count]" )
            break

        try
 :
            param0  = int( argv[1] )
            param1  = int( argv[2] )
        except
 ValueError :
            print
( "param0 and param1 must be integer" )
            break

        param2  = argv[3]
        count   = 10
        if
 len( argv ) > 4 :
            try
 :
                count   = int( argv[4] )
            except
 ValueError :
                print
( "count must be integer" )
                break

        for
 _ in range( count ) :
            pattern = f'<书签 文本="{param0}" 动作="转到页面" 页码="{param1}" 显示方式="坐标缩放" 左="0" 上="{param2}" 比例="0" />'
            print
( pattern )
            param0 += 1
            param1 += 1
        ret     = True
        break

    return
 ret

if
 "__main__" == __name__ :
    ret     = main( sys.argv )
    sys.exit( 0 if ret else -1 )

bookmark.py只生成纸版页码到绝对页码的映射行,照片、序言、目录页的映射行需手工组织。

某些书签制作教程,全自动生成some.xml。PDF是文字版时,是一种搞法;PDF是图片扫描版时,动用OCR提取纸版目录的文字说明。我没全自动的需求,略。

若非从零生成书签,只想微调零星几个映射,可不用xml,直接在PDFPatcher中操作,略。

不确认PDFPatcher是否支持Excel下拉操作,选中纸版页码、绝对页码,下拉自动递增。若支持,前面那些都是废话,忘掉即可。