吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 2201|回复: 19
收起左侧

[Python 原创] Python写的PDF批量转PNG小工具

[复制链接]
昔年科技 发表于 2025-5-30 13:48
本帖最后由 昔年科技 于 2025-6-2 17:24 编辑

起源今天需要用一下某办公软件的PDF转图片功能,居然有水印,无水印还要开XX。哎,打工人,真不容易,于是便想用Python写一个小工具。这个PDF批量转PNG工具是一款高效、易用的桌面应用程序,专为需要将PDF文档转换为高质量PNG图像的用户设计。基于Python开发,采用PyQt5构建直观的图形界面,支持批量处理多个PDF文件,满足办公、设计、出版等领域的文档转换需求。
微信截图_20250530132733.png 微信截图_20250530132743.png 微信截图_20250530132840.png
核心功能1. 批量转换能力
  • 多文件处理:可一次性选择并转换整个文件夹内的所有PDF文件
  • 自动命名:保留原文件名,自动添加页码后缀(如"文档1_page1.png")
2. 灵活输出选项
  • 分页模式:将PDF每页转换为单独的PNG文件
  • 合并模式:将PDF所有页面垂直拼接为一张长图输出
  • DPI调节:支持100-1200DPI设置(推荐300-600DPI保证清晰度)
3. 专业级转换质量
  • 基于PyMuPDF(fitz)引擎,保持原始文档的:
    • 文字清晰度
    • 矢量图形精度
    • 色彩准确度
技术特点高效架构
  • 多线程处理:转换过程不阻塞界面操作
  • 进度可视化:实时显示文件处理进度和当前页码
  • 错误处理:自动捕获并提示转换异常
使用场景
  • 文档归档:将合同/报告转换为可搜索的图像存档
  • 网页设计:提取PDF中的设计素材为透明背景PNG
  • 电子出版:准备适用于电子书的图像内容
  • 学术研究:转换论文中的图表为可编辑格式
使用方法
  • 选择PDF文件或文件夹
  • 设置输出目录
  • 选择转换模式(分页/合并)
  • 调整DPI质量参数
  • 点击"开始转换"按钮
性能参数

[td]
项目
规格
单页转换速度约0.5-2秒/页(取决于DPI设置)
输出质量24位真彩色PNG,支持透明背景
最大文件支持理论无限制(实测处理过500+页文档)
输出尺寸精度精确到1/72英寸(PDF标准单位)
大概就这样了 ,

方便大家使用 已打包成品    蓝奏:https://wwov.lanzoum.com/ihOe12xjripa  密码:52pjie

附上源码,欢迎大神指导
[Python] 纯文本查看 复制代码
import os
import sys
import fitz  # PyMuPDF
from PIL import Image
from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout,
                             QHBoxLayout, QLabel, QLineEdit, QPushButton,
                             QFileDialog, QRadioButton, QButtonGroup,
                             QSpinBox, QProgressBar, QMessageBox, QGroupBox)
from PyQt5.QtCore import Qt, QThread, pyqtSignal


