Tuesday, December 24, 2024

How to Collect Hand-written Signatures on the Intertnet

Share

I always thought it would be useful to collect hand-written signatures via the internet. One day, one of my employers mentioned the same thing.

This put me into research mode. In the end, I found a method to collect signatures and convert them to jpgs. The solution involved several third party items which happened to be priced right (free) and a little bit of Coldfusion code. The only difficult part of this application will be getting the third party items installed on your server.

This signature collecting applications has been around for a while. Recently, I was told that we are going to finally upgrade the server that hosts the signature collection application to MX. I dug up this tool to try it out on MX. It crashed and burned. So, most of the code that follows is for CF5. However, I did find a work around that you can use to make the code work on MX. The problem with MX stems from the cfmagick custom tag and the changes to the cfexecute tag from CF5 to MX. Alagad, the creator of the magicktag, has created a component that supposedly works. I haven’t tried it. Read more about the component at http://www.alagad.com/index.cfm/name-mtinfo.

In order to make this work, you will need the following items:

  • ImageMagick 5.5.7-Q8 (www.imagemagick.org)- A image manipulation program. A newer version is out, but you need to use this version due to the limitations of the MagickTag. This program allows the user to rotate, invert, resize, and draw on images (as well as many other things). There is a demo on the site so you can view its functionality. We will only be using the draw part.
  • MagickTag (www.alagad.com/index.cfm/name-mtinfo) – a set of custom tags from Alagad that are used to manipulate images using ImageMagick. In the case of this tutorial, it takes the signature from the applet, draws it on a images and saves it.
  • Goetz’s Signature Applet (http://www.lawrencegoetz.com/programs/signature/) – a java applet that allows a user to draw in a container using the mouse. The image is converted to a string of digits which is passed from the form like an input field.
  • ImageMagick may make this tutorial impossible for some. It will have to be installed on your server. The custom tags shouldn’t be difficult for anyone. Just download them and drop them into a directory The applet just goes into the directory as well. Just as an aside, the author of the applet has already performed the signature capture and conversion to an image using Perl. However, it cost 20 bucks to get his script, it is written in Perl, and it uses another image manipulation program. I didn’t have 20 bucks to drop on the code and I have never used Perl before so this wasn’t any help to me.

    Once you have ImageMagick installed, create a directory to hold your work for this tutorial. Unzip the contents of the MagickTag download to this directory. You will have to modify the config.cfm file. The path to ImageMagick will need to be changed to match its location on your server.

    <!--- set pathToImageMagick to be the path to the directory containing the image Magick executables --->
    <CFset pathToImageMagick = "C:\Program Files\ImageMagick-5.5.7-Q8">

    Now, download the signature applet. Put sign.class and scanvas.class into your working directory. Next, you need to put a jpg file into the directory. This jpg file will be used every time you make a signature image. It is your base that the image is drawn on. I created a blank jpg and named it default.jpg. Its dimensions are 476 x 299. Everything should be ready to work now. Your directory should look like this

    buildActionString.cfm
    composite.syn
    config.sys
    convert.syn
    default.jpg
    identify.syn
    LICENSE.txt
    MagickAction.cfm
    MagickTag.cfm
    mogrify.syn
    montage.syn
    README.txt
    savecontent.cfm
    validateAction.cfm
    validateAttrs.cfm

    Now, it’s time to create a page to collect the signature. Use the following page. Name it index.cfm

    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
    &nbsp <html>
    &nbsp <head>
    &nbsp&nbsp&nbsp&nbsp <title>Signature Test</title>
    &nbsp </head>
    &nbsp <body>
    &nbsp <form name="frmSignature"
    &nbsp action="act_DrawPicture.cfm"
    &nbsp method="post"
    &nbsp onSubmit="if (sign) { signature.value=sign.signature();}">
    &nbsp <table width="640px" border="1" cellpadding="2">
    &nbsp <tr>
    &nbsp <td bordercolor="#000000" width="100%">
    &nbsp <applet code=sign.class name=sign width=640 height=300 name="sign">
    &nbsp </applet>
    &nbsp </td>
    &nbsp </tr>
    &nbsp <tr>
    &nbsp <td align="center">
    &nbsp <INPUT TYPE="hidden"
    &nbsp NAME="signature" value="">
    &nbsp <input type="Submit">
    &nbsp <input type="button"
    &nbsp value="Clear"
    &nbsp onclick="if (sign) { sign.clear();}">
    &nbsp </td>
    &nbsp </tr>
    &nbsp </table>
    &nbsp </form>
    &nbsp </body>
    &nbsp </html>

    Nothing ground breaking on this page. The only thing that might cause questions is the onsubmit function. This is required by the applet. Same for the code in the clear button. If you want to clear the applet, you need this code.

    The following is the action page. It should be named: act_DrawPicture.cfm. There are some comments in the code that explain what is going on.

    <CFHEADER Name="Expires" Value="#Now()#">
    &nbsp <CFHEADER Name="cache-control" Value="No-Cache, no-store, must-revalidate">
    &nbsp <html>
    &nbsp <head>
    &nbsp&nbsp&nbsp&nbsp <title>Signature Test</title>
    &nbsp </head>
    &nbsp <cfoutput>
    &nbsp <body>
    &nbsp <!--- This holds the list of points that come in from the signature applet. --->
    &nbsp <cfset lstA="">
    &nbsp <!--- Define a loop Counter --->
    &nbsp <cfset intA="1">
    &nbsp <!--- Signature is a list of points. The following loop takes the list and--->
    &nbsp <!--- converts it into a new list of points. The new list breaks the points --->
    &nbsp <!--- into groups of 4. This allows them to be fed into the cfmagick tag.--->
    &nbsp <cfloop list="#Signature#" index="i" delimiters=" ">
    &nbsp <cfset lstA=lstA & i>
    &nbsp <cfif intA mod 4 is 0>
    &nbsp <cfset lstA=lstA & "|">
    &nbsp <cfelse>
    &nbsp <cfset lstA=lstA & ",">
    &nbsp </cfif>
    &nbsp <cfset intA = intA + 1>
    &nbsp </cfloop>
    &nbsp <cfset lstA = left(lstA,len(lstA)-1)>
    &nbsp <!--- Create a list of commands for the action tag. This command will be of the form --->
    &nbsp <!--- Line x1, y1, x2, y2. This will inform the ImageMagick to draw a line from --->
    &nbsp <!--- (x1,y1) to (x2,y2). Since the signature is a compilation of lines, we will --->
    &nbsp <!--- make a list of commands. --->
    &nbsp <cfset lstPoints="">
    &nbsp <cfloop list="#lstA#" index="i" delimiters="|">
    &nbsp <cfset lstPoints=lstPoints & " Line #listgetat(i,1)#,#listgetat(i,2)# #listgetat(i,3)#,#listgetat(i,4)# ~">
    &nbsp </cfloop>
    &nbsp <!--- Create a unique file name. We don't want to overwrite default.jpg. --->
    &nbsp <cfset strOutFile=CreateUUID()>
    &nbsp <!--- This is the action tag. It takes the list of commands created above and draws the lines --->
    &nbsp <!--- that form the signature. You have to loop over the action part because it has a--->
    &nbsp <!--- limit on size; else we could have fed the whole command list in.--->
    &nbsp <cf_MagickTag action="convert"
    &nbsp inputType="file"
    &nbsp inputfile="C:\InetPub\wwwroot\CFMTest\Tutorials\Signature\default.jpg"
    &nbsp outputType="file"
    &nbsp outputfile="C:\InetPub\wwwroot\CFMTest\Tutorials\Signature\#strOutFile#.jpg"
    &nbsp TimeOut="300">

    &nbsp <cf_MagickAction action="stroke" color="black">
    &nbsp <cfloop list="#lstPoints#" index="i" delimiters="~">
    &nbsp <cf_MagickAction action="draw" string="#i#">
    &nbsp </cfloop>
    &nbsp </cf_MagickTag>
    &nbsp <!--- this timer gives the server time to save the picture --->
    &nbsp <scrpt language="JavaScript">
    &nbsp function pause(numberMillis)
    &nbsp {
    &nbsp var now = new Date();
    &nbsp var exitTime= now.getTime() + numberMillis;
    &nbsp while(true)
    &nbsp {
    &nbsp now= new Date();
    &nbsp if (now.getTime() > exitTime)
    &nbsp return;
    &nbsp }
    &nbsp }
    &nbsp pause(3000);
    &nbsp </scrpt>
    &nbsp <!--- This is the string of digits that were created by the signature applet.--->
    &nbsp <!--- This could easily be stored in a database for future use. --->
    &nbsp <div align="center">
    &nbsp <table>
    &nbsp <tr>
    &nbsp&nbsp&nbsp&nbsp <td>
    &nbsp&nbsp&nbsp&nbsp Signature String: this can be saved to a database and then fed back to the applet to
    &nbsp&nbsp&nbsp&nbsp recreate the signature.
    &nbsp&nbsp&nbsp&nbsp </td>
    &nbsp </tr>
    &nbsp <tr>
    &nbsp <td>#signature#</td>
    &nbsp </tr>
    &nbsp <tr>
    &nbsp <td align="center">
    &nbsp <img src="#strOutFile#.jpg" width="476" height="299" alt="" border="1">
    &nbsp </td>
    &nbsp </tr>
    &nbsp </table>
    &nbsp </div>
    &nbsp </body>
    &nbsp </cfoutput>
    &nbsp </html>

    Put both of the files above into your working directory. You will need to change inputfile and output file in the call to the MagickTag so that it points to your working directory. Once all of these files are in your working directory, open your browser and call index.cfm. Draw your picture and click “Submit Query”. After a few seconds, you should see the signature converted to a jpg along with the signature digit string (which can be saved and then fed back into the applet to recreate the signature). It is important to note that the above code only works with CF5. If the code above were used on MX, all that it would return is the signature string and a blank jpg.

    I was disappointed when I tried the above code on MX and it did not work. I was even more disappointed when I read that the component that should be used for MX was not free. Not ready to give up, I started tracing the iternal code in MagickTag.cfm to see how it works. I found a relatively simple solution. Before I give this solution, I must warn you that I am using MX with the 6.1 updater installed along with the hotfix issued on 09/02/2004. So, the hack may not work with your version of MX.

    To hack the tag in order to make this work, you need to open MagickTag.cfm. Around line 290, you will find the following code:

    <cfif isdefined('server.ColdFusion.ProductVersion') AND ListFirst(server.ColdFusion.ProductVersion) GTE 6>
    &nbsp <!--- CFMX dosn't like having quotation marks in the arguments attribute --->
    &nbsp <cfexecute name="#pathToImageMagick##executableFile#"
    &nbsp arguments="#replace(actionString, '"', '', "All")#"
    &nbsp timeout="#timeout#">
    &nbsp </cfexecute>
    &nbsp <cfelse>
    &nbsp <!--- This is here for CF 4.5 and 5 versions --->
    &nbsp <cfexecute name="#pathToImageMagick##executableFile#"
    &nbsp arguments="#actionString#"
    &nbsp timeout="#timeout#">
    &nbsp </cfexecute>
    &nbsp </cfif>

    Replace the above code with the following:

    &nbsp <cfif isdefined('server.ColdFusion.ProductVersion') AND ListFirst(server.ColdFusion.ProductVersion) GTE 6>
    &nbsp <!--- CFMX dosn't like having quotation marks in the arguments attribute --->
    &nbsp <!--- <cfexecute name="#pathToImageMagick##executableFile#"
    &nbsp arguments="#replace(actionString, '"', '', "All")#"
    &nbsp timeout="#timeout#"> --->
    &nbsp <cfexecute name="#pathToImageMagick##executableFile#"
    &nbsp arguments="#actionString#"
    &nbsp timeout="#timeout#">
    &nbsp </cfexecute>
    &nbsp <cfelse>
    &nbsp <!--- This is here for CF 4.5 and 5 versions --->
    &nbsp <cfexecute name="#pathToImageMagick##executableFile#"
    &nbsp arguments="#actionString#"
    &nbsp timeout="#timeout#">
    &nbsp </cfexecute>
    &nbsp </cfif>

    All that I did was replace the first cfexecute (for mx) with the second cfexecute (for 5 and 4.5). I will not promise that this will work for any other ImageMagick commands or any version of ColdFusion. I just know that it works for the set up I described above. By making the change, you may be crippling all other capabilites of the MagickTag. I don’t use any of its other functionality so I do not know.

    This little tool has a lot of potential. One would be diagraming pictures. I emailed the creator of the applet to ask if there was a way to load an image into the background of the applet. Unfortunately, he never contacted me so I don’t know if it is possible or not. If you know how do something like this, post a comment about the process.

    Disclaimer: This work is original, unless otherwise mentioned. I am not endorsing any of the third party products used in this tutorial. This application may not work with all versions of Coldfusion, use at your own risk.

    Jerry Barnes is a ColdFusion Programmer.

    Table of contents

    Read more

    Local News