【问题标题】:Dynamically resizing font to fit space while using Graphics.DrawString使用 Graphics.DrawString 时动态调整字体大小以适应空间
【发布时间】:2013-11-09 13:59:57
【问题描述】:

在您可以动态调整字体大小以适应特定区域的同时,是否有人提供提示?例如,我有一个 800x110 的矩形,我想用支持我要显示的整个字符串的最大字体来填充它。

Bitmap bitmap = new Bitmap(800, 110);

using (Graphics graphics = Graphics.FromImage(bitmap))
using (Font font1 = new Font("Arial", 120, FontStyle.Regular, GraphicsUnit.Pixel))
{
    Rectangle rect1 = new Rectangle(0, 0, 800, 110);

    StringFormat stringFormat = new StringFormat();
    stringFormat.Alignment = StringAlignment.Center;
    stringFormat.LineAlignment = StringAlignment.Center;

    graphics.TextRenderingHint = TextRenderingHint.ClearTypeGridFit;
    graphics.DrawString("Billy Reallylonglastnameinstein", font1, Brushes.Red, rect1, stringFormat);
} 

bitmap.Save(Server.MapPath("~/Fonts/" + System.Guid.NewGuid() + ".png"));

显然,整个名称不会在大字体提供的空间中呈现。必须有一个简单的方法来做到这一点?

【问题讨论】:

标签: c# .net fonts system.drawing


【解决方案1】:

您应该对Font.Size 进行缩放变换,以下函数就是这样做的示例,但您可以对其进行改进以应用更好的结果。

这里是FindFont 函数,它获取一个房间和一个具有首选大小的文本,并为您提供一种字体,您可以在其中设置整个文本适合房间!

// This function checks the room size and your text and appropriate font
//  for your text to fit in room
// PreferedFont is the Font that you wish to apply
// Room is your space in which your text should be in.
// LongString is the string which it's bounds is more than room bounds.
private Font FindFont(
   System.Drawing.Graphics g,
   string longString,
   Size Room,
   Font PreferedFont
) {
   // you should perform some scale functions!!!
   SizeF RealSize = g.MeasureString(longString, PreferedFont);
   float HeightScaleRatio = Room.Height / RealSize.Height;
   float WidthScaleRatio = Room.Width / RealSize.Width;

   float ScaleRatio = (HeightScaleRatio < WidthScaleRatio)
      ? ScaleRatio = HeightScaleRatio
      : ScaleRatio = WidthScaleRatio;

   float ScaleFontSize = PreferedFont.Size * ScaleRatio;

   return new Font(PreferedFont.FontFamily, ScaleFontSize);
}

对于您的问题,您可以将其称为以下代码:

Bitmap bitmap = new Bitmap(800, 110);

using (System.Drawing.Graphics graphics = System.Drawing.Graphics.FromImage(bitmap))
using (Font font1 = new Font("Arial", 120, FontStyle.Regular, GraphicsUnit.Pixel))
{
   Rectangle rect1 = new Rectangle(0, 0, 800, 110);

   StringFormat stringFormat = new StringFormat();
   stringFormat.Alignment = StringAlignment.Center;
   stringFormat.LineAlignment = StringAlignment.Center;
   graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.ClearTypeGridFit;

   Font goodFont = FindFont(graphics, "Billy Reallylonglastnameinstein", rect1.Size, font1);

   graphics.DrawString(
      "Billy Reallylonglastnameinstein",
      goodFont,
      Brushes.Red,
      rect1,
      stringFormat
   );
}

【讨论】:

  • float ScaleRatio = Math.Min(HeightScaleRatio, WidthScaleRatio);怎么样
  • 这可能会让它接近,但似乎文本换行会使这不准确。对于单行文本来说,这将是一个很好的方法。
  • 可能有更多的选择......比如文本处理甚至 text2image 算法和/或反之亦然,这取决于程序员的想法。在我的帖子中,这是一个干净简单的解决方案!并且只是一个改进的基本想法!!!
  • 我在我的组件中使用了这个:stackoverflow.com/questions/25891547/… 感谢分享!
  • GraphicsUnit.Pixel 添加到FindFont() 函数返回行。没有GraphicsUnit.Pixel 系统dpi 将影响绘制的字符串。这就是这条线的样子。 return new Font(PreferedFont.FontFamily, ScaleFontSize,PreferedFont.Style,GraphicsUnit.Pixel);
【解决方案2】:

