Click here to Skip to main content
15,886,065 members
Articles / Desktop Programming / MFC

Signature Capture in Handheld or PocketPC with C#

Rate me:
Please Sign up or sign in to vote.
2.56/5 (11 votes)
25 Feb 2004CPOL 71.4K   925   27   4
Saving the signature as a TXT file and reloading again from TXT file in C# and on server side, converting the TXT file to a JPG file with Java.

Introduction

This article shows how to collect and display a signature. The signature is made up of a list of line segments. Each segment contains an array of points (x and y coordinates).

There are two *.cs files MainForm.cs and SignatureControl.cs. These two files are used in C# .NET compact Framework to capture the signature and save that in a *.txt file in PocketPC. You can load the same signature from the *.txt file.

C#
/***********CLIENT CODE****************/

/*****************************************************************/

//SignatureControl.cs File

using System;
using System.Windows.Forms;
using System.Drawing;
using System.Collections;
using System.IO; 

namespace PocketSignature
{ 
 /****************************************************************************
     *<author>Gitansu Behera emailId:gbehera@cellexchange.com</author>
     *<version>1.0</version>
     *<since>2004</since>
     *<summary>
     * Title:        SignatureControl
     * Version:      1.0
     * Author:       Geetansu Behera
     * Description:   Collects and displays a signature. The signature is made 
     * up of a list of line segments. Each segment contains an array of points 
     * (x and y coordinates). 
     * Draws to a memory bitmap to prevent flickering.
     * Raises the SignatureUpdate event when a new segment is added to 
     * the signature.
     *         Last updated
     * Modification Log :
     * Date             Author      Comments
     *
     * Unpublished Confidential Information of CellExchange.  Do not disclose.
     * Copyright © 1999-2004 CellExchange.com, Inc.  All Rights Reserved.
     ***************************************************************************
     * </summary>
     *<description></description>
     */ 
  public class SignatureControl : Control
  { 

    private string background = null; 
    public string Background
    {
      get 
      {
        return background;
      }
      set
      { 
        background = value;
  
      } 
    }   

    // GDI objects
    Bitmap bmp;
    //String bmpFile="<a href="%22file://program/%22">\\Program</a> Files\\SignCaptureV2\\sign here.png";
    
    Graphics graphics;
    Pen pen = new Pen(Color.Black); 
    // list of line segments
    ArrayList pVector = new ArrayList();
    
    Point lastPoint = new Point(0,0);
    
    // if drawing signature or not
    bool drawSign = false; 
    // notify parent that line segment was updated
    public event EventHandler SignatureUpdate; 
    public SignatureControl(){} 
    protected override void OnPaint(PaintEventArgs e) 
    {
      // we draw on the memory bitmap on mousemove so there
      // is nothing else to draw at this time 
      CreateGdiObjects();
      e.Graphics.DrawImage(bmp, 0, 0);
    } 
    protected override void OnPaintBackground(PaintEventArgs e) 
    {
      // don't pass to base since we paint everything, avoid flashing
    } 
    protected override void OnMouseDown(MouseEventArgs e) 
    {
      base.OnMouseDown(e); 
      // process if currently drawing signature
      if (!drawSign)
      {
        // start collecting points
        drawSign = true;
        
        // use current mouse click as the first point
        lastPoint.X = e.X;
        lastPoint.Y = e.Y;
      }
    } 
    protected override void OnMouseUp(MouseEventArgs e) 
    {
      
      base.OnMouseUp(e);
      // process if drawing signature
      if (drawSign)
      {
        // stop collecting points
        drawSign = false;   
      }                  
    }
  
