一、文章说明

此篇文章将解决itextpdf转PDF遇到的以下问题:

1、中文不显示问题

2、表格或者图片超宽问题

3、pdf样式问题

4、Markdown引用样式在PDF中正常显示

5、转换HTML为PDF过程中提示标签没有闭合的BUG解决

此篇文章包括的关键字(可能是你关心的内容):

1、HTML

2、Markdown

3、Java

4、blockquote

5、PDF

6、itextpdf

此篇文章中PDF样式说明:

  • 本文中使用的PDF样式模仿Typora编辑器的Vue样式。

先上效果图(PDF效果图):

二、通过itextpdf将Markdown转成PDF流程

2.1、需求说明

整体流程如下:

说明,我公司项目的需求:1、通过在线Markdown编辑器写文章,将文章显示到浏览器2、 再将在线文章下载到本地保留基本样式。因为有如上需求,所以就有了上图的流程。如果你的需求跟我不一样,我也希望本篇文章能给你一些参考。

2.2、使用到的核心框架

(1)前端

marked.js

editormd.js

vue.css (这个样式文档是我从typora软件中获取的,网上直接找不到)

(2)Java

itextpdf

springboot

jsoup

上述前端框架在文档末尾会附上下载链接

2.3、Markdown转HTML

此过程更多的是前端框架完成,使用的是editormd.js (文末附上地址)。相对比较简单,我就不进行代码展示了(毕竟我们的核心是转PDF),给大家看看效果图:

2.3.1、编辑Markdown前端代码

1、初始化Markdown在线编辑器

  • HTML代码

<html><head><link rel="stylesheet" href="css/editormd.css"/><script src="js/editormd.js"></script></head><body> <div id="test-editormd"><textarea style="display:none;"></textarea></div></body>
</html> 
  • Javascript代码

