大量のPDFに対して一斉にパスワード付与と解除をしたい話
実に2か月ぶりの更新。
やりたいこと
大量のPDFファイル全てにパスワードを付与したい。また、それの解除もしたい。
そしてこの機能を組み込みたいプログラムがPythonで作成中の物なので、できればPythonから動かしたい。
Pythonと連携はしないが、一斉にパスワードを付与するだけならAcrobat Proのアクションウィザードを使えば可能なためこれも試してみたが、パスワードを一斉解除することは出来なかった。
Google大先生に聞いてみる
まずは「Python PDF パスワード」で検索するとヒットするライブラリのPyPDF2を試してみたが、PDFファイルによってエラーが出ることが発覚した。
#エラーの例 PdfReadWarning: Object 105 0 not defined. [pdf.py:1629] Traceback (most recent call last): (中略) File "C:\...\AppData\Local\Programs\Python\Python37\lib\site-packages\PyPDF2\pdf.py", line 482, in write self._sweepIndirectReferences(externalReferenceMap, self._root) File "C:\...\AppData\Local\Programs\Python\Python37\lib\site-packages\PyPDF2\pdf.py", line 571, in _sweepIndirectReferences self._sweepIndirectReferences(externMap, realdata) File "C:\...\AppData\Local\Programs\Python\Python37\lib\site-packages\PyPDF2\pdf.py", line 547, in _sweepIndirectReferences value = self._sweepIndirectReferences(externMap, value) File "C:\...\AppData\Local\Programs\Python\Python37\lib\site-packages\PyPDF2\pdf.py", line 577, in _sweepIndirectReferences newobj = data.pdf.getObject(data) File "C:\...\AppData\Local\Programs\Python\Python37\lib\site-packages\PyPDF2\pdf.py", line 1631, in getObject raise utils.PdfReadError("Could not find object.") PyPDF2.utils.PdfReadError: Could not find object.
検索してみるとGithubやstackoverflowにも同様の事例が報告されている。stackoverflowにPyPDF2内のpdf.pyの中身を書き換える解決法が載っていたため試すも、Acrobatで開けないPDFファイルが出力されてしまい結局駄目だった。
上記のエラーとは別の話だが、改めて調べてみると、そもそもPyPDF2は日本語名フォント入りのファイルが開けなかったり少し日本人的には不便なライブラリであるらしい。
QPDF
仕方がないので別の手段を探した結果、QPDFが使えそうだと判明したため、これを使うことにした。 QPDFが使えるというアイディアはこちらの記事から頂いた。
QPDFはTeXのディストリビューションであるW32TeXを実行すると勝手にインストールされているようなので、TeXユーザーは気付かないうちにインストールされているかもしれない。自分も既に入っていた。
早速試してみたがどうも日本語が入ったファイル名を指定すると、
#inputfileにあ.pdfを指定した時のエラー例 terminate called after throwing an instance of 'QPDFSystemError' what(): open あ.pdf: No such file or directory
と言われて処理が出来ない。
仕方がないので半角英数字に名前を書き換える処理を経由することで動くようにした。QPDFで日本語名ファイルを扱うもっと上手い方法を知っていたらぜひ教えて下さい。
#フォルダ内のPDFファイルに対して一斉にパスワードを付与したり解除したりする import glob import subprocess import os PASSWORD = "password" def decrypt_password(): PDFLIST = glob.glob("*.pdf") for i in PDFLIST: os.rename(i, "decrypt_under_process.pdf") subprocess.call("qpdf --password={} --decrypt {} {}".format(PASSWORD, "decrypt_under_process.pdf", "decrypt_under_process2.pdf")) os.rename("decrypt_under_process2.pdf", i) os.remove("decrypt_under_process.pdf") def encrypt_password(): PDFLIST = glob.glob("*.pdf") for i in PDFLIST: os.rename(i, "encrypt_under_process.pdf") subprocess.call("qpdf --encrypt {} '' 40 -- {} {} ".format(PASSWORD, "encrypt_under_process.pdf", "encrypt_under_process2.pdf")) os.rename("encrypt_under_process2.pdf", i) os.remove("encrypt_under_process.pdf") #decrypt_password() encrypt_password()
QPDFの各種コマンドについては以下を参考にした。
読み取りだけでなく編集もパスワードで制限する等の場合は、encryptのオプション(encrypt_password()のsubprocess.call中--encrypt以降の部分)を適宜変更してください。
コマンドラインプログラムをPythonのsubprocessで実行しているだけなので、他の言語にもそのまま流用可能(なはず)。