C# WPF 使用CEFSharp、free spire.pdf插件
前导:新项目要求使用 C#制作一个嵌套浏览器的壳子然后连接打印机打印,而且打印的时候要求在网页点击按钮之后不提示任何弹窗就将内容输出并打印。
技术:C# WPF
插件:CEFSharp浏览器内核插件、free spire.pdf Windows桌面应用PDF文件处理插件以及PdfiumViewer文件打印三方包
遇到的最大的问题就是free spire.pdf 对文件处理的限制问题。
最开始接到任务我有点懵,因为之前做过web-view2的桌面应用,但是web-view2的限制过大,而且好像没有办法让网页和桌面应用程序进行交互!
所以搜索了一顿之后查到了可以使用 CEFSharp 三方包当做浏览器的内核使用。
最开始的网上写了好多网页与C#的交互
//注册C#对象 browser.JavascriptObjectRepository.Register("cs",obj,false,CefSharp.BindingOptions.DefaultBinder);
但是运行之后会报错,他会提示你这个方法已经过期了,而且在VS中也非常贴心的把解决方案的网址告诉你了(github网址,如果不会科.学.上网的访问有点卡。)
查看之后更改为以下代码即可访问:
this.Browser.JavascriptObjectRepository.Settings.LegacyBindingEnabled = true; this.Browser.JavascriptObjectRepository.Register("printer", new PrinterObject(), isAsync: true, options: BindingOptions.DefaultBinder);
解释一下,这里的Browser是我在.xaml文件中的CEFSharp控件、“printer”是网页端调用的对象名称,PrinterObject是我自定义个一个类,他当中包含方法、变量等。
在网页当中直接调用(因为我自定义的PrinterObject类中有一个Print(string html)方法)即可:
printer.Print(html)
别担心他报错,因为你没有在js中定义printer。这样就完成了网页端与C#的单向交互。因为公司业务问题,不需要有C#控制网页端,所以这里也就没再实践,如果大家有兴趣可以百度一下,网上有很多,这里不再赘述了!
【可能是我没有找到的问题,CEFSharp的浏览器并没有访问状态返回,就比如说访问的服务器挂了,它这玩意返回的还是正常访问成功,如果可以希望官方能改进一下,增加一下页面跳转后判断返回访问状态。】
接下来就到了激动人心的时刻了,我们要将网页传过来的html打印出来。一开始想的是有没有方法直接将html打印出来,但是查了很多都行不通,最后还是走了html转pdf再打印的路子。
★★★★★ 这里如果有着急的看官可以直接跳过我接下来讲的 free spire.pdf。因为个人经过了一顿比较之后还是觉得他的限制太大好多功能没有不说,免费版的还限制10页。推荐 wkhtmltopdf 这个软件!直接用C#去调用它。
最开始找的是 spire.pdf 插件,但是它所有的页面都会有一行水印,网上说什么就第一页有,然后还有各种声称“没有水印、没有限制”的dll。都是骗子~!该踩的坑我已经都踩过了,愿诸位不要再去走一遍!
然后看到了这个免费的 free spire.pdf 插件。虽然在NuGet上显示作者不一样,但是个人认为他就是一家的,而且好像官方也证实了他们有一个免费的版本。
用上之后刚开始还挺开心,问题都解决了。但是紧跟着就是html转pdf分页的问题,虽然他有智能分页,但是恕我直言我觉得他并不是那么的智能。有的时候会有好大一片空白,然后第二页就两行字。这里就不得不夸奖一下wkhtmltopdf了(并没有收wkhtmltopdf的钱,只是站在一个开发者角度说的。),他的自动分页是真的很顶。基本上不会有什么特殊的问题,而且他支持html控制分页或不分页,这就很nice!
终于历经千辛万苦把分页的问题解决了,但是紧跟着他恶心的限制就出来了,免费版本只能操作10页以内的PDF。
因为我的项目甚至有可能会是十页二十页的PDF,所以这个插件被我放弃掉了。
其实也没有直接放弃,我还是写了一套循环,反正我是要打印出来,我直接让他多生成几个PDF就得了呗,按照顺序发给打印机,跟一个文件出来的效果是一样的。
/// <summary> /// 调用打印机打印 /// </summary> /// <param name="html"></param> private void Print(string html) { //Console.WriteLine("html:" + html); Thread thread = new Thread((ThreadStart)(() => { //加载PDF文档 PdfDocument pdf = new PdfDocument(); PdfHtmlLayoutFormat htmlLayoutFormat = new PdfHtmlLayoutFormat(); htmlLayoutFormat.IsWaiting = false; PdfPageSettings settings = new PdfPageSettings(); settings.Size = PdfPageSize.A4; pdf.LoadFromHTML(html, false, settings, htmlLayoutFormat); //获取第一页 PdfPageBase page = pdf.Pages[0]; List<PdfPageBase> list = new List<PdfPageBase>(); this.GetPdfPageToA4Page(page, list); PdfTextLayout format = new PdfTextLayout(); format.Break = PdfLayoutBreakType.FitPage; format.Layout = PdfLayoutType.Paginate; FileUtil.CheckDir(Common.Constant.ApplicationTempDataPath); string filename = StringUtil.GenerateRandomStr(10); int index = 0; foreach (PdfPageBase pdfPage in list) { //加载PDF文档 PdfDocument doc = new PdfDocument(); //移除新文档的页边距 doc.PageSettings.Margins.All = 0; //设置新文档页面的宽度等于原文档第一页的宽度,页面高度等于原文档第一页高度的一半 doc.PageSettings.Size = PdfPageSize.A4; //添加新页面到新文档 PdfPageBase newPage = doc.Pages.Add(); //根据原文档第一页创建模板,并将模板画到新文档的新添加页面,页面画满之后自动分页 pdfPage.CreateTemplate().Draw(newPage, new PointF(0, 0), format); doc.SaveToFile(Common.Constant.ApplicationTempDataPath + @filename + "_" + (index++) + ".pdf"); doc.Dispose(); } pdf.Dispose(); for (int i = 0; i < list.Count;i++) { Print2(Common.Constant.ApplicationTempDataPath + @filename + "_" + i + ".pdf", PrinterName); } })); //设置Thread的State为STA thread.SetApartmentState(ApartmentState.STA); thread.Start(); thread.Join(); } private void GetPdfPageToA4Page(PdfPageBase page, List<PdfPageBase> list) { //加载PDF文档 PdfDocument doc = new PdfDocument(); int num = int.Parse(Math.Ceiling(page.Size.Height / PdfPageSize.A4.Height).ToString()); if (num > 9) { //移除新文档的页边距 doc.PageSettings.Margins.All = 0; //设置新文档页面的宽度等于原文档第一页的宽度,页面高度等于原文档第一页高度的一半 doc.PageSettings.Size = PdfPageSize.A4; doc.PageSettings.Height = PdfPageSize.A4.Height * ((num / 8) + 1); //添加新页面到新文档 PdfPageBase newPage = doc.Pages.Add(); PdfTextLayout format = new PdfTextLayout(); format.Break = PdfLayoutBreakType.FitPage; format.Layout = PdfLayoutType.Paginate; //根据原文档第一页创建模板,并将模板画到新文档的新添加页面,页面画满之后自动分页 page.CreateTemplate().Draw(newPage, new PointF(0, 0), format); for(int i = 0; i < doc.Pages.Count; i++) { PdfPageBase pdfPageBase = doc.Pages[i]; GetPdfPageToA4Page(pdfPageBase, list); } } else { list.Add(page); } }
这里分页我是直接粗暴的按照A4纸的大小直接分的,后期发现有问题,有的文字直接就被从中间分开了,这与人类的分页方式太不符了,所以最终这个方案还是被放弃掉了。
虽然方案被pass了,但是html转pdf这条路我觉得应该是没问题的。所以接下来就是引出 wkhtmltopdf 啦!
★★★★★ wkhtmltopdf
最一开始我是在写Java的时候用到过这个软件,一开始没用它是因为他还需要绑定安装,这个操作不是很舒适,想要一个安装包就解决所有问题。但是很明显我失败了,最后还是乖乖的用了它。
不看不知道啊,看过之后直拍大腿,咋就没早点选择它。
/// <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); ///这个路径为程序集的目录 string exePath = @"D:\\wkhtmltopdf\\wk\\wkhtmltopdf\\bin\\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 297 "); //页面高度297mm stringBuilder.Append(" --page-width 210 "); //页面宽度210mm stringBuilder.Append(" --footer-html " + AppDomain.CurrentDomain.BaseDirectory.ToString() + @"index2.html "); //设置html显示页脚//stringBuilder.Append(" --footer-line "); //页脚和内容之间显示一条直线 stringBuilder.Append(" --header-html " + AppDomain.CurrentDomain.BaseDirectory.ToString() + @"index1.html "); //设置html显示页眉 //stringBuilder.Append(" --header-center [page]/[topage] "); //设置居中显示页眉 stringBuilder.Append(" --header-line "); //页眉和内容之间显示一条直线 stringBuilder.Append(" --header-spacing 10 "); //页眉和内容之间的距离 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."); } }
调用的话:
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, @"E:\\001.pdf"); /// 把 HTML 文件转换为 PDF HtmlConvertToPdf(htmlUrl, @"E:\\002.pdf");
另外,以下是wkhtmltopdf的各种指令(就是我上面代码中,StringBuilder stringBuilder = new StringBuilder();下面写的)
全局选项: --collate 打印多个副本时进行检查(默认设置) --no-collate 打印多个副本时不进行检查 --cookie-jar 从指定的cookie JAR文件中读写 cookie 数据 --copies 打印 PDF 文件的份数(默认值为:1) --dpi 设置一个分辨率,对于 X11 系统没有作用(默认值为:96) --extended-help 相对 -H 参数的设置,显示更详细的说明文档 --grayscale 将生成灰度的 PDF 文档,占用空间小,但是不会有彩色 --help 显示帮助信息 --htmldoc 输出程序的 HTML 帮助文档 --image-dpi 当页面存在内嵌图片时,指定图像的分辨率(默认值为:600) --image-quality 当使用 JPEG 算法压缩图片时,指定图像的质量(默认值为:94) --license 输出授权许可信息并退出 --lowquality 生成低质量的 PDF/PS,能够减少最终生成文档所占用的存储空间 --manpage 输出程序的手册页 --quiet 静默模式,不输出任何信息 --read-args-from-stdin 从标准输入读取命令行参数 --readme 输出程序的 Readme 文档 --version 输出版本信息并退出 --no-pdf-compression 设置为不要对 PDF 对象使用无损压缩 --margin-bottom 设置页面的底边距,单位毫米(mm) --margin-left 设置页面的左边距 (默认值为:10mm) --margin-right 设置页面的右边距 (默认值为:10mm) --margin-top 设置页面的上边距,单位毫米(mm) --page-size 设置页面的大小,如:A4、Letter等(默认值为:A4) --page-height 设置页面高度,单位毫米(mm) --page-width 设置页面宽度,单位毫米(mm) --orientation 设置文档模式为风景或肖像(默认值为:肖像) --title 生成的 PDF 文档的标题(如果没有指定,则使用第一个文档的标题) 大纲选项: --dump-default-toc-xsl 转储到默认的 TOC xsl 样式表到标准输出文件 --dump-outline 将大纲转储到指定的文件(XML 文件) --outline 在生成的 PDF 文档中添加大纲(默认设置) --no-outline 不要在生成的 PDF 文档中添加大纲 --outline-depth 设置大纲的深度(默认值为:4) 页面选项: --allow 允许加载指定文件夹中的文件(可重复使用此参数指定多个文件) --background 输出页面背景到 PDF 文档(默认设置) --no-background 不输出页面背景到 PDF 文档 --bypass-proxy-for 设置主机的代理(可重复指定多个代理) --cache-dir Web缓存目录 --checkbox-checked-svg 使用指定的SVG文件渲染选中的复选框 --checkbox-svg 使用指定的SVG文件渲染未选中的复选框 --cookie 设置访问网页时额外的 cookie,value 应该是 url 编码的(可重复使用此参数指定多个 cookie) --custom-header 设置访问网页时额外的 HTTP 头(可重复使用此参数指定多个 HTTP 头) --custom-header-propagation 为每个资源请求添加自定义的 HTTP 头 --no-custom-header-propagation 不要为每个资源请求添加自定义的 HTTP 头 --debug-javascript 显示 JavaScript 调试输出的内容 --no-debug-javascript 不显示 JavaScript 调试输出的内容(默认设置) --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 延迟指定的时间,等待 JavaScript 执行完成,单位毫秒(ms)(默认值为:200) --load-error-handling 指定如何处理无法加载的页面:abort、ignore、skip(默认值为:abort) --load-media-error-handling 指定如何处理无法加载的媒体文件:abort、ignore、skip(默认值为:ignore) --disable-local-file-access 不允许一个本地文件加载其他的本地文件,使用命令行参数 --allow 指定的目录除外。 --enable-local-file-access 允许将本地文件转换到其他本地文件中读取(默认设置) --exclude-from-outline 不要将页面包含在内容表和大纲中 --include-in-outline 将页面包含在内容表和大纲中(默认设置) --page-offset 设置页码的起始值(默认值为:0) --minimum-font-size 设置最小的字体大小 --disable-plugins 禁用已安装的插件(默认设置) --enable-plugins 启用已安装的插件(但插件可能不起作用) --post 添加一个附加字段(可以重复使用该参数添加多个附加字段) --post-file 添加一个附加文件(可以重复使用该参数添加多个附加文件) --print-media-type 使用打印媒体类型代替屏幕 --no-print-media-type 不使用打印媒体类型代替屏幕(默认设置) --proxy 使用代理 --radiobutton-checked-svg 使用指定的SVG文件渲染选中的单选按钮 --radiobutton-svg 使用指定的SVG文件渲染未选中的单选按钮 --run-sript 在页面加载完成后运行这个额外的 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 指定一个用户样式表,以便加载每个页面 --username HTTP 身份认证的用户名 --password HTTP 身份认证的密码 --viewport-size <> 设置窗口大小,需要自定义滚动条或 CSS 属性来自适应窗口大小 --window-status 等到window.status等于这个字符串前渲染页面 --zoom 设置转换成 PDF 时页面的缩放比例(默认值为:1) --default-header 添加一个默认的页眉,左边是页面的名称,右边是页码,是下面的缩写: --header-left='[webpage]' --header-right='[page]/[toPage]' --top 2cm --header-line 页眉和页脚选项: --footer-left 居左显示页脚文本 --footer-center 居中显示页脚文本 --footer-right 居右显示页脚文本 --footer-font-name 设置页脚的字体名称(默认值为:Arial) --footer-font-size 设置页脚的字体大小(默认值为:12) --footer-html 添加一个 HTML 作为页脚 --footer-line 在页脚上方显示一条直线 --no-footer-line 不在页脚上方显示一条直线(默认设置) --footer-spacing 设置页脚与内容之间的间距,单位毫米(mm)(默认值为:0) --header-left 居左显示页眉文本 --header-center 居中显示页眉文本 --header-right 居右显示页眉文本 --header-font-name 设置页眉的字体名称(默认值为:Arial) --header-font-size 设置页眉的字体大小(默认值为:12) --header-html 添加一个 HTML 作为页眉 --header-line 在页眉下方显示一条直线 --no-header-line 不在页眉下方显示一条直线(默认设置) --header-spacing 设置页眉与内容之间的间距,单位毫米(mm)(默认值为:0) --replace 在页眉和页脚中替换指定名称的值(可以重复使用该参数指定多个需要替换的名称和值) 内容表选项: --disable-dotted-lines 不要在 TOC 中使用虚线 --toc-header-text 设置 TOC 的标题文本(默认值为:内容表) --toc-level-indentation 在 TOC 缩进每一级的标题长度(默认值为:1em) --disable-toc-links 在 TOC 中不生成指向内容锚点的超链接 --toc-text-size-shrink 在 TOC 中的每一级标题,字体按这个比例缩放(默认值为:0.8) --xsl-style-sheet 使用指定的 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://www.5axxw.com/wenku/k2/1070144b.html
另外,大家应该看到了我写的都是 --header-html 和 --footer-heml,那是不是说我没有办法在html中确定是哪页呢,看过大佬的帖子(http://www.iamlintao.com/6292.html)我做了一下更改
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script> function subst() { var vars = {}; var x = window.location.search.substring(1).split('&'); for (var i in x) { var z = x[i].split('=', 2); vars[z[0]] = unescape(z[1]); } var y = document.getElementsByClassName("page"); for (var j = 0; j < y.length; ++j) y[j].textContent = vars["page"]; if (vars["page"] == 1) { var y = document.getElementsByClassName("headtwo"); for (var j = 0; j < y.length; ++j) y[j].setAttribute("style", "display:none"); } else { var y = document.getElementsByClassName("headone"); for (var j = 0; j < y.length; ++j) y[j].setAttribute("style", "display:none"); } } </script> </head> <body onload="subst()"> <div class="headtwo" style="height:auto"> Page <span class="page"></span> of <span class="topage"></span> </div> <div class="headone" style="height:auto"> <image src="https://www.baidu.com/img/flexible/logo/pc/result.png"></image> </div> </body> </html>
这里我希望第一页和之后的页显示的页眉不同,所以我对index1.html做个更改。
而后pdf完美的生成结束了!!!撒花✿✿ヽ(°▽°)ノ✿
进入最后的打印环节了,这里我又得把free spire.pdf拿出来鞭尸。他不是限制10页之内么,我尝试了一下,我分多批给打印机发不就得了,每个PDF都不超过十页。但是!!!我万万没想到,它发一个的时候还可以,发多个之后打出来的竟然是像素块。也可能是我打印机的问题,具体原因不清楚。
而后我换成了PrintDocument ,虽然解决了模糊的问题,但是他竟然要在后台打开一下wps,这就很不友好,代码如下:
/// <summary> /// 调用打印机打印 /// </summary> /// <param name="PDFPath">PDF文件路径</param> /// <param name="PrinterName">打印机名称</param> private void Print2(string PDFPath, string PrinterName) { PrintDocument pd = new PrintDocument(); Process p = new Process(); ProcessStartInfo startInfo = new ProcessStartInfo(); startInfo.CreateNoWindow = true; startInfo.WindowStyle = ProcessWindowStyle.Hidden; startInfo.UseShellExecute = true; startInfo.FileName = PDFPath; startInfo.Verb = "print"; Console.WriteLine("PrinterName:"+ PrinterName); Console.WriteLine("pd.PrinterSettings.PrinterName", pd.PrinterSettings.PrinterName); startInfo.Arguments = @"/p /h \" + PDFPath + "\"\"" + PrinterName + "\""; p.StartInfo = startInfo; p.Start(); p.WaitForExit(); }
感谢大佬分享:https://copyfuture.com/blogs-details/20210803192914967b,但是也是因为看了这篇帖子下面的spire.pdf才走了一段弯路。
后来经过查看,发现了PdfiumViewer插件。但是!但是!但是!请大家注意
不要下载最新的2.13.0这个版本,这版本不知道为什么脑残的不两个.dll文件给删除了!!!
大家可以直接下载2.2.0版本,这个版本是有那两个文件的。下载完之后记得把两个文件的“复制到输出目录”给改成“始终复制”或“如果较新则复制”。
一下是调用打印机代码:
/// <summary> /// 调用打印机打印 /// </summary> /// <param name="PDFPath">PDF文件路径</param> /// <param name="PrinterName">打印机名称</param> private void Print2(string PDFPath, string PrinterName) { using (var document = PdfiumViewer.PdfDocument.Load(PDFPath)) { using (var printDocument = document.CreatePrintDocument()) { printDocument.PrinterSettings.PrinterName = PrinterName; printDocument.PrinterSettings.PrintFileName = FileUtil.GetFileName(PDFPath); printDocument.DocumentName = PDFPath; printDocument.PrintController = new StandardPrintController(); printDocument.Print(); } document.Dispose(); } FileUtil.DeleteFile(PDFPath); }
到此!
所有功能都已实现,完美结束!撒花✿✿ヽ(°▽°)ノ✿
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现