[关闭]
@HUST-SuWB 2016-01-21T09:16:28.000000Z 字数 6303 阅读 661

基于iText的PDF导出功能的实现

项目实战


背景

由于项目开发中的一个导出的需求,去调研了下对于大量文字、图片和表格的数据,怎样最简单的导出成PDF文件。经过短暂的调研,很快就确定下了使用iText。原因也很简单,iText的官网做得很好,相关的资料也很多,实在不行,还有本《iText in action》的书。挂一下这本书的PDF版本,大家有钱还是去支持正版图书吧。
其实,把官网上的例子看完了基本上常见的需求都能解决了。我简单的列了一下,我需要的功能大概包括:段落文本的添加、表格的绘制、图片的绘制、简单的排版、中文字符的显示等。实际的开发下来,感觉对于图表的某些操作算是费了点时间,这个就大家自己去感受了。

代码清单

  1. package tool;
  2. import java.io.FileOutputStream;
  3. import java.io.IOException;
  4. import java.net.MalformedURLException;
  5. import java.text.SimpleDateFormat;
  6. import java.util.ArrayList;
  7. import java.util.Date;
  8. import java.util.List;
  9. import com.itextpdf.text.BaseColor;
  10. import com.itextpdf.text.Document;
  11. import com.itextpdf.text.DocumentException;
  12. import com.itextpdf.text.Element;
  13. import com.itextpdf.text.Font;
  14. import com.itextpdf.text.FontFactory;
  15. import com.itextpdf.text.Image;
  16. import com.itextpdf.text.PageSize;
  17. import com.itextpdf.text.Paragraph;
  18. import com.itextpdf.text.Phrase;
  19. import com.itextpdf.text.pdf.BaseFont;
  20. import com.itextpdf.text.pdf.ColumnText;
  21. import com.itextpdf.text.pdf.PdfContentByte;
  22. import com.itextpdf.text.pdf.PdfPCell;
  23. import com.itextpdf.text.pdf.PdfPTable;
  24. import com.itextpdf.text.pdf.PdfTemplate;
  25. import com.itextpdf.text.pdf.PdfWriter;
  26. /**
  27. * pdf操作类
  28. * @author suwb
  29. * @since 2016-01-20
  30. *
  31. */
  32. public class PDFTool {
  33. private Font font;
  34. private BaseFont bfChinese;
  35. private Document doc;
  36. private static final String RESULT = "src/source/pdf/";
  37. private static final float TABLEPERCENT = 97;
  38. public PDFTool() throws Exception {
  39. // 设置中文字体
  40. bfChinese = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);
  41. font = new Font(bfChinese);
  42. font.setSize(15);
  43. font.setStyle(FontFactory.HELVETICA);
  44. // font.setStyle(Font.BOLD);//加粗
  45. font.setColor(new BaseColor(0,0,0));
  46. }
  47. public void helloWorld() throws DocumentException, IOException {
  48. doc = new Document(PageSize.A4, 20, 20, 20, 20);
  49. PdfWriter writer = PdfWriter.getInstance(doc, new FileOutputStream(RESULT +
  50. new SimpleDateFormat("HHmmss").format(new Date()) + "_中文.pdf"));
  51. //http://developers.itextpdf.com/question/how-add-text-image
  52. writer.setStrictImageSequence(true);
  53. doc.open();
  54. PdfContentByte cb = writer.getDirectContent();
  55. addText("中国人");
  56. addText("韩国人");
  57. List<Object[]> contents = new ArrayList<Object[]>();
  58. for(int i=0;i<2;i++){
  59. Object[] object = new Object[2];
  60. object[0] = "华科" + i;
  61. object[1] = "武大" + i;
  62. contents.add(object);
  63. }
  64. addImage((RESULT + "1.png"), cb, null);
  65. addText("中国人");
  66. addTable(new String[]{"序号一", "序号二"}, contents);
  67. addText("韩国人");
  68. addImage((RESULT + "2.png"), cb, null);
  69. addText("韩国人");
  70. doc.close();
  71. }
  72. /**
  73. * 编辑标题
  74. * @param text 段落的文本内容
  75. * @param fontSize 标题的字体大小
  76. * @throws DocumentException
  77. */
  78. public void addTitle(String title, int fontSize)
  79. throws DocumentException{
  80. Font titleFont = new Font(font);
  81. titleFont.setSize(fontSize);
  82. Paragraph content = new Paragraph(title, titleFont);
  83. content.setAlignment(Element.ALIGN_CENTER);
  84. doc.add(content);
  85. }
  86. /**
  87. * 编辑文本
  88. * @param text 段落的文本内容
  89. * @throws DocumentException
  90. */
  91. public void addText(String text)
  92. throws DocumentException{
  93. Paragraph content = new Paragraph(text, font);
  94. content.setAlignment(Element.ALIGN_LEFT);
  95. doc.add(content);
  96. }
  97. /**
  98. * 自定义文本大小的编辑文本
  99. * @param text 段落的文本内容
  100. * @param fontSize 字体大小
  101. * @throws DocumentException
  102. */
  103. public void addText(String text, int fontSize)
  104. throws DocumentException{
  105. Font textFont = new Font(font);
  106. textFont.setSize(fontSize);
  107. Paragraph content = new Paragraph(text, textFont);
  108. content.setAlignment(Element.ALIGN_LEFT);
  109. doc.add(content);
  110. }
  111. /**
  112. * 编辑表格
  113. * @param header 表格头
  114. * @param contents 表格内容
  115. * @throws DocumentException
  116. */
  117. public void addTable(Object[] header, List<Object[]> contents)
  118. throws DocumentException{
  119. PdfPTable table = new PdfPTable(header.length);
  120. table.setWidthPercentage(TABLEPERCENT);
  121. for(int i = 0; i < header.length; i++){
  122. Paragraph paragraph = new Paragraph(header[i].toString(), font);
  123. paragraph.setAlignment(Element.ALIGN_CENTER);
  124. paragraph.setLeading(0, 1);
  125. PdfPCell cell = new PdfPCell();
  126. cell.addElement(paragraph);
  127. table.addCell(cell);
  128. }
  129. for(int j = 0; j < header.length*contents.size(); j++){
  130. int m = j/(header.length);//行
  131. int n = j%(header.length);//列
  132. String content = contents.get(m)[n]!=null?contents.get(m)[n].toString():"";
  133. Paragraph paragraph = new Paragraph(content, font);
  134. paragraph.setAlignment(Element.ALIGN_CENTER);
  135. paragraph.setLeading(0, 1);
  136. PdfPCell cell = new PdfPCell();
  137. cell.addElement(paragraph);
  138. table.addCell(cell);
  139. }
  140. doc.add(table);
  141. }
  142. /**
  143. * 编辑图片+水印
  144. * @param imagePath 图片的路径
  145. * @param cb PdfContentByte包含用户定位文本和图形内容页面对象
  146. * @param watermark 水印[默认可设置为null]
  147. * @throws IOException
  148. * @throws MalformedURLException
  149. * @throws DocumentException
  150. */
  151. public void addImage(String imagePath, PdfContentByte cb, String watermark)
  152. throws MalformedURLException, IOException, DocumentException{
  153. if(watermark!=null){
  154. PdfPTable table = new PdfPTable(1);
  155. table.addCell(getWatermarkedImage(cb, Image.getInstance(imagePath), watermark));
  156. doc.add(table);
  157. }else {
  158. Image img = Image.getInstance(imagePath);
  159. float height = img.getHeight();
  160. float width = img.getWidth();
  161. int percent = getPercent2(height, width);
  162. img.setAlignment(Image.MIDDLE);
  163. img.scalePercent(percent + 3);
  164. doc.add(img);
  165. }
  166. }
  167. /**
  168. * 添加空行
  169. * @param number 空行数
  170. * @throws DocumentException
  171. */
  172. public void addBlankLine(int number) throws DocumentException{
  173. Paragraph blankLine = new Paragraph("\n");
  174. for(int i=0; i<number; i++){
  175. doc.add(blankLine);
  176. }
  177. }
  178. /**
  179. * 第一种解决方案 在不改变图片形状的同时,判断,如果h>w,则按h压缩,否则在w>h或w=h的情况下,按宽度压缩。
  180. * @param h
  181. * @param w
  182. * @return
  183. */
  184. public int getPercent(float h, float w) {
  185. int p = 0;
  186. float p2 = 0.0f;
  187. if (h > w) {
  188. p2 = 297 / h * 100;
  189. } else {
  190. p2 = 210 / w * 100;
  191. }
  192. p = Math.round(p2);
  193. return p;
  194. }
  195. /**
  196. * 第二种解决方案,统一按照宽度压缩。这样来的效果是,所有图片的宽度是相等的。
  197. * @param args
  198. */
  199. public int getPercent2(float h, float w) {
  200. int p = 0;
  201. float p2 = 0.0f;
  202. p2 = 530 / w * 100;
  203. p = Math.round(p2);
  204. return p;
  205. }
  206. public Image getWatermarkedImage(PdfContentByte cb, Image img, String watermark) throws DocumentException {
  207. float width = img.getScaledWidth();
  208. float height = img.getScaledHeight();
  209. PdfTemplate template = cb.createTemplate(width, height);
  210. template.addImage(img, width, 0, 0, height, 0, 0);
  211. ColumnText.showTextAligned(template, Element.ALIGN_CENTER,
  212. new Phrase(watermark, font), width / 2, height / 2, 30);
  213. return Image.getInstance(template);
  214. }
  215. }

