Strange behavior of graphics.MeasureString at different resolutions - c#

I have noticed strange behavior of Graphics.MeasureString at different resolutions.
For the default resolution (96x96) there is a linear relationship across the different font sizes I tested.
However, if I raise it to 512 x 512, the linear relationship disappears and something really strange happens when using measure string. (See 4 plots below)
If I leave the resolution at the default size for a graphics object, and measure the font size, here is the relationship between font size and the width of a string:
Graphics object, Default Resolution (96):
Font size (X-Axis), WIDTH of a particular string (Y-Axis)
Font size (X-Axis), HEIGHT of a particular string (Y-Axis)
However, if I change the resolution
Graphics object, 512 resolution:
Font size (X-Axis), WIDTH of a particular string (Y-Axis)
Font size (X-Axis), HEIGHT of a particular string (Y-Axis)
Anyone know why this might be happening?
Thanks you.
It should noted that I am using .NET 4 (Full Profile)
Code used to generate graphs (Change resolution for each type):
string str = "6 CN-3 Tie EomgVeo405- 2ss>era09rni IBne 20iopv Atdrsn - Ng72";
SizeF sizef = new SizeF(855, 14.000001f);
StringFormat stringFormat = new StringFormat()
{
Alignment = StringAlignment.Center,
LineAlignment = StringAlignment.Near,
Trimming = StringTrimming.None,
FormatFlags = StringFormatFlags.NoClip,
};
Bitmap b = new Bitmap(901, 401);
//b.SetResolution(512, 512);
Graphics g = Graphics.FromImage(b);
for (float x = origFont.Size; x >= 0.5; x -= 0.1f)
{
var data = g.MeasureString(str, new Font("Microsoft Sans Serif", x), sizef, stringFormat);
Console.WriteLine(x + "\t" + data.Width + "\t" + data.Height);
}

This answer is a guess, but the evidence fits it perfectly.
What you're seeing is the string being wrapped onto two lines.
Let's assume the following:
The width of the text is linearly proportional to the fontsize
The height of the text is linearly proportional to the fontsize
This fits the start of your 512-resolution graph, everything increases linearly.
At some point, the width is sharply reduced by a certain amount, and at the same time the height doubles.
This means that a word was moved onto line 2, which doubled the height (2 lines vs. 1 before), and the string got a certain amount less wide since the last word on the line is now on the start of line 2.
From there on, the width slowly increases again as the part of the string still on line 1 linearly gets wider as the font-size increases. At the same time the height increases linearly, but now at double the rate as before the break, since 2 lines now get higher, vs. 1 before.
At some point, again, the last word on line 1 breaks the maximum width and is moved down on line 2, before the word that was previously alone there, and at that point, the width is again sharply reduced by a certain amount.
If you were to continue your graph, I predict that the width will continue its current pattern. The exact amount it dips down each time is proportional to the width of the word being moved. At the same time, at some point the 2nd line will need to be broken, in which case you get a 3x height and the height will then increase at 3x the pace, and so on.

I believe this might be happening because of the hinting/grid fitting algorithms in GDI+. Font sizes in your tests are extremely small in terms of glyph legibility, and, because of that, the rendering engine tries to apply special transformations to each glyph to make it clearer. These transformations depend greatly on the target DPI.
It is also worth mentioning that major increase of the font size will lead to a more 'linear' dependency.
This article explains these mechanisms in more details with some examples.
Meanwhile, you can try to modify the Graphics.TextRenderingHint property and/or try TextRenderer instead of Graphics.DrawString if that will help you with your issue.

Related

How to obtain DPI equivalent of an adjustable column with no text in OpenXML

I would like to ask what units does column.width return when using Openxml? (IS it in EMU, metres, cm or dpi and how do i get its DPI equivalent?
From SpreadsheetOpenXmlFromScratch:
Note that the width of the digits depends on the font and the font size used. I'll be using the MeasureString() function of the Graphics class to measure pixel width.
Everything Vincent does is based on the font type and size. You will have to retrieve the font type and font size to run through the calculations.
EDIT: pp 40-50
No, it is using the font measurements, not the column text. Another excerpt:
Note that the width of the digits depends on the font and the font size used. I'll be using the MeasureString() function of the Graphics class to measure pixel width.
System.Drawing.Font drawfont = new System.Drawing.Font("Calibri", 11, FontStyle.Regular);
// I just need a Graphics object. Any reasonable bitmap size will do.
Graphics g = Graphics.FromImage(new Bitmap(256, 256));
Dim drawfont As New System.Drawing.Font("Calibri", 11, FontStyle.Regular)
' I just need a Graphics object. Any reasonable bitmap size will do.
Dim g As Graphics = Graphics.FromImage(New Bitmap(256, 256))
The font used should be the same as that used in your stylesheet. Otherwise you would be measuring for one font or font size, but using another font or font size to render your actual text in the Excel cell. I know it's obvious, but it's important to state here.
Specifically, it should be the font used in the Normal style. That word is capitalised for a reason. It refers to the cell style named Normal (together with "Good", "Bad" and "Neutral"). Get your friendly neighbourhood Excel geek to help you find the cell style option in Excel.
Some of his other methods do require text when he actually measures the width of a zero flanked by two underscores, then subtracts the width of the underscores to get a more accurate width, but this just requires the font name and style.
Now the MeasureString() function has a tiny problem. This is from the official documentation:
The MeasureString method is designed for use with individual strings and includes a small amount of extra space before and after the string to allow for overhanging glyphs.
What this means is that the following code will not exactly give you the pixel width of "0":
fWidthOfZero = (double)g.MeasureString("0", drawfont).Width;
fWidthOfZero = Convert.ToDouble(g.MeasureString("0", drawfont).Width)
Give it a try.

C# String.Format and SpriteBatch.DrawString spacing issues

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 );

