I'm trying to add multiple watermarks to an image with some gap in between. I have been able to achieve this with two small caveats though.
What I want is:
Watermark should be vertically center aligned.
The program need to stop adding watermark when there is no sufficient width and/or height left in the image (so that there is no cutting of text).
My code is:
static void WatermarkedImage(string path, string fileName, string message, string destFileName)
{
using (Image image = Image.FromFile(path + fileName))
{
Graphics graphics = Graphics.FromImage(image);
Font font = new Font("Arial", 20, FontStyle.Bold);
SolidBrush brush = new SolidBrush(Color.FromArgb(150, 255, 0, 0));
StringFormat format = new StringFormat();
format.Alignment = StringAlignment.Center;
format.FormatFlags = StringFormatFlags.MeasureTrailingSpaces;
int index = 0;
float offsetX = image.Width / 3;
float offsetY = image.Height / 3;
int increment = Convert.ToInt32(image.Height * 0.15);
while (offsetY * 1.25 < image.Height)
{
Matrix matrix = new Matrix();
matrix.Translate(offsetX, offsetY);
matrix.Rotate(-45.0f);
graphics.Transform = matrix;
graphics.DrawString(message, font, brush, 0, 50, format);
offsetX += increment;
offsetY += increment;
index++;
}
image.Save(path + destFileName);
}
}
This is what I'm getting with this:
Desired Output.
Any help is much appreciated.
Thank You!
Update-1
To check if all of the text is inside the image, use MeasureString to see how large the string will be. From this you can construct a rectangle, transform each corner of said rectangle using the matrix, and check if they are all inside the image.
Your desired output does not have any offset on the x-axis, so you shouldn't increment x offset at all.
offsetY += increment;
index++;
}
while (offsetY * 1.25 < image.Height);
image.Save(path + destFileName);
Related
Hello I am printing a text on Receipt printer
on which I have to print Product name But products name get overwrites on Qty field can i get that name string in new line after particular length I am using following Code.
column 3 have a Product name Like (Almond Kesar Kanti Body Cleanser) How to get this text in new line and manage space of Invoice.
while (i < dataGridView2.Rows.Count)
{
if (height > e.MarginBounds.Height)
{
height = 500;
width = 500;
//e.HasMorePages = true;
return;
}
//height += dataGridView1.Rows[i].Height;
// e.Graphics.DrawRectangle(Pens.Black, 20, height, dataGridView1.Columns[0].Width, dataGridView1.Rows[0].Height);
e.Graphics.DrawString(dataGridView2.Rows[i].Cells["Column3"].FormattedValue.ToString(), dataGridView2.Font, Brushes.Black, CurrentX, CurrentY + 20);
e.Graphics.DrawString(dataGridView2.Rows[i].Cells["Column4"].FormattedValue.ToString(), dataGridView2.Font, Brushes.Black, CurrentX + 150, CurrentY + 20);
// e.Graphics.DrawString(dataGridView2.Rows[i].Cells["Column5"].FormattedValue.ToString(), dataGridView2.Font, Brushes.Black, CurrentX + 150, CurrentY + 20);
e.Graphics.DrawString(dataGridView2.Rows[i].Cells["Column10"].FormattedValue.ToString(), dataGridView2.Font, Brushes.Black, CurrentX + 200, CurrentY + 20);
i++;
CurrentY = CurrentY + 20;
}
You are not actually incrementing your CurrentY variable between the calls to DrawString. Therefore each line is printed at the same Y-coordinate.
Add CurrentY = CurrentY + 20; between each call to DrawString and just use
e.Graphics.DrawString(dataGridView2.Rows[i].Cells["Column3"].FormattedValue.ToString(), dataGridView2.Font, Brushes.Black, CurrentX, CurrentY);
Better yet, don't increment it by a fixed value but use e.Graphics.MeasureString to calculate the actual height of each line and increment by this value+spacing, like in this example:
Bitmap bmp = new Bitmap(200, 200);
using (Graphics g = Graphics.FromImage(bmp)) {
//The individual lines to draw
string[] lines = {
"Foo1",
"Foo2",
"Foo3"
};
int y = 1; //The starting Y-coordinate
int spacing = 10; //The space between each line in pixels
for (i = 0; i <= lines.Count - 1; i++) {
g.DrawString(lines[i], this.Font, Brushes.Black, 1, y);
//Increment the coordinate after drawing each line
y += Convert.ToInt32(g.MeasureString(lines[i], this.Font).Height) + spacing;
}
}
PictureBox1.Image = bmp;
Results:
Edit
To fit text into a given area you need to use a different overload of DrawString. You need to draw the string with a given LayoutRectangle like this:
Bitmap bmp = new Bitmap(200, 200);
using (Graphics g = Graphics.FromImage(bmp)) {
g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAliasGridFit;
string LongText = "This is a really long text that should be automatically wrapped to a certain rectangle.";
Rectangle DestinationRectangle = new Rectangle(10, 10, 100, 150);
g.DrawRectangle(Pens.Red, DestinationRectangle);
using (StringFormat sf = new StringFormat()) {
g.DrawString(LongText, new Font("Arial", 9), Brushes.Black, DestinationRectangle, sf);
}
}
PictureBox1.Image = bmp;
The DestinationRectangle defines where the text is printed and it is automatically wrapped. The problem is, that the line spacing is defined by the Font you use, as you can see in this example with different fonts:
If you can't find a font that works for you (or define your own) you would need to split the text into words and fit them yourself into the given rectangle by drawing them word for word, measuring each with the MeasureString function and break the line when you would go over the limit.
But beware, text layout is hard, very very hard, to get all the corner cases right.
I have a method that takes in a bitmap, width, height, and a list of strings, overlays it on top of a black background, and adds y-axis text onto it (as demonstrated in the picture below).
In my attempt to align the text so they occupy the exact vertical space of my graph, I took the height and divided it by the number of values in the list:
float verticalDistance = height / times.Count;
However, perhaps of alignment issues, the space between each time value on the y-axis doesn't fix exactly into the height of the original bitmap, which is the variable height.
I thought it was an issue with int and it rounding numbers up or down, but changing it verticalDistance to a float did not ameliorate the issue.
private Bitmap overlayBitmap(Bitmap sourceBMP, int width, int height, List<String> times) {
int newWidth = width + (width / 3);
int newHeight = height + (height / 3);
Bitmap result = new Bitmap(newWidth, newHeight);
using (Graphics g = Graphics.FromImage(result)) {
g.FillRectangle(Brushes.Black, 0, 0, newWidth, newHeight);
g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor;
StringFormat stringFormatX = new StringFormat();
stringFormatX.LineAlignment = StringAlignment.Center;
Font drawFontX = new Font("Whitney", 10);
float verticalDistance = height / times.Count;
for (int i = 0; i < times.Count; i++) {
if (i % 5 == 0) {
g.DrawString(times[i], drawFontX, Brushes.White, 5, ((newHeight - height)/2) + (verticalDistance * i), stringFormatX);
}
}
g.DrawImage(sourceBMP, width / 6, height / 6, width, height);
}
return result;
}
What could be the issue here?
Reduce your problem to the simplest case: What if you had just two values to display on your Y axis? Your vertical distance would be the length of the axis. Your vertical distance formula would not work in that case. Therefore
float verticalDistance = height / (times.Count - 1.0);
I am trying to create a captcha image. I am generating a random string and rotating the text with a random angle and trying to create a byte array. Below is my code snippet:
Image img = Image.FromFile(#"C:\Images\BackGround.jpg");
RectangleF myRect = new RectangleF(0, 0, width, height);
objGraphics.DrawImage(img, myRect);
Matrix myMatrix = new Matrix();
int i = 0;
StringFormat formatter = new StringFormat();
formatter.Alignment = StringAlignment.Center;
for (i = 0; i <= myString.Length - 1; i++)
{
myMatrix.Reset();
int charLenght = myString.Length;
float x = width / (charLenght + 1) * i;
float y = height / 30F;
myMatrix.RotateAt(oRandom.Next(-40, 40), new PointF(x, y));
objGraphics.Transform = myMatrix;
objGraphics.DrawString(myString.Substring(i, 1), MyFont, MyFontEmSizes, MyFontStyles,
MySolidBrush, x, Math.Max(width, height) / 50, formatter );
objGraphics.ResetTransform();
}
Every thing is working fine, except that, the first character in my final image on the web page is crossing my left border of the rectangle. How can I align my text to the center of the rectangle?
Thanks.
I am trying to add a text scale to a color image.
The agcScale.jpg image (below) is 2 winform labels on the top and bottom and 2 winform pictureboxes on the left and right.
The exact same code was used to produce the strings in the right and left pictureboxes, the only difference is that pictureBoxAgcVscale contains only the strings.
Why does DrawString in pictureBoxAgc look fine but DrawString in pictureBoxAgcVscale look so bad? I can probably fix pictureBoxAgcVscale by doing a bmp.SetPixel for each pixel but that seems like the wrong way to fix this.
private void DisplayAgcVscale(double min, double max)
{
var bmp = new Bitmap(pictureBoxAgcVscale.Width, pictureBoxAgcVscale.Height);
var c = (max - min) / bmp.Height;
using (var g = Graphics.FromImage(bmp))
{
var font = new Font("Microsoft Sans Serif", 8.25F);
var y1 = bmp.Height / 10;
for (var y = y1; y < bmp.Height; y += y1)
{
var agc = y * c + min;
var text = agc.ToString("#0.000V");
var h = bmp.Height - y - font.Height / 2;
g.DrawString(text, font, Brushes.Black, 0, h);
}
}
pictureBoxAgcVscale.Image = bmp;
}
You are drawing black text on a transparent background. The anti-aliasing pixels are fading from black to black, no choice, turning the letters into blobs. It works for the text on the left because you draw the pixels first.
You forgot g.Clear().
I had a similar issue, but in a listbox, and it wasn't resolved by clearing the rectangle. I had to apply a "TextRenderingHint":
e.Graphics.TextRenderingHint = TextRenderingHint.AntiAlias;
e.Graphics.DrawString(listText, myFont, myBrush, e.Bounds, StringFormat.GenericDefault);
I have a label with unknown text. the text contains \n. I want a bigger space between the \n lines. How can I set it?
You can create custom label component and override it's measuring and painting, here you can find a snippet of a paint method which implements line spacing.
Edit: adding a snippet here in case the link is dead:
string text = "Sri Lanka";
Graphics g = e.Graphics;
Font font = new Font("Arial", 10);
Brush brush = new SolidBrush(Color.Black);
float lineSpacing = 0.5f;
SizeF size = g.MeasureString("A", font);
float pos = 0.0f;
for ( int i = 0; i < text.Length; ++i )
{
string charToDraw = new string(textIdea, 1);
g.DrawString(charToDraw, font, brush, pos, 0.0f);
SizeF sizeChar = g.MeasureString(charToDraw, font);
pos += sizeChar.Width + size.Width * lineSpacing;
}