Fill A Graphic With Text Using RectangleF and DrawString - c#

I've been asked to generate placeholder graphics for some items in a database but with one caveat - the font size needs to be dynamic so that the text fills the entire graphic.
The first iteration of the code works just fine, but the client wants to the font to be bigger so that the graphics are easier to read. Here's a snippet of the part which generates the text:
using (Graphics Graphic = Graphics.FromImage(Img))
{
// Add Some Padding
Width = Width - 20;
Height = Height - 20;
// Generate The Text
Font GraphicFont = new Font("Arial", 26, FontStyle.Bold);
RectangleF RectF = new RectangleF(10, 10, (float)Width, (float)Height);
StringFormat sf = new StringFormat();
sf.Alignment = StringAlignment.Near;
sf.LineAlignment = StringAlignment.Near;
Graphic.DrawString(Title, GraphicFont, Brushes.Black, RectF, sf);
}
Is there I can detect text overflow in my RectangleF, or can I detect the size of my DrawString before I apply it to the graphic, or is there some other magic I can do to get the text to fill the graphic?
I would like the text to wordwrap and the font to be the same size for all the words, so it doesn't necessarily need to fill the graphic entirely - hope this all makes sense.

Use Graphics.MeasureString (documentation: http://msdn.microsoft.com/en-us/library/6xe5hazb.aspx)
If you want to size text to fill a region, you can do binary search on different sizes to determine the largest size that still fits in your target region, then use that size to actually draw the string.

Related

Graphics.DrawString renders text with black outline on a transparent background

I'm drawing a string on a Bitmap with transparent background using Graphics.DrawString() and I get text with a black contour, when the Font size is smaller than 23 millimeters (the Font is created with GraphicsUnit.Millimeter).
Code:
Bitmap bmp = new Bitmap(2000, 2000);
Color alpha = Color.FromArgb(0, 0, 0, 0);
for (int x = 0; x < bmp.Width; x++)
for (int y = 0; y < bmp.Height; y++)
bmp.SetPixel(x, y, alpha);
Graphics g = Graphics.FromImage(bmp);
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
g.SmoothingMode = SmoothingMode.HighQuality;
Font labelFont = new Font("Cascadia Mono SemiBold", 23/*22*/, FontStyle.Regular, GraphicsUnit.Millimeter);
Brush brush = new SolidBrush(Color.White);
g.DrawString("Some text", labelFont, brush, 200, 200);
23 Millimeters-Units font:
22 Millimeters-Units font:
I tried to use TextRenderer, but this draws text without transparent background.
The code presented here has multiple problems:
The initial loop is counter-productive for multiple reasons:
Tries to fill a Bitmap with a transparent color, but this is already the non-color associated with a newly created Bitmap (Color.FromArb(0, 0, 0, 0))
Uses the SetPixel() method, the slowest possible tool for the task
If needed, a Bitmap can be filled with a Color using the Graphics.Clear() method, which calls a native GDI+ function to perform the task
Setting an InterpolationMode in this context is not useful, this property selects the algorithm used to scale or rotate images
The SmoothingMode property selects the algorithm used to anti-aliasing lines, curves and the edges of filled areas. It doesn't apply to the rendering of Fonts, so has no effect on the drawn text. It applies to text rendered with a GraphicsPath, since the text is converted to curves
None of the disposable objects (Graphics, Font, Brush) is either disposed explicitly or declared with using statements (which is pretty bad). It's not clear when the Bitmap is disposed, but could be the duty of the code that uses it
To specify the rendering mode of Fonts, the TextRenderingHint property is used instead. Since it's not specified, the System default smoothing of Font is used, usually ClearType.
About this rendering form, see the notes in:
Drawing a Long String on to a Bitmap results in Drawing Issues
ClearType uses intra-pixel smoothing, designed initially for LCD screens, to blend text with a background; it's especially effective with small Fonts sizes. It doesn't support alpha colors (not in this context, at least).
The device context in which the text is rendered, a GDI+ MemoryBitmap, doesn't use or understand this type of hinting (smoothing), so the pixels that fail to render are filled with an empty color, which notoriously appears as black
A black-ish contour might manifests with different Font sizes (not just the measures reported in the question), when the ClearType hinting fill is less than one pixel
To fix the rendering, remove the clutter, specify a suitable TextRenderingHint mode and declare correctly all disposable objects.
I'm not including the Bitmap, because I don't know how it's used. It must be disposed at some point, of course (very important, it allocates unmanaged resources, the Garbage Collector cannot help you)
var bmp = new Bitmap(2000, 2000);
using (var g = Graphics.FromImage(bmp))
using (var font = new Font("Cascadia Mono SemiBold", 22, FontStyle.Regular, GraphicsUnit.Millimeter))
using (var brush = new SolidBrush(Color.White)) {
g.CompositingQuality = CompositingQuality.HighQuality;
g.TextRenderingHint = TextRenderingHint.AntiAliasGridFit;
g.DrawString("Some text", font, brush, 200, 200);
}
TextRenderingHint.AntiAliasGridFit is appropriate here (see also the linked notes), the characters are drawn using their anti-aliased glyph bitmap and hinting (smoothing)
TextRenderingHint.AntiAlias can also be used in this context (and costs slightly less)
CompositingQuality.HighQuality is not actually used here, there isn't really any composition, since no image is rendered against a background (which may require gamma correction), but also has no cost. You can keep it, in case you decide to draw a bitmap onto the current, at some point; or simply remove it.
About TextRenderer draws text without transparent background
This is not correct. TextRenderer (GDI) can of course render text with a transparent background, it just doesn't support an alpha color (in this context)
As mentioned, we're working with an in-memory GDI+ Device Context
But, if you draw the same text in a different Device Context, e.g., the surface of a Control, then things change.
Also note that TextRenderer cannot be used to render text when printing (for the same reasons previously described).
Test this code, subscribing to the Paint event of a PictureBox, also adding a background Image (without a background image the result doesn't change, it's just more visible)
Graphics.DrawString() is used to render text with a semi-transparent (ARGB) Color
TextFormatFlags flags = TextFormatFlags.Left | TextFormatFlags.Top;
private void someControl_Paint(object sender, PaintEventArgs e)
{
using (var font = new Font("Segoe UI", 22, FontStyle.Regular, GraphicsUnit.Millimeter))
using (var brush = new SolidBrush(Color.White)) {
TextRenderer.DrawText(e.Graphics, "Some text", font,
new Rectangle(new Point(0, 10), pictureBox1.ClientSize), Color.White, Color.Transparent, flags);
e.Graphics.CompositingQuality = CompositingQuality.HighQuality;
e.Graphics.TextRenderingHint = TextRenderingHint.AntiAliasGridFit;
e.Graphics.DrawString("Some text", font, brush, 0, 130);
}
using (Font font1 = new Font("Segoe UI", 8.0f, FontStyle.Regular, GraphicsUnit.Millimeter))
using (Font font2 = new Font("Segoe UI", 5.5f, FontStyle.Regular, GraphicsUnit.Millimeter))
using (var brush = new SolidBrush(Color.FromArgb(100, Color.Black))) {
e.Graphics.DrawString("← TextRenderer", font1, brush, 610, 70);
e.Graphics.DrawString("ForeColor: White, BackColor: Transparent", font2, brush, 610, 110);
e.Graphics.DrawString("← GDI+ Graphics", font1, brush, 610, 190);
e.Graphics.DrawString("ForeColor: White, Hinting: AntiAliasGridFit", font2, brush, 610, 230);
}
}
Resulting in (enlarge it):

How can I set font height in system.drawings.font?

I'm using C# to write a text in a certain format. My problem is that when I edit font size both width and height are changing while I just want to change the font height.
My code:
using (Graphics graphics = Graphics.FromImage(bitmap))
{
using (System.Drawing.Font romanfont = new System.Drawing.Font("Times New Roman",11, FontStyle.Bold))
//using (System.Drawing.Font romanfont = new System.Drawing.Font("Times New Roman", 11, FontStyle.Bold))
{
SolidBrush transBrush = new SolidBrush(Color.FromArgb(65, 79, 79));
StringFormat format = new StringFormat(StringFormatFlags.DirectionRightToLeft);
graphics.DrawString(firstname, romanfont, transBrush, firstnameLocation, format);
graphics.DrawString(secondname, romanfont, transBrush, secondnameLocation, format);
graphics.DrawString(finalfirstadd, romanfont, transBrush, firstaddresslocation, format);
graphics.DrawString(finalsecondadd, romanfont, transBrush, secondaddresslocation, format);
}
}
You can achieve this effect by setting a transform on the Graphics object.
For example, if you want to make the text twice as tall but still the same width, you can do this:
graphics.scaleTransform(1, 2);
You would put this anywhere above the place where you draw your strings. Note that this change will make everything twice as tall, so you may need to adjust your positions and sizes of your rectangles (such as firstnameLocation; in this case you'd probably want to divide the top and height of the rectangle by 2.)

Text in drawn image is in wrong font (C#)

I'm trying to write a code that outputs a PNG image for a given block of text. I'm using System.Drawing to achieve this.
My problem is that the text in output image is in wrong font.
Here is the code of the function that I'm using in order to draw the output:
static Bitmap FromTextToPic(string text, Int16 size)
{
//create dummy Bitmap object
Bitmap bitmapImage = new Bitmap(2, 2);
//create dummy measurements
int imageWidth = 0;
int imageHeight = 0;
//naming font
font = "Lohit-Devanagari";
// Creates the Font object for the image text drawing.
System.Drawing.Font fontObject = new System.Drawing.Font(font, size, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Pixel);
// Creates a graphics object to measure the text's width and height.
Graphics graphicsObject = Graphics.FromImage(bitmapImage);
// This is where the bitmap size is determined.
imageWidth = (int)graphicsObject.MeasureString(text, fontObject).Width;
imageHeight = (int)graphicsObject.MeasureString(text, fontObject).Height;
// Creates the bmpImage again with the correct size for the text and font.
bitmapImage = new Bitmap(bitmapImage, new Size(imageWidth, imageHeight));
// Adds the colors to the new bitmap.
graphicsObject = Graphics.FromImage(bitmapImage);
// Sets Background color to white
graphicsObject.Clear(System.Drawing.Color.White);
//enables optimization for LCD screens
graphicsObject.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
//enables antialiasing
graphicsObject.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias;
//draws text to picture with black foreground color
graphicsObject.DrawString(text, fontObject, new SolidBrush(System.Drawing.Color.Black), 0, 0, StringFormat.GenericDefault);
graphicsObject.Flush();
return (bitmapImage);
}
Even when I'm specifying "Lohit-Devanagari" as the font, I always get the output in "Mangal" font (both are Devanagari fonts). So, what have I done wrong?
Also, the output stretches over to a long distance if there isn't a newline character in text (as in this picture). Is there a way to wrap up the output image to some fixed width?
Instead of this:
//naming font
font = "Lohit-Devanagari";
Try this:
//naming font
FontFamily font = new FontFamily("Lohit Devanagari");
The font name are probably without the "-" character.
If you declare a new object FontFamily, it will make an exception if the font does not exist.So you have to download and install it ...

How to add different text color to bitmap image?

How to add different text with different colour in bitmap image using wpf.i have written the code it will take only one colour in text line but i want different colour in bitmap
SolidBrush brush = new SolidBrush(System.Drawing.Color.White);
System.Drawing.Brush brush1 = new SolidBrush(System.Drawing.Color.Blue);
// draw your rectangle below the original image
System.Drawing.Font font = new System.Drawing.Font("Arial", fontsize, System.Drawing.FontStyle.Bold, GraphicsUnit.Pixel);
SizeF textSize = new SizeF();
graphics.DrawString(multiLineString, font, brush1, position);
please help me out from this problem
Based on your comment - you just need following:
create all the brushes you need - having red, blue and green colors;
split your source string into "i am", "going" and "home" strings;
calculate width in pixels of each string using font you've created and Graphics.MeasureString method;
draw first string from your source position using red brush;
increment position X coordinate with first string width in pixels
draw the rest of your strings using brushes you want in the same manner.

Why TextRenderer adds margins while measuring text?

Because of lack of Graphics object in certain places in my application, I decided to use TextRenderer class. What is quite surprising though is that it adds a lot of margins to measured text. For example:
private void button1_Click(object sender, EventArgs e) {
using (var g = this.CreateGraphics()) {
Font font = new Font("Calibri", 20.0f, GraphicsUnit.Pixel);
Size size = TextRenderer.MeasureText("Ala ma kota", font);
g.DrawRectangle(Pens.Red, new Rectangle(new Point(10, 10), size));
TextRenderer.DrawText(g, "Ala ma kota", font, new Point(10, 10), Color.Black);
}
}
Gives the following result:
Why does it do so? Is there a way to force it to get the real text size? (and of course draw it in the same rectangle it returns)
From MSDN:
For example, the default behavior of the TextRenderer is to add padding to the bounding rectangle of the drawn text to accommodate overhanging glyphs. If you need to draw a line of text without these extra spaces, use the versions of DrawText and MeasureText that take a Size and TextFormatFlags parameter, as shown in the example.
You must also pass the Graphics object for correct results, because:
This overload of MeasureText(String, Font, Size, TextFormatFlags) will ignore a TextFormatFlags value of NoPadding or LeftAndRightPadding. If you are specifying a padding value other than the default, you should use the overload of MeasureText(IDeviceContext, String, Font, Size, TextFormatFlags) that takes a IDeviceContext object.
Size size = TextRenderer.MeasureText(g,
"Ala ma kota",
font,
new Size(int.MaxValue, int.MaxValue),
TextFormatFlags.NoPadding);
TextRenderer.DrawText(g, "Ala ma kota", font,
new Point(10, 10),
Color.Black,
TextFormatFlags.NoPadding);
g.DrawRectangle(Pens.Red, new Rectangle(new Point(10, 10), size));
Also have a look at using the Graphics methods directly: GDI+ MeasureString() is incorrectly trimming text

Categories