Text clipping issue with size calculated from MeasureString - c#

I have a sample string for which i calculates the size and draws the string in the form through below code,
public partial class Form1 : Form
{
Rectangle textRectangle;
string text = "testing the size 1 testing the size 1 testing the size 1 testing the size 1";
public Form1()
{
InitializeComponent();
Size textSize = this.CreateGraphics().MeasureString(text, this.Font, 100).ToSize();
textRectangle = new Rectangle(0, 0, textSize.Width, textSize.Height);
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
using (SolidBrush brush = new SolidBrush(Color.Blue))
{
e.Graphics.DrawString(text, this.Font, brush, textRectangle);
}
}
}
My issue is the textRectangle calculated from the simple string is not sufficient to draw in form. Please refer to attached image, that some of the given string is not drawn.
Could anyone please update me, why the size calculated from MeasureString() is not enough to draw using the DrawString() method?

The above mentioned issue is resolved when using MeasureTrailingSpaces formatflags . Use the below code,
StringFormat stringFormat = new StringFormat(StringFormatFlags.MeasureTrailingSpaces);
Size textSize = this.CreateGraphics().MeasureString(text, this.Font, 100, stringFormat).ToSize();
textRectangle = new Rectangle(0, 0, textSize.Width, textSize.Height);
While calculating size, the spaces are not considered, so to consider spaces in string, MeasureTrailingSpaces have to be used.

Related

How to generate an image based on text with given width and no padding in C#

I want to save a given text as an image. The image should have a fixed width (200px in my example). Around the text there should be no spacing, padding or whatever. Regardless what text is entered, the width should not change, only the height of the text. This works. However, there is still white padding around the text and the text is truncated on the right side.
I have already tried to change StringFormat.GenericTypographic and also tried without AntiAlias, but I do not get it to work. Can anyone help me to get this working?
private void button1_Click(object sender, EventArgs e)
{
Font font = new Font("Arial", 1000, FontStyle.Regular);
Image i = DrawText("TEST MY STRING", font, Color.Red, Color.White);
i.Save("test.jpg", System.Drawing.Imaging.ImageFormat.Jpeg);
}
private Image DrawText(String text, Font font, Color textColor, Color backColor)
{
Image img = new Bitmap(1, 1);
Graphics drawing = Graphics.FromImage(img);
drawing.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias;
SizeF sz = drawing.MeasureString(text, font, 0, StringFormat.GenericTypographic);
img.Dispose();
drawing.Dispose();
/* Set maximum width of string. */
int textWidth = 200;
float sf = textWidth / sz.Width;
int textHeight = (int)(sz.Height * sf);
img = new Bitmap(textWidth, textHeight);
drawing = Graphics.FromImage(img);
drawing.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias;
drawing.Clear(backColor);
drawing.ScaleTransform(sf, sf);
drawing.DrawString(text, font, Brushes.Black, 0, 0, new StringFormat(StringFormatFlags.NoWrap | StringFormatFlags.NoClip));
drawing.Save();
drawing.Dispose();
return img;
}

WinForms: Measure Text With No Padding

