Preface
There is a requirement in the project to export the contract content to pdf. IText is an open source Java library for generating PDF documents. It can dynamically generate PDF from XML or database. It can also encrypt documents, control privileges, and support Java/C# etc. But the HTML parser provided by iText itself is not strong enough. Many HTML tags and attributes can not be recognized. More sadly, simple CSS does not recognize it. Understanding, typesetting adjustment style makes people head-on. Is there any way to support css? Also refer to flying-saucer, flying-saucer is also a solution to export PDF, and is based on iText open source API, and the implementation of CSS parser, can support CSS 2.1, as well as a small number of CSS. The final solution is flying-saucer + iText + Freemarker.
Concrete realization
The process is as follows
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> <!-- Support Chinese --> <dependency> <groupId>com.itextpdf</groupId> <artifactId>itext-asian</artifactId> <version>5.2.0</version> </dependency> <!-- Support css Style rendering --> <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. Output of html content
Template.html template file
<html> <head> <title>${title}</title> <!-- link Links should write file server addresses. For demonstration purposes, the 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 Links should write file server addresses. For demonstration purposes, the localhost --> <img alt="Loading..." 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">Baidu</a> </div> </body> </html>
Dynamic data acquisition
public Map<String, Object> getContent() throws IOException { // Get data from the database. For demonstration purposes, the data is not obtained from the database, but written to death directly. 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", "User list"); variables.put("userList", userList); return variables; }
Dynamic data binding, html content output
/** * 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. Export of 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 Content analysis 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) { //Typeface String fontCn = getChineseFont(); BaseFont bf; try { //Note that there is one here.,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; } }; // Picture parsing 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 analysis 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) { } } // analysis 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. Generated pdf
Attention points
1. Code in blog is not a complete project, and it can't run only depending on code in blog;
2. There are differences between local files and remote files in access to file paths. In addition, there are many ways to access local files.
3. Complete Engineering Address: itextpdf readme.txt, there are many versions in the project, and this blog corresponds to version 4.
4. It is recommended that SIMSUN.TTC be put into the project, which is independent of the operating system and more portable.