概要

“PythonでPDFの帳票を作る”という記事で、帳票を作るというシナリオ上PyPDF3で全ての権限を不可にする設定をしました。
PDFファイルの権限を制御する際、Python用のライブラリであるPyPDF3は簡単に扱えますが、現在非推奨であり、細かな権限設定ができません。
そのため、Java製のツールであるPDFBoxを使用することで、きめ細かい権限制御が可能になります。
本記事では、PythonとPDFBoxを連携させる方法を説明します。
インストール等
pdfbox
ここから pdfbox-app-3.0.3.jar をダウンロードして使います。
Py4j
pip install py4j
としてインストールします。
javaと通信するために使います。
PDFBox
pdfbox-app-x.x.x.jar は、コマンドとしても使えます。
java -jar pdfbox-app-3.0.3.jar Encrypt -O 1111 -canPrint -canExtractContent -i input.pdf -o output.pdf
このように実行すると input.pdf を読み込んで 出力ファイル output.pdf が以下の様に設定されます。
- オーナーパスワード : “1111”
- “-canPrint” : 印刷不可
- “-canExtractContent” : コピー不可
権限設定の確認
AcrobatReader で確認できる権限は、
Acrobat Readerでは一部の権限が「許可されていない」と表示される場合がありますが、これはViewerによる制約です。
詳細な権限は、Adobe Acrobat Proなどの専用ソフトで確認できます。
そこで、次のコードで確認できるようにします。
PDFの権限設定確認コード
以下のコードを環境に合わせて pdfbox-app-x.x.x.jar の場所などを変更して使ってください。
from py4j.java_gateway import JavaGateway, GatewayParameters, launch_gateway
# PDFBoxのJARファイルのパスを指定
PDFBOX_JAR_PATH = r"C:\Users\[ユーザー名]\Documents\Develop" \
r"\Python\test01\pdfbox-app-3.0.3.jar" # 環境に合わせて変更
def read_pdf_permissions(file_path):
# Gatewayを起動
port = launch_gateway(classpath=PDFBOX_JAR_PATH)
gateway = JavaGateway(gateway_parameters=GatewayParameters(port=port))
try:
# Javaクラスのロード
Loader = gateway.jvm.org.apache.pdfbox.Loader
Permission = gateway.jvm.org.apache.pdfbox.pdmodel.encryption.AccessPermission
# PDFを読み込む
document = Loader.loadPDF(gateway.jvm.java.io.File(file_path))
permissions = document.getCurrentAccessPermission() # ドキュメントの権限を取得
# 許可設定を取得
permissions_dict = {
"can_print": permissions.canPrint(),
"can_modify": permissions.canModify(),
"can _Assemble_document": permissions.canAssembleDocument(),
"can_extract_content": permissions.canExtractContent(),
"can_fill_in_form": permissions.canFillInForm(),
"can_print_faithful": permissions.canPrintFaithful(),
"can_modify_annotations": permissions.canModifyAnnotations(),
"can_extract_for_accessibility": permissions.canExtractForAccessibility(),
"can_assemble_document": permissions.canAssembleDocument(),
"is_read_only": permissions.isReadOnly(),
}
document.close()
return permissions_dict
finally:
gateway.shutdown()
# 使用例
if __name__ == "__main__":
pdf_path = "output.pdf" # 読み取りたいPDFファイルのパス
permissions = read_pdf_permissions(pdf_path)
print("PDF Permissions:")
for key, value in permissions.items():
print(f"{key}: {value}")

