如何通过EMF打印带自定义纸张尺寸的WPF Canvas(不使用DEVMODE)
自定义纸张尺寸的WPF打印任务实现问题
我正在开发一款WPF应用,需要向USB连接的打印机发送自定义纸张尺寸的打印任务。打印机及其驱动支持该功能(已通过第三方软件验证),但无法通过编程实现。
逆向分析可行应用:
分析了一款可成功实现该功能的第三方应用:
- 创建基于EMF的
.SPL假脱机文件 - 未使用
SetPrinter()或DocumentProperties() - EMF中包含定义自定义页面边界的
EMR_HEADER - 使用
EMR_SETWORLDTRANSFORM进行布局/缩放
期望实现目标:
- 将WPF Canvas(或视觉元素)渲染为EMF文件
- 在EMF元数据中直接嵌入非标准纸张尺寸
- 将该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
相关产品推荐
相关产品推荐

