在Java通过使用itext7来操作PDF

文章目录

    • 前言
    • 配置
      • 1.maven依赖
      • 2.PDFUtils
    • 使用
      • 1.Adobe Acrobat Pro DC
      • 2. 测试
      • 3. 合并

前言

最近项目需要用到有关PDF的操作,网上查了很多资料,觉得itext7比较符合我的需求,在此做一下简单地记录。

itext7相对于itext5做了很多改动,所以如果使用itext5的话本文并不适用。不过官网有着非常详细的API文档以及演示,具体可以去官网查看。

官网链接

配置

1.maven依赖

我是使用maven引入的,并且是全家桶,因为第一次使用,这样比较方便。

1
2
3
4
5
6
7
<!-- PDF操作,itext7全家桶 -->
<dependency>
    <groupId>com.itextpdf</groupId>
    <artifactId>itext7-core</artifactId>
    <version>7.1.8</version>
    <type>pom</type>
 </dependency>

2.PDFUtils

封装的PDF工具类,导包直接选中itext相关的就好,注意IOException不要导itext的(第一次无脑导的后果…)

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
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
public class PDFUtils {

    /**
     * 获取默认字体
     *
     * @return
     */
    public static PdfFont getDefaultFont() {
        try {
            return PdfFontFactory.createFont("STSong-Light", "UniGB-UCS2-H", true);
        } catch (IOException e) {
            // 记录日志
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 替换PDF文本表单域变量
     *
     * @param templatePdfPath
     *            要替换的pdf全路径
     * @param params
     *            替换参数
     * @param destPdfPath
     *            替换后保存的PDF全路径
     *
     * @throws FileNotFoundException
     * @throws IOException
     */
    public static final void replaceTextFieldPdf(String templatePdfPath, String destPdfPath, Map<String, String> params)
            throws FileNotFoundException, IOException {
        PdfDocument pdf = new PdfDocument(new PdfReader(templatePdfPath), new PdfWriter(destPdfPath));

        if (params != null && !params.isEmpty()) {// 有参数才替换
            PdfAcroForm form = PdfAcroForm.getAcroForm(pdf, true);
            Map<String, PdfFormField> fields = form.getFormFields(); // 获取所有的表单域
            for (String param : params.keySet()) {
                PdfFormField formField = fields.get(param); // 获取某个表单域
                if (formField != null) {
                    formField.setFont(getDefaultFont()).setValue(params.get(param)); // 替换值
                }
            }
            form.flattenFields();// 锁定表单,不让修改
        }
        pdf.close();
    }

    /**
     * 替换PDF图片表单域(文本)变量,1、获取表单域的大小;2、根据表单域的位置,确定图片的位置;3、如果图片的宽或者高大于表单域,则等比压缩图片。
     *
     * @param templatePdfPath
     *            要替换的pdf全路径
     * @param params
     *            替换参数
     * @param destPdfPath
     *            替换后保存的PDF全路径
     *
     * @throws FileNotFoundException
     * @throws IOException
     */
    public static final void replaceImageFieldPdf(String templatePdfPath, String destPdfPath,
            Map<String, String> params) throws FileNotFoundException, IOException {
        PdfDocument pdf = new PdfDocument(new PdfReader(templatePdfPath), new PdfWriter(destPdfPath));

        if (params != null && !params.isEmpty()) {// 有参数才替换
            PdfAcroForm form = PdfAcroForm.getAcroForm(pdf, true);
            Map<String, PdfFormField> fields = form.getFormFields(); // 获取所有的表单域
            for (String param : params.keySet()) {
                PdfFormField formField = fields.get(param);
                if (formField != null) {
                    replaceFieldImage(params, pdf, param, formField); // 替换图片
                }
            }
            form.flattenFields();// 锁定表单,不让修改
        }
        pdf.close();
    }

    /**
     * 替换域中的图片
     *
     * @param params
     * @param pdf
     * @param param
     * @param formField
     * @throws MalformedURLException
     */
    private static void replaceFieldImage(Map<String, String> params, PdfDocument pdf, String param,
            PdfFormField formField) throws MalformedURLException {
        String value = params.get(param);
        String[] values = value.split("\\|");
        Rectangle rectangle = formField.getWidgets().get(0).getRectangle().toRectangle(); // 获取表单域的xy坐标
        PdfCanvas canvas = new PdfCanvas(pdf.getPage(Integer.parseInt(values[0])));
        ImageData image = ImageDataFactory.create(values[1]);
        float imageWidth = image.getWidth();
        float imageHeight = image.getHeight();
        float rectangleWidth = rectangle.getWidth();
        float rectangleHeight = rectangle.getHeight();

        float tempWidth = 0;
        float tempHeight = 0;

        int result = 1; // 压缩宽度
        if (imageWidth > rectangleWidth) {
            tempHeight = imageHeight * rectangleWidth / imageWidth;
            if (tempHeight > rectangleHeight) {
                tempHeight = rectangleHeight;
                result = 2; // 压缩高度
            } else {
                tempWidth = rectangleWidth;
                tempHeight = imageHeight * rectangleWidth / imageWidth;
            }
        } else {
            if (imageHeight > rectangleHeight) {
                tempHeight = rectangleHeight;
                result = 2;
            } else {
                result = 3;
            }
        }

        float y = 0;

        if (result == 1) { // 压缩宽度
            y = rectangleHeight - tempHeight;
        } else if (result == 3) { // 不压缩
            y = rectangleHeight - imageHeight;
        }

        // y/=2; // 如果想要图片在表单域的上下对齐,这个值除以2就行。同理可以计算x的偏移

        if (result == 1) {
            canvas.addImage(image, rectangle.getX(), rectangle.getY() + y, tempWidth, false);
        } else if (result == 2) {
            canvas.addImage(image, rectangle.getX(), rectangle.getY(), tempHeight, false, false);
        } else if (result == 3) {
            canvas.addImage(image, rectangle.getX(), rectangle.getY() + y, false);
        }
    }

    /**
     * 添加水印
     *
     * @throws IOException
     * @throws FileNotFoundException
     */
    @SuppressWarnings("resource")
    public static final void addWatermark(String srcPdfPath, String destPdfPath, String watermarkText)
            throws FileNotFoundException, IOException {
        PdfDocument pdfDoc = new PdfDocument(new PdfReader(srcPdfPath), new PdfWriter(destPdfPath));

        pdfDoc.addEventHandler(PdfDocumentEvent.END_PAGE, new IEventHandler() {
            @Override
            public void handleEvent(Event event) {
                PdfDocumentEvent docEvent = (PdfDocumentEvent) event;
                PdfDocument pdfDoc = docEvent.getDocument();
                PdfPage page = docEvent.getPage();
                PdfFont font = null;
                try {
                    font = PdfFontFactory.createFont(StandardFonts.HELVETICA_BOLD); // 要显示中文水印的话,需要设置中文字体,这里可以动态判断
                } catch (IOException e) {
                    e.printStackTrace();
                }
                PdfCanvas canvas = new PdfCanvas(page);
                PdfExtGState gs1 = new PdfExtGState();
                gs1.setFillOpacity(0.5f); // 水印透明度
                canvas.setExtGState(gs1);
                new Canvas(canvas, pdfDoc, page.getPageSize()).setFontColor(ColorConstants.LIGHT_GRAY).setFontSize(60)
                        .setFont(font).showTextAligned(new Paragraph(watermarkText), 298, 421,
                                pdfDoc.getPageNumber(page), TextAlignment.CENTER, VerticalAlignment.MIDDLE, 45);
            }
        });
        pdfDoc.close();
    }

    /**
     *
     * @Title: pdfMerger
     * @Description: PDF合并
     * @param paths 需要合并的所有文件路径(路径+名称)
     * @param outputPath 合并后的文件路径(路径+名称)
     *  void
     * @author yanghainan
     * @date 2020年7月3日下午3:21:00
     */
    public static void pdfMerger(List<String> paths, String outputPath) {
        if (paths == null || paths.isEmpty() || StringUtils.isBlank(outputPath)) {
            return;
        }
        // 首页与其他页创建方式不同,所以需要两个
        PdfDocument pdfDocument = null;
        PdfDocument pdfDocument2 = null;
        // 合并工具类
        PdfMerger merger = null;
        try {
            for (int i = 0; i < paths.size(); i++) {
                if (i == 0) {
                    pdfDocument = new PdfDocument(new PdfReader(paths.get(i)), new PdfWriter(outputPath));
                    merger = new PdfMerger(pdfDocument);
                    continue;
                }
                pdfDocument2 = new PdfDocument(new PdfReader(paths.get(i)));
                merger.merge(pdfDocument2, 1, pdfDocument2.getNumberOfPages());
                pdfDocument2.close();
            }

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 关闭流
            if (pdfDocument != null) {
                pdfDocument.close();
            }
            if (pdfDocument2 != null) {
                pdfDocument2.close();
            }
        }
    }
}

使用

1.Adobe Acrobat Pro DC

使用Adobe Acrobat Pro DC 可以很方便的设置PDF模板,这样我们只需要将数据填充进去就可以了。如果没有安装可以去我的云盘下载:

下载链接

访问码:ynj6

如下是项目中用到的一个模板:

1

光有模板是不够的,还需要对模板进行表单设置:

2

默认选择就好,成功后会变成这样:

3

双击字段可以修改属性:

4

注意这里的名称就是javaBean的属性,需要按照相对应的设置。本文工具类里使用的是Map集合,设置对应的key就好。

2. 测试

1
2
3
4
5
6
7
8
9
10
11
public static void main(String[] args) throws FileNotFoundException, IOException {
    String templatePdfPath = "F:\\PDFtemplate\\模板.pdf";
    String destPdfPath = "F:\\PDFtemplate\\测试.pdf";
    Map<String, String> params = new HashedMap();
    params.put("fill_5", "111");
    try {
        PDFUtils.replaceTextFieldPdf(templatePdfPath, destPdfPath, params);
    } catch (IOException e) {
        e.printStackTrace();
    }
}

运行后:

5

3. 合并

刚搞好了数据填充生成PDF,又加了个合并PDF的需求,不过好在很简单.

  • 官网Demo
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import com.itextpdf.kernel.pdf.PdfDocument;
import com.itextpdf.kernel.pdf.PdfReader;
import com.itextpdf.kernel.pdf.PdfWriter;
import com.itextpdf.kernel.utils.PdfMerger;

import java.io.IOException;

public class PdfMerge {
    private static final String FILE1 = "/uploads/first.pdf";
    private static final String FILE2 = "/uploads/second.pdf";
    private static final String OUTPUT_FOLDER = "/myfiles/";

    public static void main(String args[]) throws IOException {
        PdfDocument pdfDocument = new PdfDocument(new PdfReader(FILE1), new PdfWriter(OUTPUT_FOLDER + "merged.pdf"));
        PdfDocument pdfDocument2 = new PdfDocument(new PdfReader(FILE2));

        PdfMerger merger = new PdfMerger(pdfDocument);
        merger.merge(pdfDocument2, 1, pdfDocument2.getNumberOfPages());

        pdfDocument2.close();
        pdfDocument.close();
    }
}
  • 我自己写的小Demo
1
2
3
4
5
6
7
8
9
10
11
12
    private static final String FILE1 = "F:\\PDFtemplate\\测试1.pdf";
    private static final String FILE2 = "F:\\PDFtemplate\\测试2.pdf";
    private static final String FILE3 = "F:\\PDFtemplate\\测试3.pdf";
    private static final String OUTPUT_FOLDER = "F:\\PDFtemplate\";

    public static void main(String[] args) throws FileNotFoundException, IOException {
        List<String> paths = new ArrayList<String>();
        paths.add(FILE1);
        paths.add(FILE2);
        paths.add(FILE3);
        PDFUtils.pdfMerger(paths, OUTPUT_FOLDER + "测试.pdf");
    }

具体效果就是合并为一个文件,就不再演示了。

本人也是初学,如有错漏欢迎批评指正。