دانستنی‌ها

کار با PDF در جاوا: آموزش Apache PDFBox

این مقاله توسط جناب آقای امید نژادعباسی تدوین شده و برای انتشار در اختیار جاواکاپ قرار گرفته‌است.

Apache PDFBox یکی از  کتاب‌خانه‌های متن‌باز بنیاد Apache است که به زبان جاوا نوشته شده و برای ساخت یا ویرایش فایل‌های pdf مورد استفاده قرار می‌گیرد. با این کتابخانه می‌توان کارهایی از قبیل افزودن و استخراج متن و عکس، ویرایش اطلاعات فایل، افزودن کامنت، هایلایت کردن متن, وصل کردن دو یا چند فایل، افزودن یا حذف مجوزها مثل مجوز چاپ کردن فایل، افزودن کد جاوااسکریپت، رمزگذاری فایل، افزودن امضا، تبدیل فایل به عکس و HTML و… را انجام داد.

PDFBox از چهار جز اصلی تشکیل ‌شده‌ است که هر کدام در یک فایل jar جداگانه قرار دارند و عبارتند از: PDFBox و FontBox و XmpBox و Preflight.

  • بین این بسته‌ها مهم‌ترین آن‌ها PDFBox است که تمام کلاس‌های کاربردی برای پردازش pdf در آن قرار دارند.
  • FontBox اجازه استفاده از فونت‌های مختلف را می‌دهد.
  • از XmpBox برای کنترل متادیتای XMP فایل‌ها استفاده می‌شود.
  • با کلاس‌های  Preflight می‌توان چک کرد که آیا فایل pdf از استاندارد PDF/A پیروی می‌کند یا خیر.

در بیشتر مواقع شما فقط به PDFBox و FontBox نیاز دارید که هر دو آن‌ها در فایل pdfbox-app قرار داده شده‌اند و می‌توانید آن‌ها را از سایت رسمی دانلود کنید.

در این مطلب سعی داریم با چند مثال به طور مختصر با چند تا از کلاس‌های این کتاب‌خانه آشنا شویم.

مثال ۱: ساخت صفحه جدید حاوی متن و عکس و اضافه کردن به یک فایل جدید

در این مثال می‌خواهیم یک فایل pdf بارگذاری کنیم و یک صفحه حاوی یک متن Hello World! دقیقا در وسط و یک لوگوی جاوا در زیر متن به آن اضافه کنیم. خروجی به شکل زیر خواهد بود:

تصویری از pdfی که قرار است در این مثال طراحی کنیم.

کلاس PDDocument نشان‌دهنده فایل‌های pdf است. می‌توانید با استفاده‌از new از آن شی جدیدی بسازید ولی ما در اینجا از متد استاتیک load همان کلاس استفاده می‌کنیم تا فایل موجود قبلی را بارگذاری کنیم:

PDDocument pdDocument = PDDocument.load(new File(“file.pdf”));

حالا برای اضافه کردن صفحه باید از کلاس PDPage شی بسازیم:

PDPage page1 = new PDPage(PDRectangle.A4);
pdDocument.addPage(page1);

اگر از متد سازنده بدون پارامتر یعنی PDFPage() استفاده می‌‌کردیم، اندازه صفحه 8.5 *11 اینچ می‌شد که اندازه صفحات نامه‌های اداری در آمریکا است. در این جا از ثابت PDRectangle.A4 استفاده کردیم که برابر با اندازه صفحه A4 است. سپس با متد add از شی PDDocumentمان، صفحه را  به انتهای فایل اضافه می‌کنیم. جالب است بدانید حتی بعد از اضافه کردن صفحه به فایل باز هم می‌توانید آن‌ را ویرایش کنید. این قابلیت به شما اجازه می‌دهد تا صفحه‌های موجود قبلی را هم ویرایش کنید.

برای جایگذاری متن در صفحات باید با نحوه مختصات‌دهی آشنا باشیم. نقطه (0 و 0) در هر صفحه گوشه پایین سمت چپ آن صفحه است و برای متن یا تصویر هم نقاط مختصات مکان گوشه پایینی سمت چپ آن‌ها را مشخص می‌کند. برای پیدا کردن وسط صفحه به اندازه کل صفحه نیاز داریم:

float pageHeight = page1.getMediaBox().getUpperRightY();
float pageWidth = page1.getMediaBox().getUpperRightX();

