Search code examples
c#winformsgdi

Text with different colors on Image


Im trying to draw text on image where some words have to be highlighted in a different color. I found a solution here in SO that uses TextRenderer.DrawText and MeasureCharacterRanges to split words in string and renders them individually with a different color, if word matches the keyword.

    private void AddText1(string txt, Graphics g, Rectangle rect)
    {
        TextFormatFlags flags = TextFormatFlags.HorizontalCenter | TextFormatFlags.VerticalCenter |
                    TextFormatFlags.NoPadding | TextFormatFlags.NoClipping;

        using (StringFormat format = new StringFormat())
        {
            format.Alignment = StringAlignment.Center;
            format.LineAlignment = StringAlignment.Center;

            MatchCollection mc = Regex.Matches(txt, @"[^\s]+");
            CharacterRange[] ranges = mc.Cast<Match>().Select(m => new CharacterRange(m.Index, m.Length)).ToArray();
            format.SetMeasurableCharacterRanges(ranges);

            using (Font font = new Font("Times New Roman", 40, FontStyle.Regular, GraphicsUnit.Point))
            {
                Region[] regions = g.MeasureCharacterRanges(txt, font, rect, format);

                for (int i = 0; i < ranges.Length; i++)
                {
                    Rectangle WordBounds = Rectangle.Round(regions[i].GetBounds(g));
                    string word = txt.Substring(ranges[i].First, ranges[i].Length);
                    if (word == "keyword")
                        TextRenderer.DrawText(g, word, font, WordBounds, Color.YellowGreen, flags);
                    else
                        TextRenderer.DrawText(g, word, font, WordBounds, Color.White, flags);
                }
            }
        }
    }

This code works just fine when I render the bitmap and the text on to the screen(directly to the Form in paint event).

But when I use graphics object from an image, in order to save it to a file, I get totally different bounding rectangles from MeasureCharacterRanges with a completely distorted text positions in the saved image.

I'm not sure if it's a bug in MeasureCharacterRanges method or I miss something. I'm open to other solutions if this doesn't have a solution or workaround.

Edit: The problem seems to be from somewhere else. I have pseudo code like this.

Image imgResize = resizeImage(strfile); 
Graphics g = Graphics.FromImage(imgResize);
AddText1(strText, g, rect);

This doesn't work. But when I do

Image imgResize = resizeImage(strfile);
Image img2 = new Bitmap(width, height);
Graphics g = Graphics.FromImage(img2);
g.DrawImage(imgResize, new Rectangle(0, 0, width, height));
AddText1(strText, g, rect);

In both the cases, resized image's size and resolution are same. But I don't know what exactly made it to work when explicitly drawing the resized Image after using an intermediate Image to create graphics object. Please tell me if you know what could have made a difference.

Thanks for the answers.


Solution

  • I think you need to use the same group of methods for both measuring text and drawing it. i.e. either

    var size = TextRenderer.MeasureText(word, font) ;
    ...
    TextRenderer.DrawText(g, word, font, ...);
    

    or

    var ranges = g.MeasureCharacterRanges(word, font, rect, format);
    ...
    g.DrawString(word, font, ...);
    
    

    You can read about the diffrence between TextRenderer.DrawText and Graphics.DrawString here. I would not expect Graphics.MeasureCharacterRanges to be compatible with TextRenderer.DrawText, at least not in all use cases.