var testEditor = editormd("test-editormd", {        width: "100%",        height: "75%",        markdown        : "" ,        htmlDecode      : "style,script,iframe",  // you can filter tags decode        emoji           : true,        taskList        : true,        tex             : true,  // 默认不解析        flowChart       : true,  // 默认不解析        sequenceDiagram : true,  // 默认不解析    });

上述代码就是初始化Markdown在线编辑器的代码了,一会将Markdown格式的文档转换成HTML需要用到这个editormd,所以先初始化。

上述是Markdown的编辑状态,显示纯阅览模式的代码如下。

2.3.2 纯预览状态(此状态也就是Markdown转HTML的状态)

  • HTML代码

<html><head><link rel="stylesheet" href="css/editormd.css"/><link rel="stylesheet" href="css/vue.css"/><script src="js/editormd.js"></script></head><body> <div id="test-editormd"><textarea style="display:none;"></textarea></div></body>
</html> 
  • JavaScript代码

var testEditormdView = editormd.markdownToHTML("test-editormd", {gfm:true,breaks:true,markdown        : data , //此处的data就是Markdown格式的字符串htmlDecode      : "style,script,iframe",  // you can filter tags decodetocDropdown   : true,tocTitle      : "目录 Table of Contents",emoji           : true,taskList        : true,tex             : true,  // 默认不解析flowChart       : true,  // 默认不解析sequenceDiagram : true,  // 默认不解析});
  • 上述data变量的内容如下:

  • 效果图如下:

上图,因为我引用了vue.css,所以展示如typora中的Vue风格的样式。

  • 获取上述HTML的源码

var mdHtmlContent  =testEditormdView[0].innerHTML; //获取到了源码

mdHtmlContent的内容如下:

<h2 id = "h2-u7B2Cu4E00u7AE0" >< a name = "第一章" class= "reference-link" ></a><span class="header-link octicon octicon-link"></span>第一章
</h2>
<p > 正文xxxxxxxxxxxxxxxxxx </p>
<h2 id = "h2-u7B2Cu4E8Cu7AE0" > <a name = "第二章" class= "reference-link" > </a><span class="header-link octicon octicon-link"></span > 第二章 < /h2>
<p> 正文xxxxxxxxxxxxx </p>
<blockquote>
<p> 这是一段Markdown引用 < br > xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx <br> xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx </p>
</blockquote>

上述由Markdown转换成HTML的源码,大家会发现一个问题:没有html、head、body标签。这个需要手动拼接一下。为什么要手动拼接,此处重点解释,否则后面BUG成堆:

1、因为我们获取的是Markdown转HTML后的源码,而不是整个网页的源码。其实,想获取整个网页的源码也是非常简单,但是建议不要获取整个网页的源码。因为整个网页可能有很多js代码、或者注释、或者莫名其妙多出来的东西;这些都会导致一会转pdf时发现异常。为了减少这样的BUG,最好只获取Markdown转成HTML部分的源码。

2、纯净的HTML源文件,有利于我们后面转PDF时指定PDF需要的css样式。

3、保持纯净,有利于BUG的调试。

至此,我们完成了Markdown转换成HTML的过程。效果图:

2.4、HTML转PDF

HTML转PDF用到的是jsoup和itextpdf框架。主要是在Java后端完成。流程图如下:

如上图,有两个过程需要处理,才能转换成完整样式的PDF:

1、将2.3节获取到的非完整HTML编程完整HTML,此过程较简单,就是字符串拼接就行。

2、后台Java代码完成PDF转换,此过程解决中文不显示、样式等问题。

2.4.1、拼接完整HTML,并设置CSS样式

此处一定要设置CSS样式,以便导出的PDF的样式更好看。此处CSS样式的指定,将会解决一个问题:

  • 图片超宽问题

var htmlContent = `<!DOCTYPE html><html lang="zh"><head><meta charset="utf-8" /></head><style type="text/css">body{width: 100%;}body > *:first-child {margin-top: 0 !important;}body > *:last-child {margin-bottom: 0 !important;}a {color: #42b983;/*font-weight: 600;*/padding: 0 2px;text-decoration: none;}h1,h2,h3,h4,h5,h6 {position: relative;margin-top: 15px;margin-bottom: 10px;font-weight: bold;line-height: 1.4;cursor: text;}h1:hover a.anchor,h2:hover a.anchor,h3:hover a.anchor,h4:hover a.anchor,h5:hover a.anchor,h6:hover a.anchor {text-decoration: none;}h1 tt,h1 code {font-size: inherit !important;}h2 tt,h2 code {font-size: inherit !important;}h3 tt,h3 code {font-size: inherit !important;}h4 tt,h4 code {font-size: inherit !important;}h5 tt,h5 code {font-size: inherit !important;}h6 tt,h6 code {font-size: inherit !important;}h2 a,h3 a {color: #34495e;}h1 {padding-bottom: .4rem;font-size: 30px;line-height: 1.3;}h2 {font-size: 26px;line-height: 1.225;margin: 35px 0 15px;padding-bottom: 0.5em;border-bottom: 1px solid #ddd;}h3 {font-size: 22px;line-height: 1.43;margin: 20px 0 7px;}h4 {font-size: 20px;}h5 {font-size: 18px;}h6 {font-size: 16px;color: #555;}pblockquote,ul,li,ol,dl,table {margin: 10px 0px;}.quote{border-left: 4px solid #42b983;color: #888;background-color: rgba(66, 185, 131, .2);margin-top: 15px;padding: 10px;}.on-focus-mode blockquote {border-left-color: rgba(85, 85, 85, 0.12);}//此处指定table最大宽度,用来解决超宽问题table,td,tr,img,th {width: 100%;}</style><body style="font-size:12.0pt; font-family:SimHei">`+ mdHtmlContent +`</body></html>`

拼接过程的具体的代码如下:

(1)重点:解决中文不显示的问题

上述css中有如下图这样一个配置,非常重要。用来解决中文不显示的问题。

详细说明:

a、font-family中指定的SimHei是黑体字体。只能写英文。中文无效

b、下面的java代码中,会导入SimHei的字体包。同时满足上述条件,才能正常显示中文

(2)上述css样式指定了img、table的最大宽度为100%,就可以解决超宽问题。

效果如下:我的需求是在table中有一张图片,因为图片过大导致table超宽,效果图如下:

如上,图片由一大半没有正常显示,越界了。设置img和table最大宽度之后,就正常了,效果如下:

2.4.2、通过form表达,将上述htmlContent传入后台

伪代码如下(怎么传都行,我下面只是给出一个范例伪代码):

<form id=“form1” action="/pdf" method="get"><input id="html" type="hidden" value=""/>
</form><script>
//js代码
$("#html").val(htmlContent); //此处的htmlContent就是2.4.1拼接之后的完整的HTML。
$("#form1").submit();
</script>

说明:此处是form表单请求。不能使用ajax,因为是要完成下载或者预览,后台返回的是文件流。

2.4.3、Java代码将html转成pdf。

  • 后台接收浏览器传入的HTML,PdfController类

//html:传入的html源文件
//filename:文件名称,可以自定义,仅仅用来显示下载过程中的文件名称。
//resp,Servlet响应,给前端返回的文件流就靠此类完成了。
@RequestMapping("/pdf/{filename}")
public void htmlToPdf(String html, @PathVariable("filename") String filename,HttpServletResponse resp) throws IOException {//TODO,这里就要开始进行HTML转PDF了。分成两步执行://1、解决HTML标签没有关闭,导致的转换异常//2、转换PDF
}

接收到前端传递过来的HTML之后,就要进行PDF转换了,分成两步完成:

1、解决HTML标签没有关闭,导致的转换异常

2、转换PDF

2.4.4、解决HTML标签没有关闭的异常

解决此问题用到了jsoup,所以需要导包。

  • 导包

<dependency><groupId>org.jsoup</groupId><artifactId>jsoup</artifactId><version>1.11.3</version>
</dependency>

如果HTML标签没有闭合,则会出现异常。比如< p></ p>·这属于闭合标签,< link rel="" href="">这属于没有闭合标签。在网上上类似这样没有闭合的标签还有很多,没有闭合的标签就会导致如下错误:

com.itextpdf.tool.xml.exceptions.RuntimeWorkerException: Invalid nested tag p found, expected closing tag img.at com.itextpdf.tool.xml.XMLWorker.endElement(XMLWorker.java:134)at com.itextpdf.tool.xml.parser.XMLParser.endElement(XMLParser.java:396)at com.itextpdf.tool.xml.parser.state.ClosingTagState.process(ClosingTagState.java:70)at com.itextpdf.tool.xml.parser.XMLParser.parseWithReader(XMLParser.java:236)at com.itextpdf.tool.xml.parser.XMLParser.parse(XMLParser.java:214)at com.itextpdf.tool.xml.parser.XMLParser.parse(XMLParser.java:203)at com.itextpdf.tool.xml.XMLWorkerHelper.parseXHtml(XMLWorkerHelper.java:236)at com.itextpdf.tool.xml.XMLWorkerHelper.parseXHtml(XMLWorkerHelper.java:210)at com.lhjz.portal.pdf.PDFUtil.htmlToPdf(PDFUtil.java:57)at com.lhjz.portal.controller.HomeController.htmlToPdf(HomeController.java:579)at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)at java.lang.reflect.Method.invoke(Method.java:498)at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:205)at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:133)at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:97)at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:827)at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:738)at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85)at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:967)at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:901)at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970)at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:872)at javax.servlet.http.HttpServlet.service(HttpServlet.java:661)at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846)at javax.servlet.http.HttpServlet.service(HttpServlet.java:742)

