Before I pass a string to Graphics.DrawString(), I'd like to know exactly where each character will be - not just it's offset and width, but its exact bound box with ascent/descent figured in so that I can do collision detection at the character level rather than using the entire line-height that Graphics.MeasureString() is giving me.
I can't seem to find any examples for this that accurately return the bounding box for each character. How can this be done in c#/GDI+ ?
Ideally, I'd like to get each of the gray Rectangles as in this image:
EDIT: This is the solution I used, building on @Sayka's example and a trick to get actual char width:
If you're drawing a string at X, Y using Font in Graphics g1, you can draw each char using these Rectangles:
Looks like this:
For me, the solution was to stop using Graphics.DrawString. I measured the characters by adding them to a GraphicsPath and getting the bounds from the GraphicsPath object. Then I draw the characters with Graphics.FillPath.
DrawString routine is optimized for drawing (more) text in a box. If you want to draw characters more precisely then DrawString is not sufficient. It adds space in front and after the text.
If you need precise character drawing, measure with GraphicsPath and drawing with FillPath is the way to go. Compared to looking up pixels as suggested in this thread and elsewhere GraphicsPath operations are blazingly fast.
If you also need Kerning then load the Kerning table from the font using DllImport on GetKerningPairs. For this, you also need the font in a Win32 friendly way. For that I suggest CreateFont.
Good luck!
Create an invisible window for measurement purposes with exactly the same size and font settings what you have in the "real" window. After that you can deduct the individual bounding boxes of the characters by blending together more data:
I agree with @Jason Williams though. "padding and fudge factors" can distort the results a little.
I don't think it's possible to accurately measure characters within a string in GDI+. Even if you forget subpixel positioning and kerning, etc, which mean that characters in the drawn string will be different sizes depending on their context in the string and position on screen and ClearType settings, MeasureString has a habit of adding padding and fudge factors so you can't easily get the desired info out of it.
One approach that sortof works is to Measure the first character, then the first two characters, then the first three characters, and so on, so that you get a rough idea where they will all end up when drawn in a single hit. Then you'll probably need to Measure each char again to determine its height. If MeasureString doesn't give the char height but gives the font height instead, you might have to resort to rendering each character to an offscreen bitmap and then scanning the pixels to determine the true height of the character.
At that point you may find it's better to p/invoke off to a lower level font API that just gives you the information you need directly from the font. But then you'll probably need to use something other than GDO+ to render it as the chances of it lining up with what you've worked out may not be good.
(can you tell that I have a trust issue with the quality off the GDI+ font renderer yet?)
If performance isn't critical, I would think you could get the most accurate results by converting the characters to paths and examining those. I'm not sure the best way to handle kerning issues, but for fonts without ligatures one could probably figure out the bounding boxes for characters in isolation, measured relative to their starting points, and then determining the starting points of each character in the kerned string. For fonts with ligatures, individual characters may not always have distinct glyphs. For example, if a font has an "ffi" ligature, the word "offing" may contain only four glyphs: "o", "ffi", "n", and "g"; it would be meaningless to ask about the bounding box of the "i".
Here's the code
. .
.
.
.
.
Measured first black point from the left, then frm right, then frm up and frm bottom.. Rate if this helps..