In a WinForms app, I am trying to measure the size of some text I want to draw with no padding. Here's the closest I've gotten...
protected override void OnPaint(PaintEventArgs e) {
DrawIt(e.Graphics);
}
private void DrawIt(Graphics graphics) {
var text = "123";
var font = new Font("Arial", 32);
var proposedSize = new Size(int.MaxValue, int.MaxValue);
var measuredSize = TextRenderer.MeasureText(graphics, text, font, proposedSize, TextFormatFlags.NoPadding);
var rect = new Rectangle(100, 100, measuredSize.Width, measuredSize.Height);
graphics.DrawRectangle(Pens.Blue, rect);
TextRenderer.DrawText(graphics, text, font, rect, Color.Black, TextFormatFlags.NoPadding);
}
... but as you can see from the results ...
... there is still a considerable amount of padding, particularly on the top and bottom. Is there any way to measure the actual bounds of the drawn characters (with something really awful like printing to an image and then looking for painted pixels)?
Thanks in advance.
(I've marked this answer as "the" answer just so people know it was answered, but #TaW actually provided the solution -- see his link above.)
#TaW - That was the trick. I'm still struggling to get the text to go where I want it to, but I'm over the hump. Here's the code I ended out with...
protected override void OnPaint(PaintEventArgs e) {
e.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
DrawIt(e.Graphics);
}
private void DrawIt(Graphics graphics) {
var text = "123";
var font = new Font("Arial", 40);
// Build a path containing the text in the desired font, and get its bounds.
GraphicsPath path = new GraphicsPath();
path.AddString(text, font.FontFamily, (int)font.Style, font.SizeInPoints, new Point(0, 0), StringFormat.GenericDefault);
var bounds = path.GetBounds();
// Move it where I want it.
var xlate = new Matrix();
xlate.Translate(100, 100);
path.Transform(xlate);
// Draw the path (and a bounding rectangle).
graphics.DrawPath(Pens.Black, path);
bounds = path.GetBounds();
graphics.DrawRectangle(Pens.Blue, bounds.Left, bounds.Top, bounds.Width, bounds.Height);
}
... and here is the result (notice the nice, tight bounding box) ...
Have you tried
Graphics.MeasureString("myString", myFont, int.MaxValue, StringFormat.GenericTypographic)

Position Of String Using StringFormat

Is there a way to get the position of a drawn string in a control? I'm writing a control that acts as a GroupBox and I when I draw the string that acts as the title, the border line strikes through it.
In order to fix this problem, I was just going to fill and rectangle where the title is with a back color so the title doesn't appear to be struck through. My problem is that the position of the title is dictated by the StringFormat I pass through the Graphics.DrawString() method. I don't have to explicitly declare the location of the string, but I do have to explicitly declare the location of the rectangle and I don't know where the location of the string is.
How should I go about this?
I believe you can use MeasureCharacterRanges to accurately measure string position
here is a little sample:
The method for doing measurement:
public static RectangleF MeasureStringBounds(Graphics graphics, string text, Font font,RectangleF bounding, StringFormat format)
{
var ranges =new[] {new CharacterRange(0, text.Length)};
format.SetMeasurableCharacterRanges(ranges);
var regions = graphics.MeasureCharacterRanges(text, font, bounding, format);
var accurateBoundings = regions[0].GetBounds(graphics);
return accurateBoundings;
}
Usage:
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
var str = "hello";
var format = new StringFormat {Alignment = StringAlignment.Center};
e.Graphics.DrawString(str, Font, new SolidBrush(Color.Black), new Rectangle(0, 0, Width, Height), format);
//measuring part
var region = MeasureStringBounds(e.Graphics, str, Font, new RectangleF(0, 0, Width, Height), format);
//Draw measured region
e.Graphics.DrawRectangle(new Pen(Color.Red), region.X, region.Y, region.Width, region.Height);
}

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.

Displaying a rotated string - DataGridView.RowPostPaint

I want to display a lengthy rotated string in the background of one of my rows in a DataGridView. However, this:
private void dataGridView1_RowPostPaint(object sender, DataGridViewRowPostPaintEventArgs e)
{
if (e.RowIndex == 0)
{
...
//Draw the string
Graphics g = dataGridView1.CreateGraphics();
g.Clip = new Region(e.RowBounds);
g.RotateTransform(-45);
g.DrawString(printMe, font, brush, e.RowBounds, format);
}
}
does not work because text is clipped before it's rotated.
I've also tried painting on a Bitmap first, but there seems to be a problem painting transparent Bitmaps - the text comes out pure black.
Any ideas?
I figured it out. The problem was that Bitmaps apparently don't have transparency, even when you use PixelFormat.Format32bppArgb. Drawing the string caused it to draw over a black background, which is why it was so dark.
The solution was to copy the row from the screen onto a bitmap, draw onto the bitmap, and copy that back to the screen.
g.CopyFromScreen(absolutePosition, Point.Empty, args.RowBounds.Size);
//Draw the rotated string here
args.Graphics.DrawImageUnscaledAndClipped(buffer, args.RowBounds);
Here is the full code listing for reference:
private void dataGridView1_RowPostPaint(object sender, DataGridViewRowPostPaintEventArgs args)
{
if(args.RowIndex == 0)
{
Font font = new Font("Verdana", 11);
Brush brush = new SolidBrush(Color.FromArgb(70, Color.DarkGreen));
StringFormat format = new StringFormat
{
FormatFlags = StringFormatFlags.NoWrap | StringFormatFlags.NoClip,
Trimming = StringTrimming.None,
};
//Setup the string to be printed
string printMe = String.Join(" ", Enumerable.Repeat("RUNNING", 10).ToArray());
printMe = String.Join(Environment.NewLine, Enumerable.Repeat(printMe, 50).ToArray());
//Draw string onto a bitmap
Bitmap buffer = new Bitmap(args.RowBounds.Width, args.RowBounds.Height);
Graphics g = Graphics.FromImage(buffer);
Point absolutePosition = dataGridView1.PointToScreen(args.RowBounds.Location);
g.CopyFromScreen(absolutePosition, Point.Empty, args.RowBounds.Size);
g.RotateTransform(-45, MatrixOrder.Append);
g.TranslateTransform(-50, 0, MatrixOrder.Append); //So we don't see the corner of the rotated rectangle
g.DrawString(printMe, font, brush, args.RowBounds, format);
//Draw the bitmap onto the table
args.Graphics.DrawImageUnscaledAndClipped(buffer, args.RowBounds);
}
}

Categories

Resources