使用JAVA生成PDF的時候,仍是有些注意事項須要處理的。java
第1、中文問題,默認的itext是不支持中文的,想要支持,須要作些處理。apache
一、直接引用操做系統的中文字體庫支持,因爲此方案限制性強,又綁定了操做系統,因此此處不作實現,有興趣可在網上搜索看看。api
二、引用itext-asian.jar包的字體支持,代碼稍後上。數組
itext pdf引入中文常見異常:dom
com.itextpdf.text.DocumentException: Font 'STSongStd-Light' with 'UniGB-UCS2-H' is not recognized.ide
解決方案:a、引入操做系統字體;二、將字體維護進jar包中,若是沒有,直接找到字體放入對應jar包中,若是是路徑錯誤,則更改包路徑;三、經過itext-asian.jar來加載中文包。字體
第2、表格中的設置,特別是上中下,左中右,不一樣的對象有不一樣的枚舉實現,剛入手很容易混淆。其外是前景色,背景色,表格顏色等等。this
第3、輸出圖片,很容易報錯。spa
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路徑。
最終效果: