前言
项目中有个需求,需要将合同内容导出成pdf。上网查阅到了 iText , iText 是一个生成PDF文档的开源Java库,能够动态的从XML或者数据库生成PDF,同时还可以对文档进行加密,权限控制,并且还支持Java/C#等,但是iText本身提供的HTML解析器还是不够强大,许多HTML标签和属性无法识别,更悲催的是简单的CSS它不认识,排版调整样式让人头大。那么有没有什么方式能够支持css呢,又查阅到了 flying-saucer, flying-saucer也是导出PDF的一种解决方案,并且是基于iText的开源API,并且实现了CSS解析器,能够很好的支持CSS2.1,以及少量的CSS。最终解决方案定为: flying-saucer + iText + Freemarker。
具体实现
流程如下
pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ???xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> ???<modelVersion>4.0.0</modelVersion> ???<groupId>com.yzb.lee</groupId> ???<artifactId>itextpdf</artifactId> ???<packaging>war</packaging> ???<version>0.0.1-SNAPSHOT</version> ???<name>itextpdf Maven Webapp</name> ???<url>http://maven.apache.org</url> ???<dependencies> ???????<dependency> ???????????<groupId>org.freemarker</groupId> ???????????<artifactId>freemarker</artifactId> ???????????<version>2.3.20</version> ???????</dependency> ???????<dependency> ???????????<groupId>com.itextpdf.tool</groupId> ???????????<artifactId>xmlworker</artifactId> ???????????<version>5.5.1</version> ???????</dependency> ???????????????<!-- 支持中文 --> ???????<dependency> ???????????<groupId>com.itextpdf</groupId> ???????????<artifactId>itext-asian</artifactId> ???????????<version>5.2.0</version> ???????</dependency> ???????<!-- 支持css样式渲染 --> ???????<dependency> ???????????<groupId>org.xhtmlrenderer</groupId> ???????????<artifactId>flying-saucer-pdf-itext5</artifactId> ???????????<version>9.0.3</version> ???????</dependency> ???????<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-lang3 --> ???????<dependency> ???????????<groupId>org.apache.commons</groupId> ???????????<artifactId>commons-lang3</artifactId> ???????????<version>3.4</version> ???????</dependency> ???????<dependency> ???????????<groupId>junit</groupId> ???????????<artifactId>junit</artifactId> ???????????<version>4.12</version> ???????????<scope>test</scope> ???????</dependency> ???</dependencies> ???<build> ???????<finalName>itextpdf</finalName> ???</build></project>
1、html内容的输出
模版文件fileTemplate.html
<html><head><title>${title}</title><!-- link链接应该写文件服务器地址, 出于演示,这里用的localhost --><link type="text/css" rel="stylesheet" href="http://localhost:8080/itextpdf/css/pdf.css" /><style>@page { ???size: 8.5in 11in; ???@ ???bottom-center ???{ ???????content ???????: ???????"page " ???????counter( ???????page ???????) ???????" of ?" ???????counter( ???????pages ???????); ???}}</style></head><body> ???<h1>Just a blank page.</h1> ???<div style="page-break-before: always;"> ???????<div align="center"> ???????????<h1>${title}</h1> ???????????<!-- src链接应该写文件服务器地址, 出于演示,这里用的localhost --> ???????????<img alt="加载中..." src="http://localhost:8080/itextpdf/images/aloner.jpg" /> ???????</div> ???????<table> ???????????<tr> ???????????????<td><b>Name</b></td> ???????????????<td><b>Age</b></td> ???????????????<td><b>Sex</b></td> ???????????</tr> ???????????<#list userList as user> ???????????<tr> ???????????????<td>${user.name}</td> ???????????????<td>${user.age}</td> ???????????????<td><#if user.sex = 1> male <#else> female </#if></td> ???????????</tr> ???????????</#list> ???????</table> ???</div> ???<div> ???????<a href="https://www.baidu.com/" target="_blank">百度</a> ???</div></body></html>
动态数据的获取
???public Map<String, Object> getContent() throws IOException { ???????// 从数据库中获取数据, 出于演示目的, 这里数据不从数据库获取, 而是直接写死 ???????????????Map<String, Object> variables = new HashMap<String, Object>(3); ???????List<User> userList = new ArrayList<User>(); ???????User tom = new User("Tom", 19, 1); ???????User amy = new User("Amy", 28, 0); ???????User leo = new User("Leo", 23, 1); ???????userList.add(tom); ???????userList.add(amy); ???????userList.add(leo); ???????variables.put("title", "用户列表"); ???????variables.put("userList", userList); ???????????????return variables; ???}
动态数据的绑定,html内容的输出
/** ????* Generate html string. ????* ?????* @param template ????* ???????????the name of freemarker teamlate. ????* @param variables ????* ???????????the data of teamlate. ????* @return htmlStr ????* @throws Exception ????*/ ???public static String generate(String template, Map<String, Object> variables) ???????????throws Exception { ???????Configuration config = FreemarkerConfiguration.getConfiguation(); ???????Template tp = config.getTemplate(template); ???????StringWriter stringWriter = new StringWriter(); ???????BufferedWriter writer = new BufferedWriter(stringWriter); ???????tp.setEncoding("UTF-8"); ???????tp.process(variables, writer); ???????String htmlStr = stringWriter.toString(); ???????writer.flush(); ???????writer.close(); ???????return htmlStr; ???}
2、pdf的导出
???private void generatePdf(String htmlStr, OutputStream out) ???????????throws IOException, DocumentException { ???????//final ServletContext servletContext = getServletContext(); ???????Document document = new Document(PageSize.A4, 30, 30, 30, 30); ???????document.setMargins(30, 30, 30, 30); ???????PdfWriter writer = PdfWriter.getInstance(document, out); ???????document.open(); ???????// html内容解析 ???????HtmlPipelineContext htmlContext = new HtmlPipelineContext( ???????????????new CssAppliersImpl(new XMLWorkerFontProvider() { ???????????????????@Override ???????????????????public Font getFont(String fontname, String encoding, ???????????????????????????float size, final int style) { ???????????????????????Font font = null; ???????????????????????if (fontname == null) { ???????????????????????????//字体 ?????????????????????????????String fontCn = getChineseFont(); ?????????????????????????????BaseFont bf; ???????????????????????????try { ???????????????????????????????//注意这里有一个,1 ????????????????????????????????bf = BaseFont.createFont(fontCn+",1", ?????????????????????????????????????????BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED); ???????????????????????????????font = new Font(bf, size, style); ???????????????????????????} catch (DocumentException e) { ???????????????????????????????e.printStackTrace(); ???????????????????????????} catch (IOException e) { ???????????????????????????????e.printStackTrace(); ???????????????????????????} ?????????????????????????????????????????????????????} ???????????????????????return font; ???????????????????} ???????????????})) { ???????????@Override ???????????public HtmlPipelineContext clone() ???????????????????throws CloneNotSupportedException { ???????????????HtmlPipelineContext context = super.clone(); ???????????????try { ???????????????????ImageProvider imageProvider = this.getImageProvider(); ???????????????????context.setImageProvider(imageProvider); ???????????????} catch (NoImageProviderException e) { ???????????????} ???????????????return context; ???????????} ???????}; ???????// 图片解析 ???????htmlContext.setImageProvider(new AbstractImageProvider() { ???????????// String rootPath = servletContext.getRealPath("/"); ???????????@Override ???????????public String getImageRootPath() { ???????????????return ""; ???????????} ???????????@Override ???????????public Image retrieve(String src) { ???????????????if (StringUtils.isEmpty(src)) { ???????????????????return null; ???????????????} ???????????????try { ???????????????????// String imageFilePath = new File(rootPath, src).toURI().toString(); ???????????????????Image image = Image.getInstance(src); ???????????????????image.setAbsolutePosition(400, 400); ???????????????????if (image != null) { ???????????????????????store(src, image); ???????????????????????return image; ???????????????????} ???????????????} catch (Throwable e) { ???????????????????e.printStackTrace(); ???????????????} ???????????????return super.retrieve(src); ???????????} ???????}); ???????htmlContext.setAcceptUnknown(true).autoBookmark(true) ???????????????.setTagFactory(Tags.getHtmlTagProcessorFactory()); ???????// css解析 ???????CSSResolver cssResolver = XMLWorkerHelper.getInstance() ???????????????.getDefaultCssResolver(true); ???????cssResolver.setFileRetrieve(new FileRetrieve() { ???????????@Override ???????????public void processFromStream(InputStream in, ???????????????????ReadingProcessor processor) throws IOException { ???????????????try (InputStreamReader reader = new InputStreamReader(in, ???????????????????????CHARSET_NAME)) { ???????????????????int i = -1; ???????????????????while (-1 != (i = reader.read())) { ???????????????????????processor.process(i); ???????????????????} ???????????????} catch (Throwable e) { ???????????????} ???????????} ???????????// 解析href ???????????@Override ???????????public void processFromHref(String href, ReadingProcessor processor) ???????????????????throws IOException { ???????????????// InputStream is = servletContext.getResourceAsStream(href); ???????????????URL url = new URL(href); ???????????????HttpURLConnection conn = (HttpURLConnection) url.openConnection(); ???????????????conn.setRequestMethod("GET"); ???????????????conn.setConnectTimeout(5 * 1000); ???????????????InputStream is = conn.getInputStream(); ???????????????try (InputStreamReader reader = new InputStreamReader(is, ???????????????????????CHARSET_NAME)) { ???????????????????int i = -1; ???????????????????while (-1 != (i = reader.read())) { ???????????????????????processor.process(i); ???????????????????} ???????????????} catch (Throwable e) { ???????????????????e.printStackTrace(); ???????????????} ???????????} ???????}); ???????HtmlPipeline htmlPipeline = new HtmlPipeline(htmlContext, ???????????????new PdfWriterPipeline(document, writer)); ???????Pipeline<?> pipeline = new CssResolverPipeline(cssResolver, ???????????????htmlPipeline); ???????XMLWorker worker = null; ???????worker = new XMLWorker(pipeline, true); ???????XMLParser parser = new XMLParser(true, worker, ???????????????Charset.forName(CHARSET_NAME)); ???????try (InputStream inputStream = new ByteArrayInputStream( ???????????????htmlStr.getBytes())) { ???????????parser.parse(inputStream, Charset.forName(CHARSET_NAME)); ???????} ???????document.close(); ???}
3、生成的pdf
1508383793597.pdf
注意点
1、博客中的代码不是一个完整工程,只依赖博客中的代码是运行不起来的;
2、文件路径的获取,本地文件与远程文件的获取是有区别的, 另外本地文件的获取又存在多种方式;
3、完整工程地址:itextpdf,仔细阅读readme.txt, 工程中存在多个版本, 而本博客对应的是版本4;
4、推荐将SIMSUN.TTC放到工程中, 这就不依赖操作系统了, 可移植性更强;
参考
获取java项目根目录
freemarker+Flying sauser +Itext 整合生成PDF
flying-saucer + iText + ?Freemarker实现pdf的导出, 支持中文、css以及图片
原文地址:http://www.cnblogs.com/youzhibing/p/7692366.html