I'm trying to add copyright to an image. If the resolution of image is less than 4592x2576, it works as I want. But in second case (in this case if the resolution is equals 4592x2576) it adds copyright too big size.
Look attachments for details.
class Program
{
private static string file5 = "d:\\DSC01305.JPG";
private static string file6 = "d:\\DSC01427.JPG";
static void Main(string[] args)
{
AddCopyrightWithText(file5);//good
AddCopyrightWithText(file6);//not good
}
private const string CopyrightText = "mysite.com";
private const int MaxFontSize = 190;
const int coefficient = 20;
public static void AddCopyrightWithText(string fileName)
{
using (var img = Image.FromFile(fileName))
{
using (var gr = Graphics.FromImage(img))
{
var color = Color.FromArgb(90, 241, 235, 105);
int fontSize = img.Width / coefficient;
if (fontSize > MaxFontSize)
fontSize = MaxFontSize;
var font = new Font("Comic Sans MS", (float)fontSize, FontStyle.Bold);
var stringFormat = new StringFormat();
stringFormat.Alignment = StringAlignment.Center;
stringFormat.LineAlignment = StringAlignment.Center;
gr.SmoothingMode = SmoothingMode.AntiAlias;
int y = img.Height - (int)gr.MeasureString(CopyrightText, font, int.MaxValue).Height;
int x = img.Width / 2;
gr.DrawString(CopyrightText, font, new SolidBrush(color), new Point(x, y), stringFormat);
}
using (var ms = new MemoryStream())
{
img.Save(ms, ImageFormat.Jpeg);
img.Dispose();
File.Delete(fileName);
var file = new FileStream(fileName, FileMode.Create, FileAccess.Write);
ms.Seek(0, SeekOrigin.Begin);
ms.WriteTo(file);
file.Close();
file.Dispose();
}
}
}
}
In the second case if I set fontSize = 182 manually (becase in the first case fontSize is equals 182), there is no effect, the result is the same!
How do I fix it?
P.S. First and second attachments show the result I want, third and fourth shows the bug. Pay attention in third attachment I changed the fontSize manually from 190 to 182.
Original image below
Original image below
Original image below
Original image below
It's necessary to use GraphicsUnit.Pixel
var font = new Font("Comic Sans MS", (float)fontSize, FontStyle.Bold, GraphicsUnit.Pixel);
The reason is, the constructor for Font that doesn't take a GraphicsUnit parameter, will use GraphicsUnit.Point.
This relates to the DPI information which for some image formats is stored in the image (for others it uses defaults from the system). Points are relative to inches, so different DPIs for the image means different sizes in pixels. Therefore different images end up having the text in different sizes. This would be wonderful if we wanted to print the images according to their DPI information and have the same size text in each printout. For on-screen use to be consistent, it's pixel-sizes that are important, so we make that explicit.
Related
Going through the steps mentioned here
and using IDAutomationCode39, I am getting the barcode image, however they are very blurr and only scans bigger size images. My barcode id will be upto 30 characters long, which is causing a very wide barcode image. Where could the problem lie? Is it the IDAutomationCode39 or my setting in my button click event below?
private void button1_Click(object sender, EventArgs e)
{
string abbre = GenerateProdCodeFromProductName();
txt2.Text = abbre;
string barcode = txt1.Text;
Bitmap bitm = new Bitmap(barcode.Length * 45, 160);
bitm.SetResolution(240, 240);
using (Graphics graphic = Graphics.FromImage(bitm))
{
Font newfont = new Font("IDAutomationHC39M", 6);
PointF point = new PointF(5f, 5f);
SolidBrush black = new SolidBrush(Color.Black);
SolidBrush white = new SolidBrush(Color.White);
graphic.FillRectangle(white, 0, 0, bitm.Width, bitm.Height);
graphic.DrawString("*" + barcode + "*", newfont, black, point);
}
using (MemoryStream Mmst = new MemoryStream())
{
bitm.Save("ms", ImageFormat.Jpeg);
pictureBox1.Image = bitm;
pictureBox1.Width = bitm.Width;
pictureBox1.Height = bitm.Height;
}
}
Thank you.
I don't see any PlugIn reference in your code, you are just using a Code39 ASCII font to print a Barcode on a Bitmap.
The problem I see is that the size of the resulting Bitmap is unscaled.
What I mean is, you let the size of the Barcode determine the size of the final graphic image.
It is usually the opposite. You have some defined dimensions for a Bitmap, because of layout constraints: you have to print the barcode to a label, for example.
If the generated Barcode is wider than its container, you have to normalize it (scale it to fit).
Here is what I propose. The size of the Bitmap if fixed (300, 150). When the Barcode is generated, its size is scaled to fit the Bitmap size.
The quality of the image is preserved, using an Bicubic Interpolation in case of down scaling. The resulting graphics is also Anti-Aliased.
(Anti-Alias has a good visual render. May not be the best choice for printing. It also depends on the printer.)
The generated Bitmap is then passed to a PictureBox for visualization and saved to disk as a PNG image (this format because of its loss-less compression).
Here is the result:
string Barcode = "*8457QK3P9*";
using (Bitmap bitmap = new Bitmap(300, 150))
{
bitmap.SetResolution(240, 240);
using (Graphics graphics = Graphics.FromImage(bitmap))
{
Font font = new Font("IDAutomationSHC39M", 10, FontStyle.Regular, GraphicsUnit.Point);
graphics.Clear(Color.White);
StringFormat stringformat = new StringFormat(StringFormatFlags.NoWrap);
graphics.TextRenderingHint = TextRenderingHint.AntiAliasGridFit;
graphics.TextContrast = 10;
PointF TextPosition = new PointF(10F, 10F);
SizeF TextSize = graphics.MeasureString(Barcode, font, TextPosition, stringformat);
if (TextSize.Width > bitmap.Width)
{
float ScaleFactor = (bitmap.Width - (TextPosition.X / 2)) / TextSize.Width;
graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
graphics.ScaleTransform(ScaleFactor, ScaleFactor);
}
graphics.DrawString(Barcode, font, new SolidBrush(Color.Black), TextPosition, StringFormat.GenericTypographic);
bitmap.Save(#"[SomePath]\[SomeName].png", ImageFormat.Png);
this.pictureBox1.Image = (Bitmap)bitmap.Clone();
font.Dispose();
}
}
I currently have an image which gets displayed to the user, I'm trying to add dynamic text to this image based on two parameters passed in.
The issue I have is when I step through the code it all seems to be working correctly, however when I see the image on the screen after the below code has run it doesn't have the text on it.
Below is my current set up of code:
public ActionResult GenerateImage(string savingAmount, string savingDest)
{
// Hardcoding values for testing purposes.
savingAmount = "25,000.00";
savingDest = "Canada";
PointF firstLocation = new PointF(10f, 10f);
PointF secondLocation = new PointF(10f, 50f);
Image imgBackground = Image.FromFile(Server.MapPath("~/assets/img/fb-share.jpg"));
int phWidth = imgBackground.Width; int phHeight = imgBackground.Height;
Bitmap bmBackground = new Bitmap(phWidth, phHeight, PixelFormat.Format24bppRgb);
bmBackground.SetResolution(72, 72);
Graphics grBackground = Graphics.FromImage(bmBackground);
Bitmap bmWatermark;
Graphics grWatermark;
bmWatermark = new Bitmap(bmBackground);
bmWatermark.SetResolution(imgBackground.HorizontalResolution, imgBackground.VerticalResolution);
grWatermark = Graphics.FromImage(bmWatermark);
grBackground.SmoothingMode = SmoothingMode.AntiAlias;
// Now add the dynamic text to image
using (Graphics graphics = Graphics.FromImage(imgBackground))
{
using (Font arialFont = new Font("Arial", 10))
{
grWatermark.DrawString(savingAmount, arialFont, Brushes.White, firstLocation);
grWatermark.DrawString(savingDest, arialFont, Brushes.White, secondLocation);
}
}
imgBackground.Save(Response.OutputStream, ImageFormat.Png);
Response.ContentType = "image/png";
Response.Flush();
Response.End();
return null;
}
As mentioned after this code has run, I then see the image in the browser however text is not displayed on the image, can anyone see / suggest what maybe causing this issue?
I feel like there are way to many images in that code for what you are describing as the intent of the code. What you want should reduce to this:
Load Image
Create Graphics on that Image
Draw into the Graphics and close
Output image to client
In the code sample you provided you are opening the Graphics on imgBackground then drawing into the grWatermark graphics which is opened earlier on against an image you never touch again.
public ActionResult GenerateImage(string savingAmount, string savingDest)
{
// Hardcoding values for testing purposes.
savingAmount = "25,000.00";
savingDest = "Canada";
PointF firstLocation = new PointF(10f, 10f);
PointF secondLocation = new PointF(10f, 50f);
Image imgBackground = Image.FromFile(Server.MapPath("~/assets/img/fb-share.jpg"));
using (Graphics graphics = Graphics.FromImage(imgBackground))
{
using (Font arialFont = new Font("Arial", 10))
{
graphics.DrawString(savingAmount, arialFont, Brushes.White, firstLocation);
graphics.DrawString(savingDest, arialFont, Brushes.White, secondLocation);
}
}
imgBackground.Save(Response.OutputStream, ImageFormat.Png);
Response.ContentType = "image/png";
Response.Flush();
Response.End();
return null;
}
I'm trying to add some text to an Image programmatically and later I save it. But the end result is somewhat not convincing.
The text on the image appears quite blurry.
The snippet I use to render is as follows :
static void ModifyImage()
{
Image origImage;
if (File.Exists(path))
{
using (Stream s = new FileStream(path, FileMode.Open, FileAccess.Read))
{
origImage = Image.FromStream(s);
}
using (Image newImage = new Bitmap(path))
{
// Get the image's original width and height
int originalWidth = origImage.Width;
int originalHeight = origImage.Height;
// To preserve the aspect ratio
float ratio = Math.Min((float)originalWidth, (float)originalHeight);
Graphics graphics = Graphics.FromImage(newImage);
graphics.SmoothingMode = SmoothingMode.HighQuality;
graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
graphics.TextRenderingHint = TextRenderingHint.AntiAliasGridFit;
string strSoftwareVersion = "Version " + StrVersion;
var oVersionFont = new Font("Arial", 15f, FontStyle.Bold);
SizeF strSize = graphics.MeasureString(strSoftwareVersion, oVersionFont);
float mX = (float)((originalWidth * 0.85) - 5.0);
float mY = (float)((originalHeight * 0.85) - 5.0);
float mWidth = (float) (strSize.Width + 5.0);
float mHeight = (float)(strSize.Height + 10.0);
/*
* Alternate I tried.
*/
//GraphicsPath blackfont = new GraphicsPath();
//SolidBrush brsh = new SolidBrush(Color.White);
//blackfont.AddString("TEST APP", oVersionFont.FontFamily, (int)FontStyle.Bold, 15, new Point((int)(originalWidth * 0.85), (int)(originalHeight * 0.85)), StringFormat.GenericDefault);
//graphics.FillPath(brsh, blackfont);
/*
*Software Version String
*/
graphics.DrawString("Version " + StrVersion, new Font("Arial", 15f, FontStyle.Bold), Brushes.White, (int)(originalWidth * 0.85), (int)(originalHeight * 0.85));
/*
* Save processed image
*/
newImage.Save(newPath, ImageFormat.Jpeg);
}
origImage.Dispose();
}
}
As you can see from the image, the blurriness of the text and surrounding areas are quite prominent. I'm quite sure this is due to the Aliasing or TextRendering issue with the alpha levels, but just not able to point the exact issue.
The text looks fine to me.
To make it crispier you can turn off all antialiasing.
The 'surrounding areas' obviously show some jpeg artifacts.
Good text and jpeg don't go together well.
Either turn up jpeg quality settings up from the default (of around 75%) or go for png!
Note that 90-95% quality, while greatly improving text quality also greatly enlarges size and going for png may well save you space in addition to being lossless..
I had a similar issue with the text being quite badly aliased when drawn onto the image. This is my solution which produces a reasonable text quality.
"text" is a System.Windows.Media.FormattedText instance.
BitmapFrame originalImageSource = BitmapFrame.Create(new Uri(file.FullName), BitmapCreateOptions.None, BitmapCacheOption.OnLoad);
var visual = new DrawingVisual();
using(DrawingContext drawingContext = visual.RenderOpen()) {
drawingContext.DrawImage(originalImageSource, new Rect(0, 0, originalImageSource.PixelWidth, originalImageSource.PixelHeight));
drawingContext.DrawText(text, topRight);
}
var renderTargetBitmap = new RenderTargetBitmap(originalImageSource.PixelWidth,
originalImageSource.PixelHeight,
originalImageSource.DpiX, originalImageSource.DpiY,
PixelFormats.Pbgra32);
renderTargetBitmap.Render(visual);
BitmapFrame bitmapFrame = BitmapFrame.Create(renderTargetBitmap);
BitmapEncoder encoder = new PngBitmapEncoder();
encoder.Frames.Add(bitmapFrame);
encoder.Save(stream);
Does anyone have a tip whereas you could dynamically resize a font to fit a specific area? For example, I have an 800x110 rectangle and I want to fill it with the max size font that would support the entire string I'm trying to display.
Bitmap bitmap = new Bitmap(800, 110);
using (Graphics graphics = Graphics.FromImage(bitmap))
using (Font font1 = new Font("Arial", 120, FontStyle.Regular, GraphicsUnit.Pixel))
{
Rectangle rect1 = new Rectangle(0, 0, 800, 110);
StringFormat stringFormat = new StringFormat();
stringFormat.Alignment = StringAlignment.Center;
stringFormat.LineAlignment = StringAlignment.Center;
graphics.TextRenderingHint = TextRenderingHint.ClearTypeGridFit;
graphics.DrawString("Billy Reallylonglastnameinstein", font1, Brushes.Red, rect1, stringFormat);
}
bitmap.Save(Server.MapPath("~/Fonts/" + System.Guid.NewGuid() + ".png"));
Obviously that whole name won't render in the space provided at the large font size. There has to be a simple way to do this?
You should do a scale transform on Font.Size the following function is an example of doing that but you can improve it to apply better results.
Here is FindFont function which get a room and a text with prefered size and gives you a font in which you can set that whole text fits the room!
// This function checks the room size and your text and appropriate font
// for your text to fit in room
// PreferedFont is the Font that you wish to apply
// Room is your space in which your text should be in.
// LongString is the string which it's bounds is more than room bounds.
private Font FindFont(
System.Drawing.Graphics g,
string longString,
Size Room,
Font PreferedFont
) {
// you should perform some scale functions!!!
SizeF RealSize = g.MeasureString(longString, PreferedFont);
float HeightScaleRatio = Room.Height / RealSize.Height;
float WidthScaleRatio = Room.Width / RealSize.Width;
float ScaleRatio = (HeightScaleRatio < WidthScaleRatio)
? ScaleRatio = HeightScaleRatio
: ScaleRatio = WidthScaleRatio;
float ScaleFontSize = PreferedFont.Size * ScaleRatio;
return new Font(PreferedFont.FontFamily, ScaleFontSize);
}
For your question you can call it like the following code:
Bitmap bitmap = new Bitmap(800, 110);
using (System.Drawing.Graphics graphics = System.Drawing.Graphics.FromImage(bitmap))
using (Font font1 = new Font("Arial", 120, FontStyle.Regular, GraphicsUnit.Pixel))
{
Rectangle rect1 = new Rectangle(0, 0, 800, 110);
StringFormat stringFormat = new StringFormat();
stringFormat.Alignment = StringAlignment.Center;
stringFormat.LineAlignment = StringAlignment.Center;
graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.ClearTypeGridFit;
Font goodFont = FindFont(graphics, "Billy Reallylonglastnameinstein", rect1.Size, font1);
graphics.DrawString(
"Billy Reallylonglastnameinstein",
goodFont,
Brushes.Red,
rect1,
stringFormat
);
}
I've adapted Saeed's great function to more suit my requirements. Comments explain all:
// You hand this the text that you need to fit inside some
// available room, and the font you'd like to use.
// If the text fits nothing changes
// If the text does not fit then it is reduced in size to
// make it fit.
// PreferedFont is the Font that you wish to apply
// FontUnit is there because the default font unit is not
// always the one you use, and it is info required in the
// constructor for the new Font.
public static void FindGoodFont(Graphics Graf, string sStringToFit,
Size TextRoomAvail,
ref Font FontToUse,
GraphicsUnit FontUnit)
{
// Find out what the current size of the string in this font is
SizeF RealSize = Graf.MeasureString(sStringToFit, FontToUse);
Debug.WriteLine("big string is {0}, orig size = {1},{2}",
sStringToFit, RealSize.Width, RealSize.Height);
if ((RealSize.Width <= TextRoomAvail.Width) && (RealSize.Height <= TextRoomAvail.Height))
{
Debug.WriteLine("The space is big enough already");
// The current font is fine...
return;
}
// Either width or height is too big...
// Usually either the height ratio or the width ratio
// will be less than 1. Work them out...
float HeightScaleRatio = TextRoomAvail.Height / RealSize.Height;
float WidthScaleRatio = TextRoomAvail.Width / RealSize.Width;
// We'll scale the font by the one which is furthest out of range...
float ScaleRatio = (HeightScaleRatio < WidthScaleRatio) ? ScaleRatio = HeightScaleRatio : ScaleRatio = WidthScaleRatio;
float ScaleFontSize = FontToUse.Size * ScaleRatio;
Debug.WriteLine("Resizing with scales {0},{1} chose {2}",
HeightScaleRatio, WidthScaleRatio, ScaleRatio);
Debug.WriteLine("Old font size was {0}, new={1} ",FontToUse.Size,ScaleFontSize);
// Retain whatever the style was in the old font...
FontStyle OldFontStyle = FontToUse.Style;
// Get rid of the old non working font...
FontToUse.Dispose();
// Tell the caller to use this newer smaller font.
FontToUse = new Font(FontToUse.FontFamily,
ScaleFontSize,
OldFontStyle,
FontUnit);
}
This is just an update for #Saeed's FindFont function.
GraphicsUnit.Pixel needs to be added to FindFont function return line. Without GraphicsUnit.Pixel, system dpi will effect the drawn string. Problem will arise when the dpi of system and bitmap mismatches. You can see more detail in this Windows DPI setting affects Graphics.DrawString. Since GraphicsUnit of PreferedFont is already set to GraphicsUnit.Pixel and return font is not set with GraphicsUnit.Pixel. In this case text will go out of the Room dimension, if bitmap dpi is larger than system dpi and font size will go smaller than the expected size if bitmap dpi is smaller than system dpi. Here is the updated function.
private Font FindFont( System.Drawing.Graphics g , string longString , Size Room , Font PreferedFont)
{
SizeF RealSize = g.MeasureString(longString, PreferedFont);
float HeightScaleRatio = Room.Height / RealSize.Height;
float WidthScaleRatio = Room.Width / RealSize.Width;
float ScaleRatio = (HeightScaleRatio < WidthScaleRatio) ? ScaleRatio = HeightScaleRatio : ScaleRatio = WidthScaleRatio;
float ScaleFontSize = PreferedFont.Size * ScaleRatio;
return new Font(PreferedFont.FontFamily, ScaleFontSize,PreferedFont.Style,GraphicsUnit.Pixel);
}
I don't want to bash against saaeds solution which is probably pretty awesome, too. But I found another one on msdn: Dynamic Graphic Text Resizing which worked for me.
public Font GetAdjustedFont(Graphics GraphicRef, string GraphicString, Font OriginalFont, int ContainerWidth, int MaxFontSize, int MinFontSize, bool SmallestOnFail)
{
// We utilize MeasureString which we get via a control instance
for (int AdjustedSize = MaxFontSize; AdjustedSize >= MinFontSize; AdjustedSize--)
{
Font TestFont = new Font(OriginalFont.Name, AdjustedSize, OriginalFont.Style);
// Test the string with the new size
SizeF AdjustedSizeNew = GraphicRef.MeasureString(GraphicString, TestFont);
if (ContainerWidth > Convert.ToInt32(AdjustedSizeNew.Width))
{
// Good font, return it
return TestFont;
}
}
// If you get here there was no fontsize that worked
// return MinimumSize or Original?
if (SmallestOnFail)
{
return new Font(OriginalFont.Name,MinFontSize,OriginalFont.Style);
}
else
{
return OriginalFont;
}
}
Here is my solution that support wrapping.
public static Font GetAdjustedFont(Graphics graphic, string str, Font originalFont, Size containerSize)
{
// We utilize MeasureString which we get via a control instance
for (int adjustedSize = (int)originalFont.Size; adjustedSize >= 1; adjustedSize--)
{
var testFont = new Font(originalFont.Name, adjustedSize, originalFont.Style, GraphicsUnit.Pixel);
// Test the string with the new size
var adjustedSizeNew = graphic.MeasureString(str, testFont, containerSize.Width);
if (containerSize.Height > Convert.ToInt32(adjustedSizeNew.Height))
{
// Good font, return it
return testFont;
}
}
return new Font(originalFont.Name, 1, originalFont.Style, GraphicsUnit.Pixel);
}
How to use:
var font = GetAdjustedFont(drawing, text, originalfont, wrapSize);
drawing.DrawString(text, font, textBrush, new Rectangle(0, 0, wrapSize.Width, wrapSize.Height));
I want to add 2 string to an image as follows:
This is Text
------------
------------
------------
--Other-----
How do I use the largest font size possible without have the String go off the side of the image?
example: if text is too big then it goes off the image::
This is Text That is too big
------------
------------
------------
--Other-----
I wrote this script on my previous projects to fit some text into the image by calculating its dimensions for each font-size. when the font size is bigger than the image's width, it lowers the font size by 0.1em and tries again until the text fits in the image. Here's the code :
public static string drawTextOnMarker(string markerfile, string text, string newfilename,Color textColor)
{
//Uri uri = new Uri(markerfile, UriKind.Relative);
//markerfile = uri.AbsolutePath;
//uri = new Uri(newfilename, UriKind.Relative);
//newfilename = uri.AbsolutePath;
if (!System.IO.File.Exists(System.Web.HttpContext.Current.Server.MapPath(newfilename)))
{
try
{
Bitmap bmp = new Bitmap(System.Web.HttpContext.Current.Server.MapPath(markerfile));
Graphics g = Graphics.FromImage(bmp);
g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAliasGridFit;
StringFormat strFormat = new StringFormat();
strFormat.Alignment = StringAlignment.Center;
SolidBrush myBrush = new SolidBrush(textColor);
float fontsize = 10;
bool sizeSetupCompleted = false;
while (!sizeSetupCompleted)
{
SizeF mySize = g.MeasureString(text, new Font("Verdana", fontsize, FontStyle.Bold));
if (mySize.Width > 24 || mySize.Height > 13)
{
fontsize-= float.Parse("0.1");
}
else
{
sizeSetupCompleted = true;
}
}
g.DrawString(text, new Font("Verdana", fontsize, FontStyle.Bold), myBrush, new RectangleF(4, 3, 24, 8), strFormat);
bmp.Save(System.Web.HttpContext.Current.Server.MapPath(newfilename));
return newfilename.Substring(2);
}
catch (Exception)
{
return markerfile.Substring(2);
}
}
return newfilename.Substring(2);
}
Here is a quick solution:
using (Graphics g = Graphics.FromImage(bmp))
{
float width = g.MeasureString(text, font).Width;
float scale = bmp.Width / width;
g.ScaleTransform(scale, scale); //Simple trick not to use other Font instance
g.DrawString(text, font, Brushes.Black, PointF.Empty);
g.ResetTransform();
...
}
Your text won't be always 100% width if you use TextRenderingHint.AntiAliasGridFit or similar, but I think that's not a problem as you just want to make sure the text alawys fits in the image.