注:本文为 “PyMuPDF” 相关文章合辑。
MuPDF 简介
MuPDF 是一款以 C 语言编写的自由及开放源代码软件库,是 PDF 和 XPS 解析和渲染引擎。主要用以渲染页面为位图,但也提供对其他操作诸如搜索和列举目录和链接的支持。
MuPDF 注重速度、代码轻量及高质量反锯齿渲染。自 1.2 版起,MuPDF 拥有对交互式特性的支持,如填写表单、JavaScript 和转换。
本库附带 X11 和 Windows 的基础的查看器,以及一套用于批处理(pdfdraw)、测试文件结构(pdfshow)和重写文件(pdfclean)的命令行工具。
许多自由应用软件用 MuPDF 渲染 PDF 文档,最有名的是 Sumatra PDF。还可在 Debian、Fedora、Archlinux、FreeBSD Ports 及 OpenBSD Ports 作为软件包使用。
该库被一些独立组织移植到多个平台,包括 Amazon Kindle、HP Touchpad、PlayStation Portable、Wii 和 DOS。
2002 年 Tor Andersson 开始以 Raph Levien 的 Libart 渲染库为基础开始编写 MuPDF。Artifex Software 获取 MuPDF 项目后,软件开发集中于编写名为 Fitz 的新的现代图形库。Fitz 起初用于 R&D 计划以替代陈旧的 Ghostscript 图形库,但相反却成为 MuPDF 的渲染引擎。
2005 年,含有新 Fitz 库的 MuPDF 第 1 版发布。
2009 年,Artifex Software 发起侵权诉讼反对 Palm 公司把 MuPDF 加入 webOS 时违背 GPL,声称 GPL 只允许 “非商业使用”。 Artifex 于 2011 年自动退回了诉讼。
2011 年,添加对微软 XPS 的支持,基于出自 GhostXPS 库的代码。
自 1.2 版本起,许可协议由 GNU 通用公共许可证改为 Affero 通用公共许可证。
一、修复 pip 安装
存在 Python2、 Python3 多版本的系统中需要指定版本环境,下文以系统中只有 Python3 为例
1、在 Python 3 ≥ 3.4 中
Microsoft Windows [版本 10.0.19045.2728]
(c) Microsoft Corporation。保留所有权利。
C:\WINDOWS\system32>python -m ensurepip
Installing collected packages: pip
Successfully installed pip-22.3
ensurepip 包支持将 pip 安装程序引导到现有的 Python 安装或虚拟环境中。
2、在 Python 3 ≤ 3.3 中
下载 get-pip.py 脚本 https://bootstrap.pypa.io/get-pip.py
打开终端命令提示符,到该文件的所在目录中运行:
python get-pip.py
get-pip.py 也可以用于Python 3 ≥ 3.4 中。
修复 pip 使用 ensurepip 与 get-pip.py 的区别
-
ensurepip Python 3.4 及以上版本自带的一个模块,可以自动检测系统中是否存在 pip,如果不存在则会自动下载并安装。而 get-pip.py 需要先手动下载脚本文件,后运行该脚本文件来安装 pip。
-
ensurepip 和 get-pip.py 都可以安装当前 Python 版本默认的 pip 版本,但 ensurepip 不能直接安装特定的 pip 版本,而 get-pip.py 可以直接安装特定的 pip 版本。
使用以下命令来安装特定版本的 pip:
python get-pip.py==<version>
查看 pip 所对应的 Python 版本及所关联的Python解释器的路径
pip -V、pip --version、python -m pip -V、python -m pip --version 都可以查看版本
Microsoft Windows [版本 10.0.19045.2728]
(c) Microsoft Corporation。保留所有权利。
C:\WINDOWS\system32>pip -V
pip 22.3 from C:\Python311\Lib\site-packages\pip (python 3.12)
或 pip show pip 显示详细信息
C:\WINDOWS\system32>pip show pip
Name: pip
Version: 22.3
Summary: The PyPA recommended tool for installing Python packages.
Home-page: https://pip.pypa.io/
Author: The pip developers
Author-email: distutils-sig@python.org
License: MIT
Location: C:\Python311\Lib\site-packages
Requires:
Required-by:
更新 pip
pip install pip -U
如果直接更新 pip 不起作用,使用下列命令
C:\WINDOWS\system32>python -m pip install --upgrade pip -i https://pypi.tuna.tsinghua.edu.cn/simple
Requirement already satisfied: pip in c:\python311\lib\site-packages (22.3)
Collecting pip
Using cached pip-23.0.1-py3-none-any.whl (2.1 MB)
Installing collected packages: pip
Attempting uninstall: pip
Found existing installation: pip 22.3
Uninstalling pip-22.3:
Successfully uninstalled pip-22.3
Successfully installed pip-23.0.1
二、pip 源加速的两种方法
1、临时使用(pip 版本 ≥ 1.3)
pip install <package> -i https://pypi.tuna.tsinghua.edu.cn/simple
2、默认使用(pip 版本 ≥ 10.0,10.0 版本之前设置全局源 URL 需要手动编辑 pip 配置文件 )
pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple
设置默认源 URL 后,安装包无需再带源 URL 直接使用
pip install <package>
三、PyMuPDF 安装
方法 1:使用 pip 安装 PyMuPDF
C:\WINDOWS\system32>pip install PyMuPDF -i https://pypi.tuna.tsinghua.edu.cn/simple
Looking in indexes: https://pypi.tuna.tsinghua.edu.cn/simple
Collecting PyMuPDF
Downloading https://pypi.tuna.tsinghua.edu.cn/packages/aa/c0/2a7bfe118a5c965aa358da95fe77872d8d4fabadd14ed5f46a55b6ce7cc6/PyMuPDF-1.21.1-cp311-cp311-win_amd64.whl (11.7 MB)
---------------------------------------- 11.7/11.7 MB 203.3 kB/s eta 0:00:00
Installing collected packages: PyMuPDF
Successfully installed PyMuPDF-1.21.1
- 安装指定版本
pip install PyMuPDF==<版本号>
查看 PyMuPDF,正常
pip show PyMuPDF
查看 fitz ,正常
C:\WINDOWS\system32>python
Python 3.11.0 (main, Oct 24 2022, 18:26:48) [MSC v.1933 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> help("fitz")
Help on package fitz:
NAME
fitz
DESCRIPTION
...
导入 fitz ,正常
C:\WINDOWS\system32>python
Python 3.11.0 (main, Oct 24 2022, 18:26:48) [MSC v.1933 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import fitz
方法 2: 使用 pip 安装 fitz
C:\WINDOWS\system32>pip install fitz -i https://pypi.tuna.tsinghua.edu.cn/simple
Looking in indexes: https://pypi.tuna.tsinghua.edu.cn/simple
Collecting fitz
Downloading
--------------------------------------
Installing collected packages: fitz
Successfully installed fitz-0.0.1.dev2
查看 fitz ,正常
C:\WINDOWS\system32>pip list
Package Version
------------------ ----------
fitz 0.0.1.dev2
两种方法安装 fitz 的区别
-
无论是通过安装 PyMuPDF 安装 fitz,还是直接用 pip 安装 fitz,都可以正常使用 fitz 功能。
-
使用 pip 安装 fitz 只安装 fitz 本身,而不包括 PyMuPDF 模块的其他功能。而安装 PyMuPDF 模块会自动安装 fitz ,可以使用 PyMuPDF 模块包括 fitz 的所有功能。
-
如果用 pip uninstall 卸载 pip 安装的 fitz,会把 PyMuPDF 包组件中 fitz 一并删除,实际上两种安装方式 fitz 都在同一目录。
fitz 更新
pip install --upgrade PyMuPDF
如果 --upgrade
不起作用,也可以通过--force-reinstall
重装 PyMuPDF 来完成
C:\WINDOWS\system32>pip install --force-reinstall PyMuPDF -i https://pypi.tuna.tsinghua.edu.cn/simple
Looking in indexes: https://pypi.tuna.tsinghua.edu.cn/simple
Collecting PyMuPDF
Using cached https://pypi.tuna.tsinghua.edu.cn/packages/aa/c0/2a7bfe118a5c965aa358da95fe77872d8d4fabadd14ed5f46a55b6ce7cc6/PyMuPDF-1.21.1-cp311-cp311-win_amd64.whl (11.7 MB)
Installing collected packages: PyMuPDF
Attempting uninstall: PyMuPDF
Found existing installation: PyMuPDF 1.21.1
Uninstalling PyMuPDF-1.21.1:
Successfully uninstalled PyMuPDF-1.21.1
Successfully installed PyMuPDF-1.21.1
再次查看 fitz,正常
C:\WINDOWS\system32>python
Python 3.11.0 (main, Oct 24 2022, 18:26:48) [MSC v.1933 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> help("fitz")
Help on package fitz:
NAME
fitz
DESCRIPTION
...
四、PyMuPDF 使用文档
-
PyMuPDF Documentation
https://buildmedia.readthedocs.org/media/pdf/pymupdf/latest/pymupdf.pdf -
Tutorial - PyMuPDF documentation
https://pymupdf.readthedocs.io/en/latest/tutorial.html
Python3 处理 PDF 之 PyMuPDF 入门
在奋斗的大道已于 2023-08-06 10:32:43 修改
PyMuPDF 简介
PyMuPDF 是一个用于处理 PDF 文件的 Python 库,它提供了丰富的功能来操作、分析和转换 PDF 文档。这个库的设计目标是提供一个简单易用的 API, 使得开发者能够轻松地在 Python 程序中实现 PDF 文件的各种操作。
PyMuPDF 的主要特点如下:
- 跨平台兼容性:PyMuPDF 支持多种操作系统,如 Windows、macOS 和 Linux, 可以在这些平台上运行 Python 程序。
- 强大的 PDF 处理能力:PyMuPDF 提供了丰富的功能来操作 PDF 文件,如读取、写入、分割、合并、旋转、裁剪等。此外,它还支持加密和解密 PDF 文档,以及提取文本、图像和元数据等信息。
- 易于使用:PyMuPDF 的 API 设计简洁明了,易于学习和使用。开发者可以通过简单的函数调用来实现各种 PDF 操作,而无需深入了解底层细节。
PyMuPDF 安装及其依赖第三方框架
pip 安装 PyMuPDF 模块
pip install pymupdf
验证pymupdf 模块是否安装成功
import fitz
import PIL
# 打印pymupdf模块:基本信息
from fitz import TextPage
print(fitz.__doc__)
PyMuPDF 1.22.5: Python bindings for the MuPDF 1.22.2 library.
Version date: 2023-06-21 00:00:01.
Built for Python 3.10 on win32 (64-bit).
PyMuPDF 依赖第三方框架
当使用Pixmap.pil_save()
和 Pixmap.pil_tobytes() 需要
Pillow模块
当使用Document.subset_fonts()
时需要 FontTools模块
PyMuPDF 核心类
在PyMuPDF 核心类演示涉及类
其他未使用到的其他类:Archive(档案)、Colorspace(色彩空间对象)、DisplayList(显示列表对象)、DocumentWriter(文档编辑对象)、Identity(身份对象)、 IRect(长方形对象)、linkDest(连接目的对象)、Matrix(矩阵对象)、Outline(大纲)、Quad(四边形对象)、Shape(形状对象)、 Story(章节对象)、TextPage(文本页面对象)、TextWriter(文本写入对象)、Tools(工具类)、Xml(xml 文档对象)
PyMuPDF 核心类演示
加载PDF文件
# 加载pdf 文件
doc = fitz.open("E:\doc\opencv 4.1中文官方文档v1.1版.pdf")
获取Document 属性和方法
# 获取Document 文档对象的属性和方法
# 1、获取pdf 页数
pageCount = doc.page_count
print("pdf 页数", pageCount)
# 2、获取pdf 元数据
metaData = doc.metadata
print("pdf 元数据:", metaData)
# 3、获取pdf 目录信息
toc = doc.get_toc()
print("pdf 目录:", toc)
Page 属性和方法
通过Page 对象实现以下功能:
• 您可以将页面呈现为光栅或矢量(SVG
)图像,可以选择缩放、旋转、移动或剪切页面。
• 您可以提取多种格式的页面文本和图像,并搜索文本字符串。
Page 加载方法
page = doc.load_page(pno) # loads page number 'pno' of the document (0-based)
page = doc[pno] # the short form
Documnet 迭代器加载Page 方法
for page in doc:
# do something with 'page'
# ... or read backwards
for page in reversed(doc):
# do something with 'page'
# ... or even use 'slicing'
for page in doc.pages(start, stop, step):
# do something with 'page'
# 获取Page 页面对象的属性和方法
page = doc.load_page(1) # 默认加载第一页
print("page 对象:", page)
检查页面的链接、批注或表单字段
# 1、获取Page 页面的链接、批注或表单字段
links = page.get_links()
for link in links:
# 涉及Link 对象
print("链接:", link)
annots = page.annots()
for annot in annots:
# 涉及Annot 对象
print("批注:", annot)
widgets = page.widgets()
for widget in widgets:
# 涉及表单字段
print("表单字段:", widget)
页面展示/页面图像保存到文件中
# 2、Page 页面-光栅图像
pix = page.get_pixmap()
print("打印页面图像对象:", pix)
# 保存光栅图像图像,需要依赖第三方框架:Pillow
pix.pil_save("page-%i.png" % page.number)
Page.get_pixmap()
提供了许多用于控制图像的变体:分辨率、颜色空间(例如,生成灰度图像或具有减色方案的图像)、透明度、旋转、镜像、移位、剪切等。
Pixmap
包含以下引用的许多方法和属性。其中包括整数宽度、高度(每个像素)和跨距(一个水平图像行的字节数)。属性示例表示表示图像数据的矩形字节区域(Python字节对象)。
温馨提示:page.get_svg_image()
创建页面的矢量图像。
提取文本和图像
# 3、Page 获取文本\图像\其他信息
# 温馨提示:涉及TextPage 常量类型定义
text = page.get_text("text")
print("指定页面文本内容:", text)
对opt
使用以下字符串之一以获取不同的格式:
"text"
:(默认)带换行符的纯文本。无格式、无文字位置详细信息、无图像-"blocks"
:生成文本块(段落)的列表-"words"
:生成单词列表(不包含空格的字符串)-"html"
:创建页面的完整视觉版本,包括任何图像。这可以通过internet浏览器显示-"dict"/"json"
:与HTML
相同的信息级别,但作为Python字典或resp.JSON
字符串。-"rawdict"/"rawjson"
:"dict"/"json"
的超级集合。它还提供诸如XML
之类的字符详细信息。-"xhtml"
:文本信息级别与文本版本相同,但包含图像。-"xml"
:不包含图像,但包含每个文本字符的完整位置和字体信息。使用XML
模块进行解释。
搜索文本
# 4、Page 文本检索
search = page.search_for("图像的基本操作")
print("打印检索文本的位置:", search)
提供一个矩形列表,每个矩形都包含一个字符串“mupdf”
(不区分大小写)。
PDF操作
PDF
是唯一可以使用PyMuPDF
修改的文档类型。其他文件类型是只读的。但是,您可以将任何文档(包括图像)转换为PDF,然后将所有PyMuPDF
功能应用于转换果,Document.convert_to_pdf()
。
Document.save()
始终将PDF以其当前(可能已修改)状态存储在磁盘上。
通常,您可以选择是保存到新文件,还是仅将修改附加到现有文件(“增量保存”),这通常要快得多。
# Document 操作PDF页面
# 1、PDF 页面删除
# doc.delete_page(1)
# 1、PDF 页面拷贝和移动
doc.copy_page(1) # 第一页移动最后一页,温馨提示:移动的页面还在元PDF 文件中。
# 1、 PDF 插入页面, 返回插入页面对象
new_page = doc.new_page(pno=-1, width=595, height=842)
# 插入页面, 设置文本
text = "你的文本"
point = fitz.Point(50, 50) # 这是一个下x,y 二维坐标系,在这个区域内插入你的文本
new_page.insert_text(point, text, fontsize=20)
# 2、Document 保存
doc.save("opencv pdf文件调整.pdf")
# 3、Documemt 销毁
doc.close()
PDF 删除方法
Document.delete_page()
Document.delete_pages()
PDF移动拷贝方法
Document.copy_page()
Document.fullcopy_page()
Document.move_page()
PDF插入Page 方法
Document.insert_page()
Document.new_page()
PyMuPDF 核心功能模块封装
PDF 分割
每一页单独保存为一个pdf
def split_per_page(input, output):
if not os.path.exists(output):
os.makedirs(output)
doc = fitz.open(input)
for page in range(doc.page_count):
dst_doc = fitz.open()
dst_doc.insert_pdf(doc,from_page=page,to_page=page)
dst_doc.save(os.path.join(output,f'{page}.pdf'))
dst_doc.close()
doc.close()
# 把每一个页面保存为一个pdf,并保存在test文件夹中
split_per_page("test.pdf","test")
范围内的页面保存为pdf
def split_range_page(input, output, range):
if not os.path.exists(output):
os.makedirs(output)
doc = fitz.open(input)
start = range[0] - 1
end = range[1] - 1
dst_doc = fitz.open()
dst_doc.insert_pdf(doc, from_page=start, to_page=end)
dst_doc.save(os.path.join(output,'range_page.pdf'))
dst_doc.close()
doc.close()
# 把1-10也保存为pdf,保存在test文件夹中
split_range_page('test.pdf','test', [1,10])
任意的页面保存为pdf
def split_selected_page(input, output, pages):
if not os.path.exists(output):
os.makedirs(output)
doc = fitz.open(input)
result = map(lambda x: x - 1, pages)
doc.select(list(result))
doc.save(os.path.join(output,'selected_pages.pdf'))
doc.close()
# 把第一、三、八页面保存为pdf,并保存在test文件夹中
split_selected_page('test.pdf','test',[1,3, 8])
PDF 合并
import fitz
doc_a = fitz.open("a.pdf") # open the 1st document
doc_b = fitz.open("b.pdf") # open the 2nd document
doc_a.insert_pdf(doc_b) # merge the docs
doc_a.save("a+b.pdf") # save the merged document with a new filename
# 把b.pdf合并到a.pdf,保存为a+b.pdf
PDF 中的图片提取
import fitz
doc = fitz.open("test.pdf") # open a document
for page_index in range(len(doc)): # iterate over pdf pages
page = doc[page_index] # get the page
image_list = page.get_images()
# print the number of images found on the page
if image_list:
print(f"Found {len(image_list)} images on page {page_index}")
else:
print("No images found on page", page_index)
for image_index, img in enumerate(image_list, start=1): # enumerate the image list
xref = img[0] # get the XREF of the image
pix = fitz.Pixmap(doc, xref) # create a Pixmap
if pix.n - pix.alpha > 3: # CMYK: convert to RGB first
pix = fitz.Pixmap(fitz.csRGB, pix)
pix.save("page_%s-image_%s.png" % (page_index, image_index)) # save the image as png
pix = None
PDF 保存为图片
def covert2pic(zoom):
doc = fitz.open("test.pdf")
total = doc.page_count
for pg in range(total):
page = doc[pg]
zoom = int(zoom) #值越大,分辨率越高,文件越清晰
rotate = int(0)
trans = fitz.Matrix(zoom / 100.0, zoom / 100.0).prerotate(rotate)
pm = page.get_pixmap(matrix=trans, alpha=False)
lurl='.pdf/%s.jpg' % str(pg+1)
pm.save(lurl)
doc.close()
covert2pic(200)
PDF 添加水印
def add_watermark(input, watermark):
doc = fitz.open(input)
for page in doc:
page.insert_image(page.bound(),filename=watermark, overlay=False)
doc.save(os.path.join("test","watermark.pdf"))
doc.close()
add_watermark("test.pdf","watermark.png")
PDF 加密
PDF加密有两种形式
- 用户加密,需要输入密码才能打开pdf
- 拥有者加密,可以防止打印、复制、添加注释、添加删除页面等功能
def encrypt_pdf():
perm = int(
fitz.PDF_PERM_ACCESSIBILITY # always use this
| fitz.PDF_PERM_PRINT # permit printing
| fitz.PDF_PERM_COPY # permit copying
| fitz.PDF_PERM_ANNOTATE # permit annotations
) # 可以打印,复制,添加注释
owner_pass = "owner" # owner password
user_pass = "user" # user password
encrypt_meth = fitz.PDF_ENCRYPT_AES_256 # strongest algorithm
doc = fitz.open("test.pdf") # empty pdf
doc.save("encrypt.pdf",encryption=encrypt_meth,owner_pw=owner_pass,permissions=perm,user_pw=user_pass) # 同时使用
# 这两个加密方式可以,单独使用,也可以同时使用
# 单独使用用户加密
doc.save("encrypt.pdf",encryption=encrypt_meth,owner_pw=owner_pass)
PyMuPDF 在PyQT5 运用
功能要求:在PyQT-5 展示pdf 文件.
效果展示:
PyQT-5 UI效果展示和源文件
pdfshow.ui
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Form</class>
<widget class="QWidget" name="Form">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>300</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<widget class="QLabel" name="label">
<property name="geometry">
<rect>
<x>130</x>
<y>70</y>
<width>54</width>
<height>12</height>
</rect>
</property>
<property name="text">
<string>PDF展示</string>
</property>
</widget>
</widget>
<resources/>
<connections/>
</ui>
pdfshow.py 源码
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'pdfshow.ui'
#
# Created by: PyQt5 UI code generator 5.15.9
#
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again. Do not edit this file unless you know what you are doing.
import sys
from PyQt5 import QtCore, QtWidgets
from PyQt5.QtGui import QImage, QPixmap, QTransform
from PyQt5.QtWidgets import QWidget, QApplication
# 添加PDF 文件操作依赖
import fitz
class Ui_Form(QWidget):
def __init__(self):
super().__init__()
self.label = None
self.setupUi()
self.image()
def setupUi(self):
self.setObjectName("Form")
self.resize(400, 300)
self.label = QtWidgets.QLabel(self)
self.label.setGeometry(QtCore.QRect(130, 70, 54, 12))
self.label.setObjectName("label")
self.retranslateUi()
QtCore.QMetaObject.connectSlotsByName(self)
def retranslateUi(self):
_translate = QtCore.QCoreApplication.translate
self.setWindowTitle(_translate("Form", "Form"))
self.label.setText(_translate("Form", "PDF展示"))
def image(self):
file = "E:\doc\opencv 4.1中文官方文档v1.1版.pdf"
# 打开文件
doc = fitz.open(file)
# 读取一页 0代表第1页
page_one = doc.load_page(1)
# 将第一页转换为Pixmap
page_pixmap = page_one.get_pixmap()
# 将Pixmap转换为QImage
image_format = QImage.Format_RGBA8888 if page_pixmap.alpha else QImage.Format_RGB888
page_image = QImage(page_pixmap.samples, page_pixmap.width,
page_pixmap.height, page_pixmap.stride, image_format)
width = page_image.width()
height = page_image.height()
# QImage 转为QPixmap
pix = QPixmap.fromImage(page_image)
trans = QTransform()
trans.rotate(90) # 这里设置旋转角度
new = pix.transformed(trans)
# 设置标签宽和高
self.label.setFixedSize(400, 350)
# 设置图片大小自适应标签
self.label.setScaledContents(True)
# 给标签设置图像
self.label.setPixmap(new)
if __name__ == '__main__':
app = QApplication(sys.argv)
w = Ui_Form()
w.show()
sys.exit(app.exec_())
解决思路
- 使用PyMuPDF模块打开文件。
- 读取第一页pdf文件第一页。
- 从第一页获取图像,是Pixmap类。
- 使用PyQt5的QImage将上面的Pixmap转换为QImage。
- 将QImage转换为QPixmap。
- 将QPixmap设置给Label。
PyMuPDF 预览PDF 文件
UI 原型设计:
Python 源码
ImageListWidget:自定义QListWidget 列表组件,仅仅展示图片模式
# _*_ coding : UTF-8_*_
# 开发者 : zhuozhiwengang
# 开发时间 : 2023/8/6 0:54
# 文件名称 : ImageListWidget
# 开发工具 : PyCharm
import os
from PyQt5.QtCore import QSize
from PyQt5.QtGui import QIcon
from PyQt5.QtWidgets import QListWidget, QListWidgetItem, QListView, QWidget, QApplication, QHBoxLayout, QLabel, \
QVBoxLayout
class ImageListWidget(QListWidget):
def __init__(self):
super(ImageListWidget, self).__init__()
self.setFlow(QListView.Flow(1))#0: left to right,1: top to bottom
self.setIconSize(QSize(150, 100))
# 设置控件的列表视图模式为IconMode
self.setViewMode(QListWidget.IconMode)
# 设置垂直布局
self.setLayout(QVBoxLayout())
def add_image_items(self, image_paths=[]):
for i in range(len(image_paths)):
# 创建缩略图图标
icon = QIcon()
icon.addPixmap(image_paths[i], QIcon.Normal, QIcon.Off)
# 创建QListWidgetItem对象,并设置图标和它的描述文字
item = QListWidgetItem(icon, str(i))
# 把item添加到listWidget中
self.addItem(item)
ImageViewerWidget:自定义PDF预览组件
# _*_ coding : UTF-8_*_
# 开发者 : zhuozhiwengang
# 开发时间 : 2023/8/6 0:55
# 文件名称 : ImageViewerWidget
# 开发工具 : PyCharm
import fitz
from PyQt5.QtGui import QPixmap, QImage
from PyQt5.QtWidgets import QWidget, QLabel, QHBoxLayout, QApplication, QVBoxLayout
from ImageListWidget import ImageListWidget
class ImageViewerWidget(QWidget):
def __init__(self):
super(QWidget, self).__init__()
# 显示控件
self.list_widget = ImageListWidget()
self.list_widget.setMinimumWidth(200)
self.show_label = QLabel(self)
self.show_label.setFixedSize(600, 400)
self.image_paths = []
self.currentImgIdx = 0
self.currentImg = None
# 水平布局
self.layout = QVBoxLayout(self)
self.layout.addWidget(self.show_label)
self.layout.addWidget(self.list_widget)
# 信号与连接
self.list_widget.itemSelectionChanged.connect(self.loadImage)
def load_from_paths(self, img_paths=[]):
self.image_paths = img_paths
self.list_widget.add_image_items(img_paths)
def loadImage(self):
self.currentImgIdx = self.list_widget.currentIndex().row()
if self.currentImgIdx in range(len(self.image_paths)):
self.currentImg = QPixmap(self.image_paths[self.currentImgIdx]).scaledToHeight(400)
self.show_label.setPixmap(self.currentImg)
if __name__ == "__main__":
import sys
app = QApplication(sys.argv)
# 图像路径
file = "E:\doc\opencv 4.1中文官方文档v1.1版.pdf"
# 打开文件
doc = fitz.open(file)
img_paths = []
for i in range(0, doc.page_count):
# 读取一页 0代表第1页
page = doc.load_page(i)
# 将第一页转换为Pixmap
page_pixmap = page.get_pixmap()
# 将Pixmap转换为QImage
image_format = QImage.Format_RGBA8888 if page_pixmap.alpha else QImage.Format_RGB888
page_image = QImage(page_pixmap.samples, page_pixmap.width,
page_pixmap.height, page_pixmap.stride, image_format)
width = page_image.width()
height = page_image.height()
# QImage 转为QPixmap
pix = QPixmap.fromImage(page_image)
img_paths.append(pix)
# 显示控件
main_widget = ImageViewerWidget()
main_widget.load_from_paths(img_paths)
main_widget.setWindowTitle("ImageViewer")
main_widget.show()
# 应用程序运行
sys.exit(app.exec_())
Python 效果展示
PDF 预览组件基本实现,但是没有进行细化,此代码仅供参考。
【PyMuPDF和pdf2image】Python将PDF转成图片
软测小生 已于 2023-04-30 20:44:53 修改
2023-04-30更新 PyMuPDF=1.21.1
import datetime
# PyMuPDF=1.21.1
import fitz
def pdf2img(pdf_path, img_path):
pdfDoc = fitz.open(pdf_path)
for page in pdfDoc.pages():
# 将页面转换为图片
rotate = int(0)
# 每个尺寸的缩放系数为1.3,这将为我们生成分辨率提高2.6的图像。
# 此处若是不做设置,默认图片大小为:792X612, dpi=72
# pix = page.get_pixmap()
zoom_x = 6
zoom_y = 6
# (1.33333333-->1056x816) (2-->1584x1224) (3-->3572x2526)
# x和y的值越大越清晰,图片越大,但处理也越耗时间,这里取决于你想要图片的清晰度
# 默认为1.333333,一般日常使用3就够了,不能设置太大,太大容易使电脑死机
mat = fitz.Matrix(zoom_x, zoom_y)
pix = page.get_pixmap(matrix=mat, dpi=None, colorspace='rgb', alpha=False)
imageName = pdf_path.split("\\")[len(pdf_path.split("\\"))-1]
target_img_name = img_path + '\\%s.png' % imageName #构造图片名字
# 保存图片
pix.save(target_img_name)
if __name__ == '__main__':
startTime_pdf2img = datetime.datetime.now() # 开始时间
pdf_path = "E:\\PDF\\demo\\PDF\\test.PDF"
img_path = "E:\\PDF\\demo\\img"
pdf2img(pdf_path, img_path)
endTime_pdf2img = datetime.datetime.now() # 结束时间
print('===========END==========')
print('PDF转换图片累计耗时:【%s秒】' % (endTime_pdf2img - startTime_pdf2img).seconds)
前言:在最近的测试中遇到一个与PDF相关的测试需求,其中有一个过程是将PDF转换成图片,然后对图片进行测试。
粗略的试了好几种方式,其中语言尝试了 Python 和 Java,总体而言所找到的 Python 方式相对比 Java 更快一些,更简单一些。
下面首先分享一下Python将PDF转换成图片,Java+PDFBox将PDF转成图片
需求:我需要先将PDF转换成为PNG图片,并截取图片的一部分存储,然后作为测试目标进行测试。
操作:
1、PDF转PNG图片
2、对PNG图片进行指定区域截图,在另存到指定文件夹下
1、PyMuPDF将PDF转换成图片
pip install PyMuPDF
import sys, fitz
import os
import datetime
def pyMuPDF_fitz(pdfPath, imagePath):
startTime_pdf2img = datetime.datetime.now()#开始时间
print("imagePath="+imagePath)
pdfDoc = fitz.open(pdfPath)
for pg in range(pdfDoc.pageCount):
page = pdfDoc[pg]
rotate = int(0)
# 每个尺寸的缩放系数为1.3,这将为我们生成分辨率提高2.6的图像。
# 此处若是不做设置,默认图片大小为:792X612, dpi=72
zoom_x = 1.33333333 #(1.33333333-->1056x816) (2-->1584x1224)
zoom_y = 1.33333333
mat = fitz.Matrix(zoom_x, zoom_y).preRotate(rotate)
pix = page.getPixmap(matrix=mat, alpha=False)
if not os.path.exists(imagePath):#判断存放图片的文件夹是否存在
os.makedirs(imagePath) # 若图片文件夹不存在就创建
pix.writePNG(imagePath+'/'+'images_%s.png' % pg)#将图片写入指定的文件夹内
endTime_pdf2img = datetime.datetime.now()#结束时间
print('pdf2img时间=',(endTime_pdf2img - startTime_pdf2img).seconds)
if __name__ == "__main__":
pdfPath = '../path/demo.pdf'
imagePath = '../path/image'
pyMuPDF_fitz(pdfPath, imagePath)
PDF文档页数超过 100 页的话需要十几秒,因为先转换成一整张 1056X816 的图片,再对本地文件中的所有图片进行遍历截图,时间上比较慢,通过查看文档发现:
还可以在转换的同时指定图片的大小,对图片指定区域进行截取,这样快很多,一步到位,省去了二次截图的过程,前提使我们必须要知道想要截取哪一块区域并保存
官方示例代码如下:
#下面的这段代码就是想要从一页PDF的中心点为起点截取到右下角的区域,截取整张图的1/4.
>>> mat = fitz.Matrix(2, 2) # 在每个方向缩放因子2
>>> rect = page.rect # 页面的矩形
>>> mp = rect.tl + (rect.br - rect.tl) * 0.5 # 矩形的中心
>>> clip = fitz.Rect(mp, rect.br) # 我们想要的剪切区域
>>> pix = page.getPixmap(matrix = mat, clip = clip)
**实际用到的例子是:
整张图片导出之后是1056816,但是我想要的是这张图片最底部的部分105675,相当于PDF文档的页脚部分。
import sys, fitz
import os
import datetime
def pyMuPDF_fitz(pdfPath, imagePath):
startTime_pdf2img = datetime.datetime.now()#开始时间
pdfDoc = fitz.open(pdfPath)
for pg in range(pdfDoc.pageCount):
page = pdfDoc[pg]
rotate = int(0)
# 每个尺寸的缩放系数为1.3,这将为我们生成分辨率提高2.6的图像。
# 此处若是不做设置,默认图片大小为:792X612, dpi=72
zoom_x = 1.33333333 #(1.33333333-->1056x816) (2-->1584x1224)
zoom_y = 1.33333333
mat = fitz.Matrix(zoom_x, zoom_y).preRotate(rotate)
pix = page.getPixmap(matrix=mat, alpha=False)
if not os.path.exists(imagePath):#判断存放图片的文件夹是否存在
os.makedirs(imagePath) # 若图片文件夹不存在就创建
pix.writePNG(imagePath+'/'+'images_%s.png' % pg)#将图片写入指定的文件夹内
endTime_pdf2img = datetime.datetime.now()#结束时间
print('pdf2img时间=',(endTime_pdf2img - startTime_pdf2img).seconds)
def pyMuPDF2_fitz(pdfPath, imagePath):
pdfDoc = fitz.open(pdfPath) # open document
for pg in range(pdfDoc.pageCount): # iterate through the pages
page = pdfDoc[pg]
rotate = int(0)
# 每个尺寸的缩放系数为1.3,这将为我们生成分辨率提高2.6的图像
# 此处若是不做设置,默认图片大小为:792X612, dpi=72
zoom_x = 1.33333333 #(1.33333333-->1056x816) (2-->1584x1224)
zoom_y = 1.33333333
mat = fitz.Matrix(zoom_x, zoom_y).preRotate(rotate) # 缩放系数1.3在每个维度 .preRotate(rotate)是执行一个旋转
rect = page.rect # 页面大小
mp = rect.tl + (rect.bl - (0,75/zoom_x)) # 矩形区域 56=75/1.3333
clip = fitz.Rect(mp, rect.br) # 想要截取的区域
pix = page.getPixmap(matrix=mat, alpha=False, clip=clip) # 将页面转换为图像
if not os.path.exists(imagePath):
os.makedirs(imagePath)
pix.writePNG(imagePath+'/'+'psReport_%s.png' % pg)# store image as a PNG
if __name__ == "__main__":
pdfPath = '../path/demo.pdf'
imagePath = '../path/image'
#pyMuPDF_fitz(pdfPath, imagePath)#只是转换图片
pyMuPDF2_fitz(pdfPath, imagePath)#指定想要的区域转换成图片
当然上面这种是综合下来最快的,另外再介绍一种方法pdf2image
2、pdf2image 将PDF转换成图片
pdf2image也是个包装器,真正的转换工具是poppler
GitHub地址:GitHub - Belval/pdf2image: A python module that wraps the pdftoppm utility to convert PDF to PIL Image object ,上面也有相关的配置说明。
1、安装 pdf2image: pip install pdf2image
2、Windows安装配置poppler(这里只介绍Windows,Mac和Linux去上面Github地址里面参考官网)
Windows用户必须为Windows安装 poppler (Poppler for Windows),然后将bin/文件夹添加到PATH**(开始>输入env>编辑系统环境变量>环境变量…>系统变量>Path)**
注意:这里配置之后需要重启一下电脑才会生效,不然会报如下错误:
ERROE:FileNotFoundError: [WinError 2] The system cannot find the file specified
During handling of the above exception, another exception occurred:
3、pip install pillow (如果你还没有安装过的话)
from pdf2image import convert_from_path,convert_from_bytes
import tempfile
from pdf2image.exceptions import (
PDFInfoNotInstalledError,
PDFPageCountError,
PDFSyntaxError
)
def pdf2image2(pdfPath, imagePath, pageNum):
#方法一:
#convert_from_path('a.pdf', dpi=500, "output",fmt="JPEG",output_file="ok",thread_count=4)
#这会将a.pdf转换成在output文件夹下形如ok_线程id-页码.jpg的一些文件。
#若不指定thread_count则默认为1,并且在文件名中显示id. 这种转换是直接写入到磁盘上的,因此不会占用太多内存。
#下面的写法直接写入到内存,默认是C:\Users\pppp\AppData\Local\Temp\生成的uuid4名字
images = convert_from_path(pdfPath, dpi=72)
for image in images:
if not os.path.exists(imagePath):
os.makedirs(imagePath)
image.save(imagePath+'/'+'psReport_%s.png' % images.index(image), 'PNG')
#方法二:
images = convert_from_bytes(open('/home/belval/example.pdf', 'rb').read())
for image in images:
if not os.path.exists(imagePath):
os.makedirs(imagePath)
image.save(imagePath+'/'+'psReport_%s.png' % images.index(image), 'PNG')
#方法三,也是最推荐的方法
with tempfile.TemporaryDirectory() as path:
images_from_path = convert_from_path(pdfPath, output_folder=path, dpi=72)
for image in images_from_path:
if not os.path.exists(imagePath):
os.makedirs(imagePath)
image.save(imagePath+'/'+'psReport_%s.png' % images_from_path.index(image), 'PNG')
print(images_from_path)
以下是参数定义:
convert_from_path(pdf_path, dpi=200, output_folder=None, first_page=None, last_page=None, fmt='ppm', thread_count=1, userpw=None, use_cropbox=False, strict=False, transparent=False, single_file=False, output_file=str(uuid.uuid4()), poppler_path=None)
convert_from_bytes(pdf_file, dpi=200, output_folder=None, first_page=None, last_page=None, fmt='ppm', thread_count=1, userpw=None, use_cropbox=False, strict=False, transparent=False, single_file=False, output_file=str(uuid.uuid4()), poppler_path=None)
pdf_path --> 要转换的PDF文档路径
dpi --> DPI中的图像质量(默认为200),Windows默认为96dpi
output_folder --> 将生成的图像写入文件夹(而不是直接写入内存)若是path不做指定的话,path的默认地址是:C:\Users\pppp\AppData\Local\Temp\生成的uuid4。
first_page --> 从哪一页开始转换,默认是PDF的第一页
last_page --> 转换到哪一页,默认是PDF的最后一页
fmt --> 输出图像格式默认格式是ppm,还可以设置为png和jpeg等
thread_count --> 允许生成多少个线程进行处理,一般不超过4个线程;
userpw --> PDF的密码(若有密码的话需要添加)
use_cropbox --> 使用cropbox而不是mediabox
strict --> 参数允许您使用自定义类型PDFSyntaxError捕获pdftoppm语法错误
transparent --> 参数允许生成没有背景的图像,而不是通常的白色图像(为此需要pdftocairo)
single_file --> 使用pdftoppm / pdftocairo中的-singlefile选项
output_file --> 输出文件名是什么
poppler_path --> 查找poppler二进制文件的路径,允许用户使用poppler_path指定poppler的安装路径;默认不指定的话需要将bin添加到系统PATH
pdf2image应该也可以对指定区域进行截取,暂时还没详细研究其方法,因为已经找到更快的方法解决问题了,对比如下所示:
比较 PyMuPDF 和 pdf2image
以下是对一份75页的PDF,输出DPI=96的时间性能对比,pdf2image使用的是默认线程数,下面的对比并没有设置多线程,使用多线程会快一点,当线程数设为5的时候,速度是9秒。
PDF转换图片的方式 | pdf2image | pyMuPDF_Fitz | pyMuPDF2_Fitz (对图片进行指定区域裁剪) |
---|---|---|---|
时间(秒) | 11.88 | 4.5 | 0.9 |
依赖包 | pip install pdf2image poppler pip install pillow | pip install PyMuPDF |
可以看出使用pyMuPDF_Fitz明显快一倍多,最终选取了这种方式。
3、Wand 将 PDF 转换成图片
和pdf2image一样,wand都是包装接口(bindings),而实际进行转换的工具是ImageMagick.
Wind官网:
Wand — Wand 0.5.6
ImageMagick:
ImageMagick – Download
from wand.image import Image
filename="somefile.pdf"
with(Image(filename=filename, resolution=120)) as source:
images = source.sequence
pages = len(images)
for i in range(pages):
n = i + 1
newfilename = filename[:-4] + str(n) + '.jpeg'
Image(images[i]).save(filename=newfilename)
由于问题已经解决,而且性能也还不错,就没有具体去研究Wind这种方式了,感兴趣的可以去看看。
Python图片裁剪的两种方式——Pillow和OpenCV
原创 软测小生 2019年08月04日 10:30
在这篇文章里我们聊一下Python实现图片裁剪的两种方式,一种利用了Pillow,还有一种利用了OpenCV。
两种方式都需要简单的几行代码,这可能也就是现在Python那么流行的原因吧。
OpenCV对图片进行裁剪
首先,我们有一张原始图片,如下图所示:
然后,我们利用OpenCV对其进行裁剪,代码如下所示:-
import cv2
img = cv2.imread("./data/cut/thor.jpg")
print(img.shape) # (1080, 1920, 3)
cropped = img[0:128, 0:512] # 裁剪坐标为[y0:y1, x0:x1]
cv2.imwrite("./data/cut/cv_cut_thor.jpg", cropped)
#上面是正向,即从左上角开始截图,下面的是从右下角开始反向截图
import cv2
img = cv2.imread("./data/cut/thor.jpg")
cropped = img[-128:-1, 0:512]
cv2.imwrite("./data/cut/leftlower_cv_cut.jpg", cropped)
这里,我们先用imread方法读取待裁剪的图片,然后查看它的shape,shape的输出是(1080, 1920, 3),输出的顺序的是高度、宽度、通道数。之后我们利用数组切片的方式获取需要裁剪的图片范围。
这里需要注意的是切片给出的坐标为需要裁剪的图片在原图片上的坐标,顺序为[y0:y1, x0:x1],其中原图的左上角是坐标原点。最后我们用cv2.imwrite()方法将裁剪得到的图片保存到本地(第一个参数为图片名,第二参数为需要保存的图片),如图所示:
Pillow对图片进行裁剪
接下来,我们看一下使用Pillow如何对图片进行裁剪,代码如下所示:
from PIL import Image
img = Image.open("./data/cut/thor.jpg")
print(img.size) #(1920, 1080)
cropped = img.crop((0, 0, 512, 128)) # (left, upper, right, lower)
cropped.save("./data/cut/pil_cut_thor.jpg")
#上面是正向,即从左上角开始截图,下面的是从右下角开始反向截图
from PIL import Image
img = Image.open("./data/cut/thor.jpg")
_width, _height = img.size
cropped = img.crop((0, _height-128, 512, _height)) # (left, upper, right, lower)
cropped.save("./data/cut/leftlower_pil_cut.jpg")
首先我们使用open方法读取图片,然后查看它的size(这里的size和OpenCV中的shape是类似的),size的输出是(1920, 1080),也就是图片的宽度和高度。
之后我们调用crop方法来对图片进行裁剪,crop需要给定一个box参数,box是一个四元组,元组中元素的顺序是需要裁剪得到的图片在原图中的左、上、右、下坐标,即(left, upper, right, lower)。然后,我们使用save方法保存裁剪得到的图片。如下图所示,Pillow可以同样完成OpenCV裁剪图片的工作。
另外使用PIL crop截取图像这里容易报错:
AttributeError: ‘_idat’ object has no attribute ‘fileno’
During handling of the above exception, another exception occurred:
一般这样子的错误都是(left, upper, right, lower)-tuple 坐标值不对
要注意右边 (right) 和下边 (lower) 都要分别比左边(left)和上边(upper)大,否则就会报上面的错误。
其实可以理解为 矩形对角线截图,在坐标系里面,第一个点的坐标比第二个点的坐标值大。
如下图:
via:
-
Python3 处理PDF之PyMuPDF 入门-CSDN博客
https://blog.csdn.net/zhouzhiwengang/article/details/132122124 -
【PyMuPDF和pdf2image】Python将PDF转成图片-CSDN博客
https://ruancexiaosheng.blog.csdn.net/article/details/98329442 -
Python图片裁剪的两种方式——Pillow和OpenCV
https://mp.weixin.qq.com/s/jipCot9yNJNNbPqCs3h7HA