متد getMediaBox شی PDRectanlge را برمی‌گرداند که نشان‌دهنده‌ٔ کل محدوده فیزیکی صفحه است. متدهای *getUpperRight مختصات گوشه راست بالایی را برمی‌گردانند که همان عرض و طول صفحه هم می‌باشند. حالا چند متغیر دیگر هم تعریف می‌کنیم:

String text = "Hello World!";
PDFont font = PDType1Font.TIMES_ROMAN;
float fontSize = 48f;

همان‌طور که از اسم متغیرها معلوم است، قرار است text را با فونت TIMES_ROMAN و با سایز 48 که بر حسب 1/72 اینچ است بنویسیم. برای نوشتن متن در وسط باید عرض نهایی آن را هم بدانیم. هر فونت ممکن است اندازه مختلفی داشته باشد. به خاطر همین در کلاس  PDFont یک متد تعریف شده است که می‌توان با آن عرض رشته را حساب کرد:

float textWidth = font.getStringWidth(text) /1000*(fontSize);

البته این متد مقدار واقعی عرض را برنمی‌گرداند بلکه بر حسب یک هزارم هر واحدصفحه یعنی 1/72 اینچ برمی‌گرداند. پس ابتدا آن را تقسیم بر 1000 کردیم بعد حاصل ضرب آن با اندازه فونت عرض واقعی را به ما می‌دهد.

PDPageContentStream contentStream=new PDPageContentStream(pdDocument, page1);

در مرحله بعد برای وارد کردن محتوا باید یک شی از کلاس PDPageContentStream بسازیم. به متد سازنده آن باید صفحه مورد نظر و فایل pdfای که می‌خواهیم روی آن بنویسیم را پاس کنیم. قبل از نوشتن متن هم باید متد beginText را فرا بخوانیم. بعد فونت مورد استفاده و اندازه آن را تنظیم می‌کنیم:

contentStream.beginText();
contentStream.setFont(font, fontSize);

کتابخانه PDFBox هیچ امکانی برای تنظیم جای متن مثلا درج متن در وسط صفحه ندارد. حتی اگر متن شما از صفحه خارج شود خط جدیدی ایجاد نمی‌کند و همه این پیچیدگی‌ها بر عهده برنامه‌نویس است. حالا برای مختصات متن Hello World!مان به صورت زیر عمل می‌کنیم:

float textX = (pageWidth/2) - (textWidth/2);
float textY = (pageHeight/2) - (fontSize/2);

سعی کنید به این نوع مختصات‌دهی عادت کنید! حالا که این کار را کردیم، یک خط جدید در آن نقطه شروع می‌کنیم و با متد showText متن‌مان را می‌نویسیم و با متد endText به نوشتن متن پایان می‌دهیم:

contentStream.newLineAtOffset(textX, textY);
contentStream.showText(text);
contentStream.endText();

حال برای اضافه کردن عکس به صفحه می‌توان از متد drawImage استفاده کرد که به یک شی از جنس PDImageXObject نیاز دارد:

PDImageXObject javaLogo=PDImageXObject.createFromFile(("java.png"), pdDocument);
float imgSquareSize=200

متغیر imgSquareSize را هم برای اندازه عکس استفاده خواهیم‌کرد چون ممکن است اندازه واقعی عکس خیلی بزرگ باشد. باید از همان روشی برای پیدا کردن وسط صفحه استفاده کنیم که برای متن به کار بردیم. برای محور yها به اندازه imgSquareSize پایین می‌رویم تا تصویر دقیقا زیر متن بیافتد:

contentStream.drawImage(javaLogo, (pageWidth/2) - (imgSquareSize/2), textY-imgSquareSize,imgSquareSize, imgSquareSize);
contentStream.close();

وقتی کارمان با contentStream تمام شد آن ‌را می‌بندیم. برای اتمام کار باید فایل را ذخیره کنیم:

pdDocument.save("new.pdf");
pdDocument.close();

کد کامل مربوط به این برنامه را از اینجا می‌توانید ملاحظه کنید.

مثال ۲: رمزگذاری و ویرایش مجوزهای فایل

همان‌طور که گفتیم، می‌توان با PDFBox مجوزهای فایل را تغییر داد. در این مثال سعی داریم مجوز چاپ کردن را از کاربر بگیریم. برای این کار ابتدا با استفاده از متد load فایل را بارگذاری می‌کنیم:

PDDocument pdDocument = PDDocument.load(new File("new.pdf"));

