itextpdf JAVA 输出PDF文档

时间:2022-07-16 05:45:16

使用JAVA生成PDF的时候,还是有些注意事项需要处理的。

第一、中文问题,默认的itext是不支持中文的,想要支持,需要做些处理。

  1、直接引用操作系统的中文字体库支持,由于此方案限制性强,又绑定了操作系统,所以此处不做实现,有兴趣可在网上搜索看看。

  2、引用itext-asian.jar包的字体支持,代码稍后上。

    itext pdf引入中文常见异常:

      com.itextpdf.text.DocumentException: Font 'STSongStd-Light' with 'UniGB-UCS2-H' is not recognized.

    解决方案:a、引入操作系统字体;2、将字体维护进jar包中,如果没有,直接找到字体放入对应jar包中,如果是路径错误,则更改包路径;3、通过itext-asian.jar来加载中文包。

第二、表格中的设置,特别是上中下,左中右,不同的对象有不同的枚举实现,刚入手很容易混淆。其外是前景色,背景色,表格颜色等等。

第三、输出图片,很容易报错。

    itext pdf常见输出图片异常:

    An error exists on this page. Acrobat may not display the page correctly. Please contact the person who created the PDF document to correct the problem.

    原因:PdfContentByte在addImage的时候需要在beginText()和endText()范围以外调用,否则生成的PDF就会报上述错误。

示例:

  1 package com.itext.test;
