How to use TextMetrics to measure the dimensions of a string - c#

I'm trying to get the height of a string. I've come across MeasureString but it's inaccurate for what I need it for - too small for what MS Word is showing (okay, this is probably another topic so if you can point me to the right resources...)
So, I found out about TextMetrics. I want to try it but I couldn't understand how to use it. Here is my sample code:
static public double MeasureGroup2()
{
TextMetrics txt = new TextMetrics();
double height;
height = txt.Height;
return height;
}
Where/how can I set the font and font size, and the string for it to return the values I need?
P.S. My end goal is to measure the height of a string the same way MS Word "measures" or renders it. I'm open to other solutions other than these two.
Additional info:
Code for MeasureString:
static public double MeasureGroup1(string TextFont, int FSize)
{
string Str = "Mgkfps";
Bitmap Bmp = new Bitmap(99,99);
Graphics DrawGraphics = Graphics.FromImage(Bmp);
GraphicsUnit Unit = GraphicsUnit.Point;
DrawGraphics.PageUnit = Unit;
DrawGraphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias;
StringFormat format = StringFormat.GenericTypographic;
// Set up string.
System.Drawing.Font stringFont = new System.Drawing.Font(TextFont, FSize, FontStyle.Regular, Unit);
// Measure string.
SizeF stringSize = new SizeF();
stringSize = DrawGraphics.MeasureString(Str, stringFont, 99, format);
DrawGraphics.Dispose();
format.Dispose();
stringFont.Dispose();
return stringSize.Height;
//return TextSize;
}
Notes:
Using Arial 11, and Arial 12, the output of this code needs to be multiplied by 1.0294 to be the same as how MS Word is showing. Physical measurements were made using Word's ruler, Photoshop, and a lot of ratio-and-proportion. (Maybe this is the problem?)
static public double MeasureGroup2(string TextFont, int FSize)
{
string Str = "Mgkfps";
double height;
Bitmap Bmp = new Bitmap(99, 99);
Graphics DrawGraphics = Graphics.FromImage(Bmp);
GraphicsUnit Unit = GraphicsUnit.Point;
DrawGraphics.PageUnit = Unit;
DrawGraphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAliasGridFit;
StringFormat format = StringFormat.GenericTypographic;
// Set up string.
System.Drawing.Font stringFont = new System.Drawing.Font(TextFont, FSize, FontStyle.Regular, Unit);
Rectangle Rect = new Rectangle(0, 0, 99, 99);
Size sz = new Size(99,99);
sz = TextRenderer.MeasureText(DrawGraphics, Str, stringFont, sz ,TextFormatFlags.NoPadding);
height = sz.Height;
return height;
}

First of all, thank you everyone for helping me out!
When using TextMetrics to get the dimensions of a text, refer to this post here:
https://www.cyotek.com/blog/retrieving-font-and-text-metrics-using-csharp
He has a sample project down at the bottom of the page.
As for where I was hoping to use it, which is getting the height of a text in MS Word:
I assumed that the text height and line spacing, when set to single, is the same. This is not the case. There is a small amount of space, the leading, that is not measured by MeasureString. That is why I was hoping to use the GetTextMetrics function since it returns a struct that includes the leading. Anyway, it appears to be used for something else.
Fortunately, and apparently, there is a method native to the .NET framework that returns the value I need which is FontFamily.GetLineSpacing.
LineHeight = stringFont.Size * FF.GetLineSpacing(FontStyle.Regular)/FF.GetEmHeight(FontStyle.Regular);
Reference:
https://answers.microsoft.com/en-us/office/forum/office_2013_release-word/what-reasons-make-different-line-spacing-between/ca1267f9-0cfe-4f26-8048-bbd7a6a46398?auth=1
https://learn.microsoft.com/en-us/dotnet/framework/winforms/advanced/how-to-obtain-font-metrics

Related

C# - Resize Bitmap from a variable String

