html转pdf(总结五种方法Java)

雨点打透心脏的1/2处 2024-03-31 13:59 147阅读 0赞

html转pdf(总结五种方法Java)

Java 实现html转pdf,总结五种方法。
推荐使用wkhtmltopdf,Itext
(img-M4kXdzcT-1669887116934)(\_v\_images/20221201165346879\_22464.png)\]

方法一:使用wkhtmltopdf

1、下载插件wkhtmltopdf
https://wkhtmltopdf.org/downloads.html![(img-lzLsty7k-1669887116935)(\_v\_images/20221201163606556\_16343.png)\]][img-lzLsty7k-1669887116935_v_images_20221201163606556_16343.png]

2、本机测试
本目录下cmd进入
输入命令 wkhtmltopdf.exe ‪E:\学习文档\百度常用标签.html ‪E:\学习文档\百度常用标签.pdf
\[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fPVH6Enr-1669887116935)(\_v\_images/20221201164132812\_15695.png)\]

3、java代码实现
HtmlToPdf类

  1. import java.io.File;
  2. public class HtmlToPdf {
  3. // wkhtmltopdf在系统中的路径
  4. private static final String toPdfTool = "‪D:\\wkhtmltopdf\\bin\\wkhtmltopdf.exe";
  5. /**
  6. * html转pdf
  7. *
  8. * @param srcPath html路径,可以是硬盘上的路径,也可以是网络路径
  9. * @param destPath pdf保存路径
  10. * @return 转换成功返回true
  11. */
  12. public static boolean convert(String srcPath, String destPath,String toPdfTool){
  13. File file = new File(destPath);
  14. File parent = file.getParentFile();
  15. //如果pdf保存路径不存在,则创建路径
  16. if(!parent.exists()){
  17. parent.mkdirs();
  18. }
  19. StringBuilder cmd = new StringBuilder();
  20. cmd.append(toPdfTool);
  21. cmd.append(" ");
  22. cmd.append(" --header-line");//页眉下面的线
  23. cmd.append(" --margin-top 3cm ");//设置页面上边距 (default 10mm)
  24. // cmd.append(" --header-html file:///"+WebUtil.getServletContext().getRealPath("")+FileUtil.convertSystemFilePath("\\style\\pdf\\head.html"));// (添加一个HTML页眉,后面是网址)
  25. cmd.append(" --header-spacing 5 ");// (设置页眉和内容的距离,默认0)
  26. //cmd.append(" --footer-center (设置在中心位置的页脚内容)");//设置在中心位置的页脚内容
  27. //cmd.append(" --footer-html file:///"+WebUtil.getServletContext().getRealPath("")+FileUtil.convertSystemFilePath("\\style\\pdf\\foter.html"));// (添加一个HTML页脚,后面是网址)
  28. cmd.append(" --footer-line");//* 显示一条线在页脚内容上)
  29. cmd.append(" --footer-spacing 5 ");// (设置页脚和内容的距离)
  30. cmd.append(srcPath);
  31. cmd.append(" ");
  32. cmd.append(destPath);
  33. boolean result = true;
  34. try{
  35. Process proc = Runtime.getRuntime().exec(cmd.toString());
  36. HtmlToPdfInterceptor error = new HtmlToPdfInterceptor(proc.getErrorStream());
  37. HtmlToPdfInterceptor output = new HtmlToPdfInterceptor(proc.getInputStream());
  38. error.start();
  39. output.start();
  40. proc.waitFor();
  41. }catch(Exception e){
  42. result = false;
  43. e.printStackTrace();
  44. }
  45. return result;
  46. }
  47. public static void main(String[] args) {
  48. String sourcePath = "https://blog.csdn.net/weixin_43981813/article/details/127895442?spm=1001.2014.3001.5502";
  49. HtmlToPdf.convert(sourcePath, "D:\\testpdf.pdf",toPdfTool);
  50. System.out.println("0000");
  51. }
  52. }

