上周有位做行政的朋友发来一串文件,一共十二个PDF,问我怎么把多个PDF合并成一个PDF。她说自己试了三个网站,两个要注册登录,一个合并完给文件加了页脚水印,还有一次传到一半浏览器崩了,前面几分钟白传。这类问题我这些年被问过太多次,从学生整理课程资料,到职场人拼装投标文件,再到家里人要打印一整年的缴费凭证,场景五花八门,但卡住的地方高度雷同:不是找不到工具,而是不知道该用哪一种,以及用完之后为什么文件变大了、书签没了、页码乱套了。
这篇就把我实际用过的五条路子按场景摊开讲,从完全不装东西的兜底方案,到几十上百个文件的批处理,再到写几行脚本一劳永逸。每一种我都会说清楚它到底在底层做了什么、适合什么样的文件、会在哪里翻车。你看完不需要五种全记住,挑一条匹配你手上活儿量的就行。零基础能直接照着点,有动手习惯的可以往后翻命令行和脚本那两节。
1. 合并前的三分钟:先看清你手上是哪一类PDF
很多人一上来就找工具,结果换了四五个软件都不满意,问题往往不在工具,在文件本身。PDF这个格式的脾气很怪,同样是.pdf后缀,内部结构可能天差地别。花三分钟做个体检,后面的成功率能提高一大截。
1.1 三类PDF的内部差异,决定了合并方式的选择
我把日常能遇到的PDF粗分成三类。第一类是原生电子版,由文字处理软件或排版工具直接导出,内部有完整的文字层、字体嵌入、书签树和超链接。第二类是扫描件,本质是每页一张大图套了个PDF壳子,没有文字层,体积通常很大,一页扫描件做到两三兆很常见。第三类是混合体,比如合同模板里套了扫描的签字页,或者从系统里批量导出的报表,前面几页是矢量文字,后面夹着图片。
这个分类为什么重要?因为合并操作本质上是把多个文件的页面树重新挂到一棵新的页面树上,同时处理对象编号冲突、字体子集重命名、书签重建这些事。纯文字版合并,工具能保住文字层和检索能力;一旦混进扫描件,最终文件的可检索性、体积、打开速度都会被拉低。我见过最典型的情况是:一份三百页的扫描档案合并完变成四百多兆,用阅读器打开要转圈十几秒,接收方直接拒收。
判断方法很简单:打开PDF,试着用鼠标选中一段正文文字。能选中、能复制,是电子版;只能整页框选出一个蓝色方块,是扫描件。
1.2 合并失败的四个高频原因,先排掉再动手
第一是加密或权限限制。有些PDF设置了打开密码,或者只允许查看不允许编辑,合并工具读到一半直接报错退出。这类文件得先解密,如果只是权限标记而没有打开密码,多数命令行工具支持用空密码尝试解密。
第二是文件损坏。传输中断、下载不全都会造成PDF尾部交叉引用表缺失,表现为能打开前几页,翻到后面报错。合并前用工具做一次结构校验,能提前把坏文件挑出来,避免合并到第九个才崩。
第三是页面尺寸不统一。A4和Letter混在一起,或者混进了横向排版的表格页,合并本身不会报错,但打印或归档时会出现白边、裁切、页码错位。我的习惯是合并前统一尺寸,宁可留白也不要裁掉内容。
第四是顺序依赖文件名。批量合并时工具通常按文件名的字典序排列,1.pdf、10.pdf、2.pdf这个经典坑会让你的章节顺序彻底乱掉。后面讲命令行那节我会给出具体解法。
2. 第一种:系统自带虚拟打印机,不装任何东西的兜底方案
这是最容易被低估的方法。主流桌面系统里都内置了一个"打印为PDF"的虚拟打印驱动,它的作用是把任何能打印的东西输出成PDF。很多人以为它只能转单个文件,其实配合正确的操作路径,它也能完成合并,尤其适合"格式混杂、页数不多、对体积不敏感"的场景。
2.1 借助打印队列把多个文档串成一次输出
真正的操作逻辑不是"选中十二个PDF右键打印",那样只会得到十二个PDF。正确路径是找一个能把多份内容塞进同一个打印任务的地方。具体做法有两类:一类是在支持多文档标签的阅读器里,把多个文件依次打开并加入同一个打印任务;另一类更通用,先把所有内容用一种中间格式承载起来,再一次性打印输出。
举个实际例子。我要把一份文字方案、几张产品图和一份表格合成一个PDF。做法是把文字和表格粘进同一个文档里,图片按顺序插在中间,然后用虚拟打印机输出成PDF。这样出来的文件页面顺序完全由我控制,不用担心工具自作主张排序。
如果你手上全是PDF,只想拼起来,那么更顺的路径是:先用阅读器的"页面缩略图"面板,把第二个文件的页面插入到第一个文件末尾,逐个累加,最后另存。这本质上是"插入页面"而不是"打印合并",但它同样不需要装任何额外软件,适合十页以内的小活儿。
2.2 这条路的代价,得提前知道
虚拟打印机走的是"渲染再输出"的链路,也就是把原页面重新绘制一遍再写成PDF。这个过程中会丢东西:超链接失效,因为打印不保留链接目标;书签全部消失,需要手工重建;文字层可能降级,某些驱动默认把文字栅格化成图片,输出后整页无法检索、无法复制,体积还会暴涨。
还有一个隐蔽的坑:如果你在打印设置里选了"适合页面"或"缩放至可打印区域",输出后的页面会带一圈白边,多份文件累加后白边宽度还不一致,看起来很不专业。稳妥的做法是把缩放选项明确设为"无"或"100%"。
我的定位很清楚:这是应急方案。手边没有合适工具、对方催得急、文件就三五页,用它最快。但如果要做的是正式归档、需要保留书签和检索能力、页数上百,别在这条路上耗时间。
3. 第二种:浏览器在线工具,快是真快,边界也得守住
搜索"PDF合并",第一屏几乎全是在线工具。打开网页、拖进文件、点合并、下载,四步完事,对临时用户极度友好。我不排斥它,但会用一条明确的边界来判断能不能用:文件里有没有不该出现在第三方服务器上的内容。
3.1 什么情况下可以放心用,什么情况下坚决不用
可以放心用的场景:公开的课程讲义、自己整理的学习资料、产品说明书、已经对外发布的报告。这类内容本来就公开,上传的风险接近于零。而且在线工具通常有单文件大小和总页数限制,常见的门槛是单文件一百兆、总页数十到二十页,小文件正好落在舒适区。
坚决不用的场景:身份证件、银行流水、体检报告、合同原件、内部财务报表、带客户信息的清单。这不是危言耸听,上传即意味着文件离开了你的设备,后续怎么处理完全取决于对方的策略。有些工具会宣称定时删除,但你看不到删除过程,也没法验证。
一个折中的做法是先本地脱敏:用阅读器把敏感页删掉,或者把关键字段涂黑后再上传,合并完成后在本地把敏感页补回去。听起来麻烦,但比事后补救省心得多。
3.2 顺序调整和分批合并的两个实操细节
在线工具的排序界面通常是一排缩略图,靠拖拽调顺序。这里有个细节:拖拽时看缩略图不如看文件名。缩略图缩得太小,两份版式相近的文件很容易拖反。我的习惯是上传前把文件名改成带两位序号的前缀,比如01_封面、02_正文,上传后即便排序乱了,也能一眼对照。
另一个细节是分批合并。当文件总大小超过限制时,不要硬扛。把十二个文件分成三批,每批四个,先合出三个中间文件,再把三个中间文件合成最终版。这种二分式的做法在命令行里更自然,但在网页上同样适用,代价只是多两轮上传下载。
还有一点要留意:部分在线工具会在输出文件里加页脚、加水印,或者强制你在下载前看一段广告。判断方法很简单,合并完成后先翻到最后一页看看有没有多出来的东西,再决定要不要用这个结果。如果加了,换个工具,别浪费时间想办法去掉。
4. 第三种:办公套件里的合并入口,适合天天要干这活的人
如果你每周都要处理PDF,在线工具的来回上传下载会让人烦躁,这时候装一个带PDF模块的办公套件更划算。市面上有付费的商业套件,也有开源免费的替代品,功能覆盖合并、拆分、页面重排、压缩、加水印,界面是图形化的,学习成本低。
4.1 用缩略图面板做可视化的页面级重排
这类工具的核心优势不是"能合并",而是合并过程中可以顺手做页面级操作。打开后左侧是一列页面缩略图,你可以跨文件拖拽、删除空白页、旋转方向放反的扫描页、把某个附件页插到指定位置。这在整理投标文件、装订成册这类场景里特别省事。
我通常的操作顺序是:先把所有文件一次性导入,统一看一眼缩略图,把明显的空白页和重复页删掉,再按章节顺序拖一遍,最后才点输出。这个顺序很重要,因为很多工具在你点了输出之后就不允许回头改了,只能重来。
关于页面尺寸不一致的问题,这类工具一般提供"统一页面大小"选项。勾选后它会按你指定的尺寸重排,超出的内容缩放,不足的留白。我的建议是选一个能容纳所有内容的最大尺寸,比如原件里有A4也有A3,就统一到A3再缩,别反过来把A3压进A4,表格里的字会糊。
4.2 输出参数里那几个容易被忽略的开关
图形界面工具的输出选项通常藏在"高级"或"更多设置"里,有几个开关值得留意。
保留书签:默认一般开着,但如果你的源文件本身没书签,开了也没用。想让最终文件有目录,得在合并前给每个源文件加好,或者合并后用工具补,后者在第 7 节展开。
压缩图片:合并扫描件时必开。它会把图片重采样到指定分辨率,体积能降一半以上。代价是放大后细节变软,如果对方要打印,分辨率别低于 200 dpi。
保留原页面尺寸:和上面的"统一页面大小"是一对矛盾选项。归档类文件建议统一,因为阅读体验一致;如果需要保持原样,比如把不同尺寸的图纸拼在一起,就选保留。
5. 第四种:命令行工具,几十个文件堆一起时才见真章
图形界面处理三五个文件很舒服,文件一多就开始难受:拖拽拖到手酸,导入导出一遍遍等进度条。这时候命令行是降维打击,一条命令处理上百个文件,几秒钟出结果。而且命令行的好处是可复现,今天怎么合,下个月照着命令再跑一遍就行。
5.1 qpdf 与 pdftk 的取舍,我的实际选择
常用的开源工具有几个,定位不太一样。qpdf是我现在的主力,合并语法干净,能保留书签,能处理加密文件,还能做结构校验。pdftk历史更久,语法直观,很多老教程都以它为例。pdfunite属于系统工具集的一部分,最轻量,但不保留书签,适合对目录没要求的场景。
qpdf 的基础合并语法是这样:
qpdf --empty --pages 01.pdf 02.pdf 03.pdf -- merged.pdf--empty表示从一个空文档开始,--pages后面依次列出源文件,最后用--收尾,再跟上输出文件名。这个写法的好处是支持页码范围,你可以只取某个文件的特定几页:
qpdf --empty --pages 封面.pdf 1 正文.pdf 3-20 附件.pdf 5,7-9 -- 成品.pdf上面这条命令的含义是:取封面第一页、正文第三到二十页、附件的第五页和第七到九页,按顺序拼成成品。这个能力在"从十几个文件里各抽几页"的场景下非常好用,图形界面得点很多次,命令行一行搞定。
pdftk 的写法更短:
pdftk a.pdf b.pdf c.pdf cat output merged.pdf遇到加密文件,qpdf 可以直接处理:
qpdf --password=文件密码 --decrypt 加密件.pdf 解密件.pdf如果文件只有权限标记而没有打开密码,试试空密码:
qpdf --password= --decrypt in.pdf out.pdf5.2 通配符排序陷阱,以及更稳的清单文件写法
这是命令行合并最容易翻车的地方。当你写qpdf --empty --pages *.pdf -- out.pdf时,shell 展开通配符的顺序是字典序,于是1.pdf、10.pdf、11.pdf、2.pdf这样排下来,你的第二章跑到了第十章后面。
解法有两个。轻量的做法是借助版本排序:
qpdf --empty --pages $(ls *.pdf | sort -V) -- merged.pdfsort -V会按版本号语义排,把数字当数字比,2就排在10前面。缺点是文件名里如果混着中文和特殊符号,排序结果依然可能出乎意料。
更稳的做法是显式写一份顺序清单,让工具照单执行。把文件名按想要的顺序写进list.txt,一行一个,然后:
args=() while IFS= read -r f; do args+=("$f"); done < list.txt qpdf --empty --pages "${args[@]}" -- merged.pdf这样顺序完全由你掌控,不依赖任何排序规则。文件数量上百的时候,我强烈推荐这个写法,一次写好清单,以后复用。
5.3 保留书签与结构校验的两个关键动作
合并完先做校验,这是我养成多年的习惯:
qpdf --check merged.pdf它会把文件结构扫一遍,报告有没有对象引用错误、页面树异常。合并过程中如果混进了坏文件,这一步能立刻发现,不至于等到发给客户才被指出来。
书签方面,qpdf 在合并时会尝试把各源文档的书签树合并保留,但前提是源文件本身有书签。如果源文件没有,合并后照样是空的。这一点和很多人想的不一样:合并工具几乎不会凭空给你生成目录,目录要么源文件自带,要么合并后单独补。
6. 第五种:写几行脚本,把"每月一合"变成双击运行
如果你的合并需求是周期性、规则化的,比如每月把财务凭证按日期合成一册、每周把各渠道导出的报表拼成一份、每学期整理课程材料,那前面四种方法都是在重复劳动。花半小时写个脚本,之后每次只需要把文件丢进文件夹,双击运行。
6.1 环境准备和最小可用代码
用 Python 最省事,库选pypdf。它的前身叫PyPDF2,新版本改了包名和接口,网上老教程里的写法很多已经不能直接用,注意版本区别。
pip install pypdf最小可用的合并脚本:
from pathlib import Path from pypdf import PdfWriter, PdfReader src_dir = Path("./in") files = sorted(src_dir.glob("*.pdf"), key=lambda p: p.name) writer = PdfWriter() for f in files: reader = PdfReader(str(f)) if reader.is_encrypted: try: reader.decrypt("") except Exception: print(f"跳过加密文件: {f.name}") continue if len(reader.pages) == 0: print(f"跳过空文件: {f.name}") continue writer.append(reader) with open("merged.pdf", "wb") as fh: writer.write(fh) print(f"合并完成,共 {len(writer.pages)} 页")这段代码里有几个防御性处理值得说明。is_encrypted判断是为了避免中途抛异常导致整个任务中断,空密码解密能解决大部分只设了权限标记的文件。len(reader.pages) == 0的判断是为了跳过那些零字节或者结构损坏的文件,这类文件在批量下载时很常见。
6.2 自动加书签和页码,把收尾工作一并做掉
脚本真正的价值在于能做图形界面做不了的事,比如按文件名自动生成书签。writer.append(reader)会返回新加入页面的起始索引,拿它去创建目录项:
start = len(writer.pages) writer.append(reader) writer.add_outline_item(f.stem, start)这样每个源文件在最终成品里都有一个以文件名命名的书签,点一下直接跳转。二十个文件拼起来的合订本,有没有这个功能完全是两种体验。
页码稍微复杂一点,因为合并后原有的页码会重复。思路是生成一个只含页码文字的透明 PDF,再逐页叠加到底层页面之上。这里需要另一个库:
pip install reportlab核心逻辑是遍历每一页,用merge_page把页码层盖上去。要注意页码层和水印层的区别,merge_page是在原页面内容之上叠加,不会覆盖原有内容。字号和位置建议先在两页样本上试,找到不压住正文的坐标再批量跑,因为不同来源的文件页边距不一样,统一坐标容易压字。
写这类脚本最需要克制的冲动是"一次写完美"。我的经验是先跑通合并,确认顺序和页数对了,再加书签,再加页码,每一步都单独验证。一次性堆十几个功能,出问题根本不知道是哪一段。
7. 合完不算完:书签、页码、体积这三件收尾事
多数教程到"合并完成"就结束了,实际工作中真正花时间的是后面这三步。一份三百页的合订本如果没有书签,接收方翻起来会骂人;页码不连续,打印装订时会对不上;体积超过邮件附件上限,发都发不出去。
7.1 书签重建:三种补法按文件数量选
三个文件以内,直接在阅读器里手动加,点开书签面板新建条目,指定跳转页码,五分钟搞定。
十个以上,手动就太亏了。用脚本按文件名批量生成是效率最高的,思路在上一节已经给过。文件名的命名规范直接决定了书签好不好看,所以合并前把文件名改成01_立项报告、02_预算说明这种带序号和语义的格式,脚本跑完目录就是现成的。
介于两者之间的情况,用命令行工具配合页码范围也能做出分层书签,但要写配置文件,学习曲线陡一些。我的建议是:除非你要做的目录结构非常固定且会反复使用,否则不值得为它专门学一套配置语法。
还有一点容易忽略:书签层级。如果你合并的每个源文件内部本来就有多级书签,合并工具通常会把它们原样保留在对应位置,形成"总目录—章节—小节"的层次。但如果源文件的书签是平的,合并后也是平的,看着会很乱。这种情况先整理单个文件再合并,比合并后调整省力得多。
7.2 体积膨胀的三个原因和对应的压缩手段
合并后变大是常态,原因主要有三。字体重复嵌入,每个源文件嵌入了一份字体子集,合并后如果工具没做去重,同样的字体就存了好几份。图片未重采样,扫描件的图片按原始分辨率保留。元数据和缩略图残留,每个源文件的历史版本信息都带进了成品。
对应的处理手段按力度从小到大排。最轻的是做线性化优化,让文件支持边下边看,体积基本不变但打开更快:
qpdf --linearize merged.pdf optimized.pdf中等力度是重采样图片,用 Ghostscript 把图片降到适合屏幕阅读的分辨率:
gs -dBATCH -dNOPAUSE -q -sDEVICE=pdfwrite -dPDFSETTINGS=/ebook -sOutputFile=compressed.pdf merged.pdf/ebook这一档大约是 150 dpi,文字部分仍然是矢量,不会糊,图片会有轻微软化,体积通常能降三四成。如果只是内部传阅,还有更狠的/screen档,但图片会明显发虚,打印出来能看出来。
需要提醒的是,压缩工具对书签的处理不统一。部分工具在压缩过程中会丢掉书签树,尤其是走 Ghostscript 这条路时,不同版本表现不一样。所以顺序上应该先压缩后补书签,反过来会白做一遍。压完记得翻一遍目录,确认还在。
8. 这些年踩过的坑,以及我现在的固定打法
说几个印象深的翻车现场。
有一次帮人合并一整套投标文件,一共四十七个PDF,用命令行一把梭,结果交付前发现第六章跑到了第三章前面。查了半天是文件名里有的用了一位序号有的用了两位,字典序把10排在了2前面。从那以后我批量合并前一定先跑ls | sort -V看一眼顺序,或者干脆写清单文件。
还有一次是扫描件合并后体积到了一百八十兆,邮件发不出去。当时图省事直接用了最高质量输出,图片按 600 dpi 保留。后来降到 150 dpi 重出一遍,体积二十八兆,屏幕上看着几乎没差别,因为人眼在普通显示器上分辨不出那个精度差。这件事让我形成了一个习惯:合并前先问一句这份文件最终是屏幕看还是打印用,答案不同,参数完全不同。
最折腾的一次是加密文件。十来个文件里混了两个设了打开密码的,脚本跑到第七个直接抛异常退出,前面的进度全废。后来在脚本里加了异常捕获和跳过逻辑,并且在开头先做一轮密码探测,把所有加密文件单独列出来人工处理,一次跑完的成功率才上来。
现在我的固定打法是分三档。临时急活、三五个文件,用系统自带的插入页面功能,两分钟搞定,不折腾。十到三十个文件、需要目录,用命令行加清单文件,顺序可控,书签能保。周期性重复的任务,一次性把脚本写好,之后只维护输入文件夹的命名规范。在线工具我只在完全公开的材料上偶尔用,图的是不用切窗口。
最后分享一个很省事的小习惯:所有要合并的文件,进文件夹之前就把名字改成序号_内容的格式。这一个动作能同时解决排序、书签命名、事后追溯三个问题,比任何工具的高级功能都管用。至于合并本身,工具会换代,命令会变语法,但"先看清文件、再选工具、最后做收尾"这个顺序,这些年没变过。