一、简介

之前也记录过一篇关于把 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 这个文件放到了程序集所在的目录,当然,你也可以不这么做,但是就需要修改相应的路径。

三、代码

下面不多说了,贴出我的代码。

  1. public partial class Form3 : Form
  2. {
  3. public Form3()
  4. {
  5. InitializeComponent();
  6. 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>";
  7. string htmlUrl = "https://wkhtmltopdf.org/downloads.html";
  8. /// 把 HTML 文本内容转换为 PDF
  9. HtmlTextConvertToPdf(strHtml, @"C:\Users\Administrator\Desktop\001.pdf");
  10. /// 把 HTML 文件转换为 PDF
  11. HtmlConvertToPdf(htmlUrl, @"C:\Users\Administrator\Desktop\002.pdf");
  12. }
  13. /// <summary>
  14. /// HTML文本内容转换为PDF
  15. /// </summary>
  16. /// <param name="strHtml">HTML文本内容</param>
  17. /// <param name="savePath">PDF文件保存的路径</param>
  18. /// <returns></returns>
  19. public bool HtmlTextConvertToPdf(string strHtml, string savePath)
  20. {
  21. bool flag = false;
  22. try
  23. {
  24. string htmlPath = HtmlTextConvertFile(strHtml);
  25. flag = HtmlConvertToPdf(htmlPath, savePath);
  26. File.Delete(htmlPath);
  27. }
  28. catch
  29. {
  30. flag = false;
  31. }
  32. return flag;
  33. }
  34. /// <summary>
  35. /// HTML转换为PDF
  36. /// </summary>
  37. /// <param name="htmlPath">可以是本地路径,也可以是网络地址</param>
  38. /// <param name="savePath">PDF文件保存的路径</param>
  39. /// <returns></returns>
  40. public bool HtmlConvertToPdf(string htmlPath, string savePath)
  41. {
  42. bool flag = false;
  43. CheckFilePath(savePath);
  44. ///这个路径为程序集的目录,因为我把应用程序 wkhtmltopdf.exe 放在了程序集同一个目录下
  45. string exePath = AppDomain.CurrentDomain.BaseDirectory.ToString() + "wkhtmltopdf.exe";
  46. if (!File.Exists(exePath))
  47. {
  48. throw new Exception("No application wkhtmltopdf.exe was found.");
  49. }
  50. try
  51. {
  52. ProcessStartInfo processStartInfo = new ProcessStartInfo();
  53. processStartInfo.FileName = exePath;
  54. processStartInfo.WorkingDirectory = Path.GetDirectoryName(exePath);
  55. processStartInfo.UseShellExecute = false;
  56. processStartInfo.CreateNoWindow = true;
  57. processStartInfo.RedirectStandardInput = true;
  58. processStartInfo.RedirectStandardOutput = true;
  59. processStartInfo.RedirectStandardError = true;
  60. processStartInfo.Arguments = GetArguments(htmlPath, savePath);
  61. Process process = new Process();
  62. process.StartInfo = processStartInfo;
  63. process.Start();
  64. process.WaitForExit();
  65. ///用于查看是否返回错误信息
  66. //StreamReader srone = process.StandardError;
  67. //StreamReader srtwo = process.StandardOutput;
  68. //string ss1 = srone.ReadToEnd();
  69. //string ss2 = srtwo.ReadToEnd();
  70. //srone.Close();
  71. //srone.Dispose();
  72. //srtwo.Close();
  73. //srtwo.Dispose();
  74. process.Close();
  75. process.Dispose();
  76. flag = true;
  77. }
  78. catch
  79. {
  80. flag = false;
  81. }
  82. return flag;
  83. }
  84. /// <summary>
  85. /// 获取命令行参数
  86. /// </summary>
  87. /// <param name="htmlPath"></param>
  88. /// <param name="savePath"></param>
  89. /// <returns></returns>
  90. private string GetArguments(string htmlPath,string savePath)
  91. {
  92. if (string.IsNullOrEmpty(htmlPath))
  93. {
  94. throw new Exception("HTML local path or network address can not be empty.");
  95. }
  96. if(string.IsNullOrEmpty(savePath))
  97. {
  98. throw new Exception("The path saved by the PDF document can not be empty.");
  99. }
  100. StringBuilder stringBuilder = new StringBuilder();
  101. stringBuilder.Append(" --page-height 100 "); //页面高度100mm
  102. stringBuilder.Append(" --page-width 100 "); //页面宽度100mm
  103. stringBuilder.Append(" --header-center 我是页眉 "); //设置居中显示页眉
  104. stringBuilder.Append(" --header-line "); //页眉和内容之间显示一条直线
  105. stringBuilder.Append(" --footer-center \"Page [page] of [topage]\" "); //设置居中显示页脚
  106. stringBuilder.Append(" --footer-line "); //页脚和内容之间显示一条直线
  107. stringBuilder.Append(" " + htmlPath + " "); //本地 HTML 的文件路径或网页 HTML 的URL地址
  108. stringBuilder.Append(" " + savePath + " "); //生成的 PDF 文档的保存路径
  109. return stringBuilder.ToString();
  110. }
  111. /// <summary>
  112. /// 验证保存路径
  113. /// </summary>
  114. /// <param name="savePath"></param>
  115. private void CheckFilePath(string savePath)
  116. {
  117. string ext = string.Empty;
  118. string path = string.Empty;
  119. string fileName = string.Empty;
  120. ext = Path.GetExtension(savePath);
  121. if (string.IsNullOrEmpty(ext) || ext.ToLower() != ".pdf")
  122. {
  123. throw new Exception("Extension error:This method is used to generate PDF files.");
  124. }
  125. fileName = Path.GetFileName(savePath);
  126. if (string.IsNullOrEmpty(fileName))
  127. {
  128. throw new Exception("File name is empty.");
  129. }
  130. try
  131. {
  132. path = savePath.Substring(0, savePath.IndexOf(fileName));
  133. if (!Directory.Exists(path))
  134. {
  135. Directory.CreateDirectory(path);
  136. }
  137. }
  138. catch
  139. {
  140. throw new Exception("The file path does not exist.");
  141. }
  142. }
  143. /// <summary>
  144. /// HTML文本内容转HTML文件
  145. /// </summary>
  146. /// <param name="strHtml">HTML文本内容</param>
  147. /// <returns>HTML文件的路径</returns>
  148. public string HtmlTextConvertFile(string strHtml)
  149. {
  150. if (string.IsNullOrEmpty(strHtml))
  151. {
  152. throw new Exception("HTML text content cannot be empty.");
  153. }
  154. try
  155. {
  156. string path = AppDomain.CurrentDomain.BaseDirectory.ToString() + @"html\";
  157. if (!Directory.Exists(path))
  158. {
  159. Directory.CreateDirectory(path);
  160. }
  161. string fileName = path + DateTime.Now.ToString("yyyyMMddHHmmssfff") + new Random().Next(1000, 10000) + ".html";
  162. FileStream fileStream = new FileStream(fileName, FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite);
  163. StreamWriter streamWriter = new StreamWriter(fileStream, Encoding.Default);
  164. streamWriter.Write(strHtml);
  165. streamWriter.Flush();
  166. streamWriter.Close();
  167. streamWriter.Dispose();
  168. fileStream.Close();
  169. fileStream.Dispose();
  170. return fileName;
  171. }
  172. catch
  173. {
  174. throw new Exception("HTML text content error.");
  175. }
  176. }
  177. }

