找阿里编程规范的时候在github上搜索到的,觉得pdf像书籍一样的翻页比较自由,也是手痒吧。
启发开源项目github-pdf-demo
页面是自己凑凑的看一下图:
package com.bamboo.markdown.paper.common; import com.lowagie.text.DocumentException; import com.lowagie.text.pdf.BaseFont; import freemarker.template.Template; import freemarker.template.TemplateException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.util.CollectionUtils; import org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer; import org.w3c.dom.Document; import org.xhtmlrenderer.pdf.ITextFontResolver; import org.xhtmlrenderer.pdf.ITextRenderer; import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServletResponse; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import java.io.*; import java.util.List; import java.util.Map; /** * Created by admin on 2018/6/18. * ftl模板转pdf处理工具类 */ public class FtlTransPdfUtils { private static final Logger LOGGER = LoggerFactory.getLogger(FtlTransPdfUtils.class); /** * 按模板和参数生成html字符串,再转换为flying-saucer识别的Document * * @param templateName freemarker模板名称 * @param variables freemarker模板参数 * @return Document */ private static Document generateDoc(FreeMarkerConfigurer configurer, String templateName, Map<String, Object> variables) { Template tp; try { tp = configurer.getConfiguration().getTemplate(templateName); } catch (IOException e) { LOGGER.error(e.getMessage(), e); return null; } StringWriter stringWriter = new StringWriter(); try (BufferedWriter writer = new BufferedWriter(stringWriter)) { try { tp.process(variables, writer); writer.flush(); } catch (TemplateException e) { LOGGER.error("模板不存在或者路径错误", e); } catch (IOException e) { LOGGER.error("IO异常", e); } DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); return builder.parse(new ByteArrayInputStream(stringWriter.toString().getBytes())); } catch (Exception e) { LOGGER.error(e.getMessage(), e); return null; } } /** * 核心: 根据freemarker模板生成pdf文档 * * @param configurer freemarker配置 * @param templateName freemarker模板名称 * @param out 输出流 * @param listVars freemarker模板参数 * @throws Exception 模板无法找到、模板语法错误、IO异常 */ private static void generateAll(FreeMarkerConfigurer configurer, String templateName, OutputStream out, List<Map<String, Object>> listVars) { if (CollectionUtils.isEmpty(listVars)) { LOGGER.warn("警告:freemarker模板参数为空!"); return; } ITextRenderer renderer = new ITextRenderer(); Document doc = generateDoc(configurer, templateName, listVars.get(0)); renderer.setDocument(doc, null); //设置字符集(宋体),此处必须与模板中的<body style="font-family: SimSun">一致,区分大小写,不能写成汉字"宋体" ITextFontResolver fontResolver = renderer.getFontResolver(); try { fontResolver.addFont("typeface/simsun.ttc", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED); } catch (DocumentException e) { LOGGER.error("S1DocumentException" + e.getMessage()); } catch (IOException e) { LOGGER.error("S2IOException" + e.getMessage()); } //展现和输出pdf renderer.layout(); try { renderer.createPDF(out, false); } catch (DocumentException e) { LOGGER.error("S3DocumentException" + e.getMessage()); } //根据参数集个数循环调用模板,追加到同一个pdf文档中 //(注意:此处从1开始,因为第0是创建pdf,从1往后则向pdf中追加内容) for (int i = 1; i < listVars.size(); i++) { Document docAppend = generateDoc(configurer, templateName, listVars.get(i)); renderer.setDocument(docAppend, null); renderer.layout(); try { renderer.writeNextDocument(); //写下一个pdf页面 } catch (DocumentException e) { LOGGER.error("S4DocumentException" + e.getMessage()); } } renderer.finishPDF(); //完成pdf写入 } /** * pdf下载 * * @param configurer freemarker配置 * @param templateName freemarker模板名称(带后缀.ftl) * @param listVars 模板参数集 * @param response HttpServletResponse * @param fileName 下载文件名称(带文件扩展名后缀) */ public static void download(FreeMarkerConfigurer configurer, String templateName, List<Map<String, Object>> listVars, HttpServletResponse response, String fileName) { // 设置编码、文件ContentType类型、文件头、下载文件名 response.setCharacterEncoding("utf-8"); response.setContentType("multipart/form-data"); try { response.setHeader("Content-Disposition", "attachment;fileName=" + new String(fileName.getBytes("gb2312"), "ISO8859-1")); } catch (UnsupportedEncodingException e) { LOGGER.error("X1" + e.getMessage()); } try (ServletOutputStream out = response.getOutputStream()) { generateAll(configurer, templateName, out, listVars); out.flush(); } catch (Exception e) { LOGGER.error("X2" + e.getMessage()); //X2java.io.IOException: 远程主机强迫关闭了一个现有的连接。 } } /** * pdf预览 * * @param configurer freemarker配置 * @param templateName freemarker模板名称(带后缀.ftl) * @param listVars 模板参数集 * @param response HttpServletResponse */ public static void preview(FreeMarkerConfigurer configurer, String templateName, List<Map<String, Object>> listVars, HttpServletResponse response) { try (ServletOutputStream out = response.getOutputStream()) { generateAll(configurer, templateName, out, listVars); out.flush(); } catch (Exception e) { LOGGER.error(e.getMessage(), e); } } }
package com.bamboo.markdown.paper.controller; import com.bamboo.markdown.paper.common.FtlTransPdfUtils; import com.bamboo.markdown.paper.entity.vo.ArticleView; import com.bamboo.markdown.paper.repository.ArticleRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer; import org.xml.sax.SAXException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * Created by admin on 2018/6/18. */ @Controller @RequestMapping(value = "/pdf") public class PdfController { @Autowired private FreeMarkerConfigurer configurer; @Autowired private ArticleRepository articleRepository; /** * pdf预览 * * @param request HttpServletRequest * @param response HttpServletResponse */ @RequestMapping(value = "/preview/{id}", method = RequestMethod.GET) public void preview(HttpServletRequest request, HttpServletResponse response, @PathVariable Integer id) throws IOException, SAXException { ArticleView articleView = new ArticleView(articleRepository.findById(id)); // 构造freemarker模板引擎参数,listVars.size()个数对应pdf页数 List<Map<String, Object>> listVars = new ArrayList<>(); Map<String, Object> variables = new HashMap<>(); variables.put("title", articleView.getTitle()); //补充修正 String htmlContent = articleView.getHtmlMaterial().replace("<br>", "<br/>"); //匹配img标签的正则表达式 String regxpForImgTag = "<img\\s[^>]+>"; Pattern pattern = Pattern.compile(regxpForImgTag); Matcher matcher = pattern.matcher(htmlContent); StringBuffer sbr = new StringBuffer(); while (matcher.find()) { String temp = matcher.group(); matcher.appendReplacement(sbr, temp.replace(">", " />")); } matcher.appendTail(sbr); String htmlMaterial = sbr.toString(); //// String regxpForImgTag2 = "<pre><code\\s[^>]+>"; Pattern pattern2 = Pattern.compile(regxpForImgTag2); Matcher matcher2 = pattern2.matcher(htmlMaterial); StringBuffer sbr2 = new StringBuffer(); while (matcher2.find()) { String temp2 = matcher2.group(); matcher2.appendReplacement(sbr2, temp2.replace( "<pre><code class=\"lang-java\">", " <pre class=\"prettyprint linenums prettyprinted\" style=\"\"><code class=\"lang-java\">")); } matcher2.appendTail(sbr2); variables.put("content", sbr2.toString()); variables.put("len", htmlMaterial.length()); listVars.add(variables); // FtlTransPdfUtils.preview(configurer, "pdf/pdfPage.ftl", listVars, response); } /** * pdf下载 * * @param request HttpServletRequest * @param response HttpServletResponse */ @RequestMapping(value = "/download/{id}", method = RequestMethod.GET) public void download(HttpServletRequest request, HttpServletResponse response, @PathVariable Integer id) { ArticleView articleView = new ArticleView(articleRepository.findById(id)); List<Map<String, Object>> listVars = new ArrayList<>(); Map<String, Object> variables = new HashMap<>(); variables.put("title", articleView.getTitle()); //补充修正 String htmlContent = articleView.getHtmlMaterial().replace("<br>", "<br/>"); //匹配img标签的正则表达式 String regxpForImgTag = "<img\\s[^>]+>"; Pattern pattern = Pattern.compile(regxpForImgTag); Matcher matcher = pattern.matcher(htmlContent); StringBuffer sbr = new StringBuffer(); while (matcher.find()) { String temp = matcher.group(); matcher.appendReplacement(sbr, temp.replace(">", " />")); } matcher.appendTail(sbr); String htmlMaterial = sbr.toString(); //// String regxpForImgTag2 = "<pre><code\\s[^>]+>"; Pattern pattern2 = Pattern.compile(regxpForImgTag2); Matcher matcher2 = pattern2.matcher(htmlMaterial); StringBuffer sbr2 = new StringBuffer(); while (matcher2.find()) { String temp2 = matcher2.group(); matcher2.appendReplacement(sbr2, temp2.replace( "<pre><code class=\"lang-java\">", " <pre class=\"prettyprint linenums prettyprinted\" style=\"\"><code class=\"lang-java\">")); } matcher2.appendTail(sbr2); variables.put("content", sbr2.toString()); variables.put("len", htmlMaterial.length()); listVars.add(variables); FtlTransPdfUtils.download(configurer, "pdf/pdfPage.ftl", listVars, response, articleView.getTitle() + ".pdf"); } }
文章来源: flying-saucer-pdf预览及下载