How to Adjust the Text Size according to image size while Drawing String on an Image in C#

I'm using the Drawstring method of the Graphics Class to Draw a Text on an Image.The Font is Specified before drawing.
G.DrawString(mytext, font, brush, 0, 0)
The Problem arises when the same text is drawn on an image with smaller size.The Text drawn appears to be larger.I'm looking for a solution to alter the font size according to the image size so that the text don't appear larger or smaller when drawn on images of different sizes.
I'm attaching the images with different sizes with the text of same font size drawn on it.
http://i.stack.imgur.com/ZShUI.jpg
http://i.stack.imgur.com/GUfbM.jpg
I can't directly post the image because I'm not allowed.
You would get most precise scaling by drawing on separate image and then slapping that image onto original one. You'd do that as follows:
Create in-memory Bitmap with enough space
Draw text on that bitmap in your default font
Draw image containing the text onto original image by scaling it to size you need
Code:
Bitmap textBmp = new Bitmap(100, 100);
Graphics textBmpG = Graphics.FromImage(textBmp);
textBmpG .DrawString("test 1", new Font(FontFamily.GenericSansSerif, 16), Brushes.Red, new PointF(0, 0));
Graphics origImgG = Graphics.FromImage(originalImg);
origImgG.DrawImage(textBmpG, new Rectangle(50, 50, 50, 50), new Rectangle(0, 0, 100, 100), GraphicsUnit.Pixel);
Take notice of last line and Rectangle parameters. Use them to scale your text bitmap onto original image. Alternatively, you can also choose Graphics.MeasureString method to determine how wide your text would be and make attempts until you get best one you can.
Use Graphics.MeasureString() to measure how big your string would be on the image
Decrease/increase font step by step accordingly
As you requested in comment I'll give you more detailed suggestion here. Say your original image width is WI1, and width of text on it using Graphics.MeasureString is WT1. If you resize your image to width WI2, then your perfect text width would be WT2 = WT1 * WI2 / WI1. Using DrawText method you may not be able to get this exact width because when you increase font by 1 it may jump over that value. So you have to make several attempts and find best. Pick a size of font, if resulting text width is smaller (measure with MeasureString), increase it until it becomes bigger than target and you've got about closest match. Same thing goes if it's too big. Decrease font step by step.
This is quick and dirty as you see, because you have many draws, but I can't think of better solution, unless you're using monospaced fonts.
Difference between those solutions would be that in first you can get text to fit EXACT size you need, but you probably would loose some font readability due to scaling. Second solution would give good readability, but you can't get pixel perfect size of text.
In my opinion you have two ways:
Draw text on original image and then resize resulting image (so, even text included in it)
Scale font size by a factor newImageWidth/originalImageWidth.
It's not perfect because you could have some problem with text height (if new image is not just scaled but with different aspect ratio), but it's an idea

itextsharp measure chunk width / height

I am trying to do some precise alignment with iTextSharp, but I keep falling short as I can't figure out a way to get a width / height value for a chunk or paragraph. If I create a paragraph that is a certain font and size and text, then its dimensions should be known, right?
I know that the default left/right/center alignments will do the trick for me mostly, but I have scenarios where knowing the dimensions will be most useful. Any ideas?
You can get a chunk's width using GetWidthPoint() and the height of a chunk is generally the font's size unless you're using only lowercase letters. If so then you can manually measure characters using BaseFont.GetCharBBox().
Paragraphs are flowable items, however, and they depend on the context that they are written into so measuring them is harder. (Chunks don't automatically wrap but Paragraphs do.) The best way to measure a paragraph is to write it to a PdfCell and then measure the PdfCell. You don't have to actually add the PdfCell to the document. The link below explains it a little more.
http://itext-general.2136553.n4.nabble.com/Linecount-td2146114.html
Use below code for exact size
Font font = ...;
BaseFont baseFont = font.BaseFont;
float width = baseFont.GetWidthPoint(text, fontSize);
float height = baseFont.GetAscentPoint(text, fontSize) - baseFont.GetDescentPoint(text, fontSize);

WPF definition of FontSize

I know that in WPF, FontSize = 1/96 of an inch (same as 1 pixel I think). Is the FontSize dimension the height, the width, or diagonal size of a character? I would guess it's the font height, but the Microsoft documentation doesn't really indicate what it is.
Also, is there an easy way to get the height and width of a font size?
Answer:
So it looks like the FontSize is the height, and the width can only be determined (without knowing the actual character) on monospaced fonts since proportional fonts have varying widths.
They are referring to Font Size as used in Typefaces for Typography.
You can read about it here: Wikipedia: Typeface
The size of typefaces and fonts is traditionally measured in points;2 point has been defined differently at different times, but now the most popular is the Desktop Publishing point of 1⁄72 in (0.0139 in/0.35 mm). When specified in typographic sizes (points, kyus), the height of an em-square, an invisible box which is typically a bit larger than the distance from the tallest ascender to the lowest descender, is scaled to equal the specified size.[3] For example, when setting Helvetica at 12 point, the em square defined in the Helvetica font is scaled to 12 points or 1⁄6 in (0.17 in/4.3 mm). Yet no particular element of 12-point Helvetica need measure exactly 12 points.
A note...72 as stated in this Wikipedia article is what WinForms used. WPF switched to 96.
As for the second part of your question, I found this resource from an MSDN Link:
FormattedText formattedText = new FormattedText(
textBox1.Text.Substring(0, 1),
CultureInfo.GetCultureInfo("en-us"),
FlowDirection.LeftToRight,
new Typeface(textBox1.FontFamily.ToString()),
textBox1.FontSize,
Brushes.Black
);
... formattedText.WidthIncludingTrailingWhitespace;
... formattedText.Height;

Categories