html转换为pdf的关键技术是如何处理网页中复杂的css样式、以及中文乱码处理。css
各实现对比表
于Windows平台进行测试:html
基于ITextjava
基于FlyingSaucerlinux
基于WKHtmlToPdfcss3
基于pd4mlc++
跨平台性web
跨平台vim
跨平台windows
跨平台后端
跨平台
是否安装软件
否
否
需安装WKHtmlToPdf
否
是否收费
免费
免费
免费
收费
转换Html
效率
速度快
未测
速度慢。相比URL来讲,效率较慢。能忽略一些html语法或资源是否存在问题。
速度快。部分CSS样式不支持。
效果
存在样式失真问题。
对html语法有必定要求
存在样式失真问题。对html语法有较高要求。
失真状况较小
大部分网页能按Chome浏览器显示的页面转
部分CSS样式有问题。
转换URL
效率
未测
未测
效率不是特别高
未测
效果
未测
未测
部分网页因为其限制,或将出现html网页不完整。
未测
优势
不需安装软件、转换速度快
不需安装软件、转换速度快
生成PDF质量高
不须要安装软件、转换速度快
缺点
对html标签严格,少一个结束标签就会报错;
服务器须要安装字体
对html标签严格,少一个结束标签就会报错;
服务器须要安装字体
须要安装软件、时间效率不高
对部分CSS样式不支持。
评价
综合:使用WKHtmlToPdf效果(样式)最好。但速度较慢(对于文件来讲)。其他均有大大小小的失真问题。
分页
图片
表格
连接
中文
特殊字符
总体样式
速度
IText
支持
支持
支持
支持
支持
支持
失真问题
快
FlyingSaucer
未知
未知
未知
未知
未知
未知
未知
快
WKHtmlToPdf
支持
支持
支持
支持
支持
支持
很好
慢
pd4ml
支持
支持
支持
支持
支持
支持
失真问题
快
html网页完整转换为pdf,全部的方案均有不足。
itext有时并不能知足需求,不能兼容html的样式,且从html页面导出的图片到pdf中也并很差处理。
Flying Sauser实现html2pdf,纠错能力差,支持多种中文字体(部分样式不能识别),且对html的格式也是十分的严格,若是用一种模版的话用Flying Sauser技术却是不错的选择,但对于不规则的html导出pdf就并非那么的适用。
PD4ML实现html2pdf,速度快,纠错能力强能够过滤不规则的html标记,支持多种中文字体,支持css。
WKHtmlToPdf效果最好,但转换速度慢。
1. wkhtmltopdf(速度慢、须要安装软件)
wkhtmltopdf是一个用webkit网页渲染引擎开发的用来将html转成 pdf的工具,可跟多种脚本语言进行集成来转换文档,有windows、linux等平台版本。官网地址 http://wkhtmltopdf.org/
技术特色:
Wkhtmltopdf可直接把浏览器中浏览的网页转换成一个pdf,他是一个把html页面转换成pdf的软件(须要安装在服务器上)。使用时可经过java代码调用cmd指令完成网页转换为pdf的功能。
功能测试:
直接在cmd里输入测试指令,可查看处理进度。
原理:
使用wkhtmltopdf工具对url或html进行转换
使用命令:
Wkhtmltopdf https:baidu.com /usr/local/temp/baidu.pdf
安装
下载地址:https://wkhtmltopdf.org/downloads.html
wkhtmltopdf安装方法
1.解压wkhtmltox.tar到某个文件夹$DIR
2.设置环境变量
vim /etc/profile
在最后一行加 export PATH=$DIR/wkhtmltox/bin:$PATH 保存退出、
source /etc/profile
3.运行 wkhtmltopdf 报wkhtmltopdf: error while loading shared libraries: libXrender.so.1: cannot open shared object file: No such file or directory这个错,请运行 apt-get/yum install libXrender*
运行 wkhtmltopdf 报wkhtmltopdf: error while loading shared libraries: libfontconfig.so.1: cannot open shared object file: No such file or directory这个错,请运行apt-get/yum install libfontconfig*
运行 wkhtmltopdf 报wkhtmltopdf: error while loading shared libraries: libXext.so.6: cannot open shared object file: No such file or directory这个错,请运行 apt-get/yum install libXext*
运行 wkhtmltopdf
yum install xorg-x11-fonts-75dpi.noarch
yum install xorg-x11-fonts-Type1.noarch
yum install icu.x86_64
yum install libjpeg
yum install libpng
优势:
支持中文、图片、CSS等
缺点:
有时对于html文件的转化可能比较慢,对于url的转化速度较快。存在失真状况
具体实现:
Java调用命令。
public class HtmlToPdf {
// wkhtmltopdf在系统中的路径
private static String toPdfTool = Consts.WEB.CONVERSION_PLUGSTOOL_PATH_WINDOW;
/**
* html转pdf
*
* @param srcPath
* html路径,能够是硬盘上的路径,也能够是网络路径
* @param destPath
* pdf保存路径
* @return 转换成功返回true
*/
public static boolean convert(String srcPath, String destPath) {
File file = new File(destPath);
File parent = file.getParentFile();
// 若是pdf保存路径不存在,则建立路径
if (!parent.exists()) {
parent.mkdirs();
}
StringBuilder cmd = new StringBuilder();
if (System.getProperty("os.name").indexOf("Windows") == -1) {
// 非windows 系统
toPdfTool = Consts.WEB.CONVERSION_PLUGSTOOL_PATH_LINUX;
}
cmd.append(toPdfTool);
cmd.append(" ");
cmd.append(" \"");
cmd.append(srcPath);
cmd.append("\" ");
cmd.append(" ");
cmd.append(destPath);
System.out.println(cmd.toString());
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) {
// HtmlToPdf.convert("http://www.baidu.com", "F:/pdf/baidu.pdf");
String filename = "JAVA将图片转换成pdf文件-CSDN博客";
HtmlToPdf.convert("F:/pdf/"+filename+".html", "F:/pdf/"+filename+".pdf");
// HtmlToPdf.convert("http://api.gyingyuan.com/", "F:/pdf/"+ UUID.randomUUID().toString()+".pdf");
// HtmlToPdf.convert("https://www.aliyun.com/jiaocheng/285649.html", "F:/pdf/baidu.pdf");
}
}
public class HtmlToPdfInterceptor extends Thread {
private InputStream is;
public HtmlToPdfInterceptor(InputStream is){
this.is = is;
}
@Override
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();
}
}
}
效果:
URL转换
对于url转会遇到一些网站限制的问题。
https://blog.csdn.net/m0_38138387/article/details/79314260
若是转为html则效率较慢,但能很大程度比较完美地转换
文件转换:速度较慢,失真状况比较小
68.225s
2. PhantomJS(样式有问题,须要安装软件)
PhantomJS是一个基于webkit内核的无头浏览器,即没有UI界面,即它是一个浏览器,只是其内的点击、翻页等人为相关操做须要程序设计实现。它提供javaScript API接口,即经过编写JS程序能够直接与webkit内核交互,在此之上能够结合java语言等,经过java调用js等相关操做,从而解决了之前c/c++才能比较好的基于webkit开发优质采集器的限制。它同时提供windows、linux、mac等不一样os的安装使用包,也就是说能够在不一样平台上二次开发采集项目或是自动项目测试等工做。官网地址http://phantomjs.org/
PhantomJS可作网页分析,功能不少,本次仅调用网页的截图功能。在cmd中的测试以下:
URL转
测试效果并无wkhtmltopdf好。
html2pdf.js
var page = require('webpage').create();
var system = require('system');
////读取命令行参数,也就是js文件路径。
if (system.args.length === 1) {
console.log('Usage: loadspeed.js <some URL>');
//这行代码很重要。凡是结束必须调用。不然phantomjs不会中止
phantom.exit();
}
page.settings.loadImages = true; //加载图片
page.settings.resourceTimeout = 30000;//超过10秒放弃加载
//截图设置,
//page.viewportSize = {
// width: 1000,
// height: 3000
//};
var address = system.args[1];
page.open(address, function(status) {
function checkReadyState() {//等待加载完成将页面生成pdf
setTimeout(function () {
var readyState = page.evaluate(function () {
return document.readyState;
});
if ("complete" === readyState) {
page.paperSize = { width:'297mm',height:'500mm',orientation: 'portrait',border: '1cm' };
var timestamp = Date.parse(new Date());
var pdfname = 'HT_'+timestamp + Math.floor(Math.random()*1000000);
var outpathstr = "E:/POMFiles/HTPDF/"+pdfname+".pdf";
page.render(outpathstr);
//page.render("c://test.png");
//console.log就是传输回去的内容。
console.log("生成成功");
console.log("$"+outpathstr+"$");
phantom.exit();
} else {
checkReadyState();
}
},1000);
}
checkReadyState();
});
PhantomJS对bootstap的样式支持较好。对css3的新特性如圆形图片样式支持行很差。部分页面样式会失效。对于echart图表展现,也可直接导出
3. IText(技术老旧,对样式不支持)
iText是一个第三方报表java插件,能够在后端利用java随意生成、转化pdf文件,提供了不少api,比较灵活
IText实现html2pdf,速度快,纠错能力差,支持中文(要求HTML使用unicode编码),但中支持一种中文字体,开源。
原理:
使用IText将HTML文件转化为PDF文件
优势:
速度快,支持中文(要求HTML使用unicode编码)、开源
缺点:
纠错能力差,
对CSS样式支持不是很好。
失真状况可能比较大
具体实现:
<dependency>
<groupId>org.eclipse.birt.runtime.3_7_1</groupId>
<artifactId>com.lowagie.text</artifactId>
<version>2.1.7</version>
</dependency>
<dependency>
<groupId>org.xhtmlrenderer</groupId>
<artifactId>flying-saucer-pdf</artifactId>
<version>9.0.8</version>
</dependency>
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itextpdf</artifactId>
<version>5.4.2</version>
</dependency>
Java代码
ITextRenderer renderer = new ITextRenderer();
ITextFontResolver fontResolver = renderer.getFontResolver();
fontResolver.addFont("/Users/hehe/share/Fonts/simsun.ttc", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
OutputStream os = new FileOutputStream("/Users/hehe/Desktop/iTextPDF.pdf");
String htmlstr = HttpHandler.sendGet("http://localhost:10086/test/iTextPDF.html");//HttpHandler.sendGet只是单纯得到指定网页的html字符串内容
renderer.setDocumentFromString(htmlstr);
renderer.layout();
renderer.createPDF(os);
以上只是简单利用html字符串来生成pdf,须要注意的是:
一、若是页面中有中文,服务器端须要下载字体库simsun.ttc,在后台进行引用,同时在页面的样式中加入对应字体的定义,如:body{font-family: SimSun;},不然中文没法渲染(中文处渲染出来的效果是空白);
二、页面中若是有图片,若是图片引用是绝对路径或者base64则不用考虑,若是是相对路径,须要在后台用renderer.getSharedContext().setBaseURL("图片绝对路径目录");来指定图片路径,不然图片没法渲染。
三、要转化的页面必须是标准的XHTML页面,有一处不符合规范就会报错,小编再试的时候就常常报诸如org.xml.sax.SAXParseException;lineNumber: 24; columnNumber: 6;元素类型 "span" 必须由匹配的结束标记 "</span> 终止"之类的错误,因此若是要用iText来大量爬取网络中的页面的话,仍是放弃吧,毕竟网上不少页面都是不标准的~
public class HtmlToPdfUtils {
/*** 默认中文字体 */
private static final String FONT = "C:\\Windows\\Fonts\\simhei.ttf";
public static void htmlToPdf(String sourcePath,String tagetPath) throws IOException {
htmlToPdf(sourcePath,tagetPath,FONT);
}
public static void htmlToPdf(String sourcePath,String tagetPath,String fontPath) throws IOException {
htmlToPdf(sourcePath,tagetPath,fontPath,PageSize.TABLOID);
}
public static void htmlToPdf(String sourcePath,String tagetPath,String fontPath,PageSize pageSize) throws IOException {
// 默认source路径下装载有css、image、以及html等文件的文件夹
htmlToPdf(sourcePath,tagetPath,fontPath,pageSize,FileUtils.GetFilePath(sourcePath));
}
public static void htmlToPdf(String sourcePath,String tagetPath,String fontPath,PageSize pageSize,String baseuri) throws IOException {
PdfWriter writer = new PdfWriter(tagetPath);
PdfDocument pdf = new PdfDocument(writer);
pdf.setTagged();
// 设置pdf页面大小
pdf.setDefaultPageSize(pageSize);
ConverterProperties properties = new ConverterProperties();
FontProvider fontProvider = new DefaultFontProvider();
// 字体
FontProgram fontProgram = FontProgramFactory.createFont(fontPath);
fontProvider.addFont(fontProgram);
properties.setFontProvider(fontProvider);
//properties.setBaseUri(html);
properties.setBaseUri(baseuri);
MediaDeviceDescription mediaDeviceDescription = new MediaDeviceDescription(MediaType.SCREEN);
mediaDeviceDescription.setWidth(pageSize.getWidth());
properties.setMediaDeviceDescription(mediaDeviceDescription);
// 转化
convertToPdf(sourcePath,pdf, properties);
}
private static void convertToPdf(String sourcePath,PdfDocument pdf,ConverterProperties properties ) throws IOException {
InputStream inputStream = new FileInputStream(sourcePath);
// 转化
// HtmlConverter.convertToPdf(new FileInputStream(sourcePath), pdf, properties);
HtmlConverter.convertToPdf(inputStream, pdf, properties);
inputStream.close();
}
public static void main(String[] args) throws IOException {
htmlToPdf("F:\\pdf\\1.html","F:\\pdf\\est-04.pdf");
}
}
效果:
Converting HTML to PDF _ iText Developers.html
消耗时间:3660
CSS样式丢失:
JAVA 将图片转换成pdf文件 - CSDN博客.html
消耗时间:7609
样式一样丢失问题
itext html转pdf布局问题_百度搜索.html
消耗时间:5485
4. Flying Sauser(技术老旧,对样式不支持)
Flying Sauser实现html2pdf,纠错能力差,支持中文、支持简单的页面和样式,开源
对html代码要求很严格。极易出现中文乱码问题
优势:
支持多种中文字体(部分样式不能识别),开源
缺点:
纠错能力差,对CSS支持不是很好。当页面内容较长时,处理时间慢
具体实现:
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<arrTags.size();i++){
for(int j=0;j<str.length();){
int tagStart = str.indexOf("<"+arrTags.get(i),j);
if(tagStart>=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;
}
}
5. PD4ML(样式有问题)
PD4ML是纯Java的类库,使用HTML、CSS做为页面布局和内容定义格式来生成PDF文档的强大工具,能够简化最终用户生成PDF的工做。参考网站:http://www.pd4ml.com
优势:
支持中文、对html代码不严格、速度较快
支持的HTML标签、CSS属性较全,转换失真比较小,可使用HTML+CSS实现精确的布局控制。
对网页文件标签、CSS语法错误的容错性比较好。
对不用额外的控制,就支持图片的转化输出。
缺点:
存在样式失真问题,CSS支持较很差。
不开源,最新的demo版本,下载测试之后,发现不支持中文转换。必须购买商业版本才能够。(这里很坑,测试乱码问题通不过,后面发现是原本就不支持)。
破解后的一些旧版本能够解决乱码问题,可是支持的css样式没有新版本的全。
具体实现:
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("<html>")
// .append("<head>")
// .append("<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\" />")
// .append("</head>").append("<body>")
// //.append("<font face='KaiTi_GB2312'>")
// .append("<font face='KaiTi'>")
// .append("<font color='red' size=22>显示中文aaaaaaaaaa</font>")
// .append("</font>").append("</body></html>");
// 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);
}
}