常用工具实践-HTML转PDF

本是古典 何须时尚 2022-12-03 09:41 487阅读 0赞

文章目录

    • 目标
    • HTML 转 PDF
      • textpdf类库
        • 代码实践:
      • wkhtmltopdf 软件
        • 代码实践:
      • Chrome Headless
        • 代码实践:
    • 汇总

目标

  • 了解HTML 转PDF一些方式

    参考:java实现HTML转PDF

    Headless Chrome 入门 建议阅读

    利用Chrome Headless模式,网页转PDF

    java实现HTML转PDF

    Java操作wkhtmltopdf实现Html转PDF

    最好用Html转pdf的工具——wkhtmltopdf

HTML 转 PDF

在某些业务场景下,需要把表单转换为pdf导出。

根据网上的一些参考,大致有三种实践方式,当然还有其他的

  1. itextpdf类库
  2. wkhtmltopdf 软件
  3. Chrome Headless(无头浏览器)

textpdf类库

参考:java实现HTML转PDF

官方地址:itextpdf

特点:对于html格式检测非常严格

html 头部必须声明,例如

  1. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">* <html lang="en" xmlns="http://www.w3.org/1999/xhtml">

标签都要加作为结束符,如果html不符合规定,转换时会报错。

  • 针对中文转换问题

    下载对应的中文字体(ttf)

    在页面中需要指定字体样式

    1. font-family:SimSun;

代码实践:

环境:spring boot 2.2.7 jdk1.8 windows10 (linux建议测试,可能一些命令参数需要调整)

pom:针对jar的版本,可以再参考下官网

可以参考:codycopy

  1. <!-- https://mvnrepository.com/artifact/com.itextpdf/itext-asian -->
  2. <dependency>
  3. <groupId>com.itextpdf</groupId>
  4. <artifactId>itext-asian</artifactId>
  5. <version>5.2.0</version>
  6. </dependency>
  7. <!-- https://mvnrepository.com/artifact/com.itextpdf/itextpdf -->
  8. <dependency>
  9. <groupId>com.itextpdf</groupId>
  10. <artifactId>itextpdf</artifactId>
  11. <version>5.5.13</version>
  12. </dependency>
  13. <dependency>
  14. <groupId>org.xhtmlrenderer</groupId>
  15. <artifactId>flying-saucer-pdf</artifactId>
  16. <version>9.0.7</version>
  17. </dependency>

工具类:

fun.gengzi.codecopy.business.utilstest.utils.HtmlToPdfUtils

