Signature Capture
June 19, 2012 2 Comments
I was chatting with another member of TriNug last night and we talked about the best way to capture a user’s signature. There are some pretty good articles out there, including this one. I dove right in with a WinForm application and a Panel control and built something like this:
Visual Studio 2010 and WinForms made this a pretty straightforward task. First, I built up my domain model starting with the points that are captured from the Panel.MouseMove event handler (MouseEventArgs) with the e.Location of type Point.
I created a Line class that has 2 points that make up the line:
[Serializable] public class Line { public Line() { } public Line(Point startPoint, Point endPoint) { this.StartPoint = startPoint; this.EndPoint = endPoint; } public Point StartPoint { get; set; } public Point EndPoint { get; set; } }
I then created a Glyph class that is a collection of those lines:
[Serializable] public class Glyph { public Glyph() { this.Lines = new List<Line>(); } public List<Line> Lines { get; set; } }
I could have called this class “letter” but each glyph may or may not be a letter – it could be several letters strung together in cursive, a part of a letter (the dot of the i), or may not be any letter (like when R2D2 signs his name. R2D2 is a boy, right?)
Anyway, I then created a Signature class that is a collection of glyphs:
[Serializable] public class Signature { public Signature() { this.Glyphs = new List<Glyph>(); } public List<Glyph> Glyphs { get; set; } }
If this was used in a real-world application, this class would have a uniqueId so you can relate it back to your people/user class. It might have some other properties to allow easy comparison and analysis.
I then coded in my WinForm a way to capture the user’s mouse strokes and putting them into this signature graph. First, I started with some form level variables:
Boolean IsCapturing = false; private Point startPoint; private Point endPoint; Pen pen = new Pen(Color.Black); Glyph glyph = null; Signature signature = new Signature(); String fileName = @"signature.xml";
I then handled three events from the panel:
private void SignaturePanel_MouseMove(object sender, MouseEventArgs e) { if (IsCapturing) { if (startPoint.IsEmpty && endPoint.IsEmpty) { endPoint = e.Location; } else { startPoint = endPoint; endPoint = e.Location; Line line = new Line(startPoint, endPoint); glyph.Lines.Add(line); DrawLine(line); } } }
private void SignaturePanel_MouseUp(object sender, MouseEventArgs e) { IsCapturing = false; signature.Glyphs.Add(glyph); startPoint = new Point(); endPoint = new Point(); } private void SignaturePanel_MouseDown(object sender, MouseEventArgs e) { IsCapturing = true; glyph = new Glyph(); }
Basically, every time a user clicks down it starts capturing the points and turning them into lines. When the user clicks up, all of those lines go into 1 glyph. Capture…Rinse….Repeat.
There is 1 supporting function (DrawLine) that looks like this to render it to the screen:
private void DrawLine(Line line) { using (Graphics graphic = this.SignaturePanel.CreateGraphics()) { graphic.DrawLine(pen, line.StartPoint, line.EndPoint); } }
A couple of notes on the code blocks above.
- The Point struct has a convenience property called IsEmpty. This resolves to X and Y equaling 0.
- The Point Equals overload resolved to the X and Y values. If you have 2 instances of a Point with the same X and Y, they are equal.
Once the signature is captured on the panel, I needed a way to save the signature. Most people capture the image. Me, I want the signature transformed into structured data for better down-steam analysis. Therefore, I decided to put it into XML. To that end, I marked all of the classes in the Signature graph as Serializable. I then created two functions to push and pull the signature out of a XML file:
private void SerializeSignature() { XmlSerializer serializer = new XmlSerializer(typeof(Signature)); if (File.Exists(fileName)) { File.Delete(fileName); } using (TextWriter textWriter = new StreamWriter(fileName)) { serializer.Serialize(textWriter, signature); textWriter.Close(); } }
And
private void DeserializeSignature() { XmlSerializer deserializer = new XmlSerializer(typeof(Signature)); using (TextReader textReader = new StreamReader(fileName)) { signature = (Signature)deserializer.Deserialize(textReader); textReader.Close(); } }
I then wired up the button clicks to save the signature:
private void ExportButton_Click(object sender, EventArgs e) { SerializeSignature(); }
and to load it:
private void ImportButton_Click(object sender, EventArgs e) { DeserializeSignature(); ClearSignaturePanel(); DrawSignature(); }
and these are the two other supporting methods:
private void ClearSignaturePanel() { using (Graphics graphic = this.SignaturePanel.CreateGraphics()) { SolidBrush solidBrush = new SolidBrush(Color.LightBlue); graphic.FillRectangle(solidBrush, 0, 0, SignaturePanel.Width, SignaturePanel.Height); } }
and
private void DrawSignature() { foreach (Glyph glyph in signature.Glyphs) { foreach (Line line in glyph.Lines) { DrawLine(line); } } }
Sure enough, here is the XML in the file:
The problem is that this file is larger than a image file – but much more analyzable (and yes, that is a real word). I then wanted to display the signature in a web browser. To that end, I fired up a classic ASP.NET application and created a web control. In the code, I rendered the points out of the XML file back into a graphic:
protected void Page_Load(object sender, EventArgs e) { this.Response.ContentType = "image/gif"; using (Bitmap bitmap = new Bitmap(200, 100)) { using (Graphics graphics = Graphics.FromImage(bitmap)) { SignatureFactory signatureFactory = new SignatureFactory(); String fileName = @"C:\signature.xml"; Signature signature = signatureFactory.LoadSignatureFromFileSystem(fileName); Pen pen = new Pen(Color.Red); foreach (Glyph glyph in signature.Glyphs) { foreach (Line line in glyph.Lines) { graphics.DrawLine(pen, line.StartPoint.X, line.StartPoint.Y, line.EndPoint.X, line.EndPoint.Y); } } bitmap.Save(Response.OutputStream, System.Drawing.Imaging.ImageFormat.Gif); } } }
And the output on a web form:
hello sir,
this code is more efficient for window application ,but i want to do this in web application ,then how can i done this feature website ,please provide some solution …
and also describe SignatureFactory directory or library ,how can i add this library and from ..
please revert back soon…
thanks
THANK YOU! I have been messing around for hours trying to get a signature capture in a windows form to work.