Solved

Issues with signature

Posted on 2016-10-18
19
54 Views
Last Modified: 2016-10-19
Im working on a signature for a form as at the end of the form i need to capture the signature of the users filling it out.

I have two issues which are when on a mobile device (i.e mobile or tablet) the code doesnt work to fill the box in.

The second issue is that i am unable to get the signature into the database.

I have tried to get the information into the database two ways one through direct php taking the variable #debug and attmepting to insert that into the database. The other way is the way in the documents attached through ajax post url.

In theory i would like all this done on one page if possible. Can anyone shed some light on these two areas im having issues with.

SIGN.PHP ->
<!DOCTYPE html>
<html>
<head>
  <meta http-equiv="content-type" content="text/html; charset=UTF-8">
  <meta name="robots" content="noindex, nofollow">
  <meta name="googlebot" content="noindex, nofollow">

  
  

  
  
  

  

  <script type="text/javascript" src="//code.jquery.com/jquery-1.8.3.js"></script>

  

  

  <style type="text/css">
    
  </style>

  <title></title>

  
    




<script type='text/javascript'>//<![CDATA[
$(window).load(function(){
$(document).ready(function () {
    /** Set Canvas Size **/
    var canvasWidth = 400;
    var canvasHeight = 75;

    /** IE SUPPORT **/
    var canvasDiv = document.getElementById('signaturePad');
    canvas = document.createElement('canvas');
    canvas.setAttribute('width', canvasWidth);
    canvas.setAttribute('height', canvasHeight);
    canvas.setAttribute('id', 'canvas');
    canvasDiv.appendChild(canvas);
    if (typeof G_vmlCanvasManager != 'undefined') {
        canvas = G_vmlCanvasManager.initElement(canvas);
    }
    var context = canvas.getContext("2d");

    var clickX = new Array();
    var clickY = new Array();
    var clickDrag = new Array();
    var paint;

    /** Redraw the Canvas **/
    function redraw() {
        canvas.width = canvas.width; // Clears the canvas

        context.strokeStyle = "#000000";
        context.lineJoin = "miter";
        context.lineWidth = 2;

        for (var i = 0; i < clickX.length; i++) {
            context.beginPath();
            if (clickDrag[i] && i) {
                context.moveTo(clickX[i - 1], clickY[i - 1]);
            } else {
                context.moveTo(clickX[i] - 1, clickY[i]);
            }
            context.lineTo(clickX[i], clickY[i]);
            context.closePath();
            context.stroke();
        }
    }

    /** Save Canvas **/
    $("#saveSig").click(function saveSig() {
        //encode URI
        var sigData = encodeURIComponent(canvas.toDataURL("image/png"));
        $("#imgData").html('Thank you! Your signature was saved');
        var ajax = XMLHttpRequest();
        ajax.open("POST", 'post-sign.php');
        ajax.setRequestHeader('Content-Type', 'application/upload');
        ajax.send(sigData);
        $('#debug').html(sigData);
    });

    /** Clear Canvas **/
    $('#clearSig').click(
        function clearSig() {
            clickX = new Array();
            clickY = new Array();
            clickDrag = new Array();
            context.clearRect(0, 0, canvas.width, canvas.height);
    });

    /**Draw when moving over Canvas **/
    $('#signaturePad').mousemove(function (e) {
        if (paint) {
            addClick(e.pageX - this.offsetLeft, e.pageY - this.offsetTop, true);
            redraw();
        }
    });

    /**Stop Drawing on Mouseup **/
    $('#signaturePad').mouseup(function (e) {
        paint = false;
    });

    /** Starting a Click **/
    function addClick(x, y, dragging) {
        clickX.push(x);
        clickY.push(y);
        clickDrag.push(dragging);
    }

    $('#signaturePad').mousedown(function (e) {
        var mouseX = e.pageX - this.offsetLeft;
        var mouseY = e.pageY - this.offsetTop;

        paint = true;
        addClick(e.pageX - this.offsetLeft, e.pageY - this.offsetTop);
        redraw();
    });

});
});//]]> 

</script>

  
</head>

<body>
  <center>
    <fieldset style="width: 435px">
        <br/>
        <br/>
        <div id="signaturePad" style="border: 1px solid #ccc; height: 55px; width: 400px;"></div>
        <br/>
        <button id="clearSig" type="button">Clear Signature</button>&nbsp;
        <button id="saveSig" type="button">Save Signature</button>
        <div id="imgData"></div>
        <!-- removed double imgData here -->
        <br/>
        <br/>
    </fieldset>
</center>
<div id="debug"></div>
  
</body>

</html>

Open in new window


DB FILE ->
--
-- Table structure for table `pad`
--

