1. 长图PDF到底卡在哪
1.1 三种最常见的长图PDF来源
这年头,长图PDF的出场频率比一般人想象得高得多。我碰到最多的一种是手机聊天记录导出:微信、钉钉里随便一个几百条的会话,截长图后选“导出为PDF”,生成的文件打开就是一个超长页面。第二种是浏览器打印页面时没人注意分页问题,网页内容从上到下连续排布,虚拟打印机把整个网页当成一个超大页面输出,宽度正常、高度几千像素甚至更多。第三种是设计师交付前会把海报、长横幅、商品详情页模板直接导出成PDF,被别人拿去打印时,默认就会遇到只有一页、内容全部缩在一起的问题。
这三种来源虽然行业背景不同,本质却完全相同:PDF页面矩形的高度和宽度没有保持标准纸张的长宽比,页面被记录成了一张“巨型画布”。PDF本身并不限制页面必须是A4或Letter,所以这类文件依然能正常打开,只是所有后续操作都会变得别扭。我在替朋友处理文件时发现,很多人其实已经在用“用截图软件一截再另存为图片”这种笨办法凑合,一旦涉及打印、归档、上系统,这些文档马上露馅。
1.2 拆成多页后具体能用在哪些地方
把长图PDF拆成多页,解决的不只是打印一个场景。
第一个是打印。A4、Letter这些标准纸张的比例固定,一个高两米、宽二十厘米的页面直接打印,打印机会把它等比例缩到一页纸内,结果就是文字字号变得比脚注还小,基本不能阅读。拆页之后,每页按标准纸张排版,文字保持正常大小,打印出来厚薄均匀,装订也方便。
第二个是嵌入和编辑。Word、PPT、公众号后台这类地方插入PDF只支持按页嵌入,一个超长页面插进去会被当成一整张图,拉伸后文字发虚,排版完全失控。拆成多页后,每一页都能当成普通页面处理。
第三个是移动端阅读和后续识别。手机端的PDF阅读器对超长页面支持并不好,手势缩放时经常误触,而OCR工具识别整页大图时,往往因为页面太长直接报错或者漏字。拆页后每一页就是常规尺寸,配合手机分屏、电子书阅读器使用都顺手很多。
第四个是分享与存档。很多企业内网、知识库系统有单文件大小限制,长图PDF虽然页数少,但单页体积大;拆多页后反而容易按页归档、按页编号,后续查找某一段对话或某一章节时,翻页比拖滚动条省事得多。
从实际操作看,拆页在“打印需求”“排版嵌入需求”“阅读需求”这三类场景中价值最大,如果只是自己在电脑上看,拆不拆关系不大,但先按本文方法拆好也无妨,整个处理通常不超过一分钟。
2. 动手前先算清楚:目标页面尺寸与拆分逻辑
2.1 为什么默认按A4比例切
拆页的第一步不是找工具,而是确定“切成多大的一块”。绝大多数人默认选A4,因为A4是最常见的打印和文档尺寸。A4的物理尺寸是210毫米宽、297毫米高,宽高比大约是1 : 1.4142。数字上记不住没关系,只要记得长边约等于短边的根号2倍即可。
按这个比例切的长图,输出后的每一页高度和宽度的比例和A4完全一样。这样后续无论你用什么软件打开,页面形态都非常统一:宽595点、高842点,不会出现某页特别扁、某页特别长的尴尬。
有些场景可能需要A3、Letter、B5,但逻辑完全一致,只是把比例数字从297/210换成对应纸张的宽高比。比如Letter是215.9毫米宽、279.4毫米高,比例是1 : 1.294;A3则是420毫米宽、297毫米高,转成竖版时规则也一样。做参数化脚本时,这些比例都可以提前写在配置里,需要时直接切换就行。
如果长图本身很窄,比如只有800像素宽,按A4比例切出来的页面高度大约是1131像素,打印到A4纸上宽度只有约8厘米,两侧会留很大白边。这类情况我不会硬按完整A4去处理,而是先问清楚要不要“铺满纸张”,如果要,就把页面高度调整成和长图宽度匹配的比例。简单说,A4比例是通用起点,它不是僵化的约束。
2.2 DPI、重叠和裁剪边界怎么定
拆页时真正要命的是三个参数:DPI、重叠(overlap)和裁剪边界。
DPI决定了输出清晰度。PDF内部使用的单位是点(point),1点等于1/72英寸,所以PDF的“原始分辨率”默认是72 DPI。当我们把PDF渲染成图片时,用一个放大系数zoom把每个点放大成多个像素,放大系数等于目标DPI除以72。比如想得到150 DPI的清晰度,zoom就是150/72约等于2.08;想得到300 DPI,zoom就是300/72约等于4.17。对于A4页面,300 DPI对应的像素尺寸是2480乘3508,150 DPI对应1240乘1754。普通屏幕阅读用96 DPI够用,打印建议至少150 DPI,如果原图本身很精细,调到200 DPI以上也不会太差。
重叠是防止内容被切断的保险。剪裁线经过一行文字的中间时,同一行文字被硬生生分到上下两页,读起来隔断感很明显,打印出来也容易装订时缺字。解决办法是让每一页裁剪区域比前一张的下沿用一个小重叠:上一页底部多留十几到几十像素,下一页顶部也从这段重叠处开始。这样即便裁切线刚好压在文字行上,总有一页会保留完整行。重叠值一般取页面高度像素的1%到5%比较合适,比如页面高度约1700像素时,重叠24像素是比较通用的起点。
裁剪边界则是对齐问题。长图PDF从页面顶端开始记录内容,图像来源的截图也大多从顶部开始,所以我习惯用“从顶部开始,固定page_height往下切”的方式,保持所有页面左侧和右侧完全对齐。如果你遇到页面四周本来就带白色边框的PDF,不管怎么切边都会出现白边,此时可以先去边再切,或者裁切时手动把上下边界向内缩几个像素。
2.3 工具选型:为什么优先推荐Python脚本
在拆长图PDF这件事上,现成工具没想象的那么好用。原因在于绝大多数拆分工具的核心逻辑是“按PDF页面的物理对象切分”,默认每个原页面都是一个独立对象,长图PDF在它们眼里只有一个页面,自然没东西可分。能真正按比例把一页内容切成多个矩形区域的工具少,而且多数要收费或操作极繁琐。
相比之下,Python全流程其实是一套非常朴素的“拆图-组页”思路:先把要切的内容渲染成图片,再按矩形裁剪,最后把裁剪结果塞回标准页面。这个链路每一步都有成熟库支持,可重复、可批量、可参数化,不会因为某次忘了设置就产生不一致的结果。
更重要的是,脚本可以处理“混血情况”。比如源文件虽然后缀是PDF,但内容其实来自不同的截图,尺寸不一;或者一个PDF里同时存在横版和竖版页面。脚本可以通过检测长图PDF的页面尺寸来动态计算切分区域,比手动操作可靠得多。我平时在命令行里挂这个脚本,处理上百个文件也只是循环一下的事。
3. 实操:Python把长图PDF拆成多页
3.1 安装依赖
我选了三件套:Pillow负责图像裁剪,PyMuPDF处理PDF渲染和页面生成,可选加一个img2pdf作为备选。安装命令很简单:
pip install pillow pymupdf img2pdfPyMuPDF在官方文档里通常以fitz模块导入,不少新手在这里卡壳。新版PyMuPDF也支持import pymupdf,但为了兼容性,代码里我用import fitz,装的是PyMuPDF库,两者对应同一个东西。
如果是在服务器上执行且没有图形界面,完全没有问题,这个流程不依赖显示器,只要Python环境能正常读取文件即可。如果用的是公司电脑并且没有管理员权限,可以先创建一个虚拟环境再装,避免和系统Python环境冲突。
3.2 分三步理解核心逻辑
整个拆页脚本可以分为三步。
第一步:渲染。如果源文件是PDF,用fitz.open打开后取第一页,用get_pixmap按目标DPI把整页渲染成一张长图。如果是PNG、JPEG之类图片,直接用Image.open读入即可,不用额外处理。
第二步:切分。拿到长图后,读取它的宽度,用宽度除以目标比例算出page_height。然后从顶部0向下循环:每次截取(0, top, width, bottom)区域,其中bottom等于top加page_height;下一个循环的top不能直接用bottom,要减去overlap。
第三步:组页。新建一个A4尺寸的PDF页面(宽595点、高842点),把每一张裁剪图插入到这个页面里,最后保存成新PDF。PyMuPDF的insert_image会把图片放进指定矩形,我用它来填充整页,比例不对时会按比例留边,不会硬拉变形。
这一步里常见的坑是:渲染PDF时忘记设置zoom,默认72 DPI输出,切出来的页面在屏幕上看着还行,放大或者打印立刻糊成一片;还有是把裁剪后的图片保存成PNG,体积大且速度慢,用JPG配合85到90的质量参数,文件大小能压缩一半以上。
3.3 可以直接抄的完整脚本
import os from PIL import Image import fitz INPUT = "长图.pdf" OUTPUT = "多页输出.pdf" DPI_RENDER = 150 QUALITY = 88 OVERLAP = 24 TARGET_RATIO = 297 / 210 # 第一步:把长图PDF转成一张长图 if INPUT.lower().endswith(".pdf"): doc = fitz.open(INPUT) if len(doc) != 1: print("警告:源PDF含多个页面,这里默认只处理第一页") source_page = doc[0] zoom = DPI_RENDER / 72 mat = fitz.Matrix(zoom, zoom) pix = source_page.get_pixmap(matrix=mat, alpha=False) pix.save("_temp_long.png") img = Image.open("_temp_long.png") else: img = Image.open(INPUT) # 第二步:按目标比例切分长图 width, height = img.size page_height = int(width / TARGET_RATIO) top = 0 pages = [] while top < height: bottom = min(top + page_height, height) pages.append(img.crop((0, top, width, bottom))) if bottom >= height: break top = bottom - OVERLAP print(f"切分成 {len(pages)} 页") # 第三步:生成多页A4 PDF A4_W, A4_H = 595, 842 output = fitz.open() for idx, crop_img in enumerate(pages): tmp = f"_page_{idx}.jpg" crop_img.convert("RGB").save(tmp, quality=QUALITY) page = output.new_page(width=A4_W, height=A4_H) page.insert_image(page.rect, filename=tmp, keep_proportion=True) os.remove(tmp) output.save(OUTPUT) output.close() # 清理临时长图 if INPUT.lower().endswith(".pdf"): os.remove("_temp_long.png") print("完成")几个参数可以按实际需求改。DPI_RENDER是PDF转图时的清晰度,长图源文件本身就是高分辨率时,这个值建议不低于150;OVERLAP是重叠像素,如果源图文字行高较大,可以加到40甚至60;TARGET_RATIO默认A4比例,需要Letter的话改成279.4 / 215.9即可。
这段脚本的核心思想是“渲染-切块-拼页”,不依赖任何付费接口,Python 3.8以上都可以直接跑。输出PDF的文件名可以自己改,整个流程处理一个一千像素宽、一万八千像素高的聊天记录长图,实测大约三秒完成,速度很快。
3.4 进阶:不转图片、保留原PDF文字层
如果原始PDF是文字版而不是截图,上面这种“先渲染成图片再切”的方式会把文字栅格化,虽然视觉上变化不大,但PDF里的文字层会丢失,以后无法直接搜索、复制文字。要保留文字层,可以用PyMuPDF的show_pdf_page方法,直接在PDF层面裁剪并复制页面对象。
import fitz source = fitz.open("长图.pdf") source_page = source[0] page_width_pt = source_page.rect.width page_height_pt = source_page.rect.height # 按像素坐标反算PDF坐标 # 例如目标A4比例对应的源页面裁剪高度 clip_height_pt = page_width_pt / (297 / 210) A4_W, A4_H = 595, 842 output = fitz.open() top_pt = 0 while top_pt < page_height_pt: bottom_pt = min(top_pt + clip_height_pt, page_height_pt) clip_rect = fitz.Rect(0, top_pt, page_width_pt, bottom_pt) new_page = output.new_page(width=A4_W, height=A4_H) new_page.show_pdf_page(new_page.rect, source, 0, clip=clip_rect) top_pt = bottom_pt - 5 # 重叠5pt避免文字行切断 output.save("多页_文字保留.pdf")这里有一点很关键:clip参数的单位是PDF坐标点,不是像素,所以不需要先渲染成图片。用page_width_pt来推每页高度,比如A4比例下,裁剪高度等于页面宽度除以1.4142。重叠值我设成了5pt,对应大概1.76毫米,比像素重叠小很多,因为PDF矢量文字在点坐标系下不会产生额外的模糊问题。
这个方法适合本身就有文字层的PDF,如果源文件本来就是图片PDF,用show_pdf_page保留的只是图片本身,文字层依然是空的,这时两种方法差别不大。
4. 不想写代码的替代方案
4.1 桌面软件:能行但费手
如果身边没有Python环境,又必须处理一个长图PDF,我试过几条桌面路线。老牌Adobe Acrobat Pro理论上可以做“裁剪页面”,先打开单页PDF,用裁剪工具选中要保留的区域,把页面尺寸改小,再导出,重复操作直到切完。弊端非常明显:一次手动切一块,遇到十几页的长文档费时费力,而且每次裁剪参数都要手动输入,误差很难控制。
WPS的“拆分PDF”功能可以把一个多页PDF按页码范围拆成多个文件,但它不能处理“单页内部切块”这种需求,因为WPS眼中这份PDF只有一个页面。PDF-XChange Editor的裁剪功能类似Acrobat,也是改当前页的显示区域,操作路径差不多。
所以我的判断是:桌面软件能处理单次少量拆页,适合文件只有两三个切块、轮到这块时你又不介意花十分钟手动调整的情况。但一旦批量处理或者对参数精度有要求,桌面软件的效率远不如脚本。
4.2 在线工具:慎用“拆分”能力
很多在线PDF工具首页都写着“PDF拆分”,进去之后会发现它只是把多个页面拆成多个文件,或者把一个多页PDF按指定页码提取出来。真正能自动把单页超长页面切到标准纸宽的几乎没见过,原因依然是一个页面在工具眼里只有一个对象。
如果实在要在线处理,我建议先确认工具的“页面尺寸调整”功能,看能不能把页面缩放或裁剪成固定大小。一些扫描件处理网站有“分割扫描页”选项,原理是把扫描出来的长页按物理高度均分,这跟我们的需求接近,但只能用在纯图片型PDF上,文字层同样保不住。
更稳妥的在线思路是把PDF导出成图片,用在线图片切割工具分块,再合并成PDF。这个流程至少有三步:导出、切割、合并,每次传文件都有隐私风险,不适合处理含敏感信息的聊天记录。我的建议是,非敏感文件可以应急用,重要文件还是本地脚本处理更可靠。
4.3 用Office排版兜底
很多人在没有任何专业工具时,会把长图PDF当图片塞进PowerPoint,然后用一页PPT放一个切片,最后导出成PDF。这个方法在信息量不大、文件很小时确实管用:插入图片后拖动占位符,手动调整每个切片的位置,因为没有比例卡控,输出的页面经常有大白边或者内容溢出。
如果非要用这个办法,可以先把纸面比例预设成16比9或者A4。PPT里把图片复制多份,每份显示源长图的不同纵向区域,然后裁剪每份图片。Word里则可以用“插入图片”加“布局中的裁剪”来处理,逻辑一样。这个方法适合零基础用户应急,但每次都要重新拉尺寸,费时且精度差。
由于在线工具未经授权便处理个人文件的风险不可忽视,我更倾向于把敏感数据留在本地处理,脚本方案在隐私性上天然占优。
5. 常见问题与排查技巧实录
5.1 页面边缘文字被切掉
这是拆页后最常被抱怨的问题。切成多页后某些页的上边或下边压着一行字,翻页阅读也影响心情。绝大多数原因是OVERLAP设置得不够。行高比较大的长图,比如网页文章截图,一行字有40到60像素,如果只设置10像素重叠,裁切线大概率还是会骑在文字上。
我把重叠值和源图的行高关联起来判断:先看一眼截图里正文一行大概多高,OVERLAP设为行高的一半以上。如果实在看不出,直接设成40,对付大多数网页和聊天记录都够。第二个原因是有背景色的长图,切分时裁切线正好落在带边框的元素上,边框上下被折断。这种情况要看原始版面结构,手动在参数里调整某一段top值,把裁切线移到两块视觉区域之间即可,脚本本身难以智能识别复杂版面。
5.2 输出PDF模糊、字号变小
模糊的核心原因是渲染DPI太低。PDF渲染成图片时默认72 DPI,如果你没有在get_pixmap里传Matrix(zoom, zoom),切出来的图每一页总共只有1000多像素高,打印时自然糊。
另一个原因是源图本身分辨率不够。很多手机截图只有1080像素宽,打印到A4宽度210毫米后,等效DPI约等于1080除以8.27,只有130左右,勉强及格但不够锐利。想提高打印质量,最好找回原始内容重新长图导出,导出时放大页面宽度到2000像素以上。切页脚本没法无中生有,清晰度只能从源头找。
5.3 切完文件体积暴涨
渲染成图片再合成PDF后,文件体积有时候比原文件还大几倍。原因有两个:一是渲染成PNG并用默认无损压缩保存,长图PNG体积本来就大;二是裁剪后的页面上大块纯色背景在JPG下压缩率很低。
解决方法是把中间文件统一存成JPG,质量设80到90,纯黑白内容直接转成灰度图再压成JPG,体积能降到原来的三分之一。如果输出PDF要长期打印,也可以尝试用无损压缩配置,但一般JPG已足够。
5.4 横版长图变竖版页面
长图多数是纵向的,但也有些是横向长图,比如电影海报、全景照片,宽高比反过来。直接按A4竖版切的话,切出来的页面比例不对,图片会被压扁或者出现大片白边。
处理方法是先判断图片宽高比,width > height时把页面尺寸换成横向A4,即842乘595,同时TARGET_RATIO也要改成短边除以长边。脚本里加一个方向判断即可,不复杂。还有一类长图是正方形转成的PDF,页面比例接近1比1,这时按A4切会出现上下大黑边,可以优先考虑按源图原有比例拆,不必硬套A4。
5.5 原PDF文字层丢失
前面在进阶部分说过,渲染后文字层消失,会影响搜索和复制。另一个经常被忽略的点是:如果源PDF里嵌入了字体,渲染过程中字体渲染错误,可能造成个别字符被“画”错。遇到这种情况,最简单的方案就是用show_pdf_page的方式保留文字层,哪怕页面背景被栅格化,文字仍是矢量。
如果源PDF是扫描版,转图片无所谓;如果源PDF是从Word或LaTeX生成的文字版,尽量用保留文字层的方式切。我见过不少人把文字版PDF切成图片版后,客户想改一个字都改不了,只能整体重新做,这是最需要提前沟通的需求。
6. 实操经验收尾
处理这类长图PDF拆页的需求多了之后,我有一个比较深的感受:大部分人真正想要的不只是一堆切好的页面,而是一份能打印、能编辑、能长期归档的正常文档。所以动手前我会先问三个问题:要不要打印,要不要保留文字层,文件敏感不敏感。这三个问题不同,选择的方法就完全不同。
我自己现在常驻一个精简版的拆页脚本,参数写在文件头部,换张纸尺寸或者换个重叠值,改一行就重新跑。遇到紧急文件我一般直接跑Python命令,遇到同事求助就甩一份带注释的脚本给他们,偶尔有不方便开电脑的场合才用在线工具兜底。拆页本身不神奇,真正值钱的是把参数算明白、把边界条件处理干净,这样无论输入是十兆的长图还是几十兆的图文混排PDF,结果都很稳。如果你手头正好有一份这样的长图PDF,按文章里的脚本试一次,大概率比你现在手动截图处理要快得多。