Summary
1箇所のフォルダにまとめたPDFファイルを、ディレクトリ構造はそのままに、パスワード保護を解除します。パスワードは既知です。
《追記》Pythonで再帰的プログラミングを使う方法を別の方に教えていただきました。
いきさつ
全て同じパスワードでロックされた3階層にまたがる48ディレクトリ・806のPDFファイルのパスワード保護を解除したくなりました。大学で配布される資料が漏洩時に備えてすべてロックされているけれど、私的利用に限って解除したくなることありますよね...?
※配布者に無断でロック解除したファイルを再頒布する行為はお控え下さい。
環境
- Windows10 ビルド19042
- WSL2 Ubuntu 20.04.2 LTS
- Qpdf
- zsh
ディレクトリ構成
.
├── execute.sh
├── password.txt
└── pdf_input #このディレクトリ以下のPDFを全てロック解除したい
├── hoge1
├── fuga1
︙
└── fugaN
├── hoge2
︙
└── hoge6
今回作ったシェルスクリプトexecute.sh
と同じディレクトリに、パスワードだけを記述したpassword.txt
と、変換対象のディレクトリpdf_input
を配置します。
シェルスクリプトを使った書き方
ディレクトリ構造だけをコピーする部分が冗長になってしまいました。よりよい方法があれば是非ご一報下さい。
> sudo apt update
> sudo apt install qpdf
Qpdfが未導入の場合は導入して下さい
#! /bin/zsh
# パスワードの取得
PASSWORD=`find -iname password.txt | xargs cat | head -n 1`
# ディレクトリ構造のコピー
mkdir temp_dir
find pdf_input -type d | xargs -I DIR mkdir temp_dir/DIR
mv temp_dir/pdf_input pdf_output
rm -R temp_dir
# 進捗表示用にファイル数を取得
FILE_NUMBER=`find pdf_input -name "*.pdf" | wc -l`
COUNT=0
# ファイルの復号化
for FILE_IN in `find pdf_input -name "*.pdf"`; do
(( COUNT += 1 ))
FILE_OUT=`echo ${FILE_IN} |
sed -e "s/pdf_input\(.*\)\.pdf/pdf_output\1_unlocked\.pdf/g"`
qpdf --decrypt --password=${PASSWORD} ${FILE_IN} ${FILE_OUT}
printf '\r Progress : [%d/%d]' ${COUNT} ${FILE_NUMBER}
done
また、パスワードを別途password.txt
に保存します。パスワードがpiyopiyo123の場合は以下の通りです。
piyopiyo123
ここまで準備ができたら実行するだけです。
> ./execute.sh
実行が完了すると、pdf_output
という名前のディレクトリにパスワード解除済みのPDFファイルが生成されています。なお、解除後のファイル名には「_unlocked」を付しています。
再帰的プログラミングを使った書き方
※これは別のアプローチです。結果に変わりはありません。
やりたいことを整理しなおすと、それぞれのディレクトリ階層において、
「あるオブジェクトがpdfファイルであれば複合化し、ディレクトリであれば、そのディレクトリ下でこのかぎかっこ内のプログラムと同様の処理をするプログラム」
が全ての階層で行われればよいことになります。このような同一処理が繰り返し出現する構造の課題に対しては、再帰的プログラミングを用いるとコードが簡潔になったりします。
参考までにpythonコードを載せておきます。
import os
import pikepdf
password = 'looool'
target_dir = '/pdf_input'
os.mkdir(os.getcwd() + '/pdf_output')
def recursive_decrypt(x, y):
for f in os.listdir(x):
ObjPath = x + '/' + f
NewObjPath = y + '/' + f
if os.path.isfile(ObjPath) and ".pdf" in str(ObjPath):
pdf = pikepdf.open(ObjPath, password)
pdf.save(NewObjPath)
elif os.path.isdir(ObjPath):
os.mkdir(NewObjPath)
recursive_decrypt(ObjPath, NewObjPath)
recursive_decrypt(os.getcwd() + target_dir, os.getcwd() + '/pdf_output')
トラブルシューティング
シェルスクリプトを実行できない場合は以下の@1915kekeさんの記事をどうぞ。
シェルスクリプトを実行できるようになるまで(基本知識)
また、pdf_output
というディレクトリが既に存在しているとエラーになります。