因为,不知道哪些标签闭合了,哪些标签没有闭合。所以可以采用jsoup来统一处理,规范HTML所有标签的格式,代码如下:

    private static String formatHtml(String html) {org.jsoup.nodes.Document doc = Jsoup.parse(html);// jsoup标准化标签,生成闭合标签doc.outputSettings().syntax(org.jsoup.nodes.Document.OutputSettings.Syntax.xml);doc.outputSettings().escapeMode(Entities.EscapeMode.xhtml);return doc.html();}

PdfController类代码完善如下:

@RequestMapping("/pdf/{filename}")
public void htmlToPdf(String html, @PathVariable("filename") String filename,HttpServletResponse resp) throws IOException {//TODO,这里就要开始进行HTML转PDF了。分成两步执行://1、解决HTML标签没有关闭,导致的转换异常String formatHtml = formatHtml(html);//2、转换PDF
}

至此,解决HTML标签没有闭合的问题解决,下面就开始生成PDF。

2.4.5、生成PDF

此处生成PDF用到了itextpdf,所以需要导入包:

  • 导包

<dependency><groupId>com.itextpdf</groupId><artifactId>itextpdf</artifactId><version>5.5.11</version></dependency><dependency><groupId>org.xhtmlrenderer</groupId><artifactId>core-renderer</artifactId><version>R8</version></dependency><dependency><groupId>com.itextpdf.tool</groupId><artifactId>xmlworker</artifactId><version>5.5.11</version></dependency><dependency><groupId>com.itextpdf</groupId><artifactId>itext-asian</artifactId><version>5.2.0</version></dependency>
  • 添加字体

需要在代码的resources文件夹下导入simhei字体。(字体下载见文末下载地址)

重点:此处导入的字体包中就包括了SimHei字体,跟上面body中的font-family配置对应了。用来解决中文不显示的问题。

  • java代码如下:

 public static <T> void htmlToPdf(String content, String fileName, HttpServletResponse resp) {Document document = new Document();document.addAuthor("千锋Java教研院");//        document.setHtmlStyleClass("");try {resp.setCharacterEncoding("UTF-8");resp.setHeader("content-Type", "application/pdf");resp.setHeader("Content-Disposition","attachment;filename=" + URLEncoder.encode(fileName + ".pdf", "UTF-8"));document.open();XMLWorkerFontProvider fontImp = new XMLWorkerFontProvider(XMLWorkerFontProvider.DONTLOOKFORFONTS);fontImp.register(“/pdf/simhei.ttf”); //此处字体导入XMLWorkerHelper.getInstance().parseXHtml(writer, document,new ByteArrayInputStream(content.getBytes("UTF-8")),null, Charset.forName("UTF-8"),fontImp);} catch (UnsupportedEncodingException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();} catch (DocumentException e) {e.printStackTrace();}catch (Exception e){e.printStackTrace();} finally{document.close();}}

说明

  • 下载:attachment (本篇文章是下载pdf)

resp.setHeader("Content-Disposition","attachment;filename=" + URLEncoder.encode(fileName + ".pdf", "UTF-8"));
  • 在线预览:online

resp.setHeader("Content-Disposition","online;filename=" + URLEncoder.encode(fileName + ".pdf", "UTF-8"));

PdfController类代码完善如下:

@RequestMapping("/pdf/{filename}")
public void htmlToPdf(String html, @PathVariable("filename") String filename,HttpServletResponse resp) throws IOException {//TODO,这里就要开始进行HTML转PDF了。分成两步执行://1、解决HTML标签没有关闭,导致的转换异常String formatHtml = formatHtml(html);//2、转换PDFhtmlToPdf(formatHtml,filename,resp);
}

至此,Markdown转HTML,HTML转PDF的代码结束。这样就可以下载PDF了。PDF效果如下图:

如上图,PDF导出成功了,但是Markdown引用的样式没有了。如果想恢复Markdown引用样式,则需要继续调试。

2.5、解决PDF中引用块(blockqoute)的样式失效

2.5.1、找问题

一开始碰到这个问题的时候,也不知道是为什么样式就没有了。明明CSS样式文档中有配置,并且在HTML上都显示正常,转换成PDF之后样式就丢失了。此问题解决了很久,主要是一开始不明白为什么丢失。

最后,经过反复对比研究,发现是Markdown的引用在HTML中是以blockquote标签来显示的,那也就是说,PDF不识别blockquote标签,找到问题之后,就好解决。在Java代码中将所有blockquote标签换成table标签,之所以换成table标签,因为itextpdf转换为PDF,要想显示边框,暂时只发现了能显示table的边框。其他标签的边框都无法显示。

2.5.2、解决

所以将blockquote替换成table,就可以解决问题。Java代码如下:

@RequestMapping("/pdf/{filename}")
public void htmlToPdf(String html, @PathVariable("filename") String filename,HttpServletResponse resp) throws IOException {//TODO,这里就要开始进行HTML转PDF了。分成两步执行://1、解决HTML标签没有关闭,导致的转换异常html = html.replaceAll("<blockquote>","<table class='quote'><tr><td>");html = html.replaceAll("</blockquote>","</td></tr></table>");String formatHtml = formatHtml(html);//2、转换PDFhtmlToPdf(formatHtml,filename,resp);
}

上述replaceAll完成了所有替换。解决问题,导出了PDF效果如下:

如上图,PDF中引用部分的样式跟HTML和Markdown的样式一致了。

三、其他

1、本文章可能需要的附件-关注公众号,回复【pdf样式】领取

2、中文不显示问题

解决:body标签配置font-family属性指定字体,字体必须跟后台导入字体一致。字体名称必须是英文。

3、表格或者图片超宽问题

解决:配置css样式解决。

4、pdf样式问题

解决:配置css样式

5、Markdown引用样式在PDF中正常显示

解决:替换blockquote标签,并且给新标签指定样式

6、转换HTML为PDF过程中提示标签没有闭合的BUG解决

解决:采用jsoup框架解决。

Java导出PDF样式详细解析(步骤+代码)相关推荐

  1. java导出PDF(itextpdf+Adobe Acrobat 9 Pro)

    java导出PDF 背景 摸索 maven依赖 代码实现 模板及结果展示 模板 结果 彩蛋 彩蛋一:本地临时文件删除不了 彩蛋二:导出横向A4PDF 彩蛋三:多个PDF合并(待完成) 背景 最近接到一 ...

  2. 使用java导出pdf文件

    使用java导出pdf文件 itext itext的使用 JasperReports JasperReports与itext的区别 Jaspersoft Studio 工具简介 Jaspersoft ...

  3. 咖啡汪日志——JAVA导出pdf文件加水印 文字+图片、文字

    咖啡汪日志--JAVA导出pdf文件加水印 文字和图片.文字 hello,又大家见面了! 作为一只不是在戏精就是在戏精路上的哈士奇,今天要展示给大家的就是如何快捷地给pdf文件增加各种水印.嗷呜呜,前 ...

  4. java jar 打印_三种Java打印PDF文档的实例代码

    以下内容归纳了通过Java程序打印PDF文档时的3种情形.即: 1 静默打印 2 显示打印对话框打印 3 打印PDF时自定义纸张大小 使用工具:Spire.PDF for Java Jar文件获取及导 ...

  5. JAVA导出PDF并压缩成zip

    JAVA导出PDF借助 iText pom先引入两个jar包 <dependency><groupId>com.itextpdf</groupId><arti ...

  6. java导出PDF、iText5导出漂亮表格PDF、导出指定格式水印PDF

    我们在项目当中经常要导出pdf文档,pdf文档还要按一定的格式导出,以下介绍导出pdf文档功能: 1)支持A4纸大小导出 2)指定文字显示的位置 3)支持表格展示数据 4)添加水印 开源下载 java ...

  7. java导出pdf功能记录

    这几天已在做处理导出pdf文件的功能,摸索了几天总算可以了.记录下这几天遇到的问题. 1.网上基本都是基于Itext5和Itext7来处理的.我最终是在Itext5上成功了,itext7应该是模板出问 ...

  8. 「Java工具类」pdf导出工具类java导出pdf文件工具类

    介绍语 本号主要是Java常用关键技术点,通用工具类的分享:以及springboot+springcloud+Mybatisplus+druid+mysql+redis+swagger+maven+d ...

  9. Java导出PDF文档(模板导出和自定义)

    项目场景: 需要导出PDF文档,支持模板导出和自定义文档格式. 场景分析: PDF模板创建可使用表单域创建表单字段,引入数据填充,或者根据实际需要生成html转换成pdf. 解决方案: PDF模板可以 ...