2
3 import java.io.File;
4 import java.io.FileOutputStream;
5 import java.io.IOException;
6 import java.io.InputStream;
7 import java.util.ArrayList;
8 import java.util.List;
9 import java.util.Random;
10
11 import com.itextpdf.text.BaseColor;
12 import com.itextpdf.text.Document;
13 import com.itextpdf.text.DocumentException;
14 import com.itextpdf.text.Element;
15 import com.itextpdf.text.Font;
16 import com.itextpdf.text.Image;
17 import com.itextpdf.text.PageSize;
18 import com.itextpdf.text.Paragraph;
19 import com.itextpdf.text.Rectangle;
20 import com.itextpdf.text.pdf.BarcodeQRCode;
21 import com.itextpdf.text.pdf.BaseFont;
22 import com.itextpdf.text.pdf.PdfContentByte;
23 import com.itextpdf.text.pdf.PdfGState;
24 import com.itextpdf.text.pdf.PdfPCell;
25 import com.itextpdf.text.pdf.PdfPTable;
26 import com.itextpdf.text.pdf.PdfPageEventHelper;
27 import com.itextpdf.text.pdf.PdfWriter;
28
29 public class D {
30
31 private static String path = "docs/"; // 生成PDF后的存放路径
32 private static final String logoPath = "logo.png";
33
34 public static void main(String[] args) {
35 // T t = new T();
36 initPDF(initData());
37 }
38
39 /**
40 * 初始化PDF
41 *
42 * @param apis
43 */
44 public static void initPDF(List<Api> apis) {
45 File folder = new File(path);
46 if (!folder.exists())
47 folder.mkdirs(); // 创建目录
48 Document doc = null;
49 try {
50 // 中文字体,要有itext-asian.jar支持(默认的itext.jar是不支持中文的)
51 BaseFont bfchinese = BaseFont.createFont("STSongStd-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);
52 Rectangle pageSize = new Rectangle(PageSize.A4); // 页面大小设置为A4
53 doc = new Document(pageSize, 20, 20, 40, 40); // 创建doc对象并设置边距
54 PdfWriter writer = PdfWriter.getInstance(doc, new FileOutputStream(folder.getAbsolutePath() + File.separator + "API文档.pdf"));
55 writer.setPageEvent(new SdkPdfPageEvent());
56 doc.open();
57 doc.addAuthor("Ares-xby");
58 doc.addSubject("SDK附属API文档");
59 doc.addTitle("API文档");
60 BaseColor borderColor = new BaseColor(90, 140, 200);
61 BaseColor bgColor = new BaseColor(80, 130, 180);
62 for (Api api : apis) {
63 PdfPTable table = new PdfPTable({0.25f, 0.25f, 0.25f, 0.25f});
64 // table.setWidthPercentage(100); // 设置table宽度为100%
65 // table.setHorizontalAlignment(PdfPTable.ALIGN_CENTER); // 设置table居中显示
66 for (int i = 0; i < api.getParams().size(); i++) {
67 if (i == 0) {
68 // row 1
69 table.addCell(createCell("API", bfchinese, borderColor, bgColor));
70 table.addCell(createCell(api.getApiName(), 12, bfchinese, 3, null, borderColor, bgColor));
71 // row 2
72 table.addCell(createCell("描述", bfchinese, borderColor));
73 table.addCell(createCell(api.getApiDesc(), 12, bfchinese, 3, null, borderColor));
74 } else {
75 table.addCell(createCell(api.getParams().get(i).getParamName(), 10, bfchinese, null, Paragraph.ALIGN_RIGHT, borderColor));
76 table.addCell(createCell(api.getParams().get(i).getParamName(), 10, bfchinese, null, null, borderColor));
77 table.addCell(createCell(api.getParams().get(i).getParamType(), 10, bfchinese, null, null, borderColor));
78 table.addCell(createCell(api.getParams().get(i).getParamDesc(), 10, bfchinese, null, null, borderColor));
79 }
80 }
81 doc.add(table);
82 }
83 // 二维码
84 BarcodeQRCode qrcode = new BarcodeQRCode("http://www.baidu.com", 1, 1, null);
85 Image qrcodeImage = qrcode.getImage();
86 qrcodeImage.setAbsolutePosition(10, 600);
87 qrcodeImage.scalePercent(200);
88 doc.add(qrcodeImage);
89 doc.close();
90 System.out.println("init pdf over.");
91 } catch (DocumentException e) {
92 e.printStackTrace();
93 } catch (IOException e) {
94 e.printStackTrace();
95 } finally {
96 if (doc != null)
97 doc.close();
98 }
99
100 }
101
102 public static List<Api> initData() {
103 List<Api> list = new ArrayList<Api>();
104 for (int i = 0; i < 100; i++) {
105 Api api = new Api();
106 api.setApiName("api-" + i);
107 api.setApiDesc("描述-" + i);
108 int paramSize = new Random().nextInt(20);
109 List<Params> paramList = new ArrayList<Params>();
110 for (int j = 0; j < paramSize; j++) {
111 Params param = new Params();
112 param.setParamName("param-" + i + "-" + j);
113 param.setParamType("paramType-" + i + "-" + j);
114 param.setParamDesc("描述-" + i + "-" + j);
115 paramList.add(param);
116 }
117 api.setParams(paramList);
118 list.add(api);
119 }
120 System.out.println("init data over. size=" + list.size());
121 return list;
122 }
123 // 用於生成cell
124 private static PdfPCell createCell(String text, BaseFont font, BaseColor borderColor) {
125 return createCell(text, 12, font, null, null, borderColor, null);
126 }
127 // 用於生成cell
128 private static PdfPCell createCell(String text, BaseFont font, BaseColor borderColor, BaseColor bgColor) {
129 return createCell(text, 12, font, null, null, borderColor, bgColor);
130 }
131 // 用於生成cell
132 private static PdfPCell createCell(String text, int fontsize, BaseFont font, Integer colspan, Integer align, BaseColor borderColor) {
133 return createCell(text, fontsize, font, colspan, align, borderColor, null);
134 }
135
136 /**
137 * 用於生成cell
138 * @param text Cell文字内容
139 * @param fontsize 字体大小
140 * @param font 字体
141 * @param colspan 合并列数量
142 * @param align 显示位置(左中右,Paragraph对象)
143 * @param borderColor Cell边框颜色
144 * @param bgColor Cell背景色
145 * @return
146 */
147 private static PdfPCell createCell(String text, int fontsize, BaseFont font, Integer colspan, Integer align, BaseColor borderColor, BaseColor bgColor) {
148 Paragraph pagragraph = new Paragraph(text, new Font(font, fontsize));
149 PdfPCell cell = new PdfPCell(pagragraph);
150 cell.setFixedHeight(20);
151 cell.setVerticalAlignment(Element.ALIGN_MIDDLE); // 上中下,Element对象
152 if (align != null)
153 cell.setHorizontalAlignment(align);
154 if (colspan != null && colspan > 1)
155 cell.setColspan(colspan);
156 if (borderColor != null)
157 cell.setBorderColor(borderColor);
158 if (bgColor != null)
159 cell.setBackgroundColor(bgColor);
160 return cell;
161 }
162
163 /**
164 * SDK中PDF相关的PageEvent
165 */
166 static class SdkPdfPageEvent extends PdfPageEventHelper {
167
168 @Override
169 public void onStartPage(PdfWriter writer, Document document) {
170 // 水印(water mark)
171 PdfContentByte pcb = writer.getDirectContent();
172 pcb.saveState();
173 BaseFont bf;
174 try {
175 bf = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.EMBEDDED);
176 pcb.setFontAndSize(bf, 36);
177 } catch (DocumentException e) {
178 e.printStackTrace();
179 } catch (IOException e) {
180 e.printStackTrace();
181 }
182 // 透明度设置
183 PdfGState gs = new PdfGState();
184 gs.setFillOpacity(0.2f);
185 pcb.setGState(gs);
186
187 pcb.beginText();
188 pcb.setTextMatrix(60, 90);
189 pcb.showTextAligned(Element.ALIGN_LEFT, "XX公司有限公司", 200, 300, 45);
190
191 pcb.endText();
192 pcb.restoreState();
193 }
194
195 @Override
196 public void onEndPage(PdfWriter writer, Document document) {
197 // 页眉、页脚
198 PdfContentByte pcb = writer.getDirectContent();
199 try {
200 pcb.setFontAndSize(BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.EMBEDDED), 10);
201 } catch (Exception e) {
202 e.printStackTrace();
203 } // 支持中文字体
204 pcb.saveState();
205 try {
206 // pcb.addImage()方法要在pcb.beginText();pcb.endText();之外调用,
207 // 否则生成的PDF打开时会报错: An error exists on this page. Acrobat may not display the page correctly. Please contact the person who created the PDF document to correct the problem.
208 byte[] logoBytes = new byte[1000 * 1024]; // 此处数组大小要比logo图片大小要大, 否则图片会损坏;能够直接知道图片大小最好不过.
209 InputStream logoIs = getClass().getResourceAsStream(logoPath);
210 if(logoIs != null){
211 int logoSize = logoIs.read(logoBytes); // 尝试了一下,此处图片复制不完全,需要专门写个方法,将InputStream转换成Byte数组,详情参考org.apache.io.IOUtils.java的toByteArray(InputStream in)方法
212 if(logoSize > 0){
213 byte[] logo = new byte[logoSize];
214 System.arraycopy(logoBytes, 0, logo, 0, logoSize);
215 Image image = Image.getInstance(logo);// 如果直接使用logoBytes,并且图片是jar包中的话,会报图片损坏异常;本地图片可直接getInstance时候使用路径。
216 image.setAbsolutePosition(document.left(), document.top(-5)); // 设置图片显示位置
217 image.scalePercent(12); // 按照百分比缩放
218 pcb.addImage(image);
219 }
220 }else System.err.println("logo input stream is null.");
221 } catch (Exception e) {
222 System.err.println(e);
223 }
224 pcb.beginText();
225
226 // Header
227 float top = document.top(-15);
228 pcb.showTextAligned(PdfContentByte.ALIGN_RIGHT, "XX开放平台API文档", document.right(), top, 0);
229 // Footer
230 float bottom = document.bottom(-15);
231 pcb.showTextAligned(PdfContentByte.ALIGN_CENTER, "第 " + writer.getPageNumber() + " 页", (document.right() + document.left()) / 2, bottom, 0);
232 pcb.endText();
233
234 pcb.restoreState();
235 pcb.closePath();
236 }
237 }
238 /**
239 * POJO for init Data.
240 */
241 static class Api {
242
243 private String apiName;
244 private String apiDesc;
245 private List<Params> params;
246
247 public String getApiName() {
248 return apiName;
249 }
250 public void setApiName(String apiName) {
251 this.apiName = apiName;
252 }
253 public String getApiDesc() {
254 return apiDesc;
255 }
256 public void setApiDesc(String apiDesc) {
257 this.apiDesc = apiDesc;
258 }
259 public List<Params> getParams() {
260 return params;
261 }
262 public void setParams(List<Params> params) {
263 this.params = params;
264 }
265 }
266
267 /**
268 * POJO for init Data.
269 */
270 static class Params {
271
272 private String paramName;
273 private String paramType;
274 private String paramDesc;
275
276 public String getParamName() {
277 return paramName;
278 }
279 public void setParamName(String paramName) {
280 this.paramName = paramName;
281 }
282 public String getParamType() {
283 return paramType;
284 }
285 public void setParamType(String paramType) {
286 this.paramType = paramType;
287 }
288 public String getParamDesc() {
289 return paramDesc;
290 }
291 public void setParamDesc(String paramDesc) {
292 this.paramDesc = paramDesc;
293 }
294 }
295 }

 

 

注意:

所需jar包(itext5.0以下的包路径是com.lowagie.text,与后续版本不一致,并且直接引用中文itext-asian.jar的时候会因为lowagie名称造成路径错误,导致中文引入失败):

itext-5.0.2.jar, itext-asian-5.1.1.jar

也可以是itextpdf-5.5.5.jar, itext-asian-5.2.0.jar(下载)(建议使用),注意搭配不要错误。搭配的依赖原则是itext.jar或者itextpdf.jar中com.itextpdf.text.pdf.CJKFont.java中load字体所用的路径,

 itext-asian-5.1.1.jar的路径是com/itextpdf/text/pdf/fonts/cjkfonts.properties,对应itext-5.0.2.jar中CJKFont的load路径;

 itext-asian-5.2.0.jar的路径是com/itextpdf/text/pdf/fonts/cmaps/cjk_registry.properties,对应itextpdf-5.5.5.jar中CJKFont的load路径。

 

最终效果:

itextpdf JAVA 输出PDF文档