使用xmlworker通过html生成pdf

jopen 10年前

xmlworker是一个基于iText的xml生成pdf工具。使用xmlworker是非常简单的,思路也很清晰。获取模板->拼装模板->生成pdf。但是在过程中还是有一些小问题需要注意下。

首先中文的问题,xmlworker默认是不支持中文的,需要修改源代码重新打包才能支持亚洲字体(修改详情)。

也可以下载我已经编好的包:
修改com.itextpdf.tool.xml.css.apply.ChunkCssApplier.java 中的 public Chunk apply(final Chunk c, final Tag t) :

Font f = applyFontStyles(t);      // for chinese charater display @www.micmiu.com      if (null != HTMLUtils.bfCN && HTMLUtils.isChinese(c.getContent())) {           f = new Font(HTMLUtils.bfCN, f.getSize(), f.getStyle(),                     f.getColor());      }      float size = f.getSize();      ......


在com.itextpdf.tool.xml.html.HTMLUtils.java 中 增加下面用于中文字符判断的方法:

public static BaseFont bfCN = null;   static {        try {             bfCN = BaseFont.createFont("STSongStd-Light", "UniGB-UCS2-H",                       BaseFont.NOT_EMBEDDED);        } catch (Exception e) {        }   }     // add by Michael more see:http://www.micmiu.com   private static final boolean isChinese(char c) {        Character.UnicodeBlock ub = Character.UnicodeBlock.of(c);        if (ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS                  || ub == Character.UnicodeBlock.CJK_COMPATIBILITY_IDEOGRAPHS                  || ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS_EXTENSION_A                  || ub == Character.UnicodeBlock.GENERAL_PUNCTUATION                  || ub == Character.UnicodeBlock.CJK_SYMBOLS_AND_PUNCTUATION                  || ub == Character.UnicodeBlock.HALFWIDTH_AND_FULLWIDTH_FORMS) {             return true;        }        return false;   }   // add by Michael more see:http://www.micmiu.com   public static final boolean isChinese(String strName) {        char[] ch = strName.toCharArray();        for (int i = 0; i < ch.length; i++) {             char c = ch[i];             if (isChinese(c)) {                  return true;             }        }        return false;   }

其次,在写html模板的时候,因为xmlworker支持的CSS样式极少,所以模板内容要尽量简单。对于DOCTYPE和html标签的约束页比较严格。我使用的是

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <br /><html xmlns="http://www.w3.org/1999/xhtml">

对于一个标签中含有中文、数字或英文的时候,很可能会出现字体变形。这是因为xmlworker在渲染PDF的时候是以html的标签为单位的。只要把中文和英文分隔在不同的标签就可以了。
最后贴上代码作为参考:

import java.io.File;      import java.io.FileOutputStream;      import java.io.IOException;      import java.io.StringReader;      import java.io.StringWriter;      import java.io.Writer;      import java.util.ArrayList;      import java.util.HashMap;      import java.util.List;      import java.util.Map;        import com.itextpdf.text.Document;      import com.itextpdf.text.DocumentException;      import com.itextpdf.text.pdf.PdfWriter;      import com.itextpdf.tool.xml.XMLWorkerHelper;        import freemarker.template.Configuration;      import freemarker.template.DefaultObjectWrapper;      import freemarker.template.Template;      import freemarker.template.TemplateException;        public class PDFOperation {      public static void main(String[] args) throws Exception {      // 获取html内容      String html = getHtml();      // 生成pdf      createPdf(html);      }    public static void createPdf(String html)throws DocumentException, IOException {      String file = "E:/detail.pdf";      Document doc = new Document();      PdfWriter writer = PdfWriter.getInstance(doc, new FileOutputStream(file));      doc.open();      XMLWorkerHelper.getInstance().parseXHtml(writer, doc,new StringReader(html));      doc.close();  }  public static String getHtml() throws IOException, TemplateException{      Configuration cfg = new Configuration();      cfg.setDirectoryForTemplateLoading(new File("E:/templatedir/"));      cfg.setObjectWrapper(new DefaultObjectWrapper());      Template t = cfg.getTemplate("test.ftl");      Writer out = new StringWriter();      Map<String,Object> map = new HashMap<String,Object>();      Map<String,Object> req = new HashMap<String,Object>();      req.put("title","XXX");      req.put("ctime", "2014.05.06");      req.put("province", "北京");      List<Map<String,Object>> files = new ArrayList<Map<String,Object>>();      Map<String,Object> file1 = new HashMap<String,Object>();      Map<String,Object> file2 = new HashMap<String,Object>();      file1.put("url", "http://xxx");      file1.put("name", "FreeMarker_Manual_zh_CN.pdf");              file1.put("ext","pdf");      file2.put("url", "http://xxx");      file2.put("name", "OReilly.Redis.Cookbook.2011.pdf");              file2.put("ext","pdf");      files.add(file1);      files.add(file2);      map.put("files", files);      map.put("map",map);      t.process(map, out);      out.flush();      return out.toString();          }      }

ftl模板:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">      <html xmlns="http://www.w3.org/1999/xhtml">      <head>      <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />      <title></title>      </head>        <body>      <div style="padding:24px;color:#3a3b3b;background-color:#f4f4f4;line-height:28px;">          <h1 style="color:#98989a;font-weight:bold;font-size:24px;text-align:left;">DO NOT COPY</h1>          <div style="background-color:#fff;padding:16px 24px;width:100%;">              <h1>${map.title}</h1>              <h5 style="color:#848484;font-weight:normal;"><span>${map.ctime}</span></h5>          </div>          <div style="height:24px;">&nbsp;</div>          <div style="background-color:#fff;padding:16px 24px;width:100%;">              <h2>说明</h2>              <div style="height:1px;background-color:#dbdbdb;width:100%;">&nbsp;</div>              <div style="height:16px;width:100%;">&nbsp;</div>              <ul style="list-style-type:none;margin:0px;padding:0px;padding-left:24px;">                  <li><span style="color:#848484">地区</span> ${map.province?default('')}</li>              </ul>              <#if files ?exists >              <h3>附件</h3>              <div style="background-color:#eee;width:100%;padding:12px;padding-left:24px;">文件</div>              <ul style="list-style-type:none;margin:0px;padding:0px;padding-left:24px;padding-right:24px;">                  <#list files as f>                  <li style="margin:0px;padding:12px 0;"><a href="${f.url}"><span>${f.name}</span>.<span>${f.ext}</span></a></li>                  </#list>              </ul>              </#if>          </div>          <h1 style="color:#98989a;font-weight:bold;font-size:24px;text-align:right;">DO NOT COPY</h1>      </div>      </body>      </html>

以下是可能提供帮助的资料:

支持的样式表

HTML生成PDF在线测试

itextpdf in action中的源码样例

编译好的xmlworker-cn.jar,如果使用maven并有私库,需要部署到私库。version=5.5.0,groupId=com.itextpdf.tool,artifactId=xmlworker-cn