王朝网络
分享
 
 
 

怎么用C#制作印章

王朝c#·作者佚名  2007-08-02
宽屏版  字体: |||超大  

制作印章来说,主要是如何让字均匀的显示在弧线段上,那么一般的印章要么以圆或者椭圆为底图,不过这两者的算法大致相同,为了方便说明,如下就用相对简单的圆来举例说明,如果需要做椭圆的话,可以在我的基础上进行扩展,因为核心算法是一样的,相对于圆来说,椭圆求弧长以及各个字符的位置,这两点相对麻烦些,但是这两者都可找到相应的数学公式。

这里首先提一点,我这篇文章部分借鉴了codeproject的一个例子,原文可以参看如下地址。

http://www.codeproject.com/vb/net/Text_on_Path_with_VBNET.asp

(说实话,这篇文章写得有些乱,而且对于buffer的操作近乎于疯狂)

由于印章的实现相对于这篇文章来说,相对简单多了,而且规律性很强,因此我自己考虑重新组织算法进行实现。

那么实现一个印章,大致步骤如下。

1. 计算字符串总长度,以及各个字符的长度;

2. 计算出字符串的起始角度;

3. 求出每个字符的所在的点,以及相对于中心的角度;

4. 绘制每个字符。

计算字符串总长度,以及各个字符的长度

这里需要用到“Graphics.MeasureString”和“Graphics.MeasureCharacterRanges”这两个方法,由于前者算出来的总长度有问题,所以需要后面进行重新计算(此外,这里我还考虑了字符最后显示方向)。

这部分的代码如下:

/// <summary>

/// Compute string total length and every char length

/// </summary>

/// <param name="sText"></param>

/// <param name="g"></param>

/// <param name="fCharWidth"></param>

/// <param name="fIntervalWidth"></param>

/// <returns></returns>

private float ComputeStringLength( string sText, Graphics g, float[] fCharWidth,

float fIntervalWidth,

Char_Direction Direction )

{

// Init string format

StringFormat sf = new StringFormat();

sf.Trimming = StringTrimming.None;

sf.FormatFlags = StringFormatFlags.NoClip | StringFormatFlags.NoWrap

| StringFormatFlags.LineLimit;

// Measure whole string length

SizeF size = g.MeasureString( sText, _font, (int)_font.Style );

RectangleF rect = new RectangleF( 0f,0f, size.Width, size.Height );

// Measure every character size

CharacterRange[] crs = new CharacterRange[sText.Length];

for( int i = 0; i < sText.Length; i++ )

crs[i] = new CharacterRange( i, 1 );

// Reset string format

sf.FormatFlags = StringFormatFlags.NoClip;

sf.SetMeasurableCharacterRanges( crs );

sf.Alignment = StringAlignment.Near;

// Get every character size

Region[] regs = g.MeasureCharacterRanges( sText,

_font, rect, sf );

// Re-compute whole string length with space interval width

float fTotalWidth = 0f;

for( int i = 0; i < regs.Length; i++ )

{

if( Direction == Char_Direction.Center || Direction == Char_Direction.OutSide )

fCharWidth[i] = regs[i].GetBounds( g ).Width;

else

fCharWidth[i] = regs[i].GetBounds( g ).Height;

fTotalWidth += fCharWidth[i] + fIntervalWidth;

}

fTotalWidth -= fIntervalWidth;//Remove the last interval width

return fTotalWidth;

}

计算出字符串的起始角度

为了更好地开展文章,那么首先说说在我这篇文章中,坐标的度数位置。详情参看如下图示。

对于图形角度分布有个概念后,那么对于整个字符串所跨的弧度以及起始弧度的计算,就相对比较简单了。具体如下:

// Compute arc's start-angle and end-angle

double fStartAngle, fSweepAngle;

fSweepAngle = fTotalWidth * 360 / ( _rectcircle.Width * Math.PI );

fStartAngle = 270 - fSweepAngle / 2;

求出每个字符的所在的点,以及相对于中心的角度

这一部分相对要麻烦些,大致步骤如下。

1. 通过字符长度,求出字符所跨的弧度;

2. 根据字符所跨的弧度,以及字符起始位置,算出字符的中心位置所对应的角度;

3. 由于相对中心的角度已知,根据三角公式很容易算出字符所在弧上的点,如下图所示;

4. 根据字符长度以及间隔距离,算出下一个字符的起始角度;

5. 重复1直至整个字符串结束。

那么这部分的具体代码如下。

/// <summary>

/// Compute every char position

/// </summary>

/// <param name="CharWidth"></param>

/// <param name="recChars"></param>

/// <param name="CharAngle"></param>

/// <param name="StartAngle"></param>

private void ComputeCharPos(

float[] CharWidth,

PointF[] recChars,

double[] CharAngle,

double StartAngle )

{

double fSweepAngle, fCircleLength;

//Compute the circumference

fCircleLength = _rectcircle.Width * Math.PI;

for( int i = 0; i < CharWidth.Length; i++ )

{

//Get char sweep angle

fSweepAngle = CharWidth[i] * 360 / fCircleLength;

//Set point angle

CharAngle[i] = StartAngle + fSweepAngle / 2;

//Get char position

if( CharAngle[i] < 270f )

recChars[i] = new PointF(

_rectcircle.X + _rectcircle.Width / 2

-(float)( _rectcircle.Width / 2 *

Math.Sin( Math.Abs( CharAngle[i] - 270 ) * Math.PI / 180 ) ) ,

_rectcircle.Y + _rectcircle.Width / 2

-(float)( _rectcircle.Width / 2 * Math.Cos(

Math.Abs( CharAngle[i] - 270 ) * Math.PI / 180 ) ) );

else

recChars[i] = new PointF(

_rectcircle.X + _rectcircle.Width / 2

+(float)( _rectcircle.Width / 2 *

Math.Sin( Math.Abs( CharAngle[i] - 270 ) * Math.PI / 180 ) ) ,

_rectcircle.Y + _rectcircle.Width / 2

-(float)( _rectcircle.Width / 2 * Math.Cos(

Math.Abs( CharAngle[i] - 270 ) * Math.PI / 180 ) ) );

//Get total sweep angle with interval space

fSweepAngle = ( CharWidth[i] + _letterspace ) * 360 / fCircleLength;

StartAngle += fSweepAngle;

}

}

绘制每个字符

由于每个字符所在的点以及相对于圆心的角度都已经计算出来,那么进行绘制就相对简单多了,这里只是通过Matrix来转换一下坐标而已。

具体代码如下。

/// <summary>

/// Draw every rotated character

/// </summary>

/// <param name="g"></param>

/// <param name="_text"></param>

/// <param name="_angle"></param>

/// <param name="_PointCenter"></param>

private void DrawRotatedText( Graphics g, string _text, float _angle, PointF _PointCenter )

{

// Init format

StringFormat sf = new StringFormat();

sf.Alignment = StringAlignment.Center;

sf.LineAlignment = StringAlignment.Center;

// Create graphics path

GraphicsPath gp = new GraphicsPath( System.Drawing.Drawing2D.FillMode.Winding );

int x = (int)_PointCenter.X;

int y = (int)_PointCenter.Y;

// Add string

gp.AddString( _text, _font.FontFamily, (int)_font.Style,

_font.Size, new Point( x, y ), sf );

// Rotate string and draw it

Matrix m = new Matrix();

m.RotateAt( _angle, new PointF( x,y ) );

g.Transform = m;

g.DrawPath( new Pen( _color), gp );

g.FillPath( new SolidBrush( _fillcolor ), gp );

}

以上就是绘制印章的核心算法。对于这个类的调用,如下即可。

TextOnSeal _top = new TextOnSeal();

_top.TextFont = new Font("宋体", 16, FontStyle.Regular);

_top.FillColor = Color.Red;

_top.ColorTOP = Color.Black;

_top.Text = "中华人民共和国";

_top.BaseString = "愚翁专用章";

_top.ShowPath = true;

_top.LetterSpace = 20;

_top.SealSize = 180;

_top.CharDirection = Char_Direction.Center;

_top.SetIndent( 20 );

Graphics g = this.CreateGraphics();

