一般的な問題とその解決方法#

壊れた PDF を動的にクリーンアップする方法#

これは、別のPython PDFライブラリ(素晴らしい純粋なPythonパッケージである pdfrw を例として使用しています)と組み合わせてPyMuPDFを潜在的に使用する方法を示しています。

クリーンで壊れていない / 解凍されていない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 下でも動作すると報告されています)を使用すると、類似の結果を得ることができます。詳細は こちら をご覧ください。ただし、stdinとstdoutを通信手段として使用して別のプロセスとしてsubprocess.Popenを介して呼び出す必要があります。

どの文書も 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))

MuPDF から発行されるメッセージの処理方法#

PyMuPDF v1.16.0以降、基盤となるMuPDFライブラリから発行されるエラーメッセージは、Python標準デバイス sys.stderr にリダイレクトされます。したがって、これらのメッセージはこのデバイスに出力される他の出力と同様に扱うことができます。

さらに、これらのメッセージはMuPDFの警告とともに内部バッファに送られます - 以下を参照してください。

これらのメッセージは常に識別用の文字列 "mupdf:" で始まります。復旧可能なMuPDFエラーを全く表示したくない場合は、コマンド pymupdf.TOOLS.mupdf_display_errors(False) を発行してください。

MuPDFの警告は引き続き内部バッファに保存され、 Tools.mupdf_warnings() を使用して表示できます。

MuPDFのエラーがPython例外につながる場合とつながらない場合があることに注意してください。言い換えれば、MuPDFが回復して処理を続けることができるエラーメッセージが表示される可能性があります。

回復可能なエラーの例出力です。損傷したPDFを開いていますが、MuPDFはそれを修復し、何が起こったかに関する少しの情報を提供しています。その後、ドキュメントが後で増分保存できるかどうかを調べる方法も示しています。この時点で Document.is_dirty 属性をチェックすることも、 pymupdf.open の際にドキュメントを修復する必要があったことを示しています:

>>> import pymupdf
>>> doc = pymupdf.open("damaged-file.pdf")  # leads to a sys.stderr message:
mupdf: cannot find startxref
>>> print(pymupdf.TOOLS.mupdf_warnings())  # check if there is more info:
cannot find startxref
trying to repair broken xref
repairing PDF document
object missing 'endobj' token
>>> doc.can_save_incrementally()  # this is to be expected:
False
>>> # the following indicates whether there are updates so far
>>> # this is the case because of the repair actions:
>>> doc.is_dirty
True
>>> # the document has nevertheless been created:
>>> doc
pymupdf.Document('damaged-file.pdf')
>>> # we now know that any save must occur to a new file

**復旧できないエラー**の例出力:

>>> import pymupdf
>>> doc = pymupdf.open("does-not-exist.pdf")
mupdf: cannot open does-not-exist.pdf: No such file or directory
Traceback (most recent call last):
  File "<pyshell#1>", line 1, in <module>
    doc = pymupdf.open("does-not-exist.pdf")
  File "C:\Users\Jorj\AppData\Local\Programs\Python\Python37\lib\site-packages\fitz\pymupdf.py", line 2200, in __init__
    _pymupdf.Document_swiginit(self, _pymupdf.new_Document(filename, stream, filetype, rect, width, height, fontsize))
RuntimeError: cannot open does-not-exist.pdf: No such file or directory
>>>

注釈の変更:予期しない動作#

問題#

2つのシナリオがあります:

  1. 他のソフトウェアで作成された注釈をPyMuPDFで**更新**する。

  2. PyMuPDFで注釈を**作成**し、後で他のソフトウェアで変更する。

どちらの場合でも、異なる注釈アイコンやテキストフォント、塗りつぶしの色や線の破線が消えたり、線の端のシンボルのサイズが変わったり、さらには消えたりするなど、意図しない変更が発生する可能性があります。

原因#

注釈の保守は、各PDF保守アプリケーションごとに異なる方法で処理されます。一部の注釈タイプはサポートされないか、完全にはサポートされていない場合もあり、また、他のアプリケーションとは異なる方法で詳細が処理される場合もあります。標準規格は存在しません。

ほとんどの場合、PDFアプリケーションには独自のアイコン(ファイル添付、付箋、スタンプなど)とサポートされるテキストフォントのセットが付属しています。例えば:

  • (Py-) MuPDFは、'FreeText'注釈に対してこれらの5つの基本フォントのみをサポートしています:Helvetica、Times-Roman、Courier、ZapfDingbats、Symbol - 斜体や太字のバリエーションはありません。他のアプリで作成された 'FreeText' 注釈を変更する際、そのフォントはおそらく認識されず、Helveticaに置き換えられる可能性があります。

  • PyMuPDFはすべてのPDFテキストマーカー(ハイライト、下線、取り消し線、波線)をサポートしていますが、これらのタイプはAdobe Acrobat Readerで更新することはできません。

ほとんどの場合、破線には限定的なサポートが存在し、既存の破線が直線に置き換えられることがあります。例えば:

  • PyMuPDFはすべての線の破線形式を完全にサポートしていますが、他のビューアは一部の形式しか受け入れません。

解決策#

残念ながら、これらの多くの場合、あまり対処できることはありません。

  1. 注釈の作成と変更に同じソフトウェアを使用してください。

  2. "異なる"注釈を変更する際にPyMuPDFを使用する場合、Annot.update() を避けるようにしてください。次のメソッドは、元の外観を維持するために、Annot.update() を使用せずに使用できます:

欠落または読み取り不可能な抽出テキスト#

非常にしばしば、テキスト抽出が期待通りに機能しないことがあります。テキストが欠落しているか、画面上で表示される読み取り順序に表示されないか、文字が文字化けしている(「?」や「TOFU」シンボルなど)ことがあります。これはさまざまな問題が原因で起こる可能性があります。

問題:テキストが抽出されない#

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でページを処理するのが唯一の解決策です。


This software is provided AS-IS with no warranty, either express or implied. This software is distributed under license and may not be copied, modified or distributed except as expressly authorized under the terms of that license. Refer to licensing information at artifex.com or contact Artifex Software Inc., 39 Mesa Street, Suite 108A, San Francisco CA 94129, United States for further information.

Discord logo