در این مطلب، روش کار با اسناد PDF در پایتون مورد بررسی قرار گرفته است. «فرمت اسناد قابل حمل» (Portable Document Format) یا PDF، یک فرمت فایل است که برای ارائه و تبادل اسناد به طور قابل اعتماد در میان سیستم‌عامل‌های گوناگون، مورد استفاده قرار می‌گیرد. در حالیکه PDF در اصل به وسیله شرکت «آدوبی» (Adobe) توسعه پیدا کرده است، اما یک استاندارد باز است که توسط «سازمان بین‌المللی استانداردسازی» (International Organization for Standardization | ISO) نگه‌داری می‌شود.

کار با اسناد PDF در پایتون

در «زبان برنامه‌نویسی پایتون» (Python Programming Language) می‌توان با فایل‌های PDF که از قبل موجود هستند، با استفاده از بسته PyPDF2 کار کرد. PyPDF2 یک بسته غنی پایتون است که می‌توان از آن برای انجام عملیات گوناگونی روی اسناد PDF استفاده کرد. در این مطلب، روش کار با اسناد PDF در پایتون و چگونگی انجام پردازش‌های زیر روی آن‌ها، آموزش داده می‌شود.

تاریخچه PyPDF2 ،pyPdf و PyPDF4

بسته pyPdf در سال ۲۰۰۵ منتشر شد. آخرین انتشار رسمی pyPdf در سال ۲۰۱۰ بود. پس از سپری شدن نزدیک به یک سال، شرکتی به نام «فازیت» (Phasit) اسپانسر یک فورک از pyPdf به نام PyPDF2 شد. کد PyPDF2 به گونه‌ای نوشته شده است که دارای «سازگاری رو به عقب» (Backward Compatibility) باشد. نسخه اصلی PyPDF2، برای سال‌های متمادی به خوبی کار کرده است.

یک مجموعه خلاصه شده از انتشارهای یک بسته به نام PyPDF3 وجود داشت که بعدا به PyPDF4 تغییر نام داده شد. همه این پروژه‌ها تقریبا کار مشابهی انجام می‌دهند، اما تفاوت اصلی بین pyPdf و PyPDF2+‎ آن است که نسخه‌های جدیدتر، پشتیبانی از پایتون ۳ را اضافه کرده‌اند. یک فورک متفاوت از نسخه اصلیpyPdf برای پایتون ۳ نیز وجود دارد که سال‌های زیادی است از آن نگهداری نشده است.

با وجود آنکه PyPDF2 اخیرا کنار گذاشته شده است، PyPDF4 جدید، به طور کامل دارای سازگاری رو به عقب با PyPDF2 نیست. اغلب مثال‌هایی که در این مطلب ارائه شده‌اند، به خوبی با PyPDF4 کار می‌کنند، اما برخی از آن‌ها نیز نمی‌توانند با آن کار کنند. به همین دلیل است که در این مطلب به طور کامل به PyPDF4 پرداخته نشده است.

pdfrw: یک جایگزین

«پاتریک ماوپین» (Patrick Maupin) یک بسته به نام pdfrw ساخته است که می‌تواند بسیاری از کارهایی که PyPDF2 انجام می دهد را انجام دهد و در واقع، به نوعی می‌توان گفت که جایگزینی برای آن است. از pdfrw می‌توان برای انواع مشابهی از وظایف استفاده کرد. این وظایف در این مطلب آموزش داده شده‌اند.

بزرگ‌ترین تفاوت pdfrw با دیگر گزینه‌های معرفی شده آن است که این بسته با بسته ReportLab یکپارچه می‌شود؛ بنابراین کاربر می‌تواند یک PDF از پیش موجود را بگیرد و یک PDF جدید با ReportLab با استفاده از برخی یا تمام PDF‌های از پیش موجود بسازد.

نصب pdfrw

نصب PyPDF2 با استفاده از pip امکان‌پذیر است. همچنین، کاربرانی که از «توزیع پایتون آناکوندا» (Python Anaconda Distribution) استفاده می‌کنند، می‌توانند برای نصب این بسته از «کوندا» (Conda) استفاده کنند. در ادامه، PyPDF2 با استفاده از pip نصب خواهد شد.

