一、簡介
之前也記錄過一篇關於把 HTML 文本或 HTML 文件轉換為 PDF 的博客,只是之前那種方法有些局限性。
后來又了解到 wkhtmltopdf.exe 這個工具,這個工具比起之前的那種方法簡直是太好用了。
它是一個使用 Qt WebKit 引擎做渲染的,能夠把 HTML 文檔轉換成 PDF 文檔或圖片(image) 的命令行工具。
支持多個平台,可在 windows、linux 等系統下運行。
你可以從這里獲取到它:https://wkhtmltopdf.org/downloads.html
二、安裝
下載完成之后你需要先安裝它,然后你就能獲取到 wkhtmltopdf.exe 這個文件了,還包括有一個 wkhtmltoimage.exe 文件,
第一個文件是把 HTML 文檔轉換為 PDF 文檔的,后一個文件是把 HTML 文檔轉換為圖片的(Image),使用方法類似,只是調用的文件不一樣而已,這里就不多做介紹。
我在安裝完成之后把 wkhtmltopdf.exe 這個文件放到了程序集所在的目錄,當然,你也可以不這么做,但是就需要修改相應的路徑。
三、代碼
下面不多說了,貼出我的代碼。
public partial class Form3 : Form { public Form3() { InitializeComponent(); string strHtml = "<p style='color:red;text-align:center;background-color:#000000;'>Hello World!<p><div style='width:150px;height:150px;background-color:blue;'></div>"; string htmlUrl = "https://wkhtmltopdf.org/downloads.html"; /// 把 HTML 文本內容轉換為 PDF HtmlTextConvertToPdf(strHtml, @"C:\Users\Administrator\Desktop\001.pdf"); /// 把 HTML 文件轉換為 PDF HtmlConvertToPdf(htmlUrl, @"C:\Users\Administrator\Desktop\002.pdf"); } /// <summary> /// HTML文本內容轉換為PDF /// </summary> /// <param name="strHtml">HTML文本內容</param> /// <param name="savePath">PDF文件保存的路徑</param> /// <returns></returns> public bool HtmlTextConvertToPdf(string strHtml, string savePath) { bool flag = false; try { string htmlPath = HtmlTextConvertFile(strHtml); flag = HtmlConvertToPdf(htmlPath, savePath); File.Delete(htmlPath); } catch { flag = false; } return flag; } /// <summary> /// HTML轉換為PDF /// </summary> /// <param name="htmlPath">可以是本地路徑,也可以是網絡地址</param> /// <param name="savePath">PDF文件保存的路徑</param> /// <returns></returns> public bool HtmlConvertToPdf(string htmlPath, string savePath) { bool flag = false; CheckFilePath(savePath); ///這個路徑為程序集的目錄,因為我把應用程序 wkhtmltopdf.exe 放在了程序集同一個目錄下 string exePath = AppDomain.CurrentDomain.BaseDirectory.ToString() + "wkhtmltopdf.exe"; if (!File.Exists(exePath)) { throw new Exception("No application wkhtmltopdf.exe was found."); } try { ProcessStartInfo processStartInfo = new ProcessStartInfo(); processStartInfo.FileName = exePath; processStartInfo.WorkingDirectory = Path.GetDirectoryName(exePath); processStartInfo.UseShellExecute = false; processStartInfo.CreateNoWindow = true; processStartInfo.RedirectStandardInput = true; processStartInfo.RedirectStandardOutput = true; processStartInfo.RedirectStandardError = true; processStartInfo.Arguments = GetArguments(htmlPath, savePath); Process process = new Process(); process.StartInfo = processStartInfo; process.Start(); process.WaitForExit(); ///用於查看是否返回錯誤信息 //StreamReader srone = process.StandardError; //StreamReader srtwo = process.StandardOutput; //string ss1 = srone.ReadToEnd(); //string ss2 = srtwo.ReadToEnd(); //srone.Close(); //srone.Dispose(); //srtwo.Close(); //srtwo.Dispose(); process.Close(); process.Dispose(); flag = true; } catch { flag = false; } return flag; } /// <summary> /// 獲取命令行參數 /// </summary> /// <param name="htmlPath"></param> /// <param name="savePath"></param> /// <returns></returns> private string GetArguments(string htmlPath,string savePath) { if (string.IsNullOrEmpty(htmlPath)) { throw new Exception("HTML local path or network address can not be empty."); } if(string.IsNullOrEmpty(savePath)) { throw new Exception("The path saved by the PDF document can not be empty."); } StringBuilder stringBuilder = new StringBuilder(); stringBuilder.Append(" --page-height 100 "); //頁面高度100mm stringBuilder.Append(" --page-width 100 "); //頁面寬度100mm stringBuilder.Append(" --header-center 我是頁眉 "); //設置居中顯示頁眉 stringBuilder.Append(" --header-line "); //頁眉和內容之間顯示一條直線 stringBuilder.Append(" --footer-center \"Page [page] of [topage]\" "); //設置居中顯示頁腳 stringBuilder.Append(" --footer-line "); //頁腳和內容之間顯示一條直線 stringBuilder.Append(" " + htmlPath + " "); //本地 HTML 的文件路徑或網頁 HTML 的URL地址 stringBuilder.Append(" " + savePath + " "); //生成的 PDF 文檔的保存路徑 return stringBuilder.ToString(); } /// <summary> /// 驗證保存路徑 /// </summary> /// <param name="savePath"></param> private void CheckFilePath(string savePath) { string ext = string.Empty; string path = string.Empty; string fileName = string.Empty; ext = Path.GetExtension(savePath); if (string.IsNullOrEmpty(ext) || ext.ToLower() != ".pdf") { throw new Exception("Extension error:This method is used to generate PDF files."); } fileName = Path.GetFileName(savePath); if (string.IsNullOrEmpty(fileName)) { throw new Exception("File name is empty."); } try { path = savePath.Substring(0, savePath.IndexOf(fileName)); if (!Directory.Exists(path)) { Directory.CreateDirectory(path); } } catch { throw new Exception("The file path does not exist."); } } /// <summary> /// HTML文本內容轉HTML文件 /// </summary> /// <param name="strHtml">HTML文本內容</param> /// <returns>HTML文件的路徑</returns> public string HtmlTextConvertFile(string strHtml) { if (string.IsNullOrEmpty(strHtml)) { throw new Exception("HTML text content cannot be empty."); } try { string path = AppDomain.CurrentDomain.BaseDirectory.ToString() + @"html\"; if (!Directory.Exists(path)) { Directory.CreateDirectory(path); } string fileName = path + DateTime.Now.ToString("yyyyMMddHHmmssfff") + new Random().Next(1000, 10000) + ".html"; FileStream fileStream = new FileStream(fileName, FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite); StreamWriter streamWriter = new StreamWriter(fileStream, Encoding.Default); streamWriter.Write(strHtml); streamWriter.Flush(); streamWriter.Close(); streamWriter.Dispose(); fileStream.Close(); fileStream.Dispose(); return fileName; } catch { throw new Exception("HTML text content error."); } } }
PS:在這里我遇到了一個問題,剛開始設置的命令行參數不起作用,比如:--page-height 100 等。
我也查看了輸出的錯誤信息,后來發現是因為 wkhtmltopdf.exe 這個文件的路徑存在中文的目錄。
然后我就把 wkhtmltopdf.exe 這個文件所在路徑的所有目錄都改為了英文,就這樣就可以了。
四、命令行參數
下面是一些命令行參數的介紹:
全局選項: --collate 打印多個副本時進行檢查(默認設置) --no-collate 打印多個副本時不進行檢查 --cookie-jar <path> 從指定的cookie JAR文件中讀寫 cookie 數據 --copies <number> 打印 PDF 文件的份數(默認值為:1) --dpi <dpi> 設置一個分辨率,對於 X11 系統沒有作用(默認值為:96) --extended-help 相對 -H 參數的設置,顯示更詳細的說明文檔 --grayscale 將生成灰度的 PDF 文檔,占用空間小,但是不會有彩色 --help 顯示幫助信息 --htmldoc 輸出程序的 HTML 幫助文檔 --image-dpi <integer> 當頁面存在內嵌圖片時,指定圖像的分辨率(默認值為:600) --image-quality <interger> 當使用 JPEG 算法壓縮圖片時,指定圖像的質量(默認值為:94) --license 輸出授權許可信息並退出 --lowquality 生成低質量的 PDF/PS,能夠減少最終生成文檔所占用的存儲空間 --manpage 輸出程序的手冊頁 --quiet 靜默模式,不輸出任何信息 --read-args-from-stdin 從標准輸入讀取命令行參數 --readme 輸出程序的 Readme 文檔 --version 輸出版本信息並退出 --no-pdf-compression 設置為不要對 PDF 對象使用無損壓縮 --margin-bottom <unitreal> 設置頁面的底邊距,單位毫米(mm) --margin-left <unitreal> 設置頁面的左邊距 (默認值為:10mm) --margin-right <unitreal> 設置頁面的右邊距 (默認值為:10mm) --margin-top <unitreal> 設置頁面的上邊距,單位毫米(mm) --page-size <Size> 設置頁面的大小,如:A4、Letter等(默認值為:A4) --page-height <unitreal> 設置頁面高度,單位毫米(mm) --page-width <unitreal> 設置頁面寬度,單位毫米(mm) --orientation <orientation> 設置文檔模式為風景或肖像(默認值為:肖像) --title <text> 生成的 PDF 文檔的標題(如果沒有指定,則使用第一個文檔的標題) 大綱選項: --dump-default-toc-xsl 轉儲到默認的 TOC xsl 樣式表到標准輸出文件 --dump-outline <file> 將大綱轉儲到指定的文件(XML 文件) --outline 在生成的 PDF 文檔中添加大綱(默認設置) --no-outline 不要在生成的 PDF 文檔中添加大綱 --outline-depth <level> 設置大綱的深度(默認值為:4) 頁面選項: --allow <path> 允許加載指定文件夾中的文件(可重復使用此參數指定多個文件) --background 輸出頁面背景到 PDF 文檔(默認設置) --no-background 不輸出頁面背景到 PDF 文檔 --bypass-proxy-for <value> 設置主機的代理(可重復指定多個代理) --cache-dir <path> Web緩存目錄 --checkbox-checked-svg <path> 使用指定的SVG文件渲染選中的復選框 --checkbox-svg <path> 使用指定的SVG文件渲染未選中的復選框 --cookie <name> <value> 設置訪問網頁時額外的 cookie,value 應該是 url 編碼的(可重復使用此參數指定多個 cookie) --custom-header <name> <value> 設置訪問網頁時額外的 HTTP 頭(可重復使用此參數指定多個 HTTP 頭) --custom-header-propagation 為每個資源請求添加自定義的 HTTP 頭 --no-custom-header-propagation 不要為每個資源請求添加自定義的 HTTP 頭 --debug-javascript 顯示 JavaScript 調試輸出的內容 --no-debug-javascript 不顯示 JavaScript 調試輸出的內容(默認設置) --encoding <encoding> 設置輸入文本的默認編碼 --disable-external-links 禁止頁面中的外鏈生成超鏈接 --enable-external-links 允許頁面中的外鏈生成超鏈接(默認設置) --disable-forms 不要將 HTML 表單轉換為 PDF 表單(默認設置) --enable-forms 將 HTML 表單轉換為 PDF 表單 --images 加載圖片並輸出到 PDF 文檔(默認設置) --no-images 在生成的 PDF 文檔中過濾掉圖片 --disable-internal-links 禁止頁面中的內鏈生成超鏈接 --enable-internal-links 允許頁面中的內鏈生成超連接(默認設置) --disable-javascript 禁止 Web 頁面運行 JavaScript --enable-javascript 允許 Web 頁面運行 JavaScript(默認設置) --javascript-delay <msec> 延遲指定的時間,等待 JavaScript 執行完成,單位毫秒(ms)(默認值為:200) --load-error-handling <handler> 指定如何處理無法加載的頁面:abort、ignore、skip(默認值為:abort) --load-media-error-handling <handler> 指定如何處理無法加載的媒體文件:abort、ignore、skip(默認值為:ignore) --disable-local-file-access 不允許一個本地文件加載其他的本地文件,使用命令行參數 --allow 指定的目錄除外。 --enable-local-file-access 允許將本地文件轉換到其他本地文件中讀取(默認設置) --exclude-from-outline 不要將頁面包含在內容表和大綱中 --include-in-outline 將頁面包含在內容表和大綱中(默認設置) --page-offset <offset> 設置頁碼的起始值(默認值為:0) --minimum-font-size <int> 設置最小的字體大小 --disable-plugins 禁用已安裝的插件(默認設置) --enable-plugins 啟用已安裝的插件(但插件可能不起作用) --post <name> <value> 添加一個附加字段(可以重復使用該參數添加多個附加字段) --post-file <name> <value> 添加一個附加文件(可以重復使用該參數添加多個附加文件) --print-media-type 使用打印媒體類型代替屏幕 --no-print-media-type 不使用打印媒體類型代替屏幕(默認設置) --proxy <proxy> 使用代理 --radiobutton-checked-svg <path> 使用指定的SVG文件渲染選中的單選按鈕 --radiobutton-svg <path> 使用指定的SVG文件渲染未選中的單選按鈕 --run-sript <js> 在頁面加載完成后運行這個額外的 JavaScript(可以重復使用該參數添加多個額外的 JavaScript) --disable-smart-shrinking 禁用智能收縮策略 --enable-smart-shrinking 啟用智能收縮策略(默認設置) --stop-slow-scripts 停止運行緩慢的 JavaScript 代碼(默認設置) --no-stop-slow-scripts 不停止運行緩慢的 JavaScript 代碼 --disable-toc-back-links 禁止從標頭鏈接到內容表(默認設置) --enable-toc-back-links 允許從標頭鏈接到內容表 --user-style-sheet <url> 指定一個用戶樣式表,以便加載每個頁面 --username <username> HTTP 身份認證的用戶名 --password <password> HTTP 身份認證的密碼 --viewport-size <> 設置窗口大小,需要自定義滾動條或 CSS 屬性來自適應窗口大小 --window-status <windowStatus> 等到window.status等於這個字符串前渲染頁面 --zoom <float> 設置轉換成 PDF 時頁面的縮放比例(默認值為:1) --default-header 添加一個默認的頁眉,左邊是頁面的名稱,右邊是頁碼,是下面的縮寫: --header-left='[webpage]' --header-right='[page]/[toPage]' --top 2cm --header-line 頁眉和頁腳選項: --footer-left <text> 居左顯示頁腳文本 --footer-center <text> 居中顯示頁腳文本 --footer-right <text> 居右顯示頁腳文本 --footer-font-name <name> 設置頁腳的字體名稱(默認值為:Arial) --footer-font-size <size> 設置頁腳的字體大小(默認值為:12) --footer-html <url> 添加一個 HTML 作為頁腳 --footer-line 在頁腳上方顯示一條直線 --no-footer-line 不在頁腳上方顯示一條直線(默認設置) --footer-spacing <real> 設置頁腳與內容之間的間距,單位毫米(mm)(默認值為:0) --header-left <text> 居左顯示頁眉文本 --header-center <text> 居中顯示頁眉文本 --header-right <text> 居右顯示頁眉文本 --header-font-name <name> 設置頁眉的字體名稱(默認值為:Arial) --header-font-size <size> 設置頁眉的字體大小(默認值為:12) --header-html <url> 添加一個 HTML 作為頁眉 --header-line 在頁眉下方顯示一條直線 --no-header-line 不在頁眉下方顯示一條直線(默認設置) --header-spacing <real> 設置頁眉與內容之間的間距,單位毫米(mm)(默認值為:0) --replace <name> <value> 在頁眉和頁腳中替換指定名稱的值(可以重復使用該參數指定多個需要替換的名稱和值) 內容表選項: --disable-dotted-lines 不要在 TOC 中使用虛線 --toc-header-text <text> 設置 TOC 的標題文本(默認值為:內容表) --toc-level-indentation <width> 在 TOC 縮進每一級的標題長度(默認值為:1em) --disable-toc-links 在 TOC 中不生成指向內容錨點的超鏈接 --toc-text-size-shrink <real> 在 TOC 中的每一級標題,字體按這個比例縮放(默認值為:0.8) --xsl-style-sheet <file> 使用指定的 XSL 樣式表打印內容表 頁眉和頁腳: 頁眉和頁腳可以使用參數 --header-* 和 --footer-* 添加到文檔中。 有些參數也需要提供一個字符串 text 作為參數值。例如:--header-left 可以在 text 中使用以下變量,將會把以下變量替換為對應的值。 * [page] 當前正在打印的頁面的頁碼 * [frompage] 打印的第一頁的頁碼 * [topage] 打印的最后一頁的頁碼 * [webpage] 當前正在打印的頁面的 URL * [section] 當前正在打印的章節的名稱 * [subsection] 當前正在打印的分段的名稱 * [date] 本地系統格式的當前日期 * [isodate] ISO 8601 擴展格式的當前日期 * [time] 本地系統格式的當前時間 * [title] 當前頁對象的標題 * [doctitle] 輸出文檔的標題 * [sitepage] 當前正在處理的對象中當前頁面的頁碼 * [sitepages] 當前正在處理的對象中的總頁數 舉個例子: --header-right "Page [page] of [toPage]", 會在頁面的右上角生成一個類似 Page x of y 的字符串, 其中 x 是當前頁面的頁碼, y 是當前文檔最后一頁的頁碼。
五、推薦
下面推薦兩篇比較好的文章,一篇是官網的英文版的介紹,另一篇是中文版的介紹。
具體的大家自己去看吧。
英文版推薦:https://wkhtmltopdf.org/usage/wkhtmltopdf.txt
中文版推薦:http://www.jianshu.com/p/4d65857ffe5e