印刷质量winform

尝试从WinForms应用程序打印时遇到2个问题。 无论我尝试什么,第一个是非常糟糕的质量。 第二个是我左上角有一个很大的页边距,而winform正在切割。 有任何想法吗? 这是我的代码:

Bitmap MemoryImage; public void GetPrintArea(Panel pnl) { MemoryImage = new Bitmap(pnl.Width, pnl.Height); Rectangle rect = new Rectangle(0, 0, pnl.Width, pnl.Height); pnl.DrawToBitmap(MemoryImage, new Rectangle(0, 0, pnl.Width, pnl.Height)); } protected override void OnPaint(PaintEventArgs e) { if (MemoryImage != null) { e.Graphics.DrawImage(MemoryImage, 0, 0); base.OnPaint(e); } } void printdoc1_PrintPage(object sender, PrintPageEventArgs e) { e.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality; e.Graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBilinear; e.Graphics.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighQuality; Rectangle pagearea = e.PageBounds; e.Graphics.DrawImage(MemoryImage, (pagearea.Width / 2) - (this.panel1.Width / 2), this.panel1.Location.Y); } public void Print(Panel pnl) { panel1 = pnl; GetPrintArea(pnl); printPreviewDialog1.Document = printdoc1; printPreviewDialog1.ShowDialog(); } private void button2_Click(object sender, EventArgs e) { Print(this.panel1); } 

这一次又出现了。 没有神奇的解决方案,尽管最终问题可能会消失。 “视网膜”显示器的出现是至关重要的。

核心问题是显示器的分辨率比打印机差得多。 典型的打印机的分辨率为每英寸600点。 这使它能够在一张纸上打印6600 x 5100个别像素。 比显示器可以显示的更多,全高清显示器最高可达1920 x 1080像素。 大约5倍差,给予或采取。

当您将显示在显示器上的内容打印在纸张上并尝试保持相同尺寸时,这种方法效果不佳。 不可避免地,由于显示器上缺少像素,显示器中的每个像素都会在纸上打印为5×5的斑点。 如果你试图保持像素映射一对一,你在纸上获得一个锋利的副本。 但它已经变成了一张邮票。

不可避免的是,由于这些像素斑点,打印输出看起来非常颗粒感。 看起来特别差的是文字 。 操作系统使用大量技巧使文本在分辨率较差的显示器上看起来很好。 抗锯齿是标准的,像ClearType这样的技巧旨在利用可以帮助提高感知分辨率的监视器物理。 当打印文本时,这不再有效,那些抗锯齿像素变成斑点并变得非常明显,完全破坏了效果。 对彩色打印机上的ClearType文本特别不好,现在可以清楚地看到红色和蓝色的条纹。

唯一合适的方法是使用实​​际分辨率而不是显示器分辨率渲染到打印机。 就像在.NET中使用PrintDocument类一样。 使用报告生成器可以帮助避免必须为其编写代码。

您应该在PrintDocument打印时获得的Graphics对象上绘制自己。 这为您提供了所需的一切控制。 汉斯帕斯特所说的所有内容都适用于此…请记住,这是最简单的实现,只是演示可以实现的目标,我并不是说这是最简单/最好/最有效的方式……我的代码不包含多个页面,包含在contaimers中的控件或不属于Label和PictureBox类型的控件。

我使用了System.Drawing.Graphics中的Draw …方法

sligthly改编自上面的代码,以使这工作:

 public void GetPrintArea(Panel pnl, Graphics gr) { // scale to fit on width of page... if (pnl.Width > 0) { gr.PageScale = gr.VisibleClipBounds.Width/pnl.Width; } // this should recurse... // just for demo so kept it simple foreach (var ctl in pnl.Controls) { // for every control type // come up with a way to Draw its // contents if (ctl is Label) { var lbl = (Label)ctl; gr.DrawString( lbl.Text, lbl.Font, new SolidBrush(lbl.ForeColor), lbl.Location.X, // simple based on the position in the panel lbl.Location.Y); } if (ctl is PictureBox) { var pic = (PictureBox)ctl; gr.DrawImageUnscaledAndClipped( pic.Image, new Rectangle( pic.Location.X, pic.Location.Y, pic.Width, pic.Height)); } } } void printdoc1_PrintPage(object sender, PrintPageEventArgs e) { e.Graphics.SmoothingMode = Drawing2D.SmoothingMode.HighQuality; e.Graphics.InterpolationMode =Drawing2D.InterpolationMode.HighQualityBilinear; e.Graphics.PixelOffsetMode = Drawing2D.PixelOffsetMode.HighQuality; GetPrintArea(panel1, e.Graphics); } 

实际上,您可以通过在“矢量”级别而不是位图级别上应用缩放来打印更清晰的控件。

此快照显示以下技术的结果(请不要介意我的Win2000-ish UI :-)):

综合

我们所做的是以与他的回答中显示的rene相似的方式迭代控件的ControlCollection

