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

Java 实现html转pdf,总结五种方法。
推荐使用wkhtmltopdf,Itext

方法一:使用wkhtmltopdf

1、下载插件wkhtmltopdf
https://wkhtmltopdf.org/downloads.html

2、本机测试
本目录下cmd进入
输入命令 wkhtmltopdf.exe ‪E:\学习文档\百度常用标签.html ‪E:\学习文档\百度常用标签.pdf

3、java代码实现
HtmlToPdf类

import java.io.File; public class HtmlToPdf { //wkhtmltopdf在系统中的路径private static final String toPdfTool = "‪D:\\wkhtmltopdf\\bin\\wkhtmltopdf.exe"; /** * html转pdf * * @param srcPathhtml路径,可以是硬盘上的路径,也可以是网络路径 * @param destPath pdf保存路径 * @return 转换成功返回true */public static boolean convert(String srcPath, String destPath,String toPdfTool){File file = new File(destPath);File parent = file.getParentFile();//如果pdf保存路径不存在,则创建路径if(!parent.exists()){parent.mkdirs();}StringBuilder cmd = new StringBuilder();cmd.append(toPdfTool);cmd.append(" ");cmd.append(" --header-line");//页眉下面的线cmd.append(" --margin-top 3cm ");//设置页面上边距 (default 10mm)// cmd.append(" --header-html file:///"+WebUtil.getServletContext().getRealPath("")+FileUtil.convertSystemFilePath("\\style\\pdf\\head.html"));// (添加一个HTML页眉,后面是网址)cmd.append(" --header-spacing 5 ");// (设置页眉和内容的距离,默认0)//cmd.append(" --footer-center (设置在中心位置的页脚内容)");//设置在中心位置的页脚内容//cmd.append(" --footer-html file:///"+WebUtil.getServletContext().getRealPath("")+FileUtil.convertSystemFilePath("\\style\\pdf\\foter.html"));// (添加一个HTML页脚,后面是网址)cmd.append(" --footer-line");//* 显示一条线在页脚内容上)cmd.append(" --footer-spacing 5 ");// (设置页脚和内容的距离)cmd.append(srcPath);cmd.append(" ");cmd.append(destPath);boolean result = true;try{Process proc = Runtime.getRuntime().exec(cmd.toString());HtmlToPdfInterceptor error = new HtmlToPdfInterceptor(proc.getErrorStream());HtmlToPdfInterceptor output = new HtmlToPdfInterceptor(proc.getInputStream());error.start();output.start();proc.waitFor();}catch(Exception e){result = false;e.printStackTrace();} return result;}public static void main(String[] args) {String sourcePath = "https://blog.csdn.net/weixin_43981813/article/details/127895442" />

HtmlToPdfInterceptor类

import java.io.BufferedReader;import java.io.IOException;import java.io.InputStream;import java.io.InputStreamReader; public class HtmlToPdfInterceptor extends Thread {private InputStream is; public HtmlToPdfInterceptor(InputStream is){this.is = is;} public void run(){try{InputStreamReader isr = new InputStreamReader(is, "utf-8");BufferedReader br = new BufferedReader(isr);String line = null;while ((line = br.readLine()) != null) {System.out.println(line.toString()); //输出内容}}catch (IOException e){e.printStackTrace();}}}

linux版本参考: https://blog.csdn.net/weixin_43981813/article/details/128257492

方法二:使用Itext

1、引入依赖

com.itextpdfhtml2pdf3.0.2com.itextpdffont-asian7.1.13

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

/** * 水印 */public class WaterMarkEventHandler implements IEventHandler {/** * 水印内容 */private String waterMarkContent;/** * 一页中有几列水印 */private int waterMarkX;/** * 一页中每列有多少水印 */private int waterMarkY;public WaterMarkEventHandler(String waterMarkContent) {this(waterMarkContent, 5, 5);}public WaterMarkEventHandler(String waterMarkContent, int waterMarkX, int waterMarkY) {this.waterMarkContent = waterMarkContent;this.waterMarkX = waterMarkX;this.waterMarkY = waterMarkY;}@Overridepublic void handleEvent(Event event) {PdfDocumentEvent documentEvent = (PdfDocumentEvent) event;PdfDocument document = documentEvent.getDocument();PdfPage page = documentEvent.getPage();Rectangle pageSize = page.getPageSize();PdfFont pdfFont = null;try {pdfFont = PdfFontFactory.createFont("STSongStd-Light", "UniGB-UCS2-H", false);} catch (IOException e) {e.printStackTrace();}PdfCanvas pdfCanvas = new PdfCanvas(page.newContentStreamAfter(), page.getResources(), document);Paragraph waterMark = new Paragraph(waterMarkContent).setOpacity(0.5f);Canvas canvas = new Canvas(pdfCanvas, pageSize).setFontColor(WebColors.getRGBColor("lightgray")).setFontSize(16).setFont(pdfFont);for (int i = 0; i < waterMarkX; i++) {for (int j = 0; j < waterMarkY; j++) {canvas.showTextAligned(waterMark, (150 + i * 300), (160 + j * 150), document.getNumberOfPages(), TextAlignment.CENTER, VerticalAlignment.BOTTOM, 120);}}canvas.close();}}
/** * 页码 */public class PageEventHandler implements IEventHandler {@Overridepublic void handleEvent(Event event) {PdfDocumentEvent documentEvent = (PdfDocumentEvent) event;PdfDocument document = documentEvent.getDocument();PdfPage page = documentEvent.getPage();Rectangle pageSize = page.getPageSize();PdfFont pdfFont = null;try {pdfFont = PdfFontFactory.createFont("STSongStd-Light", "UniGB-UCS2-H", false);} catch (IOException e) {e.printStackTrace();}PdfCanvas pdfCanvas = new PdfCanvas(page.getLastContentStream(), page.getResources(), document);Canvas canvas = new Canvas(pdfCanvas, pageSize);floatx = (pageSize.getLeft() + pageSize.getRight()) / 2;floaty = pageSize.getBottom() + 15;Paragraph paragraph = new Paragraph("第" + document.getPageNumber(page) + "页/共" + document.getNumberOfPages() + "页").setFontSize(10).setFont(pdfFont);canvas.showTextAligned(paragraph, x, y, TextAlignment.CENTER);canvas.close();}}

3、转换工具类

/** * Itext7转换工具类 */@Slf4jpublic class HtmlToPdfUtils {/** * html转pdf * * @param inputStream输入流 * @param waterMark水印 * @param fontPath 字体路径,ttc后缀的字体需要添加,0 * @param outputStream 输出流 * @date : 2022/11/15 14:07 */public static void convertToPdf(InputStream inputStream, String waterMark, String fontPath, OutputStream outputStream) throws IOException {PdfWriter pdfWriter = new PdfWriter(outputStream);PdfDocument pdfDocument = new PdfDocument(pdfWriter);//设置为A4大小pdfDocument.setDefaultPageSize(PageSize.A4);//添加水印pdfDocument.addEventHandler(PdfDocumentEvent.END_PAGE, new WaterMarkEventHandler(waterMark));//添加中文字体支持ConverterProperties properties = new ConverterProperties();FontProvider fontProvider = new FontProvider();//设置字体/*PdfFont sysFont = PdfFontFactory.createFont("STSongStd-Light", "UniGB-UCS2-H", false);fontProvider.addFont(sysFont.getFontProgram(), "UniGB-UCS2-H");*///添加自定义字体,例如微软雅黑if (StringUtils.isNotBlank(fontPath)) {PdfFont microsoft = PdfFontFactory.createFont(fontPath, PdfEncodings.IDENTITY_H, false);fontProvider.addFont(microsoft.getFontProgram(), PdfEncodings.IDENTITY_H);}properties.setFontProvider(fontProvider);//读取Html文件流,查找出当中的或出现类似的符号空格字符inputStream = readInputStrem(inputStream);if (inputStream != null) {//生成pdf文档HtmlConverter.convertToPdf(inputStream, pdfDocument, properties);pdfWriter.close();pdfDocument.close();return;} else {log.error("转换失败!");}}/** * 读取HTML 流文件,并查询当中的或类似符号直接替换为空格 * * @param inputStream * @return */private static InputStream readInputStrem(InputStream inputStream) {// 定义一些特殊字符的正则表达式 如:String regEx_special = "\\&[a-zA-Z]{1,10};";try {//创建字节数组输出流,用来输出读取到的内容ByteArrayOutputStream baos = new ByteArrayOutputStream();//创建缓存大小byte[] buffer = new byte[1024]; // 1KB//每次读取到内容的长度int len = -1;//开始读取输入流中的内容while ((len = inputStream.read(buffer)) != -1) { //当等于-1说明没有数据可以读取了baos.write(buffer, 0, len); //把读取到的内容写到输出流中}// 把字节数组转换为字符串String content = baos.toString();//关闭输入流和输出流//inputStream.close();baos.close();//log.info("读取的内容:{}", content);//判断HTML内容是否具有HTML的特殊字符标记Pattern compile = Pattern.compile(regEx_special, Pattern.CASE_INSENSITIVE);Matcher matcher = compile.matcher(content);String replaceAll = matcher.replaceAll("");//log.info("替换后的内容:{}", replaceAll);//将字符串转化为输入流返回InputStream stringStream = getStringStream(replaceAll);//返回结果return stringStream;} catch (Exception e) {e.printStackTrace();log.error("错误信息:{}", e.getMessage());return null;}}/** * 将一个字符串转化为输入流 * @param sInputString 字符串 * @return */public static InputStream getStringStream(String sInputString) {if (sInputString != null && !sInputString.trim().equals("")) {try {ByteArrayInputStream tInputStringStream = new ByteArrayInputStream(sInputString.getBytes());return tInputStringStream;} catch (Exception e) {e.printStackTrace();}}return null;}}

4、测试

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

5、注意事项

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

方法三:使用Spire.Doc

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

1、增加一个maven仓库路径

com.e-icebluehttp://repo.e-iceblue.cn/repository/maven-public/

依赖

e-icebluespire.doc.free3.9.0

2、转换工具类

/** * @Author:lzh * @Create:2022/11/19/18:04 * @Description:html转换pdf * @Version:1.0 */public class Html3Pdf {public static void main(String[] args) throws IOException {}/** * 免费版,只支持前三页转换 * @param inputHtml HTML地址 * @param pdfName pdf保存地址 * @throws IOException */public voidspireDoc(String inputHtml,String pdfName) throws IOException {inputHtml = "src/main/resources/html/index2.html";//新建Document对象Document doc = new Document();//添加sectionSection sec = doc.addSection();//将html转化为流字符串String htmlText = readTextFromFile(inputHtml);//添加段落并写入HTML文本sec.addParagraph().appendHTML(htmlText);pdfName = "src/main/resources/x4.pdf";//将文档另存为PDFdoc.saveToFile(pdfName, FileFormat.PDF);doc.dispose();}/** * 将该路径的HTML页面转化为流字符串 * @param fileName 文件地址 * @return * @throws IOException */public static String readTextFromFile(String fileName) throws IOException {StringBuffer sb = new StringBuffer();BufferedReader br = new BufferedReader(new FileReader(fileName));String content;while ((content = br.readLine()) != null) {sb.append(content);}return sb.toString();}}

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

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

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

实现:

public class Html2Pdf { /*** HTML代码转PDF文档** @param content 待转换的HTML代码* @param storagePath 保存为PDF文件的路径*/ public static void parsePdf(String content, String storagePath) { FileOutputStream os = null; try { File file = new File(storagePath); if(!file.exists()) { file.createNewFile(); } os = new FileOutputStream(file); ITextRenderer renderer = new ITextRenderer(); //解决中文支持问题 //ITextFontResolver resolver = renderer.getFontResolver(); //resolver.addFont("simhei.ttf", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED); //resolver.addFont("simhei.ttf", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED); renderer.setDocumentFromString(content); // 解决图片的相对路径问题,图片路径必须以file开头 // renderer.getSharedContext().setBaseURL("file:/"); renderer.layout(); renderer.createPDF(os); } catch (DocumentException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); }finally { if(null != os) { try { os.close(); } catch (IOException e) { e.printStackTrace(); } } } } /*** 对Html要求特别严格* @param args* @throws IOException*/ public static void main(String[] args) throws IOException { String htmlFilePath = ""; htmlFilePath = "F:/pdf/IText实现对PDF文档属性的基本设置 - 半亩池光 - 博客园.html"; StringBuilder content = new StringBuilder(); BufferedInputStream in; byte[] bys = new byte[1024]; int len; in = new BufferedInputStream(new FileInputStream(htmlFilePath)); while ((len = in.read(bys)) != -1) { content.append(new String(bys, 0, len)); } String html = closeHTML(content.toString()); html = html.replace(" "," "); parsePdf(html,"F:/pdf/wahaha.pdf"); } public static String closeHTML(String str){ List arrTags = new ArrayList(); arrTags.add("br"); arrTags.add("hr"); arrTags.add("link"); arrTags.add("meta"); arrTags.add("img"); arrTags.add("input"); for(int i=0;i=0){ int tagEnd = str.indexOf(">",tagStart); j = tagEnd; String preCloseTag = str.substring(tagEnd-1,tagEnd); if(!"/".equals(preCloseTag)){ String preStr = str.substring(0,tagEnd); String afterStr = str.substring(tagEnd); str = preStr + "/" + afterStr; } }else{ break; } } } return str; } }

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

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

public class HtmlToPDFUtil { public static void main(String[] args) throws Exception { //HtmlToPDFUtil htmlToPDFUtil = new HtmlToPDFUtil(); HtmlToPDFUtil.generatePDF_2(new File("F:\pdf/demo_ch_pd4ml.pdf"), "F:\pdf/flying saucer 使用中的一些问题 (java导出pdf) - 真的勇士,敢于直面这扯淡的人生 - ITeye博客.htm"); //File pdfFile = new File("D:/Test/test3.pdf"); //String pdfPath = "D:/Test1/mmt"; // //File file = new File(pdfPath); //if (!file.exists()) { //file.mkdirs(); //} //String pdfName = "aa.pdf"; //File pdfFile = new File(pdfPath+File.separator+pdfName); //StringBuffer html = new StringBuffer(); //html.append("") //.append("") //.append("") //.append("").append("") ////.append("") //.append("") //.append("显示中文aaaaaaaaaa") //.append("").append(""); //StringReader strReader = new StringReader(html.toString()); //HtmlToPDFUtil.generatePDF_1(pdfFile, strReader); } // 手动构造HTML代码 public static void generatePDF_1(File outputPDFFile, StringReader strReader) throws Exception { FileOutputStream fos = new FileOutputStream(outputPDFFile); PD4ML pd4ml = new PD4ML(); pd4ml.setPageInsets(new Insets(20, 10, 10, 10)); pd4ml.setHtmlWidth(950); pd4ml.setPageSize(pd4ml.changePageOrientation(PD4Constants.A4)); pd4ml.useTTF("java:fonts", true); //pd4ml.setDefaultTTFs("KaiTi_GB2312", "KaiTi_GB2312", "KaiTi_GB2312"); pd4ml.setDefaultTTFs("KaiTi", "KaiTi", "KaiTi"); pd4ml.enableDebugInfo(); pd4ml.render(strReader, fos); } // HTML代码来自于HTML文件 public static void generatePDF_2(File outputPDFFile, String inputHTMLFileName) throws Exception { FileOutputStream fos = new FileOutputStream(outputPDFFile); PD4ML pd4ml = new PD4ML(); pd4ml.setPageInsets(new Insets(20, 10, 10, 10)); pd4ml.setHtmlWidth(950); pd4ml.setPageSize(pd4ml.changePageOrientation(PD4Constants.A4)); pd4ml.useTTF("java:fonts", true); pd4ml.setDefaultTTFs("KaiTi", "KaiTi", "KaiTi"); pd4ml.enableDebugInfo(); pd4ml.render("file:" + inputHTMLFileName, fos); } }