1

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.");
    }
}
1
  • The "OKI docs" refer to "custom size" as width: 64 to 218; length: 90 to 1321. Your numbers appear to be outside that range ... but I'm no OKI expert. Commented Mar 27 at 14:59

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.