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

Python图片转PDF脚本异常:仅显示单图且未添加边距

图片转PDF脚本错误分析与修复

原脚本核心错误原因

1. 页面边距与尺寸计算逻辑颠倒

当启用单图单页模式时,原代码错误地将页面尺寸设置为图片尺寸减去两倍边距:

page_width = first_image.width - 2 * margin
page_height = first_image.height - 2 * margin

这会导致页面比图片还小,图片加上边距后会超出页面范围;同时所有页面固定使用第一张图的尺寸,没有根据每张图片的实际尺寸动态调整,不符合“PDF页面尺寸匹配对应图片尺寸”的需求。

2. 单图单页模式未添加分页,图片堆叠覆盖

原脚本在single_page=True时,循环绘制图片过程中没有调用c.showPage(),所有图片都被绘制在同一个PDF页面上,后绘制的图片会完全覆盖之前的内容,最终只显示最后一张图片,但所有图片的数据都已嵌入PDF,因此文件大小等于所有图片尺寸的总和。

3. 参数命名歧义

single_page参数命名容易被误解为“所有图片放在同一页”,与实际需求“每张图片单独一页”不符,容易引发逻辑错误。


修复后的完整脚本

import os
import logging
from PIL import Image
from reportlab.lib.pagesizes import letter, portrait
from reportlab.pdfgen import canvas

# Setup logging to a file
logging.basicConfig(filename="pdf_script_log.txt", level=logging.DEBUG, format="%(asctime)s %(levelname)s %(message)s")

def convert_images_to_jpg(source_dir, temp_dir, quality, scaling_factor, user_prefers_sharpness):
    """
    Converts images in the source directory to JPG format with specified parameters.

    Args:
        source_dir (str): Absolute path to the directory containing image files.
        temp_dir (str): Absolute path to the temporary directory for storing converted images.
        quality (int): Quality parameter for image compression (0-100).
        scaling_factor (float): Scaling factor for resizing images (0.0-1.0).
        user_prefers_sharpness (bool): Whether sharper downscaling is preferred (True) or not (False).

    Returns:
        list: A list of paths to the converted JPG images.
    """
    # Check if there are already converted images in the temporary directory
    existing_jpg_files = [f for f in os.listdir(temp_dir) if f.endswith('.jpg')]
    if existing_jpg_files:
        logging.info("Found existing converted images in the temporary directory. Skipping conversion.")
        return [os.path.join(temp_dir, f) for f in existing_jpg_files]

    jpg_images = []
    for filename in os.listdir(source_dir):
        if os.path.isfile(os.path.join(source_dir, filename)):
            try:
                img = Image.open(os.path.join(source_dir, filename))
                # Convert to RGB for better compression
                img = img.convert("RGB")

                # Apply scaling factor
                new_width = int(img.width * scaling_factor)
                new_height = int(img.height * scaling_factor)
                img.thumbnail((new_width, new_height), Image.Resampling.LANCZOS if user_prefers_sharpness else Image.Resampling.BICUBIC)

                # Save as JPG with specified quality
                jpg_filename = os.path.join(temp_dir, os.path.splitext(filename)[0] + ".jpg")
                img.save(jpg_filename, quality=quality)
                jpg_images.append(jpg_filename)
            except Exception as e:
                logging.error(f"Error converting image '{filename}' to JPG format: {e}")
    return jpg_images