$ pip install pypdf2

به دلیل آنکه PyPDF2 هیچ وابستگی ندارد، نصب آن به سرعت انجام می‌شود. بیشتر زمانی که صرف نصب می‌شود، مربوط به دانلود این بسته است. در ادامه، به روش کار با اسناد PDF در پایتون پرداخته می‌شود و چگونگی استخراج اطلاعات از این فایل‌ها شرح داده خواهد شد.

روش استخراج اطلاعات از یک فایل PDF در پایتون

می‌توان از PyPDF2 برای «استخراج فراداده‌ها» (Metadata) و متن از فایل PDF استفاده کرد. این کار برای هنگامی مفید است که نوع خاصی از خودکارسازی روی فایل‌های PDF از پیش موجود، اتفاق می‌افتد. در ادامه، انواع داده‌هایی که در حال حاضر می‌توان استخراج کرد، بیان شده‌اند.

  • نویسنده (Author)
  • سازنده (Creator)
  • تولید کننده (Producer)
  • موضوع (Subject)
  • عنوان (Title)
  • تعداد صفحات (Number of pages)

برای پیاده‌سازی این مثال، نیاز به یک فایل PDF است. از هر PDF می‌توان برای این کار استفاده کرد. برای ساده‌تر کردن کارها، می‌توان با مراجعه به این سایت [+] و دانلود کتابی با عنوان reportlab-sample.pdf می‌توان از PDF استفاده کرد که توسط نویسنده مطلب در این مثال استفاده شده است. در ادامه، کدهایی با استفاده از این PDF نوشته می‌شود و چگونگی دسترسی به خصیصه‌های بیان شده، طی این کدنویسی مشخص خواهد شد.

# extract_doc_info.py

from PyPDF2 import PdfFileReader

def extract_information(pdf_path):
    with open(pdf_path, 'rb') as f:
        pdf = PdfFileReader(f)
        information = pdf.getDocumentInfo()
        number_of_pages = pdf.getNumPages()

    txt = f"""
    Information about {pdf_path}: 

    Author: {information.author}
    Creator: {information.creator}
    Producer: {information.producer}
    Subject: {information.subject}
    Title: {information.title}
    Number of pages: {number_of_pages}
    """

    print(txt)
    return information

if __name__ == '__main__':
    path = 'reportlab-sample.pdf'
    extract_information(path)

در اینجا، کاربر PdfFileReader را از بسته PyPDF2 «وارد» (Import | ایمپورت) می‌کند. PdfFileReader کلاسی با چندین متد برای تعامل با فایل‌های PDF است. در این مثال، .getDocumentInfo()‎ فراخوانی می‌شود که نمونه‌ای از DocumentInformation را باز می‌گرداند. این مورد حاوی بیشتر اطلاعاتی است که کاربر تمایل به استخراج آن‌ها دارد. همچنین، کاربر می‌تواند ()getNumPages‎. را روی شی خواننده (Reader Object) فراخوانی کند که تعداد صفحات درون سند را باز می‌گرداند.

نکته: آخرین بلوک کد از قابلیت جدید پایتون ۳، یعنی f-strings، برای قالب‌بندی رشته‌ها استفاده می‌کنند.

متغیر information دارای چندین خصیصه نمونه است که می‌توان از آن‌ها برای دریافت کل فراداده‌هایی مور نظر کاربر از سند PDF استفاده کرد. کاربر این اطلاعات را چاپ می‌کند و همچنین، آن‌ها را برای استفاده‌های آتی احتمالی بازمی‌گرداند.

با وجود آنکه PyPDF2 دارای extractText()‎. است که می‌توان از آن روی اشیای صفحه استفاده کرد (در این مثال نشان داده نشده است)، اما این قابلیت به خوبی کار نمی‌کند. برخی از فایل‌های PDF، متن را باز می‌گردانند و برخی دیگر یک رشته خالی را باز می گرداند. هنگامی که کاربر قصد دارد متن را از PDF استخراج کند، باید پروژه PDFMiner را بررسی کند. PDFMiner مستحکم‌تر و به طور ویژه‌ای برای استخراج متن از PDF ساخته شده است. در ادامه، چگونگی چرخاندن صفحات PDF با پایتون، آموزش داده شده است.

