LoginSignup
0
0

ディレクトリ構造を崩さずに、PDFのパスワードを一括解除する

Last updated at Posted at 2021-05-03

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を配置します。

シェルスクリプトを使った書き方

ディレクトリ構造だけをコピーする部分が冗長になってしまいました。よりよい方法があれば是非ご一報下さい。

Qpdfのインストール
> sudo apt update
> sudo apt install qpdf

Qpdfが未導入の場合は導入して下さい

execute.sh
#! /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の場合は以下の通りです。

password.txt
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というディレクトリが既に存在しているとエラーになります。

参考記事

QPDFを用いたPDF便利操作集

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0