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.
Related
I need to take a spreadsheet and convert it into a price tag. I've done that part but I'm not to sure how to go about making an image that contains both the price an item name (This is all stored in a list.) Then lay it out on a 8 1/2 x 11 piece of paper.
I read this question here but its using the size of the text, which may vary depending on the name of the item. The TextBox (Or whatever is holding the text) needs to be the same size but have the text scale based on its size.
Take a look at these docs, in particular their example pd_PrintPage function. This takes a PrintPageEventArgs which contains a Graphics object that you can use to actually render your tag.
In particular, to leverage your linked question, there is a DrawImage(Image, Int32, Int32) method that renders the given image at a co-ordinate.
To handle scaling your text, you just need to compare how big your text would be with one font to how big you want it - work out the ratio of width/height, then scale the font you use to render so that it uses the smallest of those ratios. There's a good answer here which shows how to do that.
So:
Handle a print event
Find the right font size
Create your image
Print with your graphics object
I would do a mockup of the resulting code, but I don't have access to a C# IDE at the moment.
The default size in pixels for a tab in a RichTextBox is apparently 48 pixels, regardless of font or font size. This is set as default by .NET without me touching the SelectionTabs array. I've checked in the RTF - there's no \tx control code or anything so where the heck is this elusive '48' number stored?
I don't want to use this as a hardcoded 'magic number' in case other systems use something other than 48 pixels for a tab.
My own purpose is to help me convert from tabs to spaces (at least for fixed width fonts). But finding an answer to this also might get us closer to controlling the tab size with a single value without setting up an 'infinite' array of tab stops as implied from this answer.
I have a pretty quick (and I'm hoping basic) question. I'm modifying some C# code for my company's website. The code draws a table for me in fixed columns, the data for which is pulled from a database. The height of the each column of the table is fixed (currently), and I need to change it so if the string is a certain length, and therefore wraps, the second line of text is viewable (instead of hidden by the next row).
From my research, it seems like I can use MeasureString (since I know the font and string) to see if the string is longer/wider than my set table column, and change the height of the row if this is so. However, I'm very new to C# programming (and haven't done much programming overall in years, besides web stuff), so I'm not sure how to get all of this implemented. I have the logic in place, and I know how to change the height, I just need to know how to get an actual number I can use logic against using the MeasureString method (and how to instantiate any variables and functions I might need to use that method).
I believe you need to use this overload for MeasureString(string,font,int):
The width parameter specifies the maximum value of the width component
of the returned SizeF structure (Width). If the width parameter is
less than the actual width of the string, the returned Width component
is truncated to a value representing the maximum number of characters
that will fit within the specified width. To accommodate the entire
string, the returned Height component is adjusted to a value that
allows displaying the string with character wrap.
-- From Above Linked MSDN Page (Emphasis mine)
// Measure string (you'll need to instansiate your own graphics object,
// since you wont have PaintEventArgs)
SizeF stringSize = new SizeF();
stringSize = e.Graphics.MeasureString(measureString, stringFont, stringWidth);
int cellHeight = stringSize.Height;
You can either use e.Graphics.MeasureString() or TextRenderer.MeasureText()
Differences and advantages of each of them are describe here:
TextRenderer.MeasureText and Graphics.MeasureString mismatch in size
There you will also find usage examples, which I would skip here to avoid duplication.
MSDN gives an example where you calculate this by registering an event handler to the OnPaint method of your control (in instantiated controls), or by overriding the OnPaint method (in inherited controls), or by overriding the OnPaint method of your form (not best practice since you probably don't want to do this for EVERY form repaint). The OnPaint method will give you access to a graphics object so you can call the MeasureString method.
Consider the following:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
label1.Paint += new PaintEventHandler(label1_Paint);
}
void label1_Paint(object sender, PaintEventArgs e)
{
SizeF size = e.Graphics.MeasureString(label1.Text, label1.Font);
this.label1.Width = (int)size.Width;
this.label1.Height = (int)size.Height;
}
}
I have strings formatted using the code below
String.Format("{0,-10} {1,10}", "Kills:", kills);
String.Format("{0,-10} {1,10}", "Points:", points);
String.Format("{0,-10} {1,10}", "$:", currency);
From what I understood, the first part of the strings should be left justified with a 10 space buffer and then the integer variables should be printed right justified with a 10 space buffer.
However when attempt to draw the strings using SpriteBatch.DrawString, nothing aligns properly.
The left aligned side prints properly, but the right aligned side centres on a certain point, for example if kills = 50 and points = 5002, the 50 will be centered over the 00 in the 5002...
What is going on?
Quite simply, I suspect you're not using a monospaced font. When different characters have different widths, you can't use spaces to line things up. (Your sample works when using Console.WriteLine for example, as the console has a fixed width font by default.)
You'll either have to use a monospaced font, or you'll have to draw the strings separately - draw each string to fit the relevant area. I don't know anything about XNA, but I'd expect you to either have to measure the width of the string before you draw it (so you can subtract it from the right-hand edge, for example) or specify some sort of layout value which indicates "right-align this string with a particular point".
Most likely you draw the text with a proportional font. Bear in mind that characters don't have the same width, so you cannot align texts with spaces.
As I do not have reply privileges, (or some such thing, as there is no reply button for answers), but I would like to contribute, I will post this answer.
Jon's answer mentioned measuring the string, this is possible by spriteFont.MeasureString(string s);. This returns a Vector2, the X portion of which is the width of the rendered text. (Y is height, which could be helpful for other things) This allows you to use a font other than a monospace font.
Here is an example of using MeasureString:
I'm not really sure what the question is asking, but if you wanted a single line of text similar to "Kills:50 Points:5002" but width two different spritebatch calls you could do the following (note I typed this directly into stackoverflow, so there may be minor syntax errors):
float killStringWidth = spriteFont.MeasureString(killString).X;
spriteBatch.DrawString(spriteFont, killString, new Vector2(0,0), Color.White );
spriteBatch.DrawString(spriteFont, pointString, new Vector2(killStringWidth + 10, 0), Color.White );
I have an image like below.
What I want is a monochrome image such that white parts are kept white, the rest is black. However, the tricky part is that I also want to reduce the white parts to be one pixel in thickness.
It's the second part that I'm stuck with.
My first thought was to do a simple threshold, then use a sort of "Game of Life" type iterative process where a white pixel was removed if it had neighbours on one side but not the other (i.e. it's an edge) however I have a feeling this would reduce ends of lines to nothing over time so I'd end up with a blank image.
What algorithm can I use to get the image I want, given the original image?
(My language of choice is C#, but anything is fine)
Original Image:
After detecting the morphological extended maxima of a given height:
and then thinning gives:
You can also manipulate the height parameter, or prune the thinned image.
Code in Mathematica:
img = ColorConvert[Import["http://i.stack.imgur.com/zPtl6.png"], "Grayscale"];
max = MaxDetect[img, .55]
Thinning[max]
EDIT I followed my own advice and a height of .4 gives segments which are more precisely localized:
I suggest that you look into Binary Morphological transformations such as Erosion and Dilation. Graphics libraries such as OpenCV() http://opencv.willowgarage.com/wiki/ and that statistical/matrix tool Gnu Octave http://octave.sourceforge.net/image/function/bwmorph.html support these operations.