web-dev-qa-db-fra.com

Redimensionnement dynamique de la police pour tenir dans l’espace lors de l’utilisation de Graphics.DrawString

Quelqu'un a-t-il un conseil alors que vous pouvez redimensionner dynamiquement une police pour l'adapter à une zone spécifique? Par exemple, j'ai un rectangle de 800 x 110 et je souhaite le remplir avec la police de taille maximale qui prend en charge la chaîne entière que je tente d'afficher.

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"));

De toute évidence, ce nom ne sera pas affiché dans l'espace prévu à la taille de la police. Il doit y avoir un moyen simple de faire cela?

16
farina

Vous devez effectuer une transformation d'échelle sur Font.Size. La fonction suivante en est un exemple, mais vous pouvez l'améliorer pour appliquer de meilleurs résultats.

Voici la fonction FindFont qui permet d'obtenir une pièce et un texte de taille préférée et qui vous donne une police de caractères dans laquelle vous pouvez définir un texte complet adapté à la pièce!

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

Pour votre question, vous pouvez l'appeler comme le code suivant:

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
   );
}
22
saeed

J'ai adapté l'excellente fonctionnalité de Saeed à mes besoins. Les commentaires expliquent tout:

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

Ceci est juste une mise à jour de la fonction FindFont de @ Saeed.

GraphicsUnit.Pixel doit être ajouté à la ligne de retour de la fonction FindFont. Sans GraphicsUnit.Pixel, le système va affecter la chaîne dessinée. Le problème se posera lorsque les dpi des incompatibilités système et bitmap. Vous pouvez voir plus de détails dans ceci Le paramètre Windows DPI affecte Graphics.DrawString . Étant donné que GraphicsUnit of PreferedFont est déjà défini sur GraphicsUnit.Pixel et que la police de renvoi n'est pas définie avec GraphicsUnit.Pixel. Dans ce cas, le texte sortira de la dimension Room, si le dpi bitmap est plus grand que le dpi système et la taille de la police sera plus petite que la taille attendue si le dpi bitmap est inférieur au dpi système. Voici la fonction mise à jour.

    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);
    }
3
Rijul Sudhir

Je ne veux pas critiquer saaeds solution qui est probablement assez génial, aussi. Mais j'en ai trouvé un autre sur msdn: Redimensionnement de texte graphique dynamique qui a fonctionné pour moi.

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;
   }
}
2
Jürgen Steinblock

Voici ma solution qui supporte le wrapping.

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

Comment utiliser:

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