Python PDF神器PyMuPDF使用指南 (二)——文件和文本功能

系列文章:

Python PDF神器PyMuPDF使用指南 (一)——安装和基础功能

Python PDF神器PyMuPDF使用指南 (二)——文件和文本功能

Python PDF神器PyMuPDF使用指南 (三)——图像和注释功能

Python PDF神器PyMuPDF使用指南 (四)——绘图、多线程和OCR功能

 Python PDF神器PyMuPDF使用指南 (五)——命令行使用

Python PDF神器PyMuPDF使用指南 (六)——Document类详解

Python PDF神器PyMuPDF使用指南 (七)——Page类详解

 Python PDF神器PyMuPDF使用指南 (八)——基础使用指南


正文:

PyMuPDF是一个高性能的Python库,用于PDF(和其他)文档的数据提取、分析、转换和操作。

Github地址为:pymupdf代码库

官方文档地址为:PyMuPDF文档

前文介绍了PyMuPDF基本的安装和基础的功能,本文将详细介绍PyMuPDF处理PDF(和其他)文档的打开文件和文本处理功能。

打开文件

支持的文件类型

PyMuPDF可以打开的文件不止有PDF,还有其他文件类型,下面是目前支持的全部文件类型:

打开文件的方式详解

基本打开方式

打开文件的方式为:

doc = pymupdf.open("a.pdf")

注意:这个方式创建了一个
Document对象,还有另外一种方式:doc=pymupdf.Document("a.pdf")。因此,open只是一个方便的别名,可以在Document对象介绍中找到其完整的API。后面会仔细介绍。

打开文件扩展名错误的文件

如果你有一个文件扩展名错误的文档,你仍然可以正确地打开它。

假设“some.file”实际上是一个XPS文件,你可以像这样打开它:

doc = pymupdf.open("some.file", filetype="xps")

注意

PyMuPDF不会自动根据文件内容判断文件类型。你需要以某种方式提供文件类型信息——可以通过文件扩展名隐式提供,或者像上面一样通过
filetype参数显式提供。有一些纯Python库,例如
filetype,可以帮助你实现这一点。有关详细信息,后面会在
Document对象介绍章节中详细解释。

如果PyMuPDF遇到一个没有扩展名或扩展名未知的文件,它会尝试将其作为PDF打开。因此,在这种情况下无需额外的预防措施。对于内存中的文档,可以直接指定
doc=pymupdf.open(stream=mem_area)来将其作为PDF文档打开。

如果尝试打开一个不支持的文件类型,PyMuPDF将抛出文件数据错误。

打开远程文件

对于服务器上的远程文件(即非本地文件),你需要将文件数据流传输到PyMuPDF。

例如,可以使用requests库如下操作:

import pymupdf
import requests

r = requests.get('https://mupdf.com/docs/mupdf_explored.pdf')
data = r.content
doc = pymupdf.Document(stream=data)
从云服务打开文件

关于如何处理存储在常见云服务上的文件的更多示例,请参阅这些与云交互相关的代码片段。

以文本格式打开文件

PyMuPDF支持将任何纯文本文件作为文档打开。为此,你应为pymupdf.open函数提供filetype="txt"参数。

doc = pymupdf.open("my_program.py", filetype="txt")

通过这种方式,你可以打开各种文件类型,并执行典型的非PDF特定操作,例如文本搜索、文本提取和页面渲染。显然,一旦你渲染了txt内容,将其保存为PDF或与其他PDF文件合并是没有问题的。

示例

打开C#文件:

doc = pymupdf.open("MyClass.cs", filetype="txt")

打开XML文件:

doc = pymupdf.open("my_data.xml", filetype="txt")

打开JSON文件:

doc = pymupdf.open("more_of_my_data.json", filetype="txt")

等等!如你所见,许多基于文本的文件格式可以非常简单地通过PyMuPDF打开并解析。这使得对广泛的先前不可用的文件进行数据分析和提取成为可能。

文本处理

如何提取文档中的所有文本

