PyMuPDF 1.24.4 中文文档(三)(2)

简介: PyMuPDF 1.24.4 中文文档(三)

PyMuPDF 1.24.4 中文文档(三)(1)https://developer.aliyun.com/article/1559529


如何创建部分位图(剪辑)

并非总是需要或想要完整的页面图像。例如,当您在 GUI 中显示图像并希望用页面的放大部分填充相应的窗口时,就是这种情况。

假设你的 GUI 窗口有足够的空间来显示完整的文档页面,但现在你想要用页面的右下角的四分之一来填充这个空间,从而使用四倍更好的分辨率。

为了实现这一点,请定义一个等于您希望显示在 GUI 中的区域的矩形,并将其命名为“剪辑”。在 PyMuPDF 中构建矩形的一种方式是提供两个对角线相对的角,这就是我们在这里做的事情。


mat = pymupdf.Matrix(2, 2)  # zoom factor 2 in each direction
rect = page.rect  # the page rectangle
mp = (rect.tl + rect.br) / 2  # its middle point, becomes top-left of clip
clip = pymupdf.Rect(mp, rect.br)  # the area we want
pix = page.get_pixmap(matrix=mat, clip=clip) 

在上面的例子中,我们通过指定两个对角线相对的点来构造剪辑:页面矩形的中点mp和它的右下角rect.br


如何将剪辑缩放到 GUI 窗口

请还请阅读前一节。这次我们想要计算剪辑的缩放因子,以便其图像最适合给定的 GUI 窗口。这意味着图像的宽度或高度(或两者)将等于窗口尺寸。对于以下代码片段,您需要提供 GUI 窗口的 WIDTH 和 HEIGHT,该窗口应接收页面的剪辑矩形。

# WIDTH: width of the GUI window
# HEIGHT: height of the GUI window
# clip: a subrectangle of the document page
# compare width/height ratios of image and window
if clip.width / clip.height < WIDTH / HEIGHT:
    # clip is narrower: zoom to window HEIGHT
    zoom = HEIGHT / clip.height
else:  # clip is broader: zoom to window WIDTH
    zoom = WIDTH / clip.width
mat = pymupdf.Matrix(zoom, zoom)
pix = page.get_pixmap(matrix=mat, clip=clip) 

对于另一种情况,现在假设您已有缩放因子并且需要计算适合的剪辑

在这种情况下,我们有 zoom = HEIGHT/clip.height = WIDTH/clip.width,因此我们必须设置 clip.height = HEIGHT/zoomclip.width = WIDTH/zoom。选择页面上剪辑的左上角点tl来计算正确的位图:

width = WIDTH / zoom
height = HEIGHT / zoom
clip = pymupdf.Rect(tl, tl.x + width, tl.y + height)
# ensure we still are inside the page
clip &= page.rect
mat = pymupdf.Matrix(zoom, zoom)
pix = pymupdf.Pixmap(matrix=mat, clip=clip) 

如何创建或抑制注释图片

通常,页面的位图还显示页面的注释。偶尔,这可能是不可取的。

要在渲染页面上抑制注释图片,只需在Page.get_pixmap()中指定annots=False

您还可以单独呈现注释:它们有自己的Annot.get_pixmap()方法。结果位图的尺寸与注释矩形相同。


如何提取图片:非 PDF 文档

与前几节相反,这一节涉及提取文档中包含的图像,以便它们可以作为一个或多个页面的一部分显示。

如果您想要以文件形式或作为内存区域重新创建原始图像,您基本上有两个选项:

  1. 将您的文档转换为 PDF,然后使用其中一个仅适用于 PDF 的提取方法。此代码片段将文档转换为 PDF:
>>> pdfbytes = doc.convert_to_pdf()  # this a bytes object
>>> pdf = pymupdf.open("pdf", pdfbytes)  # open it as a PDF document
>>> # now use 'pdf' like any PDF document 
  1. 使用 Page.get_text() 方法和 “dict” 参数。适用于所有文档类型。将提取页面上显示的所有文本和图像,格式化为 Python 字典。每个图像将出现在图像块中,包含元信息和二进制图像数据。有关字典结构的详细信息,请参阅 TextPage。该方法同样适用于 PDF 文件。这将创建页面上显示的所有图像的列表:
