We have looked at fonts in a previous C# tutorial, in Auto Ellipsing in C#. But whereas in that tutorial a string gets cut off if there is not enough room, in this tutorial we are going to take a look at how to scale the font such that the text will fit in whatever you need it in. As a side note, look for a revisit on ellipsing in C# at some point in the future - as one of our readers sent us some interesting info about the performance characteristics of the API calls we used in that tutorial.
The code we will show here today can probably be used with any drawing
API with minimal tweaks, because, surprisingly, we don't need to do
anything crazy. Essentially, we are going to build a function today that
takes in a string and a Size, and returns a font size that will make
that string take up that amount of space. Below, you can see some screen
shots of the example app at different sizes:

So how do we do this? Well, here is the method signature:
public static Font AppropriateFont(Graphics g, float minFontSize,
float maxFontSize, Size layoutSize, string s, Font f, out SizeF extent)
Some of these arguments should be pretty self explanatory. For example,
minFontSize and maxFontSize are the minimum and maximum font sizes,
respectively, that the function will be allowed to return. The string
s is the string that we will be fitting, and the layoutSize is the
size that the function will fit the string to. The graphics object, g,
is needed because one of the methods we use in the function is attached
to the graphics object. The font f that is passed in is needed so we
know what font family and style the string will be drawn with (the size
set in this font object will actually be completely ignored). Finally,
the out param extent is the final size the string ends up being using
the font returned by the function.
Onto the body of the method:
public static Font AppropriateFont(Graphics g, float minFontSize,
float maxFontSize, Size layoutSize, string s, Font f, out SizeF extent)
{
if (maxFontSize == minFontSize)
f = new Font(f.FontFamily, minFontSize, f.Style);
extent = g.MeasureString(s, f);
if (maxFontSize <= minFontSize)
return f;
float hRatio = layoutSize.Height / extent.Height;
float wRatio = layoutSize.Width / extent.Width;
float ratio = (hRatio < wRatio) ? hRatio : wRatio;
float newSize = f.Size * ratio;
if (newSize < minFontSize)
newSize = minFontSize;
else if (newSize > maxFontSize)
newSize = maxFontSize;
f = new Font(f.FontFamily, newSize, f.Style);
extent = g.MeasureString(s, f);
return f;
}
Not much code at all, really. First, we check to see if the min and max font size are equal, and if so, we create a new font with that size (because we will return it without doing any work in a moment). Then we measure the string with the current font (because out params always need to be set before a function can return). Next, if the max font size is less than or equal to the min font size, we return the current font. This means that if you passed in a bad min and max, you are going to get back the font you passed in, and if you passed in min and max as the same number, you will get a font back out whose size is that number.
After those checks, we get to the real work. In order to do the calculation in a single pass, we calculate a ratio of the available size over the size of the string. A ratio above 1 means there is space left and we can make the font bigger, and a ratio below 1 means that the font is too big for the space and it need to be made smaller. And guess what? All we need to do is multiply the current font size by that ratio, and we get the correct font size for the space. Cool, eh?
Of course, that font could be above max or below the minimum value provided, so we throw in some checks to constrain the value. Finally, we create the new font, measure the string with that font (so the extent out param has the right value), and return the new font. And that is all there is to it!
Of course, it wouldn't be very nice of me if I didn't give you the code showing how to use this function. So here is the code from the silly example app:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
namespace FontScaling
{
public partial class Form1 : Form
{
string _text = "Resize Me!!";
public Form1()
{
InitializeComponent();
this.SetStyle(ControlStyles.ResizeRedraw, true);
}
protected override void OnPaint(PaintEventArgs e)
{
using (Font f = new Font("Tahoma", 15))
{
SizeF size;
using (Font f2 = AppropriateFont(e.Graphics, 5, 50,
ClientRectangle.Size, _text, f, out size))
{
PointF p = new PointF(
(ClientRectangle.Width - size.Width) / 2,
(ClientRectangle.Height - size.Height) / 2);
e.Graphics.DrawString(_text, f2, Brushes.Black, p);
}
}
base.OnPaint(e);
}
public static Font AppropriateFont(Graphics g, float minFontSize,
float maxFontSize, Size layoutSize, string s, Font f, out SizeF extent)
{
if (maxFontSize == minFontSize)
f = new Font(f.FontFamily, minFontSize, f.Style);
extent = g.MeasureString(s, f);
if (maxFontSize <= minFontSize)
return f;
float hRatio = layoutSize.Height / extent.Height;
float wRatio = layoutSize.Width / extent.Width;
float ratio = (hRatio < wRatio) ? hRatio : wRatio;
float newSize = f.Size * ratio;
if (newSize < minFontSize)
newSize = minFontSize;
else if (newSize > maxFontSize)
newSize = maxFontSize;
f = new Font(f.FontFamily, newSize, f.Style);
extent = g.MeasureString(s, f);
return f;
}
}
}
Pretty simple - I find the appropriate font for the string in the
OnPaint method, using the current size of the form as the area for the
string. I get back the font and draw the string with it, using the
size out param (and the size of the form) to center the string on the
form.
One neat little tidbit to note here is the ControlStyle I set in the
form constructor. By setting the ResizeRedraw style to true, the form
will automatically repaint every time it is resized.
And there you go - quick font scaling in C#! I even packaged my example project up for you to play with - you can download it below.
Source Files:
Hello there... it's been some time since I've been struggling with text resizing in C#, and your article certainly pointed me to a good direction.
But I'm still having trouble with one approach.
If I reduce the display string just to one letter (let's suppose: "A") and adjust the maximum font size to any high value (such as 1500), the text measurements will be getting lots of spaces.
I haven't found any way in which I can overcome those spaces. What if I want to scale letters to the pixel? Do you know how to do this?
Thanks in advance.
Alpha, the spaces are the result of what is called kerning - I am not sure how much fidelity there is in the newer .net frameworks to adjust this - hopefully a nudge in the right direction?
Great function!
Just what I needed. However, I think you could make it just a tad simpler still without the 'extent' out parameter. You can also drop the p PointF in the client code used for positioning.
With the StringFormat object, you don't need to have the size returned to the client as .Net can do the vertical and horizontal text alignment for you! You just need your function to return the 'AppropriateFont'!
Jeff
Thanks, very useful!
Thank you!
FYI, here is a simplified version I'm using:
i want to do similar thing in wpf window. It's different that normal windows form.
Is there any solution for it
Hi, Rakesh:
Did you ever find an equivilent function for scaling the font size for a WPF based Content Control class you would be able to share?
Thank you in advance for your time and help...
hi.....! It was really nice to be here As it gives great platform of idea sharing & problem solving facility...Thnx...!!
My problem is that i want to develop web application which does the Custom t-shirt designing & in that i want the functionality of choosing the ClipArts,ColorPalletes,TextSize,Rotation,Zoom in-out All this functionality should be done on the t-shirt itself...So,Can Anyone help me in finding out the source code for the same...Most important is that i am using Visual Studio 2005(.net) & C# as language....Thanks for reading me.....!!
Hi .... This is an excellent app.
But instead of on the form how do I apply to a label.
What I have is an application where you can type in text which will be displayed on a label control. We allready know the font, size, type they are using. We also know the maximum height and length of the label control. What I need to do is reduce the font size if the typed font does not fit onto that label. Which this small code would do it, but how do i apply it for a label if you could please help I would appriciate it very much.
Thank you
Hi,
We have an application where we want to chnage the font size as in Office Excel 2007 SLider bar at the right bottom corner. This is not exactly chnaging the font, instead it is resizing the entire image of the window. Any idea on how to implemet in c#
Greetings:
After a bit of reseach, I came across a following posting at http://www.dotnetmonster.com/Uwe/Forum.aspx/winform/21292/wpf-font-scaling that describes how to get automatic font scaling within WPF.
In short, to obtain the same effect in WPF, just embed a inside a . When the is resized inside the WPF Window, the contents of the will automatically be resized, provided that the Content Control doesn't have a MaxWidth nor MaxHeight constraining element. As a result, the completed XAML Code, with no custom C# code does the same as the WinForms example presented in this tutorial.
Application.xaml:
MainWindow.xaml:
Resize Me!!
Again, there is no change required to either the Application nor MainWindow C# code.
I hope this was of help....
My apologies for the oversight on my part. The XAML is as follows:
Application.xaml
OK, in my haste to post the WPF solution, I think I made my previous two postings less than readable. Perhaps the third time will be the charm. {:-)
The original posting for resizing text within a WPF window can be found at http://www.dotnetmonster.com/Uwe/Forum.aspx/winform/21292/wpf-font-scaling
In short, to obtain the same effect in WPF, just embed a TextBlock inside a Viewbox. When the TextBlock is resized inside the WPF Window, the contents of the TextBlock will automatically resize the Text property, provided that the TextBlock Content Control doesn't have a MaxWidth nor MaxHeight constraining element to a fixed minimum or maximum size. As a result, the following XAML code does the same as the WinForms example presented in this tutorial:
Application.xaml:
MainWindow.xaml:
Again, there is no change required to either the Application nor MainWindow C# code.
I hope this was of help...
Thanks! that put an end to my struggle, still new at WPF so spent a couple hours trying to make a textblock fit to a grid cell or canvas.
Just a note.
A ViewBox works good in Xaml; but if you are drawing text in a custom control for example this method still works good. You don't have the graphics object but you can use a FormattedText object to get the size of a given font.
Did anybody actually run this code?
I get a recursive stack overflow from the main app:
remove the
base.OnPaint(e);
Problem solved.
void textBlock3_SizeChanged(object sender, SizeChangedEventArgs e) { Size s = e.NewSize; textBlock3.Width = s.Width; textBlock3.Height = s.Height; }
Hi,
it is good example to re size text on the form. but I have to do same process on the control
Like I have Button1 , textBoxt1 and other controls that should be same re sized as your text.
saket.kumar@almondz.com saketkumar.dwivedi26@gmail.com
please mail to me
Great function - very clean, easy to use. Thank you!
Hi, I sub-classed a label control with this code. It works great except that it draws the text twice: once with what appears to be the default size, and again with the new size. I tried a lot of stupid things just to try to understand the behavior, but to no avail. Does anybody have any idea why this is happening?