HtmlToPdfInterceptor类

  1. import java.io.BufferedReader;
  2. import java.io.IOException;
  3. import java.io.InputStream;
  4. import java.io.InputStreamReader;
  5. public class HtmlToPdfInterceptor extends Thread {
  6. private InputStream is;
  7. public HtmlToPdfInterceptor(InputStream is){
  8. this.is = is;
  9. }
  10. public void run(){
  11. try{
  12. InputStreamReader isr = new InputStreamReader(is, "utf-8");
  13. BufferedReader br = new BufferedReader(isr);
  14. String line = null;
  15. while ((line = br.readLine()) != null) {
  16. System.out.println(line.toString()); //输出内容
  17. }
  18. }catch (IOException e){
  19. e.printStackTrace();
  20. }
  21. }
  22. }

linux版本参考: https://blog.csdn.net/weixin\_43981813/article/details/128257492

方法二:使用Itext

1、引入依赖

  1. <!-- itext7html转pdf -->
  2. <dependency>
  3. <groupId>com.itextpdf</groupId>
  4. <artifactId>html2pdf</artifactId>
  5. <version>3.0.2</version>
  6. </dependency>
  7. <!-- 中文字体支持 -->
  8. <dependency>
  9. <groupId>com.itextpdf</groupId>
  10. <artifactId>font-asian</artifactId>
  11. <version>7.1.13</version>
  12. </dependency>

2、解决水印和页码
只需实现实现com.itextpdf.kernel.events.IEventHandler接口即可

  1. /**
  2. * 水印
  3. */
  4. public class WaterMarkEventHandler implements IEventHandler {
  5. /**
  6. * 水印内容
  7. */
  8. private String waterMarkContent;
  9. /**
  10. * 一页中有几列水印
  11. */
  12. private int waterMarkX;
  13. /**
  14. * 一页中每列有多少水印
  15. */
  16. private int waterMarkY;
  17. public WaterMarkEventHandler(String waterMarkContent) {
  18. this(waterMarkContent, 5, 5);
  19. }
  20. public WaterMarkEventHandler(String waterMarkContent, int waterMarkX, int waterMarkY) {
  21. this.waterMarkContent = waterMarkContent;
  22. this.waterMarkX = waterMarkX;
  23. this.waterMarkY = waterMarkY;
  24. }
  25. @Override
  26. public void handleEvent(Event event) {
  27. PdfDocumentEvent documentEvent = (PdfDocumentEvent) event;
  28. PdfDocument document = documentEvent.getDocument();
  29. PdfPage page = documentEvent.getPage();
  30. Rectangle pageSize = page.getPageSize();
  31. PdfFont pdfFont = null;
  32. try {
  33. pdfFont = PdfFontFactory.createFont("STSongStd-Light", "UniGB-UCS2-H", false);
  34. } catch (IOException e) {
  35. e.printStackTrace();
  36. }
  37. PdfCanvas pdfCanvas = new PdfCanvas(page.newContentStreamAfter(), page.getResources(), document);
  38. Paragraph waterMark = new Paragraph(waterMarkContent).setOpacity(0.5f);
  39. Canvas canvas = new Canvas(pdfCanvas, pageSize)
  40. .setFontColor(WebColors.getRGBColor("lightgray"))
  41. .setFontSize(16)
  42. .setFont(pdfFont);
  43. for (int i = 0; i < waterMarkX; i++) {
  44. for (int j = 0; j < waterMarkY; j++) {
  45. canvas.showTextAligned(waterMark, (150 + i * 300), (160 + j * 150), document.getNumberOfPages(), TextAlignment.CENTER, VerticalAlignment.BOTTOM, 120);
  46. }
  47. }
  48. canvas.close();
  49. }
  50. }
  51. /**
  52. * 页码
  53. */
  54. public class PageEventHandler implements IEventHandler {
  55. @Override
  56. public void handleEvent(Event event) {
  57. PdfDocumentEvent documentEvent = (PdfDocumentEvent) event;
  58. PdfDocument document = documentEvent.getDocument();
  59. PdfPage page = documentEvent.getPage();
  60. Rectangle pageSize = page.getPageSize();
  61. PdfFont pdfFont = null;
  62. try {
  63. pdfFont = PdfFontFactory.createFont("STSongStd-Light", "UniGB-UCS2-H", false);
  64. } catch (IOException e) {
  65. e.printStackTrace();
  66. }
  67. PdfCanvas pdfCanvas = new PdfCanvas(page.getLastContentStream(), page.getResources(), document);
  68. Canvas canvas = new Canvas(pdfCanvas, pageSize);
  69. float x = (pageSize.getLeft() + pageSize.getRight()) / 2;
  70. float y = pageSize.getBottom() + 15;
  71. Paragraph paragraph = new Paragraph("第" + document.getPageNumber(page) + "页/共" + document.getNumberOfPages() + "页")
  72. .setFontSize(10)
  73. .setFont(pdfFont);
  74. canvas.showTextAligned(paragraph, x, y, TextAlignment.CENTER);
  75. canvas.close();
  76. }
  77. }

