How to Batch Convert Images to PDF with Python: A Complete Guide

How to Batch Convert Images to PDF with Python: A Complete Guide

Converting dozens—or even hundreds—of image files to PDF format one at a time is a tedious waste of productive hours. Whether you’re digitizing scanned documents, archiving photo collections, or preparing screenshots for documentation workflows, manual conversion tools force you to repeat the same clicks for every single file. This guide shows you how to batch convert images to PDF with Python using a streamlined automation script that processes entire directories in seconds, not hours.

This Python-based solution uses the PIL/Pillow library to handle six common image formats (JPG, PNG, BMP, GIF, TIFF, WEBP) and converts each to an individual PDF file automatically. Unlike commercial software that requires licenses or online services that compromise privacy, this script runs locally and gives you complete control over your document workflow. Office workers, researchers, archivists, and students can eliminate repetitive conversion tasks and focus on higher-value work.

Why Batch Convert Images to PDF Instead of Manual Processing?

Manual image-to-PDF conversion creates bottlenecks in document-heavy workflows. When you’re processing scanned receipts, digitizing archival photographs, or preparing research materials, opening each file individually in a PDF creator wastes minutes per document—time that multiplies exponentially with volume.

Batch processing automation transforms hours of clicking into a single command. A researcher digitizing 200 pages of historical documents saves approximately 100 minutes by automating conversion versus manual processing (assuming 30 seconds per file). The script handles format detection, color mode conversion, and file naming automatically, eliminating human error from repetitive tasks.

The architectural decision to create individual PDFs rather than merging all images into one document serves specific use cases. Separate files allow selective sharing, easier organization in document management systems, and simpler revision workflows. Users who later need merged documents can combine PDFs using complementary tools while maintaining the flexibility of granular file-level control.

For teams managing ongoing document digitization—like medical offices scanning patient forms or legal departments processing case files—the ability to drop new images into a watched folder and execute batch conversion on demand creates reproducible, scalable workflows that reduce operational overhead.

Prerequisites: Setting Up Python and Pillow for Image Conversion

Before running the batch converter, verify that Python 3.x is installed on your system. Open a terminal or command prompt and type python --version (or python3 --version on macOS/Linux). If Python isn’t installed, download it from python.org, ensuring you check “Add Python to PATH” during Windows installation.

The script depends on the PIL (Pillow) library, the modern fork of Python Imaging Library that handles image file operations. Install Pillow using pip:

pip install Pillow

On some systems, you may need to use pip3 install Pillow instead. Verify successful installation by opening a Python interpreter and typing import PIL—if no error appears, you’re ready to proceed.

The script requires no additional dependencies or configuration files. It uses only Python standard library modules (os, time, re) alongside Pillow, keeping the installation footprint minimal. This design choice prioritizes accessibility for non-technical users who need working automation without navigating complex dependency chains.

System requirements are modest: any computer capable of running Python 3.x will handle typical image conversion workloads. Processing speed scales with image file size and quantity, but even older hardware converts standard document scans (3-5 MB per image) at 2-3 files per second.

How the Batch Image to PDF Converter Works

The converter implements a straightforward pipeline: directory scanning → file sorting → image loading → format conversion → PDF generation. This architecture separates concerns cleanly, making each step independently testable and maintainable.

When executed, the script first prompts for a target directory path. If you press Enter without specifying a path, it defaults to the script’s own location—a deliberate design choice that simplifies the most common use case where users place the script directly in their image folder. This eliminates the cognitive overhead of remembering and typing full directory paths.

The sorting mechanism offers two strategies because different workflows have different organizational needs. Chronological sorting (by modification date) serves users scanning documents over time, preserving the temporal sequence of creation. Filename-based natural sorting handles sequentially numbered files correctly—ensuring image2.jpg appears before image10.jpg instead of incorrectly alphabetizing to image10.jpg, image2.jpg. This natural sorting algorithm uses regular expressions to separate numeric and alphabetic components, then compares them appropriately.

A critical technical decision addresses a limitation in the Pillow library: the script converts all images to RGB color mode before generation. Pillow’s PDF generator doesn’t currently support saving RGBA (transparency) or certain other color modes directly. Without this conversion, images with alpha channels or exotic color spaces would cause the script to crash during PDF creation. The automatic RGB conversion happens transparently—users never need to understand color theory to get working PDFs.

