[Python] PDF 파일 잠금 해제하기
- 자아실현/파이썬
- 2020. 11. 28.
1. 개요
일을 하다보면 암호가 걸려있는 PDF 파일을 수정해야 할 일이 있습니다. 물론 서명을 위조하거나, 암호를 알아내는 것은 불가능하지만, 파일 내용을 그대로 복사하여 내용이 동일한 새로운 PDF 파일을 생성하는 방법으로 잠금을 해제하여 사용할 수 있습니다. (읽기가 막혀있는 파일은 안 됩니다.)
다시 말해서, 암호가 걸려있는 파일과 동일한 내용의 새로운 파일을 생성하는 것입니다.
2. 방법
pikepdf(pypi.org/project/pikepdf/)를 이용합니다.
준비물
1) pikepdf를 설치합니다.
커맨드창에서 입력
pip install pikepdf
2) 테스트를 위한 PDF 파일을 준비합니다.
코드 순서
1) PDF 파일을 열고, 2) 내용을 복사하고 3)새로운 파일로 저장합니다.
과거에는 PyPDF2를 사용했으나, Acrobat이 아닌 다른 프로그램으로 작성한 PDF 파일은 오류가 발생하여 pikepdf로 변경하였고, 여러 파일에서 테스트 결과 문제 없이 작동이 잘 됩니다.
(PyPDF2.utils.PdfReadError: File has not been decrypted 에러 발생)
3. 코드
main2.py
import pikepdf
input_pdf = pikepdf.Pdf.open('test.pdf')
pdf = pikepdf.Pdf.new()
for n, page in enumerate(input_pdf.pages):
pdf.pages.append(page)
pdf.save('test_decrypted.pdf')
별도 설명이 필요가 없을 정도로 간단합니다.
(1) input_pdf 변수에 test.pdf 파일을 불러오고,
(2) pdf 변수에 새로운 PDF 인스턴스를 생성합니다.
(3) input_pdf 파일을 읽어서 내용을 pdf 변수에 추가(append)하고
(4) test.decrypted.pdf 파일로 저장합니다.
다듬은 버전
실행할 때 파일 이름을 입력하도록 변경하였습니다.
main.py
import os
import pikepdf
def decrypt_pdf(file_name):
input_path = os.path.join(os.getcwd(), file_name)
output_path = os.path.join(os.getcwd(), "decrypted_"+file_name)
input_pdf = pikepdf.Pdf.open(input_path)
pdf = pikepdf.Pdf.new()
for _, page in enumerate(input_pdf.pages):
pdf.pages.append(page)
pdf.save(output_path)
input_pdf.close()
print("saved at : {}".format(output_path))
if __name__ == "__main__":
if len(os.sys.argv)>1:
decrypt_pdf(os.sys.argv[1])
else:
print("re-run with file name")
위와 거의 동일하지만,
1) 파일 이름 앞에 'decrypted_'가 들어가도록 변경하였고
2) 커맨드라인에서 파일 이름과 함께 실행하도록 바꾸었습니다.
아래와 같은 방식으로 커맨드라인에서 실행합니다.
python main.py test.pdf
작동 영상
4. GUI로 만들기 (번외)
최근 PyQt를 조금 다룰 수 있게 되어, GUI 프로그램으로 만들어 보았습니다.
'Select' 버튼을 눌러 파일을 선택하고 'Run' 버튼을 누르면 선택된 파일이 같은 경로에 암호 없이 복사됩니다.
코드
gui.py
import os
import sys
import pikepdf
from PyQt5.QtWidgets import *
class Window(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("decrypt pdf")
self.setGeometry(350,150,400,100)
self.UI()
def UI(self):
vbox=QVBoxLayout()
hbox1=QHBoxLayout()
hbox2=QHBoxLayout()
self.pathEdit=QLineEdit()
fileButton = QPushButton("Select")
fileButton.clicked.connect(self.openFile)
runButton = QPushButton("Run")
runButton.clicked.connect(self.convert)
hbox1.addWidget(self.pathEdit)
hbox1.addWidget(fileButton)
vbox.addLayout(hbox1)
vbox.addLayout(hbox2)
hbox2.addStretch()
hbox2.addWidget(runButton)
hbox2.addStretch()
self.setLayout(vbox)
self.show()
def openFile(self):
url = QFileDialog.getOpenFileName(self,"Select a File","","*.pdf")
fileUrl = url[0]
self.pathEdit.setText(fileUrl)
def convert(self):
file_path = self.pathEdit.text()
if file_path == "":
QMessageBox.about(self,"select a file")
input_pdf = pikepdf.Pdf.open(file_path)
pdf = pikepdf.Pdf.new()
for _, page in enumerate(input_pdf.pages):
pdf.pages.append(page)
pdf.save(os.path.splitext(file_path)[0]+"_decrypt.pdf")
QMessageBox.about(self,"done", "<p align='center'>Saved<br>"+os.path.splitext(file_path)[0]+"_decrypt.pdf</p>")
print("done")
def main():
App = QApplication(sys.argv)
window = Window()
sys.exit(App.exec_())
if __name__ == '__main__':
main()
모든 코드는 아래 깃허브에 있습니다.
감사합니다.
github 주소 : github.com/kminito/decrypt_pdf