روش چرخاندن صفحات PDF با پایتون

گاهی اوقات، فرد PDF‌هایی دریافت می‌کند که حاوی صفحاتی هستند که به جای «حالت عمودی» (Portrait Mode)، در «حالت افقی» (Landscape Mode) قرار دارند. گاهی نیز تصاویر به صورت پایین به بالا هستند. این اتفاق ممکن است زمانی پیش بیاید که فرد یک سند را به صورت PDF یا ایمیل، اسکن می‌کند. می‌توان این سند را چاپ کرد و نسخه کاغذی آن را خواند و یا از قدرت پایتون برای چرخاندن صفحات استفاده کرد. با استفاده از قطعه کد زیر، می‌توان صفحات یک PDF را با بهره‌گیری از PyPDF2 چرخاند.

# rotate_pages.py

from PyPDF2 import PdfFileReader, PdfFileWriter

def rotate_pages(pdf_path):
    pdf_writer = PdfFileWriter()
    pdf_reader = PdfFileReader(path)
    # Rotate page 90 degrees to the right
    page_1 = pdf_reader.getPage(0).rotateClockwise(90)
    pdf_writer.addPage(page_1)
    # Rotate page 90 degrees to the left
    page_2 = pdf_reader.getPage(1).rotateCounterClockwise(90)
    pdf_writer.addPage(page_2)
    # Add a page in normal orientation
    pdf_writer.addPage(pdf_reader.getPage(2))

    with open('rotate_pages.pdf', 'wb') as fh:
        pdf_writer.write(fh)

if __name__ == '__main__':
    path = 'Jupyter_Notebook_An_Introduction.pdf'
    rotate_pages(path)

برای این مثال، نیاز به وارد کردن PdfFileWriter علاوه بر PdfFileReader است، زیرا کاربر نیاز به نوشتن یک PDF جدید دارد. rotate_pages()‎ مسیر فایل PDF که کاربر قصد ویرایش آن را دارد دریافت می‌کند.

با بهره‌گیری از این تابع، کاربر نیاز به ساخت یک «شی نویسنده» (Writer Object) دارد که می‌تواند آن را pdf_writer بنامد و یک شی خواننده که می‌تواند آن را pdf_reader بنامد. سپس، می‌تواند از GetPage()‎. برای دریافت صفحات مورد انتظار استفاده کند. در اینجا، کاربر می‌تواند صفحه صفر را بگیرد که اولین صفحه است. سپس، می‌تواند متد شی صفحه rotateClockwise()‎. را فراخوانی کند و مقدار ۹۰ درجه را به آن پاس بدهد. پس از آن، برای دو صفحه، rotateCounterClockwise()‎. را فراخوانی کند و به طور مشابه، مقدار ۹۰ درجه را به آن پاس دهد.

نکته: بسته PyPDF2 فقط امکان چرخاندن صفحات را در مقدار ۹۰ درجه فراهم می‌کند. در غیر این صورت، خطای AssertionError صادر خواهد شد.

پس از هر فراخوانی متدهای چرخش، .addPage()‎ فراخوانی می‌شود. این، نسخه چرخانده شده صفحه را به شی نویسنده اضافه می‌کند. آخرین صفحه‌ای که کاربر بدون انجام هرگونه چرخشی روی آن، به شی نویسنده اضافه می‌کند، صفحه سه است.

در نهایت، PDF جدید با استفاده از write()‎. نوشته می‌شود. این متد، شی فایل را به عنوان پارامتر می‌گیرد. این PDF جدید، حاوی سه صفحه خواهد بود. دو صفحه اول در جهت مخالف یکدیگر چرخانده می‌شوند و به صورت افقی هستند، در حالی که صفحه سوم طبیعی است. در ادامه، روش ادغام چندین فایل PDF در یک فایل، آموزش داده می‌شود.