解释&参考

首先给我最直接参考的资料来源是网上的一片博客,这篇博客基本上把基础的问题都解决了,但是有一些他没有说清楚的点,比如实际上官方的iText的jar包是不支持中文的,解决方式常用的就是去下载一个中文扩展包,具体可见这篇博客,但是我后续找到了一个更简便的方式,网上直接有人提供扩展了中文之后的iText包,最终我采用的就是这个jar包解决中文问题。
然后,针对实际操作中遇到的关于表格和图片的问题,直接参考了官网的图片资料表格资料。如果对表格有更加深入的需求,比如各种不规则的排版,则可以好好看看这一篇:http://developers.itextpdf.com/content/zugferd-future-invoicing/5-creating-pdf-invoices-basic-profile
另外,如果最终的排版中碰到了乱序的问题,可以参考官网上的这篇Q&A,重点在于

  1. writer.setStrictImageSequence(true);

最后,如果是用于常见的B/S系统中,那么你会需要看看如何在web开发中使用这种pdf的导出功能,这里,很可能你会碰到一些坑,这里只能帮你们填两个坑,参考如下:
http://bbs.csdn.net/topics/380054889
http://www.360doc.com/content/12/0612/13/1967709_217660151.shtml

最后

给图片加上水印后图片的大小变得不可控了,导致排版上非常的难看。这个功能由于时间问题,暂时还没有继续研究如何解决,只能后续在研究了。

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注