印刷质量winform

时间:2012-09-20 10:32:49

标签: c# winforms printing

尝试从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);
    }

4 个答案:

答案 0 :(得分:18)

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

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

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

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

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

答案 1 :(得分:4)

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

我使用了System.Drawing.Graphics

中的Draw ...方法

从上面的代码中调整以适应这个:

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);
}

答案 2 :(得分:3)

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

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

Composite

我们所做的是以与 rene 在答案中显示的非常相似的方式遍历控件的ControlCollection

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

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

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

//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<Control> hold = null;

    foreach (Control ctrl in c.Controls) {
        if (ctrl is GroupBox || ctrl is Panel) {
            //backup reference to controls
            hold = new List<Control>();
            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中的非缩放。恩。首先打印GroupBox控件,然后在迭代时显示缩放的控件。这是因为我们在缩放其控件之前DrawToBitmap GroupBox。这是我留给你处理的事情。

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

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

答案 3 :(得分:0)

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

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);
    }
}

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

只想分享这个宝石,欢迎互联网陌生人加入!

谢谢拉克什先生!