当前位置: 首页 > news >正文

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 上。换句话说&#xff…...

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 插入“下一页分节符”并设置下一节纸张横向的一种方法

一、需求描述 我们知道&#xff0c;有时在word中需要同时存在不同的节&#xff0c;部分页面需要竖向、部分页面需要横向。本文就是用java调用apache poi来实现用代码生成上述效果。下图是本文实现的效果&#xff0c;供各位看官查阅&#xff0c;本文以一篇课文为例&#xff0c;…...

【React】useCallback和useMemo使用指南

useCallback和useMemo是React中两个用于优化性能的Hooks。以下是它们的使用指南,分点表示并归纳了关键信息: useCallback useCallback返回一个记忆化的回调函数,该回调函数只在它的依赖项发生改变时才会更新。这对于在组件渲染之间保持稳定的引用特别有用,可以防止不必要…...

XMind软件下载-详细安装教程视频

​简介 XMind是一款实用的思维导图软件&#xff0c;简单易用、美观、功能强大&#xff0c;拥有高效的可视化思维模式&#xff0c;具备可扩展、跨平台、稳定性和性能&#xff0c;真正帮助用户提高生产率&#xff0c;促进有效沟通及协作。中文官方网站&#xff1a;http://www.x…...

一个小的画布Canvas页面,记录点的轨迹

Hello大家好&#xff0c;好久没有更新了&#xff0c;最近在忙一些其他的事&#xff0c;今天说一下画布canvas&#xff0c;下面是我的代码&#xff0c;实现了一个点从画布的&#xff08;0,0&#xff09;到&#xff08;canvas.width&#xff0c;canvas.height&#xff09;的一个实…...

docker-compose教程

1. docker-compose是什么&#xff1f; 1. 1 简介 compose、machine 和 swarm 是docker 原生提供的三大编排工具。 简称docker三剑客。Compose 项目是 Docker 官方的开源项目&#xff0c;定义和运行多个 Docker 容器的应用&#xff08;Defining and running multi-container Do…...

结果出乎意料!MySQL和MariaDB谁快?MySQL 8.0比MySQL 5.6快吗?

MySQL和MariaDB哪个更快&#xff1f;MySQL 8.0的版本和早期MySQL 5.6的版本哪个更快&#xff1f;这儿有个第三方的测试报告回答了这两个大家关心的问题&#xff0c;姚远来和大家一起解读一下。https://smalldatum.blogspot.com/2024/04/sysbench-on-small-server-mariadb-and.h…...

Alienware外星人X17R2 原装Win11系统镜像下载 带SupportAssist OS Recovery一键恢复

装后恢复到您开箱的体验界面&#xff0c;包括所有原机所有驱动AWCC、Mydell、office、mcafee等所有预装软件。 最适合您电脑的系统&#xff0c;经厂家手调试最佳状态&#xff0c;性能与功耗直接拉满&#xff0c;体验最原汁原味的系统。 原厂系统下载网址&#xff1a;http://w…...

【NI国产替代】高速数据采集模块,最大采样率为 125 Msps,支持 FPGA 定制化

• 双通道高精度数据采集 • 支持 FPGA 定制化 • 双通道高精度采样率 最大采样率为 125 Msps12 位 ADC 分辨率 最大输入电压为 0.9 V -3 dB 带宽为 30 MHz 支持 FPGA 定制化 根据需求编程实现特定功能和性能通过定制 FPGA 实现硬件加速&#xff0c;提高系统的运算速度FPGA…...

【网络安全的神秘世界】2024.6.6 Docker镜像停服?解决最近Docker镜像无法拉取问题

&#x1f31d;博客主页&#xff1a;泥菩萨 &#x1f496;专栏&#xff1a;Linux探索之旅 | 网络安全的神秘世界 | 专接本 解决Docker镜像无法拉取问题 &#x1f64b;‍♂️问题描述 常用镜像站&#xff1a;阿里云、科大、南大、上交等&#xff0c;全部挂掉 执行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.命名规则 注意&#xff1a…...

CTF Show MISC做题笔记

MISCX 30 题目压缩包为misc2.rar,其中包含三个文件:misc1.zip, flag.txt, hint.txt。其中后两个文件是加密的。 先解压出misc1.zip, 发现其中包含两个文件&#xff1a;misc.png和music.doc。其中后面文件是加密的。 解压出misc.png,发现图片尾部有消息&#xff1a;flag{flag…...

【QT5】<总览二> QT信号槽、对象树及常用函数

文章目录 前言 一、QT信号与槽 1. 信号槽连接模型 2. 信号槽介绍 3. 自定义信号槽 二、QT的对象树 三、添加资源文件 四、样式表的使用 五、QSS文件的使用 六、常用函数与宏 前言 承接【QT5】&#xff1c;总览一&#xff1e; QT环境搭建、快捷键及编程规范。若存在版…...

Button按钮类

