Posted
A client requested a form that could be signed. They were using it to conduct household surveys and wanted to record the form details with a facsimile signature of the householder (and the interviewer in the actual application). The only code that I could find to do this was 'Signature Pad', a jQuery plug-in. This FAQ gives and overview of using the plug-in with ChronoForms.
Signature Pad is a nice jQuery plug-in by Thomas J Bradley that uses an HTML canvas element to capture a signature into a JSON string so that it can be re-created later. Think of this as like 'join the dots', the code records a series of dots that can be 'joined up' to show a facsimile signature. The JSON string is a standard way of saving arrays of data in web applications.
The Signature Pad plug-in is available here where there is also some good documentation. See also the demonstration page here. In this tutorial I'll focus mostly on the ChronoForms inplementation.
One immediate problem is that this is a jQuery plug-in and they are not always compatible with the MooTools library used by Joomla! and by ChronoForms. Fortunately in this case it was solved fairly easily.
Setting up the scripts
I downloaded the Signature Pad package and copied the files I needed into a /components/com_chronoforms/extras/signature_pad folder on my site. You can use any site folder, this is a structure I use for adding extras into ChronoForms.
The files I copied were journal.ttf, journal.woff, jquery-1.8.3.min.js, jquery.signaturepad.css, jquery.signaturepad.min.js, json2.min.js
Note: if JQuery is already running on your site as it frequently is, then you may not need to load another version here.
To get these files loaded with the form I added a Custom Code action into the form On Load event with this code in it:
<?php $doc= & JFactory::getDocument(); $url = JURI::root().'/components/com_chronoforms/extras/signature_pad/'; $doc->addScript($url.'jquery-1.8.3.min.js'); $doc->addScript($url.'jquery.signaturepad.min.js'); $doc->addScript($url.'json2.min.js'); $doc->addStylesheet($url.'jquery.signaturepad.css'); // put jQuery into No Conflict mode $script = " if (typeof jQuery != 'undefined' ) { jQuery.noConflict(); } "; $doc->addScriptDeclaration($script); // initialise the Signature pad $script = " jQuery(document).ready(function () { var oc_options = { defaultAction: 'drawIt', lineTop: '100', name: '#oc_name', output: '#oc_sig', canvas: '#oc_pad', drawOnly: true }; jQuery('#oc_spad').signaturePad(oc_options); }); "; $doc->addScriptDeclaration($script); ?>
The first block of code loads the necessary files into the Joomla! page header. The second block puts JQuery into No Conflict mode and the third block initialises the Signature Pad. Note that we use jQuery instead of $ in this code to avoid confusion with the MooTools use of $.
Setting up the HTML
The HTML for the signature block was set up using a Custom Element element from the Advanced group and dragging it to the Preview box. The HTML was modifies a little from the sample on the Signature Pad site to fit with ChronoForms.
<?php $doc =& JFactory::getDocument(); $style = " .spad_container_div { width: 410px; } "; $doc->addStyleDeclaration($style); ?> <div class="ccms_form_element cfdiv_custom spad_container_div" id="oc_spad" > <h3>Occupier’s signature</h3> <label for="oc_name">Please print your name</label> <input type="text" name="oc_name" id="oc_name" class="oc_name" /> <!--<p class="typeItDesc">Review your signature</p>--> <p class="drawItDesc">Draw your signature</p> <ul class="sigNav"> <!--<li class="typeIt"> <a href="#type-it" class="current">Type It</a> </li>--> <li class="drawIt"> <a href="#draw-it">Draw It</a> </li> <li class="clearButton"> <a href="#clear">Clear</a> </li> </ul> <div class="sig sigWrapper"> <div class="typed"></div> <canvas class="pad" id='oc_pad' width="400" height="150" ></canvas> <input type="hidden" name="oc_sig" id="oc_sig" class="output" /> </div> </div>
The CSS declaration at the top is to make the containing div wider, the signature box needs to be be quite big. The commented out parts are an alternative option for the user to add a typed signature that is shown in a hand-writing font - this did not meet the client's need and so was removed.
Note also the hidden input at the end that is used to submit the JSON string from the form.
This is all that is needed to show the signature pad in the form.
Processing the submitted signature
What is submitted is a long string of data-points - nothing that is recognisable as a signature. There are two main ways of processing this for the record. One is to save the raw data which can then be used later to 're-draw' a signature; the second is to create an image of the signature and save that (in fact we did both of these). The code below was added into a Custom Code action in the On Submit event.
Recreate with JavaScript
This is the code needed to re-create a facsimile version using JavaScript:
<?php $doc= & JFactory::getDocument(); $url = JURI::root().'/components/com_chronoforms/extras/signature_pad/'; $doc->addScript($url.'jquery-1.8.3.min.js'); $doc->addScript($url.'jquery.signaturepad.min.js'); $doc->addScript($url.'json2.min.js'); $doc->addStylesheet($url.'jquery.signaturepad.css'); $script = " if (typeof jQuery != 'undefined' ) { jQuery.noConflict(); } "; $doc->addScriptDeclaration($script); $script = " var oc_sig = {$form->data['oc_sig']}; jQuery(document).ready(function () { jQuery('.oc_sigPad').signaturePad({displayOnly:true}).regenerate(oc_sig); }); "; $doc->addScriptDeclaration($script); ?>
The end result of this is to recreate the signature into an HTML canvas element on the Thank You page.
The corresponding HTML is
<h3>Occupier’s signature</h3> <div class="oc_sigPad signed"> <h4>Javascript recreated signature</h4> <div class="sigWrapper" style='height:150px;' > <div class="typed"><?php echo $form->data['oc_name']; ?></div> <canvas class="pad" width="400" height="150" ></canvas> </div> </div>
Note: The canvas element is identified by the wrapping <div class='oc_sigPad' . . .
Creating a facsimile image
There is a script available through the Signature Pad site that will convert the JSON string of data points into an image. This file signature-to-image.php was also uploaded to the extras/signature_pad folder. By default this just creates an image of the signature, for record keeping purposes I modified implementation so that the image also contained the data and time and the signer's name. This removes reliance on any external file meta data to identify the image.
The code to create this was added in the same Custom Code action as used in the section above:
<?php include JPATH_SITE.'/components/com_chronoforms/extras/signature_pad/signature-to-image.php'; $date = new DateTime(null, new DateTimeZone('Europe/London')); $oc_sig = createSigImage( 'oc', $form->data, $date ); if ( is_array( $oc_sig ) ) { $form->data = array_merge( $form->data, $oc_sig ); } function createSigImage( $sig_name, $data, $date ) { $img = sigJsonToImage( $data[$sig_name.'_sig'], array( 'imageSize' => array( 400, 150 ) ) ); $grey = imagecolorallocate( $img, 0x88, 0x88, 0x88 ); $font = JPATH_SITE.'/components/com_chronoforms/extras/signature_pad/RobotoCondensed-Regular.ttf'; $name_string = $data[$sig_name.'_name'].' '.$date->format( 'd M Y H:i' ); imagefttext( $img, 10, 0, 5, 145, $grey, $font, $name_string ); $name_string = array(); $name_string[] = $date->format( 'Y-m-d_H-i' ); $name_string[] = slugify( trim( strrchr( $data[$sig_name.'_name'], ' ' ) ) ); $name_string[] = base64_encode( $data[$sig_name.'_name'] ); $name_string[] = rand( 1000, 9999 ); $name_string[] = $sig_name; $name_string = implode( '_', $name_string ); $img_path = JPATH_SITE.'/components/com_chronoforms/uploads/signatures/'.$name_string.'_signature.png'; $img_url = JURI::root().'components/com_chronoforms/uploads/signatures/'.$name_string.'_signature.png'; $png_made = imagepng( $img, $img_path ); $return = false; if ( $png_made ) { $return = array( $sig_name.'_sig_path' => $img_path, $sig_name.'_sig_url' => $img_url, $sig_name.'_sig_name' => $name_string.'_signature.png' ); } return $return; } function slugify( $text ) { // Swap out Non "Letters" with a - $text = preg_replace( '/[^\pL\d]+/u', '-', $text ); // Trim out extra -'s $text = trim( $text, '-' ); // Convert letters that we have left to the closest ASCII representation $text = iconv( 'utf-8', 'us-ascii//TRANSLIT', $text ); // Make text lowercase $text = strtolower( $text ); // Strip out anything we haven't been able to convert $text = preg_replace( '/[^-\w]+/', '', $text ); return $text; } ?>
Note that the majority of this code is written to make sure that there is an annoymous yet unique and URL friendly file name. You may not need all of that.
Finally we need a little HTML to display the resulting image. This can be used in the Thank You page, or in an Email template.
<div> <h4>Saved image signature</h4> <img src='<?php echo $form->data['oc_sig_url']; ?>' /> </div>
That's it.