CREATE TABLE IF NOT EXISTS `pad` (
  `id` int(10) NOT NULL AUTO_INCREMENT,
  `session` varchar(255) NOT NULL,
  `image_location` varchar(255) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;

/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;

Open in new window


POST-SIGN ->
   <?php
    if (isset($GLOBALS["HTTP_RAW_POST_DATA"]))
    {
    $session_id = $_SERVER['REMOTE_ADDR'];
    // Get the data
    $imageData=$GLOBALS['HTTP_RAW_POST_DATA'];

    $filteredData=substr($imageData, strpos($imageData, ",")+1);

    $unencodedData=base64_decode($filteredData);

    $imageName = "sign_" . rand(5,1000) . rand(1, 10) . rand(10000, 150000) . rand(1500, 100000000) . ".png";

    $filepath = "uploads/signature/" . $imageName;

    $fp = fopen("$filepath", 'wb' );
    fwrite( $fp, $unencodedData);
    fclose( $fp );

    //Connect to a mySQL database and store the user's information so you can link to it later
    $link = mysql_connect('localhost','USER', 'PASS') OR DIE(mysql_error);
    mysql_select_db("DB", $link);
    mysql_query("INSERT INTO pad (`session`, `image_location`) VALUES ('$session_id', '$imageName')") OR DIE(mysql_error());
    mysql_close($link);
    }
    ?>

Open in new window


Thank you :)
pad--1-.sql
sign.php
0
Comment
Question by:RiccardoQuest
  • 9
  • 5
  • 5
19 Comments
 
LVL 51

Assisted Solution

by:Julian Hansen
Julian Hansen earned 375 total points
ID: 41848094
This works for me as a save script
<?php

$session_id = getRemoteIP();
$imageName = "sign_" . rand(5,1000) . rand(1, 10) . rand(10000, 150000) . rand(1500, 100000000) . ".png";

$rawdata = file_get_contents("php://input");
$imgdata = substr($rawdata, 22);
$imgdata = base64_decode($imgdata);
file_put_contents($imageName, $imgdata);

$mysqli = new mysqli('localhost','USER','PASS','DB');
if ($mysqli->connect_error) {
	die('Connect Error (' . $mysqli->connect_errno . ') '
		. $mysqli->connect_error);
}

$query = <<< QUERY
INSERT INTO pad (`session`, `image_location`) 
VALUES ('{$session_id}', '{$imageName}')
QUERY;
$result = $mysqli->query($query);

if ($result) {
	// Success
}
else {
	// Fail
}

$mysqli->close();

function getRemoteIP()
{
	$ipaddress = '';
	if (isset($_SERVER['HTTP_CLIENT_IP']))
		$ipaddress = $_SERVER['HTTP_CLIENT_IP'];
	else if(isset($_SERVER['HTTP_X_FORWARDED_FOR']))
		$ipaddress = $_SERVER['HTTP_X_FORWARDED_FOR'];
	else if(isset($_SERVER['HTTP_X_FORWARDED']))
		$ipaddress = $_SERVER['HTTP_X_FORWARDED'];
	else if(isset($_SERVER['HTTP_FORWARDED_FOR']))
		$ipaddress = $_SERVER['HTTP_FORWARDED_FOR'];
	else if(isset($_SERVER['HTTP_FORWARDED']))
		$ipaddress = $_SERVER['HTTP_FORWARDED'];
	else if(isset($_SERVER['REMOTE_ADDR']))
		$ipaddress = $_SERVER['REMOTE_ADDR'];
	else
		$ipaddress = 'UNKNOWN';

	return $ipaddress;
}

Open in new window

Just a note: you are using the mysql library which has deprecated - you might want to consider moving your code to mysqli. It is very similar to mysql - so conversion is relatively straight forward.
The sample above demonstrates how
1
 
LVL 108

Expert Comment

