使用itextpdf解決PDF合並的問題

itextpdf解決PDF合並的問題

本文章是我在項目開發過程中解決瞭一個關於PDF顯示的需求而記錄的。

需求是這樣的,需要將兩個PDF進行合並,一個PDF是根據數據庫的信息在在後臺形成的(實際不存在的PDF),另一個是磁盤保存的PDF文件(這個PDF文件後期會變成從雲端獲取)。

作為一個Java菜鳥,這個問題解決瞭數天,還是在leader的指導下解決的。在這裡做一下關鍵代碼的記錄。

項目主要包含瞭以下關鍵詞:(我不做詳解瞭,主要是用瞭這些)

– Spring MVC、Spring、Hibernate

– Maven

– Java

– itextpdf

– MySQL

– JavaWeb相關

首先是itextpdf的依賴

<dependency>
    <groupId>com.itextpdf</groupId>
    <artifactId>itextpdf</artifactId>
    <version>5.5.10</version>
</dependency>

如何在後臺生成一個PDF

這個問題,百度上有很多解決方案,因為我需要將這個生成的PDF和已存在的PDF拼接,於是嘗試瞭多種方案,決定將這個以文檔的形式,將這個文檔轉為字節數組,然後用itextpdf將流讀取到PDF中。

生成PDF的部分代碼:

import java.io.ByteArrayOutputStream;
import com.model.User;
import com.itextpdf.text.BaseColor;
import com.itextpdf.text.Document;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.Element;
import com.itextpdf.text.Font;
import com.itextpdf.text.Paragraph;
import com.itextpdf.text.pdf.BaseFont;
import com.itextpdf.text.pdf.PdfContentByte;
import com.itextpdf.text.pdf.PdfPCell;
import com.itextpdf.text.pdf.PdfPTable;
import com.itextpdf.text.pdf.PdfReader;
import com.itextpdf.text.pdf.PdfWriter;
import com.itextpdf.text.pdf.parser.PdfReaderContentParser;
public class ReportKit {
     public static byte[] createReport(User user) throws Exception {
               ByteArrayOutputStream ba = new ByteArrayOutputStream();
               Document doc = new Document();//創建一個document對象
               PdfWriter writer = PdfWriter.getInstance(doc, ba);//這個PdfWriter會一直往文檔裡寫內容。
              doc.open();//開啟文檔
              BaseFont bfChinese = BaseFont.createFont("c://windows//fonts//msyh.TTF", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
              com.itextpdf.text.Font FontChinese18 = new com.itextpdf.text.Font(bfChinese, 18, com.itextpdf.text.Font.BOLD);
              com.itextpdf.text.Font FontChinese12 = new com.itextpdf.text.Font(bfChinese, 12, com.itextpdf.text.Font.NORMAL);
              com.itextpdf.text.Font FontChinese11 = new com.itextpdf.text.Font(bfChinese, 11, com.itextpdf.text.Font.ITALIC);
              Font fontChinese =  new  Font(bfChinese  ,  12 , Font.NORMAL, BaseColor.BLACK);
              Paragraph pf = new Paragraph("");
              //加入空行
              Paragraph blankRow1 = new Paragraph(24f," ",FontChinese18);
              doc.add(blankRow1);
              //table2
              PdfPTable table25 = new PdfPTable(2);
              //設置每列寬度比例
              int width21[] = {2,98};
              table25.setWidths(width21);
              table25.getDefaultCell().setBorder(0);
              PdfPCell cell25 = new PdfPCell(new Paragraph("這是一個報告",FontChinese18));
              cell25.setBorder(0);
              table25.addCell("");
              table25.addCell(cell25);
              doc.add(table25);
              Paragraph blankRow3 = new Paragraph(18f, "Report ", FontChinese11);
              blankRow3.setAlignment(PdfContentByte.ALIGN_RIGHT);
              doc.add(blankRow3);        
              BaseColor lightGrey = new BaseColor(0xCC,0xCC,0xCC);
              PdfPTable table8 = new PdfPTable(6);
            //設置table的寬度為100%
            table8.setWidthPercentage(100);
            //設置不同列的寬度
            float[] columnWidths = {1.6f, 1.6f, 1.6f, 1.6f, 1.6f, 1.6f};
            table8.setWidths(columnWidths);
              PdfPCell cell1 = new PdfPCell(new Paragraph("用戶名",FontChinese12));
            PdfPCell cell2 = new PdfPCell(new Paragraph("出生日期",FontChinese12));
            PdfPCell cell3 = new PdfPCell(new Paragraph("性別",FontChinese12));
            PdfPCell cell4 = new PdfPCell(new Paragraph("身高",FontChinese12));
            PdfPCell cell5 = new PdfPCell(new Paragraph("體重",FontChinese12));
            PdfPCell cell6 = new PdfPCell(new Paragraph("地區",FontChinese12));
            PdfPCell cell7 = new PdfPCell(new Paragraph(user.getAccessname(),FontChinese12));
            PdfPCell cell8 = new PdfPCell(new Paragraph(user.getBirthday(),FontChinese12));
            PdfPCell cell9 = new PdfPCell(new Paragraph(sex,FontChinese12));
            PdfPCell cell10 = new PdfPCell(new Paragraph(String.valueOf(user.getHeight()),FontChinese12));
            PdfPCell cell11 = new PdfPCell(new Paragraph(String.valueOf(user.getWeight()),FontChinese12));
            PdfPCell cell12 = new PdfPCell(new Paragraph(user.getArea_name(),FontChinese12));
            //表格高度
            cell1.setFixedHeight(30);
            cell2.setFixedHeight(30);
            cell3.setFixedHeight(30);
            cell4.setFixedHeight(30);
            cell5.setFixedHeight(30);
            cell6.setFixedHeight(30);
            cell7.setFixedHeight(30);
            cell8.setFixedHeight(30);
            cell9.setFixedHeight(30);
            cell10.setFixedHeight(30);
            cell11.setFixedHeight(30);
            cell12.setFixedHeight(30);
            //水平居中
           cell1.setHorizontalAlignment(Element.ALIGN_CENTER);
            cell2.setHorizontalAlignment(Element.ALIGN_CENTER);
            cell3.setHorizontalAlignment(Element.ALIGN_CENTER);
            cell4.setHorizontalAlignment(Element.ALIGN_CENTER);
            cell5.setHorizontalAlignment(Element.ALIGN_CENTER);
            cell6.setHorizontalAlignment(Element.ALIGN_CENTER);
            cell7.setHorizontalAlignment(Element.ALIGN_CENTER);
            cell8.setHorizontalAlignment(Element.ALIGN_CENTER);
            cell9.setHorizontalAlignment(Element.ALIGN_CENTER);
            cell10.setHorizontalAlignment(Element.ALIGN_CENTER);
            cell11.setHorizontalAlignment(Element.ALIGN_CENTER);
            cell12.setHorizontalAlignment(Element.ALIGN_CENTER);
            //垂直居中
            cell1.setVerticalAlignment(Element.ALIGN_MIDDLE);
            cell2.setVerticalAlignment(Element.ALIGN_MIDDLE);
            cell3.setVerticalAlignment(Element.ALIGN_MIDDLE);
            cell4.setVerticalAlignment(Element.ALIGN_MIDDLE);
            cell5.setVerticalAlignment(Element.ALIGN_MIDDLE);
            cell6.setVerticalAlignment(Element.ALIGN_MIDDLE);
            cell7.setVerticalAlignment(Element.ALIGN_MIDDLE);
            cell8.setVerticalAlignment(Element.ALIGN_MIDDLE);
            cell9.setVerticalAlignment(Element.ALIGN_MIDDLE);
            cell10.setVerticalAlignment(Element.ALIGN_MIDDLE);
            cell11.setVerticalAlignment(Element.ALIGN_MIDDLE);
            cell12.setVerticalAlignment(Element.ALIGN_MIDDLE);
            //邊框顏色
            cell1.setBorderColor(lightGrey);
            cell2.setBorderColor(lightGrey);
            cell3.setBorderColor(lightGrey);
            cell4.setBorderColor(lightGrey);
            cell5.setBorderColor(lightGrey);
            cell6.setBorderColor(lightGrey);
            cell7.setBorderColor(lightGrey);
            cell8.setBorderColor(lightGrey);
            cell9.setBorderColor(lightGrey);
            cell10.setBorderColor(lightGrey);
            cell11.setBorderColor(lightGrey);
            cell12.setBorderColor(lightGrey);
            table8.addCell(cell1);
            table8.addCell(cell2);
            table8.addCell(cell3);
            table8.addCell(cell4);
            table8.addCell(cell5);
            table8.addCell(cell6);
            table8.addCell(cell7);
            table8.addCell(cell8);
            table8.addCell(cell9);
            table8.addCell(cell10);
            table8.addCell(cell11);
            table8.addCell(cell12);        
            doc.add(table8);
            doc.close();//(有開啟文檔,就要記得關閉文檔)
            writer.close();
            byte[] bytes = ba.toByteArray();          
            return bytes;
     }
}

用document來編輯文檔,真的蠻惡心的,費時費力,排版也不好調,如果能有更加好用的方式,希望大傢能告訴我。

到這裡,調用這個方法,就可以獲得這個文檔的字節數組瞭。

接下來開始拼接PDF。因為是結合前端頁面實現的。因此這個方法是我在controller完成的。

//註意這裡的produces,“application/pdf”,正是因為設置瞭這個,使得整個方法會將文檔以PDF的格式返回到頁面。
@RequestMapping(value = "/newPdf/{report_name}", produces = "application/pdf;charset=UTF-8")
    public void updateReport(Model model, @PathVariable String report_name, HttpServletRequest request,
            HttpServletResponse response,HttpSession session) {
        try {
            User user = (User) session.getAttribute("user");
            //這是用戶登錄後保存到session裡的用戶信息(可以用別的對象來替代這個)
            if(user==null){
                return ;
            }
            PdfReader reader1 =null;
            try {
                // 調用剛剛寫的生成PDF的方法,將這個字節數組獲取。
                byte[] pdfUserByte=ReportKit.createReport(user);
                if(pdfUserByte==null||pdfUserByte.length==0){
                    return;
                }
                //用pdfReader來讀取字節數組,這裡將文檔信息讀入
                 reader1 = new PdfReader(pdfUserByte);
            } catch (Exception e) {
                System.out.println(e.getMessage());
                return ;
            }
            if(reader1==null) return;
            //第二個PDF的讀取
            PdfReader reader2;
            // 報告的PDF
            reader2 = new PdfReader("C:\\Users\\Administrator\\Desktop\\report.pdf");
            Document document = new Document();
            PdfWriter writer = PdfWriter.getInstance(document, response.getOutputStream());
            document.open();
            PdfContentByte cb = writer.getDirectContent();
            int totalPages = 0;
            totalPages += reader1.getNumberOfPages();
            totalPages += reader2.getNumberOfPages();
            java.util.List<PdfReader> readers = new ArrayList<PdfReader>();
            readers.add(reader1);
            readers.add(reader2);
            int pageOfCurrentReaderPDF = 0;
            Iterator<PdfReader> iteratorPDFReader = readers.iterator();
            // Loop through the PDF files and add to the output.
            while (iteratorPDFReader.hasNext()) {
                PdfReader pdfReader = iteratorPDFReader.next();
                // Create a new page in the target for each source page.
                while (pageOfCurrentReaderPDF < pdfReader.getNumberOfPages()) {
                    document.newPage();//創建新的一頁
                    pageOfCurrentReaderPDF++;
                    PdfImportedPage page = writer.getImportedPage(pdfReader, pageOfCurrentReaderPDF);
                    cb.addTemplate(page, 0, 0);
                }
                pageOfCurrentReaderPDF = 0;
            }
            document.close();
            writer.close();
        } catch (IOException | DocumentException e) {
            e.printStackTrace();
        }
    }

關於如何在頁面預覽這個PDF,我用瞭object標簽來獲取。

jsp上的部分片段

    <div class="pdf" id="pdf" ><!-- pdf -->
    <object type="application/pdf" data="http://localhost:8080/project/newPdf/${report.report_name}" id="review" style="width:1100px; height:1000px; margin-top:25px; margin-left:50px" > 
    </object>
    </div>

標簽很好的實現瞭PDF預覽的功能,如果是URL的PDF,data直接輸入URL,就能將PDF在頁面預覽,感覺蠻好用的。

iText 合並PDF文件報錯

在使用iText操作PDF進行合並的時候報錯:

com.lowagie.text.exceptions.BadPasswordException: PdfReader not opened with owner password

public static PdfReader unlockPdf(PdfReader pdfReader) {
     if (pdfReader == null) {
      return pdfReader;
     }
     try {
      java.lang.reflect.Field f = pdfReader.getClass().getDeclaredField("encrypted");
      f.setAccessible(true);
      f.set(pdfReader, false);
     } catch (Exception e) {
       // ignore
     }
     return pdfReader;
    }

對reader使用上述方法即可解決該問題。

以上為個人經驗,希望能給大傢一個參考,也希望大傢多多支持WalkonNet。

推薦閱讀: