Heim >Web-Frontend >HTML-Tutorial >使用iText生成PDF_html/css_WEB-ITnose

使用iText生成PDF_html/css_WEB-ITnose

WBOY
WBOYOriginal
2016-06-21 08:52:192290Durchsuche

iText的更新变化很大,早期版本在PDF样式上可能会有瑕疵,所有我使用的最新的5.5.6包

1.添加Maven依赖

itext核心包 和xmlworder字体包

<dependency>      <groupId>com.itextpdf</groupId>     <artifactId>itextpdf</artifactId>     <version>5.5.6</version></dependency>
<dependency>   <groupId>com.itextpdf.tool</groupId>   <artifactId>xmlworker</artifactId>   <version>5.5.6</version></dependency>

2. 直接生成pdf的方式,非常简单,用文字创建段落等即可,设置好字体、间距、对齐方式等等即可,弄个Hello World 的例子。

package com.thunisoft.demo;  import java.io.FileOutputStream;  import com.itextpdf.text.Document;  import com.itextpdf.text.Font;  import com.itextpdf.text.FontFactoryImp;  import com.itextpdf.text.PageSize;  import com.itextpdf.text.Paragraph;  import com.itextpdf.text.pdf.BaseFont;  import com.itextpdf.text.pdf.PdfPHeaderCell;  import com.itextpdf.text.pdf.PdfPTable;  import com.itextpdf.text.pdf.PdfWriter;     /**  * 创建一个简单pdf文档,文字、表格  *  * @author zhangwd  * @version 1.0  *  */  public class PdfDemo_1 {      private static void create() throws Exception {          // 创建一个文档(默认大小A4,边距36, 36, 36, 36)          Document document = new Document();          // 设置文档大小          document.setPageSize(PageSize.A4);          // 设置边距,单位都是像素,换算大约1厘米=28.33像素          document.setMargins(50, 50, 50, 50);             // 创建writer,通过writer将文档写入磁盘          PdfWriter writer = PdfWriter.getInstance(document,newFileOutputStream("c:/demo.1.pdf"));             // demo          String title = "凉州词";          String content = "黄河远上白云间,一片孤城万仞山。羌笛何须怨杨柳,春风不度玉门关。";             // 定义字体          FontFactoryImp ffi = new FontFactoryImp();          // 注册全部默认字体目录,windows会自动找fonts文件夹的,返回值为注册到了多少字体          ffi.registerDirectories();          // 获取字体,其实不用这么麻烦,后面有简单方法          Font font = ffi.getFont("宋体",BaseFont.IDENTITY_H,BaseFont.EMBEDDED, 12, Font.UNDEFINED, null);             // 打开文档,只有打开后才能往里面加东西          document.open();             // 设置作者          document.addAuthor("王之涣");          // 设置创建者          document.addCreator("王之涣");          // 设置主题          document.addSubject("测试");          // 设置标题          document.addTitle("凉州词");             // 增加一个段落          document.add(new Paragraph(title, font));          document.add(new Paragraph(content, font));          document.add(new Paragraph("\n\r", font));             // 创建表格,5列的表格          PdfPTable table = new PdfPTable(4);          table.setTotalWidth(PageSize.A4.getWidth()- 100);          table.setLockedWidth(true);          // 创建头          PdfPHeaderCell header = new PdfPHeaderCell();          header.addElement(new Paragraph(title, font));          header.setColspan(4);          table.addCell(header);          // 添加内容          table.addCell(new Paragraph("黄河远上白云间",font));          table.addCell(new Paragraph("一片孤城万仞山",font));          table.addCell(new Paragraph("羌笛何须怨杨柳",font));          table.addCell(new Paragraph("春风不度玉门关",font));             document.add(table);          // 关闭文档,才能输出          document.close();          writer.close();     }        public static void main(String[] args) throws Exception {          create();     }  }

3.字体

我们项目文书字体比较特殊,比如用到了宋体(99%都这个吧)、华文仿宋(安装office后自带)、仿宋_GB2312等,于是就研究了一下pdf字体,网上有很多方法使用中文字体,其实5.0版以后的iText加入字体还是很方便的。

还是HelloWorld例子:

import java.io.FileOutputStream;  import com.itextpdf.text.Document;  import com.itextpdf.text.Font;  import com.itextpdf.text.FontFactoryImp;  import com.itextpdf.text.Paragraph;  import com.itextpdf.text.pdf.PdfWriter;  import com.itextpdf.tool.xml.XMLWorkerFontProvider;     /**  * 字体  *  * @author zhangwd  * @version 1.0  *  */  public class PdfDemo_2 {         public static void create() throws Exception {          Document document = new Document();          PdfWriter writer = PdfWriter.getInstance(document,new FileOutputStream("c:/demo.2.pdf"));          String title = "凉州词";          String content = "黄河远上白云间,一片孤城万仞山。羌笛何须怨杨柳,春风不度玉门关。";          document.open();          document.add(newParagraph(title, getFont("方正兰亭黑简体")));          document.add(newParagraph(content, getFont("迷你简娃娃篆")));          document.close();          writer.close();      }         private static Font getFont(String fontName) {          // xmlworker主要功能是html转pdf用的,非常好用,也是itext官方的             // 这个是xmlworker提供的获取字体方法,很方便,对中文支持很好          FontFactoryImp fp = newXMLWorkerFontProvider();          // 注册指定的字体目录,默认构造方法中会注册全部目录,我这里注册了src/font目录          fp.registerDirectory(PdfDemo_2.class.getClassLoader().getResource("font").getFile(), true);             // 最好的地方是直接支持获取中文的名字          return fp.getFont(fontName);             // 当然,最好的方法是自己继承XMLWorkerFontProvider,提供一些常用字体,简单方便      }         public static void main(String[]args) throws Exception {          create();      }  }

xmlworker的XMLWorkerFontProvider提供了很方便的获取字体方法:

1.注册一个文件夹,里面有哪些字体都可以,比如我demo中的字体

2.使用getFont(字体名)即可获得,不过字体名从哪来的呢

3.页眉页脚

iText5中并没有之前版本HeaderFooter对象设置页眉和页脚,可以利用PdfPageEvent来完成页眉页脚的设置工作。

PdfPageEvent提供了几个pdf在创建时的事件,页眉页脚就是在每页加载完写入的。

每一页加个页码还是很简单的,但是总页码就麻烦了,iText是流模式的写入内容,只有写到最后,才能知道有多少页,那么显示总页数就麻烦了,不过麻烦不代表不可能。

其实iText仅在调用释放模板方法后才将PdfTemplate写入到OutputStream中,否则对象将一直保存在内存中,直到关闭文档。

所以我们可以在最后关闭文档前,使用PdfTemplate写入总页码。可以理解成先写个占位符,然后统一替换。

还是HelloWorld例子:

<span style="font-size:18px;">package com.thunisoft.demo;  import java.io.FileOutputStream;  import com.itextpdf.text.BaseColor;  import com.itextpdf.text.Document;  import com.itextpdf.text.Element;  import com.itextpdf.text.ExceptionConverter;  import com.itextpdf.text.Font;  import com.itextpdf.text.Image;  import com.itextpdf.text.PageSize;  import com.itextpdf.text.Paragraph;  import com.itextpdf.text.Rectangle;  import com.itextpdf.text.pdf.ColumnText;  import com.itextpdf.text.pdf.PdfContentByte;  import com.itextpdf.text.pdf.PdfPCell;  import com.itextpdf.text.pdf.PdfPTable;  import com.itextpdf.text.pdf.PdfPageEventHelper;  import com.itextpdf.text.pdf.PdfTemplate;  import com.itextpdf.text.pdf.PdfWriter;  import com.itextpdf.tool.xml.XMLWorkerFontProvider;     /**  * 页眉、页脚  *  * @author zhangwd  * @version 1.0  *  */  public class PdfDemo_3 {         public static void create() throws Exception {          Document document = newDocument(PageSize.A4, 50, 50, 50, 50);          PdfWriter writer = PdfWriter.getInstance(document,new FileOutputStream("c:/demo.3.pdf"));             // 增加页眉页脚          writer.setPageEvent(newMyHeaderFooter());             String title = "凉州词";          String content = "黄河远上白云间,一片孤城万仞山。羌笛何须怨杨柳,春风不度玉门关。";          document.open();             Font font = newXMLWorkerFontProvider().getFont("宋体");          for (int i = 0; i <100; i++) {              document.add(newParagraph(title, font));              document.add(new Paragraph(content,font));              document.add(new Paragraph("\n"));          }          document.close();          writer.close();      }         public static void main(String[]args) throws Exception {          create();      }  }     /**  * iText5中并没有之前版本HeaderFooter对象设置页眉和页脚<br>  * 不过,可以利用PdfPageEventHelper来完成页眉页脚的设置工作。<br>  * 就是在页面完成但写入内容之前触发事件,插入页眉、页脚、水印等。<br>  *  * @author zhangwd  * @version 1.0  *  */  class MyHeaderFooter extends PdfPageEventHelper {         Font font = new XMLWorkerFontProvider().getFont("宋体", 12, BaseColor.RED);         // 总页数      PdfTemplate totalPage;         // 打开文档时,创建一个总页数的模版      public void onOpenDocument(PdfWriter writer,Document document) {          PdfContentByte cb =writer.getDirectContent();          totalPage = cb.createTemplate(30, 16);      }         // 一页加载完成触发,写入页眉和页脚      public void onEndPage(PdfWriter writer, Documentdocument) {          PdfPTable table = new PdfPTable(3);          try {              table.setTotalWidth(PageSize.A4.getWidth() - 100);              table.setWidths(new int[] { 24, 24, 3});              table.setLockedWidth(true);             table.getDefaultCell().setFixedHeight(-10);             table.getDefaultCell().setBorder(Rectangle.BOTTOM);                 table.addCell(new Paragraph("我是文字", font));// 可以直接使用addCell(str),不过不能指定字体,中文无法显示             table.getDefaultCell().setHorizontalAlignment(Element.ALIGN_RIGHT);              table.addCell(new Paragraph("第" + writer.getPageNumber() + "页/", font));                 // 总页数              PdfPCell cell = new PdfPCell(Image.getInstance(totalPage));              cell.setBorder(Rectangle.BOTTOM);              table.addCell(cell);                 // 将页眉写到document中,位置可以指定,指定到下面就是页脚              table.writeSelectedRows(0, -1, 50,PageSize.A4.getHeight() - 20, writer.getDirectContent());          } catch (Exception de) {              throw new ExceptionConverter(de);          }      }         // 全部完成后,将总页数的pdf模版写到指定位置      public void onCloseDocument(PdfWriter writer,Document document) {          String text = "总" + (writer.getPageNumber() - 1) + "页";          ColumnText.showTextAligned(totalPage, Element.ALIGN_LEFT, new Paragraph(text,font), 2, 2, 0);      }  }

4.html转pdf

结果还不错,虽然可以满足我们的要求,但是比较复杂,动态创建一个个的表格和内容过于繁琐,方法太粗暴了,用户的文档内容或格式变化,就要修改程序了。

于是,我们讨论了一个比较好的方法,感谢谢国庆提供的帮助。

先创建html,然后转换成pdf,demo如下:

import java.io.ByteArrayInputStream;  import java.io.FileOutputStream;  import java.io.InputStream;  import java.io.OutputStream;  import com.itextpdf.text.Document;  import com.itextpdf.text.pdf.PdfWriter;  import com.itextpdf.tool.xml.XMLWorkerHelper;     /**  * html转pdf  *  * @author zhangwd  * @version 1.0  *  */  public class PdfDemo_4 {         public static void create() throws Exception {             // html中字体非常郁闷          // 1. html中不指定字体,则默认使用英文字体,中文会不显示。          // 2. html中指定的字体必须是英文名称,如宋体:font-family:SimSun;          // 3. html中不能指定自定义字体,必须指定itext支持的字体,还好itext支持字体比较多,常见操作系统带的都支持          // 4. 暂没有找到如何html中支持自定义字体方法,网上都是修改源码实现默认字体中文,也很重要             StringBuilder html = newStringBuilder();          html.append("<html>");          html.append("<bodystyle='font-size:20px;font-family:SimSun;'>");          html.append("<table width='19cm'border='1' cellpadding='0' cellspacing='0'>");          html.append("<tr>");          html.append("<td colspan='2'>凉州词</td>");          html.append("</tr>");          html.append("<tr>");          html.append("<td>黄河远上白云间,一片孤城万仞山。</td>");          html.append("<td>羌笛何须怨杨柳,春风不度玉门关。</td>");          html.append("</tr>");          html.append("</table>");          html.append("</body>");          html.append("</html>");             InputStream is = newByteArrayInputStream(html.toString().getBytes());             OutputStream os = newFileOutputStream("c:/demo.4.pdf");          Document document = new Document();             PdfWriter writer = PdfWriter.getInstance(document,os);             document.open();             // 将html转pdf          XMLWorkerHelper.getInstance().parseXHtml(writer,document, is);             document.close();      }         public static void main(String[]args) throws Exception {          create();      }  }</span>

此处使用了XmlWorker,XmlWorker也是iText官方的,目前和iText版本一起更新,可以讲XHTML转换成pdf,支持大部分样式和标签,是大部分哦,不是全部。

目前我们就用的这个方式,写好html文档,使用时动态替换html中的标记位,然后生成pdf。

使用XHTML转pdf要注意的地方:

1. html中不指定字体,则默认使用英文字体,中文会不显示;网上很多人通过修改源码来实现默认字体中文,也是一种方式,不过感觉比较麻烦。

2. html中指定的字体必须是英文名称;

如宋体:font-family:SimSun;正确

font-family:宋体;则错误,竟然unicode也不行。

3. html中不能指定自定义字体(比如上文中的方正兰亭黑),必须指定iText支持的字体,还好iText支持字体比较多,常见操作系统带的都支持

4. 暂没有找到如何html中支持自定义字体方法。

5. pdf中添加图片也非常简单,例如: 使用iText生成PDF_html/css_WEB-ITnose ,就可以了。

6. XHTML不是HTML,所以任何标签都要完整结束,比如
错误,必须
才行。

5.其他

我们项目的文书目前20多种,以后还会大量增加,不过写一个html模版很简单,需要对html和css熟练,调生成的样式部分比较麻烦(比如文字多了会切掉,不切会影响整体样式,表格线有粗有细,xmlworker不支持全部css等),一般A4纸都是厘米单位的,html中最好也使用厘米,处理简单点。

比如我们的部分模版

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn