通過java調用系統中的wkhtmltopdf
希望達到脫離瀏覽器將頁面導成 pdf
的效果, 於是利用 wkhtmltopdf
的將 url
轉 pdf
的能力實現
利用 java
代碼觸發 wkhtmltopdf
注意事項
有幾點東西要注意一下
- 中文字體問題, 大多數
linux
系統是默認不帶中文字體的, 需要自己手動安裝一下 - 這個操作相對還是比較耗時的(特別是網絡開銷), 如果併發量比較大需要關注下性能問題
- 剩下就不細講了, 看下面的源碼應該都明白了
參數說明
命令格式
wkhtmltopdf [OPTIONS]... <input file> [More input files] <output file>
常規選項
--allow <path>
: 允許加載從指定的文件夾中的文件或文件(可重複)--book
: 設置一會打印一本書的時候,通常設置的選項--collate
: 打印多份副本時整理--cookie <name> <value>
: 設置一個額外的cookie(可重複)--cookie-jar <path>
: 讀取和寫入的Cookie,並在提供的cookie jar文件--copies <number>
: 複印打印成pdf文件數(默認爲1)--cover* <url>
: 使用HTML文件作爲封面。它會帶頁眉和頁腳的TOC之前插入--custom-header <name> <value>
: 設置一個附加的HTTP頭(可重複)--debug-javascript
: 顯示的javascript調試輸出--default-header
: 添加一個缺省的頭部,與頁面的左邊的名稱,頁面數到右邊,例如:–header-left'[webpage]' --header-right '[page]/[toPage]' --header-line
--disable-external-links
: 禁止生成鏈接到遠程網頁--disable-internal-links
: 禁止使用本地鏈接- `–disable-javascript 禁止讓網頁執行JavaScript
--disable-pdf-compression
: 禁止在PDF對象使用無損壓縮--disable-smart-shrinking
: 禁止使用WebKit的智能戰略收縮,使像素/ DPI比沒有不變--disallow-local-file-access
: 禁止允許轉換的本地文件讀取其他本地文件,除非explecitily允許用--allow
--dpi <dpi>
: 顯式更改DPI(這對基於X11的系統沒有任何影響)--enable-plugins
: 啓用已安裝的插件(如Flash--encoding <encoding>
: 設置默認的文字編碼--extended-help
: 顯示更廣泛的幫助,詳細介紹了不常見的命令開關--forms
: 打開HTML表單字段轉換爲PDF表單域--grayscale
: PDF格式將在灰階產生--help
: Display help--htmldoc
: 輸出程序HTML幫助--ignore-load-errors
: 忽略claimes加載過程中已經遇到了一個錯誤頁面--lowquality
: 產生低品質的PDF/ PS。有用縮小結果文檔的空間--manpage
: 輸出程序手冊頁--margin-bottom <unitreal>
: 設置頁面下邊距 (default 10mm)--margin-left <unitreal>
: 將左邊頁邊距 (default 10mm)--margin-right <unitreal>
: 設置頁面右邊距 (default 10mm)--margin-top <unitreal>
: 設置頁面上邊距 (default 10mm)--minimum-font-size <int>
: 最小字體大小 (default 5)--no-background
: 不打印背景--orientation <orientation>
: 設置方向爲橫向或縱向--page-height <unitreal>
: 頁面高度 (default unit millimeter)--page-offset* <offset>
: 設置起始頁碼 (default 1)--page-size <size>
: 設置紙張大小: A4, Letter, etc.--page-width <unitreal>
: 頁面寬度 (default unit millimeter)--password <password>
: HTTP驗證密碼--post <name> <value>
: Add an additional post field (repeatable)--post-file <name> <path>
: Post an aditional file (repeatable)--print-media-type
: 使用的打印介質類型,而不是屏幕--proxy <proxy>
: 使用代理--quiet
: Be less verbose--read-args-from-stdin
: 讀取標準輸入的命令行參數--readme
: 輸出程序自述--redirect-delay <msec>
: 等待幾毫秒爲JS-重定向(default 200)--replace* <name> <value>
: 替換名稱,值的頁眉和頁腳(可重複)--stop-slow-scripts
: 停止運行緩慢的JavaScripts--title <text>
: 生成的PDF文件的標題(第一個文檔的標題使用,如果沒有指定)--toc
: 插入的內容的表中的文件的開頭--use-xserver
: 使用X服務器(一些插件和其他的東西沒有X11可能無法正常工作)--user-style-sheet <url>
: 指定用戶的樣式表,加載在每一頁中--username <username>
: HTTP認證的用戶名--version
: 輸出版本信息退出--zoom <float>
: 使用這個縮放因子 (default 1)
頁眉和頁腳選項
--header-center** <text>
: (設置在中心位置的頁眉內容)--header-font-name* <name>
: (default Arial) (設置頁眉的字體名稱)--header-font-size* <size>
: (設置頁眉的字體大小)--header-html <url>
: (添加一個HTML頁眉,後面是網址)--header-left <text>
: (左對齊的頁眉文本)--header-line
: (顯示一條線在頁眉下)--header-right* <text>
: (右對齊頁眉文本)--header-spacing: <real>
: (設置頁眉和內容的距離,默認0)--footer-center: <text>
: (設置在中心位置的頁腳內容)--footer-font-name* <name>
: (設置頁腳的字體名稱)--footer-font-size* <size>
: (設置頁腳的字體大小default 11)--footer-html <url>
: (添加一個HTML頁腳,後面是網址)--footer-left <text>
: (左對齊的頁腳文本)--footer-line
: 顯示一條線在頁腳內容上)--footer-right <text>
: (右對齊頁腳文本)--footer-spacing <real>
: (設置頁腳和內容的距離)
###表內容選項中
--toc-depth <level>
: Set the depth of the toc (default 3)--toc-disable-back-links
: Do not link from section header to toc--toc-disable-links
: Do not link from toc to sections--toc-font-name* <name>
: Set the font used for the toc (default Arial)--toc-header-font-name* <name>
: The font of the toc header (if unset use --toc-font-name)--toc-header-font-size* <size>
: The font size of the toc header (default 15)--toc-header-text* <text>
: The header text of the toc (default Table Of Contents)--toc-l1-font-size* <size>
: Set the font size on level 1 of the toc (default 12)--toc-l1-indentation* <num>
: Set indentation on level 1 of the toc (default 0)--toc-l2-font-size* <size>
: Set the font size on level 2 of the toc (default 10)--toc-l2-indentation* <num>
: Set indentation on level 2 of the toc (default 20)--toc-l3-font-size* <size>
: Set the font size on level 3 of the toc (default 8)--toc-l3-indentation* <num>
: Set indentation on level 3 of the toc (default 40)--toc-l4-font-size* <size>
: Set the font size on level 4 of the toc (default 6)--toc-l4-indentation* <num>
: Set indentation on level 4 of the toc (default 60)--toc-l5-font-size* <size>
: Set the font size on level 5 of the toc (default 4)--toc-l5-indentation* <num>
: Set indentation on level 5 of the toc (default 80)--toc-l6-font-size* <size>
: Set the font size on level 6 of the toc (default 2)--toc-l6-indentation* <num>
: Set indentation on level 6 of the toc (default 100)--toc-l7-font-size* <size>
: Set the font size on level 7 of the toc (default 0)--toc-l7-indentation* <num>
: Set indentation on level 7 of the toc (default 120)--toc-no-dots
: Do not use dots, in the toc
輪廓選項-dump-outline <file>
: 轉儲目錄到一個文件--outline
: 顯示目錄(文章中h1,h2來定)--outline-depth <level>
: 設置目錄的深度(默認爲4)
頁腳和頁眉[page]
: 由當前正在打印的頁的數目代替[frompage]
: 由要打印的第一頁的數量取代[topage]
: 由最後一頁要打印的數量取代[webpage]
: 通過正在打印的頁面的URL替換[section]
: 由當前節的名稱替換[subsection]
: 由當前小節的名稱替換[date]
: 由當前日期系統的本地格式取代[time]
: 由當前時間,系統的本地格式取代
頁腳和頁眉
[page]
: 由當前正在打印的頁的數目代替[frompage]
: 由要打印的第一頁的數量取代[topage]
: 由最後一頁要打印的數量取代[webpage]
: 通過正在打印的頁面的URL替換[section]
: 由當前節的名稱替換[subsection]
: 由當前小節的名稱替換[date]
: 由當前日期系統的本地格式取代[time]
: 由當前時間,系統的本地格式取代
源碼分享
/**
* @Author: WanG
* @Date: 2019-08-22 13:54
* @version: v1.0
* @description: 使用wkhtmltopdf, 來將頁面轉成pdf
*/
@Slf4j
@Service
public class PDFService {
private Random random = new Random();
/**
* 將傳入的頁面轉pdf, 返回字節數組
* 默認自動隨機生成文件名字
* @author wangq 2019-08-22 16:27
* @param url 頁面url
* @return
*/
public byte[] html2pdf(String url) {
String tmp = System.getProperty("java.io.tmpdir");
String filename = System.currentTimeMillis() + random.nextInt(1000) + "";
return doHtml2pdf(url, tmp + "/" + filename);
}
private byte[] doHtml2pdf(String src, String dest) {
String space = " ";
String wkhtmltopdf = findExecutable();
if (StringUtils.isEmpty(wkhtmltopdf)) {
log.error("no wkhtmltopdf found!");
throw new RuntimeException("html轉換pdf出錯了");
}
File file = new File(dest);
File parent = file.getParentFile();
if (!parent.exists()) {
boolean dirsCreation = parent.mkdirs();
log.info("create dir for new file,{}", dirsCreation);
}
/*
一些特殊符號
[page] : 頁碼
[data] : 日期(例如:8/22/19)
*/
StringBuilder cmd = new StringBuilder();
cmd.append(findExecutable()).append(space)
.append(src).append(space)
.append("--footer-center").append(space).append("[page]").append(space)
.append("--footer-font-size").append(space).append("14").append(space)
.append("--disable-smart-shrinking").append(space)
.append("--load-media-error-handling")
.append(space).append("ignore").append(space)
.append("--load-error-handling").append(space).append("ignore").append(space)
.append("--footer-right").append(space).append("WanG提供技術支持").append(space)
.append(dest);
InputStream is = null;
try {
String finalCmd = cmd.toString();
log.info("final cmd:{}", finalCmd);
Process proc = Runtime.getRuntime().exec(finalCmd);
new Thread(new ProcessStreamHandler(proc.getInputStream())).start();
new Thread(new ProcessStreamHandler(proc.getErrorStream())).start();
proc.waitFor();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
is = new FileInputStream(file);
byte[] buf = new byte[1024];
while (is.read(buf, 0, buf.length) != -1) {
baos.write(buf, 0, buf.length);
}
return baos.toByteArray();
} catch (Exception e) {
log.error("html轉換pdf出錯", e);
throw new RuntimeException("html轉換pdf出錯了");
} finally {
if (is != null) {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
* 根據當前系統返回 wkhtmltopdf 執行命令
* @author wangq 2019-08-22 16:26
* @return
*/
public String findExecutable() {
Process p;
try {
String osname = System.getProperty("os.name").toLowerCase();
String cmd = osname.contains("windows") ? "where wkhtmltopdf" : "which wkhtmltopdf";
p = Runtime.getRuntime().exec(cmd);
new Thread(new ProcessStreamHandler(p.getErrorStream())).start();
p.waitFor();
return IOUtils.toString(p.getInputStream(), Charset.defaultCharset());
} catch (Exception e) {
log.error("no wkhtmltopdf found!", e);
}
return "";
}
private static class ProcessStreamHandler implements Runnable {
private InputStream is;
public ProcessStreamHandler(InputStream is) {
this.is = is;
}
@Override
public void run() {
BufferedReader reader = null;
try {
InputStreamReader isr = new InputStreamReader(is, "utf-8");
reader = new BufferedReader(isr);
String line;
while ((line = reader.readLine()) != null) {
log.debug(line);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
}