روش ادغام فایل‌های PDF با پایتون

موقعیت‌های زیادی وجود دارد که طی آن، کاربر تمایل دارد که دو یا تعداد بیشتری فایل PDF را دریافت و آن‌ها را در یک فایل واحد ادغام کند. برای مثال، کاربر ممکن است یک صفحه کاور استاندارد داشته باشد که نیاز است در گزارش‌های زیادی استفاده شود. می‌توان از پایتون برای انجام چنین کاری استفاده کرد. برای مثال، کاربر می‌تواند یک فایل PDF را باز و یک صفحه را به عنوان یک PDF جدا چاپ کند. سپس، این کار را با صفحات دیگر نیز انجام دهد. این کار ورودی‌هایی را برای استفاده در مثال‌ها در اختیار کاربر قرار می‌دهد.

# pdf_merging.py

from PyPDF2 import PdfFileReader, PdfFileWriter

def merge_pdfs(paths, output):
    pdf_writer = PdfFileWriter()

    for path in paths:
        pdf_reader = PdfFileReader(path)
        for page in range(pdf_reader.getNumPages()):
            # Add each page to the writer object
            pdf_writer.addPage(pdf_reader.getPage(page))

    # Write out the merged PDF
    with open(output, 'wb') as out:
        pdf_writer.write(out)

if __name__ == '__main__':
    paths = ['document1.pdf', 'document2.pdf']
    merge_pdfs(paths, output='merged.pdf')

اکنون جلوتر رفته و کدهایی مورد بررسی قرار می‌گیرد که برای ادغام فایل‌های PDF با یکدیگر مورد استفاده قرار می‌گیرند. کاربر می‌تواند وقتی که یک لیست از PDF‌ها را دارد و می‌خواهد آن‌ها را ادغام کند، از merge_pdfs() ‎ استفاده کند. همچنین، کاربر نیاز دارد بداند که نتایج را در کجا ذخیره کند، بنابراین این تابع یک لیست از مسیرهای ورودی و یک مسیر خروجی را دریافت می‌کند. سپس، در ورودی‌ها حلقه زده می‌شود و یک شی PDF‌خوان برای هر یک از آن‌ها ساخته می‌شود. پس از آن، تکرار در همه صفحات انجام می‌شود و از addPage()‎. برای افزودن هر یک از آن‌ها به خود صفحه استفاده می‌شود. هنگامی که چرخش در همه صفحات PDF موجود در لیست انجام می‌شود، نتایج در پایان نوشته می‌شوند. شایان ذکر است که اگر کاربر نمی‌خواهد همه صفحات همه PDF‌ها را ادغام کند، می‌تواند این اسکریپت را با افزودن طیفی از صفحاتی که باید اضافه شوند بهبود ببخشد. به عنوان یک چالش، می‌توان یک رابط خط فرمان را برای این تابع با استفاده از ماژول argparse ساخت.

روش شکستن فایل‌های PDF با پایتون

گاهی، کاربر فایل‌های PDF دارد که نیاز دارد آن‌ها را به چندین فایل بشکند. این کار به ویژه هنگامی کاربرد دارد که فایل PDF حاوی تعداد زیادی سند اسکن شده باشد. البته کاربردهای متعدد و متنوع زیاد دیگری نیز برای این موضوع وجود دارد. در ادامه، چگونگی استفاده از PyPDF2 برای شکستن یک فایل PDF به چند فایل، آموزش داده شده است.

# pdf_splitting.py

from PyPDF2 import PdfFileReader, PdfFileWriter

def split(path, name_of_split):
    pdf = PdfFileReader(path)
    for page in range(pdf.getNumPages()):
        pdf_writer = PdfFileWriter()
        pdf_writer.addPage(pdf.getPage(page))

        output = f'{name_of_split}{page}.pdf'
        with open(output, 'wb') as output_pdf:
            pdf_writer.write(output_pdf)

if __name__ == '__main__':
    path = 'Jupyter_Notebook_An_Introduction.pdf'
    split(path, 'jupyter_page')

