PyMuPDF 1.24.4 中文文档(十二)(4)https://developer.aliyun.com/article/1559510
XHTML
TextPage.extractXHTML()
(或 Page.get_text(“xhtml”))是 TEXT 的 HTML 格式变体,包含裸文本和图像(“语义”输出):
<div id="page0"> <p>Some text on first page.</p> </div>
文本提取标志的默认值
- 新增于版本 1.16.2:方法
Page.get_text()
支持关键参数 flags(整数),以控制提取数据的量和质量。下表显示了每个提取变体的默认设置(省略或设为 None 的 flags 参数)。如果指定的 flags 值不为 None,请确保设置 所有所需的 选项。有关各位设置的描述,请参阅 Text Extraction Flags。 - 新增于 v1.19.6:以下表格中的默认组合现在可作为 Python 常量使用:
TEXTFLAGS_TEXT
、TEXTFLAGS_WORDS
、TEXTFLAGS_BLOCKS
、TEXTFLAGS_DICT
、TEXTFLAGS_RAWDICT
、TEXTFLAGS_HTML
、TEXTFLAGS_XHTML
、TEXTFLAGS_XML
和TEXTFLAGS_SEARCH
。现在,您可以轻松修改默认标志,例如:
- 包括 图片在“块”输出中:
flags = TEXTFLAGS_BLOCKS | TEXT_PRESERVE_IMAGES
- 不包括 图片在“字典”输出中:
flags = TEXTFLAGS_DICT & ~TEXT_PRESERVE_IMAGES
- 在文本搜索中关闭 去连字符 选项:
flags = TEXTFLAGS_SEARCH & ~TEXT_DEHYPHENATE
指示符 | 文本 | html | xhtml | xml | 字典 | 原始字典 | 单词 | 块 | 搜索 |
保留连字 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 |
保留空格 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 |
保留图片 | n/a | 1 | 1 | n/a | 1 | 1 | n/a | 0 | 0 |
抑制空格 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
取消连字符 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 |
剪裁至媒体框 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 |
使用 CID 而非 U+FFFD | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 |
- 搜索 指的是文本搜索功能。
- “json” 的处理与 “dict” 完全相同,因此被省略。
- “rawjson” 的处理与 “rawdict” 完全相同,因此被省略。
- “n/a” 指定意味着值为 0,设置此位不会对输出产生任何影响(但会影响性能)。
- 如果您在使用默认包含图像的输出变体时对图像不感兴趣,那么请务必关闭相应的位:这将提升性能并大大减少空间需求。
要查看 TEXT_INHIBIT_SPACES
的效果,请看这个例子:
>>> print(page.get_text("text")) H a l l o ! Mo r e t e x t i s f o l l o w i n g i n E n g l i s h . . . l e t ' s s e e w h a t h a p p e n s . >>> print(page.get_text("text", flags=pymupdf.TEXT_INHIBIT_SPACES)) Hallo! More text is following in English ... let's see what happens. >>>
性能
文本提取方法在提供的信息和资源需求及运行时间方面存在显著差异。通常,更多信息意味着需要更多处理和生成更高的数据量。
注意
特别是图像对结果有非常显著的影响。确保在不需要时排除它们(通过flags参数)。处理下述提到的 2,700 页总页数使用默认标志设置需要 160 秒。当排除所有图像时,仅需要这些时间的不到 50%(77 秒)。
首先,与市场上其他产品相比,所有方法在处理速度上都非常快。就处理速度而言,我们不知道有比这个更快的(免费)工具。即使是最详细的方法,如 RAWDICT,在不到 5 秒的时间内处理完所有 1,310 页的 Adobe PDF 参考手册(简单文本在这里不到 2 秒)。
下表显示了平均相对速度(“RSpeed”,基准为 1.00 为 TEXT),涵盖了约 1,400 页文本密集和 1,300 页图像密集的页面。
Method | RSpeed | 评论 | 没有图像 |
TEXT | 1.00 | 没有图像,普通文本,换行 | 1.00 |
BLOCKS | 1.00 | 图像边界框(仅),块级文本与边界框,换行 | 1.00 |
WORDS | 1.02 | 没有图像,字级文本与边界框 | 1.02 |
XML | 2.72 | 没有图像,字符级文本,布局和字体细节 | 2.72 |
XHTML | 3.32 | base64图像,跨度级文本,无布局信息 | 1.00 |
HTML | 3.54 | base64图像,跨度级文本,布局和字体细节 | 1.01 |
DICT | 3.93 | 二进制图像,跨度级文本,布局和字体细节 | 1.04 |
RAWDICT | 4.50 | 二进制图像,字符级文本,布局和字体细节 | 1.68 |
如前所述:在排除图像提取(最后一列)时,相对速度发生了显著变化:除了 RAWDICT 和 XML 外,其他方法几乎同样快,而 RAWDICT 比现在最慢的 XML需要少 40%的执行时间。
查看附录 1章节获取更多性能信息。
对本页有任何反馈吗?
本软件按原样提供,不带任何明示或暗示的保证。本软件在许可下分发,未经授权不得复制、修改或分发。有关详细信息,请参阅artifex.com的许可信息或联系位于美国旧金山 94129 号,Mesa 街 39 号,108A 套房的 Artifex Software Inc。
此文档覆盖了所有 1.24.4 版本。
TextPage 的一般结构
TextPage 是(Py-)MuPDF 的类之一。通常在使用 Page 文本提取方法时在幕后创建(和销毁),但也可以直接使用,并可用作持久对象。除了其名称暗示的,图像也可以选择作为文本页的一部分:
<page> <text block> <line> <span> <char> <image block> <img>
文本页由块组成(≈ 段落)。
块由行及其字符或图像组成。
行由跨度组成。
跨度由具有相同字体属性的相邻字符组成:名称、大小、标志和颜色。
纯文本
函数TextPage.extractText()
(或Page.get_text(“text”))提取页面按文档创建者指定的原始顺序的纯文本。
示例输出:
>>> print(page.get_text("text")) Some text on first page.
注意
输出可能与习惯的“自然”阅读顺序不同。然而,您可以通过执行page.get_text("text", sort=True)
请求按照“从左上到右下”的方案重新排序。
BLOCKS
函数TextPage.extractBLOCKS()
(或Page.get_text(“blocks”))提取页面的文本块作为项目列表,如下所示:
(x0, y0, x1, y1, "lines in block", block_no, block_type)
前 4 个项目是块边界框的浮点坐标。每个块内的行通过换行符连接。
这是一种高速方法,默认情况下还提取图像元信息:每个图像都显示为包含元信息的一行文本块。图像本身不会显示。
与上述简单文本输出一样,sort
参数也可以用来获取阅读顺序。
示例输出:
>>> print(page.get_text("blocks", sort=False)) [(50.0, 88.17500305175781, 166.1709747314453, 103.28900146484375, 'Some text on first page.', 0, 0)]
WORDS
函数TextPage.extractWORDS()
(或Page.get_text(“words”))提取页面的文本单词作为项目列表,如下所示:
(x0, y0, x1, y1, "word", block_no, line_no, word_no)
前 4 个项目是单词边界框的浮点坐标。最后三个整数提供有关单词位置的更多信息。
这是一种高速方法。与之前的方法一样,使用参数sort=True
将重新排序单词。
示例输出:
>>> for word in page.get_text("words", sort=False): print(word) (50.0, 88.17500305175781, 78.73200225830078, 103.28900146484375, 'Some', 0, 0, 0) (81.79000091552734, 88.17500305175781, 99.5219955444336, 103.28900146484375, 'text', 0, 0, 1) (102.57999420166016, 88.17500305175781, 114.8119888305664, 103.28900146484375, 'on', 0, 0, 2) (117.86998748779297, 88.17500305175781, 135.5909881591797, 103.28900146484375, 'first', 0, 0, 3) (138.64898681640625, 88.17500305175781, 166.1709747314453, 103.28900146484375, 'page.', 0, 0, 4)
HTML
TextPage.extractHTML()
(或Page.get_text(“html”))完全反映了页面的TextPage结构,类似于下面的 DICT / JSON。这包括图像、字体信息和文本位置。如果包含在 HTML 头部和尾部代码中,它可以轻松地被互联网浏览器显示。我们的上述示例:
>>> for line in page.get_text("html").splitlines(): print(line) <div id="page0" style="position:relative;width:300pt;height:350pt; background-color:white"> <p style="position:absolute;white-space:pre;margin:0;padding:0;top:88pt; left:50pt"><span style="font-family:Helvetica,sans-serif; font-size:11pt">Some text on first page.</span></p> </div>
控制 HTML 输出的质量
尽管 HTML 输出在 MuPDF v1.12.0 中有了很大改进,但仍不是无 bug 的:我们发现了字体支持和图像定位方面的问题。
- HTML 文本包含对原始文档使用的字体的引用。如果浏览器不知道这些字体(几乎不可能!),它将用其他字体替换它们;结果可能看起来很奇怪。这个问题在不同的浏览器上差异很大——在我的 Windows 机器上,MS Edge 工作得很好,而 Firefox 看起来很糟糕。
- 对于结构复杂的 PDF,图像可能无法正确定位和/或调整大小。这似乎是旋转页面和页面等情况的问题,其中各种可能的页面 bbox 变体不重合(例如MediaBox != CropBox)。我们尚不清楚如何解决这个问题——我们在 MuPDF 的网站上报告了一个错误。
要解决字体问题,您可以使用一个简单的实用程序脚本扫描 HTML 文件并替换字体引用。这里有一个小例子,将所有字体替换为 PDF 基本 14 字体之一:有衬线字体将成为“Times”,无衬线字体将成为“Helvetica”,等宽字体将成为“Courier”。希望您的浏览器能够正确处理它们的粗体、斜体等变体:
import sys filename = sys.argv[1] otext = open(filename).read() # original html text string pos1 = 0 # search start poition font_serif = "font-family:Times" # enter ... font_sans = "font-family:Helvetica" # ... your choices ... font_mono = "font-family:Courier" # ... here found_one = False # true if search successful while True: pos0 = otext.find("font-family:", pos1) # start of a font spec if pos0 < 0: # none found - we are done break pos1 = otext.find(";", pos0) # end of font spec test = otext[pos0 : pos1] # complete font spec string testn = "" # the new font spec string if test.endswith(",serif"): # font with serifs? testn = font_serif # use Times instead elif test.endswith(",sans-serif"): # sans serifs font? testn = font_sans # use Helvetica elif test.endswith(",monospace"): # monospaced font? testn = font_mono # becomes Courier if testn != "": # any of the above found? otext = otext.replace(test, testn) # change the source found_one = True pos1 = 0 # start over if found_one: ofile = open(filename + ".html", "w") ofile.write(otext) ofile.close() else: print("Warning: could not find any font specs!")
DICT(或 JSON)
TextPage.extractDICT()
(或Page.get_text(“dict”, sort=False))的输出完全反映了TextPage的结构,并为每个块、行和跨度提供图像内容和位置详细信息(bbox——以像素为单位的边界框)。图像以字典输出的形式存储,并且以 JSON 输出的形式进行 base64 编码。
若要查看字典输出结构的可视化,请参阅字典输出结构。
这是它的外观:
{ "width": 300.0, "height": 350.0, "blocks": [{ "type": 0, "bbox": (50.0, 88.17500305175781, 166.1709747314453, 103.28900146484375), "lines": ({ "wmode": 0, "dir": (1.0, 0.0), "bbox": (50.0, 88.17500305175781, 166.1709747314453, 103.28900146484375), "spans": ({ "size": 11.0, "flags": 0, "font": "Helvetica", "color": 0, "origin": (50.0, 100.0), "text": "Some text on first page.", "bbox": (50.0, 88.17500305175781, 166.1709747314453, 103.28900146484375) }) }] }] }
RAWDICT(或 RAWJSON)
TextPage.extractRAWDICT()
(或Page.get_text(“rawdict”, sort=False))是DICT 的信息超集,并进一步深入到详细级别。它的外观与上述完全相同,只是*“text”项(string)在跨度中被“chars”列表替换。每个“chars”条目都是一个字符dict*。例如,这是上面示例中*“text”: “Text in black color.”*的替代内容:
"chars": [{ "origin": (50.0, 100.0), "bbox": (50.0, 88.17500305175781, 57.336997985839844, 103.28900146484375), "c": "S" }, { "origin": (57.33700180053711, 100.0), "bbox": (57.33700180053711, 88.17500305175781, 63.4530029296875, 103.28900146484375), "c": "o" }, { "origin": (63.4530029296875, 100.0), "bbox": (63.4530029296875, 88.17500305175781, 72.61600494384766, 103.28900146484375), "c": "m" }, { "origin": (72.61600494384766, 100.0), "bbox": (72.61600494384766, 88.17500305175781, 78.73200225830078, 103.28900146484375), "c": "e" }, { "origin": (78.73200225830078, 100.0), "bbox": (78.73200225830078, 88.17500305175781, 81.79000091552734, 103.28900146484375), "c": " " < ... deleted ... > }, { "origin": (163.11297607421875, 100.0), "bbox": (163.11297607421875, 88.17500305175781, 166.1709747314453, 103.28900146484375), "c": "." }],
XML
TextPage.extractXML()
(或Page.get_text(“xml”))版本提取了具有 RAW 字典详细级别的文本(无图像):
>>> for line in page.get_text("xml").splitlines(): print(line) <page id="page0" width="300" height="350"> <block bbox="50 88.175 166.17098 103.289"> <line bbox="50 88.175 166.17098 103.289" wmode="0" dir="1 0"> <font name="Helvetica" size="11"> <char quad="50 88.175 57.336999 88.175 50 103.289 57.336999 103.289" x="50" y="100" color="#000000" c="S"/> <char quad="57.337 88.175 63.453004 88.175 57.337 103.289 63.453004 103.289" x="57.337" y="100" color="#000000" c="o"/> <char quad="63.453004 88.175 72.616008 88.175 63.453004 103.289 72.616008 103.289" x="63.453004" y="100" color="#000000" c="m"/> <char quad="72.616008 88.175 78.732 88.175 72.616008 103.289 78.732 103.289" x="72.616008" y="100" color="#000000" c="e"/> <char quad="78.732 88.175 81.79 88.175 78.732 103.289 81.79 103.289" x="78.732" y="100" color="#000000" c=" "/> ... deleted ... <char quad="163.11298 88.175 166.17098 88.175 163.11298 103.289 166.17098 103.289" x="163.11298" y="100" color="#000000" c="."/> </font> </line> </block> </page>
注意
我们已成功测试了lxml来解释这个输出。
XHTML
TextPage.extractXHTML()
(或Page.get_text(“xhtml”))是 TEXT 的一种变体,但以 HTML 格式呈现,包含裸文本和图像(“语义”输出):
<div id="page0"> <p>Some text on first page.</p> </div>
文本提取标志默认值
- 从版本 1.16.2 开始:方法
Page.get_text()
支持关键参数 flags (int) 以控制提取数据的数量和质量。以下表格显示了每个提取变体的默认设置(省略了 flags 参数或设为 None)。如果您指定了除 None 以外的值的 flags,请注意您必须设置所有所需的选项。有关各位设置的描述,请参见文本提取标志。 - 从版本 v1.19.6 开始:以下表中的默认组合现在作为 Python 常量提供:
TEXTFLAGS_TEXT
,TEXTFLAGS_WORDS
,TEXTFLAGS_BLOCKS
,TEXTFLAGS_DICT
,TEXTFLAGS_RAWDICT
,TEXTFLAGS_HTML
,TEXTFLAGS_XHTML
,TEXTFLAGS_XML
, 和TEXTFLAGS_SEARCH
。现在您可以轻松修改默认标志,例如:
- 在“blocks”输出中包含图像:
flags = TEXTFLAGS_BLOCKS | TEXT_PRESERVE_IMAGES
- 在“dict”输出中排除图像:
flags = TEXTFLAGS_DICT & ~TEXT_PRESERVE_IMAGES
- 在文本搜索中关闭连字化:
flags = TEXTFLAGS_SEARCH & ~TEXT_DEHYPHENATE
指示符 | 文本 | html | xhtml | xml | dict | rawdict | 词语 | 块 | 搜索 |
保留连字 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 |
保留空白 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 |
保留图像 | n/a | 1 | 1 | n/a | 1 | 1 | n/a | 0 | 0 |
禁止空格 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
连字化 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 |
剪切到 mediabox | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 |
使用 CID 而非 U+FFFD | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 |
- 搜索 是指文本搜索功能。
- “json” 和 “dict” 处理方式完全相同,因此被省略。
- “rawjson” 和 “rawdict” 处理方式完全相同,因此被省略。
- “n/a” 规范意味着值为 0,并且设置此位对输出没有任何影响(但对性能有不利影响)。
- 如果在使用默认包含图像的输出变体时不对图像感兴趣,那么请务必关闭相应的位:您将体验到更好的性能和更低的空间需求。
要查看TEXT_INHIBIT_SPACES
的效果,请参考以下示例:
>>> print(page.get_text("text")) H a l l o ! Mo r e t e x t i s f o l l o w i n g i n E n g l i s h . . . l e t ' s s e e w h a t h a p p e n s . >>> print(page.get_text("text", flags=pymupdf.TEXT_INHIBIT_SPACES)) Hallo! More text is following in English ... let's see what happens. >>>
性能
文本提取方法在提供的信息量、资源需求和运行时间方面有显著差异。显然,提供更多信息意味着需要更多的处理和生成更大的数据量。
注
特别是图像对性能有非常显著的影响。确保在不需要时(通过flags参数)将其排除。在默认标志设置下处理下述提到的 2700 页总共需要 160 秒。当所有图像都排除时,只需要不到这个时间的一半(77 秒)。
首先,所有方法在市场上相比其他产品非常快。就处理速度而言,我们不知道有比这更快的(免费)工具。即使是最详细的方法 RAW 字典,在不到 5 秒的时间内就可以处理完所有 1,310 页的 Adobe PDF 参考手册(简单文本只需不到 2 秒)。
下表显示了平均相对速度(“RSpeed”,基准为 1.00 是 TEXT),涵盖了约 1400 页文本密集和 1300 页图像密集的页面。
Method | RSpeed | Comments | 无图像 |
TEXT | 1.00 | 无图像,普通文本,换行符 | 1.00 |
BLOCKS | 1.00 | 图像边界框(仅限),块级文本和边界框,换行符 | 1.00 |
WORDS | 1.02 | 无图像,单词级文本和边界框 | 1.02 |
XML | 2.72 | 无图像,字符级文本,布局和字体详细信息 | 2.72 |
XHTML | 3.32 | base64图像,跨度级文本,无布局信息 | 1.00 |
HTML | 3.54 | base64图像,跨度级文本,布局和字体详细信息 | 1.01 |
DICT | 3.93 | 二进制图像,跨度级文本,布局和字体详细信息 | 1.04 |
RAWDICT | 4.50 | 二进制图像,字符级文本,布局和字体详细信息 | 1.68 |
如前所述:当排除图像提取(最后一列)时,相对速度会发生显著变化:除了 RAWDICT 和 XML 外,其他方法几乎同样快,而 RAWDICT 的执行时间比现在最慢的 XML 要少 40%。
查看附录 1 章节获取更多性能信息。
对本页有任何反馈吗?
本软件按原样提供,不附带任何明示或暗示的保证。本软件根据许可分发,未经授权不得复制、修改或分发。请参考artifex.com获取许可信息或联系 Artifex Software Inc., 39 Mesa Street, Suite 108A, San Francisco CA 94129, United States 获取更多信息。
本文档涵盖了所有 1.24.4 版本及其之前的版本。