by:Ray Paseur
ID: 41848109
You might want to check the legal requirements for a signature.  In the USA, since the DMCA (Bill Clinton's presidency, before the millennium) we have been able to use a checkbox or radio input control to indicate a legally binding agreement, and today we can do it with a keytap.  This might make the programming easier.  I know the Apple signature pads are visually appealing, but they are legally unnecessary.  This is true in the USA, YMMV elsewhere.

Here's how to understand the changes PHP has made with the MySQL extension.  The article also shows, step-by-step, how to remediate PHP scripts like yours that still rely on the unsupported extension.  There is no supported version of PHP that has not deprecated MySQL.
https://www.experts-exchange.com/articles/11177/PHP-MySQL-Deprecated-as-of-PHP-5-5-0.html

A quick-ref for the extensions' function mapping is available here:
https://iconoun.com/mysql_mysqli_pdo_function_map.php
1
 

Author Comment

by:RiccardoQuest
ID: 41848213
I have implemented your code and it doesnt work for me. Also what does "$rawdata = file_get_contents("php://input");" mean? Its taking the data and?
0
 
LVL 51

Expert Comment

by:Julian Hansen
ID: 41848281
I have implemented your code and it doesnt work for me
That is not helpful - how does it not work - do you get an error - if so what error do you get?
$rawdata = file_get_contents("php://input");

Open in new window

Reads the raw POST data.
When data is sent to the server it is sent as a string of bytes.
With a form type post those bytes would be something like
field1=value1&field2=value2

Open in new window

With such a post PHP processes the data and arranges it nicely for you inside global arrays ($_POST, $_GET etc)
You can still access the raw post using the above method.
In this case it is not a typical key/value pair that is being sent so we read the raw post directly, chop the first 22 bytes off for the content type and then base64_decode the rest.

If it is not working for you please give us more information so that we can help you resolve it.
0
 

Author Comment

by:RiccardoQuest
ID: 41848606
if ($result) {
	// Success
	$filepath = "images/" . $imageName;
					
					// Delete previously uploaded image
					if (file_exists($filepath)) { unlink($filepath); }

					// Write $imgData into the image file
					$file = fopen($filepath, 'w');
					fwrite($file, $imgdata);
					fclose($file);
}

Open in new window


The code is working but i cant seem to create the actual file to be store on the imageName
0
 
LVL 108

Expert Comment

by:Ray Paseur
ID: 41848615
Here's what I found in the Chrome console:
Uncaught TypeError: Failed to construct 'XMLHttpRequest': Please use the 'new' operator, this DOM object constructor cannot be called as a function.

Not sure what to do with that yet...
1
 
LVL 51

Expert Comment

by:Julian Hansen
ID: 41848633
Ray is right - I picked that up while testing your code and forgot to mention it.
...
    /** Save Canvas **/
    $("#saveSig").click(function saveSig() {
        //encode URI
        var sigData = canvas.toDataURL("image/png");
		$("#imgData").html('Thank you! Your signature was saved');
        // ADD new 
        var ajax = new XMLHttpRequest();
        ajax.open("POST", 'post-sign.php');
        ajax.setRequestHeader('Content-Type', 'application/upload');
        ajax.send(sigData);
        $('#debug').html(sigData);
    });
...

Open in new window

0
 

Author Comment

by:RiccardoQuest
ID: 41848665
So that will do the upload however how do i implement the path of where to store the image? I.E images/signature
0
 
LVL 51

Expert Comment

by:Julian Hansen
ID: 41848713
Line 4 of this post
$imageName = "sign_" . rand(5,1000) . rand(1, 10) . rand(10000, 150000) . rand(1500, 100000000) . ".png";

Open in new window

Change to
$imageName = $image_path . "sign_" . rand(5,1000) . rand(1, 10) . rand(10000, 150000) . rand(1500, 100000000) . ".png";

Open in new window

And then at the top of your code define $image_path as
$image_path = "images/signature/";

Open in new window

1
Free Trending Threat Insights Every Day

Enhance your security with threat intelligence from the web. Get trending threat insights on hackers, exploits, and suspicious IP addresses delivered to your inbox with our free Cyber Daily.

 
LVL 108

Expert Comment

by:Ray Paseur
ID: 41848714
OK, some progress.  Let's step backward through this and I'll explain what I did and show you the code examples.

Here is the result of my scribbly signature.  Ray, written with a mouse
It was produced by this script
https://iconoun.com/demo/temp_riccardo_signature.php
<?php // demo/temp_riccardo_signature.php
/**
 * https://www.experts-exchange.com/questions/28977073/Issues-with-signature.html#a41848213
 */
error_reporting(E_ALL);

// THIS IS WHAT THE REQUEST DATA LOOKS LIKE
$_POST['q'] = 'data%3Aimage%2Fpng%3Bbase64%2CiVBORw0KGgoAAAANSUhEUgAAAZAAAABLCAYAAABeMdGUAAAITUlEQVR4Xu2dvapeRRSG39yAaGGrQuwTOwshegdqYwoh2gkKmk4wQSUp7KJV7FSw0CbaWargBRjwBrwCvQNlwV4w2exzvr3n55u%2FZ4Mk5nwzs9az5lvvmd99RTwQgAAEIACBCAJXIspQBAIQgAAEICAEhE4AAQhAAAJRBBCQKGwUggAEIAABBIQ%2BAAEIQAACUQQQkChsFIIABCAAgdEE5B1JNyTZn%2FaM5h89FgIQgEAzBEZJsE9LehAIRwjYxOS7ZohjCAQgAIFBCIwgICYeDyXdlPRY0peSvpX0qqRflzjdXv59kLDhBgQgAIH6BHoXkFA8fpT0nqR%2FA6w2%2Brgj6aqkdxdhqU8dCyAAAQgMQKBnATklHh4eE5FvltGJjUpCgRkghLgAAQhAoA6BXgVkr3g4VZvSuiXprqT7dVDTKgQgAIGxCPQoICYetrZxXdLWtNVWhKzMI0mvMZU1VgfGGwhAoB6BHgXE1jTuRUxJ%2BVSW7cjybb71yNMyBCAAgc4J9CYgLgI2AnkzYj3jvyVevfndeTfDfAhAYEQCPSXScOoqdkeVC8gzEeIzYvzxCQIQgEA0gZ4E5HVJPy2HAmOnoD6T9KmkP5f1EHZkRXcdCkIAArMT6EVAbPTxzxKslNGD1fObpGuS3pD08%2BwdAP8hAAEIxBLoRUB89PG5JBtFpDyhGPXif4q%2FlIUABCBQhEAvCTT32gWL6UW6E5VCAAIzEehNQGzx3E6Vp9qNgMzUy%2FEVAhAoQiA1ERcxaqNST%2Fj%2BIzsQaGsZsQ8CEkuOchCAAAQWAj0ISLhmYWbHbuENg46A8BWAAAQgkEigBwGx69k%2FDPzMYTMCkthxluJ2OWXKSDCPFdQCAQhUIZAjGZc0PHynh7eTw2YEJD1qXA2TzpAaINA1gRzJuBQAm7r6RdLLkmz7rh0AtCeHzQhIetScYY4pxXRrqAECEDg7gRzJuJTRfvbDb9z1g4SpNnMOJC1iIb%2FUzQxpllAaAhCoSiA1GZc0Pjz7YVNZdo1JeJBwvTPrqC05DiUebXOEz%2Bc81DkCD3yAwLQEehAQs3F9kDBVPOwaE1v85S6s410%2F96HO4xZQAgIQaIJAbwLi9ra4hmFTO7ZbLOWqlRbi4X7YCM2ftW8t2NnEFwgjIDAzgZYTQSgSa8FoUUB8aie1P9WOSeiH2RK%2BPth8q21fKl%2FKQwACmQi0nAzCXT52fUn4JkF%2Fx3lLO4BSp3ZaEMX1oU3rH%2BvNDEz7ZfryUQ0EeifQg4D8Len51Ql0P4PwWJItsLeQ1HIIQI461n1yb53rkYbVY9NxPiWXco1%2B798T7IcABDYI9CAgZraJyAsr%2B30U0srLocKF%2FViue5P93s7sQrtn6slHGn9IemXVQKw%2Fe%2B3kcxCAQIcEWk4MYULemqoKXw5l70i3KS77r9bTmoCErwDeIyBbO9vYrVarN9EuBDog0IuAXGSnJckPJN1bWN%2BWZHdn1XhS10DM5pwjkPWi%2FmWx9nZ%2FkHRzgddy36gRX9qEAAQ6mprwpPb7ss5xWfBsquaOpKuZbus92lFyjD5yC8h6RHGZCPsp%2F9BvBORoL%2BDzEJiMQKtJIpy7%2F0rSRzviEi6s299tbeRcT66RQ656QjFyBluxXu%2B6elvS94xAztVtaAcCfRNoVUB8gdzovnRADGwU4tNZtnsoPAxXMlK5En%2Buek4JiAnsDUn2pz%2B2SeGLZQqLa15K9hbqhsAgBFoVkJQpIRMOv7nXLmK0pFh6NJJib9iVwnpit82GV%2BCH18D4ocAHK%2BGw9q0tv2%2FML69sYWv0IF8z3IDAmARGFBCL1HVJH0t6awnbOUYjW7uY7Dd5W9Tfm4zDOmIS%2Bda23S27%2FpL0lKTngjWjHJsAxvyW4BUEILBJYFQBcWfD0ci5tvquE7Zthf15Z%2F%2FzsiYeJn5HRMTE4xNJL0p6X9KzO%2B7lCk%2F355w%2B2%2BkuH4MABHomMLqA%2BGjEFuFvLYGypGn%2Fv3dUEBvf9QK11XOKdzgK%2BDoYQV02krF2tqalTtm9PuOBgJwixs8hAIEnCJxKaLVweTLLuZhrv6GbcFxb1kTsZUilRcT4bU0hneLq6xW%2BLnHq81s%2FP3IIEPGIIUwZCExOoHUBiV1Iviis4en1I9NDubrJHjFZx8Rs3iskR0Qj9AkByRVh6oHARARaF5AS9llC9umhu5Ludx7vHMk%2FRx2dY8R8CEDgKIESCfqoDTU%2BbyLySJJNY7V0JXwMi9TkzzviY6hTBgIQOLmoOzKicMur%2B9mjoKYKCO84H7mX4xsEChLoMWHmxBEeurN6e%2BSRKiCc%2F8jZo6gLAhMR6DFhThSeXa7mEhD6wi7cfAgCEOh5yoboPUkAAaFHQAACVQjwW2cV7FkbTRGQlLJZnaAyCECgPwIISH8xW1ucIgIpZfsnhwcQgEASAQQkCV8zhWOEgO27zYQPQyDQJwEEpM%2B4XTQKWf%2F7ZW8hfMi7P8YIPl5AoBYBBKQW%2BfztXnRNytZFjH72o8Z1Lvk9p0YIQKAKAQSkCvazNXrq7q3cd42dzTEaggAE6hNAQOrHoKQFl13ESOxLkqduCExAgCQyQZBxEQIQgEAJAghICarUCQEIQGACAgjIBEHGRQhAAAIlCCAgJahSJwQgAIEJCCAgEwQZFyEAAQiUIICAlKBKnRCAAAQmIICATBBkXIQABCBQggACUoIqdUIAAhCYgAACMkGQcRECEIBACQIISAmq1AkBCEBgAgIIyARBxkUIQAACJQggICWoUicEIACBCQggIBMEGRchAAEIlCCAgJSgSp0QgAAEJiCAgEwQZFyEAAQgUIIAAlKCKnVCAAIQmIAAAjJBkHERAhCAQAkC%2FwM8GjNbiSht9QAAAABJRU5ErkJggg%3D%3D';

// REVERSE THE URL ENCODING
$img = rawurldecode($_POST['q']);

// REMOVE HEADER data:image/png;base64,
$img = str_replace('data:image/png;base64,', NULL, $img);

// REVERSE BASE-64 ENCODING
$img = base64_decode($img);

// SHOW THE IMAGE
header('Content-type: image/png');
echo $img;

Open in new window

The way I got the information that is mocked-up in $_POST was by using a data-trap server-side script that captured the information from the front-end script.  The data trap wrote this information into the error_log so I could copy it out later, examine it, and decide what to do with it.  Here is the data trap.
<?php // demo/temp_riccardo_server.php
/**
 * https://www.experts-exchange.com/questions/28977073/Issues-with-signature.html#a41848213
 */
error_reporting(E_ALL);


// CAPTURE THE REQUEST VARIABLES, IF ANY
$req = NULL;
if (!empty($_GET))    $req .= 'GET: '    . print_r($_GET, TRUE)    . PHP_EOL . PHP_EOL;
if (!empty($_POST))   $req .= 'POST: '   . print_r($_POST, TRUE)   . PHP_EOL . PHP_EOL;
if (!empty($_FILES))  $req .= 'FILES: '  . print_r($_FILES, TRUE)  . PHP_EOL . PHP_EOL;
// if (!empty($_COOKIE)) $req .= 'COOKIE: ' . print_r($_COOKIE, TRUE) . PHP_EOL . PHP_EOL;

if (!empty($req)) error_log($req);

Open in new window

And finally, here is the client-side signature capture script, mostly copied from what was originally posted with the question.  I modified it a little bit to make it work with Chrome.  I think this $.(post) method will work cross-browser but I have not tested it.
<?php // demo/temp_riccardo.php
/**
 * https://www.experts-exchange.com/questions/28977073/Issues-with-signature.html#a41848213
 */
error_reporting(E_ALL);


// CREATE OUR WEB PAGE IN HTML5 FORMAT
$htm = <<<HTML5
<!DOCTYPE html>
<html dir="ltr" lang="en-US">
<head>
<meta charset="utf-8" />
<meta name="robots" content="noindex, nofollow" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />

<style type="text/css">
/* STYLE SHEET HERE */
</style>

<script src="https://code.jquery.com/jquery-latest.min.js"></script>

<script>//<![CDATA[
$(document).ready(function () {
    /** Set Canvas Size **/
    var canvasWidth = 400;
    var canvasHeight = 75;

    /** IE SUPPORT **/
    var canvasDiv = document.getElementById('signaturePad');
    canvas = document.createElement('canvas');
    canvas.setAttribute('width', canvasWidth);
    canvas.setAttribute('height', canvasHeight);
    canvas.setAttribute('id', 'canvas');
    canvasDiv.appendChild(canvas);
    if (typeof G_vmlCanvasManager != 'undefined') {
        canvas = G_vmlCanvasManager.initElement(canvas);
    }
    var context = canvas.getContext("2d");

    var clickX = new Array();
    var clickY = new Array();
    var clickDrag = new Array();
    var paint;

    /** Redraw the Canvas **/
    function redraw() {
        canvas.width = canvas.width; // Clears the canvas

        context.strokeStyle = "#000000";
        context.lineJoin = "miter";
        context.lineWidth = 2;

        for (var i = 0; i < clickX.length; i++) {
            context.beginPath();
            if (clickDrag[i] && i) {
                context.moveTo(clickX[i - 1], clickY[i - 1]);
            } else {
                context.moveTo(clickX[i] - 1, clickY[i]);
            }
            context.lineTo(clickX[i], clickY[i]);
            context.closePath();
            context.stroke();
        }
    }

    /** Save Canvas **/
    $("#saveSig").click(function saveSig() {
        //encode URI
        var sigData = encodeURIComponent(canvas.toDataURL("image/png"));
        $("#imgData").html('Thank you! Your signature was saved');
        $.post('temp_riccardo_server.php', {q:sigData});
        $('#debug').html(sigData);
    });

    /** Clear Canvas **/
    $('#clearSig').click(function clearSig() {
            clickX = new Array();
            clickY = new Array();
            clickDrag = new Array();
            context.clearRect(0, 0, canvas.width, canvas.height);
    });

    /**Draw when moving over Canvas **/
    $('#signaturePad').mousemove(function (e) {
        if (paint) {
            addClick(e.pageX - this.offsetLeft, e.pageY - this.offsetTop, true);
            redraw();
        }
    });

    /**Stop Drawing on Mouseup **/
    $('#signaturePad').mouseup(function (e) {
        paint = false;
    });

    /** Starting a Click **/
    function addClick(x, y, dragging) {
        clickX.push(x);
        clickY.push(y);
        clickDrag.push(dragging);
    }

    $('#signaturePad').mousedown(function (e) {
        var mouseX = e.pageX - this.offsetLeft;
        var mouseY = e.pageY - this.offsetTop;

        paint = true;
        addClick(e.pageX - this.offsetLeft, e.pageY - this.offsetTop);
        redraw();
    });

});//]]>
</script>

<title>Signature Thing</title>
</head>

<body>
  <center>

    <fieldset style="width: 435px">
        <br/>
        <br/>
        <div id="signaturePad" style="border: 1px solid #ccc; height: 55px; width: 400px;"></div>
        <br/>
        <button id="clearSig" type="button">Clear Signature</button>&nbsp;
        <button id="saveSig" type="button">Save Signature</button>
        <div id="imgData"></div>
        <br/>
        <br/>
    </fieldset>

</center>
<div id="debug"></div>

</body>
</html>
HTML5;


// RENDER THE WEB PAGE
echo $htm;

Open in new window

How to get the image so you can store it?  Go back to the mockup script shown at the top of this comment.  The PNG image is located in $img.  The decoded one you want to keep is on line 17.  If you want to write this into a file, just use file_put_contents().  If you want to store this in a database, you must escape the variable before you use it in a query string.

Your server-side action script would need to process the POST request in much the same way that the mockup processed the data I captured and wrote into the error_log.  Obviously the error_log step is not necessary for your work -- just process the $_POST data when your server-side script receives it.

This is kind of a "sidebar" note, but you might want to be thoughtful about variable and file names.  Something named "session" is easy to confuse with the session, which is a term of art in PHP.  If it's really supposed to contain the remote IP address, a better name might be helpful.  Likewise, using a random number for the file name might be creating something that is unnecessarily confusing.  Since it's extremely unlikely that you will get two signatures from the same remote address at the same second, you might consider using a file name that is made up from the remote address and the timestamp.  This would let you sort or select the files relatively easily, in case you need to refer back to the signature image associated with a particular transaction.  Just a thought...

HTH, and please post back if I've made this confusing!  ~Ray
0
 

Author Comment

by:RiccardoQuest
ID: 41849845
Ray ive tried to understand what youve coded and wrote however it is quite confusing.

Ive appilied the post part and the second part together see below:

<?php // demo/temp_riccardo.php
/**
 * https://www.experts-exchange.com/questions/28977073/Issues-with-signature.html#a41848213
 */
error_reporting(E_ALL);

// CAPTURE THE REQUEST VARIABLES, IF ANY
$req = NULL;
if (!empty($_GET))    $req .= 'GET: '    . print_r($_GET, TRUE)    . PHP_EOL . PHP_EOL;
if (!empty($_POST))   $req .= 'POST: '   . print_r($_POST, TRUE)   . PHP_EOL . PHP_EOL;
if (!empty($_FILES))  $req .= 'FILES: '  . print_r($_FILES, TRUE)  . PHP_EOL . PHP_EOL;
// if (!empty($_COOKIE)) $req .= 'COOKIE: ' . print_r($_COOKIE, TRUE) . PHP_EOL . PHP_EOL;

if (!empty($req)) error_log($req);

?>

$htm = <<<HTML5

<!DOCTYPE html>
<html dir="ltr" lang="en-US">
<head>
<meta charset="utf-8" />
<meta name="robots" content="noindex, nofollow" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<script src="https://code.jquery.com/jquery-latest.min.js"></script>

<script>//<![CDATA[
$(document).ready(function () {
    /** Set Canvas Size **/
    var canvasWidth = 400;
    var canvasHeight = 75;

    /** IE SUPPORT **/
    var canvasDiv = document.getElementById('signaturePad');
    canvas = document.createElement('canvas');
    canvas.setAttribute('width', canvasWidth);
    canvas.setAttribute('height', canvasHeight);
    canvas.setAttribute('id', 'canvas');
    canvasDiv.appendChild(canvas);
    if (typeof G_vmlCanvasManager != 'undefined') {
        canvas = G_vmlCanvasManager.initElement(canvas);
    }
    var context = canvas.getContext("2d");

    var clickX = new Array();
    var clickY = new Array();
    var clickDrag = new Array();
    var paint;

    /** Redraw the Canvas **/
    function redraw() {
        canvas.width = canvas.width; // Clears the canvas

        context.strokeStyle = "#000000";
        context.lineJoin = "miter";
        context.lineWidth = 2;

        for (var i = 0; i < clickX.length; i++) {
            context.beginPath();
            if (clickDrag[i] && i) {
                context.moveTo(clickX[i - 1], clickY[i - 1]);
            } else {
                context.moveTo(clickX[i] - 1, clickY[i]);
            }
            context.lineTo(clickX[i], clickY[i]);
            context.closePath();
            context.stroke();
        }
    }

    /** Save Canvas **/
    $("#saveSig").click(function saveSig() {
        //encode URI
        var sigData = encodeURIComponent(canvas.toDataURL("image/png"));
        $("#imgData").html('Thank you! Your signature was saved');
        $.post('temp_riccardo_server.php', {q:sigData});
        $('#debug').html(sigData);
    });

    /** Clear Canvas **/
    $('#clearSig').click(function clearSig() {
            clickX = new Array();
            clickY = new Array();
            clickDrag = new Array();
            context.clearRect(0, 0, canvas.width, canvas.height);
    });

    /**Draw when moving over Canvas **/
    $('#signaturePad').mousemove(function (e) {
        if (paint) {
            addClick(e.pageX - this.offsetLeft, e.pageY - this.offsetTop, true);
            redraw();
        }
    });

    /**Stop Drawing on Mouseup **/
    $('#signaturePad').mouseup(function (e) {
        paint = false;
    });

    /** Starting a Click **/
    function addClick(x, y, dragging) {
        clickX.push(x);
        clickY.push(y);
        clickDrag.push(dragging);
    }

    $('#signaturePad').mousedown(function (e) {
        var mouseX = e.pageX - this.offsetLeft;
        var mouseY = e.pageY - this.offsetTop;

        paint = true;
        addClick(e.pageX - this.offsetLeft, e.pageY - this.offsetTop);
        redraw();
    });

});//]]>
</script>

<title>Signature Thing</title>
</head>

<body>
  <center>

    <fieldset style="width: 435px">
        <br/>
        <br/>
        <div id="signaturePad" style="border: 1px solid #ccc; height: 55px; width: 400px;"></div>
        <br/>
        <button id="clearSig" type="button">Clear Signature</button>&nbsp;
        <button id="saveSig" type="button">Save Signature</button>
        <div id="imgData"></div>
        <br/>
        <br/>
    </fieldset>

</center>
<div id="debug"></div>

</body>
</html>
HTML5;


// RENDER THE WEB PAGE
echo $htm;

Open in new window


But I'm still not 100% on this.

I see you are trying to make it cross browser compatible and mobile compatible. Great!

I still need to sort this and I see you suggest referring back to the code before hand however im unsure how to implement that into this new part.

Storing the image in the db is key with a valid path so the image can be viewed on another page at a later date.
0
 

Author Comment

by:RiccardoQuest
ID: 41849856
Right now ive taken Mr Hansens additional code it stores the path correctly however the size of the file is 0 bytes showing no data is actually stored:

files in file manager
0
 
LVL 108

Expert Comment

by:Ray Paseur
ID: 41849931
There's a concept we use in debugging complex applications that is called the SSCCE.  It guides us to deconstructing the larger problem into smaller problems so that each part of the problem can become its own "thought island" with known inputs and expected outputs.  When we try to look at the big picture, it's easy to be confused.  Conversely when we look at the parts in isolation we can often understand each of them, and eventually understand how they come together to make the big picture.

In this case, any solution we demonstrate will write information onto our server file system and our database.  Personally, that's too much exposure, and I don't want to leave such a code set on my server, so I will just try to explain a little bit of the theory.  Before you read on, please be sure you have a 100% clear understanding of HTTP client/server protocols.

On the client side, you have HTML, CSS, JavaScript (jQuery).  These technologies manipulate and respond to the human client's inputs.  In the instant case, they collect a signature object in a canvas, and send it to the server via a POST-method request.  The signature object is a string variable, representing a PNG image that has been encoded for safe transport across the internet.

On the server side, you have PHP and MySQL.  These technologies manipulate and respond to HTTP requests, such as the POST-method request that sends the encoded PNG image.  In this PHP script you need to do a few things: Decode the string variable into a PNG image, choose a name for the image file, write the image file into the server file system, write the URL into the database.  You can find good examples for each of these parts, but it's up to you, as the developer, to integrate the parts into a server-side application that responds to the AJAX request.

So my advice is this:  Deconstruct the parts until you have "bite-sized" pieces and work each of those pieces separately.  The decoding process is shown here.  you already have a scheme for choosing a name and location for the image file.  You write an image file to the server file system with file_put_contents().  There are many links that teach the basics of MySQL (here is one) and your next step would be to update the database.

Hope that helps.  If time permits I may write an E-E article on the subject.  Or you may get all the parts together and beat me to it!  Best of luck and best regards, ~Ray
1
 
LVL 51

Accepted Solution

by:
Julian Hansen earned 375 total points
ID: 41849934
Here is a working sample.

I have changed your AJAX code to use the JQuery $.ajax() function - it is much easier than using XMLHttpRequest

The sample will write the image to the same filename (just so I don't end up with a billion images on my server). It also shows the latest records - it will automatically delete older ones. The image and session data are sent back in the AJAX request.

Here is the updated AJAX - rest of the javascript is as you have it
    $("#saveSig").click(function saveSig() {
        //encode URI
        var sigData = canvas.toDataURL("image/png");
		$.ajax({
			url: 'post-sign.php',
			type: 'POST',
			data: canvas.toDataURL("image/png")
		}).done(function(resp) {
			$('#result').html(resp);
			$("#imgData").html('Thank you! Your signature was saved');
		});
    });

Open in new window

HTML - added a box for the result
<body>
  <center>
    <fieldset style="width: 435px">
        <br/>
        <br/>
        <div id="signaturePad" style="border: 1px solid #ccc; height: 55px; width: 400px;"></div>
        <br/>
        <button id="clearSig" type="button">Clear Signature</button>&nbsp;
        <button id="saveSig" type="button">Save Signature</button>
        <div id="imgData"></div>
		<div id="result"></div>
        <!-- removed double imgData here -->
        <br/>
        <br/>
    </fieldset>
</center>
<div id="debug"></div>
  
</body>

Open in new window

PHP
<?php
$imagepath = 'images/';
$session_id = getRemoteIP();
$imageName = "sign_" . rand(5,1000) . rand(1, 10) . rand(10000, 150000) . rand(1500, 100000000) . ".png";

$rawdata = file_get_contents("php://input");
$imgdata = substr($rawdata, 22);
$imgdata = base64_decode($imgdata);
file_put_contents($imagepath . $imageName, $imgdata);

$mysqli = new mysqli('localhost',username','password','db');
if ($mysqli->connect_error) {
	die('Connect Error (' .	 $mysqli->connect_errno . ') '
		. $mysqli->connect_error);
}

$query = <<< QUERY
INSERT INTO pad (`session`, `image_location`) 
VALUES ('{$session_id}', '{$imageName}')
QUERY;
$result = $mysqli->query($query);

if ($result) {
	$rand=rand();
	echo <<< OUT
	<img src="images/t1728.png?id={$rand}" />
OUT;
	$query = "SELECT id FROM pad ORDER BY id DESC LIMIT 10,1";
	$result = $mysqli->query($query);
	if ($result && $result->num_rows) {
		$row = $result->fetch_object();
		$query = "DELETE FROM pad WHERE id < {$row->id}";
		$mysqli->query($query);
	}
	$result = $mysqli->query("SELECT * FROM pad");
	if ($result) {
		echo <<< TABLE
			<table>
				<thead>
					<tr><th>ID</th><th>Session</th><th>image_location</th></tr>
				</thead>
TABLE;
		while($row = $result->fetch_array()) {
			echo '<tr><td>' . implode('</td><td>', $row) . '</td></tr>';
		}
		echo <<< TABLE
			</table>
TABLE;
	}	
}
else {
	// Fail
}

$mysqli->close();

function getRemoteIP()
{
	$ipaddress = '';
	if (isset($_SERVER['HTTP_CLIENT_IP']))
		$ipaddress = $_SERVER['HTTP_CLIENT_IP'];
	else if(isset($_SERVER['HTTP_X_FORWARDED_FOR']))
		$ipaddress = $_SERVER['HTTP_X_FORWARDED_FOR'];
	else if(isset($_SERVER['HTTP_X_FORWARDED']))
		$ipaddress = $_SERVER['HTTP_X_FORWARDED'];
	else if(isset($_SERVER['HTTP_FORWARDED_FOR']))
		$ipaddress = $_SERVER['HTTP_FORWARDED_FOR'];
	else if(isset($_SERVER['HTTP_FORWARDED']))
		$ipaddress = $_SERVER['HTTP_FORWARDED'];
	else if(isset($_SERVER['REMOTE_ADDR']))
		$ipaddress = $_SERVER['REMOTE_ADDR'];
	else
		$ipaddress = 'UNKNOWN';

	return $ipaddress;
}

Open in new window


Working sample here
0
 

Author Comment

by:RiccardoQuest
ID: 41850047
It works perfect on yours however now that ive done it on my web server it still doesnt have any data in the file. 0bytes

Empty
0
 
LVL 108

Assisted Solution

by:Ray Paseur
Ray Paseur earned 125 total points
ID: 41850070
on my web server it still doesnt
This is where the "deconstruction" strategy of the SSCCE really helps.  Isolate the AJAX server-side script and run it from the browser address bar.  Print the diagnostics to the browser.  To make this test be effective you will need to use a GET-method request instead of POST, but that should be your only change to the script.

You might also want to check the permissions on the signature  directory and make sure that PHP is allowed to write files there.
1
 

Author Comment

by:RiccardoQuest
ID: 41850219
Ive got it working now thank you guys. I do have a further question in relation to this which is to make it work on mobiles as nothing can be entered when on a mobile device. Ill post the link to that question after this comment. Thank you again for your help with this issue
0
 

Author Comment

by:RiccardoQuest
ID: 41850324
0
 

Author Closing Comment

by:RiccardoQuest
ID: 41850330
0

Featured Post

Why You Should Analyze Threat Actor TTPs

After years of analyzing threat actor behavior, it’s become clear that at any given time there are specific tactics, techniques, and procedures (TTPs) that are particularly prevalent. By analyzing and understanding these TTPs, you can dramatically enhance your security program.

Join & Write a Comment

Introduction A frequently asked question goes something like this:  "I am running a long process in the background and I want to alert my client when the process finishes.  How can I send a message to the browser?"  Unfortunately, the short answer …
This article demonstrates how to create a simple responsive confirmation dialog with Ok and Cancel buttons using HTML, CSS, jQuery and Promises
This tutorial will teach you the core code needed to finalize the addition of a watermark to your image. The viewer will use a small PHP class to learn and create a watermark.
The viewer will learn how to create a basic form using some HTML5 and PHP for later processing. Set up your basic HTML file. Open your form tag and set the method and action attributes.: (CODE) Set up your first few inputs one for the name and …

705 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

20 Experts available now in Live!

Get 1:1 Help Now