调整页码、转移批注、切割页面:这些命令行工具帮你玩转 PDF

📑 导语
无需 Adobe Acrobat 或付费 PDF 软件,也能玩转复杂 PDF 操作

如今,无论是商业文件,电子书籍,还是学术论文,大多以 PDF(Portable Document Format,便携式文档格式)文件格式存储和分发。由于 PDF 具有跨平台、跨设备、且不易修改的特点,能够保持文件呈现的一致性,因此在互联网上得到了广泛的应用。借助扫描技术,PDF 甚至让前数字时代的书籍或文章「重见天日」,为信息传播发挥了重要作用,因此,PDF 被称为 世界上最重要的文件格式。可以说,在 2023 年使用电子设备,就不可避免地要与 PDF 文件打交道。

尽管 PDF 的使用相当广泛,各类 PDF 软件也层出不穷,但是 PDF 的很多特点和功能仍然不为人知,日常学习工作中遇到 PDF 相关的问题还是会让不少同学犯难。举个例子,我的一位爱书朋友热衷于将喜爱的纸质书扫描成 PDF 电子书,前段时间他遇到一个棘手的问题:扫描得到的 PDF 文件中,每一页包含书籍的两页,他想将其分割成单独的两页,在付费的 Adobe Acrobat Pro 中折腾了半天,他也没有搞明白究竟应该该如何实现。能找到的其他方法也不是没有,但全都需要付费,且价格还不便宜,因此他没有尝试。最终,在我的推荐下,没有花费一分钱,他使用开源的 MuPDF 轻松实现了这个需求1

可见,发明 PDF 格式的 Adobe 公司推出的 Acrobat 也并非万能的,在实现某些 PDF 功能时并非总是最优选项。除此之外,Adobe Acrobat 的许多高级功能都需要付费使用,并且界面不够美观,个人不是很喜欢。有鉴于此,本文接下来将分享一些我日常用到的 PDF 处理技巧,这些技巧都是我在日常学习工作中遇到的,可能相对比较小众,但每一种方法都提供了免费的实现方式,让你无需 Adobe Acrobat 或其他付费 PDF 软件,也能轻松应对工作中处理 PDF 的问题。

本文分享的技巧主要使用命令行工具来实现,下面的表格列出了这些工具的主要信息,包括名称、语言、支持平台、许可方式、主要功能等。

名称 语言 支持平台 版本 界面 许可方式 主要功能 安装方式 (macOS) 备注
pdftk-java Java Linux, macOS, Windows 3.3.3 命令行 GNU-2.0 导出 PDF 元数据,合并、拆分、修复 PDF 等 brew install pdftk-java 本文使用的为 Java 移植版,PDFtk Server 原版已有超过 10 年未更新,停留在 2.02 版本,部分功能与 pdftk-java 有所不同。
Skim Objective-C macOS 1.6.21 图形化应用 BSD 阅读和批注 PDF,默认将 PDF 标注存储为 Skim notes brew install --cask skim 本文使用的是 Skim 附带的两个命令行工具 SkimNotes 2.9.3 和 SkimPDF 1.3.4。
MuPDF C Linux, macOS, Windows, Android, iOS 1.23.0 Library AGPL-3.0 轻量级 PDF、XPS 和电子书查看器 brew install mupdf MuPDF 包括软件库、命令行工具和适用于各种平台的阅读器,本文只用到了它的命令行工具 mutool
pdfjam Shell Linux, macOS, 通过 Cygwin 支持 Windows 3.10 命令行 GPL-2.0 主要涉及 PDF 页面的处理 安装 TeX Live 发行版或下载 release 后安装 pdfjam 为 LaTeX 包 pdfpages 提供了在命令行中的使用接口,依赖于 LaTeX 引擎和包。
pypdf Python Linux, macOS, Windows 3.16.2 Library BSD 通过 Python 对 PDF 进行分割、合并、裁剪和转换、提取文本等 pip install pypdf 2022 年 12 月以前,pypdf 的名称为 PyPDF2

