java使用freemarker模板生成html再轉為pdf
1.freemarker模板生成html
添加Maven依賴
在pom.xml文件中添加以下依賴:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-freemarker</artifactId> </dependency>
創(chuàng)建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的指令語法來實現(xiàn)動態(tài)生成表格內容,包括使用<#list>標簽來遍歷用戶列表,并使用${}語法來輸出用戶信息。
創(chuàng)建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注解來處理請求,然后創(chuàng)建一個用戶列表,并將其添加到模型中。最后返回table字符串,代表要使用的HTML模板文件。
運行項目
運行Spring Boot應用程序,然后使用瀏覽器訪問http://localhost:8080/users,即可看到動態(tài)生成的HTML表格。
注意:上面的例子僅供參考,實際應用中需要根據自己的需求進行修改擴展。
2.利用iText將生成的HTML轉換為PDF文件
理解了freemarker生成html的步驟以后,就可以利用iText把html生成pdf文件了。
1.編寫轉換代碼
添加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>
2.編寫轉換代碼
在SpringBoot應用程序中創(chuàng)建一個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>
執(zhí)行就可以看到生成的pdf文件了,文件路徑在d:/test1.pdf。
到此這篇關于java使用freemarker模板生成html再轉為pdf的文章就介紹到這了,更多相關java freemarker生成html內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
使用位運算、值交換等方式反轉java字符串的多種方法(四種方法)
這篇文章主要介紹了使用位運算、值交換等方式反轉java字符串,本文通過四種方式給大家講解,給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-07-07Springboot實現(xiàn)全局自定義異常的方法詳解
這篇文章主要介紹了Springboot實現(xiàn)全局自定義異常的方法詳解,SpringBoot的項目已經對有一定的異常處理了,但是對于我們開發(fā)者而言可能就不太合適了,因此我們需要對這些異常進行統(tǒng)一的捕獲并處理,需要的朋友可以參考下2023-11-11