乐于分享
好东西不私藏

《PDF转图片还在手动一页页另存?Python调用Poppler引擎一键批量导出,从此告别机械重复!》

《PDF转图片还在手动一页页另存?Python调用Poppler引擎一键批量导出,从此告别机械重复!》

“带你横跨办公自动化的数据江海”

@摸鱼

前言

关于摸鱼

ABOUT MOYU

闻道有先后,术业有专攻。

各位大佬朋友们好!

~我依旧是你们的老朋友摸鱼~

在职场摸爬滚打的这十多年里,我用Python悄悄干了不少“正事”——不知不觉攒下了一整套办公自动化的实用项目技巧。去年10月初创立了公众号 「码海听潮」 ,初衷很简单:把重复的劳动交给代码,把摸鱼的时间留给生活。

目前已经吭哧吭哧更新了120多篇原创文章,每一篇都是实操干货,不讲虚的,只聊怎么用代码真正解放双手,帮大家早点下班、准点摸鱼

好了,多了不说,少了不唠,今天给大佬们分享一款本人原创的【PDF文档批量转图片,这款小工具专为工程设计人员和文档管理人员设计,支持PDF文件夹递归导入、分页/合并双模式输出、PNG/JPG/BMP/TIFF多格式转换,可自定义DPI分辨率高质量渲染。

《该办公工具成品展示:》

《该办公工具核心功能亮点:》

