Graphics.MeasureCharacterRanges giving wrong size calculations










6














I'm trying to render some text into a specific part of an image in a Web Forms app. The text will be user entered, so I want to vary the font size to make sure it fits within the bounding box.



I have code that was doing this fine on my proof-of-concept implementation, but I'm now trying it against the assets from the designer, which are larger, and I'm getting some odd results.



I'm running the size calculation as follows:



StringFormat fmt = new StringFormat();
fmt.Alignment = StringAlignment.Center;
fmt.LineAlignment = StringAlignment.Near;
fmt.FormatFlags = StringFormatFlags.NoClip;
fmt.Trimming = StringTrimming.None;

int size = __startingSize;
Font font = __fonts.GetFontBySize(size);

while (GetStringBounds(text, font, fmt).IsLargerThan(__textBoundingBox))

context.Trace.Write("MyHandler.ProcessRequest",
"Decrementing font size to " + size + ", as size is "
+ GetStringBounds(text, font, fmt).Size()
+ " and limit is " + __textBoundingBox.Size());

size--;

if (size < __minimumSize)

break;


font = __fonts.GetFontBySize(size);


context.Trace.Write("MyHandler.ProcessRequest", "Writing " + text + " in "
+ font.FontFamily.Name + " at " + font.SizeInPoints + "pt, size is "
+ GetStringBounds(text, font, fmt).Size()
+ " and limit is " + __textBoundingBox.Size());


I then use the following line to render the text onto an image I'm pulling from the filesystem:



g.DrawString(text, font, __brush, __textBoundingBox, fmt);


where:




  • __fonts is a PrivateFontCollection,


  • PrivateFontCollection.GetFontBySize is an extension method that returns a FontFamily

  • RectangleF __textBoundingBox = new RectangleF(150, 110, 212, 64);

  • int __minimumSize = 8;

  • int __startingSize = 48;

  • Brush __brush = Brushes.White;


  • int size starts out at 48 and decrements within that loop


  • Graphics g has SmoothingMode.AntiAlias and TextRenderingHint.AntiAlias set


  • context is a System.Web.HttpContext (this is an excerpt from the ProcessRequest method of an IHttpHandler)

The other methods are:



private static RectangleF GetStringBounds(string text, Font font,
StringFormat fmt)

CharacterRange range = new CharacterRange(0, text.Length) ;
StringFormat myFormat = fmt.Clone() as StringFormat;
myFormat.SetMeasurableCharacterRanges(range);

using (Graphics g = Graphics.FromImage(new Bitmap(
(int) __textBoundingBox.Width - 1,
(int) __textBoundingBox.Height - 1)))

g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias;

Region regions = g.MeasureCharacterRanges(text, font,
__textBoundingBox, myFormat);
return regions[0].GetBounds(g);



public static string Size(this RectangleF rect)

return rect.Width + "×" + rect.Height;


public static bool IsLargerThan(this RectangleF a, RectangleF b)



Now I have two problems.



Firstly, the text sometimes insists on wrapping by inserting a line-break within a word, when it should just fail to fit and cause the while loop to decrement again. I can't see why it is that Graphics.MeasureCharacterRanges thinks that this fits within the box when it shouldn't be word-wrapping within a word. This behaviour is exhibited irrespective of the character set used (I get it in Latin alphabet words, as well as other parts of the Unicode range, like Cyrillic, Greek, Georgian and Armenian). Is there some setting I should be using to force Graphics.MeasureCharacterRanges only to be word-wrapping at whitespace characters (or hyphens)? This first problem is the same as post 2499067.



Secondly, in scaling up to the new image and font size, Graphics.MeasureCharacterRanges is giving me heights that are wildly off. The RectangleF I am drawing within corresponds to a visually apparent area of the image, so I can easily see when the text is being decremented more than is necessary. Yet when I pass it some text, the GetBounds call is giving me a height that is almost double what it's actually taking.



Using trial and error to set the __minimumSize to force an exit from the while loop, I can see that 24pt text fits within the bounding box, yet Graphics.MeasureCharacterRanges is reporting that the height of that text, once rendered to the image, is 122px (when the bounding box is 64px tall and it fits within that box). Indeed, without forcing the matter, the while loop iterates to 18pt, at which point Graphics.MeasureCharacterRanges returns a value that fits.



The trace log excerpt is as follows:




Decrementing font size to 24, as size is 193×122 and limit is 212×64

Decrementing font size to 23, as size is 191×117 and limit is 212×64

Decrementing font size to 22, as size is 200×75 and limit is 212×64

Decrementing font size to 21, as size is 192×71 and limit is 212×64

Decrementing font size to 20, as size is 198×68 and limit is 212×64

Decrementing font size to 19, as size is 185×65 and limit is 212×64

Writing VENNEGOOR of HESSELINK in DIN-Black at 18pt, size is 178×61 and limit is 212×64




So why is Graphics.MeasureCharacterRanges giving me a wrong result? I could understand it being, say, the line height of the font if the loop stopped around 21pt (which would visually fit, if I screenshot the results and measure it in Paint.Net), but it's going far further than it should be doing because, frankly, it's returning the wrong damn results.










share|improve this question























  • +1 for one of the most documented questions I've seen in a while!
    – SouthShoreAK
    Apr 18 '12 at 16:56















6














I'm trying to render some text into a specific part of an image in a Web Forms app. The text will be user entered, so I want to vary the font size to make sure it fits within the bounding box.



I have code that was doing this fine on my proof-of-concept implementation, but I'm now trying it against the assets from the designer, which are larger, and I'm getting some odd results.



I'm running the size calculation as follows:



StringFormat fmt = new StringFormat();
fmt.Alignment = StringAlignment.Center;
fmt.LineAlignment = StringAlignment.Near;
fmt.FormatFlags = StringFormatFlags.NoClip;
fmt.Trimming = StringTrimming.None;

int size = __startingSize;
Font font = __fonts.GetFontBySize(size);

while (GetStringBounds(text, font, fmt).IsLargerThan(__textBoundingBox))

context.Trace.Write("MyHandler.ProcessRequest",
"Decrementing font size to " + size + ", as size is "
+ GetStringBounds(text, font, fmt).Size()
+ " and limit is " + __textBoundingBox.Size());

size--;

if (size < __minimumSize)

break;


font = __fonts.GetFontBySize(size);


context.Trace.Write("MyHandler.ProcessRequest", "Writing " + text + " in "
+ font.FontFamily.Name + " at " + font.SizeInPoints + "pt, size is "
+ GetStringBounds(text, font, fmt).Size()
+ " and limit is " + __textBoundingBox.Size());


I then use the following line to render the text onto an image I'm pulling from the filesystem:



g.DrawString(text, font, __brush, __textBoundingBox, fmt);


where:




  • __fonts is a PrivateFontCollection,


  • PrivateFontCollection.GetFontBySize is an extension method that returns a FontFamily

  • RectangleF __textBoundingBox = new RectangleF(150, 110, 212, 64);

  • int __minimumSize = 8;

  • int __startingSize = 48;

  • Brush __brush = Brushes.White;


  • int size starts out at 48 and decrements within that loop


  • Graphics g has SmoothingMode.AntiAlias and TextRenderingHint.AntiAlias set


  • context is a System.Web.HttpContext (this is an excerpt from the ProcessRequest method of an IHttpHandler)

The other methods are:



private static RectangleF GetStringBounds(string text, Font font,
StringFormat fmt)

CharacterRange range = new CharacterRange(0, text.Length) ;
StringFormat myFormat = fmt.Clone() as StringFormat;
myFormat.SetMeasurableCharacterRanges(range);

using (Graphics g = Graphics.FromImage(new Bitmap(
(int) __textBoundingBox.Width - 1,
(int) __textBoundingBox.Height - 1)))

g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias;

Region regions = g.MeasureCharacterRanges(text, font,
__textBoundingBox, myFormat);
return regions[0].GetBounds(g);



public static string Size(this RectangleF rect)

return rect.Width + "×" + rect.Height;


public static bool IsLargerThan(this RectangleF a, RectangleF b)



Now I have two problems.



Firstly, the text sometimes insists on wrapping by inserting a line-break within a word, when it should just fail to fit and cause the while loop to decrement again. I can't see why it is that Graphics.MeasureCharacterRanges thinks that this fits within the box when it shouldn't be word-wrapping within a word. This behaviour is exhibited irrespective of the character set used (I get it in Latin alphabet words, as well as other parts of the Unicode range, like Cyrillic, Greek, Georgian and Armenian). Is there some setting I should be using to force Graphics.MeasureCharacterRanges only to be word-wrapping at whitespace characters (or hyphens)? This first problem is the same as post 2499067.



Secondly, in scaling up to the new image and font size, Graphics.MeasureCharacterRanges is giving me heights that are wildly off. The RectangleF I am drawing within corresponds to a visually apparent area of the image, so I can easily see when the text is being decremented more than is necessary. Yet when I pass it some text, the GetBounds call is giving me a height that is almost double what it's actually taking.



Using trial and error to set the __minimumSize to force an exit from the while loop, I can see that 24pt text fits within the bounding box, yet Graphics.MeasureCharacterRanges is reporting that the height of that text, once rendered to the image, is 122px (when the bounding box is 64px tall and it fits within that box). Indeed, without forcing the matter, the while loop iterates to 18pt, at which point Graphics.MeasureCharacterRanges returns a value that fits.



The trace log excerpt is as follows:




Decrementing font size to 24, as size is 193×122 and limit is 212×64

Decrementing font size to 23, as size is 191×117 and limit is 212×64

Decrementing font size to 22, as size is 200×75 and limit is 212×64

Decrementing font size to 21, as size is 192×71 and limit is 212×64

Decrementing font size to 20, as size is 198×68 and limit is 212×64

Decrementing font size to 19, as size is 185×65 and limit is 212×64

Writing VENNEGOOR of HESSELINK in DIN-Black at 18pt, size is 178×61 and limit is 212×64




So why is Graphics.MeasureCharacterRanges giving me a wrong result? I could understand it being, say, the line height of the font if the loop stopped around 21pt (which would visually fit, if I screenshot the results and measure it in Paint.Net), but it's going far further than it should be doing because, frankly, it's returning the wrong damn results.










share|improve this question























  • +1 for one of the most documented questions I've seen in a while!
    – SouthShoreAK
    Apr 18 '12 at 16:56













6












6








6


0





I'm trying to render some text into a specific part of an image in a Web Forms app. The text will be user entered, so I want to vary the font size to make sure it fits within the bounding box.



I have code that was doing this fine on my proof-of-concept implementation, but I'm now trying it against the assets from the designer, which are larger, and I'm getting some odd results.



I'm running the size calculation as follows:



StringFormat fmt = new StringFormat();
fmt.Alignment = StringAlignment.Center;
fmt.LineAlignment = StringAlignment.Near;
fmt.FormatFlags = StringFormatFlags.NoClip;
fmt.Trimming = StringTrimming.None;

int size = __startingSize;
Font font = __fonts.GetFontBySize(size);

while (GetStringBounds(text, font, fmt).IsLargerThan(__textBoundingBox))

context.Trace.Write("MyHandler.ProcessRequest",
"Decrementing font size to " + size + ", as size is "
+ GetStringBounds(text, font, fmt).Size()
+ " and limit is " + __textBoundingBox.Size());

size--;

if (size < __minimumSize)

break;


font = __fonts.GetFontBySize(size);


context.Trace.Write("MyHandler.ProcessRequest", "Writing " + text + " in "
+ font.FontFamily.Name + " at " + font.SizeInPoints + "pt, size is "
+ GetStringBounds(text, font, fmt).Size()
+ " and limit is " + __textBoundingBox.Size());


I then use the following line to render the text onto an image I'm pulling from the filesystem:



g.DrawString(text, font, __brush, __textBoundingBox, fmt);


where:




  • __fonts is a PrivateFontCollection,


  • PrivateFontCollection.GetFontBySize is an extension method that returns a FontFamily

  • RectangleF __textBoundingBox = new RectangleF(150, 110, 212, 64);

  • int __minimumSize = 8;

  • int __startingSize = 48;

  • Brush __brush = Brushes.White;


  • int size starts out at 48 and decrements within that loop


  • Graphics g has SmoothingMode.AntiAlias and TextRenderingHint.AntiAlias set


  • context is a System.Web.HttpContext (this is an excerpt from the ProcessRequest method of an IHttpHandler)

The other methods are:



private static RectangleF GetStringBounds(string text, Font font,
StringFormat fmt)

CharacterRange range = new CharacterRange(0, text.Length) ;
StringFormat myFormat = fmt.Clone() as StringFormat;
myFormat.SetMeasurableCharacterRanges(range);

using (Graphics g = Graphics.FromImage(new Bitmap(
(int) __textBoundingBox.Width - 1,
(int) __textBoundingBox.Height - 1)))

g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias;

Region regions = g.MeasureCharacterRanges(text, font,
__textBoundingBox, myFormat);
return regions[0].GetBounds(g);



public static string Size(this RectangleF rect)

return rect.Width + "×" + rect.Height;


public static bool IsLargerThan(this RectangleF a, RectangleF b)



Now I have two problems.



Firstly, the text sometimes insists on wrapping by inserting a line-break within a word, when it should just fail to fit and cause the while loop to decrement again. I can't see why it is that Graphics.MeasureCharacterRanges thinks that this fits within the box when it shouldn't be word-wrapping within a word. This behaviour is exhibited irrespective of the character set used (I get it in Latin alphabet words, as well as other parts of the Unicode range, like Cyrillic, Greek, Georgian and Armenian). Is there some setting I should be using to force Graphics.MeasureCharacterRanges only to be word-wrapping at whitespace characters (or hyphens)? This first problem is the same as post 2499067.



Secondly, in scaling up to the new image and font size, Graphics.MeasureCharacterRanges is giving me heights that are wildly off. The RectangleF I am drawing within corresponds to a visually apparent area of the image, so I can easily see when the text is being decremented more than is necessary. Yet when I pass it some text, the GetBounds call is giving me a height that is almost double what it's actually taking.



Using trial and error to set the __minimumSize to force an exit from the while loop, I can see that 24pt text fits within the bounding box, yet Graphics.MeasureCharacterRanges is reporting that the height of that text, once rendered to the image, is 122px (when the bounding box is 64px tall and it fits within that box). Indeed, without forcing the matter, the while loop iterates to 18pt, at which point Graphics.MeasureCharacterRanges returns a value that fits.



The trace log excerpt is as follows:




Decrementing font size to 24, as size is 193×122 and limit is 212×64

Decrementing font size to 23, as size is 191×117 and limit is 212×64

Decrementing font size to 22, as size is 200×75 and limit is 212×64

Decrementing font size to 21, as size is 192×71 and limit is 212×64

Decrementing font size to 20, as size is 198×68 and limit is 212×64

Decrementing font size to 19, as size is 185×65 and limit is 212×64

Writing VENNEGOOR of HESSELINK in DIN-Black at 18pt, size is 178×61 and limit is 212×64




So why is Graphics.MeasureCharacterRanges giving me a wrong result? I could understand it being, say, the line height of the font if the loop stopped around 21pt (which would visually fit, if I screenshot the results and measure it in Paint.Net), but it's going far further than it should be doing because, frankly, it's returning the wrong damn results.










share|improve this question















I'm trying to render some text into a specific part of an image in a Web Forms app. The text will be user entered, so I want to vary the font size to make sure it fits within the bounding box.



I have code that was doing this fine on my proof-of-concept implementation, but I'm now trying it against the assets from the designer, which are larger, and I'm getting some odd results.



I'm running the size calculation as follows:



StringFormat fmt = new StringFormat();
fmt.Alignment = StringAlignment.Center;
fmt.LineAlignment = StringAlignment.Near;
fmt.FormatFlags = StringFormatFlags.NoClip;
fmt.Trimming = StringTrimming.None;

int size = __startingSize;
Font font = __fonts.GetFontBySize(size);

while (GetStringBounds(text, font, fmt).IsLargerThan(__textBoundingBox))

context.Trace.Write("MyHandler.ProcessRequest",
"Decrementing font size to " + size + ", as size is "
+ GetStringBounds(text, font, fmt).Size()
+ " and limit is " + __textBoundingBox.Size());

size--;

if (size < __minimumSize)

break;


font = __fonts.GetFontBySize(size);


context.Trace.Write("MyHandler.ProcessRequest", "Writing " + text + " in "
+ font.FontFamily.Name + " at " + font.SizeInPoints + "pt, size is "
+ GetStringBounds(text, font, fmt).Size()
+ " and limit is " + __textBoundingBox.Size());


I then use the following line to render the text onto an image I'm pulling from the filesystem:



g.DrawString(text, font, __brush, __textBoundingBox, fmt);


where:




  • __fonts is a PrivateFontCollection,


  • PrivateFontCollection.GetFontBySize is an extension method that returns a FontFamily

  • RectangleF __textBoundingBox = new RectangleF(150, 110, 212, 64);

  • int __minimumSize = 8;

  • int __startingSize = 48;

  • Brush __brush = Brushes.White;


  • int size starts out at 48 and decrements within that loop


  • Graphics g has SmoothingMode.AntiAlias and TextRenderingHint.AntiAlias set


  • context is a System.Web.HttpContext (this is an excerpt from the ProcessRequest method of an IHttpHandler)

The other methods are:



private static RectangleF GetStringBounds(string text, Font font,
StringFormat fmt)

CharacterRange range = new CharacterRange(0, text.Length) ;
StringFormat myFormat = fmt.Clone() as StringFormat;
myFormat.SetMeasurableCharacterRanges(range);

using (Graphics g = Graphics.FromImage(new Bitmap(
(int) __textBoundingBox.Width - 1,
(int) __textBoundingBox.Height - 1)))

g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias;

Region regions = g.MeasureCharacterRanges(text, font,
__textBoundingBox, myFormat);
return regions[0].GetBounds(g);



public static string Size(this RectangleF rect)

return rect.Width + "×" + rect.Height;


public static bool IsLargerThan(this RectangleF a, RectangleF b)



Now I have two problems.



Firstly, the text sometimes insists on wrapping by inserting a line-break within a word, when it should just fail to fit and cause the while loop to decrement again. I can't see why it is that Graphics.MeasureCharacterRanges thinks that this fits within the box when it shouldn't be word-wrapping within a word. This behaviour is exhibited irrespective of the character set used (I get it in Latin alphabet words, as well as other parts of the Unicode range, like Cyrillic, Greek, Georgian and Armenian). Is there some setting I should be using to force Graphics.MeasureCharacterRanges only to be word-wrapping at whitespace characters (or hyphens)? This first problem is the same as post 2499067.



Secondly, in scaling up to the new image and font size, Graphics.MeasureCharacterRanges is giving me heights that are wildly off. The RectangleF I am drawing within corresponds to a visually apparent area of the image, so I can easily see when the text is being decremented more than is necessary. Yet when I pass it some text, the GetBounds call is giving me a height that is almost double what it's actually taking.