3、转换工具类

  1. /**
  2. * Itext7转换工具类
  3. */
  4. @Slf4j
  5. public class HtmlToPdfUtils {
  6. /**
  7. * html转pdf
  8. *
  9. * @param inputStream 输入流
  10. * @param waterMark 水印
  11. * @param fontPath 字体路径,ttc后缀的字体需要添加<b>,0<b/>
  12. * @param outputStream 输出流
  13. * @date : 2022/11/15 14:07
  14. */
  15. public static void convertToPdf(InputStream inputStream, String waterMark, String fontPath, OutputStream outputStream) throws IOException {
  16. PdfWriter pdfWriter = new PdfWriter(outputStream);
  17. PdfDocument pdfDocument = new PdfDocument(pdfWriter);
  18. //设置为A4大小
  19. pdfDocument.setDefaultPageSize(PageSize.A4);
  20. //添加水印
  21. pdfDocument.addEventHandler(PdfDocumentEvent.END_PAGE, new WaterMarkEventHandler(waterMark));
  22. //添加中文字体支持
  23. ConverterProperties properties = new ConverterProperties();
  24. FontProvider fontProvider = new FontProvider();
  25. // 设置字体
  26. /*PdfFont sysFont = PdfFontFactory.createFont("STSongStd-Light", "UniGB-UCS2-H", false);
  27. fontProvider.addFont(sysFont.getFontProgram(), "UniGB-UCS2-H");*/
  28. //添加自定义字体,例如微软雅黑
  29. if (StringUtils.isNotBlank(fontPath)) {
  30. PdfFont microsoft = PdfFontFactory.createFont(fontPath, PdfEncodings.IDENTITY_H, false);
  31. fontProvider.addFont(microsoft.getFontProgram(), PdfEncodings.IDENTITY_H);
  32. }
  33. properties.setFontProvider(fontProvider);
  34. // 读取Html文件流,查找出当中的 或出现类似的符号空格字符
  35. inputStream = readInputStrem(inputStream);
  36. if (inputStream != null) {
  37. // 生成pdf文档
  38. HtmlConverter.convertToPdf(inputStream, pdfDocument, properties);
  39. pdfWriter.close();
  40. pdfDocument.close();
  41. return;
  42. } else {
  43. log.error("转换失败!");
  44. }
  45. }
  46. /**
  47. * 读取HTML 流文件,并查询当中的 或类似符号直接替换为空格
  48. *
  49. * @param inputStream
  50. * @return
  51. */
  52. private static InputStream readInputStrem(InputStream inputStream) {
  53. // 定义一些特殊字符的正则表达式 如:
  54. String regEx_special = "\\&[a-zA-Z]{1,10};";
  55. try {
  56. //<1>创建字节数组输出流,用来输出读取到的内容
  57. ByteArrayOutputStream baos = new ByteArrayOutputStream();
  58. //<2>创建缓存大小
  59. byte[] buffer = new byte[1024]; // 1KB
  60. //每次读取到内容的长度
  61. int len = -1;
  62. //<3>开始读取输入流中的内容
  63. while ((len = inputStream.read(buffer)) != -1) { //当等于-1说明没有数据可以读取了
  64. baos.write(buffer, 0, len); //把读取到的内容写到输出流中
  65. }
  66. //<4> 把字节数组转换为字符串
  67. String content = baos.toString();
  68. //<5>关闭输入流和输出流
  69. // inputStream.close();
  70. baos.close();
  71. // log.info("读取的内容:{}", content);
  72. // 判断HTML内容是否具有HTML的特殊字符标记
  73. Pattern compile = Pattern.compile(regEx_special, Pattern.CASE_INSENSITIVE);
  74. Matcher matcher = compile.matcher(content);
  75. String replaceAll = matcher.replaceAll("");
  76. // log.info("替换后的内容:{}", replaceAll);
  77. // 将字符串转化为输入流返回
  78. InputStream stringStream = getStringStream(replaceAll);
  79. //<6>返回结果
  80. return stringStream;
  81. } catch (Exception e) {
  82. e.printStackTrace();
  83. log.error("错误信息:{}", e.getMessage());
  84. return null;
  85. }
  86. }
  87. /**
  88. * 将一个字符串转化为输入流
  89. * @param sInputString 字符串
  90. * @return
  91. */
  92. public static InputStream getStringStream(String sInputString) {
  93. if (sInputString != null && !sInputString.trim().equals("")) {
  94. try {
  95. ByteArrayInputStream tInputStringStream = new ByteArrayInputStream(sInputString.getBytes());
  96. return tInputStringStream;
  97. } catch (Exception e) {
  98. e.printStackTrace();
  99. }
  100. }
  101. return null;
  102. }
  103. }