در این مثال، کاربر مجددا یک شی PDF خوان را می‌سازد و در صفحات آن حلقه می‌زند. برای هر صفحه در فایل PDF، کاربر یک نمونه PDF writer جدید می‌سازد و یک صفحه مجرد به آن اضافه می‌کند. سپس، آن صفحه را در یک فایل با نام یکتا می‌نویسد. هنگامی که اجرای اسکریپت پایان یافت، کاربر باید هر صفحه از PDF اصلی را به فایل‌های PDF جدایی بکشد. در ادامه، روش واترمارک زدن روی فایل‌های PDF آموزش داده شده است.

روش اضافه کردن واترمارک به PDF با پایتون

«واترمارک‌ها» (Watermarks) تصاویر یا الگوهایی را روی اسناد چاپی یا دیجیتال تعیین می‌کنند. برخی از واترمارک‌ها تنها در شرایط نوری خاصی قابل مشاهده هستند. واترمارک زدن به این دلیل مهم است که به کاربر امکان محافظت از مالکیت معنوی اسناد خود، شامل تصاویر یا PDF‌ها، را می‌دهد. عنوان دیگری که به جای واترمارک استفاده می‌شود، «روکش» (Overlay) است. می‌توان از پایتون و PyPDF2 برای واترمارک زدن اسناد استفاده کرد. کاربر باید یک PDF داشته باشد که فقط حاوی تصاویر یا متن واترمارک شده است. در ادامه، قطعه کد مربوط به واترمارک زدن ارائه شده است.

# pdf_watermarker.py

from PyPDF2 import PdfFileWriter, PdfFileReader

def create_watermark(input_pdf, output, watermark):
    watermark_obj = PdfFileReader(watermark)
    watermark_page = watermark_obj.getPage(0)

    pdf_reader = PdfFileReader(input_pdf)
    pdf_writer = PdfFileWriter()

    # Watermark all the pages
    for page in range(pdf_reader.getNumPages()):
        page = pdf_reader.getPage(page)
        page.mergePage(watermark_page)
        pdf_writer.addPage(page)

    with open(output, 'wb') as out:
        pdf_writer.write(out)

if __name__ == '__main__':
    create_watermark(
        input_pdf='Jupyter_Notebook_An_Introduction.pdf', 
        output='watermarked_notebook.pdf',
        watermark='watermark.pdf')

create_watermark()‎ سه آرگومان را می‌پذیرد که در ادامه به آن‌ها پرداخته شده است.

  • input_pdf: مسیر فایل PDF که قرار است واترمارک شود.
  • output: مسیری که نسخه واترمارک شده از فایل PDF در آن ذخیره می‌شود.
  • watermark: یک PDF که حاوی تصویر یا متن واترمارک شده است.

در کد، فایل واترمارک باز و صفحه اول سند که باید واترمارک زده شود نیز گرفته می‌شود. سپس، یک شی PDF reader با استفاده از input_pdf و شی جنریک pdf_writer برای نوشتن PDF واترمارک زده شده، ساخته می‌شود. گام بعدی، تکرار در صفحات در  input_pdf است. اینجا همان جایی است که جادو به وقوع می‌پیوندد. کاربر نیاز به فراخوانی ‎.mergePage()‎ و پاس دادن  watermark_page به آن دارد. هنگامی که کاربر این کار را می‌کند، watermark_page روی صفحه کنونی را پوشش می‌دهد. سپس، صفحه اخیرا ادغام شده به شی pdf_writer اضافه می‌شود. در نهایت، PDF که جدیدا واترمارک شده است، روی دیسک نوشت می‌شود. آخرین مبحثی که در این مطلب، پیرامون کار با اسناد PDF در پایتون به آن پرداخته می‌شود، چگونگی رمزگذاری روی فایل‌های PDF با PyPDF2 است.

رمزگذاری یک فایل PDF با پایتون