The generator creates individual files with identical base names, swapping image extensions for .pdf. This maintains semantic connections between originals and outputs, simplifying file management when dealing with descriptively named source images like 2026-03-financial-report-chart.png2026-03-financial-report-chart.pdf.

Complete Python Script Walkthrough with Code Examples

The core functionality centers on Pillow’s Image.open() and Image.save() methods, wrapped in directory traversal and error handling logic. Here’s the complete script:

import os
import time
import re
from PIL import Image

def natural_sort_key(s):
    return [int(text) if text.isdigit() else text.lower()
            for text in re.split('([0-9]+)', s)]

def convert_images_to_pdf(directory=None):
    if directory is None:
        directory = os.path.dirname(os.path.abspath(__file__))

    supported_formats = ('.jpg', '.jpeg', '.png', 
                        '.bmp', '.gif', '.tiff', '.tif', '.webp')

    image_files = [f for f in os.listdir(directory) 
                   if f.lower().endswith(supported_formats)]

    if not image_files:
        print("No supported image files found.")
        return

    sort_choice = input("Sort by (1) date or (2) name? ")

    if sort_choice == '1':
        image_files.sort(key=lambda x: os.path.getmtime(
            os.path.join(directory, x)))
    else:
        image_files.sort(key=natural_sort_key)

    for img_file in image_files:
        try:
            img_path = os.path.join(directory, img_file)
            pdf_filename = os.path.splitext(img_file)[0] + '.pdf'
            pdf_path = os.path.join(directory, pdf_filename)

            image = Image.open(img_path)

            if image.mode != 'RGB':
                image = image.convert('RGB')

            image.save(pdf_path, 'PDF', resolution=100.0)

            mod_time = time.ctime(os.path.getmtime(img_path))
            print(f"Converted: {img_file} -> {pdf_filename} ({mod_time})")

        except Exception as e:
            print(f"Failed to convert {img_file}: {str(e)}")

if __name__ == "__main__":
    target_dir = input("Enter directory path (or press Enter for current): ")
    convert_images_to_pdf(target_dir if target_dir else None)

The natural_sort_key() function implements intelligent numeric sorting through regular expression splitting. The pattern ([0-9]+) captures digit sequences, and re.split() breaks strings into alternating text and number components. The list comprehension then converts numeric strings to integers for proper numerical comparison while preserving alphabetic segments as lowercase strings for case-insensitive sorting.

The RGB conversion check (if image.mode != 'RGB') prevents unnecessary processing for images already in the correct format while ensuring compatibility for those that aren’t. This defensive programming approach handles edge cases without requiring users to understand image color models.

The resolution=100.0 parameter during PDF save balances output file size against quality. Setting DPI to 100 produces readable documents without creating unnecessarily large files—important when processing hundreds of scans. Users needing higher quality for professional printing can modify this value, but 100 DPI serves typical screen viewing and archival purposes.

Error handling wraps each conversion in a try-except block, allowing the script to continue processing remaining files even if one image is corrupted or uses an unsupported variant of a listed format. This resilience prevents a single problematic file from blocking entire batch operations.

Running the Script: Step-by-Step Conversion Process

Place the script file (convert_jpg_to_pdf.py) in the same directory as your image files, or prepare to specify the target directory path. Open a terminal or command prompt and navigate to the script’s location using the cd command.

Execute the script by running:

python convert_jpg_to_pdf.py

The script prompts: Enter directory path (or press Enter for current):. Press Enter to process images in the script’s directory, or type an absolute path like /Users/username/Documents/scans (macOS/Linux) or C:\Users\username\Documents\scans (Windows).

Next, you’ll see: Sort by (1) date or (2) name? Enter 1 for chronological order based on file modification timestamps, or 2 for natural filename sorting. This choice affects processing order and console output sequence, not the final PDF content.

As conversion proceeds, the console displays progress:

Converted: scan001.jpg -> scan001.pdf (Wed Mar 15 09:23:41 2026)
Converted: scan002.jpg -> scan002.pdf (Wed Mar 15 09:24:18 2026)
Converted: document.png -> document.pdf (Thu Mar 16 14:07:52 2026)

Timestamps help verify that chronological sorting worked as expected. Failed conversions display error messages without stopping the batch process.

Once complete, check the directory—you’ll find new PDF files alongside originals. The script doesn’t delete source images, allowing you to verify outputs before manual cleanup. For production workflows, you might modify the script to move originals to an “archive” subfolder after successful conversion.

Sorting Options: Date vs. Filename for Document Organization