4、测试

  1. @Slf4j
  2. public class Test {
  3. public static void main(String[] args) throws IOException {
  4. long startTime = System.currentTimeMillis();
  5. // html文件所在相对路径
  6. String htmlFile = "src/main/resources/html/index2.html";
  7. // pdf文件存储相对路径
  8. String pdfFile = "src/main/resources/x6.pdf";
  9. // 自定义水印
  10. String waterMarkText = "";
  11. InputStream inputStream = new FileInputStream(htmlFile);
  12. OutputStream outputStream = new FileOutputStream(pdfFile);
  13. //微软雅黑在windows系统里的位置如下,linux系统直接拷贝该文件放在linux目录下即可
  14. // String fontPath = "src/main/resources/font/STHeiti Light.ttc,0";
  15. String fontPath = "src/main/resources/font/simsun.ttc,0";
  16. HtmlToPdfUtils.convertToPdf(inputStream, waterMarkText, fontPath, outputStream);
  17. log.info("转换结束,耗时:{}ms",System.currentTimeMillis()-startTime);
  18. }
  19. }

5、注意事项

  • 页面中不能出现html的特殊字符标记,如 等(代码中已经处理,所有都替换为空)可忽略
  • 页面中的图片路径,必须是在项目根路径后面的所有地址(相对路径)
  • 页面中的标签要符合规范,必须都具有结束标签等

方法三:使用Spire.Doc

  1. 将文档从一种格式转换为另一种格式是Spire.Doc的主要功能之一。这种转换只不过是加载和保存操作的组合。因此,使用Spire.DOC可以将文档从任何受支持的加载格式转换为任何受支持的保存格式。
  2. spire.doc分为商业版和免费版,免费版只支持转换前3页,以免费版为例

1、增加一个maven仓库路径

  1. <repositories>
  2. <repository>
  3. <id>com.e-iceblue</id>
  4. <url>http://repo.e-iceblue.cn/repository/maven-public/</url>
  5. </repository>
  6. </repositories>

依赖

  1. <!-- 免费版,只支持前三页转化 -->
  2. <dependency>
  3. <groupId>e-iceblue</groupId>
  4. <artifactId>spire.doc.free</artifactId>
  5. <version>3.9.0</version>
  6. </dependency>

