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

问题描述:

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

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

+1

你尝试过什么吗?请阅读[常见问题]和[问] – 2013-03-22 13:38:13

+0

现在我有一个基于字符串长度的调整大小,字符串越长,字体大小越小,但它不能很好地工作。 – 2013-03-22 21:02:11

With 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)); 
} 
+1

..因为需要进行一些迭代(基于简单的规则,比如减少0.1或更复杂,例如,除以以前大小的一半,如果它适合 - 尝试增加等等),也许有些逻辑可以在“MeasureStringMin”之前分割行中的文本(如果文本可以用多行显示)。 – Sinatr 2013-03-22 14:32:34

+0

是的,它可以有多行 – 2013-03-22 21:00:03

样品这已经有一段时间,但我碰到这个问题。 MSDN提供样品与GetAdjustedFont的方法来帮助这个过程:

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

public Font GetAdjustedFont(Graphics GraphicRef, 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 = GraphicRef.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; 
    } 
} 

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