用户操作
[即时聊天] [发私信] [加为好友]
方清ID:fangq
32814次访问,排名3575好友0人,关注者39
青春映像,风舞枫扬
fangq的文章
原创 40 篇
翻译 0 篇
转载 4 篇
评论 88 篇
最近评论
yuboswing:写的太好了!
yuboswing:新手上路,偶遇大师文章。钦佩之至!
rain705:很喜欢你的文章
iq263me:程序员后花园,累了来看看 bbs.iq263.cn
lijunen:不错,受益非浅!
文章分类
收藏
    相册
    存档
    软件项目交易
    订阅我的博客
    XML聚合  FeedSky
    订阅到鲜果
    订阅到Google
    订阅到抓虾
    订阅到BlogLines
    订阅到Yahoo
    订阅到GouGou
    订阅到飞鸽
    订阅到Rojo
    订阅到newsgator
    订阅到netvibes

    原创 C#中制作公司公章 收藏

    新一篇: 收集的一些开发技术和应用实例 | 旧一篇: 在C#.net中如何操作XML

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

    这里首先提一点,我这篇文章部分借鉴了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,

        }

    }

     

    发表于 @ 2008年05月13日 17:37:54|评论(loading...)|编辑

    新一篇: 收集的一些开发技术和应用实例 | 旧一篇: 在C#.net中如何操作XML

    评论:没有评论。

    发表评论  


    当前用户设置只有注册用户才能发表评论。如果你没有登录,请点击登录
    Csdn Blog version 3.1a
    Copyright © fangq