为了让不熟悉命令行的朋友也能上手使用,我也提供了 macOS 平台上的自动化实现方案,包括 Keyboard MaestroAutomator(自动操作)和 Shortcuts(快捷指令),你可以点击 这个链接 下载文中提到的所有 Keyboard Maestro macros、Automator workflows 和 Shortcuts。后两个软件都是 macOS 自带的,完全免费,只有 Keyboard Maestro 是付费软件。作为 Mac 用户,我非常喜欢 Keyboard Maestro,也推荐你使用它。如果你还没有购买 Keyboard Maestro,可以使用少数派读者专属优惠码 SSPAI 享受八折优惠,只需 28.8 美元即可入手 Keyboard Maestro 11。关于学习如何系统使用 Keyboard Maestro,欢迎阅读由我撰写的少数派栏目《生产力超频:Keyboard Maestro 拯救效率》。

调整 PDF 页码标签

一般情况下,我们所说的 PDF 页码指的是物理页码,即 PDF 中从第 1 页开始递增的页码,每个 PDF 文档都有物理页码,这种页码被称作 page numbers,大多数情况下,page numbers 都使用阿拉伯数字计数。除此之外,在 PDF 特别是较长的 PDF 书籍中,还有另一种被称作 page folios 的页码,它是指 PDF 页面中页眉或页脚处的序列标记,可能是英文字母、罗马数字或阿拉伯数字,在 Adobe Acrobat 中也叫作 page labels。我们常说的某本书的第几页指的是 page folios,而不是 page numbers。例如下图中的 PDF 一共有 256 页,当前位于 page numbers 的第 19 页,page folios 的第 10 页。

PDF Expert 右下角显示的 PDF 页码 PDF Expert 右下角显示的 PDF 页码

在上图所示的 PDF 中,page numbers 和 page folios 不一致。当我们在 PDF 阅读软件中页码跳转框中输入阿拉伯数字 10 之后,会跳转到 page numbers 的第 10 页,而不是 page folios 的第 10 页(即这本书的第 10 页)。造成这种情况的原因是 PDF 书籍中的封面、版权、前言、目录等部分的 page numbers 通常使用的是英文字母或罗马数字,正文部分才是阿拉伯数字。为了方便阅读,我们通常需要将 page folios 和 page numbers 设置为相同的值。

在 Adobe Acrobat 中,我们可以选中需要调整的页面缩略图,然后右键点击「Page Labels…」,在弹出的对话框中设置需要调整的页面范围,然后调整 page labels,可以选择使用阿拉伯数字、英文字母、罗马数字或自定义标记,以及为其添加前缀,最后点击「OK」即可,如下图所示。

在 Adobe Acrobat 中调整 PDF 页码 在 Adobe Acrobat 中调整 PDF 页码

尽管在 Adobe Acrobat 中调整 page labels 非常直观,但是这种方法需要付费,而且操作比较繁琐,需要多次点击,不够方便。下面介绍另一种调整 PDF 页码的方法——使用命令行工具 PDFtk。这种方法无需付费,并且非常符合 Unix 哲学「一切皆文件」的原则。需要注意的是,由于原版 PDFtk2 不支持调整 PDF 页码标签的功能,因此下文使用的都是 pdftk-java

使用下面的命令,就可以将名为 input.pdf 的元数据 导出 为一个纯文本文件 metadata.text

1
pdftk input.pdf dump_data_utf8 output metadata.text

使用文本编辑器打开生成的 metadata.text,可以看到 input.pdf 的各种元数据,其中就包括页码标签信息,如下所示:

 1
 2
 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
28
29
30
PageLabelBegin
PageLabelNewIndex: 1
PageLabelStart: 1
PageLabelPrefix: Cover
PageLabelNumStyle: NoNumber
PageLabelBegin
PageLabelNewIndex: 2
PageLabelStart: 1
PageLabelPrefix: Page
PageLabelNumStyle: UppercaseLetters
PageLabelBegin
PageLabelNewIndex: 3
PageLabelStart: 1
PageLabelNumStyle: UppercaseLetters
PageLabelBegin
PageLabelNewIndex: 6
PageLabelStart: 1
PageLabelNumStyle: LowercaseLetters
PageLabelBegin
PageLabelNewIndex: 9
PageLabelStart: 1
PageLabelNumStyle: UppercaseRomanNumerals
PageLabelBegin
PageLabelNewIndex: 12
PageLabelStart: 1
PageLabelNumStyle: LowercaseRomanNumerals
PageLabelBegin
PageLabelNewIndex: 17
PageLabelStart: 1
PageLabelNumStyle: DecimalArabicNumerals

