PDF页面尺寸与旋转的实用技巧:混合纸张的正确处理方式
文章摘要
探讨PDF中MediaBox、CropBox等页面边界框的区别,解析页面旋转矩阵的工作原理,以及如何处理不同尺寸页面混排、批量旋转、自动裁剪等实际问题。
前几天接到个需求:把扫描的A4和A3文档合并成一个PDF,还要自动裁掉白边。听起来简单,实际上涉及MediaBox、CropBox、旋转矩阵好几个概念。今天就聊聊PDF页面操作的这些门道。
四种边界框(Box)的区别
PDF的页面有四种边界框定义,很多人只知道MediaBox:
5 0 obj
/Type /Page
/MediaBox [0 0 595 842] % A4纸
/CropBox [50 50 545 792] % 裁掉四周各50点
/Contents 6 0 R
/Resources << ... >>
>>
endobj
页面旋转的两种方式
PDF支持两种旋转机制,很多人混淆:
/Rotate 90 % 顺时针旋转90度
q % 保存状态
0.866 0.5 -0.5 0.866 0 0 cm % 旋转30度
... 绘图指令 ...
Q % 恢复状态
批量旋转页面用/Rotate,因为它会更新页面的逻辑方向。变换矩阵适合旋转页面内的局部元素,比如水印。
实战:混合纸张处理
用PyMuPDF处理不同尺寸的页面:
import fitz
def normalize_pages(input_pdf, output_pdf):
doc = fitz.open(input_pdf)
# 统一所有页面为A4横向
target_size = fitz.paper_rect("a4-l") # A4 landscape
for page in doc:
# 获取当前页面尺寸
current_rect = page.rect
# 如果是竖向A3,旋转90度
if current_rect.width < current_rect.height:
page.set_rotation(90)
# 设置新的MediaBox
page.set_mediabox(target_size)
# 裁剪白边(假设白边在四周各20点)
margin = 20
crop_rect = fitz.Rect(
margin, margin,
target_size.width - margin,
target_size.height - margin
)
page.set_cropbox(crop_rect)
doc.save(output_pdf)
doc.close()
print(f"处理完成:{doc.page_count}页已统一")
normalize_pages('mixed.pdf', 'normalized.pdf')
自动裁剪白边
检测内容边界,自动计算CropBox:
def auto_crop(pdf_path, output_path):
doc = fitz.open(pdf_path)
for page in doc:
# 获取页面上所有可见内容的边界
blocks = page.get_text("dict")["blocks"]
if not blocks:
continue
# 找到内容的最小包围盒
x0 = min(b["bbox"][0] for b in blocks)
y0 = min(b["bbox"][1] for b in blocks)
x1 = max(b["bbox"][2] for b in blocks)
y1 = max(b["bbox"][3] for b in blocks)
# 加上10点的边距
margin = 10
crop_box = fitz.Rect(
x0 - margin, y0 - margin,
x1 + margin, y1 + margin
)
page.set_cropbox(crop_box)
doc.save(output_path)
doc.close()
auto_crop('scanned.pdf', 'cropped.pdf')
常见问题解决
/Rotate属性不会降低质量,但变换矩阵旋转可能导致重新光栅化。尽量用页面级旋转。快速参考
PDF页面操作看似简单,实则细节很多。MediaBox定义物理尺寸,CropBox控制可见区域,/Rotate实现页面旋转——理解这三个概念就能解决大部分问题。处理混合纸张时,记得先统一尺寸再操作,避免后续排版混乱。自动裁剪白边要小心,别把有用内容也裁掉了,加个安全边距总是好的。