Using trial and error to set the __minimumSize to force an exit from the while loop, I can see that 24pt text fits within the bounding box, yet Graphics.MeasureCharacterRanges is reporting that the height of that text, once rendered to the image, is 122px (when the bounding box is 64px tall and it fits within that box). Indeed, without forcing the matter, the while loop iterates to 18pt, at which point Graphics.MeasureCharacterRanges returns a value that fits.



The trace log excerpt is as follows:




Decrementing font size to 24, as size is 193×122 and limit is 212×64

Decrementing font size to 23, as size is 191×117 and limit is 212×64

Decrementing font size to 22, as size is 200×75 and limit is 212×64

Decrementing font size to 21, as size is 192×71 and limit is 212×64

Decrementing font size to 20, as size is 198×68 and limit is 212×64

Decrementing font size to 19, as size is 185×65 and limit is 212×64

Writing VENNEGOOR of HESSELINK in DIN-Black at 18pt, size is 178×61 and limit is 212×64




So why is Graphics.MeasureCharacterRanges giving me a wrong result? I could understand it being, say, the line height of the font if the loop stopped around 21pt (which would visually fit, if I screenshot the results and measure it in Paint.Net), but it's going far further than it should be doing because, frankly, it's returning the wrong damn results.







gdi+ system.drawing drawstring






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Jul 12 at 22:10









Drew Noakes

184k114522607




184k114522607










asked Apr 21 '10 at 16:53









Owen Blacker

3,09312566




3,09312566











  • +1 for one of the most documented questions I've seen in a while!
    – SouthShoreAK
    Apr 18 '12 at 16:56
















  • +1 for one of the most documented questions I've seen in a while!
    – SouthShoreAK
    Apr 18 '12 at 16:56















+1 for one of the most documented questions I've seen in a while!
– SouthShoreAK
Apr 18 '12 at 16:56




+1 for one of the most documented questions I've seen in a while!
– SouthShoreAK
Apr 18 '12 at 16:56












5 Answers
5






active

oldest

votes


















1














I have a similar problem. I want to know how big the text I'm drawing is going to be, and where it's going to appear, EXACTLY. I haven't had the line-break problem, so I don't think I can help you there. I had the same problems you had with all the various measuring techniques available, including ending up with MeasureCharacterRanges, which worked okay for the left and right, but not at all for the height and top. (Playing with the baseline can work well for some rare applications though.)



I've ended up with a very inelegant, inefficient, but working solution, at least for my use case. I draw the text on a bitmap, check the bits to see where they ended up, and that's my range. Since I'm mostly drawing small fonts and short strings, it's been fast enough for me (especially with the memoization I added). Maybe this won't be exactly what you need, but maybe it can lead you down the right track anyway.



Note it requires compiling the project to allow unsafe code at the moment, as I'm trying to squeeze out every bit of efficiency from it, but that constraint could be removed if you wanted to. Also, it's not as thread safe as it could be right now, you could easily add that if you needed it.



Dictionary<Tuple<string, Font, Brush>, Rectangle> cachedTextBounds = new Dictionary<Tuple<string, Font, Brush>, Rectangle>();
/// <summary>
/// Determines bounds of some text by actually drawing the text to a bitmap and
/// reading the bits to see where it ended up. Bounds assume you draw at 0, 0. If
/// drawing elsewhere, you can easily offset the resulting rectangle appropriately.
/// </summary>
/// <param name="text">The text to be drawn</param>
/// <param name="font">The font to use when drawing the text</param>
/// <param name="brush">The brush to be used when drawing the text</param>
/// <returns>The bounding rectangle of the rendered text</returns>
private unsafe Rectangle RenderedTextBounds(string text, Font font, Brush brush)

// First check memoization
Tuple<string, Font, Brush> t = new Tuple<string, Font, Brush>(text, font, brush);
try
return cachedTextBounds[t];

catch(KeyNotFoundException)
// not cached


// Draw the string on a bitmap
Rectangle bounds = new Rectangle();
Size approxSize = TextRenderer.MeasureText(text, font);
using(Bitmap bitmap = new Bitmap((int)(approxSize.Width*1.5), (int)(approxSize.Height*1.5)))
using(Graphics g = Graphics.FromImage(bitmap))
g.DrawString(text, font, brush, 0, 0);
// Unsafe LockBits code takes a bit over 10% of time compared to safe GetPixel code
BitmapData bd = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
byte* row = (byte*)bd.Scan0;
// Find left, looking for first bit that has a non-zero alpha channel, so it's not clear
for(int x = 0; x < bitmap.Width; x++)
for(int y = 0; y < bitmap.Height; y++)
if(((byte*)bd.Scan0)[y*bd.Stride + 4*x + 3] != 0)
bounds.X = x;
goto foundX;

foundX:
// Right
for(int x = bitmap.Width - 1; x >= 0; x--)
for(int y = 0; y < bitmap.Height; y++)
if(((byte*)bd.Scan0)[y*bd.Stride + 4*x + 3] != 0)
bounds.Width = x - bounds.X + 1;
goto foundWidth;

foundWidth:
// Top
for(int y = 0; y < bitmap.Height; y++)
for(int x = 0; x < bitmap.Width; x++)
if(((byte*)bd.Scan0)[y*bd.Stride + 4*x + 3] != 0)
bounds.Y = y;
goto foundY;

foundY:
// Bottom
for(int y = bitmap.Height - 1; y >= 0; y--)
for(int x = 0; x < bitmap.Width; x++)
if(((byte*)bd.Scan0)[y*bd.Stride + 4*x + 3] != 0)
bounds.Height = y - bounds.Y + 1;
goto foundHeight;

foundHeight:
bitmap.UnlockBits(bd);

cachedTextBounds[t] = bounds;
return bounds;






share|improve this answer




















  • Nice work! I'll have to give that a try and see if that solves my problem. Though I'm somewhat loathe to have to rely on unsafe code. How quick do you find that code? The circumstance I'm using it in checks whether the text is too big for a fixed-size bounding box and decreases the font size within a while loop (checking while too big or size gt hard-floor-limit), so I don't want to perform an expensive operation in that while loop…
    – Owen Blacker
    Aug 16 '11 at 12:03










  • Speed depends heavily on font size. It allocates bitmap, renders it, then searches for boundaries. Smaller fonts are a lot faster, and you lose with larger fonts something like O(size^2) I think. May want to guess small size and work up to a size that's too big instead of the other way around. You could use MeasureCharacterRanges as first guess for lower bound, since it always seems to end up too small a guess. There's a lot of room for cleverness searching for the right size, binary search with clever guessing etc. I don't need any of that for my purpose, so I haven't played with it.
    – user12861
    Aug 16 '11 at 13:14










  • As to unsafe, you can do it without that but speed takes a hit. As commented this is almost 90% faster than using GetPixel, but you can still do an in between method using LockBits without unsafe code. I didn't try that as I was ok with unsafe code and wanted to squeeze out a bit of easily obtained performance. There are lots of ways to try to squeeze out performance depending on how exactly you're using this stuff, but they're all complicated. This whole thing is kind of nasty I fully realize. I would think there would be a better way, but my search online was as unproductive as yours.
    – user12861
    Aug 16 '11 at 13:17










  • That's really helpful, user12861; thank you. Hopefully, I'll get a chance to give this a try sometime soon.
    – Owen Blacker
    Aug 25 '11 at 10:48


















0














Could you try removing the following line?



fmt.FormatFlags = StringFormatFlags.NoClip;



Overhanging parts of glyphs, and
unwrapped text reaching outside the
formatting rectangle are allowed to
show. By default all text and glyph
parts reaching outside the formatting
rectangle are clipped.




That's the best I can come up with for this :(






share|improve this answer




















  • Thanks for posting an answer. I did look at this, having thought it might be the problem, but the reported height is almost double the actual height, so I don'think it can be that. It seems that the difference StringFormatFlags.NoClip makes is that if the bowl of a letter P (for example) just pokes outside the bounding box then it's allowed to render, rather than being clipped. That doesn't seem to be the problem I'm experiencing. But thanks :o)
    – Owen Blacker
    Apr 29 '10 at 8:51


















0














I also had some problems with the MeasureCharacterRanges method. It was giving me inconsistent sizes for the same string and even the same Graphics object. Then I discovered that it depends on the value of the layoutRect parametr - I can't see why, in my opinion it's a bug in the .NET code.



For example if layoutRect was completely empty (all values set to zero), I got correct values for the string "a" - the size was Width=8.898438, Height=18.10938 using 12pt Ms Sans Serif font.



However, when I set the value of the 'X' property of the rectangle to a non-integer number (like 1.2), it gave me Width=9, Height=19.



So I really think there is a bug when you use a layout rectangle with non-integer X coordinate.






