Гид по технологиям

Оффлайн конвертер изображений в PDF на Python

7 min read Development Обновлено 14 Apr 2026
Оффлайн конвертер изображений в PDF на Python
Оффлайн конвертер изображений в PDF на Python

Открытый ноутбук на столе с яркой уличной сценой ночью

От бизнес-отчётов до фотопортфолио — часто нужно вставить изображения в PDF. Онлайн-сервисы удобны, но требуют загрузки файлов и могут вызывать вопросы безопасности и приватности. Оффлайн-конвертер позволяет оставаться в локальной сети и держать файлы под контролем.

В этом руководстве мы соберём простое приложение на Python, которое:

  • позволяет выбрать несколько изображений (JPG / PNG),
  • показывает превью выбранных файлов,
  • создаёт PDF, где каждая страница соответствует исходному изображению и сохраняет его размер.

В гайде сохранены исходные блоки кода, а также добавлены рекомендации по надёжности, отладке и альтернативные способы решения задачи.

Краткое описание используемых модулей

  • Tkinter — стандартная библиотека GUI в Python. Простая и достаточно универсальная для небольших утилит.
  • Pillow — форк PIL, библиотека для работы с изображениями: открытие, преобразования, ресайз.
  • ReportLab — библиотека для генерации PDF и графики в Python.

Установка (если требуются пакеты):

pip install tkinter
pip install Pillow
pip install reportlab

Важно: на многих системах Tkinter уже поставляется вместе с Python. Если pip-пакета нет, используйте менеджер пакетов ОС (например, apt, dnf, brew) чтобы установить компонент GUI.

Полный пример класса конвертера (объяснённый)

Ниже — исходный основной код из примера. Он создаёт класс ImageToPDFConverter с кнопками, превью и функциями выбора изображений, ресайза и конвертации в PDF. Код сохранён в оригинальном виде для воспроизводимости:

import tkinter as tk  
from tkinter import filedialog, messagebox  
from PIL import Image, ImageTk  
from reportlab.lib.pagesizes import landscape  
from reportlab.pdfgen import canvas  
  
class ImageToPDFConverter:  
    def __init__(self, root):  
        self.root = root  
        self.image_paths = []  
        self.root.title("Image to PDF Converter")  
        self.root.geometry("750x600")  
        self.select_images_button = tk.Button(self.root, text="Select Images", command=self.select_images, font=("Helvetica", 12),)  
        self.select_images_button.pack(pady=10)  
        self.convert_to_pdf_button = tk.Button(self.root, text="Convert to PDF", command=self.convert_to_pdf,font=("Helvetica", 12),)  
        self.convert_to_pdf_button.pack(pady=10)

Дальше создаётся метка и фрейм для превью:

        self.select_images_label = tk.Label(self.root, text="Select Images", font=("Helvetica", 14))  
        self.select_images_label.pack(pady=10)  
        self.preview_frame = tk.Frame(self.root, width=380, height=200)  
        self.preview_frame.pack(pady=10)

