常见问题及其解决方案#

如何动态清理损坏的 PDF#

这展示了 PyMuPDF 与另一个 Python PDF 库的潜在用法(优秀的纯 Python 包 pdfrw 在这里作为示例)。

如果需要一个干净、未损坏的 / 解压缩的 PDF,可以动态调用 PyMuPDF 以解决许多问题,如下所示:

import sys
from io import BytesIO
from pdfrw import PdfReader
import pymupdf

#---------------------------------------
# 'Tolerant' PDF reader
#---------------------------------------
def reader(fname, password = None):
    idata = open(fname, "rb").read()  # read the PDF into memory and
    ibuffer = BytesIO(idata)  # convert to stream
    if password is None:
        try:
            return PdfReader(ibuffer)  # if this works: fine!
        except:
            pass

    # either we need a password or it is a problem-PDF
    # create a repaired / decompressed / decrypted version
    doc = pymupdf.open("pdf", ibuffer)
    if password is not None:  # decrypt if password provided
        rc = doc.authenticate(password)
        if not rc > 0:
            raise ValueError("wrong password")
    c = doc.tobytes(garbage=3, deflate=True)
    del doc  # close & delete doc
    return PdfReader(BytesIO(c))  # let pdfrw retry
#---------------------------------------
# Main program
#---------------------------------------
pdf = reader("pymupdf.pdf", password = None) # include a password if necessary
print pdf.Info
# do further processing

使用命令行工具 pdftk (仅在 Windows 可用,但报告显示也可以在 Wine 下运行) 可以实现类似的结果,详见 这里。但是,您必须通过 subprocess.Popen 作为单独的进程调用它,使用 stdin 和 stdout 作为通信工具。

如何将任何文档转换为PDF#

这是一个将任何 PyMuPDF 支持的文档 转换为 PDF 的脚本。这些文档包括 XPS、EPUB、FB2、CBZ 以及包括多页 TIFF 图像在内的图像格式。

它具有维护源文档中包含的任何元数据、目录和链接的功能:

"""
Demo script: Convert input file to a PDF
-----------------------------------------
Intended for multi-page input files like XPS, EPUB etc.

Features:
---------
Recovery of table of contents and links of input file.
While this works well for bookmarks (outlines, table of contents),
links will only work if they are not of type "LINK_NAMED".
This link type is skipped by the script.

For XPS and EPUB input, internal links however **are** of type "LINK_NAMED".
Base library MuPDF does not resolve them to page numbers.

So, for anyone expert enough to know the internal structure of these
document types, can further interpret and resolve these link types.

Dependencies
--------------
PyMuPDF v1.14.0+
"""
import sys
import pymupdf
if not (list(map(int, pymupdf.VersionBind.split("."))) >= [1,14,0]):
    raise SystemExit("need PyMuPDF v1.14.0+")
fn = sys.argv[1]

print("Converting '%s' to '%s.pdf'" % (fn, fn))

doc = pymupdf.open(fn)

b = doc.convert_to_pdf()  # convert to pdf
pdf = pymupdf.open("pdf", b)  # open as pdf

toc= doc.get_toc()  # table of contents of input
pdf.set_toc(toc)  # simply set it for output
meta = doc.metadata  # read and set metadata
if not meta["producer"]:
    meta["producer"] = "PyMuPDF v" + pymupdf.VersionBind

if not meta["creator"]:
    meta["creator"] = "PyMuPDF PDF converter"
meta["modDate"] = pymupdf.get_pdf_now()
meta["creationDate"] = meta["modDate"]
pdf.set_metadata(meta)

# now process the links
link_cnti = 0
link_skip = 0
for pinput in doc:  # iterate through input pages
    links = pinput.get_links()  # get list of links
    link_cnti += len(links)  # count how many
    pout = pdf[pinput.number]  # read corresp. output page
    for l in links:  # iterate though the links
        if l["kind"] == pymupdf.LINK_NAMED:  # we do not handle named links
            print("named link page", pinput.number, l)
            link_skip += 1  # count them
            continue
        pout.insert_link(l)  # simply output the others

