PyMuPDF–超强的解析PDF的Python包

它用来干什么的?

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支持它们,其他平台也应该可以正常工作。

支持的操作系统还是很广泛的。

安装PyMuPDF

PyMuPDF托管在Github和PyPi上,因此我们可以很方便的安装。

pip install pymupdf

具体安装方法参考:https://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中的有关文本信息,其中包括:

  1. 提取HTML格式的文本并将其存储为HTML文档,以便可以在任何浏览器中查看。
  2. 通过Page.getText(“blocks”)将文本提取为文本块列表。此列表的每个项目均包含其文本的位置信息,可用于建立方便的阅读顺序。
  3. 通过Page.getText(“words”)提取单个单词的列表。它是带有位置信息的单词。使用它来确定给定矩形中包含的文本。

PDF注释

PyMuPDF大大扩展PDF的注释的功能,新的注释类型支持“墨水”,“橡胶印章”和“曲线”注释。墨水注释通过组合一个或多个互连点列表来模拟笔迹。印章旨在以视觉方式告知文档的状态或预期用途(例如“草稿”,“机密”等)。曲线是一个文本标记注释,它用锯齿形的线条在选定的文本下划线。

 

  • 扩展的“FreeText”支持:现在可以使用拉丁字符集中的所有字符,文字,矩形背景和矩形边框的颜色可以独立设置,矩形中的文本可以旋转 90或-90度文本会自动以可用矩形换行(制成多行)现在,所有Base-14字体都可用(仅普通变体,即无粗体,无斜体)。
  • MuPDF现在支持“线”注释的线末端图标。PyMuPDF已经在v1.13.x中支持该功能,并且几乎支持所有适用类型。因此调整了“Polygon”和“PolyLine”注释的外观,使其与MuPDF中“Line”的注释非常相似。
  • MuPDF现在在相关的地方提供了自己的注释图标。PyMuPDF可以很方便的切换使用它们。到目前为止,用于“FileAttachment”和“Text”(“粘滞便笺”)。
  • MuPDF现在还支持“ Caret”,“ Movie”,“ Sound”和“ Signature”注释,稍后会在PyMuPDF中添加它们。

 

代码:

# -*- 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)

效果如下:

超强的解析PDF的Python包-PyMuPDF

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")

效果如下:

超强的解析PDF的Python包-PyMuPDF

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")

效果:

超强的解析PDF的Python包-PyMuPDF

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的Python包-PyMuPDF

绘图和图形

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)


shape.commit()

import os
scriptdir = os.path.dirname(__file__)
doc.save(os.path.join(scriptdir, "symbol-list.pdf"))  

效果如下:

超强的解析PDF的Python包-PyMuPDF

提取矢量图

可以提取页面存在的绘图。这对于所有受支持的文档类型都是可能的–不仅限于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"],  
        )

shape.commit()
outpdf.save("drawings-page-0.pdf")

这是由上一个代码创建的示例页面的输入和输出之间的比较:

超强的解析PDF的Python包-PyMuPDF

总结

PyMuPDF还有很多优秀的功能方法,这里就不一一列举,需要小伙伴们自行下去摸索,开发的作者一直在持续修复bug,更新文档,文档也比较细致。

有需要用到的小伙伴可以在文档中自行了解,https://pymupdf.readthedocs.io/en/latest/intro.html,或者关注、私信小编一起探讨。

Github:https://github.com/pymupdf/PyMuPDF

内容出处:,

声明:本网站所收集的部分公开资料来源于互联网,转载的目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。如果您发现网站上有侵犯您的知识产权的作品,请与我们取得联系,我们会及时修改或删除。文章链接:http://www.yixao.com/share/12997.html

发表评论

登录后才能评论