Programmatically measuring text length using GDI+ Graphics.MeasureString issue - c#

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.

Related

AForge ExhaustiveTemplateMatching works extremely slow

I am trying to find coordinates of one image inside of another using AForge framework:
ExhaustiveTemplateMatching tm = new ExhaustiveTemplateMatching();
TemplateMatch[] matchings = tm.ProcessImage(new Bitmap("image.png"), new Bitmap(#"template.png"));
int x_coordinate = matchings[0].Rectangle.X;
ProcessImages takes about 2 minutes to perform.
Image's size is about 1600x1000 pixels
Template's size is about 60x60 pixels
Does anyone know how to speed up that process?
As addition to the other answers, I would say that for your case:
Image's size is about 1600x1000 pixels Template's size is about 60x60 pixels
This framework is not the best fit. The thing you are trying to achieve is more search-image-in-other-image, than compare two images with different resolution (like "Search Google for this image" can be used).
About this so
called pyramid search.
it's true that the algorithm works way faster for bigger images. Actually the image-pyramid is based on template matching. If we take the most popular implementation (I found and used):
private static bool IsSearchedImageFound(this Bitmap template, Bitmap image)
{
const Int32 divisor = 4;
const Int32 epsilon = 10;
ExhaustiveTemplateMatching etm = new ExhaustiveTemplateMatching(0.90f);
TemplateMatch[] tm = etm.ProcessImage(
new ResizeNearestNeighbor(template.Width / divisor, template.Height / divisor).Apply(template),
new ResizeNearestNeighbor(image.Width / divisor, image.Height / divisor).Apply(image)
);
if (tm.Length == 1)
{
Rectangle tempRect = tm[0].Rectangle;
if (Math.Abs(image.Width / divisor - tempRect.Width) < epsilon
&&
Math.Abs(image.Height / divisor - tempRect.Height) < epsilon)
{
return true;
}
}
return false;
}
It should give you a picture close to this one:
As bottom line - try to use different approach. Maybe closer to Sikuli integration with .Net. Or you can try the accord .Net newer version of AForge.
If this is too much work, you can try to just extend your screenshot functionality with cropping of the page element that is required (Selenium example).
2 minutes seems too much for a recent CPU with the image a template sizes you are using. But there are a couple of ways to speed up the process. The first one is by using a smaller scale. This is called pyramid search. You can try to divide the image and template by 4 so that you will have an image of 400x250 and a template of 15x15 and match this smaller template. This will run way faster but it will be also less accurate. You can then use the interesting pixels found with the 15x15 template and search the corresponding pixels in the 1600x1000 image using the 60x60 template instead of searching in the whole image.
Depending on the template details you may try at an even lower scale (1/8) instead.
Another thing to know is that a bigger template will run faster. This is counter-intuitive but with a bigger template you will have less pixel to compare. So if possible try to use a bigger template. Sometimes this optimization is not possible if your template is already as big as it can be.

GDI+ DrawString generic exception when drawing a string with a length over 65536?

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

Checking to see if an image is Blank in C#

I've looked everywhere but there doesn't seem to be a standard (I could see) of how one would go about checking to see if an image is blank. In C#
I have a way of doing this, but would love to know what the correct way is of checking to see if an image is blank, so everyone could also know in the future.
I'm not going to copy paste a bunch of code in, if you want me to, it will be my pleasure, but I just first want to explain how i go about checking to see if an image is blank.
You take a .jpg image, Get the width of it. For example 500 pixels
Then you divide that by 2
giving you 250
Then you check what the colour of every pixel is in the location of (250 width, and i height) (where you iterate thought the hight of the image.
What this then do is only check the middle line of pixels of an image, vertically. It goes though all the pixels checking to see if the colour is anything Except white. I've done this so you wont have to search ALL 500*height of pixels and since you will almost always come across a colour in the middle of the page.
Its working... a bit slow...There must be a better way to do this? You can change it to search 2/3/4 lines vertically to increase your chance to spot a page that's not blank, but that will take even longer.
(Also note, using the size of the image to check if it contains something will not work in this case, since a page with two sentences on and a blank page's size is too close to one another)
After solution has been added.
Resources to help with the implementation and understanding of the solution.
Writing unsafe code - pointers in C
Using Pointers in C#
/unsafe (C# Compiler Options)
Bitmap.LockBits Method (Rectangle, ImageLockMode, PixelFormat)
(Note that on the first website, the stated Pizelformat is actually Pixelformat) - Small error i know, just mentioning, might cause some confusion to some.
After I implemented the method to speed up the pixel hunting, the speed didn't increase that much. So I would think I'm doing something wrong.
Old time = 15.63 for 40 images.
New time = 15.43 for 40 images
I saw with the great article DocMax quoted, that the code "locks" in a set of pixels. (or thats how i understood it)
So what I did is lock in the middle row of pixels of each page. Would that be the right move to do?
private int testPixels(String sourceDir)
{
//iterate through images
string[] fileEntries = Directory.GetFiles(sourceDir).Where(x => x.Contains("JPG")).ToArray();
var q = from string x in Directory.GetFiles(sourceDir)
where x.ToLower().EndsWith(".jpg")
select new FileInfo(x);
int holder = 1;
foreach (var z in q)
{
Bitmap mybm= Bitmap.FromFile(z.FullName) as Bitmap;
int blank = getPixelData2(mybm);
if (blank == 0)
{
holder = 0;
break;
}
}
return holder;
}
And then the class
private unsafe int getPixelData2(Bitmap bm)
{
BitmapData bmd = bm.LockBits(new System.Drawing.Rectangle((bm.Width / 2), 0, 1, bm.Height), System.Drawing.Imaging.ImageLockMode.ReadOnly, bm.PixelFormat);
int blue;
int green;
int red;
int width = bmd.Width / 2;
for (int y = 0; y < bmd.Height; y++)
{
byte* row = (byte*)bmd.Scan0 + (y * bmd.Stride);
blue = row[width * 3];
green = row[width * 2];
red = row[width * 1];
// Console.WriteLine("Blue= " + blue + " Green= " + green + " Red= " + red);
//Check to see if there is some form of color
if ((blue != 255) || (green != 255) || (red != 255))
{
bm.Dispose();
return 1;
}
}
bm.Dispose();
return 0;
}
If you can tolerate the chance of getting it wrong, the approach seems fine; I have done something very similar in my case, although I always had a visual confirmation to deal with errors.
For the performance, the key open question is how you are getting the pixels to test. If you are using Bitmap.GetPixel, you are bound to have performance problems. (Search for "Bitmap.GetPixel slow" in Google to see lots of discussion.)
Far better performance will come from getting all the pixels at once and then looping over them. I personally like Bob Powell's LockBits discussion for clarity and completeness. With that approach, checking all of the pixels may well be reasonable depending on your performance needs.
If you're using System.Drawing.Bitmap you can speed up things up (substantially), by:
Not using GetPixel to access the pixels, use LockBits and UnlockBits to copy the image bitmap to regular memory. See the examples on the MSDN documentation for usage.
Not calling the Width, Height or Size properties in for loop. Call Size once, store the values in a local variable and use those in the loop.
Notes:
When using System.Drawing.Bitmap your image may be in device memory and accessing it may be time consuming.
I don't remember whether loading an image into a Bitmap already converts it to RGB format as other formats are more difficult to work with, but if that is not the case you can create an RGB Bitmap of the same size as your original image, get it's Graphic object (Graphics.FromImage) and use DrawImage to draw the original image in the RGB bitmap.
Edit: Beat to the punch by DocMax.
In any case for speed you can also try using alternative libraries such as the excellent FreeImage which includes C# wrappers.
Scale the image to 1x1 then check one pixel
new Bitmap(previousImage, new Size(1, 1));

how to measure width of a string precisely?

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.

Find char width in pixels for various Arial fontsizes

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

Categories

Resources