现在的位置: 首页 > 综合 > 正文

怎么用C#制作印章

2013年04月01日 ⁄ 综合 ⁄ 共 7521字 ⁄ 字号 评论关闭
制 作印章来说,主要是如何让字均匀的显示在弧线段上,那么一般的印章要么以圆或者椭圆为底图,不过这两者的算法大致相同,为了方便说明,如下就用相对简单的 圆来举例说明,如果需要做椭圆的话,可以在我的基础上进行扩展,因为核心算法是一样的,相对于圆来说,椭圆求弧长以及各个字符的位置,这两点相对麻烦些, 但是这两者都可找到相应的数学公式。
 
这里首先提一点,我这篇文章部分借鉴了codeproject的一个例子,原文可以参看如下地址。
(说实话,这篇文章写得有些乱,而且对于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];

        
forint 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;

        
forint 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 );
 
通过如上的代码,可以得到如下的效果。
 

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

完整的类如下:

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;

        
Class_Properties

抱歉!评论已关闭.