ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

长图PDF如何拆成多页?两大路线+Python代码实战解析

2026/10/8 9:37:51 拓冰建站 浏览量
长图PDF如何拆成多页?两大路线+Python代码实战解析 上周在群里收到一份PDF打开一看整个人都愣住了页面宽度是标准的595.28pt高度却飙到9000多pt。这种典型的长图PDF基本都来自网页截图、聊天记录、长表格或者地铁线路图导出用手机看没啥问题一打印就露馅——要么被缩成一条又细又长的“带鱼”要么直接打出一张一米多长的废纸。所谓“长图PDF转多页PDF文件”就是把这个比例严重失衡的超长页面拆成多个接近常规纸张比例比如A4的页面真正方便打印、归档和多人传阅。这篇文章我会从页面尺寸的底层原理讲起把拆页的两种路线、完整可跑的代码以及几个我实测踩过的坑全部摊开说清楚。1. 长图PDF的“病根”一个页面为什么会有一万多点高想解决拆页问题第一步不是急着找工具而是搞明白一个长图PDF在PDF内部到底长什么样。页面尺寸这个概念很多人忽略了但它直接决定你后续所有操作的判断。1.1 先给PDF做个体检看懂页面尺寸的三条命令PDF使用PostScript点作为单位1pt等于1/72英寸25.4毫米对应72pt。A4纸的尺寸是210mm乘以297mm换算成PDF单位就是595.28pt乘以841.89pt。普通PDF页面不管内容多复杂页面盒子PageBox的尺寸都落在这个区间附近少数宽屏PPT会用16:9比例但也属于正常范围。而长图PDF的问题在于宽度可能是595.28pt高度却到了10000pt以上比例达到20比1甚至50比1。在你打开文件的一瞬间查看器为了把整页显示到屏幕上只能把内容缩得很小所以会感觉“字怎么这么小”。其实字不小是页面太高了。想验证一个PDF是不是长图PDF用pypdf几行代码就能把尺寸打出来from pypdf import PdfReader reader PdfReader(long.pdf) print(f总页数: {len(reader.pages)}) for i, page in enumerate(reader.pages): box page.mediabox w float(box.width) h float(box.height) print(f第{i 1}页: 宽 {w:.2f}pt, 高 {h:.2f}pt, 高宽比 {h / max(w, 1):.2f})跑完你就知道问题有多严重了。我见过最夸张的一份电子合同扫描件宽度595.28pt高度将近20000pt原因是多页内容被竖着拼成了一张长图再导出PDF。这种文件后续不管是打印、发给别人还是放进OA系统都非常难受。1.2 长图PDF常见的四种来源不同来源的长图PDF拆页难度完全不同这个判断直接影响后面工具的选择。第一种是网页长截图直接另存为PDF比如浏览器插件把整个滚动页面截下来导出。这类文件大多数就是一张高分辨率图片内容没有独立的文字层切起来最无脑但切完如果需要编辑文字就没辙。第二种是用系统打印机把长截图打印成PDF。打印机驱动在“纸张大小”那里选了自定义超大纸张生成了一个单页大尺寸PDF。这种文件通常保留了图片的全部细节尺寸单位可能是像素或英寸PDF里还会记录一个“原始纸张大小”拆页时最好先看看这个值。第三种是从CAD、Visio、原理图工具里导出的工程图PDF。这类文件有大量矢量线条和文字直接拆页后文字可能被腰斩但内容清晰度极高放大也不糊很值得好好拆。第四种是扫描仪或手机App扫描出来的长卷比如银行流水、病历单、协议条款。纯图片为主但可能存在倾斜或阴影拆页以后每页可能还需要校正亮度。判断清楚来源之后再选方案就有的放矢了。1.3 为什么“打印成PDF”不能直接救你很多人第一反应是打开打印对话框选“打印到PDF”指望它自己分成多页。这个想法我用实际行动验证过两次结果都不理想。第一次我直接把长图PDF发到打印机默认设置是“缩放至适合页面”结果一整条内容被压缩到一张A4纸上文字变成了蚂蚁大小。第二次我在Acrobat的打印设置里勾了“平铺所有页”虽然能按照纸张大小把长图切成多块但它生成的是打印任务并不是一个正经的多页PDF文件而且平铺比例稍微调错内容就会被切在奇怪的位置两页之间还会出现大面积的空白或重叠。这里的关键矛盾在于打印驱动关心的是“把内容放到物理纸张上”而我们需要的是“在PDF层面把一个大页面拆成多个小页面对象”。这是两套完全不同的逻辑。只有真正在PDF结构上动手才能稳定输出一个页数正常、比例正常的文档。2. 拆页的两条路线CropBox切片和重新排版搞清楚了病根下面说治疗方案。我实际用下来拆页这件事有两条完全不同的路线分别叫CropBox路线和重新排版路线。二者适用的场景有明显区别选错后面就要走弯路。2.1 路线A用裁剪框切出多个页面保持原内容不动PDF页面有一个叫CropBox的属性翻译过来就是裁剪框。它的作用是定义这个页面“显示给用户看的区域”。PDF的原始内容可能画满了整张大纸但CropBox圈定哪里查看器和打印机就只认哪里。所以一个很巧妙的思路是把同一个源页面复制若干份每一份设置不同的CropBox区域第一份显示最上面一段第二份显示中间一段第三份显示最下面一段每个区域都接近A4的长宽比。这样输出出来的PDF就是多页了而且内容完全没有被修改只是每一页都只显示一部分。这条路线最大的优势是快和干净不需要重新编码页面里的文字和图片矢量内容依然是矢量文件不会明显变大。但它有一个限制——切出来的每一页物理尺寸就是你裁剪框的大小而不是A4纸张的大小。也就是说你得到的是若干张“原比例切片页”本身并不适合直接打印还需要打印时再做一次缩放。2.2 路线B渲染成图片后重新排版得到规整的A4页面如果你的最终目标是“每一页都是标准A4纸中间有白边最好还有页码”那CropBox路线就满足不了你了。这时候需要走路线B先把长图PDF渲染成一张完整的大图然后用图像处理工具按A4比例切成多块每块贴在独立的A4画布上最后输出成一个多页PDF。路线B的输出结果是看起来最舒服的页面大小统一内容居中留白均匀每个页面都是标准的“文档页”。代价也很明确原本的矢量内容会变成位图放大超过渲染倍数会发糊如果渲染倍数设得太高文件体积又会暴涨。所以路线B适合那些“本来就是一张大图”的PDF或者你确实需要一个规整A4版式去走打印流程的情况。2.3 两条路线的决策表为了让你能快速判断走哪条路我做了一个对比表对比项路线ACropBox切片路线B渲染重排处理对象任意PDF矢量无损纯图片型PDF最好输出页面原比例切片非标准A4标准A4带白边文件体积小内容未被修改大取决于渲染分辨率文字清晰度保持矢量清晰按渲染分辨率决定是否支持加页码不方便很方便实现复杂度低几十行代码中等需要图像处理最典型场景快速拆页传阅打印归档、正式文档我的习惯是临时的、发给别人手机上看的文件用路线A要打印装订、要交到单位系统的文件用路线B。下面把两条路线的具体做法都讲一遍。3. 路线A实操用pypdf把长条页面切成多个常规页路线A是我日常使用频率最高的方案因为能做到“收到文件三分钟内拆完”。这里直接给出完整步骤和代码。3.1 安装pypdf并准备体检脚本首先安装pypdf库pip install pypdf这里有个经验网上很多老教程还在用PyPDF2但这个库已经停止大规模维护新项目直接用pypdf就行API基本兼容坑更少。安装好后先运行上一节提到的体检代码把源PDF的宽度和高度打出来记好这两个数字。假设体检结果是这样的宽度595.28pt高度12600pt。我的目标是把高度12600pt的区域切成若干个接近A4长宽比的块。注意这里不是直接用A4高度841.89pt去当块高度因为那样切出来的每块宽度比例对不上除非后面打印时愿意被横向压缩。我的经验是做减法把A4高度841.89pt减去上下预留的白边各30pt得到可用高度781.89pt。如果长图宽度正好是595.28pt那就把781.89pt作为每块高度如果长图宽度比595.28pt宽或窄我会先让每块宽度适配A4宽度算出对应的等比高度再和A4可用高度取一个安全阈值。大多数网页截图都是窄长型的所以直接用781.89pt作为块高也不会太离谱。3.2 核心切页代码下面这段代码是把一个超级高的单页PDF切成多个高度接近A4的页面。每一步加注释说明了为什么这么写import copy from math import ceil from pypdf import PdfReader, PdfWriter reader PdfReader(long.pdf) writer PdfWriter() source reader.pages[0] w float(source.mediabox.width) h float(source.mediabox.height) # A4尺寸单位pt A4_WIDTH 595.28 A4_HEIGHT 841.89 # 每页上下留白保证视觉上有呼吸感 MARGIN 30 # 每个切块的高度按A4可用高度来 block_h A4_HEIGHT - MARGIN * 2 # 如果源页面宽度比A4窄切块宽高比例其实还是原样 # 如果比A4宽则按宽度比例重新算一个高度防止每块太矮。 if w A4_WIDTH * 0.95: block_h min(block_h, h / ceil(h / block_h)) total_blocks ceil(h / block_h) print(f源页面尺寸: {w:.2f} x {h:.2f}pt) print(f目标每块高度: {block_h:.2f}pt, 预计切 {total_blocks} 页) # 关键点每次都要用 copy.deepcopy 复制原始页面对象 # 不能直接在循环里重复引用同一个对象否则所有输出页会共用同一个裁剪框。 for i in range(total_blocks): top h - i * block_h bottom max(0.0, top - block_h) page copy.deepcopy(source) # PDF原点在左下角y轴向上。top对应源页面的上部bottom对应下部。 page.cropbox.lower_left (0.0, bottom) page.cropbox.upper_right (w, top) writer.add_page(page) print(f第{i 1}页: y范围 {bottom:.2f} ~ {top:.2f}) with open(output.pdf, wb) as fp: writer.write(fp) print(完成输出文件 output.pdf)运行完后用PDF阅读器打开output.pdf你应该能看到一个多页文档每一页都显示长图中的一段。上下顺序是正常的第一页对应长图最顶部最后一项对应长图最底部。3.3 进阶让切片页带着重叠区方便后期拼接有些场景下拆页只是第一步之后你还想把多页打印出来重新粘成一张长图比如打印一整张地铁线路图或完整的流程图。这时候有一个小技巧让相邻两页在边界处重叠一段区域重叠区通常设为20到50pt这样打印出来以后可以通过对齐重叠部分来精确拼接避免因打印机进纸误差导致的内容断裂。实现重叠区很简单只需要把上面的循环逻辑做一点调整每一页的底部比理想情况多往下一段顶部也比理想情况多往上一段。代码修改起来也不复杂overlap 30 for i in range(total_blocks): # 让每页的切割范围额外向上和向下扩展 overlap # 但第一页顶部、最后一页底部要钳制在0和h之间。 bottom_raw h - (i 1) * block_h - overlap bottom max(0.0, bottom_raw) top min(h, h - i * block_h overlap) page copy.deepcopy(source) page.cropbox.lower_left (0.0, bottom) page.cropbox.upper_right (w, top) writer.add_page(page)请注意重叠区如果设置过大相邻两页会显示大量重复内容阅读时会觉得“这一页怎么和上一页重复了”。一般控制在20到30pt就够用了。4. 踩坑实录坐标方向、对象复用和文字被切断写拆页脚本看似简单但我实际测试时踩过不少坑。下面几个问题如果你自己写代码大概率也会遇到提前知道能省很多调试时间。4.1 最隐蔽的错PDF的y轴是从下往上我第一次写切页代码时按照图像处理的习惯觉得页面左上角是原点y轴向下。结果切完打开文件一看第一页显示的是长图的底部最后一页反而显示顶部整个文档的顺序完全反了。原因就在于PDF坐标系的原点在左下角y轴向上增长。长图最顶部的位置对应的y坐标是页面高度h长图最底部的位置对应的y坐标才是0。所以切分第一块时正确范围应该是(0, h - block_h)到(w, h)而不是靠直觉写的(0, 0)到(w, block_h)。这个坑一定要记牢。只要涉及PDF裁剪或定位必须先想想坐标系方向。写代码前先跑一个只有两三页的实验输出肉眼确认第一页是不是顶部是最稳妥的做法。4.2 同一个页面对象反复add_page所有输出页都会变成最后一块另一个让我卡了半小时的坑出在对象复用上。最初我的代码是直接在循环里这样写的page source # 错误示例 page.cropbox.lower_left (0.0, bottom) page.cropbox.upper_right (w, top) writer.add_page(page)跑完后打开输出文件每一页显示的全是同一个区域——最后一页的裁剪范围。原因是cropbox是页面对象的属性而同一个page对象被反复添加实际上所有页面引用的都是同一个页面对象后一次赋值覆盖了前一次最后所有页都指向同一个裁剪框。解决方案就是我上面展开的copy.deepcopy(source)每轮循环复制出一个完全独立的页面对象再改它的cropbox就不会影响其他页面。这是拆页脚本里最容易出错的点值得牢牢记住。4.3 切到文字和图片的边界怎么处理长图PDF如果本身就是一张纯图片那切在哪里都无所谓反正都是像素。但如果PDF里有文字层尤其是试卷、论文、协议这类文字密集的文档直接按固定高度切很容易把一行字拦腰截断。打印出来可能还没那么明显但在屏幕上翻页时会觉得字被切了半截非常难受。我的处理经验是如果切割线附近正好压住了文字行在允许的范围内微调切割位置。具体做法是先用PyMuPDF读取源页面的文字块坐标找到所有文字块的底部y轴坐标然后在一个允许偏移的窗口里找“距离理想切割线最近、且不会落在任何文字块bbox内”的位置作为真实切割线。说白了就是让切割线尽量贴在行与行的空隙之间。这个逻辑写起来会多几十行代码但对于文字类PDF的提升非常明显。很多商业拆页工具宣传的“智能拆页”其实核心思路就是这个。5. 路线B备用渲染成图片后重新排版以及不写代码的替代工具如果需要输出标准A4带白边、带页码的文档走路线B会更直接。我在处理“必须交到单位系统里归档”的文件时基本都用这套流程。5.1 用PyMuPDF把长图页面渲染成完整PNGPyMuPDF库名是fitz是个非常好用的PDF渲染工具。长图PDF虽然只有一个页面但渲染时要设置合适的缩放倍数。我的经验是看原始宽度以宽度为基准计算缩放倍数因为大多数长图的宽度只有几百到一两千像素直接1倍渲染在A4上会显得糊。import fitz doc fitz.open(long.pdf) page doc[0] # 计算渲染倍数让输出图片宽度达到约1500像素 base_width 595.28 target_width 1500 zoom target_width / base_width mat fitz.Matrix(zoom, zoom) pix page.get_pixmap(matrixmat) pix.save(full_render.png) print(f渲染完成: {pix.width} x {pix.height}px)目标宽度1500到2000像素是我试下来比较均衡的值屏幕阅读足够清晰打印300dpi也勉强能看文件体积不会爆炸。如果原文件是要印刷的建议直接上300dpi甚至更高按公式换算就是缩放倍数约4.16。5.2 PIL切片并输出成带白边和页码的多页PDF拿到完整PNG后直接用Pillow按A4比例切片同时给每页加上白边和页码from PIL import Image, ImageDraw from math import ceil img Image.open(full_render.png) w, h img.size # 设定输出画布为A4比例留出白边 CANVAS_W 1500 CANVAS_H 2123 # 约A4比例1500 * 297 / 210 MARGIN 80 # 切片高度等比缩放后大约占满画布可用区域 usable_h CANVAS_H - MARGIN * 2 # 如果原始图片宽度明显小于画布宽度则按宽度等比调整切片高度 # 否则直接按可用高度切片。 if w CANVAS_W - MARGIN * 2: usable_h int(usable_h * w / (CANVAS_W - MARGIN * 2)) pages [] for idx, y in enumerate(range(0, h, usable_h)): crop img.crop((0, y, w, min(h, y usable_h))) canvas Image.new(RGB, (CANVAS_W, CANVAS_H), white) # 水平居中垂直居中 x0 (CANVAS_W - crop.width) // 2 y0 (CANVAS_H - crop.height) // 2 canvas.paste(crop, (x0, y0)) draw ImageDraw.Draw(canvas) page_num idx 1 draw.text((CANVAS_W // 2, CANVAS_H - 35), f- {page_num} -, fillblack) pages.append(canvas) # 保存为多页PDF pages[0].save(output_clean.pdf, save_allTrue, append_imagespages[1:], resolution150.0) print(f生成 {len(pages)} 页 PDF)这段代码看起来简单但你会得到一份页面统一、内容居中、每页都有页码的正规文档。整个过程相当于把长图做了一次“书籍化”排版效果比CropBox路线好很多。5.3 不想写代码的替代工具如果完全不想碰命令行也有可行的桌面工具但选择要谨慎。第一个是开源工具BrissJava编写的PDF裁剪软件。它能自动分析页面内容把一页裁成多页也支持手动拖拽矩形框选区域。对于标准双栏论文、报告这种“规则页面”效果很好但对长图类的超长页面需要手动框选多个区域效率没有脚本高。适合偶尔处理一次文件的场景。第二个是Adobe Acrobat Pro自带的“平铺打印”功能。在打印对话框里选择打印到Adobe PDF然后开启“平铺所有页”设置好缩放比例它会把一个超大页面按照物理纸张尺寸拆成多页生成新PDF。麻烦的是参数需要反复试锋利的裁切线纪律性也一般但胜在不装任何新软件。还有一个思路如果原文件根本就是一张长图而不是PDF那完全可以直接用图片处理软件或上面的PIL脚本切片不用先转PDF再拆省掉一层转换损耗。至于网上那些在线转换工具我试过几个主流的绝大多数只能按“PDF已有的页”进行拆分或合并对于“一个页面内切割成多页”这种需求几乎没有支持。偶尔有一两个支持“PDF平铺”的工具也基本是调用的打印引擎输出质量参差不齐。涉及敏感内容或大体积文件时在线工具也不安全。所以我更推荐优先用脚本或本地工具解决。就我自己的频率来说路线A的pypdf脚本占了80%以上的场景因为快、无损、可控路线B留给需要正式排版打印的少数文件。两套方案配合用基本能应对所有长图PDF拆页需求。如果你手里的长图PDF是从网页截图来的拆完之后每页比例接近正常翻起来舒服得多如果是合同或试卷那种文字密集的记得先跑一下切割线微调逻辑保住文字完整性。遇到特殊尺寸的长图核心思路就是先体检、再切、后验证整个流程跑通一次后面就都是重复劳动了。