表題の通りでちょっとハマったのでメモ。
PDFファイルを読み込み、それを分割して保存する場合、PyPDF2
というモジュールが便利です。
PyPDF2
には、PdfFileReader
とPdfFileWriter
というクラスがあり、Readerで読み込み、Writerで書き込みと、完全に役割が分かれています。
r = PdfFileReader(...)
w = PdfFileWriter()
w.addPage(r.getPages(0))
w.addPage(r.getPages(1))
w.addPage(r.getPages(2))
# …
クラスでラッピングするときの注意
で、PDFファイルの読み込み処理にいろんな処理を挟むときなど、ついついPdfFileReader
をオリジナルのクラスでラッピングしたくなりますが…。
with open(filename, mode="rb") as f:
self.__reader = PdfFileReader(f, strict=False)
# 色々な処理
よくあるプログラムのように、このように書いてしまうと、withを抜けた後にPdfFileWriter
でPDFに書き込みを行うことができません(エラーにはならないですが、出力されるページがすべて真っ白になります)。
PdfFileReader
は読み込みの時に、ファイルをすべて読み込まず、必要な時に適宜ファイルを読み込む実装になっているため、ファイルストリームは開きっぱなしにしておかなければいけません(よく見ると、いちおうPdfFileReader
のドキュメントコメントにそれっぽいことが書いてあります)。
ですので、クラスでラッピングする場合も
self.__fs = open(pdf_filename, mode="rb")
reader = self.__reader = PdfFileReader(self.__fs, strict=False)
# 色々な処理
とし、別途self.__fs.close()
を呼ぶ必要があります。他のサンプルを見てもなぜかwith open("PDF"...) as ...
のような書き方をしていないなあ と思ったら。
クラスをwith文対応にする
せっかくなのでラッピングしたクラスもwith文に対応させておくと後々使いやすいです。これまたいつもと同じですが。
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, tb):
if exc_type is not None:
import traceback
traceback.print_exception(exc_type, exc_value, tb)
self.close()
return self
という定義をクラスのどこかに加えればOKです。__enter__
は「もし__init__
に引数があったとしても」そのままself
をreturn
すればOKです。
また、サンプルコードによっては__exit__
の引数がself
だけになっている場合があるのでこちらも注意です(引数が足りないとwith
文を抜けたときにエラーが発生します)。