容易发现,每个 PDF 页码标签的元数据由 4 或 5 行组成:

  • PageLabelBegin:开始一个页码标签
  • PageLabelNewIndex:页码标签开始的索引,对应 PDF 的 page numbers
  • PageLabelStart:页码标签开始的页码
  • PageLabelPrefix:页码标签的前缀(可选)
  • PageLabelNumStyle:页码标签的类型,包括 NoNumber(无页码)、DecimalArabicNumerals(阿拉伯数字)、UppercaseLetters(大写英文字母)、LowercaseLetters(小写英文字母)、UppercaseRomanNumerals(大写罗马数字)和 LowercaseRomanNumerals(小写罗马数字)。

上面的示例对应的 PDF 页码标签元数据可以解读为:

  • 第 1 页的 page label 为自定义文本 Cover
  • 第 2 页的 page label 为带有前缀 Page 的大写英文字母 Page A
  • 第 3 页的 page label 为大写英文字母 A,依次递增
  • 第 6 页的 page label 为小写英文字母 a,依次递增
  • 第 9 页的 page label 为大写罗马数字 I,依次递增
  • 第 12 页的 page label 为小写罗马数字 i,依次递增
  • 第 17 页的 page label 为阿拉伯数字 17,依次递增直至最后一页

需要注意的是,不是每一个 PDF 都具有这些全部类型,有些 PDF 的元数据可能没有任何关于页码标签的内容,这种情况下,PDF 阅读器会使用阿拉伯数字 1 开始标记。

为了修改 PDF 的页码标签,我们需要先将 PDF 的页码标签元数据导出为文本文件,然后修改其中的页码标签信息,最后将修改后的元数据导入 PDF 文件,生成一个新的 PDF 文件。比如我们将上述示例中倒数第 3 行的 17 修改为 20,就可以将 PDF 的 page labels 修改为从阿拉伯数字 20 开始,而不是之前的 17,如下图所示。

修改元数据后得到 PDF,从第 20 页开始使用阿拉伯数字作为页码标签 修改元数据后得到 PDF,从第 20 页开始使用阿拉伯数字作为页码标签

经过上面的分析,修改 PDF 页码标签的步骤就很清晰了:

  1. 使用 pdftk 导出 PDF 的元数据为文本文件
  2. 根据 PDF 页面信息,使用文本编辑器打开元数据文件,修改页码标签信息
  3. 使用 pdftk 将修改后的元数据导入 PDF,生成页码标签修改后的 PDF

尽管上述步骤非常简单明了,但每次都需要修改文件名称,对于不熟悉命令行的朋友来说,可能还是有点难以上手。因此,我制作了一个 Keyboard Maestro macro 来实现这一功能,如下图所示。

调整 PDF 页码标签的 Keyboard Maestro macro 调整 PDF 页码标签的 Keyboard Maestro macro

这个 macro 首先获取访达(Finder)中选中文件及其所在路径,然后保存为 Keyboard Maestro 中的变量 selected_pdf。之后是一个条件判断,用于判断选中的文件是否是 PDF,如果不是,则弹出通知「No PDF was selected.」,然后取消执行 macro。如果选中的文件是 PDF 的话,则弹出一个提示框,让用户输入页码标签的起始页,这里包括了 3 种类型,也就是 3 个变量:

  • FirstPage:第一页,通常为 Cover,默认值设置为 1
  • LowercaseRomanNumerals:小写罗马数字页码标签的起始页
  • DecimalArabicNumerals:阿拉伯数字页码标签的起始页

