First of all, question How to measure width of character precisely? which is answered, doesn't really help for this case, so this isn't a duplicate of that.
I have a string. I draw using graphics.DrawString, however when I need to put another one after it, I need to know the precise width of previous string.
For this I use graphics.MeasureString with:
StringFormat format = new StringFormat(StringFormat.GenericTypographic);
format.Alignment = StringAlignment.Center;
format.Trimming = StringTrimming.None;
format.FormatFlags = StringFormatFlags.MeasureTrailingSpaces;
I have tried many other functions, just as TextRendered.MeasureText however all of them fail, with all possible combinations of parameters.
the mentioned combination of MeasureString is most close to what I need (it works in most cases, except for special characters), however using characters like # break it. The width is either shorter or longer.
Is there a way to get a precise size of text produced by DrawString function? How does the DrawString calculate the size of drawing area? It must be clearly some other function because the size always differ.
The source code of whole application is here https://gitorious.org/pidgeon/pidgeon-main/ (File where I work with this, is https://gitorious.org/pidgeon/pidgeon-main/blobs/master/scrollback/SBABox.cs)
You just need to eliminate extra width. You can do this by using string format:
GdipStringFormatGetGenericTypographic()
You could also use:
float doubleWidth = g.MeasureString(text+text,...).Width;
float singleWidth = g.MeasureString(text).Width;
float textWidth = doubleWidth-singleWidth;
This will allow you to work with other languages such as Japanese.
On codeproject, Pierre Anaud's solution was to use MeasureCharacterRanges, which returns a region matching exactly the bounding box of the specified string:
static public int MeasureDisplayStringWidth(Graphics graphics, string text, Font font)
{
System.Drawing.StringFormat format = new System.Drawing.StringFormat ();
System.Drawing.RectangleF rect = new System.Drawing.RectangleF(0, 0, 1000, 1000);
var ranges = new System.Drawing.CharacterRange(0, text.Length);
System.Drawing.Region[] regions = new System.Drawing.Region[1];
format.SetMeasurableCharacterRanges (ranges);
regions = graphics.MeasureCharacterRanges (text, font, rect, format);
rect = regions[0].GetBounds (graphics);
return (int)(rect.Right + 1.0f);
}
I'm a little late to the party here, but I was trying to do something similar and stumbled on this question. You've probably already seen the following remark from the documentation for the Graphics.MeasureString method on MSDN:
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. Also, the DrawString method adjusts glyph points to optimize display quality and might display a string narrower than reported by MeasureString. To obtain metrics suitable for adjacent strings in layout (for example, when implementing formatted text), use the MeasureCharacterRanges method or one of the MeasureString methods that takes a StringFormat, and pass GenericTypographic. Also, ensure the TextRenderingHint for the Graphics is AntiAlias.
It seems that you were trying to follow this advice because you're using StringFormat.GenericTypographic as a starting point for your custom StringFormat object. However, the line
format.FormatFlags = StringFormatFlags.MeasureTrailingSpaces;
effectively negates the fact that you started with StringFormat.GenericTypographic because it clears any previously set flags. What you probably meant to do is set the StringFormatFlags.MeasureTrailingSpaces flag while preserving the other flags, like so:
format.FormatFlags |= StringFormatFlags.MeasureTrailingSpaces;
Try to use this methods:
GDI+ (graphics.MeasureString and graphics.DrawString) >> System.Drawing.Graphics
GDI (TextRenderer.MeasureText and TextRenderer.DrawText)
It also may help you:
Write a custom measure method:
Split entry string on special characters
Use above .net methods
Calculate width of special characters and sum ...
Read Ian Boyd answer
A method using Graphics.MeasureCharacterRanges to return all the rectangles enclosing each individual letter in a string and their positions is given here: Measure character positions when drawing long strings in C#
I have used the MeasureCharactersInWord and MeasureCharacters methods from that post, then in order to find the exact width without the spaces added to each side of the string, I use this code:
var w = 0F;
var rects = MeasureCharacters(Graphics.FromHwnd(IntPtr.Zero), font, text);
if (rects.Count>0)
{
if (rects.Count == 1)
{
w = rects.First().Width;
}
else
{
var r0 = rects.First();
var rN = rects.Last();
w = rN.X - r0.X + rN.Width;
}
}
Note that the height of the rectangle is the height of the font and not of the character itself. If you need the height check this post: Determining exact glyph height in specified font
A final note: the reason why I used MeasureCharacterRanges is because all the other methods I tried were failing at giving me a bounding box without space to the left and right of the text. This post The wonders of text rendering and GDI gives a method to get the string width and remove this space using TextRenderer so the whole thing can be done in about two lines of code. I haven't checked the result though.
Related
Is there any way to make lines between points, given a simple geometry as line style, using WPF geometries? I know it is possible to make these kind of lines:
-- -- --- --
But I want to make lines, using any simple geometry (e.g: the '^' symbol). So what I want is something like these: (the line may not necessarily be horizontal or vertical):
^^^^^^^^^^^^^^^^^
*****************
Note: I don't want to make line with some characters. I want to do it using any arbitrary geometries (e.g: start shape, triangle, or any other geometry). In other word I want to repeat some geometries along a linear path between two points. So these simple geometries may be rotated somehow to follow the line and ...
I think this is an interesting problem but I can't fit a satisfying answer in the stackoverflow textbox so I uploaded a proposed solution on github:
https://github.com/mrange/CodeStack/tree/master/q14545675/LineGeometry
I don't claim this is 100% solution to your problem (for one I am not 100% of all your requirements) but if you take a look at it perhaps something can be worked and improved upon.
Unless ofc I am way wrong on what you are looking for.
If I understand correctly, you'd like to use the * or ^ or ! as a line essentially. Rather then use a normal solid, dash, dotted, and so on line you'd like to use physical characters? But you'd like those characters to become a Geometry object.
You could do something like:
// Create a line of characters.
string lineString = "^^^^^^^^^^^^^^";
// Create Formatted Text, customize accordingly.
FormattedText formatText = new FormattedText(
lineString, CultureInfo.GetCultureInfo("en-us"),
FlowDirection.LeftToRight,
new Typeface("Arial"), 32, Brushes.Black);
// Set the Width and Height.
formatText.MaxTextWidth = 200;
formatText.MaxTextHeight = 100;
// You can obviously add as many customization's and outputs of your choice.
Now I understand this isn't what you want, you want the above string to act in Geometry. To accomplish that; you just need to do:
// Build Geometry object to represent text.
Geometry lineGeometry = formatText.BuildGeometry(new System.Windows.Point(0, 0));
// Tailor Geometry object that represents our item.
Geometry hGeo = formatText.BuildHighlightGeometry(new System.Windows.Point(0, 0));
Now essentially you've built a Geometry object that represents "^^^^^^^^".
Hopefully I understood correctly, I don't know if that solves your problem.
Here is some C# code written in linqpad to reproduce the issue.
var font = new System.Drawing.Font("Arial", 8);
using (var g = System.Drawing.Graphics.FromHwnd(IntPtr.Zero))
{
//65536 characters is fine
g.DrawString("a".PadLeft(65535, 'a'), font, System.Drawing.Brushes.Black, new System.Drawing.RectangleF(0, 0, 1, 1));
//65537 characters causes an error.
g.DrawString("a".PadLeft(65536, 'a'), font, System.Drawing.Brushes.Black, new System.Drawing.RectangleF(0, 0, 1, 1));
//65537 characters is however fine if the width is over 600581
g.DrawString("a".PadLeft(65536, 'a'), font, System.Drawing.Brushes.Black, new System.Drawing.RectangleF(0, 0, 600582, 1));
}
Anyone know the exact relationship between the string's length and the layout rectangle's width? The number 600581 seems very arbitrary. Although 65536 makes more sense as that is 0x10000.
Anyone know the exact relationship between the string's length and the
layout rectangle's width? The number 600581 seems very arbitrary.
The number 600581 is indeed arbitrary and in this case it reflects the fonts char widths (or the typeface's glyphs).
Take for example 600581 / 65536 which gives an average char-width of 9.16 pixels per char which is reasonable for a fairly squared font such as Arial when you include spacing. GDI+ also adds padding to the text's bounding box in addition to this.
If you try a wider (or a narrower font, but you wouldn't notice unless reducing the number) as well as different letter combinations you should get different results. Try a mono-spaced font and you should be able to predict pretty accurately the needed boundaries (don't forget the padding).
If you really need to print long strings try to buffer the printing, however GDI+ isn't your best friend in these cases. Try the TextRenderer class (a GDI wrapper) as already suggested:
http://msdn.microsoft.com/en-us/library/system.windows.forms.textrenderer.aspx
If I calculate size in terms of width for text using below it give me says 500
intWidth = (int)objGraphics.MeasureString(String.concat(sImageText1, sImageText2), objFont).Width;
but if i do like this
intWidth = (int)objGraphics.MeasureString(sImageText1, objFont).Width + (int)objGraphics.MeasureString(sImageText2, objFont).Width;
I search lot and have done lots of different scenarios but still getting difference if we measure string width using "MeasureString" with full text or part by part.
Why is this?
The documentation has the answer:
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. Also, the DrawString method adjusts glyph points to optimize display quality and might display a string narrower than reported by MeasureString. To obtain metrics suitable for adjacent strings in layout (for example, when implementing formatted text), use the MeasureCharacterRanges method or one of the MeasureString methods that takes a StringFormat, and pass GenericTypographic. Also, ensure the TextRenderingHint for the Graphics is AntiAlias.
The emphasized parts are what is relevant here.
To fix your code, add the necessary parameters:
intWidth = (int)objGraphics.MeasureString(sImageText1, objFont, 1024, Stringformat.GenericTypographic).Width
+ (int)objGraphics.MeasureString(sImageText2, objFont, 1024, StringFormat.GenericTypographic).Width;
I have the following code that is working on my local machine (Win 7, .NET 4), but it doesn't seem to work on the server. I want to programmatically measuring text length
Could anyone please help with it?
private float GetTextSize(string text, float textSize)
{
using (Bitmap bmp = new Bitmap(1, 1))
{
bmp.SetResolution(96, 96);
using (Graphics g = Graphics.FromImage(bmp))
{
using (System.Drawing.Font font = new System.Drawing.Font(fontName, textSize))
{
return g.MeasureString(text, font).Width;
}
}
}
}
I will consider any better solution if available.
Update:
How to detect if a font type is available on the machine?
Update 2:
Why does my question get minus points? Could anyone please give it an up vote.
Thanks in advance!
The problem is that it will return different text length even for text that has the same number characters, e.g. 1234 is longer than abcd for some font type. My previous logic is to compare the number of characters for each text, and pass the longest text to the method, which is not correct based on that reason.
The solution is to call the method for each text, and text length returned from the method is reliable to compare.
I have a program that is manually generating a PDF using PDFsharp in C#. Although it is rather tedious I have to use it and am nearing completion of the task. Only one issue remains.
Problem: I am wondering how I can find out what the width of a given char is for a given font size in Arial.
I am having trouble coming up with a more precise text wrapping method. Right now one defines a width of box in pixels and then proceeds to write out a string in that box. I kinda just guess at the max length of the string that can fit in the box and there are some visual oddities that crop up from time to time.
Any help?
Thanks
I'm not sure from your question whether you want a way to measure the size of a string specifically using PDF#, or just a generic way.
In general .Net, you can use the MeasureText method of the TextRenderer class (from Windows forms):
TextRenderer.MeasureText("some text", new Font("Arial", 1.0f, FontStyle.Regular))
This will return a Size instance that will contain Width=12, Height=2.
In the days of True Type fonts with kerning etc. there is not a single character width.
The width of the string "VA" is probably less then the sum of the widths of the strings "V" and "A".
Summing up the widths if individual characters is a starting point - but finally you have to measure the complete string.
PDFsharp includes the class XTextFormatter (with full source code) that does this line wrapping. It can be adapted for specific requirements.
It uses gfx.MeasureString(token, this.font).Width to measure the width of a string.
XGraphics.MeasureString(string s, Font f) does the trick.
//l_Page is of type PdfPage
var l_Graphics = XGraphics.FromPdfPage( l_Page );
var l_TitleFont = new Font( "Arial", 15f, GraphicsUnit.World )
var l_Title = "Hallo Welt";
//l_TitleSize will be of type XSize and has properties for Width and Height
var l_TitleSize = l_Graphics.MeasureString( l_Title, l_TitleFont );