这个脚本将接受一个文档文件名,并生成一个包含文档所有文本的文本文件。

该文档可以是任何支持的类型。

此脚本作为命令行工具运行,期望将文档文件名作为参数提供。它会在脚本目录中生成一个名为“filename.txt”的文本文件。页面文本之间通过换页符(form feed)字符分隔:

import sys, pathlib, pymupdf
fname = sys.argv[1]  # 获取文档文件名
with pymupdf.open(fname) as doc:  # 打开文档
    text = chr(12).join([page.get_text() for page in doc])
# 以二进制文件格式写入以支持非ASCII字符
pathlib.Path(fname + ".txt").write_bytes(text.encode())

输出将是文档编码格式的纯文本。不会进行任何格式化,特别是对于PDF文件,这可能意味着输出顺序不符合常规阅读顺序,出现意外的换行等。

有很多方法可以修正这一点——参见附录2:关于嵌入文件的注意事项。它们包括:

  • 以HTML格式提取文本并将其存储为HTML文档,这样可以在任何浏览器中查看。
  • 通过Page.get_text("blocks")提取文本块列表。该列表的每一项包含文本的位置,可以利用这些信息来确定合适的阅读顺序。
  • 通过Page.get_text("words")提取单词列表。其项目是带有位置信息的单词。使用它来确定位于特定矩形区域内的文本——请参见下节。
  • 请参阅以下两个部分,获取更多示例和详细解释。

    如何提取文本为Markdown格式

    这对于RAG/LLM环境特别有用——请参见“以Markdown格式输出”。

    如何从页面中提取键值对

    如果页面的布局在某种程度上是“可预测的”,那么有一个简单的方法可以快速轻松地找到给定关键词集的值——而不使用正则表达式。请参见此示例脚本。

    在这个上下文中,“可预测”意味着:

  • 每个关键词后面跟着它的值——中间没有其他文本。
  • 值的边界框底部不高于关键词的边界框底部。
  • 没有其他限制:页面布局可以是固定的也可以不是,文本也可能被存储为一个字符串。键和值之间的距离可以是任意的。
  • 例如,以下五个键值对将会被正确识别:

    key1               value1
    key2
    value2
    key3
           value3 blah, blah, blah key4 value4 some other text key5 value5 ...

    如何从矩形区域中提取文本

    现在(v1.18.0及以上)有多种方法可以实现这一目标。为此,我们已经在PyMuPDF-Utilities仓库中创建了一个专门处理此话题的文件夹。

    如何按自然阅读顺序提取文本

    PDF文本提取中常见的问题之一是,提取出的文本可能没有按照特定的阅读顺序排列。

    这是PDF创建者(无论是软件还是人类)的责任。例如,页面的页眉可能在文档生成之后单独插入——在这种情况下,页眉文本会出现在页面文本提取的最后,尽管PDF查看软件会正确显示它。例如,以下代码片段会向现有PDF中添加一些页眉和页脚行:

    doc = pymupdf.open("some.pdf")
    header = "Header"  # text in header
    footer = "Page %i of %i"  # text in footer
    for page in doc:
        page.insert_text((50, 50), header)  # insert header
        page.insert_text(  # insert footer 50 points above page bottom
            (50, page.rect.height - 50),
            footer % (page.number + 1, doc.page_count),
        )

    以这种方式修改过的页面提取出的文本将如下所示:

  • 原始文本
  • 页眉行
  • 页脚行
  • PyMuPDF提供了多种方法来重新建立某些阅读顺序,甚至重新生成接近原始文档的布局:

  • 使用Page.get_text()sort参数。它会按从左上到右下的顺序对输出进行排序(XHTML、HTML和XML输出时会被忽略)。
  • 使用PyMuPDF的命令行工具:python -m pymupdf gettext ...,它会生成一个文本文件,文本经过重新排列,保持布局模式。提供了许多选项来控制输出。
  • 也可以使用上面提到的脚本,并根据需要进行修改。

    如何从文档中提取表格内容

    如果你在文档中看到表格,通常它并不是像嵌入的Excel或其他可识别对象那样的内容。它通常只是标准文本,格式化成看起来像表格数据的形式。

    因此,从页面区域中提取表格数据意味着你必须找到一种方法来识别表格区域(即其边界框),然后(1)图形化地指示表格和列边界,和(2)基于这些信息提取文本。

    这可能是一个非常复杂的任务,取决于是否有线条、矩形或其他支持的矢量图形。

    方法Page.find_tables()为你做了所有这些工作,且具有很高的表格检测精度。它的一个大优点是没有外部库依赖,也不需要使用人工智能或机器学习技术。它还提供了一个与著名Python数据分析包pandas的集成接口。

    请查看示例Jupyter笔记,它们涵盖了诸如一页上多个表格或跨多页合并表格碎片等常见情况。

    如何标记提取的文本

    有一个标准的搜索功能,可以在页面上搜索任意文本:Page.search_for()。它返回一个包含找到的文本的Rect对象列表。这些矩形可以用于自动插入注释,显著标记找到的文本。

    这种方法有优缺点。优点包括:

  • 搜索字符串可以包含空格并跨行换行
  • 大小写字符视为相同
  • 行尾的单词断字会被检测并解决
  • 返回的可能是Quad对象列表,精确定位不与轴平行的文本——当页面旋转不为零时,建议使用Quad输出。
  • 但还有其他选项:

    import sys
    import pymupdf
    
    def mark_word(page, text):
        """下划线标记包含'text'的每个单词"""
        found = 0
        wlist = page.get_text("words", delimiters=None)  # 创建单词列表
        for w in wlist:  # 遍历页面上的所有单词
            if text in w[4]:  # w[4]是单词的字符串
                found += 1  # 计数
                r = pymupdf.Rect(w[:4])  # 从单词边框创建矩形
                page.add_underline_annot(r)  # 下划线标记
        return found
    
    fname = sys.argv[1]  # 文件名
    text = sys.argv[2]  # 搜索字符串
    doc = pymupdf.open(fname)
    
    print("在文档'%s'中下划线标记包含'%s'的单词" % (word, doc.name))
    
    new_doc = False  # 如果有找到内容,则设置为True
    
    for page in doc:  # 遍历页面
        found = mark_word(page, text)  # 标记页面中的单词
        if found:  # 如果找到内容...
            new_doc = True
            print("在第%i页找到了'%s' %i次" % (text, found, page.number + 1))
    
    if new_doc:
        doc.save("marked-" + doc.name)

    此脚本使用Page.get_text("words")来查找通过CLI参数传入的字符串。此方法通过空格作为分隔符将页面的文本分为“单词”。更多说明:

  • 如果找到,完整的包含搜索字符串的单词会被标记(下划线),而不仅仅是搜索字符串。
  • 搜索字符串不能包含单词分隔符。默认的单词分隔符是空格和不换行空格(chr(0xA0))。如果你使用了额外的分隔符,例如page.get_text("words", delimiters="./,"),那么这些字符也不应出现在搜索字符串中。
  • 如上所示,大小写是区分的,但这可以通过在mark_word函数中使用lower()方法(甚至是正则表达式)来更改。
  • 没有上限:所有匹配项都会被检测到。
  • 你可以使用任何方式标记单词:‘下划线’,‘高亮’,‘删除线’或‘矩形’注释等。
  • 这是一个示例片段,其中“MuPDF”被用作搜索字符串。请注意,所有包含“MuPDF“的字符串都已完全加下划线(而不仅仅是搜索字符串)。
  • 如何标记搜索到的文本

    以下脚本用于搜索文本并标记它:

    # -*- coding: utf-8 -*-
    import pymupdf
    
    # 打开要注释的文档
    doc = pymupdf.open("tilted-text.pdf")
    
    # 要标记的文本
    needle = "¡La práctica hace el campeón!"
    
    # 仅处理第一页
    page = doc[0]
    
    # 获取文本位置的列表
    # 使用“quads”而不是矩形,因为文本可能是倾斜的!
    rl = page.search_for(needle, quads=True)
    
    # 使用一个注释标记所有找到的 quads
    page.add_squiggly_annot(rl)
    
    # 保存为新PDF
    doc.save("a-squiggly.pdf")

    结果如下所示:

    如何标记非水平文本

    前一部分已经展示了如何标记通过文本搜索找到的非水平文本。

    但是,通过Page.get_text()的“dict”/“rawdict”选项提取的文本也可能返回与x轴有非零角度的文本。这由行字典的"dir"键的值指示:它是该角度的元组(余弦,正弦)。如果line["dir"] != (1, 0),则该行所有span的文本都会旋转相同的角度。

    然而,方法返回的“bboxes”仅是矩形——而不是quads。因此,为了正确标记span文本,必须从行和span字典中恢复出它的quad。使用以下实用函数(v1.18.9新增):

    span_quad = pymupdf.recover_quad(line["dir"], span)
    annot = page.add_highlight_annot(span_quad)  # 这将标记整个span文本

    如果你想一次性标记整行或其部分span,可以使用以下代码片段(适用于v1.18.10或更高版本):

    line_quad = pymupdf.recover_line_quad(line, spans=line["spans"][1:-1])
    page.add_highlight_annot(line_quad)

    上面的spans参数可以指定line["spans"]的任何子列表。例如,上面的示例标记了从第二个到倒数第二个span。如果省略,它将标记整行。

    如何分析字体特征

    要分析PDF中文本的特征,可以使用以下简易脚本作为起点:

    import sys
    import pymupdf
    
    def flags_decomposer(flags):
        """将字体标志转换为人类可读格式。"""
        l = []
        if flags & 2 ** 0:
            l.append("上标")
        if flags & 2 ** 1:
            l.append("斜体")
        if flags & 2 ** 2:
            l.append("衬线")
        else:
            l.append("无衬线")
        if flags & 2 ** 3:
            l.append("等宽")
        else:
            l.append("比例")
        if flags & 2 ** 4:
            l.append("粗体")
        return ", ".join(l)
    
    doc = pymupdf.open(sys.argv[1])
    page = doc[0]
    
    # 读取页面文本并转换为字典形式,压制CJK字体中的额外空格
    blocks = page.get_text("dict", flags=11)["blocks"]
    for b in blocks:  # 遍历文本块
        for l in b["lines"]:  # 遍历文本行
            for s in l["spans"]:  # 遍历文本span
                print("")
                font_properties = "字体: '%s' (%s), 大小 %g, 颜色 #%06x" % (
                    s["font"],  # 字体名称
                    flags_decomposer(s["flags"]),  # 可读的字体标志
                    s["size"],  # 字体大小
                    s["color"],  # 字体颜色
                )
                print("文本: '%s'" % s["text"])  # 简单打印文本
                print(font_properties)

    这里是PDF页面和脚本输出的示例:

    如何插入文本

    PyMuPDF 提供了多种方法来在新页面或现有页面上插入文本,具有以下功能:

  • 选择字体,包括内置字体和通过文件提供的字体。
  • 选择文本特征,如粗体、斜体、字体大小、字体颜色等。
  • 在多个方式中定位文本:
  • 作为从某个起点开始的简单行输出。

    或将文本放入提供的矩形框中,在这种情况下还可以选择文本对齐方式。

    选择文本是否放置在前景中(覆盖现有内容)。

    所有文本都可以被任意“变形”,即通过矩阵变换改变其外观,从而实现缩放、剪切或镜像等效果。

    独立于变形,文本还可以按 90 度的整数倍旋转。

    所有这些都可以通过以下三个基本的 Page 和 Shape 方法实现:

  • Page.insert_font() – 安装字体以供后续引用。结果会在 Document.get_page_fonts() 输出中反映。字体可以通过以下方式提供:
  • 作为文件提供,

    通过 Font (然后使用 Font.buffer),

    已经存在于当前或其他 PDF 中,或

    使用内置字体。

  • Page.insert_text() – 写入一些文本行。内部调用了 Shape.insert_text() 方法。
  • Page.insert_textbox() – 将文本适配到给定的矩形框中。可以选择文本的对齐方式(左对齐、右对齐、居中对齐、两端对齐),并控制文本是否真正适合。内部调用了 Shape.insert_textbox() 方法。
  • 注意:两种文本插入方法都会根据需要自动安装字体。

    1. 如何写入文本行

    在页面上输出一些文本行:

    import pymupdf
    doc = pymupdf.open(...)  # 新建或打开现有 PDF
    page = doc.new_page()  # 新建或访问现有页面 via doc[n]
    p = pymupdf.Point(50, 72)  # 第一行起始点
    
    text = "Some text,\nspread across\nseveral lines."
    # 相同的效果也可以通过以下方式实现:
    # text = ["Some text", "spread across", "several lines."]
    
    rc = page.insert_text(p,  # 第一字符的左下角位置
                         text,  # 文本内容(支持 '\n' 换行符)
                         fontname="helv",  # 默认字体
                         fontsize=11,  # 默认字体大小
                         rotate=0,  # 也可以选择:90, 180, 270
                         )
    print(f"{rc} lines printed on page {page.number}.")
    
    doc.save("text.pdf")

    使用此方法时,只有文本的行数会被控制,以防止超出页面高度。多余的行将不会被写入,实际插入的行数会被返回。计算使用了字体大小和 36 点(即 0.5 英寸)作为底部边距。

    行宽会被忽略。超出的部分将会不可见。

    对于内置字体,预先计算行宽的方法可通过 get_text_length() 来实现。

    另一个例子插入了四个文本字符串,并使用四种不同的旋转选项,演示了如何选择插入点以获得所需结果:

    import pymupdf
    doc = pymupdf.open()
    page = doc.new_page()
    
    # 四个文本字符串,每个有三行
    text1 = "rotate=0\nLine 2\nLine 3"
    text2 = "rotate=90\nLine 2\nLine 3"
    text3 = "rotate=-90\nLine 2\nLine 3"
    text4 = "rotate=180\nLine 2\nLine 3"
    
    red = (1, 0, 0)  # 红色圆点的颜色
    
    # 插入点,每个插入点与页面角落相距 25 像素
    p1 = pymupdf.Point(25, 25)
    p2 = pymupdf.Point(page.rect.width - 25, 25)
    p3 = pymupdf.Point(25, page.rect.height - 25)
    p4 = pymupdf.Point(page.rect.width - 25, page.rect.height - 25)
    
    # 创建一个 Shape 用于绘制
    shape = page.new_shape()
    
    # 在插入点位置画红色实心圆
    shape.draw_circle(p1, 1)
    shape.draw_circle(p2, 1)
    shape.draw_circle(p3, 1)
    shape.draw_circle(p4, 1)
    shape.finish(width=0.3, color=red, fill=red)
    
    # 插入文本字符串
    shape.insert_text(p1, text1)
    shape.insert_text(p3, text2, rotate=90)
    shape.insert_text(p2, text3, rotate=-90)
    shape.insert_text(p4, text4, rotate=180)
    
    # 保存到页面
    shape.commit()
    doc.save("rotated_texts.pdf")

    结果:你将看到四个文本字符串分别在不同角度(0°、90°、-90°、180°)的位置插入页面,并且每个插入点都用红色圆点标记出来。

    2. 如何填写文本框

    该脚本将文本填充到四个不同的矩形框中,每次选择不同的旋转角度值:

    import pymupdf
    
    doc = pymupdf.open()  # 新建或打开现有 PDF
    page = doc.new_page()  # 新页面,或选择 doc[n]
    
    # 要写入的整体区域
    rect = pymupdf.Rect(100, 100, 300, 150)
    
    # 将该区域分成 4 个相等的子矩形
    CELLS = pymupdf.make_table(rect, cols=4, rows=1)
    
    t1 = "text with rotate = 0."  # 这些文本将被写入
    t2 = "text with rotate = 90."
    t3 = "text with rotate = 180."
    t4 = "text with rotate = 270."
    text = [t1, t2, t3, t4]
    red = pymupdf.pdfcolor["red"]  # 红色
    gold = pymupdf.pdfcolor["gold"]  # 金色
    blue = pymupdf.pdfcolor["blue"]  # 蓝色
    
    """
    我们使用一个 Shape 对象(类似画布)来输出文本和
    矩形框用于演示。
    """
    shape = page.new_shape()  # 创建 Shape
    for i in range(len(CELLS[0])):
        shape.draw_rect(CELLS[0][i])  # 绘制矩形框
        shape.insert_textbox(
            CELLS[0][i], text[i], fontname="hebo", color=blue, rotate=90 * i
        )
    
    shape.finish(width=0.3, color=red, fill=gold)
    
    shape.commit()  # 将所有内容写入页面
    doc.ez_save(__file__.replace(".py", ".pdf"))

    上面的代码使用了默认值:字体大小为 11,文本对齐方式为“左对齐”。最终的效果如下:四个矩形框中的文本分别按 0°、90°、180°、270° 的旋转角度填充。

    3. 如何用 HTML 文本填充框

    方法 Page.insert_htmlbox() 提供了一种更强大的方式将文本插入到矩形框中。

    与简单的纯文本不同,这个方法接受 HTML 源代码,其中不仅可以包含 HTML 标签,还可以包含影响字体、字体粗细(加粗)、字体样式(斜体)、颜色等的样式指令。

    此外,还可以混合使用多种字体和语言,输出 HTML 表格,插入图像和 URI 链接。

    为了获得更大的样式灵活性,还可以提供额外的 CSS 源。

    此方法基于 Story 类,因此像天城文、尼泊尔文、泰米尔文等复杂脚本系统能够得到正确的显示,这是因为它使用了 HarfBuzz 库来提供所谓的“文本塑形”功能。

    所需的字体会自动从 Google 的 Noto 字体库中获取(作为备用,当用户提供的字体未包含某些字形时)。

    以下是一个简单的示例,展示了该方法如何使用 HTML 丰富的文本:

    import pymupdf
    
    rect = pymupdf.Rect(100, 100, 400, 300)
    
    text = """Lorem ipsum dolor sit amet, consectetur adipisici elit, sed
        eiusmod tempor incidunt ut labore et dolore magna aliqua. Ut enim ad
        minim veniam, quis nostrud exercitation <b>ullamco <i>laboris</i></b>
        nisi ut aliquid ex ea commodi consequat. Quis aute iure
        <span style="color: #f00;">reprehenderit</span>
        in <span style="color: #0f0;font-weight:bold;">voluptate</span> velit
        esse cillum dolore eu fugiat nulla pariatur. Excepteur sint obcaecat
        cupiditat non proident, sunt in culpa qui
        <a href="https://www.artifex.com">officia</a> deserunt mollit anim id
        est laborum."""
    
    doc = pymupdf.Document()
    
    page = doc.new_page()
    page.insert_htmlbox(rect, text, css="* {font-family: sans-serif;font-size:14px;}")
    
    doc.ez_save(__file__.replace(".py", ".pdf"))

    请注意,css 参数是如何用来全局选择默认的 "sans-serif" 字体和 14px 字体大小的。

    最终的效果将类似于以下内容:

    1)如何输出 HTML 表格和图片

    这是一个使用此方法输出表格的例子。这次,我们将所有样式都包含在 HTML 源代码中。还请注意,如何在表格单元格中包含图片:

    import pymupdf
    import os
    
    filedir = os.path.dirname(__file__)
    
    text = """
    <style>
    body {
        font-family: sans-serif;
    }
    
    td,
    th {
        border: 1px solid blue;
        border-right: none;
        border-bottom: none;
        padding: 5px;
        text-align: center;
    }
    
    table {
        border-right: 1px solid blue;
        border-bottom: 1px solid blue;
        border-spacing: 0;
    }
    </style>
    
    <body>
    <p><b>Some Colors</b></p>
    <table>
        <tr>
        <th>Lime</th>
        <th>Lemon</th>
        <th>Image</th>
        <th>Mauve</th>
        </tr>
        <tr>
        <td>Green</td>
        <td>Yellow</td>
        <td><img src="img-cake.png" width=50></td>
        <td>Between<br>Gray and Purple</td>
        </tr>
    </table>
    </body>
    """
    
    doc = pymupdf.Document()
    
    page = doc.new_page()
    rect = page.rect + (36, 36, -36, -36)
    
    # 由于图片的存在,我们必须指定一个归档文件夹
    page.insert_htmlbox(rect, text, archive=pymupdf.Archive("."))
    
    doc.ez_save(__file__.replace(".py", ".pdf"))

    上述代码演示了如何通过 insert_htmlbox() 方法在 PDF 中插入一个带有样式和图片的 HTML 表格。图片通过 img 标签嵌入,并且表格和文本的样式通过内嵌的 CSS 进行了控制。

    说明:

  • archive=pymupdf.Archive("."):由于 HTML 中包含了图片,我们需要指定一个归档,告诉 PyMuPDF 从指定目录加载相关资源(如图片)。
  • img-cake.png:图片文件需位于当前工作目录下,或者可以指定图片的绝对路径。
  • 最终的效果将是一个包含带有样式的 HTML 表格和图片的 PDF 页面。

    2)如何输出世界语言

    第三个例子将展示自动多语言支持,包括自动文本排版,支持复杂的书写系统,如天城文(Devanagari)和从右到左的语言:

    import pymupdf
    
    greetings = (
        "Hello, World!",  # 英语
        "Hallo, Welt!",  # 德语
        "         !",  # 波斯语
        "வணக்கம், உலகம்!",  # 泰米尔语
        "สวัสดีชาวโลก!",  # 泰语
        "Привіт Світ!",  # 乌克兰语
        "שלום עולם!",  # 希伯来语
        "ওহে বিশ্ব!",  # 孟加拉语
        "你好世界!",  # 中文
        "こんにちは世界!",  # 日语
        "안녕하세요, 월드!",  # 韩语
        "नमस्कार, विश्व !",  # 梵语
        "हैलो वर्ल्ड!",  # 印地语
    )
    
    doc = pymupdf.open()
    page = doc.new_page()
    rect = (50, 50, 200, 500)
    
    # 将问候语合并成一个文本字符串
    text = " ... ".join([t for t in greetings])
    
    # 上面的输出将非常简单:
    page.insert_htmlbox(rect, text)
    doc.save(__file__.replace(".py", ".pdf"))

    此示例展示了如何自动支持多种语言,其中包括复杂的排版系统,如天城文、阿拉伯语和其他右至左语言。PyMuPDF 自动处理文本的排版。效果如下所示:

    3)如何指定自己的字体

    使用 CSS 语法中的 @font-face 语句定义字体文件。对于每种字体的粗体和斜体(例如,粗体或斜体),需要一个单独的 @font-face。以下示例使用了著名的 MS Comic Sans 字体的四个变种:常规、粗体、斜体和粗斜体。

    这些字体文件位于系统的 C:/Windows/Fonts 文件夹中,因此该方法需要一个 Archive 定义,指向该文件夹:

    import pymupdf
    
    # 示例文本
    text = """Lorem ipsum dolor sit amet, consectetur adipisici elit, sed
        eiusmod tempor incidunt ut labore et dolore magna aliqua. Ut enim ad
        minim veniam, quis nostrud exercitation <b>ullamco <i>laboris</i></b>
        nisi ut aliquid ex ea commodi consequat. Quis aute iure
        <span style="color: red;">reprehenderit</span>
        in <span style="color: green;font-weight:bold;">voluptate</span> velit
        esse cillum dolore eu fugiat nulla pariatur. Excepteur sint obcaecat
        cupiditat non proident, sunt in culpa qui
        <a href="https://www.artifex.com">officia</a> deserunt mollit anim id
        est laborum."""
    
    # 我们需要一个 Archive 对象来显示字体文件的位置
    arch = pymupdf.Archive("C:/Windows/Fonts")
    
    # 以下语句定义了用于常规、粗体、斜体和粗斜体文本的字体文件。
    # 我们为所有四个字体文件分配了一个任意的公共字体系列。
    # Story 算法会根据需要选择正确的文件。
    # 我们请求在整个文本中使用 "comic" 字体系列。
    css = """
    @font-face {font-family: comic; src: url(comic.ttf);}
    @font-face {font-family: comic; src: url(comicbd.ttf);font-weight: bold;}
    @font-face {font-family: comic; src: url(comicz.ttf);font-weight: bold;font-style: italic;}
    @font-face {font-family: comic; src: url(comici.ttf);font-style: italic;}
    * {font-family: comic;}
    """
    
    doc = pymupdf.Document()
    page = doc.new_page(width=150, height=150)  # 创建一个小页面
    
    page.insert_htmlbox(page.rect, text, css=css, archive=arch)
    
    doc.subset_fonts(verbose=True)  # 构建子集字体以减少文件大小
    doc.ez_save(__file__.replace(".py", ".pdf"))

    效果如下:

    4)如何请求文本对齐

    此示例结合了多个要求:

  • 将文本逆时针旋转90度。
  • 使用 pymupdf-fonts 包中的字体。你会发现此时 CSS 定义会更加简洁。
  • 使用 justify 选项对文本进行对齐。
  • import pymupdf
    
    # 示例文本
    text = """Lorem ipsum dolor sit amet, consectetur adipisici elit, sed
        eiusmod tempor incidunt ut labore et dolore magna aliqua. Ut enim ad
        minim veniam, quis nostrud exercitation <b>ullamco <i>laboris</i></b>
        nisi ut aliquid ex ea commodi consequat. Quis aute iure
        <span style="color: red;">reprehenderit</span>
        in <span style="color: green;font-weight:bold;">voluptate</span> velit
        esse cillum dolore eu fugiat nulla pariatur. Excepteur sint obcaecat
        cupiditat non proident, sunt in culpa qui
        <a href="https://www.artifex.com">officia</a> deserunt mollit anim id
        est laborum."""
    
    # 这类似于字体文件支持。不过,我们可以使用一个便捷的函数来创建所需的 CSS 定义。
    # 我们仍然需要一个 Archive 来找到字体二进制文件。
    arch = pymupdf.Archive()
    
    # 我们请求在整个文本中使用 "myfont" 字体系列。
    css = pymupdf.css_for_pymupdf_font("ubuntu", archive=arch, name="myfont")
    css += "* {font-family: myfont;text-align: justify;}"
    
    doc = pymupdf.Document()
    
    page = doc.new_page(width=150, height=150)
    
    page.insert_htmlbox(page.rect, text, css=css, archive=arch, rotate=90)
    
    doc.subset_fonts(verbose=True)
    doc.ez_save(__file__.replace(".py", ".pdf"))

    效果如下:

    如何提取带有颜色的文本

    可以遍历文本块,找到包含所需信息的文本片段。

    for page in doc:
        text_blocks = page.get_text("dict", flags=pymupdf.TEXTFLAGS_TEXT)["blocks"]
        for block in text_blocks:
            for line in block["lines"]:
                for span in line["spans"]:
                    text = span["text"]
                    color = pymupdf.sRGB_to_rgb(span["color"])
                    print(f"Text: {text}, Color: {color}")

    这段代码会遍历每个页面的文本块,获取文本及其颜色信息,并打印出来。

    总结

    上面是PyMuPDF打开文件和处理文本的详细功能介绍。后面会详细介绍更多功能。

    作者:塞大花

    物联沃分享整理
    物联沃-IOTWORD物联网 » Python PDF神器PyMuPDF使用指南 (二)——文件和文本功能

    发表回复