Динамическое изменение размера шрифта в пространстве при использовании Graphics.DrawString

У кого-нибудь есть подсказка, тогда как вы можете динамически изменять размер шрифта в соответствии с определенной областью? Например, у меня есть прямоугольник 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"));

Очевидно, что полное имя не будет отображаться в пространстве, предоставляемом при большом размере шрифта. Должен быть простой способ сделать это?

Ответ 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 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
   );
}

Ответ 2

Я адаптировал саидскую функцию, чтобы больше соответствовать моим требованиям. Комментарии объясняют все:

    // 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

Я не хочу bash против решения saaeds, которое, вероятно, тоже довольно классно. Но я нашел еще одно в msdn: Динамическое изменение графического текста, которое сработало для меня.

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;
   }
}

Ответ 4

Это просто обновление функции @Saeed FindFont.

GraphicsUnit.Pixel необходимо добавить в строку возврата функции FindFont. Без GraphicsUnit.Pixel, dpi в системе будет влиять на рисованную строку. Проблема будет возникать, если dpi и системный растровый файл не совпадают. Вы можете увидеть более подробно в этом Настройка Windows DPI влияет на Graphics.DrawString. Поскольку GraphicsUnit из PreferedFont уже установлен в GraphicsUnit.Pixel, а возврат шрифта не устанавливается с помощью GraphicsUnit.Pixel. В этом случае текст выйдет из измерения Room, если растровый dpi будет больше, чем dpi, а размер шрифта будет меньше ожидаемого, если растровый 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);
    }

Ответ 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));