PyPDF2 از اضافه کردن گذرواژه کاربر و گذرواژه مالک برای یک فایل PDF پشتیبانی می‌کند. در PDF، گذرواژه مالک یک امتیاز ادمین به PDF می‌دهد و همچنین، برای کاربر امکان تنظیم دسترسی روی سند را فراهم می‌کند. از  سوی دیگر، گذرواژه کاربر، فقط امکان باز کردن سند را به کاربر می‌دهد.

PyPDF2 دستکم در حال حاضر، امکان تنظیم مجوز دسترسی را روی سند به افراد نمی‌دهد؛ حتی اگر امکان ثبت گذرواژه مالک را بدهد. صرفنظر از این موضوع، در ادامه، چگونگی اضافه کردن یک گذرواژه به فایل PDF، آموزش داده شده است که فایل PDF را رمزنگاری می‌کند.

# pdf_encrypt.py

from PyPDF2 import PdfFileWriter, PdfFileReader

def add_encryption(input_pdf, output_pdf, password):
    pdf_writer = PdfFileWriter()
    pdf_reader = PdfFileReader(input_pdf)

    for page in range(pdf_reader.getNumPages()):
        pdf_writer.addPage(pdf_reader.getPage(page))

    pdf_writer.encrypt(user_pwd=password, owner_pwd=None, 
                       use_128bit=True)

    with open(output_pdf, 'wb') as fh:
        pdf_writer.write(fh)

if __name__ == '__main__':
    add_encryption(input_pdf='reportlab-sample.pdf',
                   output_pdf='reportlab-encrypted.pdf',
                   password='twofish')

add_encryption()‎ ورودی و مسیر خروجی PDF را همراه با گذرواژه‌ای که کاربر قصد اضافه کردن آن به PDF را دارد را دریافت می‌کند. سپس، یک PDF writer و reader object را همچون قبل، باز می‌کند. با توجه به اینکه کاربر می‌خواهد کل فایل ورودی را رمزنگاری کند، نیاز به یک حلقه در همه صفحات آن و اضافه کردن آن به writer دارد. گام نهایی، فراخوانی encrypt()‎. است که گذرواژه کاربر، گذرواژه مالک و اینکه آیا رمزنگاری ۱۲۸ بیتی باید اضافه شود یا خیر را دریافت می‌کند. شایان توجه است که رمزنگاری ۱۲۸ بیتی به صورت پیش‌فرض روشن است.

نکته: رمزنگاری PDF، از RC4 یا AES (استاندارد رمزنگاری پیشرفته، Advanced Encryption Standard) برای رمزنگاری PDF مطابق با [+] استفاده می‌کند. اینکه کاربر فایل PDF خود را رمزنگاری کرده، به این معنا نیست که امن محسوب می‌شود. ابزارهای گوناگونی برای حذف رمز از روی فایل‌های PDF وجود دارد.

جمع‌بندی

بسته PyPDF2 ابزار بسیار مفید و سریعی برای کار با فایل‌های PDF است. از PyPDF2 می‌توان برای خودکارسازی کارهای بزرگ و استفاده از توانایی‌های آن به عنوان اهرمی برای انجام هر چه بهتر امور، استفاده کرد. در این راهنما، مطالب زیر مورد بررسی قرار گرفته‌اند.

  • استخراج فراداده‌ها از یک PDF
  • چرخاندن صفحات
  • ادغام و شکستن PDF‌ها
  • افزودن واترمارک
  • افزودن رمزنگاری

اگر مطلب بالا برای شما مفید بوده است، آموزش‌های زیر نیز به شما پیشنهاد می‌شوند:

^^

بر اساس رای ۶ نفر
آیا این مطلب برای شما مفید بود؟
شما قبلا رای داده‌اید!
اگر بازخوردی درباره این مطلب دارید یا پرسشی دارید که بدون پاسخ مانده است، آن را از طریق بخش نظرات مطرح کنید.

«الهام حصارکی»، فارغ‌التحصیل مقطع کارشناسی ارشد مهندسی فناوری اطلاعات، گرایش سیستم‌های اطلاعات مدیریت است. او در زمینه هوش مصنوعی و داده‌کاوی، به ویژه تحلیل شبکه‌های اجتماعی، فعالیت می‌کند.