JAVA ITEXT5 匯出為PDF(一)表格匯出PDF
前不久工作中遇到這樣的需求:點選列表資料要將頁面上展示的列表資料匯出為PDF檔案進行下載,如果將HTML頁面轉化為PDF檔案,這樣資料會匯出不全面,所以只能能後臺資料庫取出資料直接進行生成(不論全面與否均可利用iText5實現)。
利用開源的API iText5 將資料匯出為PDF,在此做點總結:
(一)自定義PdfPageEventHelper
iText5中並沒有之前版本HeaderFooter物件設定頁首和頁尾,可以利用PdfPageEventHelper來完成頁首頁尾及相關水印的設定工作。
PdfPageEventHelper中包含以下事件處理器:
onOpenDocument() — 當開啟一個文件時觸發,可以用於初始化文件的全域性變數。
onStartPage() — 當一個頁面初始化時觸發,可用於初始化頁面的設定引數,但是注意這個函式觸發時,該頁面並沒有建立好,不能利用這個函式新增內容,應該利用onEndPage()進行頁面內容初始化。
onEndPage() — 在建立一個新頁面完成但寫入內容之前觸發,是新增頁首、頁尾、水印等最佳時機。
onCloseDocument() — 在文件關閉之前觸發,可以用於釋放一些資源。
/** * @author : ShiLei * @time :2018年5月7日 上午10:06:44 * @introduction : 自定義PdfPageEventHelper:iText5中並沒有之前版本HeaderFooter物件設定頁首和頁尾,可以利用PdfPageEventHelper來完成頁首頁尾及相關水印的設定工作。 */ import java.io.IOException; import com.itextpdf.text.BadElementException; 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.Image; import com.itextpdf.text.Phrase; import com.itextpdf.text.Rectangle; import com.itextpdf.text.pdf.BaseFont; import com.itextpdf.text.pdf.ColumnText; import com.itextpdf.text.pdf.PdfContentByte; import com.itextpdf.text.pdf.PdfPageEventHelper; import com.itextpdf.text.pdf.PdfTemplate; import com.itextpdf.text.pdf.PdfWriter; public class MyPdfPageEventHelper extends PdfPageEventHelper { // 頁首 public String yeMei; // 頁尾簽名 public String footerDesc; // 紙張大小 public Rectangle pageSize; // 模板 public PdfTemplate template; // 設定 中文 public BaseFont baseFont; // 字型 樣式 public Font font; // 字型大小 public static final int fontSize=12; // 文字水印內容 public String waterMarkText; // 水印圖片路徑 public String imgUrl; /** * * @param yeMei * 頁首詳情描述 * @param footerDesc * 頁尾簽名 * @param baseFont * 中文字型 基本物件 * @param pageSize * 頁面文件大小,A4,A5,A6橫轉翻轉等Rectangle物件 * @param waterMarkText * 文字水印內容 * @param imgUrl * 水印圖片路徑 */ public MyPdfPageEventHelper(String yeMei,String footerDesc, BaseFont baseFont, Rectangle pageSize) { this.yeMei = yeMei; this.footerDesc = footerDesc; this.pageSize = pageSize; this.baseFont = baseFont; } public MyPdfPageEventHelper(String yeMei,String footerDesc, BaseFont baseFont, Rectangle pageSize,String waterMarkText) { this.yeMei = yeMei; this.footerDesc = footerDesc; this.baseFont = baseFont; this.pageSize = pageSize; this.waterMarkText=waterMarkText; } public MyPdfPageEventHelper(String yeMei, String footerDesc,String imgUrl,BaseFont baseFont, Rectangle pageSize) { this.yeMei = yeMei; this.footerDesc = footerDesc; this.imgUrl=imgUrl; this.baseFont = baseFont; this.pageSize = pageSize; } // 文件開啟時建立模板 public void onOpenDocument(PdfWriter writer, Document document) { // 新增所生成的pdf檔案 資訊 document.addSubject("GCX表格資料生成PDF"); document.addCreationDate(); document.addCreator("GCX"); template = writer.getDirectContent().createTemplate(50, 50);// 共 頁 的矩形的長寬高 } // 關閉每頁的時候,寫入頁首,寫入'第幾頁共'這幾個字。 public void onEndPage(PdfWriter writer, Document document) { // 設定所支援的中文字型的 字型樣式大小 font = new Font(baseFont, fontSize, Font.NORMAL); // 獲取PdfContentByte PdfContentByte cb = writer.getDirectContent(); // 1.寫入頁首 ColumnText.showTextAligned(cb, Element.ALIGN_LEFT, new Phrase(yeMei,font), document.left(), document.top() + 20, 0); // 2.寫入前半部分的 第 X頁/共 int pageS = writer.getCurrentPageNumber(); String foot1 = "第 " + pageS + " 頁 /共"; Phrase footer = new Phrase(foot1, font); // 3.計算前半部分的foot1的長度,後面好定位最後一部分的'Y頁'這倆字的x軸座標,字型長度也要計算進去 = len float len = baseFont.getWidthPoint(foot1, fontSize); // 4.設定 頁尾簽名 Phrase footerLeft = new Phrase(footerDesc, font); ColumnText.showTextAligned(cb, Element.ALIGN_LEFT, footerLeft, document.left(), document.bottom() - 20, 0); // 5.寫入頁尾1 // x軸就是(右margin+左margin + right() -left()- len)/2.0F 再給偏移20F適合人類視覺感受,否則肉眼看上去太偏左 , // y軸就是底邊界-20,否則容易與頁尾簽名重疊;注意Y軸是從下往上累加的,最上方的Top值是大於Bottom好幾百開外的。 ColumnText.showTextAligned(cb, Element.ALIGN_CENTER, footer, (document.rightMargin() + document.right() + document.leftMargin() - document.left() - len) / 2.0F + 20F, document.bottom() - 20, 0); // 6.寫入頁尾2的模板(就是頁尾的Y頁這倆字)新增到文件中,計算模板的和Y軸,X=(右邊界-左邊界 - 前半部分的len值)/2.0F + len , y 軸和之前的保持一致,底邊界-20 cb.addTemplate(template, (document.rightMargin() + document.right() + document.leftMargin() - document.left()) / 2.0F + 20F, document.bottom() - 20); // 調節模版顯示的位置 // 新增圖片水印 if (!"".equals(imgUrl) && imgUrl!=null) { try { Image image = Image.getInstance(imgUrl); for(int i=1; i<= writer.getPageNumber(); i++){ PdfContentByte content = writer.getDirectContentUnder(); try { content.addImage(getSingletonWaterMarkImage(image,20f,40f)); } catch (DocumentException e) { e.printStackTrace(); } } } catch (BadElementException | IOException e) { e.printStackTrace(); } } // 新增文字水印 if (!"".equals(waterMarkText) && waterMarkText!=null) { for(int i=1; i<= writer.getPageNumber(); i++){ PdfContentByte content = writer.getDirectContentUnder(); content.beginText(); content.setColorFill(BaseColor.CYAN);// 文字水印 顏色 content.setFontAndSize(baseFont,38);// 文字水印 字型及字號 content.setTextMatrix(300, 350);// 文字水印 起始位置 content.showText(waterMarkText); content.endText(); } } } // 關閉文件時,替換模板,完成整個頁首頁尾元件 public void onCloseDocument(PdfWriter writer, Document document) { // 7.關閉文件的時候,將模板替換成實際的 Y 值 template.beginText(); template.setFontAndSize(baseFont, fontSize);// 生成的模版的字型、顏色 String foot2 = " " + (writer.getPageNumber()) + " 頁"; template.showText(foot2);// 模版顯示的內容 template.endText(); template.closePath(); } //調整圖片位置 public Image getSingletonWaterMarkImage(Image waterMarkImage,float xPosition,float yPosition){ waterMarkImage.setAbsolutePosition(xPosition, yPosition);//座標 waterMarkImage.setRotation(-20);//旋轉 弧度 waterMarkImage.setRotationDegrees(-45);//旋轉 角度 waterMarkImage.scalePercent(100);//依照比例縮放 return waterMarkImage; } }
(二)在程式中觸發該自定義PdfPageEventHelper的一系列事件:
MyPdfPageEventHelper helper = new MyPdfPageEventHelper();
writer.setPageEvent(helper);
@GetMapping(path="/toPdf")
@ApiOperation(value = "資料表格PDF生成")
public void tableToPdf(HttpServletResponse response,Statistic stat) throws ParseException, DocumentException, IOException{
// 預設的iText5字型設定不支援中文字型,需要下載遠東字型包,否則不能往PDF文件中輸出中文字型。
BaseFont bfChinese = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);
Font titleCN = new Font(bfChinese, 17,Font.NORMAL);
Font contentCN = new Font(bfChinese, 12,Font.NORMAL);
String[] titles = {"時間","新增條目數"};
//獲取資料集合 及 資料總數
MyResult result = dataStatistics.statisticAnalysis(stat);
@SuppressWarnings("unchecked")
List<Map<String, Object>> data = (List<Map<String, Object>>)result.getData();
int total = result.getCount();
try {
// step 1 建立文件
Document document = new Document();
// step 2 設定下載頭 並將PdfWriter定向到response的輸出流上
// response.setContentType("application/pdf");
response.setHeader("Content-Disposition", "attachment;filename="+ new String(("表格資料生成PDF"+System.currentTimeMillis() + ".pdf").getBytes(), "iso-8859-1"));
PdfWriter writer = PdfWriter.getInstance(document, response.getOutputStream());
// step 3 通過setPageEvent事件 新增頁首,頁尾,分頁及水印等資訊
MyPdfPageEventHelper helper = new MyPdfPageEventHelper("GCX有限公司","GCX您一生的信用記錄者",bfChinese,PageSize.A4);
writer.setPageEvent(helper);
// step 3 開啟文件
document.open();
// step 4 設定基本表格樣式 及 新增列資料
PdfPTable table = new PdfPTable(2); //指定為2列
table.setWidthPercentage(100); // 自定義寬度 100%
table.setSpacingBefore(10f);
table.setSpacingAfter(10f);
float[] columnWidths = {1f, 1f};//設定列寬
table.setWidths(columnWidths);
PdfPCell cell = createCell("部門:"+stat.getDep()+" 資料總數(條):"+total,titleCN);
cell.setRowspan(1);
cell.setColspan(2);
table.addCell(cell);
//時間
table.addCell(createCell(titles[0],titleCN));
//新增條目數
table.addCell(createCell(titles[1],titleCN));
for(int i = 0;i < data.size();i++){
Map<String, Object> map = data.get(i);
table.addCell(createCell(map.get("time"),contentCN));
table.addCell(createCell(map.get("count"),contentCN));
}
//step 5 將表格掛載到文件上 並進行資源關閉
document.add(table);
document.close();
writer.close();
System.out.println( "PDF Created!" );
} catch (Exception e) {
e.printStackTrace();
}
}
//生成單元格
public static PdfPCell createCell(Object value,Font font) {
PdfPCell cell = new PdfPCell(new Paragraph(value.toString(),font));
cell.setBorderColor(BaseColor.BLACK);
cell.setHorizontalAlignment(Element.ALIGN_CENTER);
cell.setVerticalAlignment(Element.ALIGN_MIDDLE);
return cell;
}
(三)需引入依賴
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itextpdf</artifactId>
<version>5.5.12</version>
</dependency>
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itext-asian</artifactId>
<version>5.2.0</version>
</dependency>
(四)注意 "STSong-Light' with 'UniGB-UCS2-H' is not recognized"問題原因:
iText5.x版本以上中的font和encoding檔案都是從String RESOURCE_PATH = “com/itextpdf/text/pdf/fonts/”載入的,而itextasian1.5.x.jar的包名是com.lowagie.text.pdf.fonts, 包名不一致,導致路徑錯誤。引入上述itext-asian依賴即可完美解決。
參考:
https://www.cnblogs.com/chenpi/p/5534595.html
http://www.anyrt.com/blog/list/itextpdf.html
http://fruitking.iteye.com/blog/1961421
https://blog.csdn.net/younkerjqb/article/details/14125955