简体   繁体   中英

Graphics.DrawString to multiple rectangles

I am trying to write a function that needs to draw a string to an image. The image has anywhere from 1-5 textboxes, which each have ax,y, width, and height. These details are defined in an XML file which I am parsing, so I have access to these for each box.

My question is whether or not I can use the graphics.DrawString (or a similar) method to do this easily. The sample function below will create a rectangle with specified x,y, width, height, and then draw a string within. If the string doesn't fit, it truncates.

    public void DrawStringRectangleFormat(Graphics g)
    {
        // Create string to draw.
        String drawString = "Sample Text is too long to fit into this tiny lil rectangle area right here";

        // Create font and brush.
        Font drawFont = new Font("Arial", 16);
        SolidBrush drawBrush = new SolidBrush(Color.Black);

        // Create rectangle for drawing.
        float x = 150.0F;
        float y = 150.0F;
        float width = 200.0F;
        float height = 50.0F;
        RectangleF drawRect = new RectangleF(x, y, width, height);

        // Set format of string.
        StringFormat drawFormat = new StringFormat();
        drawFormat.Alignment = StringAlignment.Center;

        // Draw string to screen.
        g.DrawString(drawString, drawFont, drawBrush, drawRect, drawFormat);
    }

What I want instead of this, is rather than truncating, it will stop at the last fitting word, and go to the next rectangle(textbox). This way I can use all the available textboxes.

Is there a method already made to do this? Otherwise I will need to implement my own drawString method.

OK, what you would have to do is loop through each char in the string, and concatenate to a final string.. so basically foreach (char c in mystring)... then using measurestring, you check to see if the string is over the box length, if it is, start on the next rect...

https://msdn.microsoft.com/en-us/library/6xe5hazb(v=vs.110).aspx

This solution uses the StringFormat 's settings to ensure that each call to DrawString only draws the words that fit. Then, Graphics.MeasureCharacterRanges calculates the words that don't fit into the rectangle, and the remaining text overflows into the next layout rectangle.

You might need to customize how the input string is split into words. Right now I'm just splitting it apart at whitespace boundaries.

using System.Text.RegularExpressions;
using System.Drawing;

/// <summary>
/// Draw a string using one or more layout rectangles.  Words which don't fit will overflow into the next layout rectangle.
/// </summary>
private static void DrawOverflowString(Graphics graphics, string drawString, RectangleF[] layoutRectangles, StringAlignment alignment)
{
    var drawFont = new Font("Arial", 16.0f);
    var black = new SolidBrush(Color.Black);
    var format = new StringFormat()
    {
        Alignment = alignment,
        Trimming = StringTrimming.Word,
        FormatFlags = StringFormatFlags.LineLimit
    };
    var wordRegex = new Regex("[^\\s]+");
    string remainingText = drawString;
    foreach (var layoutRect in layoutRectangles)
    {
        // Draw everything that will fit into this text box
        graphics.DrawString(remainingText, drawFont, black, layoutRect, format);

        // calculate which words did not fit
        var wordMatches = wordRegex.Matches(remainingText);
        var ranges = wordMatches.OfType<Match>().Select(x => new CharacterRange(x.Index, x.Length)).ToArray();
        format.SetMeasurableCharacterRanges(ranges);
        var wordRegions = graphics.MeasureCharacterRanges(remainingText, drawFont, layoutRect, format);

        var allfit = true;
        for (int i = 0; i < wordRegions.Length; i++)
        {
            if (wordRegions[i].GetBounds(graphics).Width == 0.0f)
            {
                allfit = false;
                remainingText = remainingText.Substring(wordMatches[i].Index);
                break;
            }
        }

        if (allfit)
            break;
    }

    drawFont.Dispose();
    black.Dispose();
}
        protected override void OnPaint(PaintEventArgs e)
    {
        // Call the OnPaint method of the base class.
        base.OnPaint(e);
        List<Rectanglestring> testrecs = new List<Rectanglestring>();
        testrecs.Add(new Rectanglestring { targetrect = new Rectangle(0, 12, 40, 12), whattodraw = "" });
        testrecs.Add(new Rectanglestring {targetrect= new Rectangle(0, 25, 35, 12),whattodraw="" });
        testrecs.Add(new Rectanglestring { targetrect = new Rectangle(0, 35, 35, 12), whattodraw = "" });
        testrecs.Add(new Rectanglestring { targetrect = new Rectangle(0, 45, 35, 12), whattodraw = "" });
        testrecs.Add(new Rectanglestring { targetrect = new Rectangle(0, 65, 35, 12), whattodraw = "" });
        testrecs.Add(new Rectanglestring { targetrect = new Rectangle(0, 85, 35, 12), whattodraw = "" });
        testrecs.Add(new Rectanglestring { targetrect = new Rectangle(0, 95, 55, 12), whattodraw = "" });
        string mystringtofit = "This is just an example";

        foreach (Rectanglestring rect in testrecs)
        {
            for (int i = 0; i < mystringtofit.Length; i++)
            {
                if (mystringtofit[i] == ' ' && rect.whattodraw.Length > 0) break;
                if (mystringtofit[i] == ' ') continue;
                string teststring = rect.whattodraw + mystringtofit[i];
                SizeF stringSize = stringSize = e.Graphics.MeasureString(teststring, new Font("Ariel", 12));
                if (stringSize.Width >= rect.targetrect.Width) break;
                rect.whattodraw += mystringtofit[i];


            }

            mystringtofit = mystringtofit.Substring(rect.whattodraw.Length);
            if (mystringtofit.StartsWith(" "))
            {
                mystringtofit = mystringtofit.Substring(1);
            }
            e.Graphics.DrawString(rect.whattodraw, Font, new SolidBrush(ForeColor), rect.targetrect);
        }
        // Call methods of the System.Drawing.Graphics object.

    }
    public class Rectanglestring
    {
       public Rectangle targetrect = new Rectangle();
       public string whattodraw = "";
    }

