利用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页面大小和水印本身的信息位置范围来确定

相关代码以及jar包说明文件:http://download.csdn.net/detail/baby_soga/7105763

Java代码   收藏代码
  1. package com.fruitking.testpdf.util;  
  2.   
  3. import java.awt.Color;  
  4. import java.io.IOException;  
  5.   
  6. import com.lowagie.text.Document;  
  7. import com.lowagie.text.DocumentException;  
  8. import com.lowagie.text.Element;  
  9. import com.lowagie.text.Font;  
  10. import com.lowagie.text.Image;  
  11. import com.lowagie.text.Phrase;  
  12. import com.lowagie.text.pdf.BaseFont;  
  13. import com.lowagie.text.pdf.ColumnText;  
  14. import com.lowagie.text.pdf.PdfContentByte;  
  15. import com.lowagie.text.pdf.PdfGState;  
  16. import com.lowagie.text.pdf.PdfPageEventHelper;  
  17. import com.lowagie.text.pdf.PdfWriter;  
  18.   
  19. public class PdfFileExportUtil {  
  20.       
  21.     private static Font pdf8Font = null;  
  22.     private static Font pdf20Font = null;  
  23.       
  24.     /** 
  25.      * 设置PDF创建者信息 
  26.      * @param pdfDocument 
  27.      */  
  28.     public static Document setCreatorInfo(Document pdfDocument){  
  29.         if(pdfDocument==null){  
  30.             return null;  
  31.         }  
  32.         //文档属性  
  33.         pdfDocument.addTitle("水果大王信息技术有限公司数据安全产品");  
  34.         pdfDocument.addAuthor("杭州水果大王信息技术有限公司");  
  35.         pdfDocument.addSubject("文件导出的信息安全管控");  
  36.         pdfDocument.addKeywords("文件导出,信息安全");//文档关键字信息  
  37.         pdfDocument.addCreator("水果大王文件取数系统");//应用程序名称  
  38.         return pdfDocument;  
  39.     }  
  40.       
  41.       
  42.     /** 
  43.      * 获取中文字符集且是8号字体,常用作表格内容的字体格式 
  44.      * @param fullFilePath 
  45.      */  
  46.     public static Font getChinese8Font()throws DocumentException,IOException{  
  47.         if(pdf8Font==null){  
  48.             //设置中文字体和字体样式  
  49.             BaseFont bfChinese = BaseFont.createFont("STSong-Light""UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);    
  50.             pdf8Font = new Font(bfChinese, 8, Font.NORMAL);  
  51.         }  
  52.         return pdf8Font;        
  53.     }  
  54.       
  55.     /** 
  56.      * 获取中文字符集且是8号字体,常用作文字水印信息 
  57.      * @param fullFilePath 
  58.      */  
  59.     public static Font getChinese20Font()throws DocumentException,IOException{  
  60.         if(pdf20Font==null){  
  61.             //设置中文字体和字体样式  
  62.             BaseFont bfChinese = BaseFont.createFont("STSong-Light""UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);    
  63.             pdf20Font = new Font(bfChinese, 20, Font.BOLD, Color.CYAN);  
  64.         }  
  65.         return pdf20Font;        
  66.     }  
  67.       
  68.     /** 
  69.      * 设置成只读权限 
  70.      * @param pdfWriter 
  71.      */  
  72.     public static PdfWriter setReadOnlyPDFFile(PdfWriter pdfWriter)throws DocumentException{  
  73.         pdfWriter.setEncryption(nullnull,PdfWriter.ALLOW_PRINTING, PdfWriter.STANDARD_ENCRYPTION_128);  
  74.         return pdfWriter;  
  75.     }  
  76.       
  77.     /** 
  78.      * 变更一个图片对象的展示位置和角度信息 
  79.      * @param waterMarkImage 
  80.      * @param xPosition 
  81.      * @param yPosition 
  82.      * @return 
  83.      */  
  84.     public static Image getWaterMarkImage(Image waterMarkImage,float xPosition,float yPosition){  
  85.         waterMarkImage.setAbsolutePosition(xPosition, yPosition);//坐标  
  86.         waterMarkImage.setRotation(-20);//旋转 弧度  
  87.         waterMarkImage.setRotationDegrees(-45);//旋转 角度  
  88.         waterMarkImage.scalePercent(100);//依照比例缩放  
  89.         return waterMarkImage;  
  90.     }  
  91.       
  92.     /** 
  93.      * 为PDF分页时创建添加文本水印的事件信息 
  94.      */  
  95.     class TextWaterMarkPdfPageEvent extends PdfPageEventHelper{  
  96.           
  97.         private String waterMarkText;  
  98.           
  99.         public TextWaterMarkPdfPageEvent(String waterMarkText){  
  100.             this.waterMarkText = waterMarkText;  
  101.         }  
  102.           
  103.         public void onEndPage(PdfWriter writer, Document document) {  
  104.             try{  
  105.                 float pageWidth = document.right()+document.left();//获取pdf内容正文页面宽度  
  106.                 float pageHeight = document.top()+document.bottom();//获取pdf内容正文页面高度  
  107.                 //设置水印字体格式  
  108.                 Font waterMarkFont = PdfFileExportUtil.getChinese20Font();  
  109.                 PdfContentByte waterMarkPdfContent = writer.getDirectContentUnder();  
  110.                 Phrase phrase = new Phrase(waterMarkText, waterMarkFont);  
  111.                 ColumnText.showTextAligned(waterMarkPdfContent,Element.ALIGN_CENTER,phrase,    
  112.                         pageWidth*0.25f,pageHeight*0.2f,45);  
  113.                 ColumnText.showTextAligned(waterMarkPdfContent,Element.ALIGN_CENTER,phrase,    
  114.                         pageWidth*0.25f,pageHeight*0.5f,45);  
  115.                 ColumnText.showTextAligned(waterMarkPdfContent,Element.ALIGN_CENTER,phrase,    
  116.                         pageWidth*0.25f,pageHeight*0.8f,45);  
  117.                 ColumnText.showTextAligned(waterMarkPdfContent,Element.ALIGN_CENTER,phrase,    
  118.                         pageWidth*0.65f,pageHeight*0.2f,45);  
  119.                 ColumnText.showTextAligned(waterMarkPdfContent,Element.ALIGN_CENTER,phrase,    
  120.                         pageWidth*0.65f,pageHeight*0.5f,45);  
  121.                 ColumnText.showTextAligned(waterMarkPdfContent,Element.ALIGN_CENTER,phrase,    
  122.                         pageWidth*0.65f,pageHeight*0.8f,45);  
  123.             }catch(DocumentException de) {  
  124.                 de.printStackTrace();  
  125.                 System.err.println("pdf watermark font: " + de.getMessage());  
  126.             }catch(IOException de) {  
  127.                 de.printStackTrace();  
  128.                 System.err.println("pdf watermark font: " + de.getMessage());  
  129.             }  
  130.         }  
  131.     }  
  132.       
  133.     /** 
  134.      * 为PDF分页时创建添加图片水印的事件信息 
  135.      */  
  136.     class PictureWaterMarkPdfPageEvent extends PdfPageEventHelper{  
  137.           
  138.         private String waterMarkFullFilePath;  
  139.         private Image waterMarkImage;  
  140.           
  141.         public PictureWaterMarkPdfPageEvent(String waterMarkFullFilePath){  
  142.             this.waterMarkFullFilePath = waterMarkFullFilePath;  
  143.         }  
  144.           
  145.         public void onEndPage(PdfWriter writer, Document document) {  
  146.             try{  
  147.                 float pageWidth = document.right()+document.left();//获取pdf内容正文页面宽度  
  148.                 float pageHeight = document.top()+document.bottom();//获取pdf内容正文页面高度  
  149.                 PdfContentByte waterMarkPdfContent = writer.getDirectContentUnder();  
  150.                 //仅设置一个图片实例对象,整个PDF文档只应用一个图片对象,极大减少因为增加图片水印导致PDF文档大小增加  
  151.                 if(waterMarkImage == null){  
  152.                     waterMarkImage = Image.getInstance(waterMarkFullFilePath);  
  153.                 }  
  154.                 //添加水印图片,文档正文内容采用横向三列,竖向两列模式增加图片水印  
  155.                 waterMarkPdfContent.addImage(getWaterMarkImage(waterMarkImage,pageWidth*0.2f,pageHeight*0.1f));  
  156.                 waterMarkPdfContent.addImage(getWaterMarkImage(waterMarkImage,pageWidth*0.2f,pageHeight*0.4f));  
  157.                 waterMarkPdfContent.addImage(getWaterMarkImage(waterMarkImage,pageWidth*0.2f,pageHeight*0.7f));  
  158.                 waterMarkPdfContent.addImage(getWaterMarkImage(waterMarkImage,pageWidth*0.6f,pageHeight*0.1f));  
  159.                 waterMarkPdfContent.addImage(getWaterMarkImage(waterMarkImage,pageWidth*0.6f,pageHeight*0.4f));  
  160.                 waterMarkPdfContent.addImage(getWaterMarkImage(waterMarkImage,pageWidth*0.6f,pageHeight*0.7f));  
  161.                 PdfGState gs = new PdfGState();  
  162.                 gs.setFillOpacity(0.2f);//设置透明度为0.2  
  163.                 waterMarkPdfContent.setGState(gs);  
  164.             }catch(DocumentException de) {  
  165.                 de.printStackTrace();  
  166.                 System.err.println("pdf watermark font: " + de.getMessage());  
  167.             }catch(IOException de) {  
  168.                 de.printStackTrace();  
  169.                 System.err.println("pdf watermark font: " + de.getMessage());  
  170.             }  
  171.         }  
  172.     }  
  173.       
  174.     /** 
  175.      * 为PDF分页时创建添加header和footer信息的事件信息 
  176.      */  
  177.     class HeadFootInfoPdfPageEvent extends PdfPageEventHelper{  
  178.           
  179.         public HeadFootInfoPdfPageEvent(){  
  180.         }  
  181.           
  182.         public void onEndPage(PdfWriter writer, Document document) {  
  183.             try{  
  184.                 PdfContentByte headAndFootPdfContent = writer.getDirectContent();  
  185.                 headAndFootPdfContent.saveState();  
  186.                 headAndFootPdfContent.beginText();  
  187.                 BaseFont bfChinese = BaseFont.createFont("STSong-Light""UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);  
  188.                 headAndFootPdfContent.setFontAndSize(bfChinese, 10);  
  189.                 //文档页头信息设置  
  190.                 float x = document.top(-20);  
  191.                 //页头信息左面  
  192.                 headAndFootPdfContent.showTextAligned(PdfContentByte.ALIGN_LEFT,  
  193.                                    "数据安全产品",  
  194.                                    document.left(), x, 0);  
  195.                 //页头信息中间  
  196.                 headAndFootPdfContent.showTextAligned(PdfContentByte.ALIGN_CENTER,  
  197.                                     "第"+writer.getPageNumber()+ "页",  
  198.                                    (document.right() + document.left())/2,  
  199.                                    x, 0);  
  200.                 //页头信息右面  
  201.                 headAndFootPdfContent.showTextAligned(PdfContentByte.ALIGN_RIGHT,  
  202.                                    "杭州水果大王信息技术有限公司",  
  203.                                    document.right(), x, 0);  
  204.                 //文档页脚信息设置  
  205.                 float y = document.bottom(-20);  
  206.                 //页脚信息左面  
  207.                 headAndFootPdfContent.showTextAligned(PdfContentByte.ALIGN_LEFT,  
  208.                                    "--",  
  209.                                    document.left(), y, 0);  
  210.                 //页脚信息中间  
  211.                 headAndFootPdfContent.showTextAligned(PdfContentByte.ALIGN_CENTER,  
  212.                                     "-",  
  213.                                    (document.right() + document.left())/2,  
  214.                                    y, 0);  
  215.                 //页脚信息右面  
  216.                 headAndFootPdfContent.showTextAligned(PdfContentByte.ALIGN_RIGHT,  
  217.                                    "--",  
  218.                                    document.right(), y, 0);  
  219.                 headAndFootPdfContent.endText();  
  220.                 headAndFootPdfContent.restoreState();  
  221.             }catch(DocumentException de) {  
  222.                 de.printStackTrace();  
  223.                 System.err.println("pdf watermark font: " + de.getMessage());  
  224.             }catch(IOException de) {  
  225.                 de.printStackTrace();  
  226.                 System.err.println("pdf watermark font: " + de.getMessage());  
  227.             }  
  228.         }  
  229.     }  
  230. }  


Java代码   收藏代码
  1. package com.fruitking.testpdf.util;  
  2.   
  3. import java.awt.Color;  
  4. import java.io.File;  
  5. import java.io.FileNotFoundException;  
  6. import java.io.FileOutputStream;  
  7. import java.io.IOException;  
  8. import java.io.OutputStream;  
  9.   
  10. import com.lowagie.text.Document;  
  11. import com.lowagie.text.DocumentException;  
  12. import com.lowagie.text.Font;  
  13. import com.lowagie.text.PageSize;  
  14. import com.lowagie.text.Paragraph;  
  15. import com.lowagie.text.pdf.BaseFont;  
  16. import com.lowagie.text.pdf.PdfPCell;  
  17. import com.lowagie.text.pdf.PdfPTable;  
  18. import com.lowagie.text.pdf.PdfWriter;  
  19.   
  20. public class PdfFileExport {  
  21.       
  22.     /** 
  23.      * 从数据库中导出数据并以PDF文件形式存储 
  24.      * 列信息较多,行信息可能超过100万 
  25.      * 文档仅有只读权限,设置文档作者信息 
  26.      * 在文档页头设置公司信息版权信息 
  27.      * 添加公司的文字和图片水印信息 
  28.      * @param fullFilePath 
  29.      * @param tableContent 
  30.      * @param rowsNumber 
  31.      * @param submitAmount 
  32.      * @return 
  33.      */  
  34.     public boolean exportTableContent(String fullFilePath,String[][] tableContent,int rowsNumber,int submitAmount){  
  35.         Document pdfDocument = new Document(PageSize.A2,50,50,50,50);  
  36.         try {  
  37.             //构建一个PDF文档输出流程  
  38.             OutputStream pdfFileOutputStream = new FileOutputStream(new File(fullFilePath));  
  39.             PdfWriter pdfWriter = PdfWriter.getInstance(pdfDocument,pdfFileOutputStream);  
  40.             //设置作者信息  
  41.             PdfFileExportUtil.setCreatorInfo(pdfDocument);  
  42.             //设置文件只读权限  
  43.             PdfFileExportUtil.setReadOnlyPDFFile(pdfWriter);  
  44.             //通过PDF页面事件模式添加文字水印功能  
  45.             PdfFileExportUtil pdfFileExportUtil = new PdfFileExportUtil();  
  46.             pdfWriter.setPageEvent(pdfFileExportUtil.new TextWaterMarkPdfPageEvent("杭州水果大王信息技术"));  
  47.             //通过PDF页面事件模式添加图片水印功能  
  48.             String waterMarkFullFilePath = "D:/temp/pdftest/login_logo.gif";//水印图片  
  49.             pdfWriter.setPageEvent(pdfFileExportUtil.new PictureWaterMarkPdfPageEvent(waterMarkFullFilePath));  
  50.             //通过PDF页面事件模式添加页头和页脚信息功能  
  51.             pdfWriter.setPageEvent(pdfFileExportUtil.new HeadFootInfoPdfPageEvent());  
  52.             //设置中文字体和字体样式  
  53.             BaseFont bfChinese = BaseFont.createFont("STSong-Light""UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);    
  54.             Font f8 = new Font(bfChinese, 8, Font.NORMAL);  
  55.             //打开PDF文件流  
  56.             pdfDocument.open();  
  57.             //创建一个N列的表格控件  
  58.             PdfPTable pdfTable = new PdfPTable(tableContent[0].length);  
  59.             //设置表格占PDF文档100%宽度  
  60.             pdfTable.setWidthPercentage(100);  
  61.             //水平方向表格控件左对齐  
  62.             pdfTable.setHorizontalAlignment(PdfPTable.ALIGN_LEFT);  
  63.             //创建一个表格的表头单元格  
  64.             PdfPCell pdfTableHeaderCell = new PdfPCell();  
  65.             //设置表格的表头单元格颜色  
  66.             pdfTableHeaderCell.setBackgroundColor(new Color(21314169));  
  67.             pdfTableHeaderCell.setHorizontalAlignment(PdfPCell.ALIGN_CENTER);  
  68.             for(String tableHeaderInfo : tableContent[0]){  
  69.                 pdfTableHeaderCell.setPhrase(new Paragraph(tableHeaderInfo, f8));  
  70.                 pdfTable.addCell(pdfTableHeaderCell);  
  71.             }  
  72.             //创建一个表格的正文内容单元格  
  73.             PdfPCell pdfTableContentCell = new PdfPCell();  
  74.             pdfTableContentCell.setHorizontalAlignment(PdfPCell.ALIGN_CENTER);  
  75.             pdfTableContentCell.setVerticalAlignment(PdfPCell.ALIGN_MIDDLE);  
  76.             //表格内容行数的填充  
  77.             for(int i = 0;i < rowsNumber;i++){  
  78.                 for(String tableContentInfo : tableContent[1]){  
  79.                     pdfTableContentCell.setPhrase(new Paragraph(tableContentInfo, f8));  
  80.                     pdfTable.addCell(pdfTableContentCell);  
  81.                 }  
  82.                 //表格内容每写满某个数字的行数时,其内容一方面写入物理文件,另一方面释放内存中存留的内容。  
  83.                 if((i%submitAmount)==0){  
  84.                     pdfDocument.add(pdfTable);  
  85.                     pdfTable.deleteBodyRows();  
  86.                 }else if(i==rowsNumber){  
  87.                     //如果全部类容完毕且又没达到某个行数限制,则也要写入物理文件中。  
  88.                     pdfDocument.add(pdfTable);  
  89.                     pdfTable.deleteBodyRows();  
  90.                 }  
  91.             }  
  92.             return true;  
  93.         }catch(FileNotFoundException de) {  
  94.             de.printStackTrace();  
  95.             System.err.println("pdf file: " + de.getMessage());  
  96.             return false;  
  97.         }catch(DocumentException de) {  
  98.             de.printStackTrace();  
  99.             System.err.println("document: " + de.getMessage());  
  100.             return false;  
  101.         }catch(IOException de) {  
  102.             de.printStackTrace();  
  103.             System.err.println("pdf font: " + de.getMessage());  
  104.             return false;  
  105.         }finally{  
  106.             //关闭PDF文档流,OutputStream文件输出流也将在PDF文档流关闭方法内部关闭  
  107.             if(pdfDocument!=null){  
  108.                 pdfDocument.close();  
  109.             }  
  110.         }          
  111.     }  
  112.       
  113.     /** 
  114.      * @param args 
  115.      */  
  116.     public static void main(String[] args) {  
  117.         String[][] tableContent = new String[][]{  
  118.                 {"序号","姓名","年龄","职业","籍贯","学历","单位名称","联系电话","联系地址",  
  119.                     "语文","数学","英语","物理","化学","生物","政治","历史","地理","音乐","美术","体育","课外实践","学校名称","备注"},  
  120.                 {"1","许果","31","软件工程师","浙江杭州","大学本科","浙江水果大王信息技术有限公司","18905710571","浙江省杭州市西湖区三墩镇三墩街188号",  
  121.                         "85","95","75","90","90","85","80","90","90","75","65","75","80","石城中学","他是一名优秀的IT工程师,日常爱好旅游,运动"}  
  122.         };  
  123.         PdfFileExport pdfFileExport = new PdfFileExport();  
  124.         pdfFileExport.exportTableContent("D:/temp/pdftest/41导出PDF文档.pdf", tableContent, 1000002000);  
  125.     }  
  126.       
  127. }  

你可能感兴趣的:(java)