How to draw a string without padding - c#

I am using the GraphicsPath.AddString() function, but it draws the text with a little space around the text. Any idea how to draw the string without that padding, only the paths of the text?
My code is like this:
GraphicsPath gp = new GraphicsPath();
gp.AddString(text, font.FontFamily, (int)font.Style, font.Size,
boundsRectangle, format);
g.DrawPath(pen, gp);

What is happening is the under the hood it is probably using Graphics.MeasureString(), from the documentation :
GDI+ adds a small amount (1/6 em) to each end of every string displayed. This 1/6 em allows >for glyphs with overhanging ends (such as italic 'f'), and also gives GDI+ a small amount >of leeway to help with grid fitting expansion.
The default action of DrawString will work against you in displaying adjacent runs:
Firstly the default StringFormat adds an extra 1/6 em at each end of each output;
Secondly, when grid fitted widths are less than designed, the string is allowed to contract
by up to an em.
To avoid these problems:
Always pass MeasureString and DrawString a StringFormat based on the typographic >StringFormat (GenericTypographic).
Set the Graphics TextRenderingHint to TextRenderingHintAntiAlias. This rendering method >uses anti-aliasing and sub-pixel glyph positioning to avoid the need for grid-fitting, and >is thus inherently resolution independent.
So it looks like you should be able to fix this using the correct StringFormat.

Related

C# draw a string pixel perfect

I'm trying to draw a string using either textrenderer.drawtext, graphics.drawstring or graphicspath.addstring - the main purpose is to extract all fonts to bitmaps to edit them and use them as bitmaps with shaders in a game.
With textrenderer.drawtext and graphics.drawstring, I get a padding on top of varying degrees - so I try graphicspath.addstring. I extract the font family's ascent height and descent height, but they are wildly unusable with emheight. (using ascent and descent with emheight is how microsoft suggest you do what I am trying to do - via http://msdn.microsoft.com/en-us/library/xwf9s90b%28v=vs.110%29.aspx. Has anyone successfully ever draw pixel perfect fonts using C#? Every time I ever try or look it up, textrenderer and graphics always' padding always screwed up drawing and this new graphicspath method seems to have an issue with using a specific scale.
The usual methods using TextRenderer or MeasureString will give you a SizeF, containing the bounds of the string you measure. Most formats include a little slack so you can compose text by adding strings together.
The aim of theses methods is to help create blocks of text by letting you measure when a line will be full or how many pixels to advance for the next line.
They are not really meant for maesuring single characters.
For this there is a special stringformat GenericTypographic as described here which leaves out the white space.
To get an even more precise measurement one can use GraphicsPath.AddString and then GetBounds, maybe after switching antialias off..
Now, if you wanted to draw a single character precisely, say centered on a Button this would do the job.
But you know all that and your aim is different - if I understand you correctl,y you want to create Bitmaps from each character in order to later join them to form text. This means you need them to line up correctly vertically, ie sit on the same baseline.
The sizes of the characters don't help you here; now, normally you'd need the baseline of each charcater, which you don't get, at least not for anything descending like 'f' or even just ',' etc..
But it wouldn't help you either because in GDI you don't print/draw to the baseline anyway..
What you should do, imo is either draw one long string with all characters, so that they're all lined up right and then cut out the characters one by one. Or you could draw each character on its own, but suffix all or some characters you know to have ascenders and descenders and then only pick the first columns from the result.
So the only way I figured out how to do this is is to first draw the string to a graphicpath, then measure all the empty spots in the graphic path, and get it's height only after I've measure every spot, then redraw the string (I have an attempt counter to limit attempts but increase em to pixel accuracy) taking the old size and new size into account by a modifier and then extract the final size and store it.
Only I got to get around the BS of every font having a weird top padding that isn't associated with it's ascent and internal overflow (ex: Ñ), as well as descent, in refrence to a 0,0 point, this way.

Why does DrawTextLayout ignore brushes from SetDrawingEffect in SharpDX?