    protected override void OnMouseMove(MouseEventArgs e)
    {
      base.OnMouseMove(e);      
      // process if drawing signature
      if (drawSign)
      { 
        // draw the new segment on the memory bitmap
        graphics.DrawLine(pen, lastPoint.X, lastPoint.Y, e.X, e.Y);
        pVector.Add(lastPoint.X+" "+lastPoint.Y+" "+e.X+" "+e.Y);
        // update the current position
        lastPoint.X = e.X;
        lastPoint.Y = e.Y; 
        // display the updated bitmap
        Invalidate();
      } 
    } 
    /// <summary>
    /// Clear the signature.
    /// </summary>
    public void Clear()
    {
      pVector.Clear();
      InitMemoryBitmap();
      Invalidate();
    } 
    /// <summary>
    /// Create any GDI objects required to draw signature.
    /// </summary>
    private void CreateGdiObjects()
    {
      // only create if don't have one or the size changed
      if (bmp == null || bmp.Width != this.Width ||
      bmp.Height != this.Height)
      {
        // memory bitmap to draw on
        InitMemoryBitmap();
      }
    }
    
    /// <summary>
    /// Create a memory bitmap that is used to draw the signature.
    /// </summary>
    private void InitMemoryBitmap()
    {
      // load the background image
      if (this.Background==null) 
      {
        bmp = new Bitmap(this.Width,this.Height);
        graphics = Graphics.FromImage(bmp);
        graphics.Clear(Color.White);
      }
      else 
      {
        bmp = new Bitmap(this.Background);
        // get graphics object now to make drawing during mousemove faster
        graphics = Graphics.FromImage(bmp);
      }
    }
    
    /// <summary>
    /// Notify container that a line segment has been added.
    /// </summary>
    private void RaiseSignatureUpdateEvent()
    {
      if (this.SignatureUpdate != null)
        SignatureUpdate(this, EventArgs.Empty);
    }
    
    private void CreateFile(String fileName,String fileContent) 
    {
      if (File.Exists(fileName)) 
      {
        Console.WriteLine("{0} already exists.", fileName);
        //return;
      }
      StreamWriter sr = File.CreateText(fileName);
      sr.WriteLine (fileContent);
      //MessageBox.Show("File creation complete");
      sr.Close();
    } 
    public void  StoreSigData(String fileName)
    {
      string sigData = "";
      for (int i = 0; i<pVector.Count ;i++) 
      {
        // This commented operation is used to convert decimal to hex and store
        //sigData = sigData + PadHex(int.Parse(xVector[i].ToString()))+" " + 
    //    PadHex(int.Parse(yVector[i].ToString()))+ "\n";
        sigData = sigData + pVector[i].ToString()+ "\n";
      }
      CreateFile(fileName,sigData); 
                              
    }
    // this method PadHex can be used to convert int to hex format //  
    // not in use
    private string PadHex(int inNum ) 
    {
      uint uiDecimal = checked((uint)System.Convert.ToUInt32(inNum));
      string s = String.Format("{0:x2}", uiDecimal);
      return(s);
    }        
  }
} 

/*****************************************************************/

//MainForm.cs File

using System;
using System.Drawing;
using System.Windows.Forms;
using System.Text;
//using Common;
using PocketSignature;

namespace SignCaptureV2
{
 /// <summary>
 /// Summary description for Form1.
 /// </summary>
 public class MainForm : System.Windows.Forms.Form
 {
  private System.Windows.Forms.Panel areaSignature;
  private System.Windows.Forms.Button butNew;
  private System.Windows.Forms.Button Save;
  private System.Windows.Forms.Button Load;

  SignatureControl signature = new SignatureControl();