注意要下载需要的字体文件,复制到 resources/font 以便使用

  1. /** * html文本转换成PDF * <p> * 由于 ITextpdf 对html 检测非常严格,html 头部必须声明 * <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> * <html lang="en" xmlns="http://www.w3.org/1999/xhtml"> * <p> * 以及其他的都要加</>结束,所以一般的页面都将不支持 转换 pdf * * @param url 网址 * @param pdfPath pdf生成路径 * @param fontFamilyEnum {@link FontFamilyEnum} 中文字体信息 */
  2. public static void htmlTextToPdf(URL url, String pdfPath, FontFamilyEnum fontFamilyEnum) {
  3. String body = HttpRequest.get(url.toString()).execute().body();
  4. htmlTextToPdf(body, pdfPath, fontFamilyEnum);
  5. }
  6. /** * html文本转换成PDF * * @param file html 文件 * @param pdfPath pdf生成路径 * @param fontFamilyEnum {@link FontFamilyEnum} 中文字体信息 */
  7. public static void htmlTextToPdf(File file, String pdfPath, FontFamilyEnum fontFamilyEnum) {
  8. try {
  9. htmlTextToPdf(FileUtil.toString(file), pdfPath, fontFamilyEnum);
  10. } catch (IOException e) {
  11. e.printStackTrace();
  12. }
  13. }
  14. /** * html文本转换成PDF * * @param htmlText html字符串 * @param pdfPath pdf生成路径 * @param fontFamilyEnum {@link FontFamilyEnum} 中文字体信息 */
  15. public static void htmlTextToPdf(String htmlText, String pdfPath, FontFamilyEnum fontFamilyEnum) {
  16. logger.info("html转pdf入参,htmltext: {} , pdfpath:{}", htmlText, pdfPath);
  17. // 为了支持中文,检查html内容中是否包含 font-family:SimSun
  18. boolean fontFamilyFlag = htmlText.contains("font-family");
  19. boolean fontNameFlag = htmlText.contains(fontFamilyEnum.getFontName());
  20. if (!fontFamilyFlag || !fontNameFlag) {
  21. logger.warn("--请注意,当前转换的html未包含字体文件,中文可能转换失败--");
  22. }
  23. try {
  24. TimeInterval timer = DateUtil.timer();
  25. OutputStream outputStream = new FileOutputStream(pdfPath);
  26. ITextRenderer iTextRenderer = new ITextRenderer();
  27. //解决中文字体,需要单独下载字体
  28. //同时在前端样式中加入font-family:SimSun;
  29. ITextFontResolver fontResolver = iTextRenderer.getFontResolver();
  30. fontResolver.addFont(Objects.requireNonNull(ClassLoader.getSystemClassLoader().getResource("font/" + fontFamilyEnum.fontFileName)).getPath(),
  31. BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
  32. iTextRenderer.setDocumentFromString(htmlText);
  33. iTextRenderer.layout();
  34. iTextRenderer.createPDF(outputStream);
  35. logger.info("转换耗时(毫秒):{}", timer.interval());
  36. outputStream.close();
  37. } catch (IOException | DocumentException e) {
  38. throw new RrException("PDF转换异常", e);
  39. }
  40. }
  41. /** * 字体枚举类 */
  42. public enum FontFamilyEnum {
  43. SIMSUN("SimSun", "simsun.ttf");
  44. // 字体名称
  45. private String fontName;
  46. // 字体文件
  47. private String fontFileName;
  48. FontFamilyEnum(String fontName, String fontFileName) {
  49. this.fontName = fontName;
  50. this.fontFileName = fontFileName;
  51. }
  52. public String getFontName() {
  53. return fontName;
  54. }
  55. public String getFontFileName() {
  56. return fontFileName;
  57. }
  58. }

wkhtmltopdf 软件

参考:Java操作wkhtmltopdf实现Html转PDF

最好用Html转pdf的工具——wkhtmltopdf 建议阅读

官方地址:wkhtmltopdf 建议看下官方文档,非常简单明了

使用方式:

  1. 下载wkhtmltopdf 对应系统的软件安装,基本的系统都支持。下载地址
  2. 配置wkhtmltopdf 为全局的环境变量,方便使用(自行百度)
  3. 代码调用命令行,执行转换命令

    1. wkhtmltopdf https://www.baidu.com baidu.pdf

代码实践:

环境:spring boot 2.2.7 jdk1.8 windows10

配置全局变量:系统-高级系统设置 将wkhtmltopdf 的bin 目录配置到 path 中

代码参考:fun.gengzi.codecopy.business.utilstest.utils.HtmlToPdfUtils

java调用cmd 执行命令,使用了工具类 hutool

  1. /** * 使用wkhtmltopdf 将html转pdf * <p> * 注意,需要安装 wkhtmltopdf 该软件 * 需要将 wkhtmltopdf 配置到环境变量中,以便使用 * 在测试时,注意当前执行main方法的环境变量中,是否包含了 wkhtmltopdf 的配置 * idea会读取系统环境变量的配置,但是刚修改的好像不能及时更新,可以重新idea 来帮助其更新配置 * 具体,可点击edit configuration 配置运行界面,的 Enviaorment variables 中查看 * <p> * 页面信息越复杂,内容越多,转换时间越慢 * * @param url 网址 * @param pdfPath 路径 */
  2. public static void WKhtmlTextToPdfBy(String url, String pdfPath) {
  3. TimeInterval timer = DateUtil.timer();
  4. if (StringUtils.isAnyBlank(url)) {
  5. throw new RrException("url参数或pdfpath参数缺少!");
  6. }
  7. String execStr = "cmd.exe /c wkhtmltopdf %s %s ";
  8. execStr = String.format(execStr, url, pdfPath);
  9. logger.info("execStr:{}", execStr);
  10. List<String> infos = RuntimeUtil.execForLines(execStr);
  11. infos.forEach(info -> logger.info("命令执行结果:{}", info));
  12. logger.info("转换耗时(毫秒):{}", timer.interval());
  13. }
  • 一些问题和解决方法

    参考:Cannot run program “xxx”: CreateProcess error=2

    在代码执行命令行时,出现错误 提示 wkhtmltopdf 不是内部命令,外部命令。

    我以为是配置的全局变量没有生效,后来发现好像是因为win10的原因,使用 powershell 执行的命令。使用cmd 命令窗口就没有问题。 所以在执行命令行,加入

    1. # /c 是执行命令完成后关闭命令行窗口
    2. cmd.exe /c

    Error: Could not save image 如果出现这个错误,应该也是 powershell 导致的。

Chrome Headless

参考:Headless Chrome 入门 建议阅读

利用Chrome Headless模式,网页转PDF

介绍:

在 Chrome 59 中开始搭载 Headless Chrome。这是一种在无需显示headless的环境下运行 Chrome 浏览器的方式。从本质上来说,就是不用 chrome 浏览器来运行 Chrome 的功能!它将 Chromium 和 Blink 渲染引擎提供的所有现代 Web 平台的功能都带入了命令行。

无需显示headless的浏览器对于自动化测试和不需要可视化 UI 界面的服务器环境是一个很好的工具。例如,你可能需要对真实的网页运行一些测试,创建一个 PDF,或者只是检查浏览器如何呈现 URL。

(其实就是使用chrome浏览器,将网页转换为pdf。当你右键点打印,可以选择另存为PDF。这个工具应该更加适用于自动化测试(Selenium WebDriver ChromeDriver))

使用方式:

  1. 下载安装goole Chrome (Mac 和 Linux 上的 Chrome 59以上版本,windows 需要 60 版本以上)
  2. 配置Chrome 为全局的环境变量,方便使用(自行百度)
  3. 代码调用命令行,执行转换命令

    1. # 会输出到命令行的目录
    2. chrome --headless --disable-gpu --print-to-pdf https://www.chromestatus.com/
    3. # 指定目录输出pdf
    4. chrome --headless --disable-gpu --print-to-pdf="D://xx/x.pdf" https://www.chromestatus.com/
    5. # 参数解释
    6. headless 无头模式
    7. disable-gpu 禁用gpu

代码实践:

环境:spring boot 2.2.7 jdk1.8 windows10

配置全局变量:系统-高级系统设置 将Chrome.exe 所在目录配置到 path 中

代码参考:fun.gengzi.codecopy.business.utilstest.utils.HtmlToPdfUtils

java调用cmd 执行命令,使用了工具类 hutool

  1. /** * 使用Chrome Headless 无头浏览器,完成对html转换pdf * * 需要安装 goole chrome 浏览器,版本在 60 以上,linux 在 59以上 * 调用命令实现转换 * @param url 网址 * @param pdfPath 路径 */
  2. public static void ChromhtmlTextToPdfBy(String url, String pdfPath) {
  3. TimeInterval timer = DateUtil.timer();
  4. if (StringUtils.isAnyBlank(url)) {
  5. throw new RrException("url参数或pdfpath参数缺少!");
  6. }
  7. // --window-size=800x1000
  8. // --virtual-time-budget=10000
  9. String execStr = "cmd.exe /c chrome --headless --run-all-compositor-stages-before-draw --disable-gpu --print-to-pdf=\"%s\" %s";
  10. execStr = String.format(execStr, pdfPath, url);
  11. logger.info("execStr:{}", execStr);
  12. List<String> infos = RuntimeUtil.execForLines(execStr);
  13. infos.forEach(info -> logger.info("命令执行结果:{}", info));
  14. logger.info("转换耗时(毫秒):{}", timer.interval());
  15. }

汇总

  • 针对访问url,转换pdf的。

    需要先访问url对应的页面,再渲染,再转换。如果访问url过慢,会严重影响转换速度。

  • 对于复杂页面的转换,都会出现问题,内容丢失,或者叠加。

    所以尽量简化转换网页的复杂程度。不过一般也不会对特别复杂的网页转换。或者先将页面截屏,再转pdf。
    在这里插入图片描述

发表评论

表情:
评论列表 (有 0 条评论,487人围观)

还没有评论,来说两句吧...

相关阅读

    相关 HTMLPDF

    最近又用到HTML转PDF的功能,而且需要对PDF进行定制,需要页眉页脚的 经过调查最终还是感觉wkhtmltopdf比较好,中间还经历了使用Google浏览器直接转pdf