In my application, I generate a Bitmap with a variable string.
Here is my function:
public void Image(String text, String font, int size)
{
Font font = new Font(font, size);
float res = ((font.SizeInPoints * text.Length) / 72) * 96;
using (Bitmap img = new Bitmap((int)res, font.Height))
{
Graphics g = Graphics.FromImage(img);
SolidBrush drawBrush = new SolidBrush(Color.Black);
g.DrawString(text, font, drawBrush, 1, 0);
String directory = AppDomain.CurrentDomain.BaseDirectory + "Content\\Images\\Signature\\";
string outputFileName = directory + "sign.png";
img.Save(outputFileName, ImageFormat.Png);
}
}
I would like the width of the image to match perfectly the width of the string printed in that bitmap.
As you can see, I tried to calculate the width with point size of the font.
The problem is that each letter printed has a different width so I can not get the size before creating the Bitmap.
Plus, I don't even know how to retrieve the actual size of the printed string...
Does anyone have an idea?
Use the Graphics.MeasureString function. It takes a string and a font, and returns the size of the rendered text as a SizeF. There are also additional overloads that can take formatting information, and one that takes a SizeF representing the maximum width for wrapping.
Details can be found here: https://msdn.microsoft.com/en-us/library/6xe5hazb(v=vs.110).aspx
// Set up string.
string measureString = "Measure String";
Font stringFont = new Font("Arial", 16);
// Set maximum layout size.
SizeF layoutSize = new SizeF(100.0F, 200.0F);
// Set string format.
StringFormat newStringFormat = new StringFormat();
newStringFormat.FormatFlags = StringFormatFlags.DirectionVertical;
// Measure string.
SizeF stringSize = new SizeF();
stringSize = e.Graphics.MeasureString(measureString, stringFont, layoutSize, newStringFormat);
// Draw rectangle representing size of string.
e.Graphics.DrawRectangle(new Pen(Color.Red, 1), 0.0F, 0.0F, stringSize.Width, stringSize.Height);
// Draw string to screen.
e.Graphics.DrawString(measureString, stringFont, Brushes.Black, new PointF(0,

Determining text width

I want to find correct approach for calculating text width for specified font in C#.
I have following approach in Java and it seems it works:
public static float textWidth(String text, Font font) {
// define context used for determining glyph metrics.
BufferedImage bufImage = new BufferedImage(2 /* dummy */, 2 /* dummy */, BufferedImage.TYPE_4BYTE_ABGR_PRE);
Graphics2D g2d = (Graphics2D) bufImage.createGraphics();
FontRenderContext fontRenderContext = g2d.getFontRenderContext();
// determine width
Rectangle2D bounds = font.createGlyphVector(fontRenderContext, text).getLogicalBounds();
return (float) bounds.getWidth();
}
But watch my code in C#:
public static float TextWidth(string text, Font f)
{
// define context used for determining glyph metrics.
Bitmap bitmap = new Bitmap(1, 1);
Graphics grfx = Graphics.FromImage(bitmap);
// determine width
SizeF bounds = grfx.MeasureString(text, f);
return bounds.Width;
}
I have different values for above two functions for the same font. Why? And what is correct approach in my case?
UPDATE: The TextRenderer.MeasureText approach gives only integer measurements. I need more precuse result.
Use TextRenderer
Size size = TextRenderer.MeasureText( < with 6 overloads> );
TextRenderer.DrawText( < with 8 overloads> );
There is a good article on TextRenderer in this MSDN Magazine article.
Nothing stands out, other than lack of disposing your objects:
public static float TextWidth(string text, Font f) {
float textWidth = 0;
using (Bitmap bmp = new Bitmap(1,1))
using (Graphics g = Graphics.FromImage(bmp)) {
textWidth = g.MeasureString(text, f).Width;
}
return textWidth;
}
The other method to try is the TextRenderer class:
return TextRenderer.MeasureText(text, f).Width;
but it returns an int, not a float.

how to get the number of char that fit in a line (Printing c#)

I already have this code but it gives me the wrong result.
private void document_PrintPage(object sender, System.Drawing.Printing.PrintPageEventArgs e)
{
int charPerLine = e.MarginBounds.Width / (int)e.Graphics.MeasureString("m", txtMain.Font).Width;
}
The txtMain is a textbox.
This should do the trick. Be careful when dividing by a variable cast to an integer. You are leaving yourself open to a divide-by-zero here in the event that the Width property is less than one, which will be truncated to zero. It may be unlikely that you will have such a small font in your application, but it is still good practice.
private void document_PrintPage(object sender, System.Drawing.Printing.PrintPageEventArgs e)
{
if( (int)e.Graphics.MeasureString("m", txtMain.Font).Width > 0 )
{
int charPerLine =
e.MarginBounds.Width / (int)e.Graphics.MeasureString("m", txtMain.Font).Width;
}
}
The real issue though is why do you even need to know the number of characters per line. Unless you are trying to do some sort of ASCII art, you can use the different overloads of Graphics.DrawString to have GDI+ layout the text for you inside a bounding rectangle without needing to know how many characters fit on a line.
This sample from MSDN shows you how to do this:
// Create string to draw.
String drawString = "Sample Text";
// Create font and brush.
Font drawFont = new Font("Arial", 16);
SolidBrush drawBrush = new SolidBrush(Color.Black);
// Create rectangle for drawing.
float x = 150.0F;
float y = 150.0F;
float width = 200.0F;
float height = 50.0F;
RectangleF drawRect = new RectangleF(x, y, width, height);
// Draw rectangle to screen.
Pen blackPen = new Pen(Color.Black);
e.Graphics.DrawRectangle(blackPen, x, y, width, height);
// Set format of string.
StringFormat drawFormat = new StringFormat();
drawFormat.Alignment = StringAlignment.Center;
// Draw string to screen.
e.Graphics.DrawString(drawString, drawFont, drawBrush, drawRect, drawFormat);
So if you are trying to print a page of text, you can just set the drawRect to the e.MarginBounds and plug a page worth of text in for drawString.
Another thing, if you are trying to print tabular data, you can just partition the page into rectangles - one for each column/row (however you need it), and use e.Graphics.DrawLine overloads to print the table borders.
If you post more details on what you are actually trying to achieve we can help more.

c# system.drawing calculate size of font for 1 character to fill a square image

If I have a System.Drawing.Bitmap with equal dimensions e.g. 100x100, 50x50 and I wanted to use this code to draw in a single character:
StringFormat stringFormat = new StringFormat();
stringFormat.Alignment = StringAlignment.Center;
stringFormat.LineAlignment = StringAlignment.Center;
font = new Font(_fontName, _fontSize, _fontStyle);
GraphicsPath path = new GraphicsPath();
path.AddString("A", font.FontFamily, (int)font.Style, font.Size, rect, stringFormat);
How would I go about calculating the value for _fontSize so that it fits tightly inside the dimensions of the image?
Graphics.MeasureString function returns the size of a string of a given font. In a loop you can determine what font size fits best.
http://msdn.microsoft.com/en-us/library/403ezxd2.aspx
its gonna help u:
float fontsizeem = height;
int charfits=0;int line=2;
for (; charfits < captchastring.Length || line < 1 ; fontsizeem--)//font support style correct // color fix
{
graphic.MeasureString(captchastring, new Font(fontFamily, fontsizeem, fontstyle), new SizeF((float)width*0.6f, (float)height*0.6f), format, out charfits, out line);
}
fontsizeem *= graphic.DpiY /72 ; // this unit converting fucks me out :(((((((
path.AddString(captchastring, fontFamily,(int) fontstyle,fontsizeem,rectangle, format);// -- size

Drawing Text on Image library

Is there any open source library for drawing text to image in C#? I have been strugling with TextRenderer and graphics.DrawString() whole day but I never got close to getting decent results, I tried every combination of Smoothing, Interpolation, TextRenderHint but quality is always semi-decent.
Here are some images and that is best I achived:
How it needs to look like:
This really looks good but with some strings seems like character spacing is wrong with some letters and the string leans.
Settings are:
objGraphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
objGraphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAliasGridFit;
objGraphics.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.GammaCorrected;
objGraphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
objGraphics.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.Half;
objGraphics.TextContrast = 0;
Format is Png and background is transparent, method is TextRenderer.Drawtext(). Seems like thickness of text is wrong, I assume it's something wrong with smoothing, when I try to bold text it stays almost the same, but only with font size of ~10px.
Here's what I use to add a Copyright watermark to photos uploaded to my website:
//Add Watermark to photo.
private System.Drawing.Image CreateWatermark(System.Drawing.Image imgPhoto, string Copyright)
{
Graphics g = Graphics.FromImage(imgPhoto);
g.SmoothingMode = SmoothingMode.HighQuality;
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
g.PixelOffsetMode = PixelOffsetMode.HighQuality;
foreach (PropertyItem pItem in imgPhoto.PropertyItems)
{
imgPhoto.SetPropertyItem(pItem);
}
int phWidth = imgPhoto.Width;
int phHeight = imgPhoto.Height;
//create a Bitmap the Size of the original photograph
Bitmap bmPhoto = new Bitmap(phWidth, phHeight, PixelFormat.Format24bppRgb);
bmPhoto.SetResolution(imgPhoto.HorizontalResolution, imgPhoto.VerticalResolution);
//load the Bitmap into a Graphics object
Graphics grPhoto = Graphics.FromImage(bmPhoto);
//------------------------------------------------------------
//Step #1 - Insert Copyright message
//------------------------------------------------------------
//Set the rendering quality for this Graphics object
grPhoto.SmoothingMode = SmoothingMode.AntiAlias;
//Draws the photo Image object at original size to the graphics object.
grPhoto.DrawImage(
imgPhoto, // Photo Image object
new Rectangle(0, 0, phWidth, phHeight), // Rectangle structure
0, // x-coordinate of the portion of the source image to draw.
0, // y-coordinate of the portion of the source image to draw.
phWidth, // Width of the portion of the source image to draw.
phHeight, // Height of the portion of the source image to draw.
GraphicsUnit.Pixel); // Units of measure
//-------------------------------------------------------
//to maximize the size of the Copyright message we will
//test multiple Font sizes to determine the largest posible
//font we can use for the width of the Photograph
//define an array of point sizes you would like to consider as possiblities
//-------------------------------------------------------
int[] sizes = new int[] { 16, 14, 12, 10, 8, 6, 4 };
Font crFont = null;
SizeF crSize = new SizeF();
//Loop through the defined sizes checking the length of the Copyright string
//If its length in pixles is less then the image width choose this Font size.
for (int i = 0; i < 7; i++)
{
//set a Font object to Arial (i)pt, Bold
crFont = new Font("arial", sizes[i], FontStyle.Bold);
//Measure the Copyright string in this Font
crSize = grPhoto.MeasureString(Copyright, crFont);
if ((ushort)crSize.Width < (ushort)phWidth)
break;
}
//Since all photographs will have varying heights, determine a
//position 5% from the bottom of the image
int yPixlesFromBottom = (int)(phHeight * .05);
//Now that we have a point size use the Copyrights string height
//to determine a y-coordinate to draw the string of the photograph
float yPosFromBottom = ((phHeight - yPixlesFromBottom) - (crSize.Height / 2));
//Determine its x-coordinate by calculating the center of the width of the image
float xCenterOfImg = (phWidth / 2);
//Define the text layout by setting the text alignment to centered
StringFormat StrFormat = new StringFormat();
StrFormat.Alignment = StringAlignment.Near;
//define a Brush which is semi trasparent black (Alpha set to 153)
SolidBrush semiTransBrush2 = new SolidBrush(System.Drawing.Color.FromArgb(153, 0, 0, 0));
//Draw the Copyright string
grPhoto.DrawString(Copyright, //string of text
crFont, //font
semiTransBrush2, //Brush
new PointF(xCenterOfImg + 1, yPosFromBottom + 1), //Position
StrFormat);
//define a Brush which is semi trasparent white (Alpha set to 153)
SolidBrush semiTransBrush = new SolidBrush(System.Drawing.Color.FromArgb(153, 255, 255, 255));
//Draw the Copyright string a second time to create a shadow effect
//Make sure to move this text 1 pixel to the right and down 1 pixel
grPhoto.DrawString(Copyright, //string of text
crFont, //font
semiTransBrush, //Brush
new PointF(xCenterOfImg, yPosFromBottom), //Position
StrFormat); //Text alignment
imgPhoto = bmPhoto;
return imgPhoto;
}
Using System.Drawing classes in ASP.NET is not supported.
Specifically, if you use it, under load from multiple threads, you will experience exceptions like this one:
Win32Exception: The operation completed successfully
at MS.Win32.HwndWrapper..ctor(Int32 classStyle, Int32 style, Int32 exStyle, Int32 x, Int32 y, Int32 width, Int32 height, String name, IntPtr parent, HwndWrapperHook[] hooks)
at System.Windows.Media.MediaContextNotificationWindow..ctor(MediaContext ownerMediaContext)
at System.Windows.Media.MediaContext..ctor(Dispatcher dispatcher)
That said, we discovered that marshaling all drawing operations to a single STA thread seemed to avoid these issues.
UPDATE: It's been five years and we still have no problem with this approach.

Categories

Resources