10

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

我根据字符串长度调整大小,字符串越长,字体越小,但效果不佳,如果字符串太长,文本会变得非常小。这种方法的问题是,如果我改变矩形大小,所有的字体大小都不好了。

4

4 回答 4

17

已经有一段时间了,但我遇到了这个问题。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;
   }
}
于 2015-06-01T07:25:21.250 回答
5

使用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));
}
于 2013-03-22T13:47:23.120 回答
3

您可以使用任何字体大小(例如 16)测量字符串宽度,然后您可以像这样计算所需的字体大小:

fontSize = (previouslyMeasuredFontSize * targetWidthOfString) / previouslyMeasuredStringWidth;
于 2019-01-29T10:58:40.087 回答
2

我也需要类似的东西。我创建了一些代码来满足在具有多个自动字体大小选项的已定义矩形内绘制文本的需求(请参阅 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 秒自动在绘图模式之间切换一次。图片框停靠在主窗体内。调整表单大小向用户展示了绘图模式之间的差异。

于 2016-10-29T13:19:37.370 回答