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…...
前端开发环境:Vue、Element Plus、Axios
目录 1. Vue简介 2. Element Plus简介 3. Axios简介 4. 创建Vue项目 4.1 Node.js安装 4.2 创建Vue项目 4.3 Vue项目的结构 4.4 安装Element-Plus 4.5 安装Axios 4.6 解决跨域问题 5. 应用实例 5.1 创建Vue组件 5.2 配置路由 5.3 配置根组件 5.4 启动前端应用服…...
我的创作纪念日-在SCDN的5年
机缘 五年前,一个偶然的机会让我接触到了SCDN这个充满活力和创造力的社区。我抱着对技术的热爱和对知识的渴望,决定在这里开启我的创作之旅。最初,我成为创作者的初心,是希望将自己在实战项目中的经验、日常学习过程中的点滴&…...
AI-知识库搭建(二)GPT-Embedding模型使用
上一篇:AI-知识库搭建(一)腾讯云向量数据库使用-CSDN博客 一、Embedding模型 Embedding模型是一种将高维度的离散数据(如文本、图像、音频等)映射到低维度的连续向量空间的技术。这种技术广泛应用于自然语言处理&…...
qt网络事件之QSocketNotifier
简介 QSocketNotifier用于处理网络事件的,即事件处理器 结构 #mermaid-svg-xcNdAyHNkKqNCLQY {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-xcNdAyHNkKqNCLQY .error-icon{fill:#552222;}#mermaid-svg-xcNdAyHNk…...
如何统计EXCEL中的数据透视表的信息?
也没什么可分析的,直接上代码,看看是不是你需要的: Sub GetPVT() 定义一个1000行的数组,如果你预判工作簿中数据透视表数量可能大小1000,那就改成10000,甚至10万,以确保能大于数据透视表数量即…...
日本结构型产品及衍生品业务变迁报告
日本结构型产品及衍生品业务变迁报告 一、业务发展阶段 阶段一:2000年之前 零售结构型产品几乎不存在,主要销售对象为机构投资者或企业。主要策略为卖出看涨期权(covered call)。会计记录准则对业务有重要影响,例如…...
解决Mac无法上网/网络异常的方法,重置网络
解放方法 1、前往文件夹:/Library/Preferences/SystemConfiguration 2 、在弹窗中输入上边的地址 3 、把文件夹中除了下图未选中的文件全部删掉,删除时需要输入密码 4 、重启mac 电脑就搞定了。...
[12] 使用 CUDA 进行图像处理
使用 CUDA 进行图像处理 当下生活在高清摄像头的时代,这种摄像头能捕获高达1920*1920像素的高解析度画幅。想要实施的处理这么多的数据,往往需要几个TFlops地浮点处理性能,这些要求CPU也无法满足通过在代码中使用CUDA,可以利用GP…...
MyBatisPlus代码生成器(交互式)快速指南
引言 本片文章是对代码生成器(交互)快速配置使用流程,更多配置方法可查看官方文档: 代码生成器配置官网 如有疑问欢迎评论区交流! 文章目录 引言演示效果图引入相关依赖创建代码生成器对象引入Freemarker模板引擎依赖支持的模板引擎 MyBat…...
深度学习模型训练之日志记录
在深度学习模型训练过程中,进行有效的训练日志记录是至关重要的。以下是一些常见的策略和工具来实现这一目标: 1. 使用TensorBoard TensorBoard是TensorFlow提供的一个可视化工具,用于记录和展示训练过程中的各种指标。 设置TensorBoard&a…...
上海高端网站开发公/武汉大学人民医院地址
对ASP.NET中关于事件委托的理解 理解一:事件委托的概念.NET框架的事件委托遵循特定的签名和命名约定。这种约定依赖于可视化设计工具,为客户端代码提供了一致性的模型。为了理解这种约定,下面来看看.NET框架中一个常见的可访问事件委托System…...
重庆网站建设红旗河沟/超级外链工具有用吗
USE masterGO sp_Configure ALLOW UPDATES, 1 /* 允许直接更新系统表 */RECONFIGURE WITH OVERRIDE /* 更新对当前配置选项值的更改 */GO -- 设置“myTestDB”为“紧急模式”。UPDATE sysdatabases SET status 32768 WHERE name bfxkGO -- 重置 ALLOW UPDATES 为…...
重庆市设计公司网站/宁波网站优化公司推荐
点击角色,显示该角色下的用户,并且可以对该角色下的用户进行添加或删除,此处类似一个穿梭框 SysRoleController部分代码如下: @RequestMapping("/changeUsers.json")@ResponseBodypublic JsonData changeUsers(@RequestParam("roleId") int roleId,...
上海给政府机关做网站开发 万/最新seo教程
ElasticSearch文档是详尽无遗的,但其结构方式尚有待改进。 这篇文章的目的是作为ElasticSearch API的备忘单入口点。<!-more-→ 类别 描述 通话范例 Document API Single Document API Adds a new document PUT /my_index/my_type/1 {"my_fie…...
建立企业网站需要什么/怎么分析一个网站seo
最近想玩下chrome的插件主要有两个原因, 一个是前段时间对chrome new tab的功能太少很不满意, 现有插件又不能满足需求, 想自己搞个. 由于界面设计实在是我的障碍而且自身需求不急, 就没动手; 另个是最近想在公司内网求一个ipad mini, 僧多粥少, 往往刚有一个卖的半小时内就被预…...
wordpress运行/zoho crm
在AWR报告中,log file sync等待事件显著。2.问题原因产生此事件的主要原因是,应用程序在插入数据的时候,采用没插入一条数据,提交一次,通常每批次数据量都较大,而且还会插入日志信息,日志也是没…...