![](http://s.codeproject.com/script/Forums/Images/t.gif) |
![Question](http://www.codeproject.com/script/Forums/Images/msg_question.gif) |
draw text within any path? |
![member member](http://s.codeproject.com/App_Themes/Std/Img/icn-member-16.gif) |
ASPUIPAB |
11:39 29 Oct '11 |
|
![](http://s.codeproject.com/script/Forums/Images/t.gif) |
Hi, Is there C# source code to draw text within any path, such as pentagon, or hexagon? right now .NET Graphics only provides API drawText to draw text within a rectangle. Thanks |
|
|
|
![](http://s.codeproject.com/script/Forums/Images/t.gif) |
![General](http://www.codeproject.com/script/Forums/Images/msg_general.gif) |
C# version and with optimization. |
![member member](http://s.codeproject.com/App_Themes/Std/Img/icn-member-16.gif) |
guidebee |
17:15 9 Jul '09 |
|
![](http://s.codeproject.com/script/Forums/Images/t.gif) |
It provide as Graphics extension methods. with similar syntax as normal MeasureString and DrawString. public static RectangleF[] MeasureString(this Graphics graphics, string s, Font font, Brush brush, TextPathAlign textPathAlign, TextPathPosition textPathPosition, int letterSpace,float rotateDegree, GraphicsPath graphicsPath); public static void DrawString(this Graphics graphics, string s, Font font, Brush brush, TextPathAlign textPathAlign, TextPathPosition textPathPosition, int letterSpace,float rotateDegree, GraphicsPath graphicsPath) sample code // Adds two Bezier curves. e.Graphics.SmoothingMode = SmoothingMode.HighQuality; Point[] myArray = { new Point(20, 100), new Point(40, 75), new Point(60, 125), new Point(80, 100), new Point(100, 50), new Point(120, 150), new Point(400, 400) }; // Create the path and add the curves. GraphicsPath myPath = new GraphicsPath(); myPath.AddBeziers(myArray); // Draw the path to the screen. Pen myPen = new Pen(Color.Black, 5); e.Graphics.DrawPath(myPen, myPath); System.Drawing.RectangleF[] regions = e.Graphics.MeasureString("Text on Path", new Font(FontFamily.GenericSerif, 24), new SolidBrush(Color.Red), TextPathAlign.Center, TextPathPosition.CenterPath, 100, 0, myPath); foreach (var region in regions) { e.Graphics.FillRectangle(new SolidBrush(Color.GreenYellow), region); } e.Graphics.DrawString("Text on Path", new Font(FontFamily.GenericSerif, 24), new SolidBrush(Color.Red), TextPathAlign.Center, TextPathPosition.CenterPath, 100, 0, myPath);
GraphicsExtenstion class
using System; using System.Collections.Generic; using System.Drawing; using System.Drawing.Drawing2D; using System.Drawing.Text; namespace WindowsFormsApplication6 { public enum TextPathAlign { Left = 0, Center = 1, Right = 2 } public enum TextPathPosition { OverPath = 0, CenterPath = 1, UnderPath = 2 } public static class GraphicsExtension { private static readonly TextOnPath TEXT_ON_PATH = new TextOnPath(); public static RectangleF[] MeasureString(this Graphics graphics, string s, Font font, Brush brush, GraphicsPath graphicsPath) { return MeasureString(graphics, s, font, brush, TextPathAlign.Left, TextPathPosition.CenterPath, 100, graphicsPath); } public static RectangleF[] MeasureString(this Graphics graphics, string s, Font font, Brush brush, TextPathAlign textPathAlign, TextPathPosition textPathPosition, GraphicsPath graphicsPath) { return MeasureString(graphics, s, font, brush, textPathAlign, textPathPosition, 100, graphicsPath); } public static void DrawString(this Graphics graphics, string s, Font font, Brush brush, GraphicsPath graphicsPath) { DrawString(graphics, s, font, brush, TextPathAlign.Left, TextPathPosition.CenterPath, 100, graphicsPath); } public static void DrawString(this Graphics graphics, string s, Font font, Brush brush, TextPathAlign textPathAlign, TextPathPosition textPathPosition, GraphicsPath graphicsPath) { DrawString(graphics, s, font, brush, textPathAlign, textPathPosition, 100, graphicsPath); } public static RectangleF[] MeasureString(this Graphics graphics, string s, Font font, Brush brush, TextPathAlign textPathAlign, TextPathPosition textPathPosition, int letterSpace, GraphicsPath graphicsPath) { return MeasureString(graphics, s, font, brush, textPathAlign, textPathPosition, 100, 0,graphicsPath); } public static void DrawString(this Graphics graphics, string s, Font font, Brush brush, TextPathAlign textPathAlign, TextPathPosition textPathPosition, int letterSpace, GraphicsPath graphicsPath) { DrawString(graphics, s, font, brush, textPathAlign, textPathPosition, 100,0, graphicsPath); } public static RectangleF[] MeasureString(this Graphics graphics, string s, Font font, Brush brush, TextPathAlign textPathAlign, TextPathPosition textPathPosition, int letterSpace,float rotateDegree, GraphicsPath graphicsPath) { TEXT_ON_PATH.Text = s; TEXT_ON_PATH.Font = font; TEXT_ON_PATH.FillColorTop = brush; TEXT_ON_PATH.TextPathPathPosition = textPathPosition; TEXT_ON_PATH.TextPathAlignTop = textPathAlign; TEXT_ON_PATH.PathDataTop = graphicsPath.PathData; TEXT_ON_PATH.LetterSpacePercentage = letterSpace; TEXT_ON_PATH._graphics = graphics; TEXT_ON_PATH._graphicsPath = graphicsPath; TEXT_ON_PATH._measureString = true; TEXT_ON_PATH._rotateDegree = rotateDegree; TEXT_ON_PATH.DrawTextOnPath(); return TEXT_ON_PATH._regionList.ToArray(); } public static void DrawString(this Graphics graphics, string s, Font font, Brush brush, TextPathAlign textPathAlign, TextPathPosition textPathPosition, int letterSpace,float rotateDegree, GraphicsPath graphicsPath) { TEXT_ON_PATH.Text = s; TEXT_ON_PATH.Font = font; TEXT_ON_PATH.FillColorTop = brush; TEXT_ON_PATH.TextPathPathPosition = textPathPosition; TEXT_ON_PATH.TextPathAlignTop = textPathAlign; TEXT_ON_PATH.PathDataTop = graphicsPath.PathData; TEXT_ON_PATH.LetterSpacePercentage = letterSpace; TEXT_ON_PATH._graphics = graphics; TEXT_ON_PATH._graphicsPath = graphicsPath; TEXT_ON_PATH._measureString = false; TEXT_ON_PATH._rotateDegree = rotateDegree; TEXT_ON_PATH.DrawTextOnPath(); } } internal class TextOnPath { private PathData _pathdata; private string _text; private Font _font; private Color _color = Color.Black; private Brush _fillBrush = new SolidBrush(Color.Black); private TextPathAlign _pathalign = TextPathAlign.Center; private int _letterspacepercentage = 100; private TextPathPosition _textPathPathPosition = TextPathPosition.CenterPath; public Exception LastError; internal Graphics _graphics; internal GraphicsPath _graphicsPath; internal bool _measureString; internal List<RectangleF> _regionList = new List<RectangleF>(); internal float _rotateDegree; public TextPathPosition TextPathPathPosition { get { return _textPathPathPosition; } set { _textPathPathPosition = value; } } public PathData PathDataTop { get { return _pathdata; } set { _pathdata = value; } } public string Text { get { return _text; } set { _text = value; } } public Font Font { get { return _font; } set { _font = value; } } public Color Color { get { return _color; } set { _color = value; } } public Brush FillColorTop { get { return _fillBrush; } set { _fillBrush = value; } } public TextPathAlign TextPathAlignTop { get { return _pathalign; } set { _pathalign = value; } } public int LetterSpacePercentage { get { return _letterspacepercentage; } set { _letterspacepercentage = value; } } public void DrawTextOnPath(PathData pathdata, string text, Font font, Color color, Brush fillcolor, int letterspacepercentage) { _pathdata = pathdata; _text = text; _font = font; _color = color; _fillBrush = fillcolor; _letterspacepercentage = letterspacepercentage; DrawTextOnPath(); } public void DrawTextOnPath() { PointF[] tmpPoints; PointF[] points = new PointF[25001]; int count = 0; GraphicsPath gp = new GraphicsPath(_pathdata.Points, _pathdata.Types) { FillMode = FillMode.Winding }; _regionList.Clear(); gp.Flatten(null, 1); try { PointF tmpPoint = gp.PathPoints[0]; int i; for (i = 0; i <= gp.PathPoints.Length - 2; i++) { if (gp.PathTypes[i + 1] == (byte)PathPointType.Start | (gp.PathTypes[i] & (byte)PathPointType.CloseSubpath) == (byte)PathPointType.CloseSubpath) { tmpPoints = GetLinePoints(gp.PathPoints[i], tmpPoint, 1); Array.ConstrainedCopy(tmpPoints, 0, points, count, tmpPoints.Length); count += 1; tmpPoint = gp.PathPoints[i + 1]; } else { tmpPoints = GetLinePoints(gp.PathPoints[i], gp.PathPoints[i + 1], 1); Array.ConstrainedCopy(tmpPoints, 0, points, count, tmpPoints.Length); count += tmpPoints.Length - 1; } } tmpPoints = new PointF[count]; Array.Copy(points, tmpPoints, count); points = CleanPoints(tmpPoints); count = points.Length - 1; DrawText(points, count); } catch (Exception ex) { LastError = ex; } } private static PointF[] CleanPoints(PointF[] points) { int i; PointF[] tmppoints = new PointF[points.Length + 1]; PointF lastpoint = default(PointF); int count = 0; for (i = 0; i <= points.Length - 1; i++) { if (i == 0 | points[i].X != lastpoint.X | points[i].Y != lastpoint.Y) { tmppoints[count] = points[i]; count += 1; } lastpoint = points[i]; } points = new PointF[count]; Array.Copy(tmppoints, points, count); return points; } private void DrawText(PointF[] points, int maxPoints) { GraphicsPath gp = new GraphicsPath(_pathdata.Points, _pathdata.Types) { FillMode = FillMode.Winding }; gp.Flatten(); gp.Dispose(); Graphics g = _graphics; //GraphicsContainer graphicsContainer= g.BeginContainer(); //g.TranslateTransform(_graphicsPath.GetBounds().X, _graphicsPath.GetBounds().Y); int count = 0; PointF point1 = default(PointF); int charStep = 0; double maxWidthText = default(double); int i; for (i = 0; i <= _text.Length - 1; i++) { maxWidthText += StringRegion(g, i); } switch (_pathalign) { case TextPathAlign.Left: point1 = points[0]; count = 0; break; case TextPathAlign.Center: count = (int)((maxPoints - maxWidthText) / 2); if (count > 0) { point1 = points[count]; } else { point1 = points[0]; } break; case TextPathAlign.Right: count = (int)(maxPoints - maxWidthText - (double)StringRegion(g, _text.Length - 1) * LetterSpacePercentage / 100); if (count > 0) { point1 = points[count]; } else { point1 = points[0]; } break; } while (!(charStep > _text.Length - 1)) { int lStrWidth = (int)(StringRegion(g, charStep) * LetterSpacePercentage / 100); if ((count + lStrWidth / 2) >= 0 & (count + lStrWidth) < maxPoints) { count += lStrWidth; PointF point2 = points[count]; PointF point = points[count - lStrWidth / 2]; double angle = GetAngle(point1, point2); DrawRotatedText(g, _text[charStep].ToString(), (float)angle, point); point1 = points[count]; } else { count += lStrWidth; } charStep += 1; } } private RectangleF StringRegionValue(Graphics g, int textpos) { string measureString = _text.Substring(textpos, 1); int numChars = measureString.Length; CharacterRange[] characterRanges = new CharacterRange[numChars + 1]; StringFormat stringFormat = new StringFormat { Trimming = StringTrimming.None, FormatFlags = StringFormatFlags.NoClip | StringFormatFlags.NoWrap | StringFormatFlags.LineLimit }; SizeF size = g.MeasureString(_text, _font, 100); RectangleF layoutRect = new RectangleF(0f, 0f, size.Width, size.Height); characterRanges[0] = new CharacterRange(0, 1); stringFormat.FormatFlags = StringFormatFlags.NoClip; stringFormat.SetMeasurableCharacterRanges(characterRanges); stringFormat.Alignment = StringAlignment.Near; Region[] stringRegions = g.MeasureCharacterRanges(_text.Substring(textpos), _font, layoutRect, stringFormat); return stringRegions[0].GetBounds(g); } private float StringRegion(Graphics g, int textpos) { return StringRegionValue(g,textpos).Width; } private static double GetAngle(PointF point1, PointF point2) { double c = Math.Sqrt(Math.Pow((point2.X - point1.X), 2) + Math.Pow((point2.Y - point1.Y), 2)); if (c == 0) { return 0; } if (point1.X > point2.X) { //We must change the side where the triangle is return Math.Asin((point1.Y - point2.Y) / c) * 180 / Math.PI - 180; } return Math.Asin((point2.Y - point1.Y) / c) * 180 / Math.PI; } private void DrawRotatedText(Graphics gr, string text, float angle, PointF pointCenter) { angle -= _rotateDegree; StringFormat stringFormat = new StringFormat { Alignment = StringAlignment.Center }; gr.SmoothingMode = SmoothingMode.HighQuality; gr.CompositingQuality = CompositingQuality.HighQuality; gr.TextRenderingHint = TextRenderingHint.AntiAlias; GraphicsPath graphicsPath = new GraphicsPath(FillMode.Winding); int x = (int)pointCenter.X; int y = (int)pointCenter.Y; switch (TextPathPathPosition) { case TextPathPosition.OverPath: graphicsPath.AddString(text, _font.FontFamily, (int)_font.Style, _font.Size, new Point(x, (int)(y - _font.Size)), stringFormat); break; case TextPathPosition.CenterPath: graphicsPath.AddString(text, _font.FontFamily, (int)_font.Style, _font.Size, new Point(x, (int)(y - _font.Size / 2)), stringFormat); break; case TextPathPosition.UnderPath: graphicsPath.AddString(text, _font.FontFamily, (int)_font.Style, _font.Size, new Point(x, y), stringFormat); break; } Matrix rotationMatrix = new Matrix(); rotationMatrix.RotateAt(angle, new PointF(x, y)); graphicsPath.Transform(rotationMatrix); if (!_measureString) { gr.DrawPath(new Pen(_color), graphicsPath); gr.FillPath(_fillBrush, graphicsPath); }else { _regionList.Add(graphicsPath.GetBounds()); } graphicsPath.Dispose(); } public PointF[] GetLinePoints(PointF p1, PointF p2, int stepWitdth) { int lCount = 0; PointF[] tmpPoints = new PointF[10001]; long ix; long iy; int dd; int id; int lStep = stepWitdth; p1.X = (int)p1.X; p1.Y = (int)p1.Y; p2.X = (int)p2.X; p2.Y = (int)p2.Y; long width = (long)(p2.X - p1.X); long height = (long)(p2.Y - p1.Y); long d = 0; if (width < 0) { width = -width; ix = -1; } else { ix = 1; } if (height < 0) { height = -height; iy = -1; } else { iy = 1; } if (width > height) { dd = (int)(width + width); id = (int)(height + height); do { if (lStep == stepWitdth) { tmpPoints[lCount].X = p1.X; tmpPoints[lCount].Y = p1.Y; lCount += 1; } else { lStep = lStep + stepWitdth; } if ((int)p1.X == (int)p2.X) break; p1.X = p1.X + ix; d = d + id; if (d > width) { p1.Y = p1.Y + iy; d = d - dd; } } while (true); } else { dd = (int)(height + height); id = (int)(width + width); do { if (lStep == stepWitdth) { tmpPoints[lCount].X = p1.X; tmpPoints[lCount].Y = p1.Y; lCount += 1; } else { lStep = lStep + stepWitdth; } if ((int)p1.Y == (int)p2.Y) break; p1.Y = p1.Y + iy; d = d + id; if (d > height) { p1.X = p1.X + ix; d = d - dd; } } while (true); } PointF[] tmpPoints2 = new PointF[lCount]; Array.Copy(tmpPoints, tmpPoints2, lCount); return tmpPoints2; } } } |
|
|
|
![](http://s.codeproject.com/script/Forums/Images/t.gif) |
![General](http://www.codeproject.com/script/Forums/Images/msg_general.gif) |
Great job |
![member member](http://s.codeproject.com/App_Themes/Std/Img/icn-member-16.gif) |
sf4all |
4:45 15 Jul '09 |
|
![](http://s.codeproject.com/script/Forums/Images/t.gif) |
Great job, thanx a lot |
|
|
|
![](http://s.codeproject.com/script/Forums/Images/t.gif) |
|
|
![](http://s.codeproject.com/script/Forums/Images/t.gif) |
![General](http://www.codeproject.com/script/Forums/Images/msg_general.gif) |
Interested in getting 1 developer license |
![member member](http://s.codeproject.com/App_Themes/Std/Img/icn-member-16.gif) |
LarryKudrow |
5:49 17 Mar '08 |
|
![](http://s.codeproject.com/script/Forums/Images/t.gif) |
Hi Alexander, I am Interested in getting 1 developer license. Could you please contact me: larrykudrow@optonline.net Thank you. |
|
|
|
![](http://s.codeproject.com/script/Forums/Images/t.gif) |
![General](http://www.codeproject.com/script/Forums/Images/msg_general.gif) |
Problem with Running Project |
![member member](http://s.codeproject.com/App_Themes/Std/Img/icn-member-16.gif) |
bnjneer |
14:30 5 Jan '08 |
|
![](http://s.codeproject.com/script/Forums/Images/t.gif) |
I downloaded the project, but it will not run. When I load the project, it indicates that 9 items are not defined or declared. What do I need to do to get the project to run so that I can check out the code? Thank you! |
|
|
|
![](http://s.codeproject.com/script/Forums/Images/t.gif) |
|
![](http://s.codeproject.com/script/Forums/Images/t.gif) |
I had to add my own version of ConstrainedCopy for .Net 1.1... other than that, the string doesn't center itself exactly in the center horizontally. The longer the string, the more skewed to the left it becomes. Any advice? Here's an easy replacement for the ConstrainedCopy of the Array. Public Sub ConstrainedCopy(ByVal sourceArray As Array, ByVal sourceIndex As Integer, ByRef destinationArray As Array, ByVal destinationIndex As Integer, ByVal length As Integer) Dim i As Integer = sourceIndex Dim j As Integer = destinationIndex Dim count As Integer = 0 Do While count < length destinationArray(j) = sourceArray(i) i += 1 j += 1 count += 1 Loop End Sub |
|
|
|
![](http://s.codeproject.com/script/Forums/Images/t.gif) |
|
![](http://s.codeproject.com/script/Forums/Images/t.gif) |
You should just use floats (or doubles) as decimal is 400 times should than either and doesn't provide any benefit. Instead of calculating the hypotenuse, you can actual just use the arctan of b/a. ---------------------------- Wesner Moise http://wesnerm.blogs.com |
|
|
|
![](http://s.codeproject.com/script/Forums/Images/t.gif) |
![General](http://www.codeproject.com/script/Forums/Images/msg_general.gif) |
Re: Sqrt and Decimal |
![member member](http://s.codeproject.com/App_Themes/Std/Img/icn-member-16.gif) |
jarvisa |
22:55 29 May '06 |
|
![](http://s.codeproject.com/script/Forums/Images/t.gif) |
My thoughts exactly. Use math.atan2(p1.y-p2.y,p1.x-p2.x) |
|
|
|
![](http://s.codeproject.com/script/Forums/Images/t.gif) |
|
![](http://s.codeproject.com/script/Forums/Images/t.gif) |
VG dot net of www.vgdotnet.com has a very similar ability. It is also a great product for other vector graphics applications. And no, I do not work for them or get paid by them, I just like their product. Great article though! I never would have been able to produce this without either your code or vgdotnet. |
|
|
|
|