如何使用c#将"Paint()"的绘制图形保存到图像中?

How to save drew Graphics of "Paint()" into image using c#?

我实际上想将 RTF 转换为图像 所以在谷歌搜索了很多之后我得到了一个代码,它通过 Picturebox1 的 Paint() 事件完成,并且它完美地工作:

private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
    e.Graphics.Clear(richTextBox1.BackColor);
    e.Graphics.DrawRtfText(this.richTextBox1.Rtf,  this.pictureBox1.ClientRectangle);            

    base.OnPaint(e);

    // below code just create an empty image file
    Bitmap newBitmap = new Bitmap(pictureBox1.Width, pictureBox1.Height);
    e.Graphics.DrawImage(newBitmap, new Rectangle(0, 0, pictureBox1.Width, pictureBox1.Height), new Rectangle(0, 0, pictureBox1.Width, pictureBox1.Height), GraphicsUnit.Pixel);
    newBitmap.Save(@"c:\adv.jpg");
}

上图中左边是我的richTextBox,右边是Picturebox。

问题 是我不知道如何将 Paint() 绘制的图形保存到文件中,因为我的代码的最后 3 行只保存了一个空图像。

更新#1:

g.SmoothingMode = SmoothingMode.AntiAlias;
g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.ClearTypeGridFit;
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
g.PixelOffsetMode = PixelOffsetMode.HighQuality;

g.Clear(richTextBox1.BackColor);
g.DrawRtfText(this.richTextBox1.Rtf, this.pictureBox1.ClientRectangle);

通过将图形从 e.graphics 更改为 g,问题已解决,但还有一个问题是位图质量太低。我已经添加了这一堆代码,但我得到了相同的结果,质量太低了! 有什么建议吗?

更新#2

这里是进行转换的 Graphics_DrawRtfText class :

public static class Graphics_DrawRtfText
{
    private static RichTextBoxDrawer rtfDrawer;
    public static void DrawRtfText(this Graphics graphics, string rtf, Rectangle layoutArea)
    {
        if (Graphics_DrawRtfText.rtfDrawer == null)
        {
            Graphics_DrawRtfText.rtfDrawer = new RichTextBoxDrawer();
        }
        Graphics_DrawRtfText.rtfDrawer.Rtf = rtf;
        Graphics_DrawRtfText.rtfDrawer.Draw(graphics, layoutArea);
    }

    private class RichTextBoxDrawer : RichTextBox
    {
        //Code converted from code found here: http://support.microsoft.com/kb/812425/en-us

        //Convert the unit used by the .NET framework (1/100 inch) 
        //and the unit used by Win32 API calls (twips 1/1440 inch)
        private const double anInch = 14.4;

        protected override CreateParams CreateParams
        {
            get
            {
                CreateParams createParams = base.CreateParams;
                if (SafeNativeMethods.LoadLibrary("msftedit.dll") != IntPtr.Zero)
                {
                    createParams.ExStyle |= SafeNativeMethods.WS_EX_TRANSPARENT; // transparent
                    createParams.ClassName = "RICHEDIT50W";
                }
                return createParams;
            }
        }
        public void Draw(Graphics graphics, Rectangle layoutArea)
        {
            //Calculate the area to render.
            SafeNativeMethods.RECT rectLayoutArea;
            rectLayoutArea.Top = (int)(layoutArea.Top * anInch);
            rectLayoutArea.Bottom = (int)(layoutArea.Bottom * anInch);
            rectLayoutArea.Left = (int)(layoutArea.Left * anInch);
            rectLayoutArea.Right = (int)(layoutArea.Right * anInch);

            IntPtr hdc = graphics.GetHdc();

            SafeNativeMethods.FORMATRANGE fmtRange;
            fmtRange.chrg.cpMax = -1;                    //Indicate character from to character to 
            fmtRange.chrg.cpMin = 0;
            fmtRange.hdc = hdc;                                //Use the same DC for measuring and rendering
            fmtRange.hdcTarget = hdc;                    //Point at printer hDC
            fmtRange.rc = rectLayoutArea;            //Indicate the area on page to print
            fmtRange.rcPage = rectLayoutArea;    //Indicate size of page

            IntPtr wParam = IntPtr.Zero;
            wParam = new IntPtr(1);

            //Get the pointer to the FORMATRANGE structure in memory
            IntPtr lParam = IntPtr.Zero;
            lParam = Marshal.AllocCoTaskMem(Marshal.SizeOf(fmtRange));
            Marshal.StructureToPtr(fmtRange, lParam, false);

            SafeNativeMethods.SendMessage(this.Handle, SafeNativeMethods.EM_FORMATRANGE, wParam, lParam);

            //Free the block of memory allocated
            Marshal.FreeCoTaskMem(lParam);

            //Release the device context handle obtained by a previous call
            graphics.ReleaseHdc(hdc);
        }

        #region SafeNativeMethods
        private static class SafeNativeMethods
        {
            [DllImport("USER32.dll")]
            public static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wp, IntPtr lp);

