Find char width in pixels for various Arial fontsizes - c#

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

Related

Measure String in MigraDoc TextFrame

I've already tried asking the question on their forums but as yet to have received a response, hope someone can help me on here.
I have setup a custom report screen in asp.net where people can drag labels and fields and Migradoc produces this accordingly by using textframes and top/left/width/height properties to lay them out in the same place they were dragged/resized to. This all works great however one issue I have is if the text is longer than the textframe it runs off the page and I need the page to move on accordingly whilst retaining the other objects in place.
I can use the below code to measure a string:
Style style = document.Styles["Normal"];
TextMeasurement tm = new TextMeasurement(style.Font.Clone());
float fh = tm.MeasureString(value, UnitType.Millimeter).Height;
float fw = tm.MeasureString(value, UnitType.Millimeter).Width;
But it's only useful for comparing the width against the frame and not the height because it could be different once put into a smaller area. Does anyone know how I can measure this string based on bound width/height values i.e. within a text frame.
Look at the CreateBlocks() method in the XTextFormatter class and how it calls MeasureString in a loop to break the text to multiple lines.
I'm afraid you have to implement such a loop yourself.
Or maybe use the PrepareDocument() method of the DocumentRenderer class to let MigraDoc do the work and just query the dimensions when it's done.
BTW: a similar question had been asked at the forum before:
http://forum.pdfsharp.net/viewtopic.php?p=3590#p3590
Answer includes some source code.
An easy way to do this (using I-liked-the-old-stack-overflow's link) is to add the PdfWordWrapper class to your project and then calculate the dimensions of your text as follows:
var wrapper = new PdfWordWrapper(g, contentWidth); //g is your XGraphics object
wrapper.Add("My text here", someFont, XBrushes.Black);
wrapper.Process();
var dimensions = wrapper.Size; //you can access .Height or .Width
//If you want to reuse the wrapper just call .Clear() and then .Add() again with some new text

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

MeasureString weird behavior

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;

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.

Categories

Resources