pythonpdf电子版教程:超强的解析PDF的Python包-PyMuPDF
pythonpdf电子版教程:超强的解析PDF的Python包-PyMuPDFPyMuPDF有很多实用的类和操作方法,下面我主要从以下几个方面介绍这个工具包的主要功能:MuPDF的原始渲染库称为Libart。“在Artifex Software收购MuPDF项目之后,开发重点转移到编写名为* Fitz的新现代图形库。Fitz最初是作为一个研发项目来替代老化的Ghostscript图形库,但后来成为支持MuPDF的渲染引擎。” *(引自Wikipedia)。PyMuPDF托管在Github和PyPi上,因此我们可以很方便的安装。pip install pymupdf具体安装方法参考:pymupdf.readthedocs.io/en/latest/installation.html该库的标准Python import语句是import fitz。这有历史原因:
它用来干什么的?PyMuPDF是MuPDF的Python绑定-“轻量级PDF和XPS查看器”。
MuPDF可以访问PDF,XPS,openXPS,CBZ(漫画书档案),FB2和EPUB(电子书)格式的文件。可以是扩展名为.pdf,.xps,.oxps,.cbz,.fb2 或.epub的文件(因此您可以使用Python开发电子书查看器……)。因此它不仅仅是针对PDF解析来,可以做很多事....
支持的操作系统是哪些?PyMuPDF可以在Mac,Linux,Windows XP SP2及更高版本上运行,Python 2.7到Python 3.7(请注意,Python仅支持v3.4以下的Windows XP),32位和64位版本上运行并经过测试。只要MuPDF和Python支持它们,其他平台也应该可以正常工作。
支持的操作系统还是很广泛的。
安装PyMuPDFPyMuPDF托管在Github和PyPi上,因此我们可以很方便的安装。
pip install pymupdf
具体安装方法参考:pymupdf.readthedocs.io/en/latest/installation.html
PyMuPDF的前世今生该库的标准Python import语句是import fitz。这有历史原因:
MuPDF的原始渲染库称为Libart。“在Artifex Software收购MuPDF项目之后,开发重点转移到编写名为* Fitz的新现代图形库。Fitz最初是作为一个研发项目来替代老化的Ghostscript图形库,但后来成为支持MuPDF的渲染引擎。” *(引自Wikipedia)。
下面介绍PyMuPDF一些超强的功能PyMuPDF有很多实用的类和操作方法,下面我主要从以下几个方面介绍这个工具包的主要功能:
从PDF文档制作图像,按照每一页制作成图片包。代码:
import sys fitz
fname = sys.argv[1]
doc = fitz.open(fname)
for page in doc:
pix = page.getPixmap(alpha = False)
pix.writePNG("page-%i.png" % page.number)
这段代码是可以将pymupdf支持的文档类型按照每一页转换成图片保存起来,保存的格式为png,方法异常简单。制作的图片包含名为page-0.png,page-1.png等PNG图像文件,生成的图片具有跟文档页面尺寸一致的大小。
提取PDF内的文字代码:
import sys fitz
fname = sys.argv[1]
doc = fitz.open(fname)
out = open(fname ".txt" "wb")
for page in doc:
text = page.getText().encode("utf8")
out.write(text)
out.write(bytes((12 )))
out.close()
这段代码是从指定的文件中获取文本生成一个文本文件。输出将是纯文本,因为它是在文档中编码的。不会以任何方式进行美化。专门针对PDF,这可能意味着输出结果不是按常规阅读顺序,或换行符等等。
还可以通过多种方法来提取pdf中的有关文本信息,其中包括:
- 提取HTML格式的文本并将其存储为HTML文档,以便可以在任何浏览器中查看。
- 通过Page.getText(“blocks”)将文本提取为文本块列表。此列表的每个项目均包含其文本的位置信息,可用于建立方便的阅读顺序。
- 通过Page.getText(“words”)提取单个单词的列表。它是带有位置信息的单词。使用它来确定给定矩形中包含的文本。
PyMuPDF大大扩展PDF的注释的功能,新的注释类型支持“墨水”,“橡胶印章”和“曲线”注释。墨水注释通过组合一个或多个互连点列表来模拟笔迹。印章旨在以视觉方式告知文档的状态或预期用途(例如“草稿”,“机密”等)。曲线是一个文本标记注释,它用锯齿形的线条在选定的文本下划线。
代码:
# -*- coding: utf-8 -*-
from __future__ import print_function
import gc
import os
import sys
import fitz
print(fitz.__doc__)
if fitz.VersionBind.split(".") < ["1" "17" "0"]:
sys.exit("PyMuPDF v1.17.0 is needed.")
gc.set_debug(gc.DEBUG_UNCOLLECTABLE)
highlight = "this text is highlighted"
underline = "this text is underlined"
strikeout = "this text is striked out"
squiggled = "this text is zigzag-underlined"
red = (1 0 0)
blue = (0 0 1)
gold = (1 1 0)
green = (0 1 0)
displ = fitz.rect(0 50 0 50)
r = fitz.Rect(72 72 220 100)
t1 = u"têxt üsès Lätiñ charß \nEUR: € mu: µ super scripts: ²³!"
def print_descr(annot):
annot.parent.insertText(
annot.rect.br (10 -5) "%s annotation" % annot.type[1] color=red
)
doc = fitz.open()
page = doc.newPage()
page.setRotation(0)
annot = page.addCaretAnnot(r.tl)
print_descr(annot)
r = r displ
annot = page.addFreetextAnnot(
r
t1
fontsize=10
rotate=90
text_color=blue
fill_color=gold
align=fitz.TEXT_ALIGN_CENTER
)
annot.setBorder(width=0.3 dashes=[2])
annot.update(text_color=blue fill_color=gold)
print_descr(annot)
r = annot.rect displ
annot = page.addTextAnnot(r.tl t1)
print_descr(annot)
pos = annot.rect.tl displ.tl
page.insertText(
pos
highlight
morph=(pos fitz.Matrix(-5))
)
rl = page.searchFor(highlight quads=True)
annot = page.addHighlightAnnot(rl[0])
print_descr(annot)
pos = annot.rect.bl
page.insertText(pos underline morph=(pos fitz.Matrix(-10)))
rl = page.searchFor(underline quads=True)
annot = page.addUnderlineAnnot(rl[0])
print_descr(annot)
pos = annot.rect.bl
page.insertText(pos strikeout morph=(pos fitz.Matrix(-15)))
rl = page.searchFor(strikeout quads=True)
annot = page.addStrikeoutAnnot(rl[0])
print_descr(annot)
pos = annot.rect.bl
page.insertText(pos squiggled morph=(pos fitz.Matrix(-20)))
rl = page.searchFor(squiggled quads=True)
annot = page.addSquigglyAnnot(rl[0])
print_descr(annot)
pos = annot.rect.bl
r = fitz.Rect(pos pos.x 75 pos.y 35) (0 20 0 20)
annot = page.addPolylineAnnot([r.bl r.tr r.br r.tl])
annot.setBorder(width=0.3 dashes=[2])
annot.setColors(stroke=blue fill=green)
annot.setLineEnds(fitz.PDF_ANNOT_LE_CLOSED_ARROW fitz.PDF_ANNOT_LE_R_CLOSED_ARROW)
annot.update(fill_color=(1 1 0))
print_descr(annot)
r = displ
annot = page.addPolygonAnnot([r.bl r.tr r.br r.tl])
annot.setBorder(width=0.3 dashes=[2])
annot.setColors(stroke=blue fill=gold)
annot.setLineEnds(fitz.PDF_ANNOT_LE_DIAMOND fitz.PDF_ANNOT_LE_CIRCLE)
annot.update()
print_descr(annot)
r = displ
annot = page.addLineAnnot(r.tr r.bl)
annot.setBorder(width=0.3 dashes=[2])
annot.setColors(stroke=blue fill=gold)
annot.setLineEnds(fitz.PDF_ANNOT_LE_DIAMOND fitz.PDF_ANNOT_LE_CIRCLE)
annot.update()
print_descr(annot)
r = displ
annot = page.addRectAnnot(r)
annot.setBorder(width=1 dashes=[1 2])
annot.setColors(stroke=blue fill=gold)
annot.update(opacity=0.5)
print_descr(annot)
r = displ
annot = page.addCircleAnnot(r)
annot.setBorder(width=0.3 dashes=[2])
annot.setColors(stroke=blue fill=gold)
annot.update()
print_descr(annot)
r = displ
annot = page.addFileAnnot(
r.tl b"just anything for testing" "testdata.txt"
)
print_descr(annot)
r = displ
annot = page.addStampAnnot(r stamp=10)
annot.setColors(stroke=green)
annot.update()
print_descr(annot)
r = displ (0 0 50 10)
rc = page.insertTextbox(
r
"This content will be removed upon applying the redaction."
color=blue
align=fitz.TEXT_ALIGN_CENTER
)
annot = page.addRedactAnnot(r)
print_descr(annot)
outfile = os.path.abspath(__file__).replace(".py" "-%i.pdf" % page.rotation)
doc.save(outfile deflate=True)
效果如下:
1、标记pdf中的文字
搜索某一段文字添加直线或者下划线等,代码:
# -*- coding: utf-8 -*-
import fitz
doc = fitz.open("tilted-text.pdf")
t = "¡La práctica hace el campeón!"
page = doc[0]
rl = page.searchFor(t quads = True)
page.addSquigglyAnnot(rl)
doc.save("a-squiggly.pdf")
效果如下:
2、使用自由文本添加注释。
代码:
# -*- coding: utf-8 -*-
import fitz
blue = (0 0 1)
green = (0 1 0)
red = (1 0 0)
gold = (1 1 0)
doc = fitz.open()
page = doc.newPage()
r1 = fitz.Rect(100 100 200 150)
r2 = r1 (0 75 0 75)
r3 = r2 (0 75 0 75)
t = "¡Un pequeño texto para practicar!"
a1 = page.addFreetextAnnot(r1 t color=red)
a2 = page.addFreetextAnnot(r2 t fontname="Ti" color=blue)
a3 = page.addFreetextAnnot(r3 t fontname="Co" color=blue rotate=90)
a3.setBorder(width=0)
a3.update(fontsize=8 fill_color=gold)
doc.save("a-freetext.pdf")
效果:
3、使用墨水注释
墨水注释用于包含徒手绘制。一个典型的示例可能是您的签名图片,其中包含名字和姓氏。从技术上讲,墨水注释被实现为点列表的列表。每个点列表被视为连接点的连续线。不同的点列表代表注释的独立线段。
代码:
import math
import fitz
w360 = math.pi * 2
deg = w360 / 360
rect = fitz.Rect(100 200 300 300)
first_x = rect.x0
first_y = rect.y0 rect.height / 2
x_step = rect.width / 360
y_scale = rect.height / 2
cos_points = []
for x in range(362):
x_coord = x * x_step first_x
y = -math.sin(x * deg)
p = (x_coord y * y_scale first_y)
sin_points.append(p)
y = -math.cos(x * deg)
p = (x_coord y * y_scale first_y)
cos_points.append(p)
doc = fitz.open()
page = doc.newPage()
annot = page.addInkAnnot((sin_points cos_points))
annot.setBorder(width=0.3 dashes=[1 ])
annot.setColors(stroke=(0 0 1))
annot.update()
page.drawRect(rect width=0.3)
doc.save("a-inktest.pdf")
效果:
绘图和图形PDF文件支持基本绘图操作,包括基本的几何对象,如直线,曲线,圆,包括指定颜色的矩形。
PyMuPDF通过其Shape类实现了大部分可用功能,这与其他包(例如reportlab)中的“画布”等概念相当。
绘制的形状总是创建为页面的子级,通常使用诸如shape = page.newShape()之类的指令来创建。该类定义了许多在页面区域执行绘图操作的方法。例如,last_point = shape.drawRect(rect)沿着适当定义的rect = fitz.Rect(…)的边界绘制一个矩形。
返回的last_point 始终是绘制操作结束的点(“最后一点”)。每一个这样的基本图都需要随后Shape.finish()的“关闭”它,但是可能会有多个具有一个通用finish()方法的图。
实际上,定义了一组先前的绘制操作以形成一个(可能相当复杂的)图形对象。PyMuPDF提供了几个预定义的图形shapes_and_symbols.py这证明这一点是如何工作的。Shape.finish()
如以下示例所示:
import fitz
import shapes_and_symbols as sas
tlist = [
(sas.arrow "arrow (easy)")
(sas.caro "caro (easy)")
(sas.clover "clover (easy)")
(sas.diamond "diamond (easy)")
(sas.dontenter "do not enter (medium)")
(sas.frowney "frowney (medium)")
(sas.hand "hand (complex)")
(sas.heart "heart (easy)")
(sas.pencil "pencil (very complex)")
(sas.smiley "smiley (easy)")
]
r = fitz.Rect(50 50 100 100)
d = fitz.Rect(0 r.height 10 0 r.height 10)
p = (15 -r.height * 0.2)
rlist = [r]
for i in range(1 len(tlist)):
rlist.append(rlist[i-1] d)
doc = fitz.open()
page = doc.newPage()
shape = page.newShape()
for i r in enumerate(rlist):
tlist[i][0](shape rlist[i])
shape.insertText(rlist[i].br p
tlist[i][1] fontsize=r.height/1.2)
shapemit()
import os
scriptdir = os.path.dirname(__file__)
doc.save(os.path.join(scriptdir "symbol-list.pdf"))
效果如下:
提取矢量图可以提取页面存在的绘图。这对于所有受支持的文档类型都是可能的–不仅限于PDF:因此也可以将其用于XPS,EPUB和其他文件。
一种新的页面方法,Page.getDrawings()访问绘制命令并将其转换为Python词典列表。每个字典(称为“路径”)代表一个单独的图形,它可能像一条直线一样简单,也可能是代表上一节形状之一的直线和曲线的复杂组合。
以下是一个代码段,该代码段提取页面的图形并将其重新绘制到新页面上:
import fitz
doc = fitz.open("some.file")
page = doc[0]
paths = page.getDrawings() # 提取页面存在的绘图
outpdf = fitz.open()
outpage = outpdf.newPage(width=page.rect.width height=page.rect.height)
shape = outpage.newShape()
for path in paths:
for item in path["items"]:
if item[0] == "l":
shape.drawLine(item[1] item[2])
elif item[0] == "re":
shape.drawRect(item[1])
elif item[0] == "c":
shape.drawBezier(item[1] item[2] item[3] item[4])
else:
raise ValueError("unhandled drawing" item)
shape.finish(
fill=path["fill"]
color=path["color"]
dashes=path["dashes"]
even_odd=path["even_odd"]
closePath=path["closePath"]
lineJoin=path["lineJoin"]
lineCap=max(path["lineCap"])
width=path["width"]
stroke_opacity=path["opacity"]
fill_opacity=path["opacity"]
)
shapemit()
outpdf.save("drawings-page-0.pdf")
这是由上一个代码创建的示例页面的输入和输出之间的比较:
总结PyMuPDF还有很多优秀的功能方法,这里就不一一列举,需要小伙伴们自行下去摸索,开发的作者一直在持续修复bug,更新文档,文档也比较细致。
有需要用到的小伙伴可以在文档中自行了解,pymupdf.readthedocs.io/en/latest/intro.html,或者关注、私信小编一起探讨。
Github:github/pymupdf/PyMuPDF