You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过EMF打印带自定义纸张尺寸的WPF Canvas(不使用DEVMODE)

自定义纸张尺寸的WPF打印任务实现问题

我正在开发一款WPF应用,需要向USB连接的打印机发送自定义纸张尺寸的打印任务。打印机及其驱动支持该功能(已通过第三方软件验证),但无法通过编程实现。

逆向分析可行应用:

分析了一款可成功实现该功能的第三方应用:

  • 创建基于EMF的.SPL假脱机文件
  • 未使用SetPrinter()或DocumentProperties()
  • EMF中包含定义自定义页面边界的EMR_HEADER
  • 使用EMR_SETWORLDTRANSFORM进行布局/缩放

期望实现目标:

  1. 将WPF Canvas(或视觉元素)渲染为EMF文件
  2. 在EMF元数据中直接嵌入非标准纸张尺寸
  3. 将该EMF发送至打印机,让驱动识别嵌入的尺寸——不依赖DEVMODE

问题:

如何从WPF Canvas创建打印任务并传递给打印机,同时设置自定义纸张尺寸?尺寸将通过编程设置,且在打印机允许的范围内。

我使用的是OKI B432打印机,在打印机设置中有一个名为“User Defined Size”的纸张尺寸选项,选择后会弹出对话框允许手动输入具体尺寸,这可能是我需要针对的纸张尺寸选项,除非可以完全绕过它。

提前感谢任何建议!

已尝试方案:

  • 直接通过WPF中的PrintDialog和XpsDocumentWriter打印——打印机默认使用A4,忽略自定义尺寸。
  • 打印前手动设置DEVMODE参数——不可靠且因打印机而异。
  • 使用System.Drawing.Printing生成EMF——但仍无法强制使用自定义尺寸。

已尝试代码

EMFHelper.cs

using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Imaging;

public static class EMFHelper
{
    public static void RenderCanvasToEMF(Canvas canvas, string emfPath, float widthMm, float heightMm)
    {
        int widthPx = (int)(canvas.ActualWidth);
        int heightPx = (int)(canvas.ActualHeight);

        using (Graphics refGraphics = Graphics.FromHwnd(IntPtr.Zero))
        {
            IntPtr hdc = refGraphics.GetHdc();
            var frameRect = new Rectangle(0, 0, (int)(widthMm * 100), (int)(heightMm * 100)); // hundredths of mm

            using (Metafile metafile = new Metafile(emfPath, hdc, frameRect, MetafileFrameUnit.Millimeter))
            using (Graphics g = Graphics.FromImage(metafile))
            {
                g.Clear(Color.White);

                RenderTargetBitmap renderBitmap = new RenderTargetBitmap(widthPx, heightPx, 96, 96, PixelFormats.Pbgra32);
                renderBitmap.Render(canvas);

                var encoder = new PngBitmapEncoder();
                encoder.Frames.Add(BitmapFrame.Create(renderBitmap));

                using (MemoryStream ms = new MemoryStream())
                {
                    encoder.Save(ms);
                    using (Bitmap bitmap = new Bitmap(ms))
                    {
                        g.DrawImage(bitmap, 0, 0, widthPx, heightPx);
                    }
                }
            }

            refGraphics.ReleaseHdc(hdc);
        }
    }
}

EMFPrinter.cs

using System.Drawing.Printing;
using System.Drawing;

public static class EMFPrinter
{
    public static void PrintEMF(string emfPath, string printerName)
    {
        using (PrintDocument printDoc = new PrintDocument())
        {
            printDoc.PrinterSettings.PrinterName = printerName;

            // 400mm ≈ 1574, 120mm ≈ 472 (hundredths of an inch)
            PaperSize customSize = new PaperSize("User Defined Size", 1574, 472);
            printDoc.DefaultPageSettings.PaperSize = customSize;
            printDoc.DefaultPageSettings.Landscape = false;

            printDoc.PrintPage += (sender, e) =>
            {
                using (Metafile mf = new Metafile(emfPath))
                {
                    e.Graphics.DrawImage(mf, e.PageBounds);
                }
            };

            printDoc.Print();
        }
    }
}

MainWindow.xaml.cs (Print Button)

private void PrintButton_Click(object sender, RoutedEventArgs e)
{
    if (printerComboBox.SelectedItem is string printerName)
    {
        string emfPath = System.IO.Path.Combine(System.IO.Path.GetTempPath(), "CanvasPrint.emf");

        // 1. Render canvas to EMF with 400mm x 120mm
        EMFHelper.RenderCanvasToEMF(myCanvas, emfPath, 400, 120);

        // 2. Send EMF to printer
        EMFPrinter.PrintEMF(emfPath, printerName);

        MessageBox.Show("EMF sent to printer.");
    }
    else
    {
        MessageBox.Show("Please select a printer.");
    }
}

内容的提问来源于stack exchange,提问作者Tom

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 16:06:02