我已经调整了 Saeed 的强大功能以更适合我的要求。评论说明一切:

    // You hand this the text that you need to fit inside some
    // available room, and the font you'd like to use.
    // If the text fits nothing changes
    // If the text does not fit then it is reduced in size to
    // make it fit.
    // PreferedFont is the Font that you wish to apply
    // FontUnit is there because the default font unit is not
    // always the one you use, and it is info required in the
    // constructor for the new Font.
    public static void FindGoodFont(Graphics Graf, string sStringToFit,
                                    Size TextRoomAvail, 
                                    ref Font FontToUse,
                                    GraphicsUnit FontUnit)
    {
        // Find out what the current size of the string in this font is
        SizeF RealSize = Graf.MeasureString(sStringToFit, FontToUse);
        Debug.WriteLine("big string is {0}, orig size = {1},{2}",
                         sStringToFit, RealSize.Width, RealSize.Height);
        if ((RealSize.Width <= TextRoomAvail.Width) && (RealSize.Height <= TextRoomAvail.Height))
        {
            Debug.WriteLine("The space is big enough already");
            // The current font is fine...
            return;
        }

        // Either width or height is too big...
        // Usually either the height ratio or the width ratio
        // will be less than 1. Work them out...
        float HeightScaleRatio = TextRoomAvail.Height / RealSize.Height;
        float WidthScaleRatio = TextRoomAvail.Width / RealSize.Width;

        // We'll scale the font by the one which is furthest out of range...
        float ScaleRatio = (HeightScaleRatio < WidthScaleRatio) ? ScaleRatio = HeightScaleRatio : ScaleRatio = WidthScaleRatio;
        float ScaleFontSize = FontToUse.Size * ScaleRatio;

        Debug.WriteLine("Resizing with scales {0},{1} chose {2}",
                         HeightScaleRatio, WidthScaleRatio, ScaleRatio);

        Debug.WriteLine("Old font size was {0}, new={1} ",FontToUse.Size,ScaleFontSize);

        // Retain whatever the style was in the old font...
        FontStyle OldFontStyle = FontToUse.Style;

        // Get rid of the old non working font...
        FontToUse.Dispose();

        // Tell the caller to use this newer smaller font.
        FontToUse = new Font(FontToUse.FontFamily,
                                ScaleFontSize,
                                OldFontStyle,
                                FontUnit);
    }

【讨论】:

    【解决方案3】:

    这只是@Saeed 的FindFont 函数的更新。

    GraphicsUnit.Pixel 需要添加到FindFont 函数返回行。如果没有GraphicsUnit.Pixel,系统dpi 将影响绘制的字符串。当系统和位图的 dpi 不匹配时,就会出现问题。您可以在此Windows DPI setting affects Graphics.DrawString 中查看更多详细信息。由于 PreferedFont 的 GraphicsUnit 已设置为 GraphicsUnit.Pixel 并且返回字体未设置为 GraphicsUnit.Pixel。在这种情况下,如果位图 dpi 大于系统 dpi,文本将超出Room 维度,如果位图 dpi 小于系统 dpi,则字体大小将小于预期大小。这是更新后的功能。

        private Font FindFont(  System.Drawing.Graphics g , string longString , Size Room , Font PreferedFont)
        {
            SizeF RealSize = g.MeasureString(longString, PreferedFont);
            float HeightScaleRatio = Room.Height / RealSize.Height;
            float WidthScaleRatio = Room.Width / RealSize.Width;
            float ScaleRatio = (HeightScaleRatio < WidthScaleRatio) ? ScaleRatio = HeightScaleRatio : ScaleRatio = WidthScaleRatio;
            float ScaleFontSize = PreferedFont.Size * ScaleRatio;
            return new Font(PreferedFont.FontFamily, ScaleFontSize,PreferedFont.Style,GraphicsUnit.Pixel);
        }
    

    【讨论】:

    • 这很有趣。我从来没有遇到过这个问题,但我相信这是可能的。
    【解决方案4】:

    我不想抨击 saaeds 解决方案,它可能也非常棒。但我在 msdn 上找到了另一个:Dynamic Graphic Text Resizing,它对我有用。

    public Font GetAdjustedFont(Graphics GraphicRef, string GraphicString, Font OriginalFont, int ContainerWidth, int MaxFontSize, int MinFontSize, bool SmallestOnFail)
    {
       // We utilize MeasureString which we get via a control instance           
       for (int AdjustedSize = MaxFontSize; AdjustedSize >= MinFontSize; AdjustedSize--)
       {
          Font 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 new Font(OriginalFont.Name,MinFontSize,OriginalFont.Style);
       }
       else
       {
          return OriginalFont;
       }
    }
    

    【讨论】:

    • 我还发现这篇博文 (hanselman.com/blog/…) 非常有用。在我问这个问题的时候,saeed 的解决方案是完美的,我认为所有这些解决方案都是真正的目标。
    【解决方案5】:

    这是我支持包装的解决方案。

    public static Font GetAdjustedFont(Graphics graphic, string str, Font originalFont, Size containerSize)
        {
            // We utilize MeasureString which we get via a control instance           
            for (int adjustedSize = (int)originalFont.Size; adjustedSize >= 1; adjustedSize--)
            {
                var testFont = new Font(originalFont.Name, adjustedSize, originalFont.Style, GraphicsUnit.Pixel);
    
                // Test the string with the new size
                var adjustedSizeNew = graphic.MeasureString(str, testFont, containerSize.Width);
    
                if (containerSize.Height > Convert.ToInt32(adjustedSizeNew.Height))
                {
                    // Good font, return it
                    return testFont;
                }
            }
    
            return new Font(originalFont.Name, 1, originalFont.Style, GraphicsUnit.Pixel);
        }
    

    使用方法:

    var font = GetAdjustedFont(drawing, text, originalfont, wrapSize);
    drawing.DrawString(text, font, textBrush, new Rectangle(0, 0, wrapSize.Width, wrapSize.Height));
    

    【讨论】:

      猜你喜欢
      • 2013-06-04
      • 2011-05-23
      • 1970-01-01
      • 2017-04-13
      • 2011-09-03
      • 2012-08-10
      • 2016-04-21
      • 1970-01-01
      • 2016-12-19
      相关资源
      最近更新 更多