最新文章

  1. 比explain更加详细的分析计划:Query Profiler
  2. 文件上传oss服务器
  3. 前端学习(2433):创建页面组件
  4. 编程同写作,写代码只是在码字
  5. redis rdb aof区别_聊一聊RDB、AOF在redis持久化里的底层原理
  6. 基于Adobe LCDS产品的数据访问解决方案Part4
  7. mysql 与 mycat集成读写分离
  8. 实例分割和语义分割使用labelme制作的数据集如何转换为voc和coco格式的数据
  9. 支付宝“手机网站支付”开发的相关文档和工具
  10. paip.获取当前实际北京时间API
  11. 串口与Modbus调试工具
  12. 【系统分析师之路】第五章 复盘软件工程(软件过程改进)
  13. Java-输出100以内的质数
  14. 教室录播系统方案_录播教室技术解决方案
  15. MDK5 代码折叠功能
  16. unity打箱子小游戏demo
  17. 鸿蒙 OS 2.0 公测!已适配多款机型
  18. 互联网晚报 | 7月9日 星期六 |马斯克终止收购推特;​B 站回应 2 亿余条用户账号疑泄露传闻;上海逐步开放电影院和演出场所...
  19. 蓝桥杯第十二届真题解析
  20. 今日头条张一鸣:30亿估值之后怎么玩?

热门文章

  1. quick-cocos 集成ShareSDK
  2. Qt图表绘制(QtCharts)-绘制简单的圆环图(7)
  3. wr890n虚拟服务器,TP-Link TL-WR890N无线路由器的上网设置教程
  4. VR+农业的碰撞,VR数字农业的实际应用有哪些?
  5. LeetCode876 --- 剑指Offer 22
  6. 指标归因平台建设思路
  7. 开源网页图片打印服务
  8. 这或许是全网最全时间序列特征工程构造的文章了
  9. 美股日志|三大股票指数升金价重上1800
  10. Win11设置鼠标箭头图案的方法教程