コンテンツストリームの処理を追加する素晴らしいPDFRWライブラリの拡張機能(およびその中で参照されるすべてのオブジェクトなど、画像、フォントなど)を可能な限りシンプルに保ちます。
それでは、PYPDFのような本格的なPDFライブラリがあるのに、なぜPDFRWを拡張するのでしょうか?このように考えてください:ほとんどのPDFライブラリは、一般的なPDF処理タスクを簡素化する関数を提供しようとします。これらのライブラリは、彼らがすることに非常に優れていますが、たまに新しい機能を必要とするタスクが出てきます。今、あなたがそのような状況にある開発者である場合、これらのライブラリの大量のソースコードを掘り始めなければなりません。
PDFRWのアプローチは異なります。結果を可能な限りシンプルに保ちながら、PDFを可能な限り解析しましょう。特に、PDFオブジェクトモデル(他の辞書への参照である値の一部が単なる辞書である)が標準のPython辞書へのマッピングに非常に適しているという事実を考えると、悪い考えではありません。それに加えて、 PDFRWは、PDFオブジェクトをさらに単純にするために、別の素晴らしいパッケージの責任の背後にあるアイデアを実装しています。たとえば、ページフォントへのアクセスは、次のように行うことができます。
for fontName , fontDict in page . Resources . Font . items ():
( do something )次のステップは、辞書のストリームを解析しようとすることです。これらは、テキスト、画像、ベクターグラフィックスなどの興味深いものを含む辞書の特別なエントリです。PDFRWはそれを実行しません。生成するデータ構造は完全です。PDFのすべてが含まれており、可能な処理タスクに十分です。そして、それは十分に簡単ですので、開発者はすぐにコーディングを開始し、AdobeのMagnum Opusの通路を楽しむのにもっと時間を費やすことができます。そして、実際、 PDFRWは、PDF構文を実行することでPDF構文を学ぶのに役立つ理想的なツールです!
わかりました、もしあなたがこの時点に到達したら、あなたは不思議に思っている可能性があります:それで、私はここからどこに行きますか?具体的には、辞書ストリームを解析するにはどうすればよいですか?これは、 PDFRWXが役立つ場所です。辞書ストリームを解析し、他の便利なことを行うことができます。しかし、最初のこと:
PDFRWXは何よりもまず、上記のPDFRWの哲学を維持しようとします。これに、多くのPDF処理タスクでは、ほとんどの時間がソフトウェアソリューションの開発に費やされ、実行するのではないという観察が追加されます。これは、デザインの選択につながります:
今、私たちはPDFRWXが何をしているのか、それがそれを達成する方法を見る準備ができました:
examples.pdfを読み取り、すべてのページからすべてのテキストコンテンツを削除し、結果をexample-out.pdfに書き込みます。
from pdfrw import PdfReader , PdfWriter , PdfArray
from pdfrwx . pdffilter import PdfFilter
from pdfrwx . pdfstreamparser import PdfStream
toArray = lambda obj : obj if isinstance ( obj , PdfArray )
else PdfArray ([ obj ]) if obj != None else PdfArray ()
pdfIn = PdfReader ( 'example.pdf' )
pdfOut = PdfWriter ( 'example-out.pdf' )
for page in pdfIn . pages :
contentsArray = toArray ( page . Contents )
for contents in contentsArray :
stream = PdfFilter . uncompress ( contents ). stream
treeIn = PdfStream . stream_to_tree ( stream )
treeOut = []
for leaf in treeIn :
cmd , args = leaf [: 2 ]
if cmd != 'BT' : treeOut . append ( leaf )
contents . stream = PdfStream . tree_to_stream ( treeOut )
contents . Filter = None
pdfOut . addPage ( page )
pdfOut . write ()ご覧のとおり、コードはページで実行され、すべてのページのコンテンツを越えて、コンテンツを圧縮して圧縮してから圧縮してから、コンテンツストリームをツリーに解析し、BT/ETテキストブロックを削除してから、結果のツリーをストリームに戻します。上記の各コード行の意味は、おそらくToarray Lamda関数とは別に明確でなければなりません。PDFのページには複数のコンテンツ辞書、その場合はPage.ContentsがPDFARRAYであり、それぞれの要素を個別に処理する必要があります。したがって、Toarray Lambda関数は、1つの要素を持つPDFARRAYに配列ではないコンテンツを回すことにより、ページ内容の状況をより均一にします。
他のいくつかのことも注意する必要があります。まず、タスクを非表示にするために、コードはPDFRWXの2つの新しいクラスのみを使用していることに注意してください:pdffilterとpdfStreamはそれぞれ1つ/2つの関数呼び出しを使用します。第二に、解析されたツリー私は、次の些細な形式のネストされた標準Pythonリストだけです。
[
['q', []],
['cm', ['1','0','0','1','0','0']],
...
['BT', [], [ /a tree list of text operators/ ]],
...
['Q', []]
]
したがって、ツリーリストの各葉(要素)は2つまたは3つの要素のリストです。最初の2つの要素はコマンドと引数のリスト(コマンドに引数がない場合は空のリスト)です。一方、3番目のオプションの引数は、葉がコマンドのブロックである場合に存在します。設計上、ブロックには2種類しかありません:解析ツリーにコマンド名「BT」を使用するBT/ETテキストブロックと、解析されたツリーにコマンド名「BI」を使用するBI/ID/EIインライン画像ブロック。元のPDFストリームには、一連のコマンドがあることに注意してください。便宜上、これらのブロックを出力に作成するのはPDFSTREAMパーサーです。ストリームパーサーの出力の構造にさらに慣れるには、パーサーへの呼び出し直後にpprint(treein)のようなコマンドを挿入してみてください。
また、 ToArray関数はどんなに有用であってもモジュールに実装されていないため、解析するたびにコーディングする必要があることに注意してください。これは奇妙に聞こえるかもしれませんが、それは同じ設計原則の結果です。モジュールはストリームを解析するだけです。他のすべてをコーディングするために開発者次第です。
PDFSTREAMパーサークラスは、David Beazleyによる人気のある(および純粋なPython!)Sly Parserジェネレーターライブラリの助けを借りて、約300行のコードを使用して純粋なPythonに実装されています。好奇心の強い:パーサーは、PDFリテラル文字列(括弧内の文字列)などを解析するために、2つのパーサー状態(つまり、動作中に自分自身を切り替える異なる文法を持つ2つの異なるパーサー)を使用します。 YEP、文字通りの文字列が文字列の一部としてエンコーディングの括弧をサポートするために、PDFリテラル文字列の形式は非常に複雑になっているため、それらを解析するためだけに別のパーサーを必要とします。したがって、何らかの理由で(速度?)、別のパーサージェネレーターライブラリを使用してストリームパーサーを実装したい場合は、パーサー状態のスタックをサポートしていることを確認してください。
フォント地獄を解決します。すぐに来て、お楽しみに。
サポートされているフィルター:
画像エクスポートの色の精度に関しては、Adobe自身の製品のバグにうんざりしていませんか?それからあなたは正しい場所に来ました!すべての中で最も正確なPDF画像操作クラスになることを目指しています。すぐに来て、お楽しみに。
サポートされているコーデック(エンコード/デコード):
サポートされているカラースペース:
プラス:
この時点でモジュールは完全に使用可能であり、多数のテストを実行して、それが正しく行うことを約束することを確認するようにしています。ただし、アルファに近いところにはありません。インターフェイスはまだ完全に確定していません。さらに、エラー処理は壊れています(おそらくすぐに修正され、 PDFRWでエラーが処理される方法と同様になります)。だから、あなた自身の責任で遊んでくださいリスクなしでは、まだ生産品質があるとは思わないでください。