class PdfConverterThread(QThread):
    """PDF转换线程(支持批量处理)"""
    progress_updated = pyqtSignal(int, str)
    conversion_finished = pyqtSignal(bool, str)
    file_progress_updated = pyqtSignal(int, int)  # 当前文件进度,总文件数

    def __init__(self, input_path, output_folder, mode, dpi, is_folder=False):
        super().__init__()
        self.input_path = input_path
        self.output_folder = output_folder
        self.mode = mode
        self.dpi = dpi
        self.is_folder = is_folder
        self._is_running = True

    def run(self):
        try:
            if self.is_folder:
                # 处理文件夹中的所有PDF文件
                pdf_files = [f for f in os.listdir(self.input_path)
                             if f.lower().endswith('.pdf')]
                total_files = len(pdf_files)

                for file_idx, pdf_file in enumerate(pdf_files):
                    if not self._is_running:
                        break

                    pdf_path = os.path.join(self.input_path, pdf_file)
                    # 为每个PDF创建单独的输出子文件夹
                    output_subfolder = os.path.join(
                        self.output_folder,
                        os.path.splitext(pdf_file)[0]
                    )
                    os.makedirs(output_subfolder, exist_ok=True)

                    self.file_progress_updated.emit(file_idx + 1, total_files)
                    self.process_pdf(pdf_path, output_subfolder)

                if self._is_running:
                    self.conversion_finished.emit(
                        True,
                        f"批量转换完成!共处理 {total_files} 个PDF文件"
                    )
            else:
                # 处理单个PDF文件
                os.makedirs(self.output_folder, exist_ok=True)
                self.process_pdf(self.input_path, self.output_folder)
                if self._is_running:
                    self.conversion_finished.emit(
                        True,
                        f"转换完成!文件保存在: {self.output_folder}"
                    )

        except Exception as e:
            self.conversion_finished.emit(False, f"转换失败: {str(e)}")

    def process_pdf(self, pdf_path, output_folder):
        """处理单个PDF文件"""
        pdf_document = fitz.open(pdf_path)
        total_pages = len(pdf_document)
        base_name = os.path.splitext(os.path.basename(pdf_path))[0]

        # 计算缩放因子,基于DPI
        zoom = self.dpi / 72
        mat = fitz.Matrix(zoom, zoom)

        if self.mode == 'separate':
            # 分页输出模式
            for page_num in range(total_pages):
                if not self._is_running:
                    break

                page = pdf_document.load_page(page_num)
                pix = page.get_pixmap(matrix=mat)

                output_path = os.path.join(
                    output_folder,
                    f"{base_name}_page_{page_num + 1}.png"
                )
                pix.save(output_path)

                progress = int((page_num + 1) / total_pages * 100)
                self.progress_updated.emit(
                    progress,
                    f"正在转换 {base_name} 第 {page_num + 1}/{total_pages} 页..."
                )

        elif self.mode == 'merge':
            # 合并输出模式
            images = []

            for page_num in range(total_pages):
                if not self._is_running:
                    break

                page = pdf_document.load_page(page_num)
                pix = page.get_pixmap(matrix=mat)

                img = Image.frombytes("RGB", [pix.width, pix.height], pix.samples)
                images.append(img)

                progress = int((page_num + 1) / total_pages * 100)
                self.progress_updated.emit(
                    progress,
                    f"正在处理 {base_name} 第 {page_num + 1}/{total_pages} 页..."
                )

            if self._is_running and images:
                # 计算总高度和最大宽度
                total_height = sum(img.height for img in images)
                max_width = max(img.width for img in images)

                # 创建新图像
                merged_image = Image.new('RGB', (max_width, total_height))

                # 拼接图像
                y_offset = 0
                for img in images:
                    merged_image.paste(img, (0, y_offset))
                    y_offset += img.height

                output_path = os.path.join(output_folder, f"{base_name}_merged.png")
                merged_image.save(output_path)

        pdf_document.close()

    def stop(self):
        self._is_running = False