>>> d = page.get_text("dict")
>>> blocks = d["blocks"]  # the list of block dictionaries
>>> imgblocks = [b for b in blocks if b["type"] == 1]
>>> pprint(imgblocks[0])
{'bbox': (100.0, 135.8769989013672, 300.0, 364.1230163574219),
 'bpc': 8,
 'colorspace': 3,
 'ext': 'jpeg',
 'height': 501,
 'image': b'\xff\xd8\xff\xe0\x00\x10JFIF\...',  # CAUTION: LARGE!
 'size': 80518,
 'transform': (200.0, 0.0, -0.0, 228.2460174560547, 100.0, 135.8769989013672),
 'type': 1,
 'width': 439,
 'xres': 96,
 'yres': 96} 

如何提取图像:PDF 文档

就像 PDF 中的任何其他“对象”一样,图像由交叉引用编号(xref,整数)标识。如果您知道这个编号,您有两种方法可以访问图像的数据:

  1. 创建一个 Pixmap 图像,使用指令 pix = pymupdf.Pixmap(doc, xref)。该方法非常快速(单个数字微秒)。Pixmap 的属性(宽度、高度等)将反映图像的属性。在这种情况下,无法确定嵌入原始图像的图像格式。
  2. 提取图像,使用 img = doc.extract_image(xref)。这是一个包含二进制图像数据的字典,如 img[“image”] 所示。还提供了许多元数据 – 大多数与图像的 Pixmap 中相同。主要区别在于字符串 img[“ext”],它指定图像格式:除了“png”外,还可以出现像“jpeg”、“bmp”、“tiff”等的字符串。如果要将其存储到磁盘,请使用此字符串作为文件扩展名。此方法的执行速度应与语句 pix = pymupdf.Pixmap(doc, xref); pix.tobytes() 的组合速度进行比较。如果嵌入的图像是 PNG 格式,则 Document.extract_image() 的速度大约相同(并且二进制图像数据相同)。否则,此方法快几千倍,并且图像数据更小

问题仍然是:“如何知道这些‘xref’图像的编号?”。对此有两个答案:

  1. “检查页面对象:” 遍历 Page.get_images() 的项。这是一个列表的列表,其项看起来像 [xref, smask, …],包含图像的 xref。然后可以使用其中一个上述方法使用此 xref。对于**有效(未损坏)**文档使用此方法要小心。然而,请注意,同一图像可能被多个页面引用,因此可能需要提供机制以避免多次提取。
  2. “不需要知道:” 遍历文档的所有 xref 列表,并对每个执行 Document.extract_image()。如果返回的字典为空,则继续 - 此xref不是图像。如果 PDF 文件损坏(无法使用的页面),请使用此方法。注意,PDF 文件通常包含具有特殊目的(定义其他图像的透明度)的“伪图像”(“模版掩码”)。您可能希望提供逻辑来排除这些内容的提取。也请查看下一节。

对于这两种提取方法,都有现成的通用脚本可供使用:

extract-from-pages.py 按页提取图像:


extract-from-xref.py 通过 xref 表提取图像:



如何处理图像掩码

PDF 中的某些图像伴随着图像掩码。在最简单的形式下,掩码表示作为单独图像存储的 alpha(透明度)字节。为了重建具有掩码的图像的原始图像,必须从其掩码中提取透明度字节来“丰富”它。

在 PyMuPDF 中可以通过以下两种方式之一识别图像是否有掩码:

  1. Document.get_page_images() 的项具有一般格式 (xref, smask, ...),其中 xref 是图像的xref,如果 smask 为正,则为掩码的xref
  2. Document.extract_image() 的(字典)结果具有一个 “smask” 键,如果为正,则包含任何掩码的xref

如果 smask == 0,那么通过xref遇到的图像可以直接处理。

使用 PyMuPDF 恢复原始图像,需执行以下过程:


>>> pix1 = pymupdf.Pixmap(doc.extract_image(xref)["image"])    # (1) pixmap of image w/o alpha
>>> mask = pymupdf.Pixmap(doc.extract_image(smask)["image"])   # (2) mask pixmap
>>> pix = pymupdf.Pixmap(pix1, mask)                           # (3) copy of pix1, image mask added 

步骤(1)创建基本图像的像素图。步骤(2)使用图像掩码完成相同操作。步骤(3)添加 alpha 通道并填充透明信息。

脚本 extract-from-pages.pyextract-from-xref.py 也包含此逻辑。


如何制作所有图片(或文件)的一个 PDF 文件

我们在这里展示了三个脚本,它们接受一个(图像和其他)文件列表,并将它们全部放入一个 PDF 中。