但是 – 此外我们在将位图绘制到预设尺寸位图之前将位置,大小和字体应用于控件本身,在这种情况下,该位图大5倍(4倍代表大约300 DPI,这是有效的打印分辨率。大多数打印机)。

这样做的原因是在打印时保持控件上的细线清晰,或者我们可以只缩放位图本身,这不会给我们带来任何分辨率的好处。 通过缩放字体,我们可以减少抗锯齿效果并提供更好的打印质量。

为此,您可以先在按钮的单击事件设置中进行以下操作:

 //this will produce 5x "sharper" print MemoryImage = new Bitmap((Panel1.Width * 5), (Panel1.Height * 5)); Using Graphics g = Graphics.FromImage(MemoryImage) { ScaleControls(Panel1, g, 5); }; PrintPreviewDialog1.Document = printdoc1; PrintPreviewDialog1.ShowDialog(); 

现在在ScaleControls函数中,它是递归的,我们缩放位置,大小和字体,以便在我们将它们绘制到位图之前使每个控件本身具有更高的分辨率:

 private void ScaleControls(Control c, ref Graphics g, double s) { //To detach controls for panels, groupboxes etc. List hold = null; foreach (Control ctrl in c.Controls) { if (ctrl is GroupBox || ctrl is Panel) { //backup reference to controls hold = new List(); foreach (Control gctrl in ctrl.Controls) { hold.Add(gctrl); } ctrl.Controls.Clear(); } //backup old location, size and font (see explanation) Point oldLoc = ctrl.Location; Size oldSize = ctrl.Size; Font oldFont = ctrl.Font; //calc scaled location, size and font ctrl.Location = new Point(ctrl.Location.X * s, ctrl.Location.Y * s); ctrl.Size = new Size(ctrl.Size.Width * s, ctrl.Height * s); ctrl.Font = new Font(ctrl.Font.FontFamily, ctrl.Font.Size * 5, ctrl.Font.Style, ctrl.Font.Unit); //draw this scaled control to hi-res bitmap using (Bitmap bmp = new Bitmap(ctrl.Size.Width, ctrl.Size.Height)) { ctrl.DrawToBitmap(bmp, ctrl.ClientRectangle); g.DrawImage(bmp, ctrl.Location); } //restore control's geo ctrl.Location = oldLoc; ctrl.Size = oldSize; ctrl.Font = oldFont; //recursive for panel, groupbox and other controls if (ctrl is GroupBox || ctrl is Panel) { foreach (Control gctrl in hold) { ctrl.Controls.Add(gctrl); } ScaleControls(ctrl, g, s); } } } 

最后在事件处理程序中进行打印:

 double scale = MemoryImage.Width / e.PageBounds.Width; e.Graphics.DrawImage(MemoryImage, 0, 0, Convert.ToInt32(MemoryImage.Width / scale), Convert.ToInt32(MemoryImage.Height / scale)); 

现在,在此示例中,我们就地缩放控件。 这当然不是理想的,因为在我们进行打印预览时,它们似乎过着自己的生活。

理想情况下,我们会在迭代时克隆每个控件,并在绘制到位图后将其丢弃。 这也消除了备份几何的需要。 但是对于原理的例子,我把它保持原样。 我会留给你克隆等。

分离控件的原因是,如果我们不这样做(因为代码现在 – 通过提供另一种迭代方法,即预先缩放克隆控件,肯定可以改变),f.ex中的非缩放。 首先打印GroupBox控件,然后在迭代时显示缩放的控件。 这是因为我们在缩放其控件之前DrawToBitmap GroupBox 。 这是我留给你处理的东西。

我们正在处理的位图不一定符合用户在设置打印对话框时最终得到的打印分辨率,但是我们获得了更高的分辨率来处理,从而产生比不良屏幕位图更好的结果我们最初的决议。

您当然需要为PanelGroupBox之外的其他控件添加对特殊情况的支持,而PanelGroupBox可以容纳其他控件,图像控件等。

我花了几天时间寻找一种方法来打印高质量的面板及其内容。 这不起作用,我尝试了其他人的代码,他们都是错的或只是质量差,直到我发现这个:

http://rkinfopedia.blogspot.com/2008/07/printing-contents-of-panel-control.html

只需将eventhandler放在打印按钮单击事件处理程序中,并在其中包含print方法,如下所示:

 private void button3_Click(object sender, EventArgs e) { printdoc1.PrintPage += new PrintPageEventHandler(printdoc1_PrintPage); Print(panel1); } 

并在覆盖OnPaint方法中放置一个if语句,如下所示:

 protected override void OnPaint(PaintEventArgs e) { if (MemoryImage != null) { e.Graphics.DrawImage(MemoryImage, 0, 0); base.OnPaint(e); } } 

rest很好,你最终会得到几乎完美的打印质量

只是想分享这个gem,欢迎互联网陌生人!

谢谢拉克什先生!