I use drawstring method to draw text inside rectangle ,.. i wanted to rotate the text 180 degree so i used this code
Rectangle displayRectangleh =
new Rectangle(new Point((int)posh.X, (int)posh.Y), new Size(rectwdh, recth));
StringFormat format1h = new StringFormat(StringFormatFlags.DirectionVertical);
format1h.LineAlignment = StringAlignment.Center;
format1h.Alignment = StringAlignment.Center;
b = new SolidBrush(Color.Black);
e.ChartGraphics.Graphics.TranslateTransform((float)rectwdh / 2 + posh.X, recth / 2 + posh.Y);
e.ChartGraphics.Graphics.RotateTransform(180);
e.ChartGraphics.Graphics.TranslateTransform(-((float)rectwdh / 2 + posh.X), -(recth / 2 + posh.Y));
Font boldFonth = new Font(FontFamily.GenericSansSerif, 16, FontStyle.Bold, GraphicsUnit.Pixel, 1, true);
e.ChartGraphics.Graphics.DrawRectangle(new Pen(Color.Black, 2), displayRectangleh);
e.ChartGraphics.Graphics.DrawString("This is rotated long text test ", boldFonth, b, (RectangleF)displayRectangleh, format1h);
e.ChartGraphics.Graphics.ResetTransform();
where posh.X and posh.Y (recatangle position) , rectwdh (rectangle width) and recth (rectangle height)
It works perfect for short text but for long text the new line will be above my old line see the next pic :
I even tried to add new line character \n but same result.
my question : how to rotate the text 180 inside rectangle and end up with proper alignment ??
Never having even used the StringFormat APIs before, I pounded at it randomly until I found this:
StringFormat format1h = new StringFormat(StringFormatFlags.DirectionVertical | StringFormatFlags.DirectionRightToLeft);
Related
What I am trying to achieve is - I want to draw a text watermark on an image using GDI. This text watermark should be white and semi-transparent, shadow - grey semi-transparent.
I tried this solution but obviously, when I draw a white semi-transparent text on top of a grey semi-transparent text, I see no white text at all, because grey pops up.
Would be very helpful if anyone could guide me through this, as I have no experience with graphics at all.
What I also tried, except of this:
drawing.DrawString(text, font, shadowBrush, x + shadowOffset.Width, y + shadowOffset.Height);
drawing.DrawString(text, font, textBrush, x, y);
Is, I tried to apply the outlining here. I thought that, maybe it is possible to apply the outlining at only one side so it would look like a shadow, but I failed on it either.
Here's the code I have so far:
var scalingRatio = GetWatermarkScalingRatio(image);
var scaledFontSize = (int)Math.Ceiling(scalingRatio * textProperties.Size);
var fontFamily = textProperties.Font ?? DefaultFont;
var alpha = (int)(255.0f * textProperties.Opacity / 100.0f);
var color = Color.FromArgb(alpha, Color.White);
using (var gr = Graphics.FromImage(image))
using (var font = new Font(fontFamily, scaledFontSize, DefaultFontStyle, GraphicsUnit.Pixel))
using (var semiTransparentBrush = new SolidBrush(color))
using (var shadowBrush = new SolidBrush(_shadowBrushColor))
{
var textSize = gr.MeasureString(textProperties.Message, font);
double wmWidth = textSize.Width;
double wmHeight = textSize.Height;
double angleRadian = (DefaultTextAngle % 360 / 180.0) * Math.PI;
var offset = GetTxtWatermarkOffset(textProperties.Position, image, wmWidth, wmHeight, angleRadian);
using var m = gr.Transform;
m.RotateAt(DefaultTextAngle, new PointF(offset.X, offset.Y), MatrixOrder.Append);
gr.Transform = m;
gr.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAliasGridFit;
gr.DrawString(textProperties.Message, font, shadowBrush, offset.X, offset.Y + DefaultShadowOffset);
gr.DrawString(textProperties.Message, font, semiTransparentBrush, offset.X, offset.Y);
gr.ResetTransform();
}
I have a class for writing texts on screen or printer.
public void draw(float scale, Graphics g)
{
Font font = new Font(fontName, size, FontStyle.Regular, GraphicsUnit.Millimeter);
SolidBrush brush = new SolidBrush(Color.Black);
Pen pen = new Pen(Color.Black, 1);
PointF point = new PointF(0, 0);// new PointF(l + scale * x, t + scale * y);
rect = new Rectangle(0, 0, (int)(g.MeasureString(text, font).Width), (int)(g.MeasureString(text, font).Height));
System.Drawing.Drawing2D.Matrix m = g.Transform;
g.ScaleTransform(scale, scale);
g.TranslateTransform(x - rect.Width / 2, y - rect.Height / 2);
g.RotateTransform(angle);
g.DrawString(text, font, brush, point);
g.Transform = m;
}
The different textblocks are of different font sizes and placed at different x positions at the "page". I place two blocks of diffent size at an x where the text ends in the same position on the screen. When I print the "page" the two words don't end at the same position even if the center seem to be at the same position.
I have searched but not been able to find anything that resolves my problem.
I am not sure if this will help or not, but I learned long ago that printers compress spacing differently than screens do. For example, if you wrote a bunch of letters on a document with tabs and space bar spaces between them, it would look different on the screen than if you printed the document on paper. Maybe it gives you a place to start?
I want to distribute string in rectangle.
Except each character set position
Rectangle displayRectangle = new Rectangle (new Point(40, 40), new Size (80, 80));
StringFormat format1 = new StringFormat(StringFormatFlags.NoClip);
format1.LineAlignment = StringAlignment.Center;
e.Graphics.DrawRectangle(Pens.Black, displayRectangle);
e.Graphics.DrawString("Showing Format1", this.Font,
Brushes.Black, (RectangleF)displayRectangle, format1);
But, StringFormat Alignment doesn't have distribute alignment. So I want to know a way how to distribute string in rectangle.
For the moment, I'm going to assume you can/will use the Win32 API (e.g., via. P/Invoke). .NET may have a wrapper for the function I'm going to suggest (but then again, it may not -- I'm really not sure). If it does, it'll be up to you to find and use it. Most of what I'm suggesting is more about the basic approach than the function anyway.
You can use GetTextExtentExPointI, which will compute the size of a rectangle necessary to hold a set characters you specify and (importantly) the horizontal position of each character in that rectangle.
So, what you want to do is use this to compute the size of a rectangle and position of each character in that rectangle, with it assuming normal kerning of the characters. Then, you'll divide the width of that rectangle into the width you actually want. This will give you a factor by which each position must increase to get that character to the position you want. You'll then multiply the position it returned for each character by that factor to get your desired position.
Just for example, let's assume it gave you positions of 0, 17, 35 and 44 for the characters with normal spacing. Let's also assume your target rectangle is 1.8 times as wide as the rectangle it computed for normal spacing. You'll take each of those positions and multiply by 1.8 to get the position you want to use for that character, giving 0, 31, 63, and 79 for the "corrected" positions.
Then you'll (obviously enough) go through your string and draw each character at the computed position.
Here's how to do it if you just want to literally distribute the characters evenly across the middle of the display rectangle:
private void Form1_Paint(object sender, PaintEventArgs e)
{
string text = "this is distribute";
Rectangle displayRectangle = new Rectangle(new Point(40, 40), new Size(400, 80));
e.Graphics.DrawRectangle(Pens.Black, displayRectangle);
int step = displayRectangle.Width / text.Length;
SizeF szF = e.Graphics.MeasureString(text, this.Font); // just to get the HEIGHT
int y = (displayRectangle.Y + displayRectangle.Height / 2) - (int)szF.Height / 2;
for (int i = 0; i < text.Length; i++)
{
e.Graphics.DrawString(text.Substring(i, 1), this.Font, Brushes.Black, displayRectangle.X + (i * step), y);
}
}
Here's my stab at #Jerry Coffin's algorithm using .Net managed methods:
private void Form1_Paint(object sender, PaintEventArgs e)
{
string text = "this is distribute";
Rectangle displayRectangle = new Rectangle(new Point(40, 40), new Size(400, 80));
e.Graphics.DrawRectangle(Pens.Black, displayRectangle);
StringFormat format1 = new StringFormat(StringFormatFlags.NoClip);
format1.LineAlignment = StringAlignment.Center;
format1.Alignment = StringAlignment.Near;
// SetMeasurableCharacterRanges() can only handle 32 regions max at a time!
// The below workaround simply measures each character separately:
RectangleF rcF = (RectangleF)displayRectangle;
List<Region> regions = new List<System.Drawing.Region>();
for (int i = 0; i < text.Length; i++)
{
format1.SetMeasurableCharacterRanges(new CharacterRange[] {new CharacterRange(i, 1)});
regions.AddRange(e.Graphics.MeasureCharacterRanges(text, this.Font, rcF, format1));
}
RectangleF minBounds = regions[0].GetBounds(e.Graphics);
RectangleF maxBounds = regions[regions.Count - 1].GetBounds(e.Graphics);
float ratio = (float)displayRectangle.Width / (float)((maxBounds.X + maxBounds.Width) - minBounds.X);
for(int i = 0; i < regions.Count; i++)
{
Region region = regions[i];
RectangleF boundsF = region.GetBounds(e.Graphics);
PointF ptF = new PointF(displayRectangle.X + (int)((boundsF.Left - minBounds.X) * ratio), (int)boundsF.Top);
e.Graphics.DrawString(text.Substring(i, 1), this.Font, Brushes.Black, ptF);
}
}
I have my custom button where I have overridden the OnPaint() and draw the text in it only. On runtime the text looks differently - spacing between chars is lacking. Here is the image of design & runtime of the button :
The paint methods is as:
protected override void OnPaint(PaintEventArgs pevent)
{
base.OnPaint(pevent);
if (base.ContainsFocus)
{
// Draw inner dotted rectangle when button is on focus
Pen pen = new Pen(Color.Gray, 3);
Point p = base.Location;
pen.DashStyle = System.Drawing.Drawing2D.DashStyle.Dot;
Rectangle rectangle = new Rectangle(4, 4, Size.Width - 8,
Size.Height - 8);
ControlPaint.DrawFocusRectangle(pevent.Graphics, rectangle);
}
// Draw the string to screen
SizeF sf = pevent.Graphics.MeasureString(displayText, this.Font,
this.Width);
Point ThePoint = new Point();
ThePoint.X = (int)((this.Width / 2) - (sf.Width / 2));
ThePoint.Y = (int)((this.Height / 2) - (sf.Height / 2));
pevent.Graphics.DrawString(displayText, Font,
new SolidBrush(Color.FromArgb(255, 255, 254, 255)), ThePoint);
this.Text = "";
}
Any idea where am I going wrong and how to take care of the same?
You need to set the correct smoothing mode like this:
Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality
Then, the result should look OK.
Devils Child's answer will affect the quality of lines and circles, etc.
But for text rendering, you can use:
e.Graphics.TextRenderingHint = TextRenderingHint.ClearTypeGridFit;
I'm using the .NETCF (Windows Mobile) Graphics class and the DrawString() method to render a single character to the screen.
The problem is that I can't seem to get it centred properly. No matter what I set for the Y coordinate of the location of the string render, it always comes out lower than that and the larger the text size the greater the Y offset.
For example, at text size 12, the offset is about 4, but at 32 the offset is about 10.
I want the character to vertically take up most of the rectangle it's being drawn in and be centred horizontally. Here's my basic code. this is referencing the user control it's being drawn in.
Graphics g = this.CreateGraphics();
float padx = ((float)this.Size.Width) * (0.05F);
float pady = ((float)this.Size.Height) * (0.05F);
float width = ((float)this.Size.Width) - 2 * padx;
float height = ((float)this.Size.Height) - 2 * pady;
float emSize = height;
g.DrawString(letter, new Font(FontFamily.GenericSansSerif, emSize, FontStyle.Regular),
new SolidBrush(Color.Black), padx, pady);
Yes, I know there is the label control that I could use instead and set the centring with that, but I actually do need to do this manually with the Graphics class.
I'd like to add another vote for the StringFormat object.
You can use this simply to specify "center, center" and the text will be drawn centrally in the rectangle or points provided:
StringFormat format = new StringFormat();
format.LineAlignment = StringAlignment.Center;
format.Alignment = StringAlignment.Center;
However there is one issue with this in CF. If you use Center for both values then it turns TextWrapping off. No idea why this happens, it appears to be a bug with the CF.
To align a text use the following:
StringFormat sf = new StringFormat();
sf.LineAlignment = StringAlignment.Center;
sf.Alignment = StringAlignment.Center;
e.Graphics.DrawString("My String", this.Font, Brushes.Black, ClientRectangle, sf);
Please note that the text here is aligned in the given bounds. In this sample this is the ClientRectangle.
Through a combination of the suggestions I got, I came up with this:
private void DrawLetter()
{
Graphics g = this.CreateGraphics();
float width = ((float)this.ClientRectangle.Width);
float height = ((float)this.ClientRectangle.Width);
float emSize = height;
Font font = new Font(FontFamily.GenericSansSerif, emSize, FontStyle.Regular);
font = FindBestFitFont(g, letter.ToString(), font, this.ClientRectangle.Size);
SizeF size = g.MeasureString(letter.ToString(), font);
g.DrawString(letter, font, new SolidBrush(Color.Black), (width-size.Width)/2, 0);
}
private Font FindBestFitFont(Graphics g, String text, Font font, Size proposedSize)
{
// Compute actual size, shrink if needed
while (true)
{
SizeF size = g.MeasureString(text, font);
// It fits, back out
if (size.Height <= proposedSize.Height &&
size.Width <= proposedSize.Width) { return font; }
// Try a smaller font (90% of old size)
Font oldFont = font;
font = new Font(font.Name, (float)(font.Size * .9), font.Style);
oldFont.Dispose();
}
}
So far, this works flawlessly.
The only thing I would change is to move the FindBestFitFont() call to the OnResize() event so that I'm not calling it every time I draw a letter. It only needs to be called when the control size changes. I just included it in the function for completeness.
To draw a centered text:
TextRenderer.DrawText(g, "my text", Font, Bounds, ForeColor, BackColor,
TextFormatFlags.HorizontalCenter |
TextFormatFlags.VerticalCenter |
TextFormatFlags.GlyphOverhangPadding);
Determining optimal font size to fill an area is a bit more difficult. One working soultion I found is trial-and-error: start with a big font, then repeatedly measure the string and shrink the font until it fits.
Font FindBestFitFont(Graphics g, String text, Font font,
Size proposedSize, TextFormatFlags flags)
{
// Compute actual size, shrink if needed
while (true)
{
Size size = TextRenderer.MeasureText(g, text, font, proposedSize, flags);
// It fits, back out
if ( size.Height <= proposedSize.Height &&
size.Width <= proposedSize.Width) { return font; }
// Try a smaller font (90% of old size)
Font oldFont = font;
font = new Font(font.FontFamily, (float)(font.Size * .9));
oldFont.Dispose();
}
}
You'd use this as:
Font bestFitFont = FindBestFitFont(g, text, someBigFont, sizeToFitIn, flags);
// Then do your drawing using the bestFitFont
// Don't forget to dispose the font (if/when needed)
Here's some code. This assumes you are doing this on a form, or a UserControl.
Graphics g = this.CreateGraphics();
SizeF size = g.MeasureString("string to measure");
int nLeft = Convert.ToInt32((this.ClientRectangle.Width / 2) - (size.Width / 2));
int nTop = Convert.ToInt32((this.ClientRectangle.Height / 2) - (size.Height / 2));
From your post, it sounds like the ClientRectangle part (as in, you're not using it) is what's giving you difficulty.
You can use an instance of the StringFormat object passed into the DrawString method to center the text.
See Graphics.DrawString Method and StringFormat Class.