  public MainForm()
  {
   //
   // Required for Windows Form Designer support
   //
   InitializeComponent();

   // add signature control to form
   signature.Location = areaSignature.Location;
   signature.Size = areaSignature.Size;
   signature.Background = "<a href="%22file://program/%22">\\Program</a> Files\\SignCaptureV2\\sign here.png";
   this.Controls.Add(signature);
  }
  /// <summary>
  /// Clean up any resources being used.
  /// </summary>
  protected override void Dispose( bool disposing )
  {
   base.Dispose( disposing );
  }
  #region Windows Form Designer generated code
  /// <summary>
  /// Required method for Designer support - do not modify
  /// the contents of this method with the code editor.
  /// </summary>
  private void InitializeComponent()
  {
   this.areaSignature = new System.Windows.Forms.Panel();
   this.butNew = new System.Windows.Forms.Button();
   this.Save = new System.Windows.Forms.Button();
   this.Load = new System.Windows.Forms.Button();
   // 
   // areaSignature
   // 
   this.areaSignature.BackColor = System.Drawing.Color.Gainsboro;
   this.areaSignature.Location = new System.Drawing.Point(8, 8);
   this.areaSignature.Size = new System.Drawing.Size(224, 120);
   this.areaSignature.Visible = false;
   // 
   // butNew
   // 
   this.butNew.Font = new System.Drawing.Font("Tahoma", 8.25F, 
    System.Drawing.FontStyle.Regular);
   this.butNew.Location = new System.Drawing.Point(0, 232);
   this.butNew.Size = new System.Drawing.Size(54, 24);
   this.butNew.Text = "Clear";
   this.butNew.Click += new System.EventHandler(this.butNew_Click);
   // 
   // Save
   // 
   this.Save.Font = new System.Drawing.Font("Tahoma", 8.25F, 
    System.Drawing.FontStyle.Regular);
   this.Save.Location = new System.Drawing.Point(64, 232);
   this.Save.Size = new System.Drawing.Size(72, 24);
   this.Save.Text = "Save in File";
   this.Save.Click += new System.EventHandler(this.Save_Click);
   // 
   // Load
   // 
   this.Load.Font = new System.Drawing.Font("Tahoma", 8.25F, 
    System.Drawing.FontStyle.Regular);
   this.Load.Location = new System.Drawing.Point(144, 232);
   this.Load.Size = new System.Drawing.Size(88, 24);
   this.Load.Text = "Load from file";
   this.Load.Click += new System.EventHandler(this.Load_Click);
   // 
   // MainForm
   // 
   this.ClientSize = new System.Drawing.Size(258, 270);
   this.Controls.Add(this.Load);
   this.Controls.Add(this.Save);
   this.Controls.Add(this.butNew);
   this.Controls.Add(this.areaSignature);
   this.Text = "PocketSignature";
  }
  #endregion

  /// <summary>
  /// The main entry point for the application.
  /// </summary>

  static void Main() 
  {
   Application.Run(new MainForm());
  }

  private void butNew_Click(object sender, System.EventArgs e)
  {
   signature.Clear();
   this.Refresh();
  }

  private void Save_Click(object sender, System.EventArgs e)
  {
   signature.StoreSigData("SignFile.txt");
  }

  private void Load_Click(object sender, System.EventArgs e)
  {
   int baseX = 10;
   int baseY = 100;
   string signatureFile = "SignFile.txt";
   load_signature(baseX,baseY,signatureFile);   
  }
  void load_signature(int baseX,int baseY,string signatureFile) 
  {
   System.IO.StreamReader streamReader
         = new System.IO.StreamReader("SignFile.txt");
   string pointString = null;
           
   while ((pointString = streamReader.ReadLine())!= null) 
   {
    if(pointString.Trim().Length>0)
    {
     String[] points = new String[4];
     points = pointString.Split(new Char[]{' '});
     Pen pen = new Pen(Color.Black);
     this.CreateGraphics().DrawLine(pen, 
    (baseX+int.Parse(points[0].ToString())), 
    (baseY+int.Parse(points[1].ToString())), 
    (baseX+int.Parse(points[2].ToString())), 
    (baseY+int.Parse(points[3].ToString())));
    }
   }
   streamReader.Close();
  }
 }
}

/***********SERVER CODE****************/

On the server side, the server gets the *.txt file from the client . You can use the SaveSignature.java to convert the *.txt file to a *.jpg file.

Java
/***************Converting the TXT file to JPG on server using Java ***********/

// SaveSignature.java File

import java.io.File;
import java.io.FileReader;
import java.io.BufferedReader;
import java.util.Vector;
import java.util.Hashtable;
import java.awt.Color;
import java.awt.Graphics;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.awt.image.RenderedImage;