class PdfToPngConverter(QMainWindow):
    """PDF转PNG转换器主窗口(支持文件夹批量处理)"""

    def __init__(self):
        super().__init__()
        self.setWindowTitle("PDF转PNG转换器 (批量版)")
        self.setGeometry(100, 100, 600, 450)

        # 初始化UI
        self.init_ui()

        # 转换线程
        self.converter_thread = None

    def init_ui(self):
        """初始化用户界面"""
        main_widget = QWidget()
        self.setCentralWidget(main_widget)

        layout = QVBoxLayout()
        main_widget.setLayout(layout)

        # 输入选择
        input_group = QGroupBox("输入选择")
        input_layout = QVBoxLayout()

        # 选择模式
        mode_btn_group = QButtonGroup(self)

        single_file_btn = QRadioButton("单个PDF文件")
        single_file_btn.setChecked(True)
        mode_btn_group.addButton(single_file_btn)

        folder_btn = QRadioButton("PDF文件夹(批量处理)")
        mode_btn_group.addButton(folder_btn)

        input_layout.addWidget(single_file_btn)
        input_layout.addWidget(folder_btn)

        # 文件/文件夹选择
        file_layout = QHBoxLayout()
        file_layout.addWidget(QLabel("路径:"))

        self.path_edit = QLineEdit()
        self.path_edit.setPlaceholderText("请选择PDF文件或文件夹")
        file_layout.addWidget(self.path_edit)

        browse_btn = QPushButton("浏览...")
        browse_btn.clicked.connect(self.browse_path)
        file_layout.addWidget(browse_btn)

        input_layout.addLayout(file_layout)
        input_group.setLayout(input_layout)
        layout.addWidget(input_group)

        # 输出设置
        output_group = QGroupBox("输出设置")
        output_layout = QVBoxLayout()

        # 输出文件夹
        out_folder_layout = QHBoxLayout()
        out_folder_layout.addWidget(QLabel("输出文件夹:"))

        self.output_edit = QLineEdit()
        self.output_edit.setPlaceholderText("请选择输出文件夹")
        out_folder_layout.addWidget(self.output_edit)

        out_browse_btn = QPushButton("浏览...")
        out_browse_btn.clicked.connect(self.browse_output)
        out_folder_layout.addWidget(out_browse_btn)

        output_layout.addLayout(out_folder_layout)

        # 输出模式
        out_mode_layout = QHBoxLayout()
        out_mode_layout.addWidget(QLabel("输出模式:"))

        self.separate_radio = QRadioButton("分页输出")
        self.separate_radio.setChecked(True)
        out_mode_layout.addWidget(self.separate_radio)

        self.merge_radio = QRadioButton("合并输出")
        out_mode_layout.addWidget(self.merge_radio)

        output_layout.addLayout(out_mode_layout)

        # DPI设置
        dpi_layout = QHBoxLayout()
        dpi_layout.addWidget(QLabel("DPI 数值越高,图片越清晰[推荐 300-600]:"))

        self.dpi_spin = QSpinBox()
        self.dpi_spin.setRange(100, 1200)
        self.dpi_spin.setValue(300)
        dpi_layout.addWidget(self.dpi_spin)

        output_layout.addLayout(dpi_layout)
        output_group.setLayout(output_layout)
        layout.addWidget(output_group)

        # 进度显示
        self.file_progress = QLabel("")
        self.file_progress.setAlignment(Qt.AlignCenter)
        layout.addWidget(self.file_progress)

        self.progress_bar = QProgressBar()
        layout.addWidget(self.progress_bar)

        self.status_label = QLabel("准备就绪")
        self.status_label.setAlignment(Qt.AlignCenter)
        layout.addWidget(self.status_label)

        # 操作按钮
        btn_layout = QHBoxLayout()

        self.convert_btn = QPushButton("开始转换")
        self.convert_btn.clicked.connect(self.start_conversion)
        btn_layout.addWidget(self.convert_btn)

        self.cancel_btn = QPushButton("取消")
        self.cancel_btn.clicked.connect(self.cancel_conversion)
        self.cancel_btn.setEnabled(False)
        btn_layout.addWidget(self.cancel_btn)

        layout.addLayout(btn_layout)

    def browse_path(self):
        """浏览文件或文件夹"""
        if self.sender().parent().findChildren(QRadioButton)[1].isChecked():
            # 选择文件夹
            folder = QFileDialog.getExistingDirectory(self, "选择PDF文件夹")
            if folder:
                self.path_edit.setText(folder)
                # 自动设置输出文件夹
                self.output_edit.setText(os.path.join(folder, "PNG_Output"))
        else:
            # 选择文件
            file, _ = QFileDialog.getOpenFileName(
                self, "选择PDF文件", "", "PDF文件 (*.pdf)"
            )
            if file:
                self.path_edit.setText(file)
                # 自动设置输出文件夹
                folder = os.path.dirname(file)
                name = os.path.splitext(os.path.basename(file))[0]
                self.output_edit.setText(os.path.join(folder, f"{name}_PNG"))

    def browse_output(self):
        """浏览输出文件夹"""
        folder = QFileDialog.getExistingDirectory(self, "选择输出文件夹")
        if folder:
            self.output_edit.setText(folder)

    def start_conversion(self):
        """开始转换"""
        input_path = self.path_edit.text().strip()
        output_folder = self.output_edit.text().strip()
        is_folder = self.sender().parent().findChildren(QRadioButton)[1].isChecked()

        # 验证输入
        if not input_path:
            QMessageBox.warning(self, "警告", "请选择PDF文件/文件夹")
            return

        if is_folder:
            if not os.path.isdir(input_path):
                QMessageBox.warning(self, "警告", "文件夹不存在")
                return
        else:
            if not os.path.isfile(input_path):
                QMessageBox.warning(self, "警告", "PDF文件不存在")
                return

        if not output_folder:
            QMessageBox.warning(self, "警告", "请选择输出文件夹")
            return

        # 获取转换选项
        mode = 'separate' if self.separate_radio.isChecked() else 'merge'
        dpi = self.dpi_spin.value()

        # 更新UI状态
        self.convert_btn.setEnabled(False)
        self.cancel_btn.setEnabled(True)
        self.progress_bar.setValue(0)
        self.status_label.setText("准备开始转换...")
        self.file_progress.setText("")

        # 创建并启动转换线程
        self.converter_thread = PdfConverterThread(
            input_path, output_folder, mode, dpi, is_folder
        )
        self.converter_thread.progress_updated.connect(self.update_progress)
        self.converter_thread.conversion_finished.connect(self.conversion_finished)
        self.converter_thread.file_progress_updated.connect(self.update_file_progress)
        self.converter_thread.start()

    def update_progress(self, progress, message):
        """更新进度"""
        self.progress_bar.setValue(progress)
        self.status_label.setText(message)

    def update_file_progress(self, current, total):
        """更新文件处理进度"""
        self.file_progress.setText(f"正在处理文件: {current}/{total}")

    def conversion_finished(self, success, message):
        """转换完成"""
        if success:
            QMessageBox.information(self, "输出完成", message)
        else:
            QMessageBox.critical(self, "输出错误", message)

        self.reset_ui()

    def cancel_conversion(self):
        """取消转换"""
        if self.converter_thread and self.converter_thread.isRunning():
            self.converter_thread.stop()
            self.converter_thread.wait()

        self.status_label.setText("转换已取消")
        self.reset_ui()

    def reset_ui(self):
        """重置UI状态"""
        self.convert_btn.setEnabled(True)
        self.cancel_btn.setEnabled(False)
        self.progress_bar.setValue(0)
        self.file_progress.setText("")

    def closeEvent(self, event):
        """窗口关闭事件"""
        if self.converter_thread and self.converter_thread.isRunning():
            self.converter_thread.stop()
            self.converter_thread.wait()
        event.accept()


