Python实现长图PDF按A4比例拆分多页的完整方法
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.294A3则是420毫米宽、297毫米高转成竖版时规则也一样。做参数化脚本时这些比例都可以提前写在配置里需要时直接切换就行。如果长图本身很窄比如只有800像素宽按A4比例切出来的页面高度大约是1131像素打印到A4纸上宽度只有约8厘米两侧会留很大白边。这类情况我不会硬按完整A4去处理而是先问清楚要不要“铺满纸张”如果要就把页面高度调整成和长图宽度匹配的比例。简单说A4比例是通用起点它不是僵化的约束。2.2 DPI、重叠和裁剪边界怎么定拆页时真正要命的是三个参数DPI、重叠overlap和裁剪边界。DPI决定了输出清晰度。PDF内部使用的单位是点point1点等于1/72英寸所以PDF的“原始分辨率”默认是72 DPI。当我们把PDF渲染成图片时用一个放大系数zoom把每个点放大成多个像素放大系数等于目标DPI除以72。比如想得到150 DPI的清晰度zoom就是150/72约等于2.08想得到300 DPIzoom就是300/72约等于4.17。对于A4页面300 DPI对应的像素尺寸是2480乘3508150 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(matrixmat, alphaFalse) 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, qualityQUALITY) page output.new_page(widthA4_W, heightA4_H) page.insert_image(page.rect, filenametmp, keep_proportionTrue) os.remove(tmp) output.save(OUTPUT) output.close() # 清理临时长图 if INPUT.lower().endswith(.pdf): os.remove(_temp_long.png) print(完成)几个参数可以按实际需求改。DPI_RENDER是PDF转图时的清晰度长图源文件本身就是高分辨率时这个值建议不低于150OVERLAP是重叠像素如果源图文字行高较大可以加到40甚至60TARGET_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(widthA4_W, heightA4_H) new_page.show_pdf_page(new_page.rect, source, 0, clipclip_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按文章里的脚本试一次大概率比你现在手动截图处理要快得多。