g.DrawImage( _top.TextOnPathBitmap(), 0, 0 );

_top.CharDirection = Char_Direction.ClockWise;

g.DrawImage( _top.TextOnPathBitmap(), 180, 0 );

_top.CharDirection = Char_Direction.AntiClockWise;

g.DrawImage( _top.TextOnPathBitmap(), 0, 180 );

_top.SetIndent( 20 );

_top.CharDirection = Char_Direction.OutSide;

g.DrawImage( _top.TextOnPathBitmap(), 180, 180 );

通过如上的代码,可以得到如下的效果。

其实如果做印章来说,还有很多地方需要细化,那么如果网友对此有兴趣,可以在我的基础上进行扩展,在此我就不一一述说。

如下是整个类的完整代码。

//--------------------------- TextOnSeal class ---------------------------------------

//------------------------------------------------------------------------------------

//---File: TextOnSeal

//---Description: The class file to create seal bitmap with text

//---Author: Knight

//---Date: Nov.3, 2006

//------------------------------------------------------------------------------------

//---------------------------{TextOnSeal class}---------------------------------------

namespace Seal

{

using System;

using System.Drawing;

using System.Drawing.Drawing2D;

using System.Diagnostics;

/// <summary>

/// Summary description for TextOnSeal.

/// </summary>

public class TextOnSeal

{

private string _text;

private Font _font;

private Color _pathcolor = Color.Red;

private Color _color = Color.Black;

private Color _fillcolor = Color.Black;

private int _letterspace = 10;

private bool _showpath = true;

private Rectangle _rectcircle;

private Rectangle _rect;

private int _intentlength = 10;

private Char_Direction _chardirect = Char_Direction.Center;

private int _degree = 90;

private string _basestring;

#region Class_Properties

public Char_Direction CharDirection

{

get{ return _chardirect;}

set{

if( _chardirect != value )

{

_chardirect = value;

switch( _chardirect )

{

case Char_Direction.Center:

_degree = 90;

break;

case Char_Direction.ClockWise:

_degree = 0;

break;

case Char_Direction.OutSide:

_degree = -90;

break;

case Char_Direction.AntiClockWise:

_degree = 180;

break;

}

}

}

}

public string BaseString

{

get{ return _basestring;}

set{ _basestring = value; }

}

public string Text

{

get{ return _text;}

set{ _text = value;}

}

public Font TextFont

{

get{ return _font;}

set{ _font = value;}

}

public Color PathColor

{

get{ return _pathcolor;}

set{ _pathcolor = value; }

}

public Color ColorTOP

{

get{ return _color;}

set{ _color = value;}

}

public Color FillColor

{

get{ return _fillcolor;}

set{ _fillcolor = value;}

}

public int LetterSpace

{

get{ return _letterspace;}

set{ _letterspace = value;}

}

public bool ShowPath

{

get{ return _showpath;}

set{ _showpath = value;}

}

public int SealSize

{

set{

_rect = new Rectangle( 0, 0, value, value );

_rectcircle = new Rectangle(

new Point( _rect.X + _intentlength, _rect.Y + _intentlength ),

new Size( _rect.Width - 2 * _intentlength, _rect.Height - 2 * _intentlength ) ) ;

}

}

#endregion {Class_Properties}

public void SetIndent( int IntentLength )

{

_intentlength = IntentLength;

_rectcircle = new Rectangle( _intentlength, _intentlength,

_rect.Width - _intentlength * 2, _rect.Height - _intentlength * 2 );

}

public TextOnSeal()

{

//

// TODO: Add constructor logic here

//

}

public Bitmap TextOnPathBitmap(

Rectangle rectCircle,

string strText,

Font fntText,

Color clrColor,

Color clrFill,

int nPercentage )

{

_rect = rectCircle;

_rectcircle = new Rectangle(

new Point( _rect.X + _intentlength, _rect.Y + _intentlength ),

new Size( _rect.Width - 2 * _intentlength, _rect.Height - 2 * _intentlength ) ) ;

_text = strText;

_font = fntText;

_color = clrColor;

_fillcolor = clrFill;

_letterspace = nPercentage;

return TextOnPathBitmap();

}

/// <summary>

/// Compute string total length and every char length

/// </summary>

/// <param name="sText"></param>

/// <param name="g"></param>

/// <param name="fCharWidth"></param>

/// <param name="fIntervalWidth"></param>

/// <returns></returns>

private float ComputeStringLength( string sText, Graphics g, float[] fCharWidth,

float fIntervalWidth,

Char_Direction Direction )

{

// Init string format

StringFormat sf = new StringFormat();

sf.Trimming = StringTrimming.None;

sf.FormatFlags = StringFormatFlags.NoClip | StringFormatFlags.NoWrap

| StringFormatFlags.LineLimit;

// Measure whole string length

SizeF size = g.MeasureString( sText, _font, (int)_font.Style );

RectangleF rect = new RectangleF( 0f,0f, size.Width, size.Height );

// Measure every character size

CharacterRange[] crs = new CharacterRange[sText.Length];

for( int i = 0; i < sText.Length; i++ )

crs[i] = new CharacterRange( i, 1 );

// Reset string format

sf.FormatFlags = StringFormatFlags.NoClip;

sf.SetMeasurableCharacterRanges( crs );

sf.Alignment = StringAlignment.Near;

// Get every character size

Region[] regs = g.MeasureCharacterRanges( sText,

_font, rect, sf );

// Re-compute whole string length with space interval width

float fTotalWidth = 0f;

for( int i = 0; i < regs.Length; i++ )

{

if( Direction == Char_Direction.Center || Direction == Char_Direction.OutSide )

fCharWidth[i] = regs[i].GetBounds( g ).Width;

else

fCharWidth[i] = regs[i].GetBounds( g ).Height;

fTotalWidth += fCharWidth[i] + fIntervalWidth;

}

fTotalWidth -= fIntervalWidth;//Remove the last interval width

return fTotalWidth;

}

/// <summary>

/// Compute every char position

/// </summary>

/// <param name="CharWidth"></param>

/// <param name="recChars"></param>

/// <param name="CharAngle"></param>

/// <param name="StartAngle"></param>

private void ComputeCharPos(

float[] CharWidth,

PointF[] recChars,

double[] CharAngle,

double StartAngle )

{

double fSweepAngle, fCircleLength;

//Compute the circumference

fCircleLength = _rectcircle.Width * Math.PI;

for( int i = 0; i < CharWidth.Length; i++ )

{

//Get char sweep angle

fSweepAngle = CharWidth[i] * 360 / fCircleLength;

//Set point angle

CharAngle[i] = StartAngle + fSweepAngle / 2;

//Get char position

if( CharAngle[i] < 270f )

recChars[i] = new PointF(

_rectcircle.X + _rectcircle.Width / 2

-(float)( _rectcircle.Width / 2 *

Math.Sin( Math.Abs( CharAngle[i] - 270 ) * Math.PI / 180 ) ) ,

_rectcircle.Y + _rectcircle.Width / 2

-(float)( _rectcircle.Width / 2 * Math.Cos(

Math.Abs( CharAngle[i] - 270 ) * Math.PI / 180 ) ) );

else

recChars[i] = new PointF(

_rectcircle.X + _rectcircle.Width / 2

+(float)( _rectcircle.Width / 2 *

Math.Sin( Math.Abs( CharAngle[i] - 270 ) * Math.PI / 180 ) ) ,

_rectcircle.Y + _rectcircle.Width / 2

-(float)( _rectcircle.Width / 2 * Math.Cos(

Math.Abs( CharAngle[i] - 270 ) * Math.PI / 180 ) ) );

//Get total sweep angle with interval space

fSweepAngle = ( CharWidth[i] + _letterspace ) * 360 / fCircleLength;

StartAngle += fSweepAngle;

}

}

/// <summary>

/// Generate seal bitmap

/// </summary>

/// <returns></returns>

public Bitmap TextOnPathBitmap()

{

// Create bitmap and graphics

Bitmap bit = new Bitmap( _rect.Width, _rect.Height );

Graphics g = Graphics.FromImage( bit );

// Compute string length in graphics

float[] fCharWidth = new float[_text.Length];

float fTotalWidth = ComputeStringLength( _text, g, fCharWidth,

_letterspace, _chardirect );

// Compute arc's start-angle and end-angle

double fStartAngle, fSweepAngle;

fSweepAngle = fTotalWidth * 360 / ( _rectcircle.Width * Math.PI );

fStartAngle = 270 - fSweepAngle / 2;

// Compute every character's position and angle

PointF[] pntChars = new PointF[ _text.Length ];

double[] fCharAngle = new double[ _text.Length ];

ComputeCharPos( fCharWidth, pntChars, fCharAngle, fStartAngle );

g.SmoothingMode = SmoothingMode.HighQuality;

g.CompositingQuality = CompositingQuality.HighQuality;

g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias;

DrawSealBase( g );

// Draw every character

for( int i = 0; i < _text.Length; i++ )

DrawRotatedText( g, _text[i].ToString(), (float)(fCharAngle[i] + _degree), pntChars[i] );

g.Dispose();

// Return bitmap

return bit;

}

/// <summary>

/// Draw seal base

/// </summary>

/// <param name="g"></param>

private void DrawSealBase( Graphics g )

{

// Draw background

g.FillRectangle( Brushes.Black, _rect );

g.FillEllipse( new SolidBrush( _fillcolor ),

new Rectangle( 1,1, _rect.Width - 2, _rect.Height - 2 ) );

g.FillEllipse( Brushes.White,

new Rectangle( 4, 4, _rect.Width - 8, _rect.Height - 8 ) );

// Draw start signal

StringFormat sf = new StringFormat();

string strStar = "★";

Font fnt = new Font( _font.FontFamily, _font.Size * 3 );

sf.Alignment = StringAlignment.Center;

sf.LineAlignment = StringAlignment.Center;

SizeF siz = g.MeasureString( strStar, fnt );

g.DrawString( strStar, fnt, new SolidBrush( _fillcolor ),

new RectangleF( _rect.Width / 2 - siz.Width / 2,

_rect.Height / 2 - siz.Height/ 2,

siz.Width, siz.Height ), sf );

// Draw base string

float[] fCharWidth = new float[_basestring.Length];

float fTotalWidths = ComputeStringLength( _basestring, g, fCharWidth, 0,

Char_Direction.Center );

float fLeftPos = ( _rect.Width - fTotalWidths ) / 2;

PointF pt;

for( int i = 0; i < _basestring.Length; i++ )

{

pt = new PointF( fLeftPos + fCharWidth[i] / 2,

_rect.Height / 2 + siz.Height / 2 + 10 );

DrawRotatedText( g, _basestring[i].ToString(), 0, pt );

fLeftPos += fCharWidth[i];

}

}

/// <summary>

/// Draw every rotated character

/// </summary>

/// <param name="g"></param>

/// <param name="_text"></param>

/// <param name="_angle"></param>

/// <param name="_PointCenter"></param>

private void DrawRotatedText( Graphics g, string _text, float _angle, PointF _PointCenter )

{

// Init format

StringFormat sf = new StringFormat();

sf.Alignment = StringAlignment.Center;

sf.LineAlignment = StringAlignment.Center;

// Create graphics path

GraphicsPath gp = new GraphicsPath( System.Drawing.Drawing2D.FillMode.Winding );

int x = (int)_PointCenter.X;

int y = (int)_PointCenter.Y;

// Add string

gp.AddString( _text, _font.FontFamily, (int)_font.Style,

_font.Size, new Point( x, y ), sf );

// Rotate string and draw it

Matrix m = new Matrix();

m.RotateAt( _angle, new PointF( x,y ) );

g.Transform = m;

g.DrawPath( new Pen( _color), gp );

g.FillPath( new SolidBrush( _fillcolor ), gp );

}

}

public enum Char_Direction

{

Center = 0,

OutSide = 1,

ClockWise = 2,

AntiClockWise = 3,

}

}

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
>>返回首页<<
推荐阅读
 
 
频道精选
 
静静地坐在废墟上,四周的荒凉一望无际,忽然觉得,凄凉也很美
© 2005- 王朝网络 版权所有