Created
July 9, 2020 19:53
-
-
Save JanWichelmann/5d1b8210a9bd5c3c25c0703ac2af6540 to your computer and use it in GitHub Desktop.
Compute maximum font size to fit image (https://stackoverflow.com/questions/62608763/measure-font-to-fit-text-into-specific-width)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.Drawing; | |
namespace ConsoleApp1 | |
{ | |
class Program | |
{ | |
static void Main(string[] args) | |
{ | |
string fontFamilyName = "Arial"; | |
int minFontSize = 10; | |
int maxFontSize = 200; | |
var fm = new FontMeasure(fontFamilyName, minFontSize, maxFontSize); | |
// Create test images for many width/height combinations | |
for(int width = 100; width < 300; width += 20) | |
{ | |
for(int height = 100; height < 300; height += 20) | |
{ | |
// Test input | |
string input = "1188yyyiiiii_Š_?!mmmm"; | |
// Allocate image, add light background to better highlight image size | |
using var img = new Bitmap(width, height); | |
using var g = Graphics.FromImage(img); | |
g.Clear(Color.LightYellow); | |
// Get correctly sized font | |
var font = fm.GetFont(input, g, width, height); | |
// Render | |
g.DrawString(input, font, Brushes.Black, 0, 0); | |
//img.Save($"img_{width}_{height}.png"); | |
} | |
} | |
} | |
} | |
class FontMeasure | |
{ | |
int _minFontSize; | |
int _maxFontSize; | |
Dictionary<int, Font> _fontCache = new Dictionary<int, Font>(); | |
public FontMeasure(string fontFamily, int minFontSize, int maxFontSize) | |
{ | |
_minFontSize = minFontSize; | |
_maxFontSize = maxFontSize; | |
// Cache all potentially needed font sizes | |
for(int i = minFontSize; i <= maxFontSize; ++i) | |
_fontCache.Add(i, new Font(fontFamily, i)); | |
} | |
public Font GetFont(string str, Graphics g, int imgWidth, int imgHeight) | |
{ | |
int counter = 0; | |
// Measure with maximum sized font | |
var baseSize = g.MeasureString(str, _fontCache[_maxFontSize]); | |
++counter; | |
// Downsample to actual image size | |
float widthRatio = imgWidth / baseSize.Width; | |
float heightRatio = imgHeight / baseSize.Height; | |
float minRatio = Math.Min(widthRatio, heightRatio); | |
int estimatedFontSize = (int)(_maxFontSize * minRatio); | |
// Make sure the precomputed font list is always hit | |
if(estimatedFontSize > _maxFontSize) | |
estimatedFontSize = _maxFontSize; | |
else if(estimatedFontSize < _minFontSize) | |
estimatedFontSize = _minFontSize; | |
// Make sure the estimated size is not too large | |
var estimatedSize = g.MeasureString(str, _fontCache[estimatedFontSize]); | |
++counter; | |
bool estimatedSizeWasReduced = false; | |
while(estimatedSize.Width > imgWidth || estimatedSize.Height > imgHeight) | |
{ | |
if(estimatedFontSize == _minFontSize) | |
break; | |
--estimatedFontSize; | |
estimatedSizeWasReduced = true; | |
estimatedSize = g.MeasureString(str, _fontCache[estimatedFontSize]); | |
++counter; | |
} | |
// Can we increase the size a bit? | |
if(!estimatedSizeWasReduced) | |
{ | |
while(estimatedSize.Width < imgWidth && estimatedSize.Height < imgHeight) | |
{ | |
if(estimatedFontSize == _maxFontSize) | |
break; | |
++estimatedFontSize; | |
estimatedSize = g.MeasureString(str, _fontCache[estimatedFontSize]); | |
++counter; | |
} | |
// We increase the size until it is larger than the image, so we need to go back one step afterwards | |
if(estimatedFontSize > _minFontSize) | |
--estimatedFontSize; | |
} | |
Console.WriteLine("Calls to g.MeasureString(): " + counter); | |
return _fontCache[estimatedFontSize]; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment