You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

使用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

关键修改点:

  1. 读取原始裁剪框:直接从页面的PDF字典page.dict["CropBox"]获取,避免PyMuPDF使用文档默认的612x792值
  2. 自动适配旋转page.get_pixmap()会自动应用页面的rotation属性,所以横向页面会被正确渲染为横向图片
  3. 精准控制分辨率:通过dpi参数直接指定渲染精度,无需额外计算zoom值(PyMuPDF会自动换算)

三、为什么你之前打印的CropBox全是默认值?

这是PyMuPDF的默认行为:如果页面没有显式设置/CropBox条目,就会从文档的Pages字典继承默认裁剪框(612x792,即Letter纸尺寸)。但很多PDF的横向/纵向布局是通过旋转角度page.rotation)+媒体框实现的,而非直接修改裁剪框。你可以试试打印page.rotation,原始PDF里的横向页面这个值应该是90或270度——之前保存时这个旋转属性被默认清理了,所以导出的PDF就变成了统一的纵向。

用上面的方案处理后,不管是保存的PDF还是渲染的图片,都会和原始PDF的布局完全匹配。

备注:内容来源于stack exchange,提问作者axelmukwena

火山引擎 最新活动