自动调整大小字体以适合矩形

时间:2013-03-22 13:35:40

标签: c# .net fonts

如何在.NET 4.5 / C#中创建一个具有自适应大小的字体以适合指定的矩形?

我有一个基于字符串长度的调整大小,字符串越长,字体大小越小,但它不能很好地工作,如果字符串太长,文本变得非常小。这种方法的问题是,如果我改变矩形大小,所有字体大小都不再好了。

4 个答案:

答案 0 :(得分:11)

已经有一段时间,但我遇到了这个问题。 MSDN提供了一个带有GetAdjustedFont方法的示例来帮助完成此过程:

(从https://msdn.microsoft.com/en-us/library/bb986765.aspx无耻地被盗)

public Font GetAdjustedFont(Graphics g, string graphicString, Font originalFont, int containerWidth, int maxFontSize, int minFontSize, bool smallestOnFail)
{
   Font testFont = null;
   // We utilize MeasureString which we get via a control instance           
   for (int adjustedSize = maxFontSize; adjustedSize >= minFontSize; adjustedSize--)
   {
      testFont = new Font(originalFont.Name, adjustedSize, originalFont.Style);

      // Test the string with the new size
      SizeF adjustedSizeNew = g.MeasureString(graphicString, testFont);

      if (containerWidth > Convert.ToInt32(adjustedSizeNew.Width))
      {
         // Good font, return it
         return testFont;
      }
   }

   // If you get here there was no fontsize that worked
   // return minimumSize or original?
   if (smallestOnFail)
   {
      return testFont;
   }
   else
   {
      return originalFont;
   }
}

答案 1 :(得分:4)

使用Graphics.MeasureString,您可以衡量字符串的大小,以便计算出您需要的内容。

来自MSDN的样本

private void MeasureStringMin(PaintEventArgs e)
{

    // Set up string. 
    string measureString = "Measure String";
    Font stringFont = new Font("Arial", 16);

    // Measure string.
    SizeF stringSize = new SizeF();
    stringSize = e.Graphics.MeasureString(measureString, stringFont);

    // Draw rectangle representing size of string.
    e.Graphics.DrawRectangle(new Pen(Color.Red, 1), 0.0F, 0.0F, stringSize.Width, stringSize.Height);

    // Draw string to screen.
    e.Graphics.DrawString(measureString, stringFont, Brushes.Black, new PointF(0, 0));
}

答案 2 :(得分:1)

您可以使用任何字体大小(例如16)来测量字符串宽度,然后可以按以下方式计算所需的字体大小:

fontSize = (previouslyMeasuredFontSize * targetWidthOfString) / previouslyMeasuredStringWidth;

答案 3 :(得分:0)

我也需要类似的东西。我创建了一些代码,以满足在具有多个自动字体大小选项的已定义矩形内绘制文本的需要(请参阅DrawMethod枚举)。它还支持使用和不使用自动字体大小的自动扭曲。我的解决方案受到上述答案的启发。 请务必添加WindowsBase和PresentationCore程序集。

<强> DrawTextToBitmap.cs

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Text;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ReportPrepare
{
    class TextDrawing
    {
        public enum DrawMethod
        {
            AutosizeAccordingToText, // create the smallest bitmap needed to draw the text without word warp
            AutoFitInConstantRectangleWithoutWarp, // draw text with the biggest font possible while not exceeding rectangle dimensions, without word warp
            AutoWarpInConstantRectangle, // draw text in rectangle while performing word warp. font size is a constant input. drawing may exceed bitmap rectangle.
            AutoFitInConstantRectangleWithWarp // draw text with the biggest font possible while not exceeding rectangle dimensions, with word warp
        }

        private static void SetGraphicsHighQualityForTextRendering(Graphics g)
        {
            // The smoothing mode specifies whether lines, curves, and the edges of filled areas use smoothing (also called antialiasing). One exception is that path gradient brushes do not obey the smoothing mode. Areas filled using a PathGradientBrush are rendered the same way (aliased) regardless of the SmoothingMode property.
            g.SmoothingMode = SmoothingMode.AntiAlias;

            // The interpolation mode determines how intermediate values between two endpoints are calculated.
            g.InterpolationMode = InterpolationMode.HighQualityBicubic;

            // Use this property to specify either higher quality, slower rendering, or lower quality, faster rendering of the contents of this Graphics object.
            g.PixelOffsetMode = PixelOffsetMode.HighQuality;

            // This one is important
            g.TextRenderingHint = TextRenderingHint.AntiAliasGridFit;
        }

        public static Size MeasureDrawTextBitmapSize(string text, Font font)
        {
            Bitmap bmp = new Bitmap(1, 1);
            using (Graphics g = Graphics.FromImage(bmp))
            {
                SizeF size = g.MeasureString(text, font);
                return new Size((int)(Math.Ceiling(size.Width)), (int)(Math.Ceiling(size.Height)));
            }

        }

        public static int GetMaximumFontSizeFitInRectangle(string text, Font font, RectangleF rectanglef, bool isWarp, int MinumumFontSize=6, int MaximumFontSize=1000)
        {
            Font newFont;
            Rectangle rect = Rectangle.Ceiling(rectanglef);

            for (int newFontSize = MinumumFontSize; ; newFontSize++)
            {
                newFont = new Font(font.FontFamily, newFontSize, font.Style);

                List<string> ls = WarpText(text, newFont, rect.Width);

                StringBuilder sb = new StringBuilder();
                if (isWarp)
                {
                    for (int i = 0; i < ls.Count; ++i)
                    {
                        sb.Append(ls[i] + Environment.NewLine);
                    }
                }
                else
                {
                    sb.Append(text);
                }

                Size size = MeasureDrawTextBitmapSize(sb.ToString(), newFont);
                if (size.Width > rectanglef.Width || size.Height > rectanglef.Height)
                {
                    return (newFontSize - 1);
                }
                if (newFontSize >= MaximumFontSize)
                {
                    return (newFontSize - 1);
                }

            }

        }

        public static List<string> WarpText(string text, Font font, int lineWidthInPixels)
        {
            string[] originalLines = text.Split(new string[] { " " }, StringSplitOptions.None);

            List<string> wrappedLines = new List<string>();

            StringBuilder actualLine = new StringBuilder();
            double actualWidthInPixels = 0;

            foreach (string str in originalLines)
            {
                Size size = MeasureDrawTextBitmapSize(str, font);

                actualLine.Append(str + " ");
                actualWidthInPixels += size.Width;

                if (actualWidthInPixels > lineWidthInPixels)
                {
                    actualLine =  actualLine.Remove(actualLine.ToString().Length - str.Length - 1, str.Length);
                    wrappedLines.Add(actualLine.ToString());
                    actualLine.Clear();
                    actualLine.Append(str + " ");
                    actualWidthInPixels = size.Width;
                }
            }

            if (actualLine.Length > 0)
            {
                wrappedLines.Add(actualLine.ToString());
            }

            return wrappedLines;
        }

        public static Bitmap DrawTextToBitmap(string text, Font font, Color color, DrawMethod mode, RectangleF rectanglef)
        {
            StringFormat drawFormat = new StringFormat();
            Bitmap bmp;
            switch (mode)
            {
                case DrawMethod.AutosizeAccordingToText:
                    {
                        Size size = MeasureDrawTextBitmapSize(text, font);

                        if (size.Width == 0 || size.Height == 0)
                        {
                            bmp = new Bitmap(1, 1);
                        }
                        else
                        {
                            bmp = new Bitmap(size.Width, size.Height);
                        }

                        using (Graphics g = Graphics.FromImage(bmp))
                        {
                            SetGraphicsHighQualityForTextRendering(g);

                            g.DrawString(text, font, new SolidBrush(color), 0, 0);

                            return bmp;
                        }

                    }
                case DrawMethod.AutoWarpInConstantRectangle:
                    {
                        Rectangle rect =  Rectangle.Ceiling(rectanglef);
                        bmp = new Bitmap(rect.Width,rect.Height);

                        if (rect.Width == 0 || rect.Height == 0)
                        {
                            bmp = new Bitmap(1, 1);
                        }
                        else
                        {
                            bmp = new Bitmap(rect.Width, rect.Height);
                        }

                        using (Graphics g = Graphics.FromImage(bmp))
                        {
                            SetGraphicsHighQualityForTextRendering(g);

                            g.DrawString(text, font, new SolidBrush(color), rectanglef, drawFormat);

                            return bmp;
                        }

                    }
                case DrawMethod.AutoFitInConstantRectangleWithoutWarp:
                    {
                        Rectangle rect = Rectangle.Ceiling(rectanglef);

                        bmp = new Bitmap(rect.Width, rect.Height);

                        if (rect.Width == 0 || rect.Height == 0)
                        {
                            bmp = new Bitmap(1, 1);
                        }
                        else
                        {
                            bmp = new Bitmap(rect.Width, rect.Height);
                        }

                        using (Graphics g = Graphics.FromImage(bmp))
                        {
                            int fontSize = GetMaximumFontSizeFitInRectangle(text, font, rectanglef, false);

                            SetGraphicsHighQualityForTextRendering(g);

                            g.DrawString(text, new Font(font.FontFamily, fontSize,font.Style, GraphicsUnit.Point), new SolidBrush(color), rectanglef, drawFormat);


                            return bmp;
                        }

                    }
                case DrawMethod.AutoFitInConstantRectangleWithWarp:
                    {
                        Rectangle rect = Rectangle.Ceiling(rectanglef);

                        if (rect.Width == 0 || rect.Height == 0)
                        {
                            bmp = new Bitmap(1, 1);
                        }
                        else
                        {
                            bmp = new Bitmap(rect.Width, rect.Height);
                        }

                        using (Graphics g = Graphics.FromImage(bmp))
                        {
                            int fontSize = GetMaximumFontSizeFitInRectangle(text, font, rectanglef, true);

                            SetGraphicsHighQualityForTextRendering(g);

                            g.DrawString(text, new Font(font.FontFamily, fontSize, font.Style, GraphicsUnit.Point), new SolidBrush(color), rectanglef, drawFormat);


                            return bmp;
                        }

                    }
            }
            return null;

        }



    }
}

用法示例: Form1.cs的

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;


// add WindowsBase and PresentationCore assemblies

namespace ReportPrepare
{
    public partial class Form1 : Form
    {
        PictureBox picbox = new PictureBox();
        int i = 0;
        Timer t = new Timer();

        public Form1()
        {
            InitializeComponent();


            this.Controls.Add(picbox);
            picbox.Dock = DockStyle.Fill;

            t.Interval = 5000;
            t.Tick += t_Tick;
            t.Enabled = true;

            this.Shown += Form1_Shown;
            this.SizeChanged += Form1_SizeChanged;

            this.Size = new Size(812, 400);

            this.StartPosition = FormStartPosition.CenterScreen;

        }

        void Form1_Shown(object sender, EventArgs e)
        {
            DrawText();
        }

        void t_Tick(object sender, EventArgs e)
        {
            i++;
            if (i > 3)
            {
                i = 0;
            }
            DrawText();
        }


        private void DrawText()
        {

            // text and font
            string text = "one two three four five six seven eight nine ten eleven twelve";

            Font font = new System.Drawing.Font("Arial", 30, FontStyle.Regular, GraphicsUnit.Point);

            switch (i)
            {
                case 0:
                    picbox.Image = TextDrawing.DrawTextToBitmap(text, font, Color.Red, TextDrawing.DrawMethod.AutosizeAccordingToText, new RectangleF(0, 0, picbox.Width, picbox.Height));
                    break;
                case 1:
                    picbox.Image = TextDrawing.DrawTextToBitmap(text, font, Color.Red, TextDrawing.DrawMethod.AutoFitInConstantRectangleWithoutWarp, new RectangleF(0, 0, picbox.Width, picbox.Height));
                    break;
                case 2:
                    picbox.Image = TextDrawing.DrawTextToBitmap(text, font, Color.Red, TextDrawing.DrawMethod.AutoWarpInConstantRectangle, new RectangleF(0, 0, picbox.Width, picbox.Height));
                    break;
                case 3:
                    picbox.Image = TextDrawing.DrawTextToBitmap(text, font, Color.Red, TextDrawing.DrawMethod.AutoFitInConstantRectangleWithWarp, new RectangleF(0, 0, picbox.Width, picbox.Height));
                    break;
            }
            this.Text = ((TextDrawing.DrawMethod)(i)).ToString() + "                      Please resize window size by mouse to see drawing methods differences";

        }

        private void Form1_SizeChanged(object sender, EventArgs e)
        {
            t.Enabled = false;
            t.Enabled = true;
            DrawText();
        }





    }
}

该示例每5秒自动在绘图模式之间切换一次。图片框停靠在主窗体内。调整表单大小可向用户显示绘图模式之间的差异。