Align text to center in System.Drawing.RectangleF - c#

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.

Related

How to align watermark in an image

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);

How do I determine the rotation point of a polygon (triangle)?

I would like to draw a thick, transparent arrow with an arrowhead:
Here's the code that draws the arrow shaft. Notice that I have to offset the rectangle so the calculations are done from the midpoint of the rectangle.
private void DrawMovementArrow(bool color, double StartX, double StartY, double EndX, double EndY)
{
SolidColorBrush partiallyTransparentSolidColorBrush;
Rectangle myRectangle = new Rectangle();
// This will be replaced by piece size
int width = 35;
myRectangle.Width = width;
// Apparently necessary to offset the drawing of the path so that the point is in the center of the path; not the edge.
StartX -= width / 2;
EndX -= width / 2;
myRectangle.Height = Map.EuclideanDistance(StartX, StartY, EndX, EndY) ;
int angle = CalculateAngle(StartX , StartY , EndX , EndY );
// This selects the midpoint of edge of the rectangle to rotate around (weird system)
myRectangle.RenderTransformOrigin = new Point(0.5, 0);
angle = angle - 180;
RotateTransform rotateTransform1 = new RotateTransform(angle, 0 , 0 );
myRectangle.RenderTransform = rotateTransform1;
if (color)
partiallyTransparentSolidColorBrush = new SolidColorBrush(Colors.Blue);
else
partiallyTransparentSolidColorBrush = new SolidColorBrush(Colors.Red);
partiallyTransparentSolidColorBrush.Opacity = 0.4;
myRectangle.Fill = partiallyTransparentSolidColorBrush;
MovementCanvas1.Children.Clear();
MovementCanvas1.Children.Add(myRectangle);
Canvas.SetTop(myRectangle, StartY);
Canvas.SetLeft(myRectangle, StartX);
DrawArrowhead(color, EndX, EndY, angle + 90, width);
ShowUnitCenter(MovementArrowList[0]);
}
Note that this code selects a point in the middle of the edge to rotate the rectangle:
// This selects the midpoint of edge of the rectangle to rotate around (weird system)
myRectangle.RenderTransformOrigin = new Point(0.5, 0);
The problem is that I can't find that point with the arrowhead (triangle). Here's the code that draws the arrowhead:
public void DrawArrowhead(bool color, double x, double y, int angle, int width)
{
x += width /2 ;
width = width + (width / 2);
//Add the Polygon Element
Polygon myPolygon = new Polygon();
myPolygon.Opacity = 0.4;
if (color)
{
myPolygon.Fill = new SolidColorBrush(Colors.Blue);
myPolygon.Stroke = System.Windows.Media.Brushes.Blue;
}
else
{
myPolygon.Fill = new SolidColorBrush(Colors.Red);
myPolygon.Stroke = System.Windows.Media.Brushes.Red;
}
myPolygon.StrokeThickness = 0;
RotateTransform rotateTransform1 = new RotateTransform(angle, 0, 0);
myPolygon.RenderTransform = rotateTransform1;
// This selects the midpoint of edge of the triangle to rotate around (weird system)
myPolygon.RenderTransformOrigin = new Point(0.0, 0.5);
System.Windows.Point Point1 = new System.Windows.Point(0, 0);
System.Windows.Point Point2 = new System.Windows.Point(width / 2, width / 2);
System.Windows.Point Point3 = new System.Windows.Point(0,width);
PointCollection myPointCollection = new PointCollection();
myPointCollection.Add(Point1);
myPointCollection.Add(Point2);
myPointCollection.Add(Point3);
myPolygon.Points = myPointCollection;
MovementCanvas1.Children.Add(myPolygon);
Canvas.SetTop(myPolygon, y );
Canvas.SetLeft(myPolygon, x );
}
Note the myPointCollection that creates the triangle. The problem is that I've tried almost every conceivable combination of values in RenderTransformOrigin to find the point that (center bottom of triangle) to use for the rotation point. Nothing seems to be working out.
Can anybody suggest the correct value?
Edit Solved
I solved it by changing the points of the triangle. That was easier than trying to figure out the rotation point.
Changing the points that made up the triangle solved the problem. This was easier than trying to find the rotation point.

