• 热门搜索 热门搜索
菜单

您现在的位置是:博客 > 文章详情文章详情

C# 使用 wkhtmltopdf 将HTML文本或文件转换为PDF

原创
时间2023/09/02 00:42:49 发布 预览数量417
分类: c# .net 标签: PDF

一、简介

最近遇到一个需求, 要求将执行页面导出成pdf文件,刚开始为了响应速度, 选择使用客户端导出,也找到了一些jquery包, 但是效果不是那么理想,无奈只能试着找服务器端导出,于是了解到 wkhtmltopdf.exe 这个工具,这个工具比起之前的那种方法简直是太好用了。

它是一个使用 Qt WebKit 引擎做渲染的,能够把 HTML 文档转换成 PDF 文档或图片(image) 的命令行工具,且要还支持网页的js代码和css代码的渲染,简直不能在爽了,并且支持多个平台,可在 windows、linux 等系统下运行。

你可以从这里获取到它:https://wkhtmltopdf.org/downloads.html

二、安装

下载完成之后你需要先安装它,然后你就能获取到 wkhtmltopdf.exe 这个文件了,还包括有一个 wkhtmltoimage.exe 文件,第一个文件是把 HTML 文档转换为 PDF 文档的,后一个文件是把 HTML 文档转换为图片的(Image),使用方法类似,只是调用的文件不一样而已,这里就不多做介绍。

三、使用

废话不多说,先上代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Diagnostics;

namespace Demo
{
    /// <summary>
    /// 网页或html代码转换类
    /// </summary>
    public class HtmlConvert
    {

        /// <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/wkhtmltopdf.exe";
            if (!File.Exists(exePath))
            {
                throw new Exception("没有找到wkhtmltopdf.exe应用程序");
            }

            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">html路径,可以是本地路径,也可以是网络地址</param>
        /// <param name="savePath">PDF文件保存的路径</param>
        /// <returns></returns>
        private string GetArguments(string htmlPath, string savePath)
        {
            if (string.IsNullOrEmpty(htmlPath))
            {
                throw new Exception("HTML本地路径或网络地址不能为空.");
            }

            if (string.IsNullOrEmpty(savePath))
            {
                throw new Exception("PDF文档保存的路径不能为空.");
            }

            StringBuilder stringBuilder = new StringBuilder();
            stringBuilder.Append(" --page-height 200 ");        //页面高度100mm
            stringBuilder.Append(" --page-width 250 ");         //页面宽度100mm
            stringBuilder.Append(" --header-center 我是页眉(www.51baidu.com.cn) ");  //设置居中显示页眉
            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("此方法用于生成PDF文件.");
            }

            fileName = Path.GetFileName(savePath);
            if (string.IsNullOrEmpty(fileName))
            {
                throw new Exception("文件名为空.");
            }

            try
            {
                path = savePath.Substring(0, savePath.IndexOf(fileName));
                if (!Directory.Exists(path))
                {
                    Directory.CreateDirectory(path);
                }
            }
            catch
            {
                throw new Exception("文件路径不存在.");
            }
        }

        /// <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文本内容不能为空.");
            }

            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文本内容错误.");
            }
        }

    }
}

调用

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;


namespace Demo
{
    class Program
    {
        /// <summary>
        /// 白码驿站 (www.51baidu.com.cn)
        /// </summary>
        /// <param name="args"></param>
        static void Main(string[] args)
        {
            string strHtml = @"<p style='color:red;text-align:center;background-color:#000000;'>Hello World!<p>";
            string htmlUrl = "https://www.51baidu.com.cn";

            HtmlConvert convert = new HtmlConvert();

            //把 HTML 文本内容转换为 PDF
            convert.HtmlTextConvertToPdf(strHtml, AppDomain.CurrentDomain.BaseDirectory.ToString() + @"File/strHtml.pdf");

            //把 HTML 文件转换为 PDF
            convert.HtmlConvertToPdf(htmlUrl, AppDomain.CurrentDomain.BaseDirectory.ToString() + @"File/htmlUrl.pdf");
        }
    }
}

命令行参数

全局选项:
--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 是当前文档最后一页的页码。

四、Demo下载

地址:https://pan.baidu.com/s/1IrTd1T4Jzv08qAyK1Q44ow
提取码:p5fp

版权声明:本文为Converts的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。

原文链接:https://www.converts.cn/article/13556.html

暂无评论

暂无评论

目录