share|improve this answer




















  • Interesting — looks like it's always rounding your size up. Unfortunately, my layoutRect is always integral — it's defined as private static readonly RectangleF __textBoundingBox = new RectangleF(150, 110, 212, 64); and its value never changes (obviously, as it's marked readonly). It's definitely a bug in the .Net code, but it doesn't look like your bug and my bug are the same one.
    – Owen Blacker
    Aug 9 '10 at 11:31


















0














Ok so 4 years late but this question EXACTLY matched my symptoms and I've actually worked out the cause.



There is most certainly a bug in MeasureString AND MeasureCharacterRanges.



The simple answer is:
Make sure you divide your width restriction (int width in MeasureString or the Size.Width property of the boundingRect in MeasureCharacterRanges) by 0.72. When you get your results back multiply each dimension by 0.72 to get the REAL result



int measureWidth = Convert.ToInt32((float)width/0.72);
SizeF measureSize = gfx.MeasureString(text, font, measureWidth, format);
float actualHeight = measureSize.Height * (float)0.72;


or



float measureWidth = width/0.72;
Region regions = gfx.MeasureCharacterRanges(text, font, new RectangleF(0,0,measureWidth, format);
float actualHeight = 0;
if(regions.Length>0)

actualHeight = regions[0].GetBounds(gfx).Size.Height * (float)0.72;



The explanation (that I can figure out) is that something to do with the context is triggering a conversion in the Measure methods (that doesn't trigger in the DrawString method) for inch->point (*72/100). When you pass in the ACTUAL width limitation it is adjusting this value so the MEASURED width limitation is, in effect, shorter than it should be. Your text then wraps earlier than it is supposed to and so you get a longer height result than expected. Unfortunately the conversion applies to the actual height result as well so it's a good idea to 'unconvert' that value too.






share|improve this answer




















  • On a REALLY annoying side note, I have absolutely NO idea why MeasureString takes an int for width. If you set your graphics context unit of measurement to inches then (due to the int) you can only set your width limitation to 1 inch, or 2 inches, etc. Completely ridiculous!
    – Red Nightingale
    Nov 16 '14 at 1:07


















0














To convert from points to dpi as in screen resolution you need to divide by 72 and multiply by DPI, for example:
graphics.DpiY * text.Width / 72



Red Nightengale was really close, because graphics.DpiY is usually 96 for screen resolutions.






share|improve this answer




















  • That's not an answer to the question as a whole, though. Could you expand this into an answer — including any of Red Nightingale's answer that you need to duplicate? Alternatively, post this as a comment on Red Nightingale's answer? Thanks!
    – Owen Blacker
    Nov 12 at 7:25










Your Answer






StackExchange.ifUsing("editor", function ()
StackExchange.using("externalEditor", function ()
StackExchange.using("snippets", function ()
StackExchange.snippets.init();
);
);
, "code-snippets");

StackExchange.ready(function()
var channelOptions =
tags: "".split(" "),
id: "1"
;
initTagRenderer("".split(" "), "".split(" "), channelOptions);

StackExchange.using("externalEditor", function()
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled)
StackExchange.using("snippets", function()
createEditor();
);

else
createEditor();

);

function createEditor()
StackExchange.prepareEditor(
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader:
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
,
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
);



);













draft saved

draft discarded


















StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f2684894%2fgraphics-measurecharacterranges-giving-wrong-size-calculations%23new-answer', 'question_page');

);

Post as a guest















Required, but never shown

























5 Answers
5






active

oldest

votes








5 Answers
5






active

oldest

votes









active

oldest

votes






active

oldest

votes









1














I have a similar problem. I want to know how big the text I'm drawing is going to be, and where it's going to appear, EXACTLY. I haven't had the line-break problem, so I don't think I can help you there. I had the same problems you had with all the various measuring techniques available, including ending up with MeasureCharacterRanges, which worked okay for the left and right, but not at all for the height and top. (Playing with the baseline can work well for some rare applications though.)



I've ended up with a very inelegant, inefficient, but working solution, at least for my use case. I draw the text on a bitmap, check the bits to see where they ended up, and that's my range. Since I'm mostly drawing small fonts and short strings, it's been fast enough for me (especially with the memoization I added). Maybe this won't be exactly what you need, but maybe it can lead you down the right track anyway.



Note it requires compiling the project to allow unsafe code at the moment, as I'm trying to squeeze out every bit of efficiency from it, but that constraint could be removed if you wanted to. Also, it's not as thread safe as it could be right now, you could easily add that if you needed it.



Dictionary<Tuple<string, Font, Brush>, Rectangle> cachedTextBounds = new Dictionary<Tuple<string, Font, Brush>, Rectangle>();
/// <summary>
/// Determines bounds of some text by actually drawing the text to a bitmap and
/// reading the bits to see where it ended up. Bounds assume you draw at 0, 0. If
/// drawing elsewhere, you can easily offset the resulting rectangle appropriately.
/// </summary>
/// <param name="text">The text to be drawn</param>
/// <param name="font">The font to use when drawing the text</param>
/// <param name="brush">The brush to be used when drawing the text</param>
/// <returns>The bounding rectangle of the rendered text</returns>
private unsafe Rectangle RenderedTextBounds(string text, Font font, Brush brush)

// First check memoization
Tuple<string, Font, Brush> t = new Tuple<string, Font, Brush>(text, font, brush);
try
return cachedTextBounds[t];

catch(KeyNotFoundException)
// not cached


// Draw the string on a bitmap
Rectangle bounds = new Rectangle();
Size approxSize = TextRenderer.MeasureText(text, font);
using(Bitmap bitmap = new Bitmap((int)(approxSize.Width*1.5), (int)(approxSize.Height*1.5)))
using(Graphics g = Graphics.FromImage(bitmap))
g.DrawString(text, font, brush, 0, 0);
// Unsafe LockBits code takes a bit over 10% of time compared to safe GetPixel code
BitmapData bd = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
byte* row = (byte*)bd.Scan0;
// Find left, looking for first bit that has a non-zero alpha channel, so it's not clear
for(int x = 0; x < bitmap.Width; x++)
for(int y = 0; y < bitmap.Height; y++)
if(((byte*)bd.Scan0)[y*bd.Stride + 4*x + 3] != 0)
bounds.X = x;
goto foundX;

foundX:
// Right
for(int x = bitmap.Width - 1; x >= 0; x--)
for(int y = 0; y < bitmap.Height; y++)
if(((byte*)bd.Scan0)[y*bd.Stride + 4*x + 3] != 0)
bounds.Width = x - bounds.X + 1;
goto foundWidth;

foundWidth:
// Top
for(int y = 0; y < bitmap.Height; y++)
for(int x = 0; x < bitmap.Width; x++)
if(((byte*)bd.Scan0)[y*bd.Stride + 4*x + 3] != 0)
bounds.Y = y;
goto foundY;

foundY:
// Bottom
for(int y = bitmap.Height - 1; y >= 0; y--)
for(int x = 0; x < bitmap.Width; x++)
if(((byte*)bd.Scan0)[y*bd.Stride + 4*x + 3] != 0)
bounds.Height = y - bounds.Y + 1;
goto foundHeight;

foundHeight:
bitmap.UnlockBits(bd);

cachedTextBounds[t] = bounds;
return bounds;






share|improve this answer




















  • Nice work! I'll have to give that a try and see if that solves my problem. Though I'm somewhat loathe to have to rely on unsafe code. How quick do you find that code? The circumstance I'm using it in checks whether the text is too big for a fixed-size bounding box and decreases the font size within a while loop (checking while too big or size gt hard-floor-limit), so I don't want to perform an expensive operation in that while loop…
    – Owen Blacker
    Aug 16 '11 at 12:03










  • Speed depends heavily on font size. It allocates bitmap, renders it, then searches for boundaries. Smaller fonts are a lot faster, and you lose with larger fonts something like O(size^2) I think. May want to guess small size and work up to a size that's too big instead of the other way around. You could use MeasureCharacterRanges as first guess for lower bound, since it always seems to end up too small a guess. There's a lot of room for cleverness searching for the right size, binary search with clever guessing etc. I don't need any of that for my purpose, so I haven't played with it.
    – user12861
    Aug 16 '11 at 13:14










  • As to unsafe, you can do it without that but speed takes a hit. As commented this is almost 90% faster than using GetPixel, but you can still do an in between method using LockBits without unsafe code. I didn't try that as I was ok with unsafe code and wanted to squeeze out a bit of easily obtained performance. There are lots of ways to try to squeeze out performance depending on how exactly you're using this stuff, but they're all complicated. This whole thing is kind of nasty I fully realize. I would think there would be a better way, but my search online was as unproductive as yours.
    – user12861
    Aug 16 '11 at 13:17










  • That's really helpful, user12861; thank you. Hopefully, I'll get a chance to give this a try sometime soon.
    – Owen Blacker
    Aug 25 '11 at 10:48















1














I have a similar problem. I want to know how big the text I'm drawing is going to be, and where it's going to appear, EXACTLY. I haven't had the line-break problem, so I don't think I can help you there. I had the same problems you had with all the various measuring techniques available, including ending up with MeasureCharacterRanges, which worked okay for the left and right, but not at all for the height and top. (Playing with the baseline can work well for some rare applications though.)



I've ended up with a very inelegant, inefficient, but working solution, at least for my use case. I draw the text on a bitmap, check the bits to see where they ended up, and that's my range. Since I'm mostly drawing small fonts and short strings, it's been fast enough for me (especially with the memoization I added). Maybe this won't be exactly what you need, but maybe it can lead you down the right track anyway.



Note it requires compiling the project to allow unsafe code at the moment, as I'm trying to squeeze out every bit of efficiency from it, but that constraint could be removed if you wanted to. Also, it's not as thread safe as it could be right now, you could easily add that if you needed it.



Dictionary<Tuple<string, Font, Brush>, Rectangle> cachedTextBounds = new Dictionary<Tuple<string, Font, Brush>, Rectangle>();
/// <summary>
/// Determines bounds of some text by actually drawing the text to a bitmap and
/// reading the bits to see where it ended up. Bounds assume you draw at 0, 0. If
/// drawing elsewhere, you can easily offset the resulting rectangle appropriately.
/// </summary>
/// <param name="text">The text to be drawn</param>
/// <param name="font">The font to use when drawing the text</param>
/// <param name="brush">The brush to be used when drawing the text</param>
/// <returns>The bounding rectangle of the rendered text</returns>
private unsafe Rectangle RenderedTextBounds(string text, Font font, Brush brush)

// First check memoization
Tuple<string, Font, Brush> t = new Tuple<string, Font, Brush>(text, font, brush);
try
return cachedTextBounds[t];

catch(KeyNotFoundException)
// not cached


// Draw the string on a bitmap
Rectangle bounds = new Rectangle();
Size approxSize = TextRenderer.MeasureText(text, font);
using(Bitmap bitmap = new Bitmap((int)(approxSize.Width*1.5), (int)(approxSize.Height*1.5)))
using(Graphics g = Graphics.FromImage(bitmap))
g.DrawString(text, font, brush, 0, 0);
// Unsafe LockBits code takes a bit over 10% of time compared to safe GetPixel code
BitmapData bd = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
byte* row = (byte*)bd.Scan0;
// Find left, looking for first bit that has a non-zero alpha channel, so it's not clear
for(int x = 0; x < bitmap.Width; x++)
for(int y = 0; y < bitmap.Height; y++)
if(((byte*)bd.Scan0)[y*bd.Stride + 4*x + 3] != 0)
bounds.X = x;
goto foundX;

foundX:
// Right
for(int x = bitmap.Width - 1; x >= 0; x--)
for(int y = 0; y < bitmap.Height; y++)
if(((byte*)bd.Scan0)[y*bd.Stride + 4*x + 3] != 0)
bounds.Width = x - bounds.X + 1;
goto foundWidth;

foundWidth:
// Top
for(int y = 0; y < bitmap.Height; y++)
for(int x = 0; x < bitmap.Width; x++)
if(((byte*)bd.Scan0)[y*bd.Stride + 4*x + 3] != 0)
bounds.Y = y;
goto foundY;

foundY:
// Bottom
for(int y = bitmap.Height - 1; y >= 0; y--)
for(int x = 0; x < bitmap.Width; x++)
if(((byte*)bd.Scan0)[y*bd.Stride + 4*x + 3] != 0)
bounds.Height = y - bounds.Y + 1;
goto foundHeight;

foundHeight:
bitmap.UnlockBits(bd);

cachedTextBounds[t] = bounds;
return bounds;






share|improve this answer




















  • Nice work! I'll have to give that a try and see if that solves my problem. Though I'm somewhat loathe to have to rely on unsafe code. How quick do you find that code? The circumstance I'm using it in checks whether the text is too big for a fixed-size bounding box and decreases the font size within a while loop (checking while too big or size gt hard-floor-limit), so I don't want to perform an expensive operation in that while loop…
    – Owen Blacker
    Aug 16 '11 at 12:03










  • Speed depends heavily on font size. It allocates bitmap, renders it, then searches for boundaries. Smaller fonts are a lot faster, and you lose with larger fonts something like O(size^2) I think. May want to guess small size and work up to a size that's too big instead of the other way around. You could use MeasureCharacterRanges as first guess for lower bound, since it always seems to end up too small a guess. There's a lot of room for cleverness searching for the right size, binary search with clever guessing etc. I don't need any of that for my purpose, so I haven't played with it.
    – user12861
    Aug 16 '11 at 13:14










  • As to unsafe, you can do it without that but speed takes a hit. As commented this is almost 90% faster than using GetPixel, but you can still do an in between method using LockBits without unsafe code. I didn't try that as I was ok with unsafe code and wanted to squeeze out a bit of easily obtained performance. There are lots of ways to try to squeeze out performance depending on how exactly you're using this stuff, but they're all complicated. This whole thing is kind of nasty I fully realize. I would think there would be a better way, but my search online was as unproductive as yours.
    – user12861
    Aug 16 '11 at 13:17










  • That's really helpful, user12861; thank you. Hopefully, I'll get a chance to give this a try sometime soon.
    – Owen Blacker
    Aug 25 '11 at 10:48













1












1








1






I have a similar problem. I want to know how big the text I'm drawing is going to be, and where it's going to appear, EXACTLY. I haven't had the line-break problem, so I don't think I can help you there. I had the same problems you had with all the various measuring techniques available, including ending up with MeasureCharacterRanges, which worked okay for the left and right, but not at all for the height and top. (Playing with the baseline can work well for some rare applications though.)



I've ended up with a very inelegant, inefficient, but working solution, at least for my use case. I draw the text on a bitmap, check the bits to see where they ended up, and that's my range. Since I'm mostly drawing small fonts and short strings, it's been fast enough for me (especially with the memoization I added). Maybe this won't be exactly what you need, but maybe it can lead you down the right track anyway.



Note it requires compiling the project to allow unsafe code at the moment, as I'm trying to squeeze out every bit of efficiency from it, but that constraint could be removed if you wanted to. Also, it's not as thread safe as it could be right now, you could easily add that if you needed it.



Dictionary<Tuple<string, Font, Brush>, Rectangle> cachedTextBounds = new Dictionary<Tuple<string, Font, Brush>, Rectangle>();
/// <summary>
/// Determines bounds of some text by actually drawing the text to a bitmap and
/// reading the bits to see where it ended up. Bounds assume you draw at 0, 0. If
/// drawing elsewhere, you can easily offset the resulting rectangle appropriately.
/// </summary>
/// <param name="text">The text to be drawn</param>
/// <param name="font">The font to use when drawing the text</param>
/// <param name="brush">The brush to be used when drawing the text</param>
/// <returns>The bounding rectangle of the rendered text</returns>
private unsafe Rectangle RenderedTextBounds(string text, Font font, Brush brush)

// First check memoization
Tuple<string, Font, Brush> t = new Tuple<string, Font, Brush>(text, font, brush);
try
return cachedTextBounds[t];

catch(KeyNotFoundException)
// not cached


// Draw the string on a bitmap
Rectangle bounds = new Rectangle();
Size approxSize = TextRenderer.MeasureText(text, font);
using(Bitmap bitmap = new Bitmap((int)(approxSize.Width*1.5), (int)(approxSize.Height*1.5)))
using(Graphics g = Graphics.FromImage(bitmap))
g.DrawString(text, font, brush, 0, 0);
// Unsafe LockBits code takes a bit over 10% of time compared to safe GetPixel code
BitmapData bd = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
byte* row = (byte*)bd.Scan0;
// Find left, looking for first bit that has a non-zero alpha channel, so it's not clear
for(int x = 0; x < bitmap.Width; x++)
for(int y = 0; y < bitmap.Height; y++)
if(((byte*)bd.Scan0)[y*bd.Stride + 4*x + 3] != 0)
bounds.X = x;
goto foundX;

foundX:
// Right
for(int x = bitmap.Width - 1; x >= 0; x--)
for(int y = 0; y < bitmap.Height; y++)
if(((byte*)bd.Scan0)[y*bd.Stride + 4*x + 3] != 0)
bounds.Width = x - bounds.X + 1;
goto foundWidth;

foundWidth:
// Top
for(int y = 0; y < bitmap.Height; y++)
for(int x = 0; x < bitmap.Width; x++)
if(((byte*)bd.Scan0)[y*bd.Stride + 4*x + 3] != 0)
bounds.Y = y;
goto foundY;

foundY:
// Bottom
for(int y = bitmap.Height - 1; y >= 0; y--)
for(int x = 0; x < bitmap.Width; x++)
if(((byte*)bd.Scan0)[y*bd.Stride + 4*x + 3] != 0)
bounds.Height = y - bounds.Y + 1;
goto foundHeight;

foundHeight:
bitmap.UnlockBits(bd);

cachedTextBounds[t] = bounds;
return bounds;






share|improve this answer












I have a similar problem. I want to know how big the text I'm drawing is going to be, and where it's going to appear, EXACTLY. I haven't had the line-break problem, so I don't think I can help you there. I had the same problems you had with all the various measuring techniques available, including ending up with MeasureCharacterRanges, which worked okay for the left and right, but not at all for the height and top. (Playing with the baseline can work well for some rare applications though.)



I've ended up with a very inelegant, inefficient, but working solution, at least for my use case. I draw the text on a bitmap, check the bits to see where they ended up, and that's my range. Since I'm mostly drawing small fonts and short strings, it's been fast enough for me (especially with the memoization I added). Maybe this won't be exactly what you need, but maybe it can lead you down the right track anyway.



Note it requires compiling the project to allow unsafe code at the moment, as I'm trying to squeeze out every bit of efficiency from it, but that constraint could be removed if you wanted to. Also, it's not as thread safe as it could be right now, you could easily add that if you needed it.



Dictionary<Tuple<string, Font, Brush>, Rectangle> cachedTextBounds = new Dictionary<Tuple<string, Font, Brush>, Rectangle>();
/// <summary>
/// Determines bounds of some text by actually drawing the text to a bitmap and
/// reading the bits to see where it ended up. Bounds assume you draw at 0, 0. If
/// drawing elsewhere, you can easily offset the resulting rectangle appropriately.
/// </summary>
/// <param name="text">The text to be drawn</param>
/// <param name="font">The font to use when drawing the text</param>
/// <param name="brush">The brush to be used when drawing the text</param>
/// <returns>The bounding rectangle of the rendered text</returns>
private unsafe Rectangle RenderedTextBounds(string text, Font font, Brush brush)

// First check memoization
Tuple<string, Font, Brush> t = new Tuple<string, Font, Brush>(text, font, brush);
try
return cachedTextBounds[t];

catch(KeyNotFoundException)
// not cached


// Draw the string on a bitmap
Rectangle bounds = new Rectangle();
Size approxSize = TextRenderer.MeasureText(text, font);
using(Bitmap bitmap = new Bitmap((int)(approxSize.Width*1.5), (int)(approxSize.Height*1.5)))
using(Graphics g = Graphics.FromImage(bitmap))
g.DrawString(text, font, brush, 0, 0);
// Unsafe LockBits code takes a bit over 10% of time compared to safe GetPixel code
BitmapData bd = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
byte* row = (byte*)bd.Scan0;
// Find left, looking for first bit that has a non-zero alpha channel, so it's not clear
for(int x = 0; x < bitmap.Width; x++)
for(int y = 0; y < bitmap.Height; y++)
if(((byte*)bd.Scan0)[y*bd.Stride + 4*x + 3] != 0)
bounds.X = x;
goto foundX;

foundX:
// Right
for(int x = bitmap.Width - 1; x >= 0; x--)
for(int y = 0; y < bitmap.Height; y++)
if(((byte*)bd.Scan0)[y*bd.Stride + 4*x + 3] != 0)
bounds.Width = x - bounds.X + 1;
goto foundWidth;

foundWidth:
// Top
for(int y = 0; y < bitmap.Height; y++)
for(int x = 0; x < bitmap.Width; x++)
if(((byte*)bd.Scan0)[y*bd.Stride + 4*x + 3] != 0)
bounds.Y = y;
goto foundY;

foundY:
// Bottom
for(int y = bitmap.Height - 1; y >= 0; y--)
for(int x = 0; x < bitmap.Width; x++)
if(((byte*)bd.Scan0)[y*bd.Stride + 4*x + 3] != 0)
bounds.Height = y - bounds.Y + 1;
goto foundHeight;

foundHeight:
bitmap.UnlockBits(bd);

cachedTextBounds[t] = bounds;
return bounds;







share|improve this answer












share|improve this answer



share|improve this answer










answered Aug 15 '11 at 15:45









user12861

1,36931131




1,36931131











  • Nice work! I'll have to give that a try and see if that solves my problem. Though I'm somewhat loathe to have to rely on unsafe code. How quick do you find that code? The circumstance I'm using it in checks whether the text is too big for a fixed-size bounding box and decreases the font size within a while loop (checking while too big or size gt hard-floor-limit), so I don't want to perform an expensive operation in that while loop…
    – Owen Blacker
    Aug 16 '11 at 12:03










  • Speed depends heavily on font size. It allocates bitmap, renders it, then searches for boundaries. Smaller fonts are a lot faster, and you lose with larger fonts something like O(size^2) I think. May want to guess small size and work up to a size that's too big instead of the other way around. You could use MeasureCharacterRanges as first guess for lower bound, since it always seems to end up too small a guess. There's a lot of room for cleverness searching for the right size, binary search with clever guessing etc. I don't need any of that for my purpose, so I haven't played with it.
    – user12861
    Aug 16 '11 at 13:14










  • As to unsafe, you can do it without that but speed takes a hit. As commented this is almost 90% faster than using GetPixel, but you can still do an in between method using LockBits without unsafe code. I didn't try that as I was ok with unsafe code and wanted to squeeze out a bit of easily obtained performance. There are lots of ways to try to squeeze out performance depending on how exactly you're using this stuff, but they're all complicated. This whole thing is kind of nasty I fully realize. I would think there would be a better way, but my search online was as unproductive as yours.
    – user12861
    Aug 16 '11 at 13:17










  • That's really helpful, user12861; thank you. Hopefully, I'll get a chance to give this a try sometime soon.
    – Owen Blacker
    Aug 25 '11 at 10:48
















  • Nice work! I'll have to give that a try and see if that solves my problem. Though I'm somewhat loathe to have to rely on unsafe code. How quick do you find that code? The circumstance I'm using it in checks whether the text is too big for a fixed-size bounding box and decreases the font size within a while loop (checking while too big or size gt hard-floor-limit), so I don't want to perform an expensive operation in that while loop…
    – Owen Blacker
    Aug 16 '11 at 12:03










  • Speed depends heavily on font size. It allocates bitmap, renders it, then searches for boundaries. Smaller fonts are a lot faster, and you lose with larger fonts something like O(size^2) I think. May want to guess small size and work up to a size that's too big instead of the other way around. You could use MeasureCharacterRanges as first guess for lower bound, since it always seems to end up too small a guess. There's a lot of room for cleverness searching for the right size, binary search with clever guessing etc. I don't need any of that for my purpose, so I haven't played with it.
    – user12861
    Aug 16 '11 at 13:14










  • As to unsafe, you can do it without that but speed takes a hit. As commented this is almost 90% faster than using GetPixel, but you can still do an in between method using LockBits without unsafe code. I didn't try that as I was ok with unsafe code and wanted to squeeze out a bit of easily obtained performance. There are lots of ways to try to squeeze out performance depending on how exactly you're using this stuff, but they're all complicated. This whole thing is kind of nasty I fully realize. I would think there would be a better way, but my search online was as unproductive as yours.
    – user12861
    Aug 16 '11 at 13:17










  • That's really helpful, user12861; thank you. Hopefully, I'll get a chance to give this a try sometime soon.
    – Owen Blacker
    Aug 25 '11 at 10:48















Nice work! I'll have to give that a try and see if that solves my problem. Though I'm somewhat loathe to have to rely on unsafe code. How quick do you find that code? The circumstance I'm using it in checks whether the text is too big for a fixed-size bounding box and decreases the font size within a while loop (checking while too big or size gt hard-floor-limit), so I don't want to perform an expensive operation in that while loop…
– Owen Blacker
Aug 16 '11 at 12:03




Nice work! I'll have to give that a try and see if that solves my problem. Though I'm somewhat loathe to have to rely on unsafe code. How quick do you find that code? The circumstance I'm using it in checks whether the text is too big for a fixed-size bounding box and decreases the font size within a while loop (checking while too big or size gt hard-floor-limit), so I don't want to perform an expensive operation in that while loop…
– Owen Blacker
Aug 16 '11 at 12:03












Speed depends heavily on font size. It allocates bitmap, renders it, then searches for boundaries. Smaller fonts are a lot faster, and you lose with larger fonts something like O(size^2) I think. May want to guess small size and work up to a size that's too big instead of the other way around. You could use MeasureCharacterRanges as first guess for lower bound, since it always seems to end up too small a guess. There's a lot of room for cleverness searching for the right size, binary search with clever guessing etc. I don't need any of that for my purpose, so I haven't played with it.
– user12861
Aug 16 '11 at 13:14




Speed depends heavily on font size. It allocates bitmap, renders it, then searches for boundaries. Smaller fonts are a lot faster, and you lose with larger fonts something like O(size^2) I think. May want to guess small size and work up to a size that's too big instead of the other way around. You could use MeasureCharacterRanges as first guess for lower bound, since it always seems to end up too small a guess. There's a lot of room for cleverness searching for the right size, binary search with clever guessing etc. I don't need any of that for my purpose, so I haven't played with it.
– user12861
Aug 16 '11 at 13:14












As to unsafe, you can do it without that but speed takes a hit. As commented this is almost 90% faster than using GetPixel, but you can still do an in between method using LockBits without unsafe code. I didn't try that as I was ok with unsafe code and wanted to squeeze out a bit of easily obtained performance. There are lots of ways to try to squeeze out performance depending on how exactly you're using this stuff, but they're all complicated. This whole thing is kind of nasty I fully realize. I would think there would be a better way, but my search online was as unproductive as yours.
– user12861
Aug 16 '11 at 13:17




As to unsafe, you can do it without that but speed takes a hit. As commented this is almost 90% faster than using GetPixel, but you can still do an in between method using LockBits without unsafe code. I didn't try that as I was ok with unsafe code and wanted to squeeze out a bit of easily obtained performance. There are lots of ways to try to squeeze out performance depending on how exactly you're using this stuff, but they're all complicated. This whole thing is kind of nasty I fully realize. I would think there would be a better way, but my search online was as unproductive as yours.
– user12861
Aug 16 '11 at 13:17












That's really helpful, user12861; thank you. Hopefully, I'll get a chance to give this a try sometime soon.
– Owen Blacker
Aug 25 '11 at 10:48




That's really helpful, user12861; thank you. Hopefully, I'll get a chance to give this a try sometime soon.
– Owen Blacker
Aug 25 '11 at 10:48













0














Could you try removing the following line?



fmt.FormatFlags = StringFormatFlags.NoClip;



Overhanging parts of glyphs, and
unwrapped text reaching outside the
formatting rectangle are allowed to
show. By default all text and glyph
parts reaching outside the formatting
rectangle are clipped.




That's the best I can come up with for this :(






share|improve this answer




















  • Thanks for posting an answer. I did look at this, having thought it might be the problem, but the reported height is almost double the actual height, so I don'think it can be that. It seems that the difference StringFormatFlags.NoClip makes is that if the bowl of a letter P (for example) just pokes outside the bounding box then it's allowed to render, rather than being clipped. That doesn't seem to be the problem I'm experiencing. But thanks :o)
    – Owen Blacker
    Apr 29 '10 at 8:51















0














Could you try removing the following line?



fmt.FormatFlags = StringFormatFlags.NoClip;



Overhanging parts of glyphs, and
unwrapped text reaching outside the
formatting rectangle are allowed to
show. By default all text and glyph
parts reaching outside the formatting
rectangle are clipped.




That's the best I can come up with for this :(






share|improve this answer




















  • Thanks for posting an answer. I did look at this, having thought it might be the problem, but the reported height is almost double the actual height, so I don'think it can be that. It seems that the difference StringFormatFlags.NoClip makes is that if the bowl of a letter P (for example) just pokes outside the bounding box then it's allowed to render, rather than being clipped. That doesn't seem to be the problem I'm experiencing. But thanks :o)
    – Owen Blacker
    Apr 29 '10 at 8:51













0












0








0






Could you try removing the following line?



fmt.FormatFlags = StringFormatFlags.NoClip;



Overhanging parts of glyphs, and
unwrapped text reaching outside the
formatting rectangle are allowed to
show. By default all text and glyph
parts reaching outside the formatting
rectangle are clipped.




That's the best I can come up with for this :(






share|improve this answer












Could you try removing the following line?



fmt.FormatFlags = StringFormatFlags.NoClip;



Overhanging parts of glyphs, and
unwrapped text reaching outside the
formatting rectangle are allowed to
show. By default all text and glyph
parts reaching outside the formatting
rectangle are clipped.




That's the best I can come up with for this :(







share|improve this answer












share|improve this answer



share|improve this answer










answered Apr 29 '10 at 8:22









Codesleuth

8,38364363




8,38364363











  • Thanks for posting an answer. I did look at this, having thought it might be the problem, but the reported height is almost double the actual height, so I don'think it can be that. It seems that the difference StringFormatFlags.NoClip makes is that if the bowl of a letter P (for example) just pokes outside the bounding box then it's allowed to render, rather than being clipped. That doesn't seem to be the problem I'm experiencing. But thanks :o)
    – Owen Blacker
    Apr 29 '10 at 8:51
















  • Thanks for posting an answer. I did look at this, having thought it might be the problem, but the reported height is almost double the actual height, so I don'think it can be that. It seems that the difference StringFormatFlags.NoClip makes is that if the bowl of a letter P (for example) just pokes outside the bounding box then it's allowed to render, rather than being clipped. That doesn't seem to be the problem I'm experiencing. But thanks :o)
    – Owen Blacker
    Apr 29 '10 at 8:51















Thanks for posting an answer. I did look at this, having thought it might be the problem, but the reported height is almost double the actual height, so I don'think it can be that. It seems that the difference StringFormatFlags.NoClip makes is that if the bowl of a letter P (for example) just pokes outside the bounding box then it's allowed to render, rather than being clipped. That doesn't seem to be the problem I'm experiencing. But thanks :o)
– Owen Blacker
Apr 29 '10 at 8:51




