将HTML呈现并打印到非默认打印机

时间:2017-02-22 11:55:58

标签: c# wpf printing

我有一个C#WPF .NET 4.6程序,可以创建HTML文件,我想用一个已知的非默认打印机自动打印它们(没有对话框)。当然,这包括首先呈现HTML。由于程序会创建这些文件,因此HTML数据可以来自MemoryStreamFileStream,也可以直接来自字符串。

该程序具有允许用户使用System.Drawing.Printing.PrinterSettings.InstalledPrinters指定要打印到哪个打印机的设置,因为每个文件可能需要不同的打印机。在打印时,打印机的名称是已知的,但可能与Windows默认打印机不同。

我已经研究了很多其他项目,但它们似乎并没有说明打印机与默认设置不同。更改默认打印机将是反社会的,并将导致与线程相关的痛苦世界。这似乎是#1接受的解决方案,虽然不是最好的解决方案??

研究和解决方案着眼于:

Printing the contents of a WPF WebBrowserSilently print HTML from WPF WebBrowsercorresponding MSDN forum discussions 由于COM ExecWB功能仅打印到默认打印机(?)

,因此不够用

MSDN example仅使用WebBrowser上的Print()命令,该命令再次使用默认打印机。

所以我走了试图改变打印机选项的路线。 Programmatically changing the destination printer for a WinForms WebBrowser control被问到,但答案相当令人不满意,因为链接断了,我不知道正在运行的计算机有哪些外部程序,所以我不能保证Adobe,OpenOffice等OP提到他们解析了ActiveX COM而没有详细说明。听起来很棘手。

也许我可以通过写入this project之类的RichTextBox来解决一些问题,并隐藏这个框?

我认为Silent print HTML file in C# using WPF处于良好的路径上,但原始帖子的屏幕尺寸有硬编码,而OP提到打印机切断了文档。接受(和重新计算)的答案再次使用ExecWB默认打印机设置方法。

