1.freemarker模板生成html
- 添加Maven依赖
在pom.xml文件中添加以下依赖:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-freemarker</artifactId> </dependency>
复制
- 创建Freemarker模板
新建一个HTML文件,例如table.ftl,然后在其中编写HTML模板,包括表格的头部、内容和尾部等部分。具体可以参考下面这个例子:
<table border="1"> <thead> <tr> <th>ID</th> <th>Name</th> <th>Age</th> </tr> </thead> <tbody> <#list users as user> <tr> <td>${user.id}</td> <td>${user.name}</td> <td>${user.age}</td> </tr> </#list> </tbody> </table>
复制
上面的模板中使用了Freemarker的指令语法来实现动态生成表格内容,包括使用<#list>
标签来遍历用户列表,并使用${}
语法来输出用户信息。
- 创建Controller方法
在Controller中编写一个方法来获取用户列表,然后渲染上面的模板并返回HTML内容。示例代码如下:
@Controller public class UserController { @GetMapping("/users") public String userList(Model model) { List<User> users = new ArrayList<>(); users.add(new User(1, "Tom", 18)); users.add(new User(2, "Jerry", 20)); users.add(new User(3, "John", 22)); model.addAttribute("users", users); return "table"; } }
复制
上面的方法使用@GetMapping
注解来处理请求,然后创建一个用户列表,并将其添加到模型中。最后返回table
字符串,代表要使用的HTML模板文件。
- 运行项目
运行Spring Boot应用程序,然后使用浏览器访问http://localhost:8080/users
,即可看到动态生成的HTML表格。
注意:上面的例子仅供参考,实际应用中需要根据自己的需求进行修改扩展。
2.利用iText将生成的HTML转换为PDF文件
理解了freemarker生成html的步骤以后,就可以利用iText把html生成pdf文件了。
- 编写转换代码
添加Maven依赖
在pom.xml文件中添加以下依赖:
<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.9</version> </dependency>
复制
- 编写转换代码
在SpringBoot应用程序中创建一个Service或者Controller类,然后编写HTML转PDF的代码。
freeMarker转换为html的方法:
public class HtmlGenerator { public static String generate(String template, Map<String, Object> variables) throws IOException, TemplateException, IOException { Configuration config = new Configuration(Configuration.DEFAULT_INCOMPATIBLE_IMPROVEMENTS); // 指定FreeMarker模板文件的位置 config.setClassForTemplateLoading(HtmlGenerator.class, "/filePath"); //读取模板文件地址 config.setDefaultEncoding("UTF-8"); //获取模板文件 Template tp = config.getTemplate(template); StringWriter stringWriter = new StringWriter(); BufferedWriter writer = new BufferedWriter(stringWriter); tp.setEncoding("UTF-8"); //把map数据写入 tp.process(variables, writer); String htmlStr = stringWriter.toString(); writer.flush(); writer.close(); return htmlStr; } }
复制
/filePath为项目中的ftl文件相对路径。
html生成pdf的方法:
public class PdfDocumentGenerator { private static final Logger logger = LoggerFactory.getLogger(PdfDocumentGenerator.class); /** * Output a pdf to the specified outputstream * * @param htmlStr * the htmlstr * @param out * the specified outputstream * @throws Exception */ public static void generate(String htmlStr, OutputStream out) throws Exception { DocumentBuilderFactory df = DocumentBuilderFactory.newInstance(); df.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, ""); // Compliant df.setAttribute(XMLConstants.ACCESS_EXTERNAL_SCHEMA, ""); // compliant DocumentBuilder builder = df.newDocumentBuilder(); org.w3c.dom.Document doc = builder.parse(new ByteArrayInputStream(htmlStr .getBytes())); ITextRenderer renderer = new ITextRenderer(); renderer.setDocument(doc, null); renderer.layout(); renderer.createPDF(out); out.close(); } public static void generatePlus(String htmlStr, OutputStream out) throws IOException, DocumentException { final String charsetName = "UTF-8"; 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) { if (fontname == null) { fontname = getChineseFont(); } return super.getFont(fontname, encoding, size, style); } })) { @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 = PdfDocumentGenerator.class.getResource("/").getPath(); @Override public String getImageRootPath() { return rootPath; } @Override public Image retrieve(String src) { if (StringUtils.isEmpty(src)) { return null; } try { Image image = Image.getInstance(new File(rootPath, src).toURI().toString()); // 图片显示位置 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, charsetName)) { 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 = PdfDocumentGenerator.class.getResourceAsStream("/" + href); try (InputStreamReader reader = new InputStreamReader(is,charsetName)) { 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(charsetName)); try (InputStream inputStream = new ByteArrayInputStream(htmlStr.getBytes())) { parser.parse(inputStream, Charset.forName(charsetName)); } document.close(); } /** * 获取中文字体位置 * @return */ public static String getChineseFont() { String chineseFont = null; chineseFont = Object.class.getResource("/").getPath() + "font/simsun.ttc"; if(!new File(chineseFont).exists()){ throw new RuntimeException("字体文件不存在!"+chineseFont); } return chineseFont; } }
复制
运行测试方法:
public class Pdfdest { public static void main(String[] args) throws Exception { String outputFile = "d:/test1.pdf"; Map<String, Object> map = new HashMap<>(); map.put("XXX", "测试"); //生成工具,下面有代码 String htmlStr = HtmlGenerator.generate("test.ftl", map); //生成工具,下面有代码 OutputStream out = new FileOutputStream(outputFile); PdfDocumentGenerator.generatePlus(htmlStr,out); } }
复制
准备一个test.ftl放到resource/filePath下,当然字体最好也放到resource/font下,运行时需要使用。
test.ftl的代码如下:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"></meta> <style> @page { @top-center { content: element(header) } } @page { @bottom-center { content: element(footer) } } .apply { margin: 0 auto; padding: 0 30px; } .title { margin-top: 40px ; text-align: center; font-weight: bold; //字体需要和后台对应上 font-family: SimSun; font-weight: bold; font-size: 20px; color: #333333; letter-spacing: 0; } .table { border-collapse: collapse; width: 100%; margin-top: 30px; font-family: SimSun; font-size: 14px; color: #111111; letter-spacing: 0.54px; } .label { background-color: #E6E6E6; width: 20%; } .normaltd { padding: 10px 0; } .maxtd { height: 250px; } .value { width: 30%; padding-left: 10px; } .apply { margin: 0 auto; padding: 0 30px; } .title { margin-top: 40px ; text-align: center; font-weight: bold; //字体需要和后台对应上 font-family: SimSun; font-weight: bold; font-size: 20px; color: #333333; letter-spacing: 0; } .table { width: 100%; margin-top: 30px; font-family: SimSun; font-size: 14px; color: #111111; letter-spacing: 0.54px; } .label { background-color: #E6E6E6; width: 20%; } .normaltd { padding: 10px 0; } .maxtd { height: 250px; } .value { width: 30%; padding-left: 10px; } tr { page-break-inside: avoid; page-break-after: auto; } </style> </head> <body style="font-family: SimSun"> <div class="apply"> <p class="title">申请单</p> <table border="1" cellspacing="0" class="table"> <tr> <td class="label normaltd" align="center">XXX</td> <td class="normaltd value">${XXX}</td> <td class="label normaltd" align="center">XXX</td> <td class="normaltd value">${XXX}</td> </tr> <tr> <td class="label normaltd" align="center">XXX</td> <td class="normaltd value">${XXX}</td> <td class="label normaltd" align="center">XXX</td> <td class="normaltd value">${XXX}</td> </tr> <tr> <td class="label" align="center">XXX</td> <td class="normaltd value">${XXX}</td> <td class="label normaltd" align="center">XXX</td> <td class="normaltd value">${XXX}</td> </tr> <tr> <td class="label normaltd" align="center">XXX</td> <td class="normaltd value">XXX</td> <td class="label normaltd" align="center">XXX</td> <td class="normaltd value">${XXX}</td> </tr> <tr> <td class="label normaltd" align="center">XXX</td> <td class="normaltd value">${XXX}</td> <td class="label normaltd" align="center">XXX</td> <td class="normaltd value">${XXX}</td> </tr> <tr > <td valign="middle" colspan="1" class="label maxtd" align="center">XXX</td> <td valign="middle" colspan="3" class="maxtd value">${XXX}</td> </tr> <tr> <td colspan="1" class="label normaltd" align="center">XXX</td> <td colspan="3" class="normaltd value">${XXX}</td> </tr> <tr> <td colspan="1" class="label normaltd" align="center">XXX</td> <td colspan="3" class="normaltd value">${XXX}</td> </tr> <tr> <td colspan="1" class="label normaltd" align="center">XXX</td> <td colspan="3" class="normaltd value">${XXX}</td> </tr> <tr> <td class="label normaltd" align="center">XXX</td> <td class="normaltd value">${XXX}</td> <td class="label normaltd" align="center">XXX</td> <td class="normaltd value">${XXX}</td> </tr> <tr> <td class="label normaltd" align="center">XXX</td> <td class="normaltd value">${XXX}</td> <td class="label normaltd" align="center">XXX</td> <td class="normaltd value">${XXX}</td> </tr> <tr> <td class="label" align="center">XXX</td> <td class="normaltd value">${XXX}</td> <td class="label normaltd" align="center">XXX</td> <td class="normaltd value">${XXX}</td> </tr> <tr> <td class="label normaltd" align="center">XXX</td> <td class="normaltd value">XXX</td> <td class="label normaltd" align="center">XXX</td> <td class="normaltd value">${XXX}</td> </tr> <tr> <td class="label normaltd" align="center">XXX</td> <td class="normaltd value">${XXX}</td> <td class="label normaltd" align="center">XXX</td> <td class="normaltd value">${XXX}</td> </tr> <tr > <td valign="middle" colspan="1" class="label maxtd" align="center">XXX</td> <td valign="middle" colspan="3" class="maxtd value">${XXX}</td> </tr> <tr> <td colspan="1" class="label normaltd" align="center">XXX</td> <td colspan="3" class="normaltd value">${XXX}</td> </tr> <tr> <td colspan="1" class="label normaltd" align="center">XXX</td> <td colspan="3" class="normaltd value">${XXX}</td> </tr> <tr> <td colspan="1" class="label normaltd" align="center">XXX</td> <td colspan="3" class="normaltd value">${XXX}</td> </tr> <tr> <td class="label normaltd" align="center">XXX</td> <td class="normaltd value">${XXX}</td> <td class="label normaltd" align="center">XXX</td> <td class="normaltd value">${XXX}</td> </tr> <tr> <td class="label normaltd" align="center">XXX</td> <td class="normaltd value">${XXX}</td> <td class="label normaltd" align="center">XXX</td> <td class="normaltd value">${XXX}</td> </tr> <tr> <td class="label" align="center">XXX</td> <td class="normaltd value">${XXX}</td> <td class="label normaltd" align="center">XXX</td> <td class="normaltd value">${XXX}</td> </tr> <tr> <td class="label normaltd" align="center">XXX</td> <td class="normaltd value">XXX</td> <td class="label normaltd" align="center">XXX</td> <td class="normaltd value">${XXX}</td> </tr> <tr> <td class="label normaltd" align="center">XXX</td> <td class="normaltd value">${XXX}</td> <td class="label normaltd" align="center">XXX</td> <td class="normaltd value">${XXX}</td> </tr> </table> </div> </body> </html>
复制
执行就可以看到生成的pdf文件了,文件路径在d:/test1.pdf。