How to draw a rotating string as an image using System.Drawing?

I draw lines for images. The size of the images is dynamic, or in other words, the images are as large as necessary to display the strings. To achieve this, I measure the size with Graphics.MeasureString () before rendering the text with Graphics.DrawString ().

This is all fine until the spin comes into play. So far, I am drawing a string into a bitmap and rotate the whole bitmap that I get as a result.

The problem is that I have a very limited color palette and not mixed colors. Therefore, I should avoid any anti-aliasing, which is only possible with InterpolationMode.NearestNeighbor by rotating the text bitmap. Although this ensures that there is no unwanted color, the result is really very ugly (from the point of view of users).

My thought: it should be possible to draw text in a bitmap by rotating it with Graphics.RotateTransform () and avoiding cropping, right?

Because I have to determine the size of the image to draw first, and because this size is increased by rotation, I do not know how to do this.

Any help is much appreciated!

+6
c # system.drawing rotatetransform drawstring
source share
2 answers

My solution as MVC executable action:

public class ImageController : Controller { public ActionResult Test() { var text = DateTime.Now.ToString(); var font = new Font("Arial", 20, FontStyle.Regular); var angle = 233; SizeF textSize = GetEvenTextImageSize(text, font); SizeF imageSize; if (angle == 0) imageSize = textSize; else imageSize = GetRotatedTextImageSize(textSize, angle); using (var canvas = new Bitmap((int)imageSize.Width, (int)imageSize.Height)) { using(var graphics = Graphics.FromImage(canvas)) { graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; graphics.SmoothingMode = SmoothingMode.HighQuality; graphics.TextRenderingHint = TextRenderingHint.SingleBitPerPixelGridFit; SizeF textContainerSize = graphics.VisibleClipBounds.Size; graphics.TranslateTransform(textContainerSize.Width / 2, textContainerSize.Height / 2); graphics.RotateTransform(angle); graphics.DrawString(text, font, Brushes.Black, -(textSize.Width / 2), -(textSize.Height / 2)); } var stream = new MemoryStream(); canvas.Save(stream, ImageFormat.Png); stream.Seek(0, SeekOrigin.Begin); return new FileStreamResult(stream, "image/png"); } } private static SizeF GetEvenTextImageSize(string text, Font font) { using (var image = new Bitmap(1, 1, PixelFormat.Format32bppArgb)) { using (Graphics graphics = Graphics.FromImage(image)) { return graphics.MeasureString(text, font); } } } private static SizeF GetRotatedTextImageSize(SizeF fontSize, int angle) { // Source: http://www.codeproject.com/KB/graphics/rotateimage.aspx double theta = angle * Math.PI / 180.0; while (theta < 0.0) theta += 2 * Math.PI; double adjacentTop, oppositeTop; double adjacentBottom, oppositeBottom; if ((theta >= 0.0 && theta < Math.PI / 2.0) || (theta >= Math.PI && theta < (Math.PI + (Math.PI / 2.0)))) { adjacentTop = Math.Abs(Math.Cos(theta)) * fontSize.Width; oppositeTop = Math.Abs(Math.Sin(theta)) * fontSize.Width; adjacentBottom = Math.Abs(Math.Cos(theta)) * fontSize.Height; oppositeBottom = Math.Abs(Math.Sin(theta)) * fontSize.Height; } else { adjacentTop = Math.Abs(Math.Sin(theta)) * fontSize.Height; oppositeTop = Math.Abs(Math.Cos(theta)) * fontSize.Height; adjacentBottom = Math.Abs(Math.Sin(theta)) * fontSize.Width; oppositeBottom = Math.Abs(Math.Cos(theta)) * fontSize.Width; } int nWidth = (int)Math.Ceiling(adjacentTop + oppositeBottom); int nHeight = (int)Math.Ceiling(adjacentBottom + oppositeTop); return new SizeF(nWidth, nHeight); } } 
+1
source share

This code will give you an idea:

  public void DrawText(bool debug, Graphics g, string text, Font font, Brush brush, StringFormat format, float x, float y, float width, float height, float rotation) { float centerX = width / 2; float centerY = height / 2; if (debug) { g.FillEllipse(Brushes.Green, centerX - 5f, centerY - 5f, 10f, 10f); } GraphicsState gs = g.Save(); Matrix mat = new Matrix(); mat.RotateAt(rotation, new PointF(centerX, centerY), MatrixOrder.Append); g.Transform = mat; SizeF szf = g.MeasureString(text, font); g.DrawString(text, font, brush, (width / 2) - (szf.Width / 2), (height / 2) - (szf.Height / 2), format); g.Restore(gs); } 

The following is a method for measuring text rotary bounds using GraphicsPath. The logic is simple, GraphicsPath converts the text to a list of points, and then calculates a rectangle of borders.

  public RectangleF GetRotatedTextBounds(string text, Font font, StringFormat format, float rotation, float dpiY) { GraphicsPath gp = new GraphicsPath(); float emSize = dpiY * font.Size / 72; gp.AddString(text, font.FontFamily, (int)font.Style, emSize, new PointF(0, 0), format); Matrix mat = new Matrix(); mat.Rotate(rotation, MatrixOrder.Append); gp.Transform(mat); return gp.GetBounds(); } 

Test code:

  float fontSize = 25f; float rotation = 30f; RectangleF txBounds = GetRotatedTextBounds("TEST TEXT", new Font("Verdana", fontSize, System.Drawing.FontStyle.Bold), StringFormat.GenericDefault, rotation, 96f); float inflateValue = 10 * (fontSize / 100f); txBounds.Inflate(inflateValue, inflateValue); Bitmap bmp = new System.Drawing.Bitmap((int)txBounds.Width, (int)txBounds.Height); using (Graphics gr = Graphics.FromImage(bmp)) { gr.Clear(Color.White); DrawText(true, gr, "TEST TEXT", new Font("Verdana", fontSize, System.Drawing.FontStyle.Bold), Brushes.Red, new StringFormat(System.Drawing.StringFormatFlags.DisplayFormatControl), 0, 0, txBounds.Width, txBounds.Height, rotation); } 
+3
source share

All Articles