如果上述 3 个值留空的话,则表示不包括该类页码标签。接下来,Keyboard Maestro 将用户输入的值作为变量传递给下面的 Shell 脚本:

 1
 2
 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
28
29
30
31
32
33
34
export PATH="$PATH:/opt/homebrew/bin:/usr/local/bin"

cd "$KMVAR_pdf_path"

# Dump PDF metadata
pdftk "$KMVAR_selected_pdf" dump_data_utf8 output metadata.text

# Path to the metadata file dumped by pdftk
METADATA_FILE="metadata.text"

# Remove existing page label information if present
sed -i '' '/PageLabelBegin/,$d' "$METADATA_FILE"

# Function to append page label information
append_page_label(){
    cat <<EOF >> "$METADATA_FILE"
PageLabelBegin
PageLabelNewIndex: $1
PageLabelStart: $2
$3
$4
EOF
}

# Append new page label information if variables are not empty
[ -n "$KMVAR_FirstPage" ] && append_page_label "$KMVAR_FirstPage" "1" "PageLabelPrefix: Cover" "PageLabelNumStyle: NoNumber"
[ -n "$KMVAR_LowercaseRomanNumerals" ] && append_page_label "$KMVAR_LowercaseRomanNumerals" "1" "PageLabelNumStyle: LowercaseRomanNumerals"
[ -n "$KMVAR_DecimalArabicNumerals" ] && append_page_label "$KMVAR_DecimalArabicNumerals" "1" "PageLabelNumStyle: DecimalArabicNumerals"

# Update PDF metadata
pdftk "$KMVAR_selected_pdf" update_info_utf8 metadata.text output adjusted-page-labels.pdf

# Remove `metadata.text`
rm metadata.text

这段代码主要分为 6 个部分:

  • 使用 export 命令将 pdftk 命令所在的路径添加到 PATH 环境变量中,这样就可以直接使用 pdftk 命令,而不需要输入完整的路径。/opt/homebrew/bin/usr/local/bin 分别是 Homebrew 在 Apple Silicon Mac 上和 Intel Mac 上的默认安装路径。
  • 使用 cd 命令切换到选中 PDF 文件所在的路径。
  • 使用 pdftk 命令导出 PDF 的元数据为文本文件 metadata.text
  • 使用 sed 命令删除 metadata.text 中已有的页码标签信息,然后使用 cat 命令将用户输入的页码标签信息追加到 metadata.text
  • 使用 pdftk 命令将修改后的元数据导入 PDF,生成页码标签修改后的 PDF。
  • 使用 rm 命令删除 metadata.text

激活这个 macro 之后,在访达中选中需要修改页码标签的 PDF 文件,然后按下快捷键 ⌃ + ⌥ + L 或点击菜单栏的 Keyboard Maestro Engine 图标,在弹出的窗口中输入不同类型的页码标签的起始页,就可以得到页码标签修改后的 PDF 文件,使用效果见下图。

调整 PDF 页码标签的 Keyboard Maestro macro 使用效果 调整 PDF 页码标签的 Keyboard Maestro macro 使用效果

在上图的示例中,默认将第 1 页的 page label 设置为 Cover,小写罗马数字的起始页为 2,阿拉伯数字的起始页为 5,生成的 PDF 的页码标签如下图所示。

调整 PDF 页码标签后得到的 PDF 调整 PDF 页码标签后得到的 PDF

需要注意的是,上述 macro 只包括了自定义的 Cover、小写罗马数字和阿拉伯数字这 3 种类型的页码标签,如果你需要调整其他类型的页码标签,可以参考上面的示例,自行修改 Shell 脚本中的代码。需要注意的是,KMVAR_ 是 Keyboard Maestro 中 Shell 脚本使用变量所必需的前缀,不可省略。

转移 PDF 批注

🔖 Note
本文为少数派会员文章,以上是其中部分内容,欢迎加入少数派会员 阅读全文

  1. 尽管过程中遇到的最大问题是如何安装 MuPDF。 ↩︎

  2. 安装命令为 brew tap zph/cervezas && brew install pdftk。 ↩︎