方法 1:将图像插入为页面

第一个脚本将每个图像转换为具有相同尺寸的 PDF 页面。结果将是一个每个图像一个页面的 PDF。它只适用于支持的图像文件格式:

import os, pymupdf
import PySimpleGUI as psg  # for showing a progress bar
doc = pymupdf.open()  # PDF with the pictures
imgdir = "D:/2012_10_05"  # where the pics are
imglist = os.listdir(imgdir)  # list of them
imgcount = len(imglist)  # pic count
for i, f in enumerate(imglist):
    img = pymupdf.open(os.path.join(imgdir, f))  # open pic as document
    rect = img[0].rect  # pic dimension
    pdfbytes = img.convert_to_pdf()  # make a PDF stream
    img.close()  # no longer needed
    imgPDF = pymupdf.open("pdf", pdfbytes)  # open stream as PDF
    page = doc.new_page(width = rect.width,  # new page with ...
                       height = rect.height)  # pic dimension
    page.show_pdf_page(rect, imgPDF, 0)  # image fills the page
    psg.EasyProgressMeter("Import Images",  # show our progress
        i+1, imgcount)
doc.save("all-my-pics.pdf") 

这将生成一个 PDF 文件,大小仅比合并图片的大小稍大一点。一些关于性能的数字:

上述脚本在我的机器上用于 149 张图片大约需要 1 分钟,总共大小为 514 MB(生成的 PDF 文件大小大约相同)。


查看这里获取更完整的源代码:它提供了一个目录选择对话框并跳过了不支持的文件和非文件条目。

注意

我们可以使用 Page.insert_image() 而不是 Page.show_pdf_page(),结果会是一个外观相似的文件。但是,根据图像类型,它可能以未压缩的图像存储。因此,必须使用保存选项 deflate = True 来获得合理的文件大小,这会极大地增加大量图像的运行时间。因此,在这里不能推荐这种替代方案。

方法 2:嵌入文件

第二个脚本嵌入了任意文件 - 不仅仅是图像。由于技术原因,生成的 PDF 将只有一页(空白)。要稍后再次访问嵌入的文件,您需要一个能够显示和/或提取嵌入文件的合适的 PDF 查看器:

import os, pymupdf
import PySimpleGUI as psg  # for showing progress bar
doc = pymupdf.open()  # PDF with the pictures
imgdir = "D:/2012_10_05"  # where my files are
imglist = os.listdir(imgdir)  # list of pictures
imgcount = len(imglist)  # pic count
imglist.sort()  # nicely sort them
for i, f in enumerate(imglist):
    img = open(os.path.join(imgdir,f), "rb").read()  # make pic stream
    doc.embfile_add(img, f, filename=f,  # and embed it
                        ufilename=f, desc=f)
    psg.EasyProgressMeter("Embedding Files",  # show our progress
        i+1, imgcount)
page = doc.new_page()  # at least 1 page is needed
doc.save("all-my-pics-embedded.pdf") 


这绝对是最快的方法,同时也产生了可能的最小输出文件大小。上述图片在我的机器上需要 20 秒,并生成了一个大小为 510 MB 的 PDF 文件。查看这里获取更完整的源代码:它提供了一个目录选择对话框并跳过了非文件条目。

方法 3:附加文件

实现此任务的第三种方法是通过页面注释附加文件,请查看这里获取完整的源代码。

这与前一个脚本具有类似的性能,并且也产生了类似的文件大小。它将为每个附加文件显示一个‘FileAttachment’图标的 PDF 页面。

注意

嵌入附加方法都可用于任意文件 - 不仅仅是图像。

注意

我们强烈建议使用出色的包PySimpleGUI来为可能运行较长时间的任务显示进度条。它纯粹使用 Tkinter(无需额外的 GUI 包),只需再加一行代码!


如何创建矢量图像

从文档页面创建图像的常规方法是Page.get_pixmap()。像素图表示一幅光栅图像,因此必须在创建时决定其质量(即分辨率)。后期无法更改。

PyMuPDF 还提供了一种方式来创建 SVG 格式(可缩放矢量图形,以 XML 语法定义)的矢量图像。SVG 图像在各种缩放级别下保持精确(当然除了其中嵌入的任何栅格图形元素)。

指令svg = page.get_svg_image(matrix=pymupdf.Identity)提供了一个 UTF-8 字符串svg,可以用扩展名“.svg”存储。


如何转换图像