            [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
            public static extern IntPtr LoadLibrary(string lpFileName);

            [StructLayout(LayoutKind.Sequential)]
            public struct RECT
            {
                public int Left;
                public int Top;
                public int Right;
                public int Bottom;
            }

            [StructLayout(LayoutKind.Sequential)]
            public struct CHARRANGE
            {
                public int cpMin;        //First character of range (0 for start of doc)
                public int cpMax;        //Last character of range (-1 for end of doc)
            }

            [StructLayout(LayoutKind.Sequential)]
            public struct FORMATRANGE
            {
                public IntPtr hdc;                //Actual DC to draw on
                public IntPtr hdcTarget;    //Target DC for determining text formatting
                public RECT rc;                        //Region of the DC to draw to (in twips)
                public RECT rcPage;                //Region of the whole DC (page size) (in twips)
                public CHARRANGE chrg;        //Range of text to draw (see earlier declaration)
            }

            public const int WM_USER = 0x0400;
            public const int EM_FORMATRANGE = WM_USER + 57;
            public const int WS_EX_TRANSPARENT = 0x20;

        }
        #endregion
    }
}

您的代码生成一个空图像文件,因为您没有在 'newBitmap' 上绘制任何内容。

如果您想在 'newBitmap' 上绘制任何东西,您需要从中创建一个 Graphics 对象。因为我不知道 'DrawRtfText' 来自哪里以及它是如何工作的,所以我的猜测是:

private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
        //... 
        Bitmap newBitmap = new Bitmap(pictureBox1.Width, pictureBox1.Height);
        Graphics g = Graphics.FromImage(newBitmap);
        g.DrawRtfText(this.richTextBox1.Rtf, this.pictureBox1.ClientRectangle);
        newBitmap.Save(@"d:\adv.jpg");
}

免责声明:我没有时间深入研究已发布的扩展方法,但它很有趣并且效果很好,至少在控制表面上绘制时是这样。

但我可以重现绘制到位图中时的结果有多糟糕..

但是:如果操作正确,保存的结果非常好!

所以这里有几点要记住:

  • 保存在Paint事件中是个坏主意,因为系统会在需要重绘控件时触发此事件;通过执行 minimize/maximize 循环进行测试。

  • 此外 DrawRtfText semms 在绘制到位图中时创建双视效果。

  • 所以请务必使用 DrawToBitmap 获取结果。为此,您需要在控件的 Paint 事件中调用 DrawRtfText

  • 还要确保在控件(像素大小)和位图 (dpi) 中具有足够大的分辨率,以获得漂亮、清晰和(如果需要)可打印的结果。

  • 不要保存到jpg,因为这必然会导致文本模糊! Png是选择的格式!

这是一个 Paint 事件:

private void panel1_Paint(object sender, PaintEventArgs e)
{
    e.Graphics.Clear(richTextBox1.BackColor);
    e.Graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias;
    e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
    Padding pad = new Padding(120, 230, 10, 30);  // pick your own numbers!
    Size sz = panel1.ClientSize;
    Rectangle rect = new Rectangle(pad.Left, pad.Top, 
                                   sz.Width - pad.Horizontal, sz.Height - pad.Vertical);
    e.Graphics.DrawRtfText(this.richTextBox1.Rtf, rect);            
        
}

请注意,改进默认质量设置是值得的;如果你不这样做,结果文件中的文本在放大时会分开..

点击保存按钮:

private void button1_Click(object sender, EventArgs e)
{
    Size sz = panel1.ClientSize;

    // first we (optionally) create a bitmap in the original panel size:
    Rectangle rect1 = panel1.ClientRectangle;
    Bitmap bmp = new Bitmap(rect1.Width, rect1.Height);
    panel1.DrawToBitmap(bmp, rect);
    bmp.Save("D:\rtfImage1.png", ImageFormat.Png);

    // now we create a 4x larger one:
    Rectangle rect2 = new Rectangle(0, 0, sz.Width * 4, sz.Height * 4);
    Bitmap bmp2 = new Bitmap(rect2.Width, rect2.Height);

    // we need to temporarily enlarge the panel:
    panel1.ClientSize = rect2.Size;

    // now we can let the routine draw
    panel1.DrawToBitmap(bmp2, rect2);
    // and before saving we optionally can set the dpi resolution
    bmp2.SetResolution(300, 300);

    // optionally make background transparent:
    bmp2.MakeTransparent(richTextBox1.BackColor);
    UnSemi(bmp2);  // see the link in the comment!

    // save text always as png; jpg is only for fotos!
    bmp2.Save("D:\rtfImage2.png", ImageFormat.Png);

    // restore the panels size
    panel1.ClientSize = sz;
}

我发现结果非常好。

注意DrawToBitmap会在内部触发Paint事件抓取绘制的图形

当然你不需要这两部分 - 只使用你想要的部分(例如跳过第一部分,在 firstnow 之间)并使用你自己的号码。它有助于了解输出应该是什么并从那里向后计算必要的大小和分辨率。

我添加了放大版本,因为通常显示器分辨率(所有控件都有)相当有限,大约在 75-100dpi 左右,而打印质量仅在 150dpi..

这里是link到