コード実行結果
このようにPDFの権限設定が表示されます。
権限の種類
以下は、コードの一部です。
# アクセス許可を設定
access_permission = AccessPermission()
access_permission.setCanPrint(False) # 印刷を許可するか (true: 許可, false: 禁止)
# access_permission.setCanModify(False) # コンテンツの変更を許可するか
access_permission.setCanExtractContent(False) # テキストや画像の抽出を許可するか
# access_permission.setCanExtractForAccessibility(False) # アクセシビリティのためのテキスト抽出を許可するか
# access_permission.setCanFillInForm(False) # フォームの記入を許可するか
# access_permission.setCanModifyAnnotations(False) # 注釈やフォームフィールドの変更を許可するか
access_permission.setCanAssembleDocument(False) # ドキュメントの結合を許可するか
# access_permission.setCanPrintFaithful(False) # 印刷時の品質を維持するか
# access_permission.setReadOnly() # 文書アセンブリ不可、ページの抽出不可
これだけのものが設定で変更できます。
setreadOnly() は、他の設定を無視してすべて不可にする設定です。
権限は印刷可能と設定されていても、無視され印刷されません。
詳しくは、以下にドキュメントがありますので参考にしてください。
org.apache.pdfbox.pdmodel.encryption.AccessPermission のメソッドの説明をご覧ください。
既存のPDFに権限設定をするコード
コード
from py4j.java_gateway import JavaGateway, GatewayParameters, launch_gateway
# JVMを起動
port = launch_gateway(classpath=r"C:\Users\[ユーザー名]\Documents" \
r"\Develop\Python\test01\pdfbox-app-3.0.3.jar")
gateway = JavaGateway(gateway_parameters=GatewayParameters(port=port))
# JavaのPDFBox APIを取得
Loader = gateway.jvm.org.apache.pdfbox.Loader
PDDocument = gateway.jvm.org.apache.pdfbox.pdmodel.PDDocument
AccessPermission = gateway.jvm.org.apache.pdfbox.pdmodel.encryption.AccessPermission
StandardProtectionPolicy = gateway.jvm.org.apache.pdfbox. \
pdmodel.encryption.StandardProtectionPolicy
try:
# 既存のPDFファイルを読み込み
input_pdf_path = r"input.pdf" # 入力PDFのパス
output_pdf_path = r"output.pdf" # 出力PDFのパス
owner_password = "1111" # オーナーパスワード
user_password = "" # ユーザーパスワード
# PDFをロード
document = Loader.loadPDF(gateway.jvm.java.io.File(input_pdf_path))
# アクセス許可を設定
access_permission = AccessPermission()
access_permission.setCanPrint(True) # 印刷を許可するか (true: 許可, false: 禁止)
access_permission.setCanModify(False) # コンテンツの変更
access_permission.setCanExtractContent(False) # テキストや画像の抽出
access_permission.setCanExtractForAccessibility(False) # アクセシビリティのためのテキスト抽出
access_permission.setCanFillInForm(False) # フォームの記入
access_permission.setCanModifyAnnotations(False) # 注釈やフォームフィールドの変更
access_permission.setCanAssembleDocument(False) # ドキュメントの結合
access_permission.setCanPrintFaithful(False) # 印刷時の品質を維持するか
# access_permission.setReadOnly() # 他の設定は無視 読み取り専用にするか
# 保護ポリシーを作成
spp = StandardProtectionPolicy(owner_password, user_password, access_permission)
spp.setEncryptionKeyLength(128)
document.protect(spp)
# 保護されたPDFを保存
document.save(output_pdf_path)
document.close()
print(f"権限設定したのPDFを作成しました: {output_pdf_path}")
except Exception as e:
print(f"エラーが発生しました: {e}")
コード解説
Py4jを使って pdfbox-app-3.0.3.jar と通信を行ってコードを実行しています。
from py4j.java_gateway import JavaGateway, GatewayParameters, launch_gateway # JVMを起動 port = launch_gateway(classpath=r"C:\Users\[ユーザー名]\Documents" \ r"\Develop\Python\test01\pdfbox-app-3.0.3.jar") gateway = JavaGateway(gateway_parameters=GatewayParameters(port=port))
gateway を使ってJavaのクラスを呼び出します。
# JavaのPDFBox APIを取得
Loader = gateway.jvm.org.apache.pdfbox.Loader
PDDocument = gateway.jvm.org.apache.pdfbox.pdmodel.PDDocument
AccessPermission = gateway.jvm.org.apache.pdfbox.pdmodel.encryption.AccessPermission
StandardProtectionPolicy = gateway.jvm.org.apache.pdfbox. \
pdmodel.encryption.StandardProtectionPolicy
PythonのコードでつかうJavaのクラスを取得します。
owner_password = "1111" # オーナーパスワード user_password = "" # ユーザーパスワード
ユーザーパスワードを掛けるとファイルを開くときにパスワードを要求されます。
コードとしては、省略できないので空のパスワードを入れるとパスワードを
要求されません。
オーナーパスワードは、権限設定をする場合は必須です。
意味合い的に、誰でも権限を変更可能ではセキュリティの意味がないからです。
# 保護ポリシーを作成
spp = StandardProtectionPolicy(owner_password, user_password, access_permission)
spp.setEncryptionKeyLength(128)
document.protect(spp)
各種、権限の設定をしたらここで実際にセキュリティを掛けます。
StandardProtectionPolicy()で パスワードと権限設定を保存します。
spp.setEncryptionKeyLength() で 128bitの鍵長を使用してAES暗号化を行い、PDFの権限設定を保護します。
document.protect() で実際に暗号化します。
そして、ファイルを出力して完了です。
まとめ
実務では欠かせないPDFのセキュリティについて説明しました。
PDFの権限設定をPDFBoxを使って細かに設定する方法を説明しました。
権限設定の確認方法と権限の種類についても説明しました。
そして既存のPDFを取り込んで権限設定を設定する方法を説明しました。
これでPythonで帳票などを作る際も安心して使えるのではないでしょうか。
次回は、既存のPDFファイルを書き換えるコードを説明予定です。
お楽しみに。
Commnts