execCommand("Print", false, IDon'tUnderstandThisArgument)也显示了承诺,因为其答案是更新后的MSDN answer,但发送到打印机的文件流不允许HTML,DocumentStream也不允许WebBrowser似乎工作(打印机打印一个空白页)。

除了将注册表更改为解决方案外,

How do I programatically change printer settings with the WebBrowser control?对我的要求非常相似。

除了研究其他人如何做到这一点之外,我还尝试直接打印WPF WebBrowser,因为它是Visual控件:

    public static bool Print(string printer, Visual objToPrint)
    {
      if (string.IsNullOrEmpty(printer))
      {
        return false;
      }

      var dlg = new PrintDialog
      {
        PrintQueue = new PrintServer().GetPrintQueue(printer)
      };

      dlg.PrintTicket.CopyCount = 1;
      dlg.PrintTicket.PageOrientation = PageOrientation.Portrait;
      dlg.PrintTicket.PagesPerSheet = 1;

      dlg.PrintVisual(objToPrint, "Print description");
      return true;
    }

然而,这不会打印任何内容(因为WebBrowser不可见?)。 并尝试了PrintDocument作为更新的MSDN article建议:

    public static async Task<bool> PrintHTMLAsync(string printer, string html)
    {
      bool result;
      using (var webBrowser = new System.Windows.Forms.WebBrowser())
      {
        webBrowser.DocumentCompleted += ((sender, e) => browserReadySemaphore.Release());
        byte[] buffer = Encoding.UTF8.GetBytes(html);
        webBrowser.DocumentStream = new MemoryStream(buffer);

        // Wait until the page loads.
        await browserReadySemaphore.WaitAsync();

        try
        {
          using (PrintDocument pd = new PrintDocument())
          {
            pd.PrinterSettings.PrinterName = printer;
            pd.PrinterSettings.Collate = false;
            pd.PrinterSettings.Copies = 1;
            pd.PrinterSettings.FromPage = 1;
            pd.PrinterSettings.ToPage = 1;
            pd.Print();
            result = true;
          }
        }
        catch (Exception ex)
        {
          result = false;
          Debug.WriteLine(ex);
        }

        return result;
      }
    }

没有快乐。

我还使用了PRINT DOS命令:

public static string PerformSilentPrinting(string fileName, string printerName)
{
  try
  {
    ProcessStartInfo startInfo = new ProcessStartInfo(fileName)
    {
      Arguments = string.Format("/C PRINT /D:\"{0}\" \"{1}\"", printerName, fileName),
      FileName = "cmd.exe",
      RedirectStandardOutput = true,
      UseShellExecute = false,
      WindowStyle = ProcessWindowStyle.Hidden,
    };

    // Will execute the batch file with the provided arguments
    Process process = Process.Start(startInfo);

    // Reads the output        
    return process.StandardOutput.ReadToEnd();
  }
  catch (Exception ex)
  {
    return ex.ToString();
  }
}

但是打印命令似乎只接受文本文件。

2 个答案:

答案 0 :(得分:1)

我找到的最佳解决方案是here

这样可以无声地打印。但是,这不允许您更改默认打印机。因此,作为解决方法,您可以将默认打印机设置为您要使用的打印机,然后在完成打印后再切换回来。

我会是这样的:

protected static class PrinterSetter
    {
        [DllImport("winspool.drv", CharSet = CharSet.Auto, SetLastError = true)]
        public static extern bool SetDefaultPrinter(string Name);
    }

然后在上面链接中引用的代码中添加:

if (wb != null)
            {
                PrinterSettings settings = new PrinterSettings();
                string defaultPrinter = settings.PrinterName;

                Printer.SetDefaultPrinter("Microsoft Print to PDF");

                wb.ExecWB(OLECMDID_PRINT, OLECMDEXECOPT_DONTPROMPTUSER, null, null);


                (new System.Action(() =>
                {
                    Thread.Sleep(5000);
                    Printer.SetDefaultPrinter(defaultPrinter);
                })).BeginInvoke(null, null);

            }

答案 1 :(得分:0)

编辑:如果您只需打印一页A4,此解决方案效果很好。但是它只打印一页,并截断任何页面。

最后,我使用WinForms WebBrowser,将控件复制到位图中,然后使用PrintDialog打印,System.Windows.Forms也位于using Microsoft.Win32; using System; using System.ComponentModel; using System.Drawing; using System.Drawing.Printing; using System.IO; using System.Threading; using System.Threading.Tasks; using System.Windows.Forms; public static class PrintUtility { private static readonly SemaphoreSlim browserReadySemaphore = new SemaphoreSlim(0); // A4 dimensions. private const int DPI = 600; private const int WIDTH = (int)(8.3 * DPI); private const int HEIGHT = (int)(11.7 * DPI); public static void Print(this Image image, string printer, bool showDialog = false) { if (printer == null) { throw new ArgumentNullException("Printer cannot be null.", nameof(printer)); } using (PrintDialog printDialog = new PrintDialog()) { using (PrintDocument printDoc = new PrintDocument()) { printDialog.Document = printDoc; printDialog.Document.DocumentName = "My Document"; printDialog.Document.OriginAtMargins = false; printDialog.PrinterSettings.PrinterName = printer; printDoc.PrintPage += (sender, e) => { // Draw to fill page e.Graphics.DrawImage(image, 0, 0, e.PageSettings.PrintableArea.Width, e.PageSettings.PrintableArea.Height); // Draw to default margins // e.Graphics.DrawImage(image, e.MarginBounds); }; bool doPrint = !showDialog; if (showDialog) { var result = printDialog.ShowDialog(); doPrint = (result == DialogResult.OK); } if (doPrint) { printDoc.Print(); } } } } public static async Task<bool> RenderAndPrintHTMLAsync(string html, string printer) { bool result = false; // Enable HTML5 etc. (assuming we're running IE9+) SetFeatureBrowserFeature("FEATURE_BROWSER_EMULATION", 9000); // Force software rendering SetFeatureBrowserFeature("FEATURE_IVIEWOBJECTDRAW_DMLT9_WITH_GDI", 1); SetFeatureBrowserFeature("FEATURE_GPU_RENDERING", 0); using (var webBrowser = new WebBrowser()) { webBrowser.ScrollBarsEnabled = false; webBrowser.Width = WIDTH; webBrowser.Height = HEIGHT; webBrowser.DocumentCompleted += ((s, e) => browserReadySemaphore.Release()); webBrowser.LoadHTML(html); // Wait until the page loads. await browserReadySemaphore.WaitAsync(); // Save the picture using (var bitmap = webBrowser.ToBitmap()) { bitmap.Save("WebBrowser_Bitmap.bmp"); Print(bitmap, printer); result = true; } } return result; } /// <summary> /// Make a Bitmap from the Control. /// Remember to dispose after. /// </summary> /// <param name="control"></param> /// <returns></returns> public static Bitmap ToBitmap(this Control control) { Bitmap bitmap = new Bitmap(control.Width, control.Height); Rectangle rect = new Rectangle(0, 0, control.Width, control.Height); control.DrawToBitmap(bitmap, new Rectangle(0, 0, control.Width, control.Height)); return bitmap; } /// <summary> /// Required because of a bug where the WebBrowser only loads text once or not at all. /// </summary> /// <param name="webBrowser"></param> /// <param name="htmlToLoad"></param> /// <remarks> /// http://stackoverflow.com/questions/5362591/how-to-display-the-string-html-contents-into-webbrowser-control/23736063#23736063 /// </remarks> public static void LoadHTML(this WebBrowser webBrowser, string htmlToLoad) { webBrowser.Document.OpenNew(true); webBrowser.Document.Write(htmlToLoad); webBrowser.Refresh(); } /// <summary> /// WebBrowser Feature Control /// </summary> /// <param name="feature"></param> /// <param name="value"></param> /// <remarks> /// http://stackoverflow.com/questions/21697048/how-to-fix-a-opacity-bug-with-drawtobitmap-on-webbrowser-control/21828265#21828265 /// http://msdn.microsoft.com/en-us/library/ie/ee330733(v=vs.85).aspx /// </remarks> private static void SetFeatureBrowserFeature(string feature, uint value) { if (LicenseManager.UsageMode != LicenseUsageMode.Runtime) { return; } var appName = Path.GetFileName(System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName); Registry.SetValue( @"HKEY_CURRENT_USER\Software\Microsoft\Internet Explorer\Main\FeatureControl\" + feature, appName, value, RegistryValueKind.DWord); } } 命名空间中。

package org.foo.bar