日期:2014-05-16  浏览次数:20432 次

利用itext操作pdf从数据库导出大量数据--添加水印(四)
【原始需求】
通过SQL及JDBC模式导出各类业务数据,以PDF文件格式存放,要求该文件只能查看和打印(不能编辑和篡改),文件要有公司相关标志和水印功能。

【需求分析】
1、 通过SQL及JDBC模式导出业务数据,业务数据以表格内容格式存放于PDF文件
2、 PDF文件注明版权
3、 PDF页面中增加水印,公司文字或图片

【设计分析】
1、 生成PDF文件
2、 PDF文件注明版权
3、 PDF增加文字和图片水印
4、 PDF表格列数可能很多,比如1-50列信息,导出时需判断A4纸格式或其他格式宽度。
5、 PDF表格行数量可能超大,比如10万以上,甚至100万以上。
6、 增加水印信息后,文件大小增量应比较小,比如小于5%。

【功能开发】
1、 生成PDF文件
2、 支持中文字体
3、 PDF文件内容为表格,表格有表头
4、 PDF文件内容支持中文,表格内容上下居中,左右居中或左对齐/右对齐
5、 PDF列信息多寡不同,PDF采用页面宽度也能根据列信息按比例调整
6、 PDF行信息超大时写入模式,不能引起内存溢出等问题,有一定的并发性支撑能力。
7、 PDF文件增加作者相关版权信息
8、 PDF页头增加版权相关信息
9、 PDF文件修改权限限制,实现文档只可读取的权限
10、 PDF文件增加文字或图片水印功能,要求文字或图片在整个页面清晰可见。增加的水印信息不能使PDF文件大小增长超过5%。
11、 对PDF文件进行加密

【开发总结】
1、 大数据量写入的内存溢出问题采用文件流模式解决
2、 图片水印需要采用单例图片对象来处理,避免增加图片水印后PDF文件大小猛增
3、 PDF文件生成时,特别是当表格数据比较大时,CPU和JVM内存资源消耗都比较高,这样系统并发性较低。个人笔记本电脑单PDF文件导出时,CPU资源使用率在30%左右,JVM内存资源达到堆大小的极限,因为垃圾回收的缘故避免了内存溢出(已按流式数据处理方式)。
4、 水印的位置需要根据PDF页面大小和水印本身的信息位置范围来确定

利用itext操作pdf从数据库导出大量数据--功能汇总(一)

利用itext操作pdf从数据库导出大量数据--创建PDF相关(二)

利用itext操作pdf从数据库导出大量数据--创建PDF表格(三)

利用itext操作pdf从数据库导出大量数据--添加水印(四)

package com.fruitking.testpdf.util;

import java.awt.Color;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.MalformedURLException;

import com.lowagie.text.BadElementException;
import com.lowagie.text.Document;
import com.lowagie.text.DocumentException;
import com.lowagie.text.Element;
import com.lowagie.text.Font;
import com.lowagie.text.Image;
import com.lowagie.text.Paragraph;
import com.lowagie.text.Phrase;
import com.lowagie.text.pdf.BaseFont;
import com.lowagie.text.pdf.ColumnText;
import com.lowagie.text.pdf.PdfContentByte;
import com.lowagie.text.pdf.PdfGState;
import com.lowagie.text.pdf.PdfPCell;
import com.lowagie.text.pdf.PdfPTable;
import com.lowagie.text.pdf.PdfPageEventHelper;
import com.lowagie.text.pdf.PdfReader;
import com.lowagie.text.pdf.PdfStamper;
import com.lowagie.text.pdf.PdfWriter;

public class PDFCreate2WaterMark {
	
	/**
	 * 根据图片文件路径构建一个图片对象
	 * @param waterMarkFullFilePath
	 * @param xPosition
	 * @param yPosition
	 * @return
	 * @throws BadElementException
	 * @throws MalformedURLException
	 * @throws IOException
	 */
	private Image getWaterMarkImage(String waterMarkFullFilePath,float xPosition,float yPosition)throws BadElementException, MalformedURLException, IOException{
    	Image waterMarkImage = Image.getInstance(waterMarkFullFilePath);
    	waterMarkImage.setAbsolutePosition(xPosition, yPosition);//坐标
    	waterMarkImage.setRotation(-20);//旋转 弧度
    	waterMarkImage.setRotationDegrees(-45);//旋转 角度
    	waterMarkImage.scalePercent(100);//依照比例缩放
        return waterMarkImage;
    }
    
	/**
	 * 对一个图片对象设置展示位置等信息,该对象重复利用,减少PDF文件大小
	 * @param waterMarkImage
	 * @param xPosition
	 * @param yPosition
	 * @return
	 */
    private Image getSingletonWaterMarkImage(Image waterMarkImage,float xPosition,float yPosition){
    	waterMarkImage.setAbsolutePosition(xPosition, yPosition);//坐标
    	waterMarkImage.setRotation(-20);//旋转 弧度
    	waterMarkImage.setRotationDegrees(-45);//旋转 角度
    	waterMarkImage.scalePercent(100);//依照比例缩放
        return waterMarkImage;
    }
	
    /**
     * 对于已存在的PDF文件增加图片水印
     * 图片水印按照两列三行模式,并旋转45度正斜形式添加水印图片
     * @param pdfInputFile
     * @param pdfOutputFile
     * @param waterMarkFullFilePath
     */
	public void addPictureWaterMark(String pdfInputFile,String pdfOutputFile,String waterMarkFullFilePath) {
		PdfStamper pdfStamper = null;
		PdfReader reader = null;
        try {
        	//构造需要添加水印的PDF文件对象
            reader = new PdfReader(pdfInputFile);
            int pdfFileTotalPages = reader.getNumberOfPages() + 1;
            //创建添加水印后的PDF文件对象
            OutputStream pdfOutputStream =