def create_pdf(output_dir, desired_name, title, author, keywords, per_image_page=True, margin=20):
    """
    Creates a PDF document from a series of JPG image files.

    Args:
        output_dir (str): Output directory for the created PDF.
        desired_name (str): Desired name for the PDF (without extension).
        title (str): Title of the PDF.
        author (str): Author of the PDF.
        keywords (list): Keywords associated with the PDF.
        per_image_page (bool, optional): Whether to create a separate page for each image (matching image size + margin). Defaults to True.
        margin (int, optional): Margin size in pixels for each image on the page. Defaults to 20.
    """

    logging.info("Starting PDF creation...")

    temp_dir = os.path.join(output_dir, 'temp')
    jpg_files = [f for f in os.listdir(temp_dir) if f.endswith('.jpg')]
    if not jpg_files:
        logging.error("No JPG image files found for PDF creation.")
        return

    jpg_files.sort(key=lambda x: int(''.join(filter(str.isdigit, os.path.splitext(x)[0]))))

    try:
        pdf_path = os.path.join(output_dir, desired_name + ".pdf")
        # Initialize canvas without fixed page size (will set per page)
        c = canvas.Canvas(pdf_path)

        # Add Metadata to the PDF
        c.setTitle(title)
        c.setAuthor(author)
        c.setKeywords(keywords)

        # Iterate through all JPG images and draw them
        for jpg_file in jpg_files:
            if jpg_file.endswith(".jpg"):
                image_path = os.path.join(temp_dir, jpg_file)
                try:
                    img = Image.open(image_path)
                    img_width, img_height = img.size

                    if per_image_page:
                        # Set page size to image size + 2*margin (left/right, top/bottom)
                        page_width = img_width + 2 * margin
                        page_height = img_height + 2 * margin
                        c.setPageSize(portrait((page_width, page_height)))
                        # Calculate image position (margin from each edge)
                        x = margin
                        y = margin
                    else:
                        # Use letter size with margin
                        page_size = portrait(letter)
                        c.setPageSize(page_size)
                        max_width = page_size[0] - 2 * margin
                        max_height = page_size[1] - 2 * margin
                        # Scale image to fit within page bounds while maintaining aspect ratio
                        scale = min(max_width / img_width, max_height / img_height)
                        scaled_width = img_width * scale
                        scaled_height = img_height * scale
                        x = (page_size[0] - scaled_width) / 2
                        y = (page_size[1] - scaled_height) / 2
                        img_width, img_height = scaled_width, scaled_height

                    # Draw the image
                    c.drawImage(image_path, x, y, width=img_width, height=img_height)
                    # Add new page after each image
                    c.showPage()

                except IOError as e:
                    logging.error(f"Error reading image: {image_path} - {e}")

        # Save the PDF and log success message
        c.save()
        logging.info(f"PDF created successfully at: {pdf_path}")

    except Exception as e:
        logging.error(f"Error during PDF creation: {e}")

    # Clean up temporary directory (optional, consider manual review)
    for file in os.listdir(temp_dir):
        file_path = os.path.join(temp_dir, file)
        os.remove(file_path)
    logging.info("Temporary files cleaned up.")


# Main execution
try:
    # Define variables for customization
    source_dir = ""  # Replace with your source directory path
    output_dir = ""  # Replace with your output directory path
    desired_name = "converted_images"
    title = "Image Collection PDF"
    author = "User"
    keywords = ["images", "pdf", "conversion"]
    per_image_page = True
    quality = 60
    scaling_factor = 0.7
    user_prefers_sharpness = True

    # Temporary directory for storing converted images
    temp_dir = os.path.join(output_dir, 'temp')

    # Create temporary directory if it doesn't exist
    os.makedirs(temp_dir, exist_ok=True)

    # Convert images to JPG format
    jpg_images = convert_images_to_jpg(source_dir, temp_dir, quality, scaling_factor, user_prefers_sharpness)

    # Create PDF from converted images
    create_pdf(output_dir, desired_name, title, author, keywords, per_image_page)

    # Close logging and exit
    logging.shutdown()
    logging.info("Script execution completed.")

except Exception as e:
    logging.error(f"An error occurred during script execution: {e}")
    logging.shutdown()

修复关键点

  1. 修正页面尺寸计算:将页面尺寸设置为图片尺寸 + 2*边距,确保图片放在边距位置时刚好适配页面,同时为每张图片动态设置对应页面尺寸。
  2. 强制分页逻辑:无论哪种模式,每次绘制完图片后都调用c.showPage(),确保每张图片在独立页面,避免堆叠覆盖。
  3. 参数命名优化:将single_page重命名为per_image_page,明确表示“每张图片单独一页”的逻辑。
  4. 非单图页面模式优化:当禁用单图单页时,自动缩放图片以适配letter页面的边距范围,保持图片比例。

内容的提问来源于stack exchange,提问作者Rafique Suchwani

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.29 23:37:03