You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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。核心思路是:

  1. 获取原PDF页面尺寸,精准计算上下两半的裁剪区域
  2. 对每个裁剪区域生成新的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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 03:24:57