【问题标题】:Render and Print HTML to non-default printer将 HTML 渲染并打印到非默认打印机
【发布时间】:2017-07-12 10:57:29
【问题描述】:

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

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

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


研究和解决方案:

Printing the contents of a WPF WebBrowserSilently print HTML from WPF WebBrowsercorresponding MSDNforum discussions 不足,因为 COM ExecWB 函数仅打印到默认打印机 (?)

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

所以我尝试更改打印机选项。 Programmatically changing the destination printer for a WinForms WebBrowser control 被问到了,但答案不太令人满意,因为它的链接断开了,而且我不知道正在运行的计算机有哪些外部程序,所以我不能保证 Adob​​e、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,来自 WebBrowserDocumentStream 似乎也不起作用(打印机打印一个单个空白页)。

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();
  }
}

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

【问题讨论】:

    标签: c# wpf printing


    【解决方案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);
    
                }
    

    【讨论】:

      【解决方案2】:

      编辑:如果您只需要打印一页 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);
        }
      }
      

      【讨论】:

      • 在formload中调用异常> webbrowser文档为空!!
      • 文档为空修复....在webBrowser.DocumentCompleted += ((s, e) ....上方添加这一行webBrowser.DocumentText = "fix Document is null";
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-05-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多