if __name__ == "__main__":
    app = QApplication(sys.argv)
    converter = PdfToPngConverter()
    converter.show()
    sys.exit(app.exec_())

这款工具特别适合需要定期处理大量PDF转图像任务的用户,相比在线转换工具,它提供了更好的隐私保护(所有处理在本地完成)和更稳定的批量处理能力。

Code-PDF转PNG.zip

3.63 KB, 下载次数: 47, 下载积分: 吾爱币 -1 CB

仅包含源码

免费评分

参与人数 1吾爱币 +7 热心值 +1 收起 理由
苏紫方璇 + 7 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!

查看全部评分

本帖被以下淘专辑推荐:

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

jjkkii 发表于 2025-5-30 16:28
我是直接 WPS  转的
flyer_2001 发表于 2025-5-30 19:50
anonyman 发表于 2025-5-30 14:09
win7不能启动
Failed to load Python DLL 'C:\ Users\ Administrator\ AppData\ Local\ Temp\ ME490042\  ...

python3.13不支持win7,3.8以内用源码自己编译一个版本好了
gao887 发表于 2025-5-30 14:05
头像被屏蔽
anonyman 发表于 2025-5-30 14:06
提示: 作者被禁止或删除 内容自动屏蔽
cioceo 发表于 2025-5-30 14:08
感谢,我都是用Acrobat DC直接转的
usogmai 发表于 2025-5-30 14:09
有用过的朋友说下体验怎么样么
头像被屏蔽
anonyman 发表于 2025-5-30 14:09
提示: 作者被禁止或删除 内容自动屏蔽
ttgjyie 发表于 2025-5-30 14:14
感谢楼主分享
Tick12333 发表于 2025-5-30 14:18
感谢楼主分享。
sqzh 发表于 2025-5-30 16:12
新东西要多试一试
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - 52pojie.cn ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2026-4-17 19:05

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表