自学python如何成为大佬(目录):https://blog.csdn.net/weixin_67859959/article/details/139049996?spm1001.2014.3001.5501 按钮是GUI界面中应用最为广泛的控件&#xff0c;它常用于捕获用户生成的单击事件&#xff0c;其最明显的用途是触发绑定到一个处理函数。 wxPython类…...

代码随想录-二叉树 | 111 二叉树的最小深度

代码随想录-二叉树 | 111 二叉树的最小深度 LeetCode 111 二叉树的最小深度解题思路代码难点总结 LeetCode 111 二叉树的最小深度 题目链接 代码随想录 题目描述 给定一个二叉树&#xff0c;找出其最小深度。 最小深度是从根节点到最近叶子节点的最短路径上的节点数量。 说…...

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中&#xff0c;通过SS算法得到2000个候选框&#xff0c;则需要进行2000…...

Python爬虫实战:研究MechanicalSoup库相关技术

一、MechanicalSoup 库概述 1.1 库简介 MechanicalSoup 是一个 Python 库,专为自动化交互网站而设计。它结合了 requests 的 HTTP 请求能力和 BeautifulSoup 的 HTML 解析能力,提供了直观的 API,让我们可以像人类用户一样浏览网页、填写表单和提交请求。 1.2 主要功能特点…...

多云管理“拦路虎”:深入解析网络互联、身份同步与成本可视化的技术复杂度​

一、引言&#xff1a;多云环境的技术复杂性本质​​ 企业采用多云策略已从技术选型升维至生存刚需。当业务系统分散部署在多个云平台时&#xff0c;​​基础设施的技术债呈现指数级积累​​。网络连接、身份认证、成本管理这三大核心挑战相互嵌套&#xff1a;跨云网络构建数据…...

大话软工笔记—需求分析概述

需求分析&#xff0c;就是要对需求调研收集到的资料信息逐个地进行拆分、研究&#xff0c;从大量的不确定“需求”中确定出哪些需求最终要转换为确定的“功能需求”。 需求分析的作用非常重要&#xff0c;后续设计的依据主要来自于需求分析的成果&#xff0c;包括: 项目的目的…...

爬虫基础学习day2

# 爬虫设计领域 工商&#xff1a;企查查、天眼查短视频&#xff1a;抖音、快手、西瓜 ---> 飞瓜电商&#xff1a;京东、淘宝、聚美优品、亚马逊 ---> 分析店铺经营决策标题、排名航空&#xff1a;抓取所有航空公司价格 ---> 去哪儿自媒体&#xff1a;采集自媒体数据进…...

DeepSeek 技术赋能无人农场协同作业:用 AI 重构农田管理 “神经网”

目录 一、引言二、DeepSeek 技术大揭秘2.1 核心架构解析2.2 关键技术剖析 三、智能农业无人农场协同作业现状3.1 发展现状概述3.2 协同作业模式介绍 四、DeepSeek 的 “农场奇妙游”4.1 数据处理与分析4.2 作物生长监测与预测4.3 病虫害防治4.4 农机协同作业调度 五、实际案例大…...

深度学习水论文:mamba+图像增强

&#x1f9c0;当前视觉领域对高效长序列建模需求激增&#xff0c;对Mamba图像增强这方向的研究自然也逐渐火热。原因在于其高效长程建模&#xff0c;以及动态计算优势&#xff0c;在图像质量提升和细节恢复方面有难以替代的作用。 &#x1f9c0;因此短时间内&#xff0c;就有不…...

Webpack性能优化:构建速度与体积优化策略

一、构建速度优化 1、​​升级Webpack和Node.js​​ ​​优化效果​​&#xff1a;Webpack 4比Webpack 3构建时间降低60%-98%。​​原因​​&#xff1a; V8引擎优化&#xff08;for of替代forEach、Map/Set替代Object&#xff09;。默认使用更快的md4哈希算法。AST直接从Loa…...

day36-多路IO复用

一、基本概念 &#xff08;服务器多客户端模型&#xff09; 定义&#xff1a;单线程或单进程同时监测若干个文件描述符是否可以执行IO操作的能力 作用&#xff1a;应用程序通常需要处理来自多条事件流中的事件&#xff0c;比如我现在用的电脑&#xff0c;需要同时处理键盘鼠标…...

【LeetCode】3309. 连接二进制表示可形成的最大数值(递归|回溯|位运算)

LeetCode 3309. 连接二进制表示可形成的最大数值&#xff08;中等&#xff09; 题目描述解题思路Java代码 题目描述 题目链接&#xff1a;LeetCode 3309. 连接二进制表示可形成的最大数值&#xff08;中等&#xff09; 给你一个长度为 3 的整数数组 nums。 现以某种顺序 连接…...

uniapp 小程序 学习(一)

利用Hbuilder 创建项目 运行到内置浏览器看效果 下载微信小程序 安装到Hbuilder 下载地址 &#xff1a;开发者工具默认安装 设置服务端口号 在Hbuilder中设置微信小程序 配置 找到运行设置&#xff0c;将微信开发者工具放入到Hbuilder中&#xff0c; 打开后出现 如下 bug 解…...