2、转换工具类

  1. /**
  2. * @Author:lzh
  3. * @Create:2022/11/19/18:04
  4. * @Description:html转换pdf
  5. * @Version:1.0
  6. */
  7. public class Html3Pdf {
  8. public static void main(String[] args) throws IOException {
  9. }
  10. /**
  11. * 免费版,只支持前三页转换
  12. * @param inputHtml HTML地址
  13. * @param pdfName pdf保存地址
  14. * @throws IOException
  15. */
  16. public void spireDoc(String inputHtml,String pdfName) throws IOException {
  17. inputHtml = "src/main/resources/html/index2.html";
  18. //新建Document对象
  19. Document doc = new Document();
  20. //添加section
  21. Section sec = doc.addSection();
  22. // 将html转化为流字符串
  23. String htmlText = readTextFromFile(inputHtml);
  24. //添加段落并写入HTML文本
  25. sec.addParagraph().appendHTML(htmlText);
  26. pdfName = "src/main/resources/x4.pdf";
  27. //将文档另存为PDF
  28. doc.saveToFile(pdfName, FileFormat.PDF);
  29. doc.dispose();
  30. }
  31. /**
  32. * 将该路径的HTML页面转化为流字符串
  33. * @param fileName 文件地址
  34. * @return
  35. * @throws IOException
  36. */
  37. public static String readTextFromFile(String fileName) throws IOException {
  38. StringBuffer sb = new StringBuffer();
  39. BufferedReader br = new BufferedReader(new FileReader(fileName));
  40. String content;
  41. while ((content = br.readLine()) != null) {
  42. sb.append(content);
  43. }
  44. return sb.toString();
  45. }
  46. }

可参考:https://blog.csdn.net/csdnerM/article/details/120649237

方法四:使用Flying Sauser(技术老旧,对样式不支持)

Flying Sauser实现html2pdf,纠错能力差,支持中文、支持简单的页面和样式,开源
对html代码要求很严格。极易出现中文乱码问题

实现:

  1. public class Html2Pdf {
  2. /**
  3. * HTML代码转PDF文档
  4. *
  5. * @param content 待转换的HTML代码
  6. * @param storagePath 保存为PDF文件的路径
  7. */
  8. public static void parsePdf(String content, String storagePath) {
  9. FileOutputStream os = null;
  10. try {
  11. File file = new File(storagePath);
  12. if(!file.exists()) {
  13. file.createNewFile();
  14. }
  15. os = new FileOutputStream(file);
  16. ITextRenderer renderer = new ITextRenderer();
  17. //解决中文支持问题
  18. // ITextFontResolver resolver = renderer.getFontResolver();
  19. // resolver.addFont("simhei.ttf", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
  20. // resolver.addFont("simhei.ttf", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
  21. renderer.setDocumentFromString(content);
  22. // 解决图片的相对路径问题,图片路径必须以file开头
  23. // renderer.getSharedContext().setBaseURL("file:/");
  24. renderer.layout();
  25. renderer.createPDF(os);
  26. } catch (DocumentException e) {
  27. e.printStackTrace();
  28. } catch (IOException e) {
  29. e.printStackTrace();
  30. }finally {
  31. if(null != os) {
  32. try {
  33. os.close();
  34. } catch (IOException e) {
  35. e.printStackTrace();
  36. }
  37. }
  38. }
  39. }
  40. /**
  41. * 对Html要求特别严格
  42. * @param args
  43. * @throws IOException
  44. */
  45. public static void main(String[] args) throws IOException {
  46. String htmlFilePath = "";
  47. htmlFilePath = "F:/pdf/IText实现对PDF文档属性的基本设置 - 半亩池光 - 博客园.html";
  48. StringBuilder content = new StringBuilder();
  49. BufferedInputStream in;
  50. byte[] bys = new byte[1024];
  51. int len;
  52. in = new BufferedInputStream(new FileInputStream(htmlFilePath));
  53. while ((len = in.read(bys)) != -1) {
  54. content.append(new String(bys, 0, len));
  55. }
  56. String html = closeHTML(content.toString());
  57. html = html.replace(" "," ");
  58. parsePdf(html,"F:/pdf/wahaha.pdf");
  59. }
  60. public static String closeHTML(String str){
  61. List arrTags = new ArrayList();
  62. arrTags.add("br");
  63. arrTags.add("hr");
  64. arrTags.add("link");
  65. arrTags.add("meta");
  66. arrTags.add("img");
  67. arrTags.add("input");
  68. for(int i=0;i<arrTags.size();i++){
  69. for(int j=0;j<str.length();){
  70. int tagStart = str.indexOf("<"+arrTags.get(i),j);
  71. if(tagStart>=0){
  72. int tagEnd = str.indexOf(">",tagStart);
  73. j = tagEnd;
  74. String preCloseTag = str.substring(tagEnd-1,tagEnd);
  75. if(!"/".equals(preCloseTag)){
  76. String preStr = str.substring(0,tagEnd);
  77. String afterStr = str.substring(tagEnd);
  78. str = preStr + "/" + afterStr;
  79. }
  80. }else{
  81. break;
  82. }
  83. }
  84. }
  85. return str;
  86. }
  87. }