Calculating text width for DXF

I am using netDXF (https://netdxf.codeplex.com/) to generate a DXF file for use with AutoCAD. However, I have an issue with getting the width of MText correct. I want to be able to define a width that the text should fit into, and change the width factor of the text (squash it horizontally) so that it fits in the defined area. So if I have a 40mm width to fit the text into and the text is 80mm long, it needs to have a width factor of 0.5. The only problem is that I don't know how to accurately determine the width of the text. I have tried the following methods and was unsuccessful in getting the correct result:
Why is Graphics.MeasureString() returning a higher than expected number?
Measure a String without using a Graphics object?
http://www.codeproject.com/Articles/2118/Bypass-Graphics-MeasureString-limitations
I have attached my code. I am basically printing a horizontal line using each of the 3 methods to calculate text width and comparing it to the actual text width. If I change the font, I get varying results. I have attached two images. One using the code with Calibri and one with Arial. I need the line to be on the edges of the text no matter what font I use.
Here is my code:
public void TestMethod1()
{
Application.SetCompatibleTextRenderingDefault(false);
//text width in mm
float textWidth = 40;
float textHeight = 200;
string labelText = "HELLO WORLD!";
TextStyle textStyle = new TextStyle("Calibri");
DxfDocument dxf = new DxfDocument();
Layer layer1 = new Layer("layer1");
layer1.Color = new AciColor(0, 0, 255);
layer1.Name = "Text";
MText text1 = new MText(new Vector2(0, 0), textHeight, 0, textStyle);
text1.Layer = layer1;
text1.AttachmentPoint = MTextAttachmentPoint.MiddleCenter;
//Will the text fit in the bounds of the rectangle? If not change width factor so it does.
Font f = new Font(textStyle.FontName, textHeight);
Size size = TextRenderer.MeasureText(labelText, f);
SizeF sizeF = graphicsMeasureString(labelText, f);
int width = MeasureDisplayStringWidth(labelText, f);
float widthFactor = Math.Min(1, textWidth / sizeF.Width);
MTextFormattingOptions mtextOptions = new MTextFormattingOptions(text1.Style);
//mtextOptions.WidthFactor = widthFactor;
text1.Write(labelText, mtextOptions);
//Red, g.MeasureString
Line line1 = new Line(new Vector2(0 - sizeF.Width / 2, 0), new Vector2(0 + sizeF.Width / 2, 0));
line1.Color = new AciColor(255, 0, 0);
//Green, TextRenderer
Line line2 = new Line(new Vector2(0 - size.Width / 2, 5), new Vector2(0 + size.Width / 2, 5));
line2.Color = new AciColor(0, 255, 0);
//Yellow, MeasureDisplayStringWidth
Line line3 = new Line(new Vector2(0 - width / 2, -5), new Vector2(0 + width / 2, -5));
line3.Color = new AciColor(255, 255, 0);
dxf.AddEntity(text1);
dxf.AddEntity(line1);
dxf.AddEntity(line2);
dxf.AddEntity(line3);
dxf.Save("Text Width Test.dxf");
}
public SizeF graphicsMeasureString(string text, Font f)
{
Bitmap fakeImage = new Bitmap(1, 1);
Graphics g = Graphics.FromImage(fakeImage);
SizeF sizeF = g.MeasureString(text, f, new PointF(100, 0), StringFormat.GenericTypographic);
return sizeF;
}
public int MeasureDisplayStringWidth(string text, Font f)
{
Size size = TextRenderer.MeasureText(text, f);
Bitmap fakeImage = new Bitmap(1, 1);
Graphics g = Graphics.FromImage(fakeImage);
System.Drawing.StringFormat format = new System.Drawing.StringFormat();
System.Drawing.RectangleF rect = new System.Drawing.RectangleF(0, 0, 1000, 1000);
System.Drawing.CharacterRange[] ranges = { new System.Drawing.CharacterRange(0, text.Length) };
System.Drawing.Region[] regions = new System.Drawing.Region[1];
format.SetMeasurableCharacterRanges(ranges);
regions = g.MeasureCharacterRanges(text, f, rect, format);
rect = regions[0].GetBounds(g);
return (int)(rect.Right + 1.0f);
}

In my heatmap graphing component, the y-axis time text does not exactly align

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);