My goal is to draw text in a single layout with certain ranges different sizes and opacities.
The ID2D1RenderTarget::DrawTextLayout method seems to be the way to go.
The documentation for the defaultForegroundBrush parameter:
The brush used to paint any text in textLayout that does not already
have a brush associated with it as a drawing effect (specified by the
IDWriteTextLayout::SetDrawingEffect method).
According to the Remarks section of the IDWriteTextLayout::SetDrawingEffect method,
An ID2D1Brush, such as a color or gradient brush, can be set as a
drawing effect if you are using the ID2D1RenderTarget::DrawTextLayout
to draw text and that brush will be used to draw the specified range
of text.
This drawing effect is associated with the specified range and will be
passed back to the application by way of the callback when the range
is drawn at drawing time.
It sounds like ID2D1RenderTarget::DrawTextLayout will definitely use any brush set by IDWriteTextLayout::SetDrawingEffect. This unmanaged C++ answer seems to corroborate this idea.
However, in practice, DrawTextLayout ignores any SolidColorBrush I set using SetDrawingEffect. I get styles and sizes in the appropriate ranges, but everything is painted using the default brush.
I worked around this by implementing a custom text renderer (gist) which is dead simple and drew exactly what I expected from ID2D1RenderTarget::DrawTextLayout as per the documentation. I would have been satisfied but the performance of a TextRendererBase and DrawGlyphRun are more than 25% slower than ID2D1RenderTarget::DrawTextLayout.
What might be causing this issue? Can I use color as the documentation suggests and still use ID2D1RenderTarget::DrawTextLayout?
instead of:
layout.SetDrawingEffect(myBrush, new TextRange(1, 5));
call it like this:
layout.SetDrawingEffect(myBrush.NativePointer, new TextRange(1, 5));

Draw a lot of single characters to a WinForms Control

I have a custom WinForms control which could be described as some kind of terminal control (like a control containing Putty). According to this, the control should display a lot of characters, each with a different color and background color (worst case).
Currently I'm using the (kind of obsolete) Graphics.MeasureString method to determine the size of a single character in my fixed-size font so I can calculate the position of a character at a specific row and column. Then I use Graphics.DrawString to draw the characters. To optimize the performance I create a BufferedGraphics and group characters by their properties to draw consecutive characters with the same color with just one DrawString call. (Because one DrawString call per character is really slow.)
Now the problem is that DrawString apparently calculates the width of a character slightly different from MeasureString. When I draw a complete line at once, the resulting text width is different from what I calculated using the width of a single character multiplied by the character count of the line. It's just one or two pixels (or maybe less), but you can see it clearly - especially because I'm using anti-alias so you can even see a difference of just half a pixel.
The following sample code draws a long string a on the form's graphics, followed by character 'B'. Then it draws just a 'B' on the position calculated by measuring a.
var f = new Form {
Width = 1200,
Height = 500,
Font = new Font("Consolas", 11, FontStyle.Regular)
};
f.Paint += delegate(object sender, PaintEventArgs e) {
var a = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
var size = e.Graphics.MeasureString(a, f.Font, new PointF(0, 0), StringFormat.GenericTypographic);
using (var black = new SolidBrush(Color.Black))
{
e.Graphics.DrawString(a + "B", f.Font, black, 0, 0, StringFormat.GenericTypographic);
e.Graphics.DrawString("B", f.Font, black, size.Width, 20, StringFormat.GenericTypographic);
}
};
f.Show();
And if you look closely, you will see that the second B is about one pixel more right - at least with my display settings (100% dpi scale, ClearType enabled). Although one pixel is not much, when you draw lines using unicode characters U+2500 through U+257F it looks pretty ugly if the characters aren't perfectly aligned.
Also I can't use the TextRenderer class because its MeasureString method returns integer values, but DrawString of course does not draw each character on a full pixel position (what would be required to align it with a position calculated using row/column and the measured integer character size).
So my question is: Is there any (efficient) method to draw strings which are perfectly aligned to their corresponding position calculated using the character size?
P.S.: MeasureString with a 200-character-string returns exactly 200 times the return value of MeasureString with a single-character-string.
I have had some similar issues with measuring the strings in Graphics. I have used SizeInPoints, a property from the class Font and multiplied it for the number of characters I have in the string... I dont't know if it helps.
If not it can be a "rounding" problem with the pixels... then I would try to scale up the font size (maybe 10 times), measure it and then divide it by 10 again when using the size to color the background.
I hope it helps.
Good luck! Regards!
I'm having same issue.
It seems that the behavior depends on the font size.
After changing font with following code, this issue didn't occur.
Font = new Font("Consolas", 20.0F, FontStyle.Regular, GraphicsUnit.Pixel)
However, with another font size as below this issue still occurs.
Font = new Font("Consolas", 20.3F, FontStyle.Regular, GraphicsUnit.Pixel)
My guess on background of this issue: MeasureString and DrawString uses different layout routine and they have different characteristic in rounding error of float number. Only with 'simple' font size they gives same results.
Also, this issue didn't occur with bitmap fonts.

How to create a font with precise character height in Windows Forms?

