0

I actually wanted to Convert RTF into Image so after googling a lot I've got a code that does it by Paint() Event of Picturebox1 and it works perfectly :

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

My App

in the picture above the left is my richTextBox and the right is a Picturebox.

the ISSUE is I don't know how to save Paint() drew graphic into a file because the 3 last lines of my code just save an empty image.

UPDATE #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);

by changing the graphics from e.graphics to g the issue is resolved but with one other issue that the quality of bitmap is too low. I've Added this bunch of code but I've got same result, the quality is too low! Any suggestions?

UPDATE #2

here is the Graphics_DrawRtfText class that does the conversion :

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
    }
}
Charlie Brumbaugh
  • 219
  • 1
  • 5
  • 15
Khaled Rakhisi
  • 317
  • 1
  • 4
  • 18
  • 1
    Create the bitmap first (not in the paint event), create a graphic object from that bitmap, draw into that bitmap, set the PictureBox image property to that bitmap. Save the bitmap with your code, but NOT in the paint event. – LarsTech Aug 18 '17 at 14:01
  • Save to workspace. – Orkhan Alikhanov Aug 18 '17 at 14:07
  • What is `DrawRtfText` ??? – TaW Aug 18 '17 at 14:45
  • Thanks for the ocde. Really interesting. I can't study it atm but the answer below will be a reasonable workaround – TaW Aug 18 '17 at 19:04
  • Did you get the code [from here](http://andrewvos.com/2008/05/23/draw-rtf-text-on-a-graphics-object-in-c)? – TaW Aug 19 '17 at 11:58
  • ok thank u man.i replaced my picturebox by a panel and it worked.great job you done here. can you please tell how to remove produced image background and use transparent background instead? – Khaled Rakhisi Aug 19 '17 at 12:19
  • RichTextBox doesn't support a Transparent Backcolor. Nor is is a really good idea with all the anti-aliasing. If you really need it you will have to a) replace all semi-transparent pixels in the result by full transparency and also best remove or tweak the quality settings, ie turn off smoothingmode and set texthint to a gridfit value. See [here](https://stackoverflow.com/questions/43372549/remove-outline-from-image-on-transparent-form/43373628?s=5|0.0000#43373628) for the former! – TaW Aug 19 '17 at 17:08
  • sorry if i am new to c#. i checkd out [your link](https://stackoverflow.com/questions/43372549/remove-outline-from-image-on-transparent-form/43373628?s=5|0.0000#43373628) but when i run the program it transparent my form background not my bmp that i have produced from rtf to bmp. – Khaled Rakhisi Aug 19 '17 at 21:22
  • Huh?? You need to call the `UnSemi` function, feeding in the bitmap you have created before. Note that you also will want to make all opaque backcolor pixels transparent. There is a [system function](https://msdn.microsoft.com/en-us/library/8517ckds(v=vs.110).aspx) for this.. I have just tested it and it works fine. I have added the two necessary line in the answer, also two example screenshots. - If you are happy with an answer, please consider [accepting](http://stackoverflow.com/help/accepted-answer) it..! – TaW Aug 20 '17 at 15:06
  • @TaW i really would like to use your [SOLUTION](https://stackoverflow.com/questions/43372549/remove-outline-from-image-on-transparent-form/43373628#43373628) but it seems it used to making a FORM bg transparent. do you have any code to make a bitmap transparent? – Khaled Rakhisi Aug 24 '17 at 08:59

2 Answers2

1

Your code produces an empty image file because you are not drawing anything onto 'newBitmap'.

If you want to draw anything onto 'newBitmap' you need to create a Graphics object from it. As I do not know where 'DrawRtfText' comes from and how it works my guess would be:

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");
}
richej
  • 834
  • 4
  • 21
  • it worked but saving the image with very low quality. why? – Khaled Rakhisi Aug 18 '17 at 14:22
  • Pictuurebox is too small. – TaW Aug 18 '17 at 14:44
  • Try to set SmoothingMode and TextRenderingHint: https://msdn.microsoft.com/de-de/library/system.drawing.graphics.smoothingmode(v=vs.110).aspx https://msdn.microsoft.com/de-de/library/system.drawing.graphics.textrenderinghint%28v=vs.110%29.aspx?f=255&MSPPError=-2147217396 – richej Aug 18 '17 at 14:52
  • @TaW see the attached image.the quality of right image is good even it is small. – Khaled Rakhisi Aug 18 '17 at 14:53
  • Well without seeing the result or a better description than 'low quality', how can we help but guess? Again: What is `DrawRtfText` ?? – TaW Aug 18 '17 at 15:20
  • @TaW its method of a class that convert rtf to image – Khaled Rakhisi Aug 18 '17 at 17:56
  • _its method of a class that convert rtf to image_ Well that much was obvious. Can you point us to the source? It may well allow you to influence the resolution of the result.. Ot you simply have to enlarge the Graphics resolution.. – TaW Aug 18 '17 at 18:05
  • @TaW yeah sure. please check out update#2 on my post – Khaled Rakhisi Aug 18 '17 at 18:14
1

Disclaimer: I don't have the time to dig into the posted extension method but it is interesting and works well, at least when drawing onto a control surface.

But I could reproduce how bad the results are when drawing into a bitmap..

But: When done right the saved results are excellent!

So here here are a few things to keep in mind:

  • Saving in the Paint event is a bad idea, as this event will be triggered by the system whenever it needs to redraw the control; test by doing a minimize/maximize cycle.

  • In addition the DrawRtfText semms to create a double-vision effect when drawing into a bitmap.

  • So make sure you use DrawToBitmap to grab the results. For this you need to place the call to DrawRtfText in the Paint event of a control!

  • Also make sure to have large enough resolutions both in the control (pixel size) and the Bitmap (dpi) to get nice, crispy and (if needed) printable results.

  • Do not save to jpg as this is bound to result in blurry text! Png is the format of choice!

Here is a Paint event:

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

Note that it pays to improve on the default quality settings; if you don't the text in the resulting file will break apart when zooming in..

Here is a Save button click:

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

I found the result to be really good.

Note that DrawToBitmap will internally trigger the Paint event to grab the drawn graphics.

Of course you don't need both parts - use only the one you want (.e. skip the 1st part, between first and now ) and do use your own numbers. It helps to know what the output shall be and calculate the necessary sizes and resolutions backward from there.

I added the enlarged version because usually the monitor resolution, which is what the controls all have, is rather limited, around 75-100dpi, while print quality starts only at 150dpi..

enter image description hereenter image description here

Here is a link to the UnSemi function

Community
  • 1
  • 1
TaW
  • 53,122
  • 8
  • 69
  • 111
  • special thanks to you. i tried every single advice in your answer but the issue still remain.png cause same result and i tested the button_click code but it produce empty image file – Khaled Rakhisi Aug 19 '17 at 05:12
  • Hm, that can't be since the code above was copied from my tests and does work fine. So you must have missed soemthing.. You did [hook up](http://stackoverflow.com/questions/33275763/copy-datagridview-values-to-textbox/33276161?s=14|0.0000#33276161) the `Paint` event, of course? – TaW Aug 19 '17 at 07:42
  • no the event runs without problems but i using picturebox insted a panel. is there any difference? – Khaled Rakhisi Aug 19 '17 at 11:14
  • No difference, really. (Sorry about the pintruebox/pamnnel mixup, my fault) You did separate the code between the Paint event and a Save button, right? – TaW Aug 19 '17 at 11:52
  • i used a panel and it worked like a clock!. thank you – Khaled Rakhisi Aug 24 '17 at 09:12
  • i use **bitmap.makeTransparent(Color.White)** method. it works fine but leaves white border around the text even thou i used **TextRenderingHint.SingleBitPerPixel** and **SmoothingMode.None** of your code. how can i solve this issue? – Khaled Rakhisi Sep 09 '17 at 05:29
  • Do you also use the unsemi function? – TaW Sep 09 '17 at 06:31