Drawing a polygon according to the input coordinates

How can i draw a polygon according to the input coordinates which are given in C#.
You didn't show any code because based on those coordinate, you are applying some form of scaling to the image.
Using the Paint event of a PictureBox, here is an example using those coordinates on the screen. It fills in the polygon, then draws the border, then it loops through all the points to draw the red circle:
void pictureBox1_Paint(object sender, PaintEventArgs e) {
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
e.Graphics.Clear(Color.White);
// draw the shading background:
List<Point> shadePoints = new List<Point>();
shadePoints.Add(new Point(0, pictureBox1.ClientSize.Height));
shadePoints.Add(new Point(pictureBox1.ClientSize.Width, 0));
shadePoints.Add(new Point(pictureBox1.ClientSize.Width,
pictureBox1.ClientSize.Height));
e.Graphics.FillPolygon(Brushes.LightGray, shadePoints.ToArray());
// scale the drawing larger:
using (Matrix m = new Matrix()) {
m.Scale(4, 4);
e.Graphics.Transform = m;
List<Point> polyPoints = new List<Point>();
polyPoints.Add(new Point(10, 10));
polyPoints.Add(new Point(12, 35));
polyPoints.Add(new Point(22, 35));
polyPoints.Add(new Point(24, 22));
// use a semi-transparent background brush:
using (SolidBrush br = new SolidBrush(Color.FromArgb(100, Color.Yellow))) {
e.Graphics.FillPolygon(br, polyPoints.ToArray());
}
e.Graphics.DrawPolygon(Pens.DarkBlue, polyPoints.ToArray());
foreach (Point p in polyPoints) {
e.Graphics.FillEllipse(Brushes.Red,
new Rectangle(p.X - 2, p.Y - 2, 4, 4));
}
}
}
You may use Graphics.DrawPolygon. You can store the coordinates in an array of Point and then you can pass that to DrawPolygon method. You may wanna see:
Drawing with Graphics in WinForms using C#
private System.Drawing.Graphics g;
System.Drawing.Point[] p = new System.Drawing.Point[6];
p[0].X = 0;
p[0].Y = 0;
p[1].X = 53;
p[1].Y = 111;
p[2].X = 114;
p[2].Y = 86;
p[3].X = 34;
p[3].Y = 34;
p[4].X = 165;
p[4].Y = 7;
g = PictureBox1.CreateGraphics();
g.DrawPolygon(pen1, p);
This simple function is able to generate an array of PointF equal to the vertices of the regular polygon to be drawn, where "center" is the center of the polygon, "sides" is its number of sides, "sideLength" is the size of each side in pixels and "offset" is its slope.
public PointF[] GetRegularPolygonScreenVertex(Point center, int sides, int sideLength, float offset)
{
var points = new PointF[sides];
for (int i = 0; i < sides; i++)
{
points[i] = new PointF(
(float)(center.X + sideLength * Math.Cos((i * 360 / sides + offset) * Math.PI / 180f)),
(float)(center.Y + sideLength * Math.Sin((i * 360 / sides + offset) * Math.PI / 180f))
);
}
return points;
}
The result obtained can be used to draw a polygon, e.g. with the function:
GraphicsObject.DrawPolygon(new Pen(Brushes.Black, GetRegularPolygonScreenVertex(new Point(X, Y), 6, 30, 60f));
Which will generate a regular hexagon with a side of 30 pixels inclined by 30°.
hex

Categories

Resources