This is what I have. It does what I described. Thanks for the answers.

    public void DrawStringInTextboxes(string text, Graphics g)
    {

        String drawString = text;

        PrivateFontCollection fontCollection = new PrivateFontCollection();
        fontCollection.AddFontFile(System.Web.Hosting.HostingEnvironment.MapPath("~/Content/Fonts/Squidgingtons.ttf"));
        var squidingtonsFontFamily = fontCollection.Families[0];
        Font squidingtons = new Font(squidingtonsFontFamily, textParameters[0].MaxFontSize);
        Font drawFont = new Font("Arial", 60);
        SolidBrush drawBrush = new SolidBrush(Color.Black);

        StringFormat drawFormat = new StringFormat();
        drawFormat.Alignment = StringAlignment.Center;

        char[] delimiterChars = { ' ' };
        string[] words = drawString.Split(delimiterChars);
        string finalString = "";
        int textBoxIndex = 0;

        foreach (string word in words)
        {
            //set the dimensions for the first textbox and create a rectangle with those specifications.
            float x = textParameters[textBoxIndex].Left;
            float y = textParameters[textBoxIndex].Top;
            float width = textParameters[textBoxIndex].Width;
            float height = textParameters[textBoxIndex].Height;
            RectangleF Rect = new RectangleF(x, y, width, height);
            //if the current finalString + the next word fits in the current box, add the word to finalString.
            if (g.MeasureString(finalString + word + " ", squidingtons).Width < textParameters[textBoxIndex].Width) 
            {     
                finalString = finalString + " " + word;
                //if this is the last word, print the finalString and we are done.
                if (word == words[words.Length - 1])
                {
                    g.DrawString(finalString, squidingtons, drawBrush, Rect, drawFormat);
                    break;
                }

            }
            //the current finalString + next word did not fit in the box. Draw what we have to the first box.
            else {
                g.DrawString(finalString, squidingtons, drawBrush, Rect, drawFormat);
                //Hold onto the word that didnt fit. It will be the first word of the next box.
                finalString = word;
                if (textBoxIndex +1 >= textParameters.Length)
                {
                    //if we are out of textboxes, we are done.
                    break;
                }
                else
                {
                    //move on to the next textbox. The loop begins again with new specifications set for the textbox.
                    textBoxIndex++;
                }
            }

        }
        squidingtons.Dispose();
        drawBrush.Dispose();
        drawFont.Dispose();
    }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM