先看一下效果圖:
這個的難點在於排版的算法上,其他的就是對GDI+中GraphicsPath的應用。以下為核心部分的源代碼:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Imaging;
using System.Drawing.Drawing2D;
namespace Lgms.net.Drawing
{
public enum PathAlign //文字在路徑方向的對齊方向
{
Left = 0, //從第一點開始排列
Center = 1, //居中排列
Right = 2 //居右排列
}
public enum TextPosition //文字排列在路徑上的位置
{
OverPath = 0, //路徑之上
CenterPath = 1, //路徑中間,文字中心即路徑經過區域
UnderPath = 2 //路徑下方
}
//基礎類
public class TextOnPath
{
private PathData _pathdataTOP;
private string _textTOP;
private Font _fontTOP;
private Color _pathcolorTOP = Color.Red;
private Color _colorTOP = Color.Black;
private Color _fillcolorTOP = Color.Black;
private PathAlign _pathalignTOP = PathAlign.Center;
private int _letterspacepercentageTOP = 100;
private bool _addsvg = false;
private System.Text.StringBuilder _SVG;
private int _currentPathID;
private TextPosition _textPathPosition = TextPosition.CenterPath;
public Exception LastError = null;
public bool ShowPath = true;
public TextPosition TextPathPosition
{
get { return _textPathPosition; }
set { _textPathPosition = value; }
}
public PathData PathDataTOP
{
get { return _pathdataTOP; }
set { _pathdataTOP = value; }
}
public string TextTOP
{
get { return _textTOP; }
set { _textTOP = value; }
}
public Font FontTOP
{
get { return _fontTOP; }
set { _fontTOP = value; }
}
public Color PathColorTOP
{
get { return _pathcolorTOP; }
set { _pathcolorTOP = value; }
}
public Color ColorTOP
{
get { return _colorTOP; }
set { _colorTOP = value; }
}
public Color FillColorTOP
{
get { return _fillcolorTOP; }
set { _fillcolorTOP = value; }
}
public PathAlign PathAlignTOP
{
get { return _pathalignTOP; }
set { _pathalignTOP = value; }
}
public int LetterSpacePercentage
{
get { return _letterspacepercentageTOP; }
set { _letterspacepercentageTOP = value; }
}
public Bitmap TextOnPathBitmap(PathData _pathdata, string _text, Font _font, Color _color, Color _fillcolor, int _letterspacepercentage)
{
_pathdataTOP = _pathdata;
_textTOP = _text;
_fontTOP = new Font(_font, _fontTOP.Style);
_colorTOP = _color;
_fillcolorTOP = _fillcolor;
_letterspacepercentageTOP = _letterspacepercentage;
return TextOnPathBitmap();
}
//核心方法
public Bitmap TextOnPathBitmap()
{
int i = 0;
PointF[] _TmpPoints = null;
PointF _TmpPoint = default(PointF);
PointF[] _Points = new PointF[25001];
//= oGP.PathPoints()
int _count = 0;
GraphicsPath _gp = new GraphicsPath(_pathdataTOP.Points, _pathdataTOP.Types);
_gp.FillMode = FillMode.Winding;
_gp.Flatten(null, 1);
try
{
_TmpPoint = _gp.PathPoints[0];
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 + 1];
Array.Copy(_Points, _TmpPoints, _count);
_Points = CleanPoints(_TmpPoints);
_count = _Points.Length - 1;
return DrawText(_Points, _count);
}
catch (Exception ex)
{
LastError = ex;
return null;
}
}
private PointF[] CleanPoints(PointF[] _points)
{
int i = 0;
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 + 1];
Array.Copy(_tmppoints, _points, _count);
return _points;
}
//排版
private Bitmap DrawText(PointF[] _Points, int _MaxPoints)
{
GraphicsPath _gp = new GraphicsPath(_pathdataTOP.Points, _pathdataTOP.Types);
_gp.FillMode = FillMode.Winding;
_gp.Flatten();
Bitmap _bitmap = new Bitmap(
Convert.ToInt32(_gp.GetBounds().Right + _fontTOP.Size * 2),
Convert.ToInt32(_gp.GetBounds().Bottom + _fontTOP.Size * 2)
);
_gp.Dispose();
Graphics _G = Graphics.FromImage(_bitmap);
int _count = 0;
PointF _Point1 = default(PointF);
PointF _Point2 = default(PointF);
PointF _Point = default(PointF);
int _CharStep = 0;
int lStrWidth = 0;
double _Angle = default(double);
double _MaxWidthText = default(double);
int i = 0;
float[] _widths = null;
//_widths = MeasureWidths(_G)
Pen _pathpen = new Pen(_pathcolorTOP);
if (ShowPath == true)
{
foreach (PointF _Point1_loopVariable in _Points)
{
_Point1 = _Point1_loopVariable;
_G.DrawEllipse(_pathpen, _Point1.X, _Point1.Y, 1, 1);
}
}
_pathpen.Dispose();
for (i = 0; i <= _textTOP.Length - 1; i++)
{
_MaxWidthText += StringRegion(_G, i);
// _widths(i)
}
switch (_pathalignTOP)
{
case PathAlign.Left:
_Point1 = _Points[0];
_count = 0;
break;
case PathAlign.Center:
_count = (int)(_MaxPoints - _MaxWidthText) / 2;
if (_count > 0)
{
_Point1 = _Points[_count];
}
else
{
_Point1 = _Points[0];
}
break;
case PathAlign.Right:
_count = (int)(_MaxPoints - _MaxWidthText - StringRegion(_G, _textTOP.Length - 1) * LetterSpacePercentage / 100);
if (_count > 0)
{
_Point1 = _Points[_count];
}
else
{
_Point1 = _Points[0];
}
break;
}
while (!(_CharStep > _textTOP.Length - 1))
{
lStrWidth = (int)StringRegion(_G, _CharStep) * LetterSpacePercentage / 100;
if ((_count + lStrWidth / 2) >= 0 & (_count + lStrWidth) < _MaxPoints)
{
_count += lStrWidth;
_Point2 = _Points[_count];
_Point = _Points[_count - lStrWidth / 2];
_Angle = GetAngle(_Point1, _Point2);
DrawRotatedText(_G, _textTOP[_CharStep].ToString(), (float)_Angle, _Point);
_Point1 = _Points[_count];
}
else
{
_count += lStrWidth;
}
_CharStep += 1;
}
_G.Dispose();
return _bitmap;
}
private float StringRegion(Graphics _g, int _textpos)
{
string measureString = _textTOP.Substring(_textpos, 1);
int numChars = measureString.Length;
CharacterRange[] characterRanges = new CharacterRange[numChars + 1];
StringFormat stringFormat = new StringFormat();
stringFormat.Trimming = StringTrimming.None;
stringFormat.FormatFlags = StringFormatFlags.NoClip | StringFormatFlags.NoWrap | StringFormatFlags.LineLimit;
SizeF size = _g.MeasureString(_textTOP, _fontTOP);
RectangleF layoutRect = new RectangleF(0f, 0f, size.Width, size.Height);
Region[] stringRegions = new Region[numChars + 1];
characterRanges[0] = new CharacterRange(0, 1);
stringFormat.FormatFlags = StringFormatFlags.NoClip;
stringFormat.SetMeasurableCharacterRanges(characterRanges);
stringFormat.Alignment = StringAlignment.Near;
stringRegions = _g.MeasureCharacterRanges(_textTOP.Substring(_textpos), _fontTOP, layoutRect, stringFormat);
return stringRegions[0].GetBounds(_g).Width;
}
private float[] MeasureWidths(Graphics graphics)
{
float[] widths = new float[_textTOP.Length + 1];
StringFormat format = StringFormat.GenericTypographic;
format.Trimming = StringTrimming.None;
format.FormatFlags = StringFormatFlags.NoClip | StringFormatFlags.NoWrap | StringFormatFlags.LineLimit;
graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias;
RectangleF layout = new RectangleF(0, 0, 10000, 10000);
int remainder = _textTOP.Length;
int start = 0;
while (remainder > 0)
{
int length = remainder;
if (length > 32)
{
length = 32;
}
CharacterRange[] ranges = new CharacterRange[length + 1];
int i = 0;
while (i < ranges.Length)
{
ranges[i] = new CharacterRange(start + i, 1);
i += 1;
}
format.SetMeasurableCharacterRanges(ranges);
Region[] regions = graphics.MeasureCharacterRanges(_textTOP, _fontTOP, layout, format);
i = 0;
while (i < regions.Length)
{
Region region = regions[i];
RectangleF cb = region.GetBounds(graphics);
widths[start + i] = cb.Width;
i += 1;
}
start += length;
remainder -= 31;
}
return widths;
}
private double GetAngle(PointF _point1, PointF _point2)
{
double c = default(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)
{
return Math.Asin((_point1.Y - _point2.Y) / c) * 180 / Math.PI - 180;
}
else
{
return Math.Asin((_point2.Y - _point1.Y) / c) * 180 / Math.PI;
}
}
private void DrawRotatedText(Graphics _gr, string _text, float _angle, PointF _PointCenter)
{
StringFormat string_format = new StringFormat();
string_format.Alignment = StringAlignment.Center;
_gr.SmoothingMode = SmoothingMode.HighQuality;
_gr.CompositingQuality = CompositingQuality.HighQuality;
_gr.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias;
GraphicsPath graphics_path = new GraphicsPath(System.Drawing.Drawing2D.FillMode.Winding);
int x = Convert.ToInt32(_PointCenter.X);
int y = Convert.ToInt32(_PointCenter.Y);
switch (TextPathPosition)
{
case TextPosition.OverPath:
graphics_path.AddString(_text, _fontTOP.FontFamily, (int)_fontTOP.Style, (float)_fontTOP.Size, new Point(x, y - (int)_fontTOP.Size), string_format);
break;
case TextPosition.CenterPath:
graphics_path.AddString(_text, _fontTOP.FontFamily, (int)_fontTOP.Style, (float)_fontTOP.Size, new Point(x, y - (int)_fontTOP.Size / 2), string_format);
break;
case TextPosition.UnderPath:
graphics_path.AddString(_text, _fontTOP.FontFamily, (int)_fontTOP.Style, (float)_fontTOP.Size, new Point(x, y), string_format);
break;
}
Matrix rotation_matrix = new Matrix();
rotation_matrix.RotateAt(_angle, new PointF(x, y));
graphics_path.Transform(rotation_matrix);
_gr.DrawPath(new Pen(_colorTOP), graphics_path);
_gr.FillPath(new SolidBrush(_fillcolorTOP), graphics_path);
graphics_path.Dispose();
}
public PointF[] GetLinePoints(PointF _p1, PointF _p2, int _stepWitdth)
{
int lCount = 0;
PointF[] _tmpPoints = new PointF[10001];
float _width = 0;
float _height = 0;
long d = 0;
long ix = 0;
long iy = 0;
int dd = 0;
int id = 0;
int lStep = _stepWitdth;
_p1.X = Convert.ToInt32(_p1.X);
_p1.Y = Convert.ToInt32(_p1.Y);
_p2.X = Convert.ToInt32(_p2.X);
_p2.Y = Convert.ToInt32(_p2.Y);
_width = _p2.X - _p1.X;
_height = _p2.Y - _p1.Y;
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 += _stepWitdth;
}
if (Convert.ToInt32(_p1.X) == Convert.ToInt32(_p2.X))
break;
_p1.X += ix;
d += id;
if (d > _width)
{
_p1.Y += iy;
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 += _stepWitdth;
}
if (Convert.ToInt32(_p1.Y) == Convert.ToInt32(_p2.Y))
break; // TODO: might not be correct. Was : Exit Do
_p1.Y += iy;
d += id;
if (d > _height)
{
_p1.X += ix;
d -= dd;
}
} while (true);
}
PointF[] _tmpPoints2 = null;
_tmpPoints2 = new PointF[lCount + 1];
Array.Copy(_tmpPoints, _tmpPoints2, lCount);
return _tmpPoints2;
}
}
}
下為測試部分的核心源代碼:
PointF[] _points = new PointF[8];
TextOnPath _top = new TextOnPath();
//初始化的相關代碼:
_points[0] = new PointF(303, 485);
_points[1] = new PointF(476, 616);
_points[2] = new PointF(745, 599);
_points[3] = new PointF(876, 434);
_points[4] = new PointF(829, 179);
_points[5] = new PointF(657, 73);
_points[6] = new PointF(518, 269);
_points[7] = new PointF(308, 349);
_top.FontTOP = new Font("楷體_GB2312", 48, FontStyle.Bold);
_top.FillColorTOP = Color.Red;
_top.ColorTOP = Color.Black;
_top.TextTOP = "龍崗民生網http://www.lgms.net";
_top.ShowPath = true;
_top.PathAlignTOP = PathAlign.Left;
_top.LetterSpacePercentage = 80;
_top.TextPathPosition = TextPosition.OverPath;
//關鍵的調用方法:
private Image DrawText()
{
GraphicsPath _gp = new GraphicsPath();
_gp.AddClosedCurve(_points);
_top.PathDataTOP = _gp.PathData;
return _top.TextOnPathBitmap();
}
在相應地方加上DrawText()即可。