PDF技術(四)-Java實現Html轉PDF文件

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("&nbsp;","&#160;");

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);
}

}

 

http://www.javashuo.com/article/p-hagzwzdj-dt.html

https://blog.csdn.net/Warren_one/article/details/78625546

相關文章
相關標籤/搜索