使用itextpdf解决PDF合并的问题

时间:2022-09-27 15:35:21

itextpdf解决PDF合并的问题

本文章是我在项目开发过程中解决了一个关于PDF显示的需求而记录的。

需求是这样的,需要将两个PDF进行合并,一个PDF是根据数据库的信息在在后台形成的(实际不存在的PDF),另一个是磁盘保存的PDF文件(这个PDF文件后期会变成从云端获取)。

作为一个Java菜鸟,这个问题解决了数天,还是在leader的指导下解决的。在这里做一下关键代码的记录。

项目主要包含了以下关键词:(我不做详解了,主要是用了这些)

- Spring MVC、Spring、Hibernate

- Maven

- Java

- itextpdf

- MySQL

- JavaWeb相关

首先是itextpdf的依赖

?
1
2
3
4
5
<dependency>
    <groupId>com.itextpdf</groupId>
    <artifactId>itextpdf</artifactId>
    <version>5.5.10</version>
</dependency>

如何在后台生成一个PDF

这个问题,百度上有很多解决方案,因为我需要将这个生成的PDF和已存在的PDF拼接,于是尝试了多种方案,决定将这个以文档的形式,将这个文档转为字节数组,然后用itextpdf将流读取到PDF中。

生成PDF的部分代码:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
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完成的。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
//注意这里的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上的部分片段

?
1
2
3
4
<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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
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使用上述方法即可解决该问题。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持服务器之家。

原文链接:https://blog.csdn.net/MissEel/article/details/75220571