In my program I need to generate a bitmap with all digit characters (0..9) laid from left to right, plus a few other characters.
The user will select from the UI:
the desired font, and
the desired character height in pixels.
So I want to create a Bitmap, then a Graphics from this bitmap, then draw the digits one by one to this bitmap, and then save it to disk.
What I couldn't figure out in hours:
How do I create a font with the correct size so that the digit '0' has the height given by the user?
I played with all the parameters in the Font constructor, with properties of StringFormat, with MeasureString/MeasureText/MeasureCharacterRanges, I tried creating the font with the native CreateFont() via P/Invoke (with positive/negative nHeight). All parameters called "size" or "height" seem to indicate the size of some hypothetical character that's much larger than '0'.
I did read the theory with line height, em height, ascent, descent etc. There seem to be no notion for the real height of a character, without padding and spacing and so on
Again my question: Given the font name and the desired pixel height for the '0' glyph, how can I create a font which draws that glyph with the required height?
I would appreciate any guidance.
LATER EDIT
Some more details about my project: It's an embedded device with a big display, and I need to provide an easy way for the designers to to generate and try out bitmap fonts of their liking. A Windows tool that generates such bitmap fonts seemed like a good solution to me.
While I wasn't far off the mark with what I posted here earlier, it didn't actually work. In banging my head against this I found it interesting to note that graphics.MeasureString("M", ...) reports a height far larger than graphics.MeasureString("MM", ...). In calculating the font sizes by hand I've found the first(which correlates with GetEMSize's response) is actually the full line size. In the end I came to realize the actual character sizes of the digits aren't required to correlate to any of the metrics, whether real or .net.
I believe you could use this Font Constructor to specify the GraphicsUnit to be pixels. Then it should create the font with the appropriate size.
Adding test code - edit accordingly for your case and don't judge for style, I just wanted something I could paste in LINQPad and would produce an image.
using (var font = new Font("Arial",10,FontStyle.Regular, GraphicsUnit.Pixel))
using (var image = new Bitmap(30, 15))
using (var graphics = Graphics.FromImage(image))
{
graphics.FillRectangle(Brushes.White, new Rectangle(0, 0, 30, 15));
graphics.DrawString("Ay", font, Brushes.Black, 0, 0);
image.Save(#"E:\test.bmp", ImageFormat.Bmp);
}
Remember when setting size by Pixels that all characters in the Font need to fit in that range, meaning letters with a descender and letters with an ascender.

Accuracy of TextRenderer.MeasureText results

Calling TextRenderer.MeasureText as follows:
TextRenderer.MeasureText(myControl.Text, myControl.Font);
and comparing the result to the size of the control to check if text fits. The results are sometimes incorrect. Have observed the following two issues:
Often when a Label is set to AutoSize, TextRenderer will report a width that is 1 pixel wider than the auto-sized width of the Control.
False negative where TextRenderer reports a width smaller than the control's but the text is still cut off. This occurred with "Estación de trabajo" -- not sure if the accent could somehow affect the width calculation?
Is there any way to improve the accuracy of the MeasureText method? Should I be calling one of the overrides that accepts a device context and/or format flags?
I know it's probably no actual anymore. Yet for future readers here is a simple yet accurate method of measuring text in a control:
Graphics g=Graphics.FromHwnd(YOUR CONTROL HERE.Handle);
SizeF s=g.MeasureString("YOUR STRING HERE", Font, NULL, NULL, STRING LENGTH HERE, 1)
Is there any way to improve the accuracy of the MeasureText method? Should I be calling one of the overrides that accepts a device context and/or format flags?
You have answered your question by yourself. Actually MeasureText based on Win32 DrawTextEx, and this function cannot work without valid device context. So when you call MeasureText override without hdc, it internally create desktop compatible hdc to do measurement.
Of course measurement depends on additional TextFormatFlags. Also keep in mind that Label painting (and measurement) depends on UseCompatibleTextRendering.
So general conclusion you should use MeasureText for your own code, for example when you then call DrawText with exactly same parameters, in all other cases size returned by MeasureText cannot be treated as precise.
If you need to get expected Label size, you should use GetPreferredSize method.
Check out the TextFormatFlags parameter to this function:
TextRenderer::MeasureText(String, Font, Size, TextFormatFlags)
http://msdn.microsoft.com/en-us/library/8wafk2kt.aspx
"The Size, in pixels, of text drawn on a single line with the specified font. You can manipulate how the text is drawn by using one of the DrawText overloads that takes a TextFormatFlags parameter. For example, the default behavior of the TextRenderer is to add padding to the bounding rectangle of the drawn text to accommodate overhanging glyphs. If you need to draw a line of text without these extra spaces you should use the versions of DrawText and MeasureText that take a Size and TextFormatFlags parameter. For an example, see MeasureText(IDeviceContext, String, Font, Size, TextFormatFlags)."
hth
I don't know if I have a perfect solution but I ran into this when I was doing WinForms a few years back. The way I ended up compensating was by adjusting the returned measurement by a percentage. I cannot recall what I used (maybe 5% or 105?), but I do recall that I ended up using a constant percentage across the app and always rounded up.
I haven't got enough points to comment yet, so I've had to put this as an answer:
Perhaps ClearType affects measurement accuracy, because although a character has a known width calculated from its glyph, its rendering and position are adjusted to place axial lines on whole pixels.
Just a thought.

Categories

Resources