Метод выбора файлов и отображения превью (резайз для превью):

    def select_images(self):  
        self.image_paths = filedialog.askopenfilenames(initialdir="/", title="Select Images", filetypes=(("Image Files", "*.jpg *.png"),))  
  
        for i, image_path in enumerate(self.image_paths):  
            image = Image.open(image_path)  
            image = self.resize_image(image, width=150, height=150)  
            photo = ImageTk.PhotoImage(image)  
            label = tk.Label(self.preview_frame, image=photo)  
            label.image = photo  
            label.grid(row=i // 3, column=i % 3, padx=10, pady=10)

Функция ресайза с сохранением пропорций и би-линейной интерполяцией:

    def resize_image(self, image, width, height):  
        aspect_ratio = min(width / float(image.size[0]), height / float(image.size[1]))  
        new_width = int(aspect_ratio * image.size[0])  
        new_height = int(aspect_ratio * image.size[1])  
        resized_image = image.resize((new_width, new_height), resample=Image.Resampling.BILINEAR)  
        return resized_image

Функция записи PDF с установкой размера страницы под размер изображения:

    def convert_to_pdf(self):  
        pdf_path = filedialog.asksaveasfilename(defaultextension=".pdf", filetypes=(("PDF Files", "*.pdf"),))  
        c = canvas.Canvas(pdf_path, pagesize=landscape)  
        for image_path in self.image_paths:  
            image = Image.open(image_path)  
            width, height = image.size  
            c.setPageSize((width, height))  
            c.drawImage(image_path, 0, 0, width=width, height=height)  
            c.showPage()  
        c.save()  
        messagebox.showinfo("Conversion Successful", f"PDF saved at {pdf_path}")

И стандартный блок запуска:

if __name__ == "__main__":  
    root = tk.Tk()  
    app = ImageToPDFConverter(root)  
    root.mainloop()

Как это работает — шаг за шагом

  1. Вызывается filedialog.askopenfilenames — пользователь выбирает несколько файлов.
  2. Для каждого файла создаётся превью: изображение открывается через Pillow, уменьшается до миниатюры и показывается в сетке.
  3. При конвертации каждый файл снова открывается, берутся его пиксельные размеры и устанавливаются как размеры страницы PDF. Затем изображение рисуется в левом верхнем углу (координаты 0,0) с заданными размерами.
  4. Для каждого изображения вызывается showPage(), чтобы создать новую страницу.
  5. Файл сохраняется и пользователь видит сообщение об успешном завершении.

Полезные пояснения и советы

Important: Размер страницы PDF в ReportLab задаётся в пунктах (points). В коде мы передаём пиксели как числа. В большинстве практических случаев это работает, но при печати может потребоваться конвертация в точки/мм/дюймы в зависимости от DPI. Для экранных PDF это обычно не критично.

  • DPI и единицы: PDF использует points (1 point = 1/72 inch). Если нужно точное соответствие размерам в миллиметрах или дюймах, пересчитайте пиксели через DPI: points = (pixels / DPI) * 72.
  • Цветовые профили: ReportLab хитро работает с цветами. Если у вас CMYK-изображения, убедитесь, что Pillow возвращает ожидаемый режим (обычно RGB). Для профессиональной печати возможна необходимость в конвертации цветового профиля.

Ошибки и когда это не сработает

  • Очень большие изображения (несколько десятков мегапикселей) потребляют много RAM. При выборе большого количества таких файлов программа может вылететь по памяти.
  • Форматы с прозрачностью (PNG с альфа) при простом вставлении в PDF могут отрисовываться на чёрном фоне. Решение: перед вставкой преобразовать изображение в RGB и при необходимости добавить белый фон.
  • Некорректные пути/права записи: если пользователь выбирает место, куда нет прав записи, сохранение упадёт.

Улучшения и альтернативные подходы

  1. Командная строка с img2pdf (быстро и без GUI):
# установить img2pdf
pip install img2pdf

# пример использования в Python
import img2pdf
with open("out.pdf","wb") as f:
    f.write(img2pdf.convert(["a.jpg","b.png"]))

img2pdf встраивает изображения без перекодирования и обычно экономнее по памяти.

  1. Использовать PIL.Image.save с опцией save_all для многостраничного PDF (работает для последовательности изображений):
from PIL import Image
images = [Image.open(p).convert('RGB') for p in paths]
images[0].save('out.pdf', save_all=True, append_images=images[1:])

Этот метод проще и часто эффективнее для большинства задач, особенно когда не нужна точная верстка.

  1. PyPDF2 / pikepdf для пост-обработки PDF: если нужно объединить, вставить страницы, добавить метаданные.

  2. Для высоконагруженных задач: использовать очереди задач (Celery/RQ) и лимитировать количество одновременно обрабатываемых изображений.

Паттерны принятия решений (ментальные модели)

  • “Keep it simple” — если вам нужно просто собрать фотоальбом или распечатать снимки, PIL.Image.save обычно достаточно.
  • “Control vs Convenience” — ReportLab даёт контроль над страницами и вёрсткой. img2pdf — максимально прост и эффективен по памяти.
  • “Оффлайн приватность” — если файлы нельзя отправлять на серверы третьих сторон, используйте локальный инструмент с минимальным набором зависимостей.

Чек-листы по ролям

Разработчик:

  • Тестировать на изображениях разных размеров и режимов (RGBA, RGB, CMYK).
  • Добавить обработку ошибок чтения/записи.
  • Ограничить максимальный размер изображения и количество файлов.

Обычный пользователь:

  • Выбирать файлы из локальных папок.
  • Проверить место для сохранения и права записи.
  • Если видите предупреждение про память — закрыть другие приложения.

Системный администратор:

  • Развернуть приложение в виртуальной среде (venv).
  • Установить мониторинг использования памяти и логирование ошибок.

Критерии приёмки

  • Приложение открывается и показывает кнопки «Select Images» и «Convert to PDF».
  • Можно выбрать несколько изображений, и их превью отображаются.
  • PDF сохраняется по указанному пути, и у каждой страницы размер соответствует исходному изображению (проверка в свойствах PDF — размеры страниц совпадают по пикселам).
  • Приложение не падает при выборе 10 изображений 1920×1080 (проверка в тестовой среде).

Производительность и надёжность

  • Память: при чтении каждого изображения лучше закрывать объекты Image после использования. При массовой обработке читайте и конвертируйте по одному файлу, не храните все большие изображения в памяти.
  • Параллельность: GUI-операции выполняйте в основном потоке, тяжёлые операции (конвертация) выносите в фон (threading / multiprocessing) и показывайте индикатор прогресса.
  • Логи: записывайте ошибки с трассировкой в файл журнала для отладки.

Примеры улучшений интерфейса

  • Добавить кнопки «Удалить выбранное», «Очистить превью», «Переместить вверх/вниз» для управления порядком изображений.
  • Поддержка drag&drop для удобства.
  • Предварительный выбор ориентации страницы (портрет/альбом) или автоориентация по размеру изображения.

Совместимость и переносимость

  • Windows, macOS, Linux: Tkinter присутствует не везде по умолчанию. Проверьте системные зависимости. Для задач в Linux используйте apt/ yum/ dnf для установки пакета python3-tk.
  • Версии Python: код совместим с Python 3.8+. В старых версиях Pillow/ReportLab могут отличаться API.

Пример альтернативной команды для пакетной обработки (CLI)

# Простой bash-скрипт для всех jpg в папке
python - <<'PY'
import img2pdf, glob
paths = sorted(glob.glob('*.jpg'))
with open('album.pdf','wb') as f:
    f.write(img2pdf.convert(paths))
PY

Факты и рекомендации

  • Для экранного просмотра конвертация пикселей в точки обычно опускается. Для печати учитывайте DPI.
  • Для больших объёмов предпочитайте img2pdf или потоковую обработку.
  • Для прозрачности PNG перед вставкой в PDF конвертируйте в RGB и добавляйте фон при необходимости.

Шаблон поведенческого инцидента (если конвертация не удалась)

  1. Проверить лог-приложения на ошибки чтения/записи.
  2. Убедиться, что файлы доступны и не повреждены.
  3. Проверить свободное место на диске.
  4. Попробовать конвертировать один файл вручную через PIL или img2pdf, чтобы локализовать проблему.
  5. Если падает на больших файлах — уменьшить количество одновременно обрабатываемых изображений.

Часто задаваемые вопросы

Можно ли обрабатывать PNG с прозрачностью?

Да. Но перед записью в PDF часто полезно конвертировать изображения в RGB и заполнить прозрачные области белым или любым другим фоном:

img = Image.open(path)
if img.mode in ('RGBA', 'LA'):
    background = Image.new('RGB', img.size, (255,255,255))
    background.paste(img, mask=img.split()[-1])
    img = background

Как уменьшить размер итогового PDF?

  • Понизить качество/разрешение изображений перед вставкой.
  • Использовать сжатие JPEG при преобразовании (сохранение в JPEG с нужным quality).
  • Использовать инструменты оптимизации PDF после создания (например, ghostscript).

Что лучше для массовой обработки — ReportLab или img2pdf?

Для массовой пакетной обработки img2pdf обычно эффективнее и экономнее по памяти. ReportLab лучше, если требуется сложная верстка и добавление графики/текста.

Начальный экран конвертера изображений в PDF

Окно выбора изображений в конвертере

Превью выбранных изображений перед конвертацией

Сохранённый PDF открыт в браузере

Короткая инструкция по развёртыванию (SOP)

  1. Создайте виртуальное окружение: python -m venv venv && source venv/bin/activate
  2. Установите зависимости: pip install Pillow reportlab tkinter (если необходимо)
  3. Скопируйте скрипт и запустите: python app.py
  4. Тестируйте на контролируемом наборе изображений.

Краткое резюме

  • Для простых задач используйте PIL.Image.save или img2pdf.
  • Для тонкого контроля верстки и добавления меток/водяных знаков — ReportLab.
  • Всегда учитывайте память и цветовые режимы.
  • Оффлайн-приложение защищает приватность и подходит для конфиденциальных файлов.

FAQ (коротко)

  • Поддерживаются ли TIFF и другие форматы? Зависит от Pillow. TIFF обычно поддерживается, но для многостраничных TIFF потребуется дополнительная логика.
  • Можно ли менять порядок страниц? Да — реализуйте перемещение элементов в image_paths до конвертации.
  • Как добавить водяной знак? Используйте ReportLab для рисования текста или изображения поверх каждой страницы.
Поделиться: X/Twitter Facebook LinkedIn Telegram
Автор
Редакция

Похожие материалы

Соберите Bluetooth‑динамик своими руками
Сделай сам

Соберите Bluetooth‑динамик своими руками

Как запланировать встречу в Microsoft Teams
Инструменты

Как запланировать встречу в Microsoft Teams

Как освободить место на Android с малой памятью
Android.

Как освободить место на Android с малой памятью

Forest — приложение для фокуса и борьбы с телефоном
Продуктивность

Forest — приложение для фокуса и борьбы с телефоном

Android для пожилых: простая настройка телефона
Мобильные устройства

Android для пожилых: простая настройка телефона

Режим энергосбережения Chrome — включение и настройка
Браузеры

Режим энергосбережения Chrome — включение и настройка