Thanks for posting an answer. I did look at this, having thought it might be the problem, but the reported height is almost double the actual height, so I don'think it can be that. It seems that the difference StringFormatFlags.NoClip makes is that if the bowl of a letter P (for example) just pokes outside the bounding box then it's allowed to render, rather than being clipped. That doesn't seem to be the problem I'm experiencing. But thanks :o)
– Owen Blacker
Apr 29 '10 at 8:51











0














I also had some problems with the MeasureCharacterRanges method. It was giving me inconsistent sizes for the same string and even the same Graphics object. Then I discovered that it depends on the value of the layoutRect parametr - I can't see why, in my opinion it's a bug in the .NET code.



For example if layoutRect was completely empty (all values set to zero), I got correct values for the string "a" - the size was Width=8.898438, Height=18.10938 using 12pt Ms Sans Serif font.



However, when I set the value of the 'X' property of the rectangle to a non-integer number (like 1.2), it gave me Width=9, Height=19.



So I really think there is a bug when you use a layout rectangle with non-integer X coordinate.






share|improve this answer




















  • Interesting — looks like it's always rounding your size up. Unfortunately, my layoutRect is always integral — it's defined as private static readonly RectangleF __textBoundingBox = new RectangleF(150, 110, 212, 64); and its value never changes (obviously, as it's marked readonly). It's definitely a bug in the .Net code, but it doesn't look like your bug and my bug are the same one.
    – Owen Blacker
    Aug 9 '10 at 11:31















0














I also had some problems with the MeasureCharacterRanges method. It was giving me inconsistent sizes for the same string and even the same Graphics object. Then I discovered that it depends on the value of the layoutRect parametr - I can't see why, in my opinion it's a bug in the .NET code.



For example if layoutRect was completely empty (all values set to zero), I got correct values for the string "a" - the size was Width=8.898438, Height=18.10938 using 12pt Ms Sans Serif font.



However, when I set the value of the 'X' property of the rectangle to a non-integer number (like 1.2), it gave me Width=9, Height=19.



So I really think there is a bug when you use a layout rectangle with non-integer X coordinate.






share|improve this answer




















  • Interesting — looks like it's always rounding your size up. Unfortunately, my layoutRect is always integral — it's defined as private static readonly RectangleF __textBoundingBox = new RectangleF(150, 110, 212, 64); and its value never changes (obviously, as it's marked readonly). It's definitely a bug in the .Net code, but it doesn't look like your bug and my bug are the same one.
    – Owen Blacker
    Aug 9 '10 at 11:31













0












0








0






I also had some problems with the MeasureCharacterRanges method. It was giving me inconsistent sizes for the same string and even the same Graphics object. Then I discovered that it depends on the value of the layoutRect parametr - I can't see why, in my opinion it's a bug in the .NET code.



For example if layoutRect was completely empty (all values set to zero), I got correct values for the string "a" - the size was Width=8.898438, Height=18.10938 using 12pt Ms Sans Serif font.



However, when I set the value of the 'X' property of the rectangle to a non-integer number (like 1.2), it gave me Width=9, Height=19.



So I really think there is a bug when you use a layout rectangle with non-integer X coordinate.






share|improve this answer












I also had some problems with the MeasureCharacterRanges method. It was giving me inconsistent sizes for the same string and even the same Graphics object. Then I discovered that it depends on the value of the layoutRect parametr - I can't see why, in my opinion it's a bug in the .NET code.



For example if layoutRect was completely empty (all values set to zero), I got correct values for the string "a" - the size was Width=8.898438, Height=18.10938 using 12pt Ms Sans Serif font.



However, when I set the value of the 'X' property of the rectangle to a non-integer number (like 1.2), it gave me Width=9, Height=19.



So I really think there is a bug when you use a layout rectangle with non-integer X coordinate.







share|improve this answer












share|improve this answer



share|improve this answer










answered Aug 9 '10 at 9:19









Pavel

1




1











  • Interesting — looks like it's always rounding your size up. Unfortunately, my layoutRect is always integral — it's defined as private static readonly RectangleF __textBoundingBox = new RectangleF(150, 110, 212, 64); and its value never changes (obviously, as it's marked readonly). It's definitely a bug in the .Net code, but it doesn't look like your bug and my bug are the same one.
    – Owen Blacker
    Aug 9 '10 at 11:31
















  • Interesting — looks like it's always rounding your size up. Unfortunately, my layoutRect is always integral — it's defined as private static readonly RectangleF __textBoundingBox = new RectangleF(150, 110, 212, 64); and its value never changes (obviously, as it's marked readonly). It's definitely a bug in the .Net code, but it doesn't look like your bug and my bug are the same one.
    – Owen Blacker
    Aug 9 '10 at 11:31















Interesting — looks like it's always rounding your size up. Unfortunately, my layoutRect is always integral — it's defined as private static readonly RectangleF __textBoundingBox = new RectangleF(150, 110, 212, 64); and its value never changes (obviously, as it's marked readonly). It's definitely a bug in the .Net code, but it doesn't look like your bug and my bug are the same one.
– Owen Blacker
Aug 9 '10 at 11:31




Interesting — looks like it's always rounding your size up. Unfortunately, my layoutRect is always integral — it's defined as private static readonly RectangleF __textBoundingBox = new RectangleF(150, 110, 212, 64); and its value never changes (obviously, as it's marked readonly). It's definitely a bug in the .Net code, but it doesn't look like your bug and my bug are the same one.
– Owen Blacker
Aug 9 '10 at 11:31











0














Ok so 4 years late but this question EXACTLY matched my symptoms and I've actually worked out the cause.



There is most certainly a bug in MeasureString AND MeasureCharacterRanges.



The simple answer is:
Make sure you divide your width restriction (int width in MeasureString or the Size.Width property of the boundingRect in MeasureCharacterRanges) by 0.72. When you get your results back multiply each dimension by 0.72 to get the REAL result



int measureWidth = Convert.ToInt32((float)width/0.72);
SizeF measureSize = gfx.MeasureString(text, font, measureWidth, format);
float actualHeight = measureSize.Height * (float)0.72;


or



float measureWidth = width/0.72;
Region regions = gfx.MeasureCharacterRanges(text, font, new RectangleF(0,0,measureWidth, format);
float actualHeight = 0;
if(regions.Length>0)

actualHeight = regions[0].GetBounds(gfx).Size.Height * (float)0.72;



The explanation (that I can figure out) is that something to do with the context is triggering a conversion in the Measure methods (that doesn't trigger in the DrawString method) for inch->point (*72/100). When you pass in the ACTUAL width limitation it is adjusting this value so the MEASURED width limitation is, in effect, shorter than it should be. Your text then wraps earlier than it is supposed to and so you get a longer height result than expected. Unfortunately the conversion applies to the actual height result as well so it's a good idea to 'unconvert' that value too.






share|improve this answer




















  • On a REALLY annoying side note, I have absolutely NO idea why MeasureString takes an int for width. If you set your graphics context unit of measurement to inches then (due to the int) you can only set your width limitation to 1 inch, or 2 inches, etc. Completely ridiculous!
    – Red Nightingale
    Nov 16 '14 at 1:07















0














Ok so 4 years late but this question EXACTLY matched my symptoms and I've actually worked out the cause.



There is most certainly a bug in MeasureString AND MeasureCharacterRanges.



The simple answer is:
Make sure you divide your width restriction (int width in MeasureString or the Size.Width property of the boundingRect in MeasureCharacterRanges) by 0.72. When you get your results back multiply each dimension by 0.72 to get the REAL result



int measureWidth = Convert.ToInt32((float)width/0.72);
SizeF measureSize = gfx.MeasureString(text, font, measureWidth, format);
float actualHeight = measureSize.Height * (float)0.72;


or



float measureWidth = width/0.72;
Region regions = gfx.MeasureCharacterRanges(text, font, new RectangleF(0,0,measureWidth, format);
float actualHeight = 0;
if(regions.Length>0)

actualHeight = regions[0].GetBounds(gfx).Size.Height * (float)0.72;



The explanation (that I can figure out) is that something to do with the context is triggering a conversion in the Measure methods (that doesn't trigger in the DrawString method) for inch->point (*72/100). When you pass in the ACTUAL width limitation it is adjusting this value so the MEASURED width limitation is, in effect, shorter than it should be. Your text then wraps earlier than it is supposed to and so you get a longer height result than expected. Unfortunately the conversion applies to the actual height result as well so it's a good idea to 'unconvert' that value too.






share|improve this answer




















  • On a REALLY annoying side note, I have absolutely NO idea why MeasureString takes an int for width. If you set your graphics context unit of measurement to inches then (due to the int) you can only set your width limitation to 1 inch, or 2 inches, etc. Completely ridiculous!
    – Red Nightingale
    Nov 16 '14 at 1:07













0












0








0






Ok so 4 years late but this question EXACTLY matched my symptoms and I've actually worked out the cause.



There is most certainly a bug in MeasureString AND MeasureCharacterRanges.



The simple answer is:
Make sure you divide your width restriction (int width in MeasureString or the Size.Width property of the boundingRect in MeasureCharacterRanges) by 0.72. When you get your results back multiply each dimension by 0.72 to get the REAL result



int measureWidth = Convert.ToInt32((float)width/0.72);
SizeF measureSize = gfx.MeasureString(text, font, measureWidth, format);
float actualHeight = measureSize.Height * (float)0.72;


or



float measureWidth = width/0.72;
Region regions = gfx.MeasureCharacterRanges(text, font, new RectangleF(0,0,measureWidth, format);
float actualHeight = 0;
if(regions.Length>0)

actualHeight = regions[0].GetBounds(gfx).Size.Height * (float)0.72;



The explanation (that I can figure out) is that something to do with the context is triggering a conversion in the Measure methods (that doesn't trigger in the DrawString method) for inch->point (*72/100). When you pass in the ACTUAL width limitation it is adjusting this value so the MEASURED width limitation is, in effect, shorter than it should be. Your text then wraps earlier than it is supposed to and so you get a longer height result than expected. Unfortunately the conversion applies to the actual height result as well so it's a good idea to 'unconvert' that value too.






share|improve this answer












Ok so 4 years late but this question EXACTLY matched my symptoms and I've actually worked out the cause.



There is most certainly a bug in MeasureString AND MeasureCharacterRanges.



The simple answer is:
Make sure you divide your width restriction (int width in MeasureString or the Size.Width property of the boundingRect in MeasureCharacterRanges) by 0.72. When you get your results back multiply each dimension by 0.72 to get the REAL result



int measureWidth = Convert.ToInt32((float)width/0.72);
SizeF measureSize = gfx.MeasureString(text, font, measureWidth, format);
float actualHeight = measureSize.Height * (float)0.72;


or



float measureWidth = width/0.72;
Region regions = gfx.MeasureCharacterRanges(text, font, new RectangleF(0,0,measureWidth, format);
float actualHeight = 0;
if(regions.Length>0)

actualHeight = regions[0].GetBounds(gfx).Size.Height * (float)0.72;



The explanation (that I can figure out) is that something to do with the context is triggering a conversion in the Measure methods (that doesn't trigger in the DrawString method) for inch->point (*72/100). When you pass in the ACTUAL width limitation it is adjusting this value so the MEASURED width limitation is, in effect, shorter than it should be. Your text then wraps earlier than it is supposed to and so you get a longer height result than expected. Unfortunately the conversion applies to the actual height result as well so it's a good idea to 'unconvert' that value too.







share|improve this answer












share|improve this answer



share|improve this answer










answered Nov 16 '14 at 1:06









Red Nightingale

471217




471217











  • On a REALLY annoying side note, I have absolutely NO idea why MeasureString takes an int for width. If you set your graphics context unit of measurement to inches then (due to the int) you can only set your width limitation to 1 inch, or 2 inches, etc. Completely ridiculous!
    – Red Nightingale
    Nov 16 '14 at 1:07
















  • On a REALLY annoying side note, I have absolutely NO idea why MeasureString takes an int for width. If you set your graphics context unit of measurement to inches then (due to the int) you can only set your width limitation to 1 inch, or 2 inches, etc. Completely ridiculous!
    – Red Nightingale
    Nov 16 '14 at 1:07















On a REALLY annoying side note, I have absolutely NO idea why MeasureString takes an int for width. If you set your graphics context unit of measurement to inches then (due to the int) you can only set your width limitation to 1 inch, or 2 inches, etc. Completely ridiculous!
– Red Nightingale
Nov 16 '14 at 1:07




On a REALLY annoying side note, I have absolutely NO idea why MeasureString takes an int for width. If you set your graphics context unit of measurement to inches then (due to the int) you can only set your width limitation to 1 inch, or 2 inches, etc. Completely ridiculous!
– Red Nightingale
Nov 16 '14 at 1:07











0














To convert from points to dpi as in screen resolution you need to divide by 72 and multiply by DPI, for example:
graphics.DpiY * text.Width / 72



Red Nightengale was really close, because graphics.DpiY is usually 96 for screen resolutions.






share|improve this answer




















  • That's not an answer to the question as a whole, though. Could you expand this into an answer — including any of Red Nightingale's answer that you need to duplicate? Alternatively, post this as a comment on Red Nightingale's answer? Thanks!
    – Owen Blacker
    Nov 12 at 7:25















0














To convert from points to dpi as in screen resolution you need to divide by 72 and multiply by DPI, for example:
graphics.DpiY * text.Width / 72



Red Nightengale was really close, because graphics.DpiY is usually 96 for screen resolutions.






share|improve this answer




















  • That's not an answer to the question as a whole, though. Could you expand this into an answer — including any of Red Nightingale's answer that you need to duplicate? Alternatively, post this as a comment on Red Nightingale's answer? Thanks!
    – Owen Blacker
    Nov 12 at 7:25













0












0








0






To convert from points to dpi as in screen resolution you need to divide by 72 and multiply by DPI, for example:
graphics.DpiY * text.Width / 72



Red Nightengale was really close, because graphics.DpiY is usually 96 for screen resolutions.






share|improve this answer












To convert from points to dpi as in screen resolution you need to divide by 72 and multiply by DPI, for example:
graphics.DpiY * text.Width / 72



Red Nightengale was really close, because graphics.DpiY is usually 96 for screen resolutions.







share|improve this answer












share|improve this answer



share|improve this answer










answered Nov 10 at 22:38









JBStCyr

13




13











  • That's not an answer to the question as a whole, though. Could you expand this into an answer — including any of Red Nightingale's answer that you need to duplicate? Alternatively, post this as a comment on Red Nightingale's answer? Thanks!
    – Owen Blacker
    Nov 12 at 7:25
















  • That's not an answer to the question as a whole, though. Could you expand this into an answer — including any of Red Nightingale's answer that you need to duplicate? Alternatively, post this as a comment on Red Nightingale's answer? Thanks!
    – Owen Blacker
    Nov 12 at 7:25















That's not an answer to the question as a whole, though. Could you expand this into an answer — including any of Red Nightingale's answer that you need to duplicate? Alternatively, post this as a comment on Red Nightingale's answer? Thanks!
– Owen Blacker
Nov 12 at 7:25




That's not an answer to the question as a whole, though. Could you expand this into an answer — including any of Red Nightingale's answer that you need to duplicate? Alternatively, post this as a comment on Red Nightingale's answer? Thanks!
– Owen Blacker
Nov 12 at 7:25

















draft saved

draft discarded
















































Thanks for contributing an answer to Stack Overflow!


  • Please be sure to answer the question. Provide details and share your research!

But avoid …


  • Asking for help, clarification, or responding to other answers.

  • Making statements based on opinion; back them up with references or personal experience.

To learn more, see our tips on writing great answers.





Some of your past answers have not been well-received, and you're in danger of being blocked from answering.


Please pay close attention to the following guidance:


  • Please be sure to answer the question. Provide details and share your research!

But avoid …


  • Asking for help, clarification, or responding to other answers.

  • Making statements based on opinion; back them up with references or personal experience.

To learn more, see our tips on writing great answers.




draft saved


draft discarded














StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f2684894%2fgraphics-measurecharacterranges-giving-wrong-size-calculations%23new-answer', 'question_page');

);

Post as a guest















Required, but never shown





















































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown

































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown







Popular posts from this blog

Top Tejano songwriter Luis Silva dead of heart attack at 64

ReactJS Fetched API data displays live - need Data displayed static

Evgeni Malkin