1. 程式人生 > >JAVA ITEXT5 匯出為PDF(一)表格匯出PDF

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