PS:在这里我遇到了一个问题,刚开始设置的命令行参数不起作用,比如:—page-height 100 等。
我也查看了输出的错误信息,后来发现是因为 wkhtmltopdf.exe 这个文件的路径存在中文的目录。
然后我就把 wkhtmltopdf.exe 这个文件所在路径的所有目录都改为了英文,就这样就可以了。

四、命令行参数

下面是一些命令行参数的介绍:

  1. 全局选项:
  2. --collate 打印多个副本时进行检查(默认设置)
  3. --no-collate 打印多个副本时不进行检查
  4. --cookie-jar <path> 从指定的cookie JAR文件中读写 cookie 数据
  5. --copies <number> 打印 PDF 文件的份数(默认值为:1)
  6. --dpi <dpi> 设置一个分辨率,对于 X11 系统没有作用(默认值为:96)
  7. --extended-help 相对 -H 参数的设置,显示更详细的说明文档
  8. --grayscale 将生成灰度的 PDF 文档,占用空间小,但是不会有彩色
  9. --help 显示帮助信息
  10. --htmldoc 输出程序的 HTML 帮助文档
  11. --image-dpi <integer> 当页面存在内嵌图片时,指定图像的分辨率(默认值为:600)
  12. --image-quality <interger> 当使用 JPEG 算法压缩图片时,指定图像的质量(默认值为:94)
  13. --license 输出授权许可信息并退出
  14. --lowquality 生成低质量的 PDF/PS,能够减少最终生成文档所占用的存储空间
  15. --manpage 输出程序的手册页
  16. --quiet 静默模式,不输出任何信息
  17. --read-args-from-stdin 从标准输入读取命令行参数
  18. --readme 输出程序的 Readme 文档
  19. --version 输出版本信息并退出
  20. --no-pdf-compression 设置为不要对 PDF 对象使用无损压缩
  21. --margin-bottom <unitreal> 设置页面的底边距,单位毫米(mm)
  22. --margin-left <unitreal> 设置页面的左边距 (默认值为:10mm)
  23. --margin-right <unitreal> 设置页面的右边距 (默认值为:10mm)
  24. --margin-top <unitreal> 设置页面的上边距,单位毫米(mm)
  25. --page-size <Size> 设置页面的大小,如:A4Letter等(默认值为:A4)
  26. --page-height <unitreal> 设置页面高度,单位毫米(mm)
  27. --page-width <unitreal> 设置页面宽度,单位毫米(mm)
  28. --orientation <orientation> 设置文档模式为风景或肖像(默认值为:肖像)
  29. --title <text> 生成的 PDF 文档的标题(如果没有指定,则使用第一个文档的标题)
  30. 大纲选项:
  31. --dump-default-toc-xsl 转储到默认的 TOC xsl 样式表到标准输出文件
  32. --dump-outline <file> 将大纲转储到指定的文件(XML 文件)
  33. --outline 在生成的 PDF 文档中添加大纲(默认设置)
  34. --no-outline 不要在生成的 PDF 文档中添加大纲
  35. --outline-depth <level> 设置大纲的深度(默认值为:4)
  36. 页面选项:
  37. --allow <path> 允许加载指定文件夹中的文件(可重复使用此参数指定多个文件)
  38. --background 输出页面背景到 PDF 文档(默认设置)
  39. --no-background 不输出页面背景到 PDF 文档
  40. --bypass-proxy-for <value> 设置主机的代理(可重复指定多个代理)
  41. --cache-dir <path> Web缓存目录
  42. --checkbox-checked-svg <path> 使用指定的SVG文件渲染选中的复选框
  43. --checkbox-svg <path> 使用指定的SVG文件渲染未选中的复选框
  44. --cookie <name> <value> 设置访问网页时额外的 cookievalue 应该是 url 编码的(可重复使用此参数指定多个 cookie)
  45. --custom-header <name> <value> 设置访问网页时额外的 HTTP 头(可重复使用此参数指定多个 HTTP 头)
  46. --custom-header-propagation 为每个资源请求添加自定义的 HTTP
  47. --no-custom-header-propagation 不要为每个资源请求添加自定义的 HTTP
  48. --debug-javascript 显示 JavaScript 调试输出的内容
  49. --no-debug-javascript 不显示 JavaScript 调试输出的内容(默认设置)
  50. --encoding <encoding> 设置输入文本的默认编码
  51. --disable-external-links 禁止页面中的外链生成超链接
  52. --enable-external-links 允许页面中的外链生成超链接(默认设置)
  53. --disable-forms 不要将 HTML 表单转换为 PDF 表单(默认设置)
  54. --enable-forms HTML 表单转换为 PDF 表单
  55. --images 加载图片并输出到 PDF 文档(默认设置)
  56. --no-images 在生成的 PDF 文档中过滤掉图片
  57. --disable-internal-links 禁止页面中的内链生成超链接
  58. --enable-internal-links 允许页面中的内链生成超连接(默认设置)
  59. --disable-javascript 禁止 Web 页面运行 JavaScript
  60. --enable-javascript 允许 Web 页面运行 JavaScript(默认设置)
  61. --javascript-delay <msec> 延迟指定的时间,等待 JavaScript 执行完成,单位毫秒(ms)(默认值为:200)
  62. --load-error-handling <handler> 指定如何处理无法加载的页面:abortignoreskip(默认值为:abort)
  63. --load-media-error-handling <handler> 指定如何处理无法加载的媒体文件:abortignoreskip(默认值为:ignore)
  64. --disable-local-file-access 不允许一个本地文件加载其他的本地文件,使用命令行参数 --allow 指定的目录除外。
  65. --enable-local-file-access 允许将本地文件转换到其他本地文件中读取(默认设置)
  66. --exclude-from-outline 不要将页面包含在内容表和大纲中
  67. --include-in-outline 将页面包含在内容表和大纲中(默认设置)
  68. --page-offset <offset> 设置页码的起始值(默认值为:0)
  69. --minimum-font-size <int> 设置最小的字体大小
  70. --disable-plugins 禁用已安装的插件(默认设置)
  71. --enable-plugins 启用已安装的插件(但插件可能不起作用)
  72. --post <name> <value> 添加一个附加字段(可以重复使用该参数添加多个附加字段)
  73. --post-file <name> <value> 添加一个附加文件(可以重复使用该参数添加多个附加文件)
  74. --print-media-type 使用打印媒体类型代替屏幕
  75. --no-print-media-type 不使用打印媒体类型代替屏幕(默认设置)
  76. --proxy <proxy> 使用代理
  77. --radiobutton-checked-svg <path> 使用指定的SVG文件渲染选中的单选按钮
  78. --radiobutton-svg <path> 使用指定的SVG文件渲染未选中的单选按钮
  79. --run-sript <js> 在页面加载完成后运行这个额外的 JavaScript(可以重复使用该参数添加多个额外的 JavaScript)
  80. --disable-smart-shrinking 禁用智能收缩策略
  81. --enable-smart-shrinking 启用智能收缩策略(默认设置)
  82. --stop-slow-scripts 停止运行缓慢的 JavaScript 代码(默认设置)
  83. --no-stop-slow-scripts 不停止运行缓慢的 JavaScript 代码
  84. --disable-toc-back-links 禁止从标头链接到内容表(默认设置)
  85. --enable-toc-back-links 允许从标头链接到内容表
  86. --user-style-sheet <url> 指定一个用户样式表,以便加载每个页面
  87. --username <username> HTTP 身份认证的用户名
  88. --password <password> HTTP 身份认证的密码
  89. --viewport-size <> 设置窗口大小,需要自定义滚动条或 CSS 属性来自适应窗口大小
  90. --window-status <windowStatus> 等到window.status等于这个字符串前渲染页面
  91. --zoom <float> 设置转换成 PDF 时页面的缩放比例(默认值为:1)
  92. --default-header 添加一个默认的页眉,左边是页面的名称,右边是页码,是下面的缩写:
  93. --header-left='[webpage]'
  94. --header-right='[page]/[toPage]'
  95. --top 2cm
  96. --header-line
  97. 页眉和页脚选项:
  98. --footer-left <text> 居左显示页脚文本
  99. --footer-center <text> 居中显示页脚文本
  100. --footer-right <text> 居右显示页脚文本
  101. --footer-font-name <name> 设置页脚的字体名称(默认值为:Arial)
  102. --footer-font-size <size> 设置页脚的字体大小(默认值为:12)
  103. --footer-html <url> 添加一个 HTML 作为页脚
  104. --footer-line 在页脚上方显示一条直线
  105. --no-footer-line 不在页脚上方显示一条直线(默认设置)
  106. --footer-spacing <real> 设置页脚与内容之间的间距,单位毫米(mm)(默认值为:0)
  107. --header-left <text> 居左显示页眉文本
  108. --header-center <text> 居中显示页眉文本
  109. --header-right <text> 居右显示页眉文本
  110. --header-font-name <name> 设置页眉的字体名称(默认值为:Arial)
  111. --header-font-size <size> 设置页眉的字体大小(默认值为:12)
  112. --header-html <url> 添加一个 HTML 作为页眉
  113. --header-line 在页眉下方显示一条直线
  114. --no-header-line 不在页眉下方显示一条直线(默认设置)
  115. --header-spacing <real> 设置页眉与内容之间的间距,单位毫米(mm)(默认值为:0)
  116. --replace <name> <value> 在页眉和页脚中替换指定名称的值(可以重复使用该参数指定多个需要替换的名称和值)
  117. 内容表选项:
  118. --disable-dotted-lines 不要在 TOC 中使用虚线
  119. --toc-header-text <text> 设置 TOC 的标题文本(默认值为:内容表)
  120. --toc-level-indentation <width> TOC 缩进每一级的标题长度(默认值为:1em)
  121. --disable-toc-links TOC 中不生成指向内容锚点的超链接
  122. --toc-text-size-shrink <real> TOC 中的每一级标题,字体按这个比例缩放(默认值为:0.8)
  123. --xsl-style-sheet <file> 使用指定的 XSL 样式表打印内容表
  124. 页眉和页脚:
  125. 页眉和页脚可以使用参数 --header-* --footer-* 添加到文档中。
  126. 有些参数也需要提供一个字符串 text 作为参数值。例如:--header-left
  127. 可以在 text 中使用以下变量,将会把以下变量替换为对应的值。
  128. * [page] 当前正在打印的页面的页码
  129. * [frompage] 打印的第一页的页码
  130. * [topage] 打印的最后一页的页码
  131. * [webpage] 当前正在打印的页面的 URL
  132. * [section] 当前正在打印的章节的名称
  133. * [subsection] 当前正在打印的分段的名称
  134. * [date] 本地系统格式的当前日期
  135. * [isodate] ISO 8601 扩展格式的当前日期
  136. * [time] 本地系统格式的当前时间
  137. * [title] 当前页对象的标题
  138. * [doctitle] 输出文档的标题
  139. * [sitepage] 当前正在处理的对象中当前页面的页码
  140. * [sitepages] 当前正在处理的对象中的总页数
  141. 举个例子:
  142. --header-right "Page [page] of [toPage]"
  143. 会在页面的右上角生成一个类似 Page x of y 的字符串,
  144. 其中 x 是当前页面的页码, y 是当前文档最后一页的页码。

五、推荐

下面推荐两篇比较好的文章,一篇是官网的英文版的介绍,另一篇是中文版的介绍。
具体的大家自己去看吧。
英文版推荐:https://wkhtmltopdf.org/usage/wkhtmltopdf.txt
中文版推荐:http://www.jianshu.com/p/4d65857ffe5e