کلاس AccessPermission مجوزهای فایل را در خود نگه می‌دارد. از آن یک شی می‌سازیم و مجوز پرینت گرفتن آن را لغو می‌کنیم:

AccessPermission access=new AccessPermission();
access.setCanPrint(false); 

ولی ما این مجوز دسترسی را مستقیما به فایل اعمال نمی‌کنیم بلکه باید آن‌ را به شی StandardProtectionPolicy که خط مشی استفاده از فایل را مشخص می‌کند پاس کنیم. این کلاس فقط یک متد سازنده دارد:

StandardProtectionPolicy policy=new StandardProtectionPolicy("123", "465", access);

پارامتر اول، گذرواژه متعلق به صاحب فایل است. اگر با این گذرواژه فایل را باز کنید تمام مجوزها را (حتی اگر غیرفعال هم باشند) دارید. پارامتر دوم گذرواژه‌ای است که شما به کاربر فایل می‌دهید و پارامتر سوم هم شی AccessPermissionمان هست. سپس با متد protect فایل را رمز‌گذاری می‌کنیم. در نهایت هم باید آن ‌را ذخیره کنیم:

pdDocument.protect(policy);
pdDocument.save("new.pdf");

اگر سعی کنید این فایل را دوباره با متد load باز کنید باید رمز فایل را هم به عنوان پارامتر پاس کنید.

کد کامل این قسمت را می‌توانید در این لینک ببینید.

مثال ۳: وصل کردن چند فایل به هم

وصل‌کردن فایل در PDFBox به راحتی انجام می‌شود. در کد زیر فرض کنید متغیر list، حاوی تمامی فایل‌هایی است که می‌خواهیم به هم وصل کنیم. با اجرای کد زیر فایلی با نام merged ساخته می‌شود که نتیجه اتصال همه فایل‌ها به همان ترتیبی که در لیست اولیه قرار دارند خواهد بود.

List<String> filesToMerge=new ArrayList<>();
PDFMergerUtility merger = new PDFMergerUtility();
for(String file:filesToMerge)
    merger.addSource(file);        
merger.setDestinationFileName("merged.pdf");
merger.mergeDocuments();

سخن نهایی

در این مطلب، مثال‌هایی از کار با PDFBox را دیدیم. این کتاب‌خانه همانطور که دیدید بسیار قدرتمند است و در بسیاری از موارد، پاسخگوی نیاز برنامه‌نویسان است.

البته کتاب‌خانه‌‌های دیگری هم مثل IText برای کار با فایل‌های pdf وجود دارد ولی به نظر نویسنده هیچ یک به کاملی PDFBox نیستند. برای مثال، با توجه به مجوز استفاده خاص، از iText فقط می‌توانید در پروژه‌های متن‌باز استفاده کنید. می‌توانید برای مقایسه بیش‌تر بین کتاب‌خانه‌های کار با فایل PDf در جاوا، به این مطلب رجوع کنید.

.

.

.


با ما همراه باشید

کانال تلگرام: JavaCupIR@

اکانت توییتر: JavaCupIR@

صفحه اینستاگرام: javacup.ir

صفحه ویرگول: javcup

گروه لینکدین: Iranian Java Developers

نوشته های مشابه

‫4 دیدگاه ها

  1. سلام
    ممنون از آموزش خوبتان.امکانش هست یک منبع کامل برای آموزش این apache pdf معرفی میکنید؟ممنون

    1. سلام.
      برای اکثر کتاب‌خانه‌ها و فریم‌ورک‌ها، کامل‌ترین منبع یادگیری همان مستندات اصلی است که توسط توسعه‌دهندگان خود ابزار نوشته شده،
      برای نسخه‌ی ۲ از apache pdfbox می‌توانید مستندات را اینجا پیدا کنید.
      https://pdfbox.apache.org/2.0/getting-started.html

    1. نه !
      با PdfBox امکان انجام این کار وجود ندارد. برای این کار می توانید از کتاب خانه Apose.PDF استفاده کنید که این کار را با دو خط کد هم برای شما انجام می دهد:
      https://docs.aspose.com/pdf/java/convert-pdf-to-doc-and-docx/
      برای دیدن مثال های بیشتر از تبدیل بین انواع قالب های دیگر به این مطلب مراجعه کنید:
      https://www.baeldung.com/pdf-conversions-java

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

دکمه بازگشت به بالا