The dual sorting strategy addresses fundamentally different organizational paradigms. Date-based sorting serves workflows where temporal sequence matters—think scanning paper documents in the order they were received, or processing daily report screenshots that need chronological arrangement for audit trails.

Filename sorting assumes your source images already encode logical order through naming conventions. Screenshots automatically named by tools (like Screenshot 2026-03-15 at 2.34.28 PM.png) benefit from natural sorting that respects embedded timestamps. Sequentially numbered scans (page001.jpg, page002.jpg, …, page123.jpg) require natural sorting to avoid the classic alphabetical trap where page10.jpg appears before page2.jpg.

The natural sorting implementation handles mixed alphanumeric names intelligently. A filename like report-2026-q1-draft3.png splits into components ['report-', 2026, '-q', 1, '-draft', 3, '.png'], where numeric portions compare as integers. This produces intuitive ordering even with complex naming schemes.

Users managing ongoing digitization projects might run the script multiple times on the same directory as new images arrive. The script’s idempotent behavior—it converts only image files, ignoring existing PDFs—prevents duplicate processing. Already-converted images simply generate new PDFs that overwrite previous versions, which is usually desired behavior when re-scanning improved copies.

For specialized needs like merging all images into a single multi-page PDF, consider the script a preprocessing step that standardizes inputs before using PDF manipulation tools like PyPDF2 or pdfunite. The individual-file architecture maintains flexibility while solving the most common use case without feature bloat.

Troubleshooting Common Image to PDF Conversion Issues

“No module named ‘PIL'” error: The Pillow library isn’t installed or isn’t accessible to the Python interpreter running the script. Verify installation with pip show Pillow and ensure you’re using the same Python version for both installation and execution. On systems with multiple Python installations, explicitly use python3 -m pip install Pillow and python3 convert_jpg_to_pdf.py.

Image converts but displays incorrectly in PDF viewers: This often indicates color space issues not fully resolved by RGB conversion. CMYK images from professional scanners may require additional handling. Consider adding explicit color profile conversion or using an ICC profile if working with print-industry images.

“Permission denied” errors: The script lacks write permissions in the target directory. On Unix-like systems, check directory permissions with ls -la and adjust with chmod. Windows users should verify folder isn’t marked read-only and that anti-virus software isn’t blocking Python file operations.

PDFs significantly larger than original images: The 100 DPI resolution and lack of compression optimization can increase file sizes for already-compressed formats like JPEG. Consider adding the optimize=True parameter to the save call: image.save(pdf_path, 'PDF', resolution=100.0, optimize=True). This enables Pillow’s built-in compression without changing the script’s core logic.

Script processes wrong files: Verify that your image files actually use listed extensions. Some systems hide extensions by default—enable “show file extensions” in your file manager. Case sensitivity on Linux/macOS means .JPG and .jpg are different; the script already handles this with .lower() comparison, but manual file operations might not.

Batch processing freezes on large image collections: Processing thousands of high-resolution images can exhaust memory. Add progress indicators or implement chunked processing that processes files in batches of 50-100, periodically clearing memory with explicit garbage collection: import gc followed by gc.collect() after each chunk.

Getting Started with Automated Image-to-PDF Workflows

This Python automation script demonstrates how targeted tools solve specific problems better than generic commercial software. By focusing exclusively on batch image-to-PDF conversion with sensible defaults, it eliminates the UI complexity and feature bloat that slow down repetitive tasks.

Start by identifying your most common image-to-PDF scenarios: Are you processing daily scans? Archiving project screenshots? Digitizing historical photos? Understanding your workflow patterns helps you decide between date and filename sorting and reveals whether you need to customize the output filename template or add subdirectory processing.

For recurring tasks, integrate the script into larger automation workflows. Place it in a cron job (Linux/macOS) or Task Scheduler (Windows) to automatically process a designated folder at specified intervals. Combine with file-watching tools like watchdog to trigger conversion whenever new images appear, creating a true automated document processing pipeline.

The script’s simplicity makes it an ideal foundation for customization. Add filename prefix/suffix options, implement multi-page PDF merging for related image sets, or integrate with cloud storage APIs to automatically upload converted PDFs. Each enhancement builds on the robust core architecture of directory traversal, format handling, and error resilience.

By learning to batch convert images to PDF with Python, you’ve gained more than a single utility—you’ve seen how thoughtful automation design prioritizes user needs over technical complexity, handles edge cases gracefully, and creates reproducible workflows that scale from personal projects to organizational processes.

Leave a Comment