# save the conversion result
pdf.save(fn + ".pdf", garbage=4, deflate=True)
# say how many named links we skipped
if link_cnti > 0:
    print("Skipped %i named links of a total of %i in input." % (link_skip, link_cnti))

更改注解:意外行为#

问题#

有两种情况:

  1. 更新由其他软件创建的注释,使用PyMuPDF。

  2. 创建一个使用PyMuPDF的注释,然后用其他软件进行更改。

在这两种情况下,您可能会遇到意想不到的变化,例如不同的注释图标或文本字体,填充颜色或线条虚线消失,线条结束符号的大小改变甚至消失等。

原因#

注释维护在每个PDF维护应用程序中处理方式不同。某些注释类型可能不受支持,或者支持不完全,或者某些细节可能以与其他应用程序不同的方式处理。没有标准。

几乎所有的 PDF 应用程序也会配备其自己的图标(文件附件、便签和印章)以及其支持的文本字体集。例如:

  • (Py-) MuPDF 仅支持这 5 种基本字体用于“自由文本”注释:Helvetica、Times-Roman、Courier、ZapfDingbats 和 Symbol – 不支持斜体/粗体变体。当更改由其他应用程序创建的“自由文本”注释时,其字体将可能无法识别或接受,并将被替换为 Helvetica。

  • PyMuPDF 支持所有 PDF 文本标记(突出显示、下划线、删除线、波浪线),但这些类型无法通过 Adobe Acrobat Reader 更新。

在大多数情况下,也存在对虚线的有限支持,这导致现有的破折号被直线替代。例如:

  • PyMuPDF 完全支持所有线条虚线形式,而其他查看器只接受有限的子集。

解决方案#

不幸的是,在大多数情况下,您能做的事情并不多。

  1. 保持使用相同的软件来创建和修改注释。

  2. 当使用 PyMuPDF 更改一个“外来”注释时,尽量 避免 Annot.update()。以下方法 在不使用它的情况下可以使用, 以便保持原始外观:

缺失或无法读取的提取文本#

相当常见的是,文本提取并不像你预期的那样工作:文本可能缺失,或者可能不出现在你屏幕上可见的阅读顺序中,或者包含乱码字符(如?或“豆腐”符号)等。这可能是由许多不同的问题引起的。

问题:没有提取到文本#

您的PDF查看器确实显示文本,但您无法用光标选择它,文本提取没有任何结果。

原因#

  1. 您可能正在查看嵌入在PDF页面中的图像(例如,扫描的PDF)。

  2. PDF 创建者没有使用字体,而是通过绘制小线条和曲线来模拟文本。例如,大写字母“D”可以通过“一条线“|”和一个左开半圆绘制,一个“o”可以通过一个椭圆绘制,等等。

解决方案#

使用像 OCRmyPDF 这样的OCR软件在可见页面下插入一个隐藏的文本层。生成的PDF应该按预期工作。

问题:无法阅读的文本#

文本提取未按可读顺序交付文本,重复了一些文本,或以其他方式混乱。

原因#

  1. 单个字符可以如此读取(没有“”符号),但文本在文件中的编码顺序偏离了阅读顺序。这样做的动机可能是技术性的或为了保护数据免受不必要的复制。

  2. 许多“”符号出现,这表明MuPDF无法解释这些字符。这种字体可能确实不被MuPDF支持,或者PDF创建者可能使用了一种能够显示可读文本的字体,但故意混淆了其对应的unicode字符。

解决方案#

  1. 使用保持布局的文本提取: python -m fitz gettext file.pdf.

  2. 如果其他文本提取工具也不起作用,那么唯一的解决方案就是对页面进行OCR识别。


本软件按原样提供,不作任何明示或暗示的担保。该软件根据许可证分发,除非按照该许可证的条款明确授权,否则不得复制、修改或分发。有关许可信息,请参阅artifex.com或联系Artifex Software Inc.,地址:39 Mesa Street, Suite 108A, San Francisco CA 94129, United States以获取更多信息。