方法五:使用PD4ML(样式有问题)

PD4ML是纯Java的类库,使用HTML、CSS作为页面布局和内容定义格式来生成PDF文档的强大工具,可以简化最终用户生成PDF的工作。参考网站:http://www.pd4ml.com
可参考:https://github.com/linkamnal/Html2Pdf
工具类:

  1. public class HtmlToPDFUtil {
  2. public static void main(String[] args) throws Exception {
  3. //HtmlToPDFUtil htmlToPDFUtil = new HtmlToPDFUtil();
  4. HtmlToPDFUtil.generatePDF_2(new File("F:\pdf/demo_ch_pd4ml.pdf"),
  5. "F:\pdf/flying saucer 使用中的一些问题 (java导出pdf) - 真的勇士,敢于直面这扯淡的人生 - ITeye博客.htm");
  6. //File pdfFile = new File("D:/Test/test3.pdf");
  7. // String pdfPath = "D:/Test1/mmt";
  8. //
  9. // File file = new File(pdfPath);
  10. // if (!file.exists()) {
  11. // file.mkdirs();
  12. // }
  13. // String pdfName = "aa.pdf";
  14. // File pdfFile = new File(pdfPath+File.separator+pdfName);
  15. // StringBuffer html = new StringBuffer();
  16. // html.append("<html>")
  17. // .append("<head>")
  18. // .append("<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />")
  19. // .append("</head>").append("<body>")
  20. // //.append("<font face='KaiTi_GB2312'>")
  21. // .append("<font face='KaiTi'>")
  22. // .append("<font color='red' size=22>显示中文aaaaaaaaaa</font>")
  23. // .append("</font>").append("</body></html>");
  24. // StringReader strReader = new StringReader(html.toString());
  25. // HtmlToPDFUtil.generatePDF_1(pdfFile, strReader);
  26. }
  27. // 手动构造HTML代码
  28. public static void generatePDF_1(File outputPDFFile, StringReader strReader)
  29. throws Exception {
  30. FileOutputStream fos = new FileOutputStream(outputPDFFile);
  31. PD4ML pd4ml = new PD4ML();
  32. pd4ml.setPageInsets(new Insets(20, 10, 10, 10));
  33. pd4ml.setHtmlWidth(950);
  34. pd4ml.setPageSize(pd4ml.changePageOrientation(PD4Constants.A4));
  35. pd4ml.useTTF("java:fonts", true);
  36. //pd4ml.setDefaultTTFs("KaiTi_GB2312", "KaiTi_GB2312", "KaiTi_GB2312");
  37. pd4ml.setDefaultTTFs("KaiTi", "KaiTi", "KaiTi");
  38. pd4ml.enableDebugInfo();
  39. pd4ml.render(strReader, fos);
  40. }
  41. // HTML代码来自于HTML文件
  42. public static void generatePDF_2(File outputPDFFile, String inputHTMLFileName)
  43. throws Exception {
  44. FileOutputStream fos = new FileOutputStream(outputPDFFile);
  45. PD4ML pd4ml = new PD4ML();
  46. pd4ml.setPageInsets(new Insets(20, 10, 10, 10));
  47. pd4ml.setHtmlWidth(950);
  48. pd4ml.setPageSize(pd4ml.changePageOrientation(PD4Constants.A4));
  49. pd4ml.useTTF("java:fonts", true);
  50. pd4ml.setDefaultTTFs("KaiTi", "KaiTi", "KaiTi");
  51. pd4ml.enableDebugInfo();
  52. pd4ml.render("file:" + inputHTMLFileName, fos);
  53. }
  54. }

发表评论

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

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

相关阅读

    相关 HTMLPDF

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