最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

winapi - How to print WPF Canvas with custom paper size via EMF (without using DEVMODE)? - Stack Overflow

programmeradmin15浏览0评论

I'm developing a WPF application that needs to send print jobs to a USB-connected printer using custom paper sizes. The printer and its driver support this (verified using 3rd party software), but I can't get it to work programmatically.

Reverse engineering a working app:

Analyzed a third-party app that successfully handles this:

  • Creates EMF-based .SPL spool files
  • No use of SetPrinter() or DocumentProperties()
  • EMF includes EMR_HEADER defining custom page bounds
  • Uses EMR_SETWORLDTRANSFORM for layout/scaling

What I want to achieve:

  1. Render a WPF Canvas (or visual) into an EMF file
  2. Embed a non-standard paper size directly in the EMF metadata
  3. Send that EMF to the printer and have the driver respect the embedded size — without relying on DEVMODE

Questions:

  • How can I create a print job from a WPF Canvas and pass it to the printer with a custom paper size? The size will be set programmatically and will be within the allowable bounds set by the printers.

I am using an OKI B432 printer. Within the printer settings, there is a paper size option called 'User Defined Size'. When I select this, a dialog pops up allowing me to enter a specific size manually. This might be the paper size option I target unless it's bypassed completely.

Thanks in advance for any tips!

What I've tried:

  • Printing directly via PrintDialog and XpsDocumentWriter in WPF — the printer defaults to A4 and ignores custom sizes.
  • Setting DEVMODE parameters manually before printing — unreliable and varies by printer.
  • Using System.Drawing.Printing to generate EMF — but still couldn't enforce a custom size.

Code I have tried

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.");
    }
}
发布评论

评论列表(0)

  1. 暂无评论