PyQT5とPyPDF2を用いたPDF画像化の実装
この記事では、PythonのGUIライブラリであるPyQT5とPDF処理ライブラリのPyPDF2を組み合わせ、PDFファイルの全ページをJPEG画像形式に一括変換するアプリケーションの構築手法を解説します。ドキュメント内の画像抽出や内容の可視化を目的とした実用的なツール開発に焦点を当てています。
PDFレンダリングと画像出力の基本フロー
処理の核心となるのは、PDFのページデータを画像として読み込み、GUI上に表示後にファイル保存する一連の流れです。主な工程は以下の通りです。
- PyPDF2を用いたPDFファイルの読み込みとページ分割
- PyQT5のQGraphicsViewおよびQGraphicsSceneによるページの可視化
- QPixmapを経由したQImageオブジェクトへの変換
- 指定品質でのJPEGフォーマットへのエンコードと保存
サンプルコード: PDF読み込みと画像変換
import sys
from PyPDF2 import PdfReader
from PyQt5.QtWidgets import QApplication, QGraphicsScene, QGraphicsView
from PyQt5.QtGui import QPixmap, QImage
class PDFConverter:
def __init__(self):
self.app = QApplication(sys.argv)
self.scene = QGraphicsScene()
self.view = QGraphicsView(self.scene)
def load_and_render(self, pdf_path):
pdf_document = PdfReader(open(pdf_path, 'rb'))
total_pages = len(pdf_document.pages)
for idx in range(total_pages):
page_obj = pdf_document.pages[idx]
# 仮想のレンダリング: 実際には画像ライブラリの組み合わせが必要
# ここではプレースホルダー
page_image = self.render_page_to_image(page_obj)
self.add_to_scene(page_image)
def render_page_to_image(self, page):
# 例: ページを画像としてレンダリングするロジック
# 実際の実装では外部ライブラリ(e.g., pdf2image)の使用を検討
from PIL import Image
import io
# 仮の画像生成
img = Image.new('RGB', (800, 1100), color='white')
return img
def add_to_scene(self, pil_image):
# PIL ImageをQPixmapに変換
byte_arr = io.BytesIO()
pil_image.save(byte_arr, format='PNG')
pixmap = QPixmap()
pixmap.loadFromData(byte_arr.getvalue())
self.scene.addPixmap(pixmap)
def save_as_jpeg(self, output_path, quality=85):
# シーン全体を画像として保存
view_rect = self.view.viewport().rect()
scene_image = QImage(view_rect.size(), QImage.Format_RGB32)
painter = QPainter(scene_image)
self.view.render(painter)
painter.end()
scene_image.save(output_path, 'JPEG', quality)
if __name__ == '__main__':
converter = PDFConverter()
converter.load_and_render('sample.pdf')
converter.view.show()
converter.save_as_jpeg('output.jpg')
sys.exit(converter.app.exec_())
GUIアプリケーションの構築例
ユーザーがファイルを選択し、変換設定を調整できる基本的なインターフェースの実装例を示します。
from PyQt5.QtWidgets import (QMainWindow, QPushButton, QFileDialog,
QLabel, QVBoxLayout, QWidget, QSpinBox)
from PyQt5.QtCore import Qt
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.init_ui()
self.pdf_path = ''
def init_ui(self):
self.setWindowTitle('PDF to JPG Converter')
self.resize(400,백)
layout = QVBoxLayout()
self.file_label = QLabel('ファイルが選択されていません')
layout.addWidget(self.file_label)
select_btn = QPushButton('PDFを選択')
select_btn.clicked.connect(self.select_file)
layout.addWidget(select_btn)
self.quality_spin = QSpinBox()
self.quality_spin.setRange(1, 100)
self.quality_spin.setValue(90)
self.quality_spin.setPrefix('品質: ')
self.quality_spin.setSuffix('%')
layout.addWidget(self.quality_spin)
convert_btn = QPushButton('変換開始')
convert_btn.clicked.connect(self.start_conversion)
layout.addWidget(convert_btn)
container = QWidget()
container.setLayout(layout)
self.setCentralWidget(container)
def select_file(self):
file_name, _ = QFileDialog.getOpenFileName(
self, 'PDFファイルを選択', '', 'PDF Files (*.pdf)')
if file_name:
self.pdf_path = file_name
self.file_label.setText(f'選択済: {file_name}')
def start_conversion(self):
if not self.pdf_path:
return
# ここで変換ロジックを呼び出す
quality = self.quality_spin.value()
print(f'変換開始: {self.pdf_path}, 品質: {quality}')
app = QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec_())
画像処理におけるQPixmapとQImageの使い分け
表示に特化したQPixmapと、画像処理操作に適したQImageの違いを理解することが重要です。変換処理では以下のように使い分けます。
- QPixmap: GUIウィジェット上への画像表示に最適化されており、ハードウェアアクセラレーションを活用できます。
- QImage: ピクセルデータへの直接アクセスが可能で、形式変換やフィルタ処理などの画像処理に適しています。
保存前には、通常QPixmapをQImageに変換して処理を行います。
def convert_and_save(pixmap, filename, quality):
# QPixmapからQImageへ変換
temp_image = pixmap.toImage()
# 必要に応じてフォーマット変換
if temp_image.format() != QImage.Format_RGB32:
temp_image = temp_image.convertToFormat(QImage.Format_RGB32)
# JPEGで保存
success = temp_image.save(filename, 'JPEG', quality)
return success
バッチ処理の実装と考慮点
複数のPDFファイルを連続処理する場合は、進捗状況のフィードバックとエラー処理が不可欠です。スレッドを使用してGUIの応答性を維持する例を示します。
from PyQt5.QtCore import QThread, pyqtSignal
class ConversionWorker(QThread):
progress = pyqtSignal(int)
finished = pyqtSignal(list)
def __init__(self, file_list, output_dir, quality):
super().__init__()
self.files = file_list
self.output = output_dir
self.quality_setting = quality
def run(self):
results = []
total = len(self.files)
for idx, pdf_file in enumerate(self.files):
try:
# 変換処理の実行
success = self.convert_single(pdf_file)
results.append((pdf_file, success))
except Exception as e:
results.append((pdf_file, str(e)))
# 進捗を通知
self.progress.emit(int((idx + 1) / total * 100))
self.finished.emit(results)
def convert_single(self, pdf_path):
# 個別ファイルの変換ロジック
# ...
return True
このアプローチにより、大量のファイルを変換中でもユーザーインターフェースがフリーズすることを防ぎ、処理状況を可視化できます。