推荐阅读

  • .net 项目发布中的 Debug和Release的区别是什么?

    ![.net 项目发布中的 Debug和Release的区别是什么?](/ArticleFile/2023-09-09/93b4042444ff40aba6e7c913250e425f.png '.net 项目发布中的 Debug和Release的区别是什么?') 一、Debug 版本 Debug 是“调试”的意思,

  • Vs2022 设置类、接口默认创建的模板

    一、前言 在团队协作中,每个人都负责这自己模块的代码。为了清楚的分辨代码是谁开发的, 我们往往会在类、接口增加一个额外的注释信息,例如:创建人、创建时间、描述等等。所以有些时候,我们希望 visual Studio 在创建类,接口的时候,生成的代码文件能更丰富一下, 例如:自动增加基础的注释功能, 类文件自动增加 p

  • 如何使用 vs 2022 远程调试 Linux系统中的Docker容器项目

    一、前言 在项目上,总会遇到一些奇怪的问题,例如:“在本地好好的, 为什么部署到线上就不行” 等等,这样的问题时长困扰着我们。但是作为一个资深的码农,“远程调试” 是一个不可或缺的手段。下面就来看下如果使用 vs 在本地远程调试部署在Linux系统中,运行在Docker容器中的项目吧。 二、远程调试 调试的前提,

  • 移除Linux系统启动时的等待时间

    一、前言 VMware 虚拟机中安装了很多Linux 系统,每次启动的时候, 都卡在系统选择的界面上,除非手动选择, 否则要等很久才会进入系统,如下图所示: ![移除Linux系统启动时的等待时间](/ArticleFile/2024-06-24/e3ab02212cc946bd9c23159895177fb3.png '移除Linux系统启动时的等待时间') 二、关闭系统等待的事件 我们

  • GitLab 私有化部署

    一、GitLab 概述 git作为目前最流行的代码管理工具,已经成为了程序员必备的技能。虽然目前有 github(对国人不太友好) 、gitee 两个比较大的代码托管平台。但是出于各种原因,搭建自己的私有代码平台也成了一部分开发人员的选择。针对这种需求,这里为大家介绍的是其中的佼佼者gitlab,也是我们公司内部正在使用的一个代码管理平台。 GitLab是一个基于Git的开源代码管理平台,它

  • 云服务器购买按量付费实例,并搭建私有网络图解

    一、前言 最近在自学 k8s 集群化部署, 奈何云服务器包年包月太贵了, 学习成本飙升。好在各大云服务商支持按量计费 , 接下来就带大家一起过一下购买流程,并且使用私有网络。 二、私有网络 在购买云服务器之前,先了解下什么是私有网络。官网给出的解释是:私有网络(Virtual Private Cloud,VPC)是一块在云服务器上自定义的逻辑隔离网络空间,可以使云服务器, 云数据库资源构建逻

  • Docker 部署FastTunnel,实现内网穿透

    一、前言 最近在学习搭建Elasticsearch集群,但是发现云服务(2核4G)资源根本就不够用,部署上去就直接宕机了。想着服务器资源太贵, 家里刚好有一台64G内存的闲置电脑。不如做一个内网穿透,可以远程访问。工作学习两不误。其实目前市面上已经有很多穿透工具了,比如向日葵~~ ,但是奈何带宽太小了,免费的才1M~~~ 二、什么是 FastTunnel FastTunnel 是用.net

  • IP地址和子网掩码的关系

    一、概述 IP地址(Internet Protocol Address)和子网掩码(Subnet Mask)是计算机网络中两个核心的概念,它们共同工作以确保数据能够正确地在复杂的网络环境中传输到目标设备。 二、IP地址 `IP地址`是IP协议提供的一种统一的地址格式,它为互联网上的每一个网络和每一台主机分配一个逻辑地址,以此来屏蔽物理地址的差异。它是一个32位的二进制数,但是在习惯上,我们

  • 修改Docker默认的存储驱动程序目录

    一、前言 今天使用jenkins对程序进行更新的时候,发现更新失败。经排查是因为系统磁盘满了,然而通过堡垒机看了下,系统空间还有很多,于是看了下系统分区: ![修改Docker默认的存储驱动程序目录](/ArticleFile/2024-06-06/444ede8a1b1b42058db18690fa995952.png '修改Docker默认的存储驱动程序目录') ![修改Docker默认

  • Windows 安装git的详细安装步骤 ,以及TortoiseGit 图形化工具

    一、前言 `Git` 是一个开源的分布式版本控制系统,用于有效、高速地处理从很小到非常大的项目版本管理。Git 与常用的版本控制工具 CVS, Subversion 等不同,它采用了分布式版本库的方式,不需要服务器端软件支持。 二、安装Git 1、Git 下载地址 [Git 下载地址](https://git-scm.com/downloads "Git 下载地址") ![Windows

加载中