public  class SaveSignature {
  static final int baseX = 25;
  static final int baseY = 25;
        public static void doExecute(String inputTxtFile,String outputGraphicsFile)
        throws Exception {
          Vector v = readFile(inputTxtFile);
          Hashtable ht = getMinMax(v);
          int minX = ( (Integer) ht.get("minX")).intValue();
          int maxX = ( (Integer) ht.get("maxX")).intValue();
          int minY = ( (Integer) ht.get("minY")).intValue();
          int maxY = ( (Integer) ht.get("maxY")).intValue();

          int width = maxX - minX + baseX;
          int height = maxY - minY + baseY;

          // Create a buffered image in which to draw
          BufferedImage bufferedImage = new BufferedImage
            (width, height, BufferedImage.TYPE_INT_RGB);

          // Create a graphics contents on the buffered image
          Graphics g2d = bufferedImage.createGraphics();
          g2d.setColor(Color.white);
          // Draw graphics
          // g2d.setColor(Color.red);
          g2d.fillRect(0, 0, width, height);
          drawImage(g2d, v, minX, minY);

          // Graphics context no longer needed so dispose it
          g2d.dispose();

          File file = new File(outputGraphicsFile);
          ImageIO.write(bufferedImage, "jpg", file);
        }

        private static void drawImage(Graphics g2d,Vector v, int minX,int minY)
        throws Exception{
          g2d.setColor(Color.black);
          for (int j =0; j<v.size();j++){
            String[] points = (String[]) v.get(j);
            int x1 =0;
            int y1 =0;
            int x2 =0;
            int y2 =0;
            for (int i = 0; i < 4; i++) {
             x1 =  Integer.parseInt(points[0])-minX+(baseX/2);
             x2 =  Integer.parseInt(points[2])-minX+(baseX/2);
             y1 =  Integer.parseInt(points[1])-minY+(baseY/2);
             y2 =  Integer.parseInt(points[3])-minY+(baseY/2);
            }
            g2d.drawLine(x1,y1,x2,y2);
          }
        }

       private static Hashtable getMinMax(Vector v) {
        Hashtable ht = new Hashtable();
        int minX =9999;
        int maxX =0;
        int minY =9999;
        int maxY =0;
        for (int j = 0; j< v.size();j++) {
          String[] points = (String[]) v.get(j);
          for (int i = 0; i < 4; i++) {
            minX = (Integer.parseInt(points[0])<minX) ? 
                        Integer.parseInt(points[0]):minX;
            minX = (Integer.parseInt(points[2])<minX) ? 
                        Integer.parseInt(points[2]):minX;
            maxX = (Integer.parseInt(points[0])>maxX) ? 
                        Integer.parseInt(points[0]):maxX;
            maxX = (Integer.parseInt(points[2])>maxX) ? 
                        Integer.parseInt(points[2]):maxX;

            minY = (Integer.parseInt(points[1]) < minY) ? 
                        Integer.parseInt(points[1]) : minY;
            minY = (Integer.parseInt(points[3]) < minY) ? 
                        Integer.parseInt(points[3]) : minY;
            maxY = (Integer.parseInt(points[1]) > maxY) ? 
                       Integer.parseInt(points[1]) : maxY;
            maxY = (Integer.parseInt(points[3]) > maxY) ? 
                       Integer.parseInt(points[3]) : maxY;
          }
        }
        ht.put("minX",new Integer(minX));
        ht.put("maxX", new Integer(maxX));
        ht.put("minY", new Integer(minY));
        ht.put("maxY", new Integer(maxY));
        return ht;
        }

       private static Vector readFile(String fileName)  throws Exception{
          Vector v = new Vector();
          FileReader fr = new FileReader(fileName);
          BufferedReader br = new BufferedReader(fr);
          String record = null;
          while ( (record = br.readLine()).trim().length() > 0) {
            String[] points = new String[4];
            points = record.split(" ");
            v.add(points);
          }
          return v;
        }

  public static void main(String[] args) {

    try {
      String inputTextFile = "c:\\MyFile.txt";
      String outputImageFile = "c:\\Sign.jpg";
      doExecute( inputTextFile,outputImageFile);

    } catch (Exception e) {
      System.out.println(" exception: "+ e.toString());
    }
  }
} 

Sample Files

The generated signature text file has contents like below:

/
****Sign.txt File **/

64 152 65 152
65 152 68 151
68 151 72 149
72 149 75 147
75 147 79 143
79 143 83 139
83 139 86 136
86 136 88 133
88 133 90 131
90 131 91 128
91 128 92 126
92 126 92 124
92 124 92 123
92 123 91 122
91 122 89 121
89 121 87 121
87 121 84 121
84 121 81 121
81 121 79 122
79 122 75 124
75 124 71 128
71 128 67 132
67 132 63 136
63 136 60 141
60 141 58 145
58 145 57 148
57 148 57 150
57 150 57 152
57 152 58 152
58 152 61 152
61 152 65 151
65 151 68 149
68 149 69 147
69 147 69 145
69 145 69 144
69 144 69 142
69 142 67 140
67 140 66 138
66 138 66 136
66 136 67 132
67 132 70 127
70 127 75 123
75 123 78 120
78 120 81 117
81 117 81 119
81 119 78 135
78 135 75 151
75 151 72 157
72 157 69 161
69 161 69 162
69 162 71 157
71 157 73 152
73 152 77 146
77 146 80 140
80 140 85 135
85 135 89 132
89 132 92 130
92 130 94 129
94 129 95 129
95 129 95 130
95 130 95 136
95 136 93 141
93 141 90 146
90 146 87 148
87 148 85 149
85 149 83 150
83 150 83 149
83 149 84 147
84 147 87 146
87 146 90 146
90 146 91 146
91 146 91 149
91 149 90 152
90 152 88 155
88 155 86 156
86 156 85 156
85 156 85 155
98 151 101 149
101 149 104 146
104 146 106 143
106 143 107 140
107 140 107 139
107 139 106 139
106 139 103 142
103 142 101 145
101 145 99 148
99 148 98 151
98 151 98 153
98 153 98 154
98 154 99 155
99 155 101 155
101 155 105 153
105 153 108 150
108 150 112 145
112 145 114 139
119 106 117 111
117 111 115 125
115 125 113 139
113 139 112 153
112 153 111 158
111 158 110 160
110 160 110 159
110 159 112 155
112 155 114 152
114 152 118 147
118 147 121 144
121 144 123 143
123 143 124 142
124 142 124 143
124 143 125 146
125 146 125 150
125 150 125 154
125 154 125 156
125 156 125 157
125 157 127 156
127 156 130 154
130 154 133 151
133 151 136 148
136 148 138 144
138 144 139 141
139 141 139 140
139 140 138 140
138 140 137 141
137 141 134 144
134 144 133 147
133 147 133 151
133 151 133 154
133 154 135 156
135 156 136 157
136 157 137 157
137 157 139 156
149 139 149 141
149 141 149 144
149 144 149 146
149 146 149 149
149 149 149 151
149 151 148 154
148 154 147 155
147 155 147 154
147 154 148 149
148 149 150 143
150 143 153 137
153 137 155 133
155 133 157 130
157 130 157 129
171 139 170 139
170 139 169 141
169 141 167 145
167 145 165 149
165 149 165 151
165 151 165 153
165 153 166 153
166 153 168 153
168 153 170 149
170 149 172 146
172 146 174 143
174 143 175 142
175 142 175 141
175 141 176 143
176 143 176 145
176 145 177 146
177 146 178 147

History

  • 26th February, 2004: Initial post

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
India India
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralWebform & Winform Signature Capture Pin
jay_dubal31-Mar-11 0:17
jay_dubal31-Mar-11 0:17 
GeneralConvert to JPEG for C# Pin
Jeff Doolittle23-Dec-08 19:41
Jeff Doolittle23-Dec-08 19:41 
GeneralMake sure that the Image Size is correct Pin
draphael13-Nov-05 22:52
draphael13-Nov-05 22:52 
GeneralA web control for signature capture Pin
Anonymous7-Jun-05 11:47
Anonymous7-Jun-05 11:47 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.