使用PyMuPDF保存PDF时如何保留原始页面方向与裁剪框?
使用PyMuPDF保存PDF时如何保留原始页面方向与裁剪框?
我明白你遇到的问题了——用PyMuPDF简单打开再保存PDF后,原本不同尺寸、旋转方向的页面全变成统一的默认大小,裁剪框也没保留下来,这确实头疼。我来帮你解决这个问题,分两种场景给你方案:一种是直接保存PDF时保留属性,另一种是适配你转图片的目标函数。
一、直接保存PDF时保留原始布局
PyMuPDF默认的save()方法会做一些自动优化(比如清理冗余数据、重置页面框),这就是导致属性丢失的核心原因。你需要调整保存参数,同时显式锁定页面的关键属性:
import pymupdf pdf = pymupdf.open(pdf_path) # 遍历所有页面,显式保留旋转、裁剪框、媒体框属性 for page in pdf: # 锁定原始旋转角度(避免被自动重置) page.set_rotation(page.rotation) # 保留当前页面的裁剪框(如果有自定义设置会生效) page.set_cropbox(page.cropbox) # 同步保留媒体框(确保页面基础尺寸正确) page.set_mediabox(page.mediabox) # 保存时关闭自动清理,避免破坏原始属性 pdf.save( "preserved-output.pdf", garbage=0, # 关闭所有自动垃圾回收/清理操作 clean=False, # 不清理页面冗余对象 no_new_id=True # 保留原始PDF的对象ID,避免关联属性丢失 ) pdf.close()
参数详解:
garbage=0:默认值是3,会深度清理页面的“冗余”数据(包括自定义裁剪框和旋转属性),设为0就能完全关闭这个行为clean=False:禁止清理页面的非标准属性,确保自定义布局设置不被删除no_new_id=True:保留原始PDF的对象标识符,避免页面关联的布局配置被破坏
二、适配你的PDF转图片数组函数
你的最终目标是生成和原始PDF布局一致的图片数组,需要确保渲染时读取到最原始的页面属性,而不是PyMuPDF的默认继承值。修改后的函数如下:
import pymupdf import numpy as np from PIL import Image import cv2 def convert_pdf_to_image_arrays(pdf_path: str, dpi: int) -> list[np.ndarray]: """ Convert a PDF to high-resolution image arrays, preserving color fidelity and original layout. :param pdf_path: Path to the PDF file. :param dpi: DPI (dots per inch) for rendering high-resolution images. :return: List of NumPy arrays representing images of the PDF pages. """ pdf = pymupdf.open(pdf_path) images: list[np.ndarray] = [] for page in pdf: # 从页面原始字典读取自定义裁剪框,避免使用默认继承值 if "CropBox" in page.dict: original_cropbox = pymupdf.Rect(page.dict["CropBox"]) page.set_cropbox(original_cropbox) # 渲染pixmap:自动应用页面旋转+裁剪框,dpi确保分辨率 pix = page.get_pixmap(dpi=dpi) # 转换为PIL Image(保留色彩精度) img_pil = Image.frombytes( mode="RGB" if pix.n == 3 else "RGBA", size=(pix.width, pix.height), data=pix.samples, ) # 转换为NumPy数组并调整颜色通道为OpenCV格式 img_array = np.array(img_pil) if pix.n == 4: img_array = cv2.cvtColor(img_array, cv2.COLOR_RGBA2BGR) else: img_array = cv2.cvtColor(img_array, cv2.COLOR_RGB2BGR) images.append(img_array) pdf.close() return images
关键修改点:
- 读取原始裁剪框:直接从页面的PDF字典
page.dict["CropBox"]获取,避免PyMuPDF使用文档默认的612x792值 - 自动适配旋转:
page.get_pixmap()会自动应用页面的rotation属性,所以横向页面会被正确渲染为横向图片 - 精准控制分辨率:通过
dpi参数直接指定渲染精度,无需额外计算zoom值(PyMuPDF会自动换算)
三、为什么你之前打印的CropBox全是默认值?
这是PyMuPDF的默认行为:如果页面没有显式设置/CropBox条目,就会从文档的Pages字典继承默认裁剪框(612x792,即Letter纸尺寸)。但很多PDF的横向/纵向布局是通过旋转角度(page.rotation)+媒体框实现的,而非直接修改裁剪框。你可以试试打印page.rotation,原始PDF里的横向页面这个值应该是90或270度——之前保存时这个旋转属性被默认清理了,所以导出的PDF就变成了统一的纵向。
用上面的方案处理后,不管是保存的PDF还是渲染的图片,都会和原始PDF的布局完全匹配。
备注:内容来源于stack exchange,提问作者axelmukwena




