I'm attempting to convert a text file to an image using a certain font (Courier New). The problem I'm having is that the font is fixed width, but the text is not being rendered that way on the image. Here's the code I'm currently using
var fontName = textToImageSection.GetString("FontName", "Courier New");
var fontSize = textToImageSection.GetInt("FontSize", 12);
textFont = new Font(fontName, fontSize);
var sf = new StringFormat(StringFormatFlags.MeasureTrailingSpaces);
sf.Trimming = StringTrimming.Character;
var text = File.ReadAllText(textFile.Path);
var image = new Bitmap(1, 1);
var textSize = new Size();
using (var g = Graphics.FromImage(image))
textSize = g.MeasureString(text, textFont, int.MaxValue, sf).ToSize();
image = new Bitmap(image, textSize);
using (var g = Graphics.FromImage(image))
{
g.Clear(Color.White);
//g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAliasGridFit;
g.DrawString(text, textFont, Brushes.Black, borderLeft, borderTop, sf);
}
image.SaveAsTiff(path);
I've been trying different values for TextRenderingHint without much luck and palying around with the StringFormat.
Here's the resulting image
Here's the text in Notepad++ displayed with Courier New Font
Related
i created a function which is drawing a circle with a letter inside.
i write it into a stream and set it to picture box in Zoom Mode.
It looks nice but some pieces of the circle are cut off.
Here is the Code for the Circle:
public MemoryStream GenerateCircle(string name)
{
var avatarString = string.Format("{0}", name[0]).ToUpper();
var bgColour = ColorTranslator.FromHtml("#007FBC");
var bmp = new Bitmap(70, 70);
var sf = new StringFormat();
sf.Alignment = StringAlignment.Center;
sf.LineAlignment = StringAlignment.Center;
var font = new Font("Arial", 34, FontStyle.Bold, GraphicsUnit.Pixel);
var graphics = Graphics.FromImage(bmp);
graphics.Clear(Color.White);
graphics.SmoothingMode = SmoothingMode.AntiAlias;
graphics.TextRenderingHint = TextRenderingHint.AntiAliasGridFit;
Rectangle rect = new Rectangle(0, 0, 70, 70);
Brush b = new SolidBrush(bgColour);
Pen pen = new Pen(bgColour);
graphics.DrawEllipse(pen, rect);
graphics.FillEllipse(b, rect);
graphics.DrawString(avatarString, font, new SolidBrush(Color.WhiteSmoke), 35, 35, sf);
graphics.Flush();
var ms = new MemoryStream();
bmp.Save(ms, ImageFormat.Png);
return ms;
}
i set it like that:
avatarpicturebox.Image = Image.FromStream(GenerateCircle("Test"));
That is what it looks like:
Circle Cuts Off
Can someone help here please?
Just reduce the circle's width and height 1 pixel each to avoid clipping the right and bottom sides due to the pixels offset. Use Rectangle.Inflate method and pass negative width and height values to shrink the destination rectangle to fit.
Modifying your function to return a Bitmap instead of MemoryStream.
public Bitmap GenerateCircle(string name)
{
var bgColour = ColorTranslator.FromHtml("#007FBC");
var bmp = new Bitmap(70, 70, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
using (var graphics = Graphics.FromImage(bmp))
using (var sf = new StringFormat())
using (var font = new Font("Arial", 34, FontStyle.Bold, GraphicsUnit.Pixel))
using (var b = new SolidBrush(bgColour))
using (var pen = new Pen(bgColour))
{
sf.Alignment = StringAlignment.Center;
sf.LineAlignment = StringAlignment.Center;
graphics.Clear(Color.White);
graphics.SmoothingMode = SmoothingMode.AntiAlias;
graphics.TextRenderingHint = TextRenderingHint.ClearTypeGridFit;
graphics.PixelOffsetMode = PixelOffsetMode.Half;
Rectangle rect = new Rectangle(Point.Empty, bmp.Size);
rect.Inflate(-2, -2);
graphics.DrawEllipse(pen, rect);
graphics.FillEllipse(b, rect);
graphics.DrawString(name.ToUpper(), font, Brushes.WhiteSmoke, rect, sf);
}
return bmp;
}
And the caller:
private void SomeButton_Click(object sender, EventArgs e)
{
avatarpicturebox.Image?.Dispose();
avatarpicturebox.Image = GenerateCircle("T");
}
Side Notes
Always dispose of the graphics objects you create either by calling explicitly the .Dispose method or by creating them with using keyword.
Use the Graphics.DrawString overloads that take a Rectangle and StringFormat params to draw the strings. Use the later to dictate how and where the string should be drawn in the given Rectangle. See also StringFormatFlags enum.
I am trying to print 40x40mm labels from a programmatically created image.
The label must have text on it, and a logo. Since the label is fairly small I am finding myself fiddling with how to do proper smooting, antialias and such.
I have tried multipl settings but I am not sure it's even the right way to go about it.
First I draw the container Bitmap:
private Bitmap DrawLabelCircle()
{
var labelImage = new Bitmap(152, 152);
using (Graphics gfx = Graphics.FromImage(labelImage))
{
var pen = new Pen(Color.Black, 1);
gfx.SmoothingMode = SmoothingMode.AntiAlias;
gfx.DrawEllipse(pen, 1, 1, 150, 150);
}
return labelImage;
}
Then I overlay different text snippets on that container Bitmap
private Bitmap DrawDistributorTextRectangle(string text)
{
var bitmap = new Bitmap(113, 113);
var rectangle = new Rectangle(0, 0, 110, 110);
using (Graphics gfx = Graphics.FromImage(bitmap))
{
gfx.SmoothingMode = SmoothingMode.AntiAlias;
gfx.InterpolationMode = InterpolationMode.HighQualityBicubic;
gfx.PixelOffsetMode = PixelOffsetMode.HighQuality;
var font = new Font(FontFamily.GenericSansSerif, 5, FontStyle.Regular, GraphicsUnit.Point);
var brush = new SolidBrush(Color.Black);
gfx.TextRenderingHint = TextRenderingHint.AntiAlias;
gfx.DrawString(text, font, brush, rectangle);
}
bitmap.RotateFlip(RotateFlipType.Rotate270FlipNone);
return bitmap;
}
Overlay that text on the previous created Bitmap.
private Bitmap DistributorTextOverlay(Bitmap source, Bitmap overlay)
{
var result = new Bitmap(source.Width, source.Height);
var graphics = Graphics.FromImage(result);
graphics.CompositingMode = CompositingMode.SourceOver;
graphics.SmoothingMode = SmoothingMode.HighQuality;
graphics.DrawImage(source, 0, 0);
graphics.DrawImage(overlay, 120, 0);
return result;
}
And the I save it.
var imageCodecInfo = ImageCodecInfo.GetImageEncoders().First(encoder => encoder.MimeType == "image/png");
var encoderInfo = new EncoderParameters() { Param = { [0] = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, 100L) } };
image.SetResolution(203, 203);
image.Save("img.png", imageCodecInfo, encoderInfo);
The big challenge here is that the image I get is actually looking alright, all things considered.
But when I print it, it looks terrible pixelated.
I would really like to give some pointers for what settings I should apply to all these bitmaps before saving the final result, and what settings should apply for the final image I save.
I am by no means a .NET graphics expert so all help is much appreciated.
40mm is 1.5748 inches. So if you plan to print it at 300 dpi resolution, your bitmap should be 1.5748*300 = 472 pixels instead of 152.
Im drawing Text using the following code onto a Bitmap
GraphicsPath pth = new GraphicsPath();
var style = (int)myfont.Style;
pth.AddString(tcaption.Text, myfont.FontFamily, style, myfont.Size, point, StringFormat.GenericTypographic);
p = new Pen(new SolidBrush(bc), 2f);
mygraphics.DrawPath(p, pth);
I'm using the TextRenderer to measure the size of the string..
int Width = TextRenderer.MeasureText(tcaption.Text, myfont).Width;
But this does not produce the correct size of the drawn string; there is around 20-30% difference from the actual size of the drawn string?
What im i doing wrong? Please advice.
UPDATE:
I want to draw a Text and an Image onto a Bitmap,so inorder to accommodate both i'm creating an Bitmap like this
intWidth = TextRenderer.MeasureText(tcaption.Text, cfont).Width + image.Width;
intHeight = TextRenderer.MeasureText(tcaption.Text, cfont).Height +image.Height;
tempimage= new Bitmap(intWidth, intHeight);
Then i create Graphics object from the Bitmap like this
using (Graphics newg = Graphics.FromImage(tempimage))
#Hans Passant
I have also tried the Graphics.MeasureString as an alternative to TextRenderer
Now i set the position of the text and image-I need to draw the image at the top left corner .. so
imageposy = 0;
imageposx = 10;
textposy = image.Height;
textposx = 0;
Then i draw the text like this
po=new Point(textposx, textposy);
newg.SmoothingMode = SmoothingMode.AntiAlias;
GraphicsPath pth = new GraphicsPath();
var style = (int)myfont.Style;
pth.AddString(tcaption.Text, myfont.FontFamily, style, myfont.Size, po,
StringFormat.GenericTypographic);
newg.FillPath(new SolidBrush(fc), pth);
Now i draw the image like this
Rectangle nrect = new Rectangle(imageposx, imageposy, image.Width,
image.Height);
objGraphics = Graphics.FromImage(tempimage);
objGraphics.DrawImage(image, nrect);
As you have seen i need to add the offset 10 to imageposition x coordinate to correct the measurement issue.
Hope my update throws more light into the question... what im i doing wrong?
Please advice..
instead of using TextRenderer use GraphicsPath:
var path = new GraphicsPath();
path.AddString(text, font.FontFamily, (int)font.Style, size, new Point(0, 0), StringFormat.GenericTypographic);
var area = Rectangle.Round(path.GetBounds());
Here is sample code that generates image with size of text:
private Image DrawText(String text, Font font, int size, Color textColor, Color backColor)
{
var path = new GraphicsPath();
path.AddString(text, font.FontFamily, (int)font.Style, size, new Point(0, 0), StringFormat.GenericTypographic);
var area = Rectangle.Round(path.GetBounds());
Rectangle br = Rectangle.Round(path.GetBounds());
var img = new Bitmap(br.Width, br.Height);
var drawing = Graphics.FromImage(img);
drawing.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAliasGridFit;
drawing.SmoothingMode = SmoothingMode.HighSpeed;
drawing.Clear(backColor);
drawing.TranslateTransform((img.Width - br.Width) / 2 - br.X, (img.Height - br.Height) / 2 - br.Y);
drawing.FillPath(Brushes.Black, path);
Brush textBrush = new SolidBrush(textColor);
drawing.Save();
textBrush.Dispose();
drawing.Dispose();
return img;
}
Here are sample results:
I have made an application which generates me a QR Code in a PNG image, but now I have to insert the text from QR Code next to the QR Code image.
I don't have any experience using ZXing Library but I'm thinking that it may contain an option for this...
Example:
Code:
namespace QR_Code_with_WFA
{
public void CreateQRImage(string inputData)
{
if (inputData.Trim() == String.Empty)
{
System.Windows.Forms.MessageBox.Show("Data must not be empty.");
}
BarcodeWriter qrcoder = new ZXing.BarcodeWriter
{
Format = BarcodeFormat.QR_CODE,
Options = new ZXing.QrCode.QrCodeEncodingOptions
{
ErrorCorrection = ZXing.QrCode.Internal.ErrorCorrectionLevel.H,
Height = 250,
Width = 250
}
};
string tempFileName = System.IO.Path.GetTempPath() + inputData + ".png";
Image image;
String data = inputData;
var result = qrcoder.Write(inputData);
image = new Bitmap(result);
image.Save(tempFileName);
System.Diagnostics.Process.Start(tempFileName);
}
}
Well, ZXing.BarcodeWriter.Options has property PureBarcode, which will put source text into generated image when set to false.
Unfortunately it has no effect when format of barcode is BarcodeFormat.QR_CODE (and it is by design).
But you can draw your text manually after you've generated barcode image:
var result = qrcoder.Write(inputData);
using (var g = Graphics.FromImage(result))
using (var font = new Font(FontFamily.GenericMonospace, 12))
using (var brush = new SolidBrush(Color.Black))
using(var format = new StringFormat(){Alignment = StringAlignment.Center})
{
int margin = 5, textHeight = 20;
var rect = new RectangleF(margin, result.Height - textHeight,
result.Width - 2 * margin, textHeight);
g.DrawString(inputData, font, brush, rect, format);
}
result.Save(tempFileName);
Note you can select your own font size and fontfamily which will better suite your goals.
Update:
In the case you're trying to place text to the right from image - you have to "extend" to the right your generated image first, and then draw text:
var result = qrcoder.Write(inputData);
int textWidth = 200, textHeight = 20;
// creating new bitmap having imcreased width
var img = new Bitmap(result.Width + textWidth, result.Height);
using (var g = Graphics.FromImage(img))
using (var font = new Font(FontFamily.GenericMonospace, 12))
using (var brush = new SolidBrush(Color.Black))
using (var bgBrush = new SolidBrush(Color.White))
using (var format = new StringFormat() { Alignment = StringAlignment.Near })
{
// filling background with white color
g.FillRectangle(bgBrush, 0, 0, img.Width, img.Height);
// drawing your generated image over new one
g.DrawImage(result, new Point(0,0));
// drawing text
g.DrawString(inputData, font, brush, result.Width, (result.Height - textHeight) / 2, format);
}
img.Save(tempFileName);
I have been searching and looking for Drawing text with outline onto images?
Here my code
private static void tulisnamafile(string imagepath, string textnya)
{
Image image = Image.FromStream(new MemoryStream(File.ReadAllBytes(imagepath)));
Bitmap newImage = new Bitmap(640, 380);
using (Graphics g = Graphics.FromImage(newImage))
{
// Draw base image
g.DrawImageUnscaled(image, 0, 0);
//Static is HERE
SolidBrush brushing = new SolidBrush(Color.White);
Font font = new Font(("Comic Sans MS"), 20.0f);
int napoint = newImage.Height - 90;
int napointa = image.Width - 200;
FontFamily ff = new FontFamily("Times New Roman");
int fontSize = 24;
Font f = new Font(ff, fontSize, FontStyle.Regular);
StringFormat sf = new StringFormat();
Rectangle displayRectangle = new Rectangle(new Point(5, napoint), new Size(newImage.Width - 1, newImage.Height - 1));
g.DrawEllipse(Pens.Magenta, new Rectangle(0, 0, 1, 1));
GraphicsPath gp = new GraphicsPath();
gp.AddString(textnya, ff, (int)FontStyle.Bold, fontSize + 4, new Point(0, 0), sf);
g.FillPath(Brushes.White, gp);
g.DrawPath(Pens.Black, gp);
g.Flush(FlushIntention.Sync);
g.Dispose();
}
image.Dispose();
string fileName = "ab.jpg";
string path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, fileName);
MessageBox.Show(path);
newImage.Save(path, System.Drawing.Imaging.ImageFormat.Jpeg);
newImage.Dispose();
}
and Its the trigger
private void button3_Click(object sender, EventArgs e)
{
string imagename = "C:\\Documents and Settings\\admin\\My Documents\\Visual Studio 2008\\Projects\\template\\template\\bin\\Debug\\bg.jpg";
tulisnamafile(imagename, "SlimPort® SP1002; Connect mobile devices to any big screen. High Speed micro USB");
}
Check the Code result:
Such messing result, UNWRAPPED and with white color
This what I want, and with wrapping?
I found in CodeProject but not luck, its using C++. Based someone in neowin and tried this one too..
But still not luck.
UPDATE:
Here my working code, for who maybe need it... based from Abdias Software's code ( check the answers ), I make small changes ( there some error in those code).
private static void tulisnamafile(string imagepath, string textnya)
{
float fontSize = 22;
Image image = Image.FromStream(new MemoryStream(File.ReadAllBytes(imagepath)));
//some test image for this demo
Bitmap bmp = (Bitmap)Image.FromFile(imagepath);
Graphics g = Graphics.FromImage(bmp);
//this will center align our text at the bottom of the image
StringFormat sf = new StringFormat();
sf.Alignment = StringAlignment.Center;
sf.LineAlignment = StringAlignment.Far;
//define a font to use.
Font f = new Font("Impact", fontSize, FontStyle.Bold, GraphicsUnit.Pixel);
//pen for outline - set width parameter
Pen p = new Pen(ColorTranslator.FromHtml("#77090C"), 8);
p.LineJoin = LineJoin.Round; //prevent "spikes" at the path
//this makes the gradient repeat for each text line
Rectangle fr = new Rectangle(0, bmp.Height - f.Height, bmp.Width, f.Height);
LinearGradientBrush b = new LinearGradientBrush(fr,
ColorTranslator.FromHtml("#FF6493"),
ColorTranslator.FromHtml("#D00F14"),
90);
//this will be the rectangle used to draw and auto-wrap the text.
//basically = image size
Rectangle r = new Rectangle(0, 0, bmp.Width, bmp.Height);
GraphicsPath gp = new GraphicsPath();
//look mom! no pre-wrapping!
gp.AddString(textnya, f.FontFamily, (int)FontStyle.Bold, fontSize, r, sf);
//these affect lines such as those in paths. Textrenderhint doesn't affect
//text in a path as it is converted to ..well, a path.
g.SmoothingMode = SmoothingMode.AntiAlias;
g.PixelOffsetMode = PixelOffsetMode.HighQuality;
//TODO: shadow -> g.translate, fillpath once, remove translate
g.DrawPath(p, gp);
g.FillPath(b, gp);
//cleanup
gp.Dispose();
b.Dispose();
b.Dispose();
f.Dispose();
sf.Dispose();
g.Dispose();
string fileName = "ab.jpg";
string path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, fileName);
bmp.Save(path, System.Drawing.Imaging.ImageFormat.Jpeg);
bmp.Dispose();
}
Just to sum up:
Define a GraphicPath and then use DrawPath to draw outlined version of text, FillPath to draw a filled version.
For the second image with the woman, a second (filled) version is drawn first at a small offset.
For the gradient use LinearGradientBrush for brush. Thickness of the outline is defined by the Pen's thickness.
For wrapping define a StringFormat and use a Rectangle to define the region you want the text to be in.
To center the text you can define the rectangle to have the same width as the image, then set strformat.Alignment to Center.
UPDATE: To replicate the text in the second image you can use this code:
float fontSize = 52;
//some test image for this demo
Bitmap bmp = (Bitmap)Image.FromFile(s"test.jpg");
Graphics g = Graphics.FromImage(bmp);
//this will center align our text at the bottom of the image
StringFormat sf = new StringFormat();
sf.Alignment = StringAlignment.Center;
sf.LineAlignment = StringAlignment.Far;
//define a font to use.
Font f = new Font("Impact", fontSize, FontStyle.Bold, GraphicsUnit.Pixel);
//pen for outline - set width parameter
Pen p = new Pen(ColorTranslator.FromHtml("#77090C"), 8);
p.LineJoin = LineJoin.Round; //prevent "spikes" at the path
//this makes the gradient repeat for each text line
Rectangle fr = new Rectangle(0, bmp.Height - f.Height, bmp.Width, f.Height);
LinearGradientBrush b = new LinearGradientBrush(fr,
ColorTranslator.FromHtml("#FF6493"),
ColorTranslator.FromHtml("#D00F14"),
90);
//this will be the rectangle used to draw and auto-wrap the text.
//basically = image size
Rectangle r = new Rectangle(0, 0, bmp.Width, bmp.Height);
GraphicsPath gp = new GraphicsPath();
//look mom! no pre-wrapping!
gp.AddString("Demo for Stack Overflow",
f.FontFamily, (int)f.Style, fontSize, r, sf);
//these affect lines such as those in paths. Textrenderhint doesn't affect
//text in a path as it is converted to ..well, a path.
g.SmoothingMode = SmoothingMode.AntiAlias;
g.PixelOffsetMode = PixelOffsetMode.HighQuality;
//TODO: shadow -> g.translate, fillpath once, remove translate
g.DrawPath(p, gp);
g.FillPath(b, gp);
//cleanup
gp.Dispose();
b.Dispose();
b.Dispose();
f.Dispose();
sf.Dispose();
g.Dispose();
bmp.Save(s"test_result.jpg", System.Drawing.Imaging.ImageFormat.Jpeg);
bmp.Dispose();
This will produce this result:
To produce the extra "shadow" just translate the gfirst, draw filled, then remove the translation.
The FromHtml is used here as I picked the colors from your image and was to lazy to convert. Just use Color.FromARGB() or a fixed color - as you want.
VB version:
Dim fontSize As Single = 52
Dim bmp As Bitmap = Bitmap.FromFile("c:\test.jpg")
Dim g As Graphics = Graphics.FromImage(bmp)
Dim sf As New StringFormat(StringFormatFlags.NoClip)
sf.Alignment = StringAlignment.Center
sf.LineAlignment = StringAlignment.Far
Dim f As New Font("Impact", fontSize, FontStyle.Bold, GraphicsUnit.Pixel)
Dim p As New Pen(ColorTranslator.FromHtml("#77090C"), 4)
p.LineJoin = LineJoin.Round
'rectangle for font to repeat gradient for each line
Dim fr As New Rectangle(0, bmp.Height - f.Height, bmp.Width, f.Height)
Dim b As New LinearGradientBrush(fr,
ColorTranslator.FromHtml("#FF6493"),
ColorTranslator.FromHtml("#D00F14"),
90)
Dim r As New Rectangle(0, 0, bmp.Width, bmp.Height)
Dim gp As New GraphicsPath
gp.AddString("Demo for Stack Overflow",
f.FontFamily,
f.Style,
fontSize,
r,
sf)
g.SmoothingMode = SmoothingMode.AntiAlias
g.PixelOffsetMode = PixelOffsetMode.HighQuality
g.DrawPath(p, gp)
g.FillPath(b, gp)
gp.Dispose() 'path
b.Dispose() 'b
b.Dispose() 'p
f.Dispose() 'font
sf.Dispose() 'stringformat
g.Dispose() 'g
bmp.Save("c:\test_result.jpg", Imaging.ImageFormat.Jpeg)
bmp.Dispose()
A simple way to get "better" results could be to draw the text twice. Draw the shadow first, for example some pixels to the right and to the bottom, if you want the classic drop-shadow look, in gray color. You may also want to consider using a different font, any font without serifs will look better i would guess.
For the gradient effect see the msdn page or google how on how to use it.
Also, play around with the SmoothingMode and TextRenderingHint of the graphics object, HighQuality and Antialias should produce better looking results.