就像 PyMuPDF 的一个特性一样,图像转换非常简单。在许多情况下,它可以避免使用其他图形包(如 PIL/Pillow)。

尽管与 Pillow 接口几乎是微不足道的。

输入格式 输出格式 描述
BMP . Windows 位图
JPEG JPEG 联合图像专家组
JXR . JPEG 扩展范围
JPX/JP2 . JPEG 2000
GIF . 图形交换格式
TIFF . 标记图像文件格式
PNG PNG 可移植网络图形
PNM PNM 可移植任意图形
PGM PGM 可移植灰度图
PBM PBM 可移植位图
PPM PPM 可移植像素图
PAM PAM 可移植任意地图
. PSD Adobe Photoshop 文档
. PS Adobe Postscript

基本方案仅包括以下两行:

pix = pymupdf.Pixmap("input.xxx")  # any supported input format
pix.save("output.yyy")  # any supported output format 

备注

  1. *pymupdf.Pixmap(arg)*的输入参数可以是包含图像的文件或字节/io.BytesIO 对象。
  2. 而不是一个输出文件,你也可以通过*pix.tobytes(“yyy”)*创建一个字节对象并传递它。
  3. 当然,输入和输出格式在颜色空间和透明度方面必须兼容。如果需要调整,Pixmap类已经包括了相关功能。

注意

将 JPEG 转换为 Photoshop

pix = pymupdf.Pixmap("myfamily.jpg")
pix.save("myfamily.psd") 

注意

JPEG 转换为 Tkinter PhotoImage。任何RGB / 无 Alpha图像的工作原理都是相同的。转换为可移植任意图形格式(PPM、PGM 等)是解决问题的方法,因为它们被所有 Tkinter 版本支持:

import tkinter as tk
pix = pymupdf.Pixmap("input.jpg")  # or any RGB / no-alpha image
tkimg = tk.PhotoImage(data=pix.tobytes("ppm")) 

注意

带 Alpha 通道的 PNG 转换为 Tkinter PhotoImage。这需要移除 Alpha 字节,然后进行 PPM 转换:

import tkinter as tk
pix = pymupdf.Pixmap("input.png")  # may have an alpha channel
if pix.alpha:  # we have an alpha channel!
    pix = pymupdf.Pixmap(pix, 0)  # remove it
tkimg = tk.PhotoImage(data=pix.tobytes("ppm")) 


PyMuPDF 1.24.4 中文文档(三)(3)https://developer.aliyun.com/article/1559532

相关文章
|
3天前
|
XML 数据安全/隐私保护 数据格式
PyMuPDF 1.24.4 中文文档(七)(3)
PyMuPDF 1.24.4 中文文档(七)
11 0
|
3天前
PyMuPDF 1.24.4 中文文档(五)(1)
PyMuPDF 1.24.4 中文文档(五)
14 3
|
3天前
|
安全 API 数据安全/隐私保护
PyMuPDF 1.24.4 中文文档(一)(5)
PyMuPDF 1.24.4 中文文档(一)
12 3
PyMuPDF 1.24.4 中文文档(一)(5)
|
3天前
|
文字识别 API 数据安全/隐私保护
PyMuPDF 1.24.4 中文文档(一)(4)
PyMuPDF 1.24.4 中文文档(一)
12 3
|
3天前
|
文字识别 API 数据安全/隐私保护
PyMuPDF 1.24.4 中文文档(一)(1)
PyMuPDF 1.24.4 中文文档(一)
12 1
|
3天前
|
XML 编解码 文字识别
PyMuPDF 1.24.4 中文文档(八)(4)
PyMuPDF 1.24.4 中文文档(八)
11 1
|
3天前
|
存储 XML 编解码
PyMuPDF 1.24.4 中文文档(八)(3)
PyMuPDF 1.24.4 中文文档(八)
16 1
|
3天前
|
存储 资源调度 JavaScript
PyMuPDF 1.24.4 中文文档(八)(1)
PyMuPDF 1.24.4 中文文档(八)
11 0
PyMuPDF 1.24.4 中文文档(八)(1)
|
3天前
|
XML JSON API
PyMuPDF 1.24.4 中文文档(六)(5)
PyMuPDF 1.24.4 中文文档(六)
7 0
PyMuPDF 1.24.4 中文文档(六)(5)
|
3天前
|
XML API 数据安全/隐私保护
PyMuPDF 1.24.4 中文文档(七)(1)
PyMuPDF 1.24.4 中文文档(七)
8 0