PDF转图片工具
背景:
今天有个朋友找我:“我有个文件需要更改,但是文档是PDF的,需要你帮我改下内容,你是搞软件的,这个对你应该是轻车熟路了吧,帮我弄弄吧”,听到这话我本想反驳,我是开发不是美工,然后跟他科普科普两者的分工和区别。后来想想还是算了,隔行如隔山,讲了可能也是白讲。干脆给他干了得了。毕竟这种类似“程序员=修电脑的”印象在亲戚朋友中早已广为流传。
起因:
一开始觉得做这个工作很简单,打开WPS,直接按他的要求编辑下就算完成就可以的,可当我打开文档编辑的时候:


呵呵,这特么干个免费的活,感情还要自己掏腰包?
于是,一个想法冒出来了,把文档转成图片,再用PS改得了,于是我又尝试转换成图片

挣扎:
我了个擦,要点脸不,也不知道啥时候起金山也养成了企鹅家的作风。于是我想想既然是帮人干活,这个钱怎么也不至于我掏吧,对,让他掏!!可话又说回来,就这么点屁事,让人花几十上百也是有点坑。
既然WPS处处要花钱,那就不用了,自己写一个不就OK
import fitz
import os
from PIL import Image
from reportlab.pdfgen import canvas
from reportlab.lib.pagesizes import letterdef pdf_to_images(pdf_path, zoom_x=2.0, zoom_y=2.0):# 创建输出文件夹pdf_dir = os.path.dirname(pdf_path)sub_folder = os.path.basename(pdf_path).split(".")[0]output_folder = '{}/{}/imgs'.format(pdf_dir, sub_folder)if not os.path.exists(output_folder):os.makedirs(output_folder)# 打开PDF文件pdf_document = fitz.open(pdf_path)for page_num in range(len(pdf_document)):# 获取页面page = pdf_document.load_page(page_num)# 设置变换矩阵以增加图像分辨率mat = fitz.Matrix(zoom_x, zoom_y)# 转换页面为图像pix = page.get_pixmap(matrix=mat)# 保存图像output_image_path = os.path.join(output_folder, f'page_{page_num + 1}.png')pix.save(output_image_path)print(f"PDF {pdf_path} 已成功转换为图像,并保存到文件夹 {output_folder}")def images_to_pdf(images_folder, output_pdf_path):# 获取所有图片文件image_files = [f for f in os.listdir(images_folder) if f.endswith(('png', 'jpg', 'jpeg'))]image_files.sort() # 按名称排序,确保顺序正确if not image_files:print("没有找到图片文件。")return# 创建一个空白的 PDF 文件c = canvas.Canvas(output_pdf_path, pagesize=letter)for image_file in image_files:image_path = os.path.join(images_folder, image_file)# 打开图片并获取其尺寸with Image.open(image_path) as img:img_width, img_height = img.size# 将图片按比例缩放以适应页面page_width, page_height = letterscale = min(page_width / img_width, page_height / img_height)img_width *= scaleimg_height *= scale# 将图片绘制到 PDF 页面上c.drawImage(image_path, 0, page_height - img_height, width=img_width, height=img_height)c.showPage() # 开始一个新页面c.save()print(f"图片已成功合并为 PDF 文件:{output_pdf_path}")if __name__ == "__main__":# 输入 PDF 文档路径# pdf_path = input("请输入 PDF 文档的路径:")# pdf_to_images(pdf_path)images_folder = r'E:\PDF_PROJECT\马赛克\images_output' # 图片文件夹路径output_pdf_path = r'E:\PDF_PROJECT\马赛克\马赛克.pdf' # 输出PDF文件路径images_to_pdf(images_folder, output_pdf_path)
转成图片修改好以后,再给合回去,60+行代码换了100多的会员,头一次感受到了原来技术也不是一文不值,O(∩_∩)O哈哈~!
输出:
完事后,想想这个东西既然花了时间写出来,干脆加个界面,打包成程序提供给有需要的人用,岂不是更能发挥它的价值?
说干就干:
import os
import fitz
import tkinter as tk
from tkinter import ttk, filedialog, messagebox
from PIL import Image
from reportlab.pdfgen import canvas
from reportlab.lib.pagesizes import letter
from datetime import datetimeclass PDFImageConverterApp(tk.Tk):def __init__(self):super().__init__()self.title("PDF-图片 转换工具")self.geometry("650x500")self.create_widgets()def create_widgets(self):self.tabControl = ttk.Notebook(self)self.pdf_to_img_tab = ttk.Frame(self.tabControl)self.img_to_pdf_tab = ttk.Frame(self.tabControl)self.tabControl.add(self.pdf_to_img_tab, text="PDF转图片")self.tabControl.add(self.img_to_pdf_tab, text="图片转PDF")self.create_pdf_to_img_widgets()self.create_img_to_pdf_widgets()self.tabControl.pack(expand=1, fill="both")def create_pdf_to_img_widgets(self):ttk.Label(self.pdf_to_img_tab, text="请选择PDF文件路径:").grid(column=0, row=0, padx=10, pady=10)self.pdf_path = tk.StringVar()ttk.Entry(self.pdf_to_img_tab, width=50, textvariable=self.pdf_path).grid(column=1, row=0, padx=10, pady=10)ttk.Button(self.pdf_to_img_tab, text="Browse", command=self.browse_pdf).grid(column=2, row=0, padx=10, pady=10)ttk.Label(self.pdf_to_img_tab, text="请选择图片输出目录:").grid(column=0, row=1, padx=10, pady=10)self.img_output_folder = tk.StringVar()ttk.Entry(self.pdf_to_img_tab, width=50, textvariable=self.img_output_folder).grid(column=1, row=1, padx=10,pady=10)ttk.Button(self.pdf_to_img_tab, text="Browse", command=self.browse_img_output_folder).grid(column=2, row=1,padx=10, pady=10)ttk.Label(self.pdf_to_img_tab, text="图片质量:").grid(column=0, row=2, padx=10, pady=10)self.img_quality = tk.StringVar(value="标清")ttk.Combobox(self.pdf_to_img_tab, textvariable=self.img_quality, values=["标清", "高清", "超清"]).grid(column=1, row=2, padx=10, pady=10)self.pdf_to_img_progress = ttk.Progressbar(self.pdf_to_img_tab, orient="horizontal", length=400,mode="determinate")self.pdf_to_img_progress.grid(column=0, row=3, columnspan=3, padx=10, pady=10)self.pdf_to_img_log = tk.Text(self.pdf_to_img_tab, height=10, width=70)self.pdf_to_img_log.grid(column=0, row=4, columnspan=3, padx=10, pady=10)ttk.Button(self.pdf_to_img_tab, text="转换", command=self.convert_pdf_to_images).grid(column=0, row=5,columnspan=3, padx=10,pady=10)def create_img_to_pdf_widgets(self):ttk.Label(self.img_to_pdf_tab, text="请选择图片目录:").grid(column=0, row=0, padx=10, pady=10)self.images_folder = tk.StringVar()ttk.Entry(self.img_to_pdf_tab, width=50, textvariable=self.images_folder).grid(column=1, row=0, padx=10,pady=10)ttk.Button(self.img_to_pdf_tab, text="Browse", command=self.browse_images_folder).grid(column=2, row=0, padx=10,pady=10)ttk.Label(self.img_to_pdf_tab, text="请选择PDF输出目录:").grid(column=0, row=1, padx=10, pady=10)self.pdf_output_path = tk.StringVar()ttk.Entry(self.img_to_pdf_tab, width=50, textvariable=self.pdf_output_path).grid(column=1, row=1, padx=10,pady=10)ttk.Button(self.img_to_pdf_tab, text="Browse", command=self.browse_pdf_output_path).grid(column=2, row=1,padx=10, pady=10)self.img_to_pdf_progress = ttk.Progressbar(self.img_to_pdf_tab, orient="horizontal", length=400,mode="determinate")self.img_to_pdf_progress.grid(column=0, row=2, columnspan=3, padx=10, pady=10)self.img_to_pdf_log = tk.Text(self.img_to_pdf_tab, height=10, width=70)self.img_to_pdf_log.grid(column=0, row=3, columnspan=3, padx=10, pady=10)ttk.Button(self.img_to_pdf_tab, text="转换", command=self.convert_images_to_pdf).grid(column=0, row=4,columnspan=3, padx=10,pady=10)def browse_pdf(self):file_path = filedialog.askopenfilename(filetypes=[("PDF files", "*.pdf")])if file_path:self.pdf_path.set(file_path)def browse_img_output_folder(self):folder_path = filedialog.askdirectory()if folder_path:self.img_output_folder.set(folder_path)def browse_images_folder(self):folder_path = filedialog.askdirectory()if folder_path:self.images_folder.set(folder_path)def browse_pdf_output_path(self):file_folder = filedialog.askdirectory()if file_folder:timestamp = datetime.now().strftime("%y-%m-%d_%H%M%S")output_pdf_path = os.path.join(file_folder, f"output_{timestamp}.pdf")self.pdf_output_path.set(output_pdf_path)def log_message(self, log_widget, message):log_widget.insert(tk.END, message + "\n")log_widget.see(tk.END)def convert_pdf_to_images(self):pdf_path = self.pdf_path.get()output_folder = self.img_output_folder.get()quality = self.img_quality.get()if not pdf_path or not output_folder or not quality:messagebox.showwarning("Warning", "请选择所有输入项.")returnzoom_x, zoom_y = 1.0, 1.0if quality == "高清":zoom_x, zoom_y = 2.0, 2.0elif quality == "超清":zoom_x, zoom_y = 3.0, 3.0self.pdf_to_img_progress['value'] = 0self.update()pdf_document = fitz.open(pdf_path)total_pages = len(pdf_document)for page_num in range(total_pages):page = pdf_document.load_page(page_num)mat = fitz.Matrix(zoom_x, zoom_y)pix = page.get_pixmap(matrix=mat)output_image_path = os.path.join(output_folder, f'page_{page_num + 1}.png')pix.save(output_image_path)self.pdf_to_img_progress['value'] = (page_num + 1) / total_pages * 100self.log_message(self.pdf_to_img_log, f"Page {page_num + 1}/{total_pages} converted.")self.update()messagebox.showinfo("Info", "图片输出完成.")def convert_images_to_pdf(self):images_folder = self.images_folder.get()output_pdf_path = self.pdf_output_path.get()if not images_folder or not output_pdf_path:messagebox.showwarning("Warning", "请选择所有输入项.")returnself.img_to_pdf_progress['value'] = 0self.update()image_files = [f for f in os.listdir(images_folder) if f.endswith(('png', 'jpg', 'jpeg'))]image_files.sort()total_images = len(image_files)if not image_files:messagebox.showwarning("Warning", "该文件夹下没有图片,请重新选择!")returnc = canvas.Canvas(output_pdf_path, pagesize=letter)for idx, image_file in enumerate(image_files):image_path = os.path.join(images_folder, image_file)with Image.open(image_path) as img:img_width, img_height = img.sizepage_width, page_height = letterscale = min(page_width / img_width, page_height / img_height)img_width *= scaleimg_height *= scalec.drawImage(image_path, 0, page_height - img_height, width=img_width, height=img_height)c.showPage()self.img_to_pdf_progress['value'] = (idx + 1) / total_images * 100self.log_message(self.img_to_pdf_log, f"Image {idx + 1}/{total_images} added to PDF.")self.update()c.save()messagebox.showinfo("Info", "PDF转换完成!")if __name__ == "__main__":app = PDFImageConverterApp()app.mainloop()