01

 亮点1: Poppler引擎集成🎯

        • 支持自定义Poppler路径,确保PDF解析精度

        02

         亮点2:多格式支持📱

            • PNG、JPG、BMP、TIFF 四种主流图片格式自由选择

            03

            亮点3:灵活输入模式

            • 单个PDF文件转换:选择单一文件快速转换

            • 文件夹递归批量转换:自动遍历所有子文件夹,保持原始目录结构

            《该办公工具代码展示:》

            下面,我就用python代码让各位大佬见识一下,什么叫"传统文化"遇上"赛博效率"

            import sysfrom PyQt6.QtWidgets import (    QApplicationQMainWindowQWidgetQVBoxLayoutQHBoxLayout,    QLabelQPushButtonQFileDialogQTextEditQMessageBox,    QGroupBoxQRadioButtonQButtonGroupQLineEditQComboBox,    QSpinBoxQCheckBox)from PyQt6.QtCore import Qtclass PdfToPngConverter(QMainWindow):    """PDF转图像转换器主窗口(支持文件夹批量处理)"""    def __init__(self):        super().__init__()        self.setWindowTitle("PDF→图像转换器 (欢迎关注微信公众号:码海听潮)")        self.setGeometry(100100850750)        self.setMinimumSize(800700)        # 支持的输出格式        self.supported_formats = ['png', 'jpg', 'bmp', 'tiff']        # 应用样式        self.apply_stylesheet()        # 初始化UI        self.init_ui()    def apply_stylesheet(self):        """应用现代化样式表"""        style = """        /* 主窗口 */        QMainWindow {            background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,                stop: 0 #1a1a2e, stop: 1 #16213e);        }        QWidget {            color: #e0e0e0;            font-family: "Microsoft YaHei", "PingFang SC", sans-serif;        }        /* 分组框 */        QGroupBox {            font-weight: bold;            border: 2px solid #2a3a5e;            border-radius: 12px;            margin-top: 12px;            padding-top: 12px;            background: rgba(30, 40, 60, 0.5);        }        QGroupBox::title {            subcontrol-origin: margin;            left: 15px;            padding: 0 10px 0 10px;            color: #88b4e8;            font-size: 13px;            font-weight: bold;        }        /* 按钮 */        QPushButton {            background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,                stop: 0 #4a7db5, stop: 1 #2a5a8a);            color: white;            border: none;            border-radius: 8px;            padding: 8px 20px;            font-weight: bold;            font-size: 13px;            min-height: 25px;        }        QPushButton:hover {            background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,                stop: 0 #5a8dc5, stop: 1 #3a6a9a);        }        QPushButton:pressed {            background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,                stop: 0 #2a5a8a, stop: 1 #1a4a7a);        }        QPushButton:disabled {            background: #3a4a6a;            color: #7a8aaa;        }        /* 主要操作按钮 - 开始转换 */        QPushButton#convertBtn {            background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,                stop: 0 #4CAF50, stop: 1 #2E7D32);            font-size: 14px;            min-height: 30px;        }        QPushButton#convertBtn:hover {            background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,                stop: 0 #66BB6A, stop: 1 #388E3C);        }        QPushButton#convertBtn:disabled {            background: #3a5a3a;            color: #7a9a7a;        }        /* 取消按钮 */        QPushButton#cancelBtn {            background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,                stop: 0 #e74c3c, stop: 1 #c0392b);        }        QPushButton#cancelBtn:hover {            background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,                stop: 0 #ef5350, stop: 1 #d32f2f);        }        QPushButton#cancelBtn:disabled {            background: #5a3a3a;            color: #9a7a7a;        }        /* 输入框 */        QLineEdit {            background: rgba(20, 30, 50, 0.8);            border: 2px solid #2a3a5e;            border-radius: 8px;            padding: 8px 12px;            color: #e0e0e0;            font-size: 12px;            selection-background-color: #4a7db5;        }        QLineEdit:focus {            border-color: #4a7db5;            background: rgba(25, 35, 55, 0.9);        }        QLineEdit:disabled {            background: rgba(15, 20, 35, 0.5);            color: #6a7a8a;        }        /* 单选框和复选框 */        QRadioButton {            color: #c0d0e0;            font-size: 12px;            spacing: 8px;        }        QRadioButton::indicator {            width: 18px;            height: 18px;            border-radius: 9px;            border: 2px solid #4a7db5;            background: rgba(20, 30, 50, 0.8);        }        QRadioButton::indicator:checked {            background: qradialgradient(cx: 0.5, cy: 0.5, radius: 0.5,                stop: 0 #4a7db5, stop: 0.4 #4a7db5, stop: 0.5 rgba(20, 30, 50, 0.8));        }        QRadioButton::indicator:hover {            border-color: #5a8dc5;        }        QCheckBox {            color: #c0d0e0;            font-size: 12px;            spacing: 8px;        }        QCheckBox::indicator {            width: 18px;            height: 18px;            border-radius: 4px;            border: 2px solid #4a7db5;            background: rgba(20, 30, 50, 0.8);        }        QCheckBox::indicator:checked {            background: #4a7db5;        }        QCheckBox::indicator:hover {            border-color: #5a8dc5;        }        /* 下拉框 */        QComboBox {            background: rgba(20, 30, 50, 0.8);            border: 2px solid #2a3a5e;            border-radius: 8px;            padding: 6px 12px;            color: #e0e0e0;            font-size: 12px;            min-width: 70px;        }        QComboBox:hover {            border-color: #4a7db5;        }        QComboBox::drop-down {            border: none;            width: 20px;        }        QComboBox::down-arrow {            image: none;            border-left: 5px solid transparent;            border-right: 5px solid transparent;            border-top: 6px solid #4a7db5;            margin-right: 5px;        }        QComboBox QAbstractItemView {            background: rgba(20, 30, 50, 0.95);            border: 2px solid #4a7db5;            border-radius: 8px;            selection-background-color: #4a7db5;            color: #e0e0e0;            padding: 5px;        }        /* 数字输入框 */        QSpinBox {            background: rgba(20, 30, 50, 0.8);            border: 2px solid #2a3a5e;            border-radius: 8px;            padding: 6px 8px;            color: #e0e0e0;            font-size: 12px;        }        QSpinBox:focus {            border-color: #4a7db5;        }        QSpinBox::up-button, QSpinBox::down-button {            background: rgba(30, 40, 60, 0.5);            border: none;            border-radius: 3px;            width: 20px;        }        QSpinBox::up-button:hover, QSpinBox::down-button:hover {            background: #4a7db5;        }        QSpinBox::up-arrow {            image: none;            border-left: 5px solid transparent;            border-right: 5px solid transparent;            border-bottom: 6px solid #8a9aaa;            margin: 3px;        }        QSpinBox::down-arrow {            image: none;            border-left: 5px solid transparent;            border-right: 5px solid transparent;            border-top: 6px solid #8a9aaa;            margin: 3px;        }        /* 文本显示区域 - 日志 */        QTextEdit {            background: rgba(10, 15, 30, 0.9);            border: 2px solid #2a3a5e;            border-radius: 10px;            color: #b0c8e0;            font-family: "Consolas", "Courier New", monospace;            font-size: 11px;            padding: 10px;            selection-background-color: #4a7db5;        }        /* 标签 */        QLabel {            color: #c0d0e0;            font-size: 12px;        }        /* 滚动条 */        QScrollBar:vertical {            background: rgba(20, 30, 50, 0.5);            border-radius: 10px;            width: 12px;            margin: 2px;        }        QScrollBar::handle:vertical {            background: #4a7db5;            border-radius: 10px;            min-height: 30px;        }        QScrollBar::handle:vertical:hover {            background: #5a8dc5;        }        QScrollBar::add-line:vertical, QScrollBar::sub-line:vertical {            border: none;            background: none;        }        QScrollBar:horizontal {            background: rgba(20, 30, 50, 0.5);            border-radius: 10px;            height: 12px;            margin: 2px;        }        QScrollBar::handle:horizontal {            background: #4a7db5;            border-radius: 10px;            min-width: 30px;        }        QScrollBar::handle:horizontal:hover {            background: #5a8dc5;        }        QScrollBar::add-line:horizontal, QScrollBar::sub-line:horizontal {            border: none;            background: none;        }        /* 提示框 */        QMessageBox {            background: #1a1a2e;        }        QMessageBox QLabel {            color: #e0e0e0;        }        """        self.setStyleSheet(style)    def init_ui(self):        """初始化用户界面"""        # 主窗口部件        central_widget = QWidget()        self.setCentralWidget(central_widget)        main_layout = QVBoxLayout(central_widget)        main_layout.setSpacing(15)        main_layout.setContentsMargins(20202020)        # 输入设置        input_group = QGroupBox("📂 输入设置")        input_layout = QVBoxLayout(input_group)        input_layout.setSpacing(12)        # 选择模式        mode_layout = QHBoxLayout()        self.input_mode_group = QButtonGroup()        self.single_file_radio = QRadioButton("📄 单个PDF文件")        self.single_file_radio.setChecked(True)        self.folder_radio = QRadioButton("📁 PDF文件夹(含子文件夹)")        self.input_mode_group.addButton(self.single_file_radio, 0)        self.input_mode_group.addButton(self.folder_radio, 1)        mode_layout.addWidget(self.single_file_radio)        mode_layout.addWidget(self.folder_radio)        mode_layout.addStretch()        input_layout.addLayout(mode_layout)        # 文件/文件夹选择        path_layout = QHBoxLayout()        path_layout.addWidget(QLabel("路径:"))        self.path_edit = QLineEdit()        self.path_edit.setPlaceholderText("请选择PDF文件或文件夹...")        path_layout.addWidget(self.path_edit)        browse_btn = QPushButton("浏览...")        browse_btn.clicked.connect(self.browse_path)        path_layout.addWidget(browse_btn)        input_layout.addLayout(path_layout)        # Poppler路径设置        poppler_layout = QHBoxLayout()        self.use_poppler_check = QCheckBox("⚙️ 指定Poppler路径")        self.use_poppler_check.toggled.connect(self.toggle_poppler_path)        poppler_layout.addWidget(self.use_poppler_check)        poppler_layout.addWidget(QLabel("Poppler路径:"))        self.poppler_edit = QLineEdit()        self.poppler_edit.setEnabled(False)        self.poppler_edit.setPlaceholderText("例如: C:\\poppler\\bin")        poppler_layout.addWidget(self.poppler_edit)        poppler_browse_btn = QPushButton("浏览...")        poppler_browse_btn.clicked.connect(self.browse_poppler)        poppler_browse_btn.setEnabled(False)        self.poppler_browse_btn = poppler_browse_btn        poppler_layout.addWidget(poppler_browse_btn)        input_layout.addLayout(poppler_layout)        main_layout.addWidget(input_group)        # 输出设置        output_group = QGroupBox("⚙️ 输出设置")        output_layout = QVBoxLayout(output_group)        output_layout.setSpacing(12)        # 输出文件夹        out_path_layout = QHBoxLayout()        out_path_layout.addWidget(QLabel("输出文件夹:"))        self.output_edit = QLineEdit()        self.output_edit.setPlaceholderText("请选择输出文件夹...")        out_path_layout.addWidget(self.output_edit)        out_browse_btn = QPushButton("浏览...")        out_browse_btn.clicked.connect(self.browse_output)        out_path_layout.addWidget(out_browse_btn)        output_layout.addLayout(out_path_layout)        # 转换选项        options_layout = QHBoxLayout()        options_layout.setSpacing(15)        # 输出模式        options_layout.addWidget(QLabel("输出模式:"))        self.output_mode_group = QButtonGroup()        self.separate_radio = QRadioButton("分页输出")        self.separate_radio.setChecked(True)        self.merge_radio = QRadioButton("合并输出")        self.output_mode_group.addButton(self.separate_radio, 0)        self.output_mode_group.addButton(self.merge_radio, 1)        options_layout.addWidget(self.separate_radio)        options_layout.addWidget(self.merge_radio)        # 分隔线        line = QLabel("|")        line.setStyleSheet("color: #3a4a6a;")        options_layout.addWidget(line)        # 输出格式        options_layout.addWidget(QLabel("输出格式:"))        self.format_combo = QComboBox()        self.format_combo.addItems(self.supported_formats)        self.format_combo.setMaximumWidth(80)        options_layout.addWidget(self.format_combo)        # 分隔线        line2 = QLabel("|")        line2.setStyleSheet("color: #3a4a6a;")        options_layout.addWidget(line2)        # DPI设置        options_layout.addWidget(QLabel("DPI:"))        self.dpi_spin = QSpinBox()        self.dpi_spin.setRange(1001200)        self.dpi_spin.setSingleStep(50)        self.dpi_spin.setValue(300)        self.dpi_spin.setMaximumWidth(70)        options_layout.addWidget(self.dpi_spin)        options_layout.addStretch()        output_layout.addLayout(options_layout)        main_layout.addWidget(output_group)        # 日志输出        log_group = QGroupBox("📋 操作日志")        log_layout = QVBoxLayout(log_group)        log_layout.setSpacing(8)        self.log_text = QTextEdit()        self.log_text.setReadOnly(True)        log_layout.addWidget(self.log_text)        main_layout.addWidget(log_group)        # 操作按钮        btn_layout = QHBoxLayout()        btn_layout.setSpacing(15)        self.convert_btn = QPushButton("🚀 开始转换")        self.convert_btn.setObjectName("convertBtn")        self.convert_btn.clicked.connect(self.start_conversion)        btn_layout.addWidget(self.convert_btn)        self.cancel_btn = QPushButton("⏹ 取消")        self.cancel_btn.setObjectName("cancelBtn")        self.cancel_btn.clicked.connect(self.cancel_conversion)        self.cancel_btn.setEnabled(False)        btn_layout.addWidget(self.cancel_btn)        btn_layout.addStretch()        self.clear_log_btn = QPushButton("🗑 清空日志")        self.clear_log_btn.clicked.connect(self.clear_log)        btn_layout.addWidget(self.clear_log_btn)        main_layout.addLayout(btn_layout)    def browse_path(self):        """浏览文件或文件夹"""        if self.folder_radio.isChecked():            folder = QFileDialog.getExistingDirectory(self"选择PDF文件夹(将递归搜索所有子文件夹)")            if folder:                self.path_edit.setText(osp.normpath(folder))                # 使用文件夹名作为输出文件夹名                folder_name = osp.basename(folder)                self.output_edit.setText(osp.normpath(osp.join(folder, f"{folder_name}_Images")))        else:            file, _ = QFileDialog.getOpenFileName(                self"选择PDF文件""""PDF文件 (*.pdf);;所有文件 (*.*)"            )            if file:                self.path_edit.setText(osp.normpath(file))                folder = osp.dirname(file)                name = osp.splitext(osp.basename(file))[0]                self.output_edit.setText(osp.normpath(osp.join(folder, f"{name}_Images")))    def browse_output(self):        """浏览输出文件夹"""        folder = QFileDialog.getExistingDirectory(self"选择输出文件夹")        if folder:            self.output_edit.setText(folder)    def browse_poppler(self):        """浏览Poppler路径"""        folder = QFileDialog.getExistingDirectory(self"选择Poppler路径 (包含bin文件夹)")        if folder:            # 自动检测并添加bin路径            poppler_path = folder            # 如果选择的是bin文件夹,直接使用;如果是上级文件夹,添加bin            if osp.basename(folder).lower() == 'bin':                self.poppler_edit.setText(osp.normpath(folder))            else:                # 检查是否存在bin子文件夹                bin_path = osp.join(folder, 'bin')                if osp.isdir(bin_path):                    self.poppler_edit.setText(osp.normpath(bin_path))                else:                    self.poppler_edit.setText(osp.normpath(folder))            self.log_message(f"✅ 已设置Poppler路径: {self.poppler_edit.text()}")    def toggle_poppler_path(self, checked):        """切换Poppler路径输入框状态"""        self.poppler_edit.setEnabled(checked)        self.poppler_browse_btn.setEnabled(checked)        if not checked:            self.poppler_edit.clear()        else:            # 如果开启,可以设置默认路径或提示            self.log_message("💡 请设置Poppler路径(如未设置系统环境变量)")    def start_conversion(self):        """开始转换(占位方法)"""        self.log_message("🚀 开始转换功能待实现...")        # 这里只是界面演示,实际转换逻辑已移除    def cancel_conversion(self):        """取消转换(占位方法)"""        self.log_message("⏹ 取消转换功能待实现...")        self.cancel_btn.setEnabled(False)        self.convert_btn.setEnabled(True)    def clear_log(self):        """清空日志"""        self.log_text.clear()    def log_message(self, message):        """向日志框添加消息"""        self.log_text.append(message)        # 滚动到底部        scrollbar = self.log_text.verticalScrollBar()        scrollbar.setValue(scrollbar.maximum())def main():    app = QApplication(sys.argv)    app.setStyle('Fusion')  # 使用Fusion风格作为基础    window = PdfToPngConverter()    window.show()    sys.exit(app.exec())if __name__ == "__main__":    main()

            通过这款PDF文档批量转图片工具,仅需几分钟配置好源文件夹、输出路径、输出模式及DPI分辨率等核心参数,即可自动完成原需手动逐一打开PDF、逐页导出图片、调整分辨率的一整天重复性工作。从最初逐个打开PDF文件手动截图保存的疲惫与焦虑,到用自动化工具实现一键批量处理上百份文档的畅快,工作效率获得指数级提升,终于解放了双手和时间!

            大佬们也可以举一反三,参照上面的代码思路根据自己工作中的实际情况来具体问题具体分析,实现自己定制化的需求。

            《该办公工具的适用场景:》

            • 工程设计图纸归档:将大量PDF工程图纸批量转为图片格式,便于归档

            • 文档管理系统对接:将PDF文档转换为图片后,上传至仅支持图片格式的文档管理系统或图库

            • PPT/报告素材提取:从PDF报告中提取单页内容作为PPT、宣传材料的配图素材

            • 图纸审查与批注:将PDF图纸转为图片后,直接在图片上进行圈阅、批注和审核

            • 网页/公众号配图:将PDF文档内容转为JPG/PNG格式,用于网站、公众号文章配图展示

            • CAD设计参考:将PDF参考图纸批量导出为高清图片,方便在设计软件中作为底图参照

            • 多格式存档备份:将PDF统一转换为TIFF等格式,满足档案长期保存的格式规范要求

            • 批量缩略图生成:为大量PDF文档批量生成封面缩略图,便于文件浏览和管理

            结语

            当Python遇见办公,牛马打工人终于笑出了猪叫声

            【职场人必看】每天早上一睁眼,想到又要面对:

            1.📊 堆积如山的Excel表格

            2.📑 机械重复的复制粘贴

            3.✍️ 永远改不完的各类文档

            4.诸如此类的更多........

            是不是连Ctrl+Alt+Delete的心都有了?

            别慌!别急,摸鱼这位“职场外挂”已经带着Python代码来拯救你了!

            感谢各位大佬观看,还望各位大佬抬抬贵手一键三连,多多关注点赞转发评论,大佬们的支持才是摸鱼孜孜不倦更新原创干货的动力!

            另外,本篇文章的exe已上传nas私有云盘,有需要的大佬私信摸鱼君获取