如何在Django中用Python裁剪A3 PDF为等分A3页面?无Poppler方案
问题:无需Poppler依赖实现A3 PDF纵向分割与重排
场景说明
我要上传一份带图片的A3规格PDF(尺寸29.7cm×42cm),需求是把它裁剪成2等分。具体逻辑是:将PDF纵向分割为上下两半——上半部分尺寸29.7cm×21cm(对应原PDF的0-21cm高度范围),下半部分29.7cm×21cm(对应原PDF的21-42cm高度范围);之后把每个裁剪后的部分调整回A3尺寸,最终生成包含2页的PDF,效果类似图片裁剪,但要直接对PDF操作。
现有代码尝试
from django.shortcuts import render from django.http import HttpResponse from .forms import CartazUploadForm import PyPDF2 from io import BytesIO from reportlab.pdfgen import canvas from reportlab.lib.pagesizes import A3 from pdf2image import convert_from_bytes def make_gradil(request): if request.method == 'POST': form = CartazUploadForm(request.POST, request.FILES) if form.is_valid(): gradil = form.cleaned_data['gradil'] cartaz_file = request.FILES['cartaz_file'] # Create a new PDF writer writer = PyPDF2.PdfWriter() # Read the uploaded PDF reader = PyPDF2.PdfReader(cartaz_file) for page_num in range(len(reader.pages)): page = reader.pages[page_num] # Converter a página em uma imagem pdf_bytes = BytesIO() writer_temp = PyPDF2.PdfWriter() writer_temp.add_page(page) writer_temp.write(pdf_bytes) pdf_bytes.seek(0) images = convert_from_bytes(pdf_bytes.read()) if not images: continue # Obter a imagem da página image = images[0] # Criar um buffer para o novo PDF buffer = BytesIO() c = canvas.Canvas(buffer, pagesize=A3) # Dividir a imagem em duas metades left_half = image.crop((0, 0, image.width // 2, image.height)) right_half = image.crop((image.width // 2, 0, image.width, image.height)) # Redimensionar e adicionar a primeira metade left_half.save("left_half.png") c.drawImage("left_half.png", 0, 0, width=A3[0] / 2, height=A3[1], preserveAspectRatio=True) c.showPage() # Redimensionar e adicionar a segunda metade right_half.save("right_half.png") c.drawImage("right_half.png", 0, 0, width=A3[0] / 2, height=A3[1], preserveAspectRatio=True) c.showPage() c.save() buffer.seek(0) # Adicionar as páginas redimensionadas ao writer new_reader = PyPDF2.PdfReader(buffer) for new_page in new_reader.pages: writer.add_page(new_page) # Create the response response = HttpResponse(content_type='application/pdf') response['Content-Disposition'] = 'attachment; filename="gradil.pdf"' # Write the PDF to the response with BytesIO() as output_buffer: writer.write(output_buffer) response.write(output_buffer.getvalue()) return response else: form = CartazUploadForm() return render(request, 'core/index.html', {'form': form})
问题
基于Django开发,上述代码运行时提示需要安装Poppler组件,但我希望避免依赖额外的系统级程序,求可行的替代方案。
解决方案:直接操作PDF页面(无需Poppler)
可以跳过转图片的步骤,直接通过PyPDF2和ReportLab对PDF页面进行裁剪、缩放和重排,完全不需要依赖Poppler。核心思路是:
- 获取原PDF页面尺寸,精准计算上下两半的裁剪区域
- 对每个裁剪区域生成新的A3页面,将裁剪内容缩放填充到A3尺寸中
修改后的代码如下:
from django.shortcuts import render from django.http import HttpResponse from .forms import CartazUploadForm import PyPDF2 from io import BytesIO from reportlab.pdfgen import canvas from reportlab.lib.pagesizes import A3 def make_gradil(request): if request.method == 'POST': form = CartazUploadForm(request.POST, request.FILES) if form.is_valid(): cartaz_file = request.FILES['cartaz_file'] writer = PyPDF2.PdfWriter() reader = PyPDF2.PdfReader(cartaz_file) # A3尺寸转换:1cm = 28.3465点,A3高度42cm,半高21cm HALF_HEIGHT_POINTS = 21 * 28.3465 for page_num in range(len(reader.pages)): page = reader.pages[page_num] orig_width = float(page.mediabox.width) orig_height = float(page.mediabox.height) # 定义上下两半的裁剪区域(x1, y1, x2, y2) upper_crop = (0, orig_height - HALF_HEIGHT_POINTS, orig_width, orig_height) lower_crop = (0, 0, orig_width, HALF_HEIGHT_POINTS) # 处理上半部分,生成A3页面 upper_buffer = BytesIO() c = canvas.Canvas(upper_buffer, pagesize=A3) c.drawImage( cartaz_file, 0, 0, width=A3[0], height=A3[1], preserveAspectRatio=True, crop=upper_crop ) c.showPage() c.save() upper_buffer.seek(0) writer.add_page(PyPDF2.PdfReader(upper_buffer).pages[0]) # 处理下半部分,生成A3页面 lower_buffer = BytesIO() c = canvas.Canvas(lower_buffer, pagesize=A3) c.drawImage( cartaz_file, 0, 0, width=A3[0], height=A3[1], preserveAspectRatio=True, crop=lower_crop ) c.showPage() c.save() lower_buffer.seek(0) writer.add_page(PyPDF2.PdfReader(lower_buffer).pages[0]) # 返回生成的PDF文件 response = HttpResponse(content_type='application/pdf') response['Content-Disposition'] = 'attachment; filename="gradil.pdf"' with BytesIO() as output_buffer: writer.write(output_buffer) response.write(output_buffer.getvalue()) return response else: form = CartazUploadForm() return render(request, 'core/index.html', {'form': form})
代码说明
- 直接用ReportLab的
drawImage读取原PDF,通过crop参数指定裁剪区域,无需转图片 - 基于厘米与PDF点的转换关系,精准匹配A3上下半部分的裁剪范围
- 裁剪后的内容自动缩放填充到A3页面,保持原比例不变
- 仅依赖Python库(PyPDF2、ReportLab),无系统级组件依赖
内容的提问来源于stack exchange,提问作者Patrik Rufino
相关产品推荐
相关产品推荐