打包exe传送门:
https://download.csdn.net/download/Hfengxiang/89409663
结语:
突然冒出个想法,朋友们,生活或工作中遇到类似这样的痛点,欢迎在评论区讨论,一起研究研究看看能否用代码解决^_^
相关文章:
PDF转图片工具
背景: 今天有个朋友找我:“我有个文件需要更改,但是文档是PDF的,需要你帮我改下内容,你是搞软件的,这个对你应该是轻车熟路了吧,帮我弄弄吧”,听到这话我本想反驳,我是开…...
Day 19:419. 甲板上的战舰
Leetcode 419. 甲板上的战舰 给你一个大小为 m x n 的矩阵 board 表示甲板,其中,每个单元格可以是一艘战舰 ‘X’ 或者是一个空位 ‘.’ ,返回在甲板 board 上放置的 战舰 的数量。 战舰 只能水平或者垂直放置在 board 上。换句话说ÿ…...
Web前端专科实习:技能提升、实践挑战与职业展望
Web前端专科实习:技能提升、实践挑战与职业展望 在数字化时代,Web前端技术作为连接用户与互联网世界的桥梁,其重要性日益凸显。作为一名Web前端专科实习生,我有幸在这个充满机遇和挑战的领域进行实践学习。接下来,我将…...
简单脉冲动画效果实现
简单脉冲动画效果实现 效果展示 CSS 知识点 CSS 变量的灵活使用CSS 动画使用 页面整体结构实现 <div class"pulse"><span style"--i: 1"></span><span style"--i: 2"></span><span style"--i: 3"…...
apache poi 插入“下一页分节符”并设置下一节纸张横向的一种方法
一、需求描述 我们知道,有时在word中需要同时存在不同的节,部分页面需要竖向、部分页面需要横向。本文就是用java调用apache poi来实现用代码生成上述效果。下图是本文实现的效果,供各位看官查阅,本文以一篇课文为例,…...
【React】useCallback和useMemo使用指南
useCallback和useMemo是React中两个用于优化性能的Hooks。以下是它们的使用指南,分点表示并归纳了关键信息: useCallback useCallback返回一个记忆化的回调函数,该回调函数只在它的依赖项发生改变时才会更新。这对于在组件渲染之间保持稳定的引用特别有用,可以防止不必要…...
XMind软件下载-详细安装教程视频
简介 XMind是一款实用的思维导图软件,简单易用、美观、功能强大,拥有高效的可视化思维模式,具备可扩展、跨平台、稳定性和性能,真正帮助用户提高生产率,促进有效沟通及协作。中文官方网站:http://www.x…...
一个小的画布Canvas页面,记录点的轨迹
Hello大家好,好久没有更新了,最近在忙一些其他的事,今天说一下画布canvas,下面是我的代码,实现了一个点从画布的(0,0)到(canvas.width,canvas.height)的一个实…...
docker-compose教程
1. docker-compose是什么? 1. 1 简介 compose、machine 和 swarm 是docker 原生提供的三大编排工具。 简称docker三剑客。Compose 项目是 Docker 官方的开源项目,定义和运行多个 Docker 容器的应用(Defining and running multi-container Do…...
结果出乎意料!MySQL和MariaDB谁快?MySQL 8.0比MySQL 5.6快吗?
MySQL和MariaDB哪个更快?MySQL 8.0的版本和早期MySQL 5.6的版本哪个更快?这儿有个第三方的测试报告回答了这两个大家关心的问题,姚远来和大家一起解读一下。https://smalldatum.blogspot.com/2024/04/sysbench-on-small-server-mariadb-and.h…...
Alienware外星人X17R2 原装Win11系统镜像下载 带SupportAssist OS Recovery一键恢复
装后恢复到您开箱的体验界面,包括所有原机所有驱动AWCC、Mydell、office、mcafee等所有预装软件。 最适合您电脑的系统,经厂家手调试最佳状态,性能与功耗直接拉满,体验最原汁原味的系统。 原厂系统下载网址:http://w…...
【NI国产替代】高速数据采集模块,最大采样率为 125 Msps,支持 FPGA 定制化
• 双通道高精度数据采集 • 支持 FPGA 定制化 • 双通道高精度采样率 最大采样率为 125 Msps12 位 ADC 分辨率 最大输入电压为 0.9 V -3 dB 带宽为 30 MHz 支持 FPGA 定制化 根据需求编程实现特定功能和性能通过定制 FPGA 实现硬件加速,提高系统的运算速度FPGA…...
【网络安全的神秘世界】2024.6.6 Docker镜像停服?解决最近Docker镜像无法拉取问题
🌝博客主页:泥菩萨 💖专栏:Linux探索之旅 | 网络安全的神秘世界 | 专接本 解决Docker镜像无法拉取问题 🙋♂️问题描述 常用镜像站:阿里云、科大、南大、上交等,全部挂掉 执行docker pull命…...
【Python入门与进阶】1基本输入和输出
基本输入输出 1.等号赋值 1.1 基本赋值 number_110number_1 1.2 多个赋值 number_2number_3number_420 number_2 number_3 number_4 1.3 多重赋值 number_5,number_6,number_730,35,40 number_5 number_6 number_7 1.4 下划线赋值 _50 _ 2.命名规则 注意:…...
CTF Show MISC做题笔记
MISCX 30 题目压缩包为misc2.rar,其中包含三个文件:misc1.zip, flag.txt, hint.txt。其中后两个文件是加密的。 先解压出misc1.zip, 发现其中包含两个文件:misc.png和music.doc。其中后面文件是加密的。 解压出misc.png,发现图片尾部有消息:flag{flag…...
【QT5】<总览二> QT信号槽、对象树及常用函数
文章目录 前言 一、QT信号与槽 1. 信号槽连接模型 2. 信号槽介绍 3. 自定义信号槽 二、QT的对象树 三、添加资源文件 四、样式表的使用 五、QSS文件的使用 六、常用函数与宏 前言 承接【QT5】<总览一> QT环境搭建、快捷键及编程规范。若存在版…...
Button按钮类
自学python如何成为大佬(目录):https://blog.csdn.net/weixin_67859959/article/details/139049996?spm1001.2014.3001.5501 按钮是GUI界面中应用最为广泛的控件,它常用于捕获用户生成的单击事件,其最明显的用途是触发绑定到一个处理函数。 wxPython类…...
代码随想录-二叉树 | 111 二叉树的最小深度
代码随想录-二叉树 | 111 二叉树的最小深度 LeetCode 111 二叉树的最小深度解题思路代码难点总结 LeetCode 111 二叉树的最小深度 题目链接 代码随想录 题目描述 给定一个二叉树,找出其最小深度。 最小深度是从根节点到最近叶子节点的最短路径上的节点数量。 说…...
PCA降维算法
decomposition.h #pragma once #include <arrayfire.h>namespace decomposition {class PCA{public:af::array zero_centred(af::array...
Fast R-CNN 与 R-CNN的不同之处
目录 一、Fast R-CNN如何生成候选框特征矩阵 二、 关于正负样本的解释 三、训练样本的候选框 四、Fast R-CNN网络架构 4.1 分类器 4.2 边界框回归器 一、Fast R-CNN如何生成候选框特征矩阵 在R-CNN中,通过SS算法得到2000个候选框,则需要进行2000…...
WeChatExporter深度解析:如何三步搞定iOS微信聊天记录完整导出
WeChatExporter深度解析:如何三步搞定iOS微信聊天记录完整导出 【免费下载链接】WeChatExporter 一个可以快速导出、查看你的微信聊天记录的工具 项目地址: https://gitcode.com/gh_mirrors/wec/WeChatExporter 还在为无法备份微信聊天记录而烦恼吗ÿ…...
【算法对抗】打穿查重黑盒!论文降AI太难?8个实测有效策略与高性价比工具
上周匆匆写完论文初稿交给导师,结果被一眼识破,当场打回。还被导师认为不认真不负责态度不端正! 为了搞定这件事,我测评了市面上大部分的主流工具、试了无数方法,终于把AI率降到6%。 我们要先端正态度:论文…...
医学图像分类实战:基于kvasir v2胃病数据集的深度卷积网络性能对比
1. 医学图像分类与KVASIR V2数据集简介 胃镜图像分类是计算机辅助诊断系统中的关键环节。KVASIR V2作为目前最全面的公开胃病数据集,包含8类常见胃部病变的8000张高清图像,每类1000张。这些图像由专业胃肠病专家标注,覆盖了从正常黏膜到早期…...
如何在5分钟内将网页SVG完美保存为可编辑矢量文件?
如何在5分钟内将网页SVG完美保存为可编辑矢量文件? 【免费下载链接】svg-crowbar Extracts an SVG node and accompanying styles from an HTML document and allows you to download it all as an SVG file. 项目地址: https://gitcode.com/gh_mirrors/sv/svg-cr…...
SEO_10个提升网站排名的实用SEO技巧分享(220 )
<h1 id"seo10seo">SEO:10个提升网站排名的实用SEO技巧分享</h1> <p>在当今互联网时代,搜索引擎优化(SEO)已经成为提升网站流量和吸引潜在客户的关键手段。百度作为中国最大的搜索引擎,其优化规则对整…...
网络工程师-核心考点:计算机硬件基础全解析
一、引言计算机硬件基础是软考网络工程师考试的前置知识点,占选择题分值约 3-5 分,是理解网络设备(路由器、交换机、服务器)硬件架构的底层基础。本知识点体系起源于 1945 年冯・诺依曼提出的存储程序思想,历经 70 余年…...
从“炼丹”到“调参”:聊聊反向传播里那些容易被忽略的梯度细节(以PyTorch为例)
从“炼丹”到“调参”:聊聊反向传播里那些容易被忽略的梯度细节(以PyTorch为例) 在深度学习的世界里,反向传播算法就像炼金术士的魔法书,而梯度则是那些隐藏在公式背后的神秘力量。许多开发者能够熟练地调用.backward(…...
Word自动编号的隐藏玩法:用题注和交叉引用,打造能“自我修复”的智能文档
Word文档工程化:构建自动编号与交叉引用的智能系统 在技术文档撰写过程中,最令人头疼的莫过于图表编号的维护。当你在200页的文档中插入新图表时,手动编号意味着要逐个修改后续所有编号和引用——这种痛苦只有经历过的人才懂。但很少有人意识…...
Windows 11优化终极指南:一键清理预装软件与提升系统性能
Windows 11优化终极指南:一键清理预装软件与提升系统性能 【免费下载链接】Win11Debloat 一个简单的PowerShell脚本,用于从Windows中移除预装的无用软件,禁用遥测,从Windows搜索中移除Bing,以及执行各种其他更改以简化…...
Outline数据迁移架构解析:构建跨平台知识库的无缝衔接方案
Outline数据迁移架构解析:构建跨平台知识库的无缝衔接方案 【免费下载链接】outline Outline 是一个基于 React 和 Node.js 打造的快速、协作式团队知识库。它可以让团队方便地存储和管理知识信息。你可以直接使用其托管版本,也可以自己运行或参与开发。…...
