html轉換為pdf的關鍵技術是如何處理網頁中復雜的css樣式、以及中文亂碼處理。
各實現對比表
於Windows平台進行測試:
基於IText
基於FlyingSaucer
基於WKHtmlToPdf
基於pd4ml
跨平台性
跨平台
跨平台
跨平台
跨平台
是否安裝軟件
否
否
需安裝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,需要注意的是:
1、如果頁面中有中文,服務器端需要下載字體庫simsun.ttc,在后台進行引用,同時在頁面的樣式中加入對應字體的定義,如:body{font-family: SimSun;},否則中文無法渲染(中文處渲染出來的效果是空白);
2、頁面中如果有圖片,如果圖片引用是絕對路徑或者base64則不用考慮,如果是相對路徑,需要在后台用renderer.getSharedContext().setBaseURL("圖片絕對路徑目錄");來指定圖片路徑,否則圖片無法渲染。
3、要轉化的頁面必須是標准的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);
}
}
https://www.cnblogs.com/goldenVip/p/6104051.html
https://blog.csdn.net/Warren_one/article/details/78625546
---------------------
作者:晉文子上
來源:CSDN
原文:https://blog.csdn.net/qq_34190023/article/details/82999702
版權聲明:本文為博主原創文章,轉載請附上博文鏈接!