Link Search Menu Expand Document

Replace Text With Signature (jQuery) - JavaScript

PDF Search and Replace Text with Image sample in JavaScript demonstrating ‘Replace Text With Signature (jQuery)’

index.html
<!DOCTYPE html>
<html lang="en">

<head>
    <!-- Required meta tags -->
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <!-- Bootstrap CSS -->
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet"
        integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">

    <title>Accept a Signature · Signature Pad</title>
    <style>
        body {
            font: normal 100.01%/1.375 "Helvetica Neue", Helvetica, Arial, sans-serif;
        }
    </style>

    <link href="./assets/jquery.signaturepad.css" rel="stylesheet">
    <!--[if lt IE 9]><script src="../assets/flashcanvas.js"></script><![endif]-->
    <script src="https://code.jquery.com/jquery-2.2.4.min.js"
        integrity="sha256-BbhdlvQf/xTY9gja0Dq3HiwQF8LaCRTXxZKRutelT44=" crossorigin="anonymous"></script>
</head>

<body>
    <div class="container">
        <div class="row mt-4">
            <div class="col-md-12">
                <label>PDF.co API Key:</label>
            </div>
            <div class="col-md-12">
                <input id="txtPDFcoAPIKey" value="" class="form-control"
                    type="text" />
            </div>
        </div>
        <div class="row mt-4">
            <div class="col-md-12">
                <label>Input PDF URL:</label>
            </div>
            <div class="col-md-12">
                <input id="txtInputPDFUrl" value="https://bytescout-com.s3.us-west-2.amazonaws.com/files/demo-files/cloud-api/pdf-sign/sample_affidavit.pdf"
                    class="form-control" type="text" />
            </div>
        </div>
        <div class="row mt-4">
            <div class="col-md-12">
                <label>Draw Your Signature:</label>
            </div>
            <div class="col-md-12 clsCanvasContainer">
                <canvas class="pad" style="border: solid 1px black; background: transparent;"></canvas>
                <a id="btnClear" href="javascript:">Clear</a>
            </div>
        </div>
        <div class="row mt-4">
            <div class="col-md-12">
                <label>Text to Replace With:</label>
            </div>
            <div class="col-md-12">
                <input id="txtTextToReplaceWith" value="[Your_Sign_Here]" class="form-control" type="text" />
            </div>
        </div>
        <div class="row mt-4">
            <div class="col-md-12">
                <button id="btnAddSignatureToPDF" class="btn btn-primary" type="button">Add to PDF</button>
            </div>
        </div>
        <div class="row mt-4">
            <a style="display: none;" id="aViewResult" target="_blank" href="javascript:">View Result</a>
            <div style="display: none;" id="divError" class="alert alert-warning"></div>
        </div>
    </div>

    <script src="./js/signature-pad/jquery.signaturepad.js"></script>
    <script src="./js/index.js"></script>
    <script src="./assets/json2.min.js"></script>
</body>

</html>
flashcanvas.js
/*
 * FlashCanvas
 *
 * Copyright (c) 2009      Tim Cameron Ryan
 * Copyright (c) 2009-2011 FlashCanvas Project
 * Released under the MIT/X License
 */
window.ActiveXObject&&!window.CanvasRenderingContext2D&&function(h,j){function D(a){this.code=a;this.message=T[a]}function U(a){this.width=a}function E(a){this.id=a.C++}function t(a){this.G=a;this.id=a.C++}function u(a,b){this.canvas=a;this.B=b;this.d=a.uniqueID;this.D();this.C=0;this.t="";var c=this;setInterval(function(){n[c.d]===0&&c.e()},30)}function A(){if(j.readyState==="complete"){j.detachEvent(F,A);for(var a=j.getElementsByTagName(r),b=0,c=a.length;b<c;++b)B.initElement(a[b])}}function G(){var a=
event.srcElement,b=a.parentNode;a.blur();b.focus()}function H(){var a=event.propertyName;if(a==="width"||a==="height"){var b=event.srcElement,c=b[a],d=parseInt(c,10);if(isNaN(d)||d<0)d=a==="width"?300:150;if(c===d){b.style[a]=d+"px";b.getContext("2d").I(b.width,b.height)}else b[a]=d}}function I(){h.detachEvent(J,I);for(var a in s){var b=s[a],c=b.firstChild,d;for(d in c)if(typeof c[d]==="function")c[d]=k;for(d in b)if(typeof b[d]==="function")b[d]=k;c.detachEvent(K,G);b.detachEvent(L,H)}h[M]=k;h[N]=
k;h[O]=k;h[C]=k;h[P]=k}function V(){var a=j.getElementsByTagName("script");a=a[a.length-1];return j.documentMode>=8?a.src:a.getAttribute("src",4)}function v(a){return(""+a).replace(/&/g,"&amp;").replace(/</g,"&lt;")}function W(a){return a.toLowerCase()}function i(a){throw new D(a);}function Q(a){var b=parseInt(a.width,10),c=parseInt(a.height,10);if(isNaN(b)||b<0)b=300;if(isNaN(c)||c<0)c=150;a.width=b;a.height=c}var k=null,r="canvas",M="CanvasRenderingContext2D",N="CanvasGradient",O="CanvasPattern",
C="FlashCanvas",P="G_vmlCanvasManager",K="onfocus",L="onpropertychange",F="onreadystatechange",J="onunload",w=((h[C+"Options"]||{}).swfPath||V().replace(/[^\/]+$/,""))+"flashcanvas.swf",e=new function(a){for(var b=0,c=a.length;b<c;b++)this[a[b]]=b}(["toDataURL","save","restore","scale","rotate","translate","transform","setTransform","globalAlpha","globalCompositeOperation","strokeStyle","fillStyle","createLinearGradient","createRadialGradient","createPattern","lineWidth","lineCap","lineJoin","miterLimit",
"shadowOffsetX","shadowOffsetY","shadowBlur","shadowColor","clearRect","fillRect","strokeRect","beginPath","closePath","moveTo","lineTo","quadraticCurveTo","bezierCurveTo","arcTo","rect","arc","fill","stroke","clip","isPointInPath","font","textAlign","textBaseline","fillText","strokeText","measureText","drawImage","createImageData","getImageData","putImageData","addColorStop","direction","resize"]),x={},n={},s={},y={};u.prototype={save:function(){this.b();this.c();this.m();this.l();this.z();this.w();
this.F.push([this.f,this.g,this.A,this.u,this.j,this.h,this.i,this.k,this.p,this.q,this.n,this.o,this.v,this.r,this.s]);this.a.push(e.save)},restore:function(){var a=this.F;if(a.length){a=a.pop();this.globalAlpha=a[0];this.globalCompositeOperation=a[1];this.strokeStyle=a[2];this.fillStyle=a[3];this.lineWidth=a[4];this.lineCap=a[5];this.lineJoin=a[6];this.miterLimit=a[7];this.shadowOffsetX=a[8];this.shadowOffsetY=a[9];this.shadowBlur=a[10];this.shadowColor=a[11];this.font=a[12];this.textAlign=a[13];
this.textBaseline=a[14]}this.a.push(e.restore)},scale:function(a,b){this.a.push(e.scale,a,b)},rotate:function(a){this.a.push(e.rotate,a)},translate:function(a,b){this.a.push(e.translate,a,b)},transform:function(a,b,c,d,f,g){this.a.push(e.transform,a,b,c,d,f,g)},setTransform:function(a,b,c,d,f,g){this.a.push(e.setTransform,a,b,c,d,f,g)},b:function(){var a=this.a;if(this.f!==this.globalAlpha){this.f=this.globalAlpha;a.push(e.globalAlpha,this.f)}if(this.g!==this.globalCompositeOperation){this.g=this.globalCompositeOperation;
a.push(e.globalCompositeOperation,this.g)}},m:function(){if(this.A!==this.strokeStyle){var a=this.A=this.strokeStyle;this.a.push(e.strokeStyle,typeof a==="object"?a.id:a)}},l:function(){if(this.u!==this.fillStyle){var a=this.u=this.fillStyle;this.a.push(e.fillStyle,typeof a==="object"?a.id:a)}},createLinearGradient:function(a,b,c,d){isFinite(a)&&isFinite(b)&&isFinite(c)&&isFinite(d)||i(9);this.a.push(e.createLinearGradient,a,b,c,d);return new t(this)},createRadialGradient:function(a,b,c,d,f,g){isFinite(a)&&
isFinite(b)&&isFinite(c)&&isFinite(d)&&isFinite(f)&&isFinite(g)||i(9);if(c<0||g<0)i(1);this.a.push(e.createRadialGradient,a,b,c,d,f,g);return new t(this)},createPattern:function(a,b){a||i(17);var c=a.tagName,d,f=this.d;if(c){c=c.toLowerCase();if(c==="img")d=a.getAttribute("src",2);else if(c===r||c==="video")return;else i(17)}else if(a.src)d=a.src;else i(17);b==="repeat"||b==="no-repeat"||b==="repeat-x"||b==="repeat-y"||b===""||b===k||i(12);this.a.push(e.createPattern,v(d),b);if(x[f]){this.e();++n[f]}return new E(this)},
z:function(){var a=this.a;if(this.j!==this.lineWidth){this.j=this.lineWidth;a.push(e.lineWidth,this.j)}if(this.h!==this.lineCap){this.h=this.lineCap;a.push(e.lineCap,this.h)}if(this.i!==this.lineJoin){this.i=this.lineJoin;a.push(e.lineJoin,this.i)}if(this.k!==this.miterLimit){this.k=this.miterLimit;a.push(e.miterLimit,this.k)}},c:function(){var a=this.a;if(this.p!==this.shadowOffsetX){this.p=this.shadowOffsetX;a.push(e.shadowOffsetX,this.p)}if(this.q!==this.shadowOffsetY){this.q=this.shadowOffsetY;
a.push(e.shadowOffsetY,this.q)}if(this.n!==this.shadowBlur){this.n=this.shadowBlur;a.push(e.shadowBlur,this.n)}if(this.o!==this.shadowColor){this.o=this.shadowColor;a.push(e.shadowColor,this.o)}},clearRect:function(a,b,c,d){this.a.push(e.clearRect,a,b,c,d)},fillRect:function(a,b,c,d){this.b();this.c();this.l();this.a.push(e.fillRect,a,b,c,d)},strokeRect:function(a,b,c,d){this.b();this.c();this.m();this.z();this.a.push(e.strokeRect,a,b,c,d)},beginPath:function(){this.a.push(e.beginPath)},closePath:function(){this.a.push(e.closePath)},
moveTo:function(a,b){this.a.push(e.moveTo,a,b)},lineTo:function(a,b){this.a.push(e.lineTo,a,b)},quadraticCurveTo:function(a,b,c,d){this.a.push(e.quadraticCurveTo,a,b,c,d)},bezierCurveTo:function(a,b,c,d,f,g){this.a.push(e.bezierCurveTo,a,b,c,d,f,g)},arcTo:function(a,b,c,d,f){f<0&&isFinite(f)&&i(1);this.a.push(e.arcTo,a,b,c,d,f)},rect:function(a,b,c,d){this.a.push(e.rect,a,b,c,d)},arc:function(a,b,c,d,f,g){c<0&&isFinite(c)&&i(1);this.a.push(e.arc,a,b,c,d,f,g?1:0)},fill:function(){this.b();this.c();
this.l();this.a.push(e.fill)},stroke:function(){this.b();this.c();this.m();this.z();this.a.push(e.stroke)},clip:function(){this.a.push(e.clip)},w:function(){var a=this.a;if(this.v!==this.font)try{var b=y[this.d];b.style.font=this.v=this.font;var c=b.currentStyle;a.push(e.font,[c.fontStyle,c.fontWeight,b.offsetHeight,c.fontFamily].join(" "))}catch(d){}if(this.r!==this.textAlign){this.r=this.textAlign;a.push(e.textAlign,this.r)}if(this.s!==this.textBaseline){this.s=this.textBaseline;a.push(e.textBaseline,
this.s)}if(this.t!==this.canvas.currentStyle.direction){this.t=this.canvas.currentStyle.direction;a.push(e.direction,this.t)}},fillText:function(a,b,c,d){this.b();this.l();this.c();this.w();this.a.push(e.fillText,v(a),b,c,d===void 0?Infinity:d)},strokeText:function(a,b,c,d){this.b();this.m();this.c();this.w();this.a.push(e.strokeText,v(a),b,c,d===void 0?Infinity:d)},measureText:function(a){var b=y[this.d];try{b.style.font=this.font}catch(c){}b.innerText=a.replace(/[ \n\f\r]/g,"\t");return new U(b.offsetWidth)},
drawImage:function(a,b,c,d,f,g,o,l,z){a||i(17);var p=a.tagName,m,q=arguments.length,R=this.d;if(p){p=p.toLowerCase();if(p==="img")m=a.getAttribute("src",2);else if(p===r||p==="video")return;else i(17)}else if(a.src)m=a.src;else i(17);this.b();this.c();m=v(m);if(q===3)this.a.push(e.drawImage,q,m,b,c);else if(q===5)this.a.push(e.drawImage,q,m,b,c,d,f);else if(q===9){if(d===0||f===0)i(1);this.a.push(e.drawImage,q,m,b,c,d,f,g,o,l,z)}else return;if(x[R]){this.e();++n[R]}},D:function(){this.globalAlpha=
this.f=1;this.globalCompositeOperation=this.g="source-over";this.fillStyle=this.u=this.strokeStyle=this.A="#000000";this.lineWidth=this.j=1;this.lineCap=this.h="butt";this.lineJoin=this.i="miter";this.miterLimit=this.k=10;this.shadowBlur=this.n=this.shadowOffsetY=this.q=this.shadowOffsetX=this.p=0;this.shadowColor=this.o="rgba(0, 0, 0, 0.0)";this.font=this.v="10px sans-serif";this.textAlign=this.r="start";this.textBaseline=this.s="alphabetic";this.a=[];this.F=[]},H:function(){var a=this.a;this.a=
[];return a},e:function(){var a=this.H();if(a.length>0)return eval(this.B.CallFunction('<invoke name="executeCommand" returntype="javascript"><arguments><string>'+a.join("&#0;")+"</string></arguments></invoke>"))},I:function(a,b){this.e();this.D();if(a>0)this.B.width=a;if(b>0)this.B.height=b;this.a.push(e.resize,a,b)}};t.prototype={addColorStop:function(a,b){if(isNaN(a)||a<0||a>1)i(1);this.G.a.push(e.addColorStop,this.id,a,b)}};D.prototype=Error();var T={1:"INDEX_SIZE_ERR",9:"NOT_SUPPORTED_ERR",11:"INVALID_STATE_ERR",
12:"SYNTAX_ERR",17:"TYPE_MISMATCH_ERR",18:"SECURITY_ERR"},B={initElement:function(a){if(a.getContext)return a;var b=a.uniqueID,c="external"+b;x[b]=false;n[b]=1;Q(a);a.innerHTML='<object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" codebase="'+location.protocol+'//fpdownload.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=9,0,0,0" width="100%" height="100%" id="'+c+'"><param name="allowScriptAccess" value="always"><param name="flashvars" value="id='+c+'"><param name="wmode" value="transparent"></object><span style="margin:0;padding:0;border:0;display:inline-block;position:static;height:1em;overflow:visible;white-space:nowrap"></span>';
s[b]=a;var d=a.firstChild;y[b]=a.lastChild;var f=j.body.contains;if(f(a))d.movie=w;else var g=setInterval(function(){if(f(a)){clearInterval(g);d.movie=w}},0);if(j.compatMode==="BackCompat"||!h.XMLHttpRequest)y[b].style.overflow="hidden";var o=new u(a,d);a.getContext=function(l){return l==="2d"?o:k};a.toDataURL=function(l,z){(""+l).replace(/[A-Z]+/g,W)==="image/jpeg"?o.a.push(e.toDataURL,l,typeof z==="number"?z:""):o.a.push(e.toDataURL,l);return o.e()};d.attachEvent(K,G);return a},saveImage:function(a){a.firstChild.saveImage()},
setOptions:function(){},trigger:function(a,b){s[a].fireEvent("on"+b)},unlock:function(a,b){n[a]&&--n[a];if(b){var c=s[a],d=c.firstChild,f,g;Q(c);f=c.width;g=c.height;c.style.width=f+"px";c.style.height=g+"px";if(f>0)d.width=f;if(g>0)d.height=g;d.resize(f,g);c.attachEvent(L,H);x[a]=true}}};j.createElement(r);j.createStyleSheet().cssText=r+"{display:inline-block;overflow:hidden;width:300px;height:150px}";j.readyState==="complete"?A():j.attachEvent(F,A);h.attachEvent(J,I);if(w.indexOf(location.protocol+
"//"+location.host+"/")===0){var S=new ActiveXObject("Microsoft.XMLHTTP");S.open("GET",w,false);S.send(k)}h[M]=u;h[N]=t;h[O]=E;h[C]=B;h[P]={init:function(){},init_:function(){},initElement:B.initElement};keep=u.measureText}(window,document);

json2.min.js
if(!this.JSON){this.JSON={};}
(function(){function f(n){return n<10?'0'+n:n;}
if(typeof Date.prototype.toJSON!=='function'){Date.prototype.toJSON=function(key){return isFinite(this.valueOf())?this.getUTCFullYear()+'-'+
f(this.getUTCMonth()+1)+'-'+
f(this.getUTCDate())+'T'+
f(this.getUTCHours())+':'+
f(this.getUTCMinutes())+':'+
f(this.getUTCSeconds())+'Z':null;};String.prototype.toJSON=Number.prototype.toJSON=Boolean.prototype.toJSON=function(key){return this.valueOf();};}
var cx=/[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,escapable=/[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,gap,indent,meta={'\b':'\\b','\t':'\\t','\n':'\\n','\f':'\\f','\r':'\\r','"':'\\"','\\':'\\\\'},rep;function quote(string){escapable.lastIndex=0;return escapable.test(string)?'"'+string.replace(escapable,function(a){var c=meta[a];return typeof c==='string'?c:'\\u'+('0000'+a.charCodeAt(0).toString(16)).slice(-4);})+'"':'"'+string+'"';}
function str(key,holder){var i,k,v,length,mind=gap,partial,value=holder[key];if(value&&typeof value==='object'&&typeof value.toJSON==='function'){value=value.toJSON(key);}
if(typeof rep==='function'){value=rep.call(holder,key,value);}
switch(typeof value){case'string':return quote(value);case'number':return isFinite(value)?String(value):'null';case'boolean':case'null':return String(value);case'object':if(!value){return'null';}
gap+=indent;partial=[];if(Object.prototype.toString.apply(value)==='[object Array]'){length=value.length;for(i=0;i<length;i+=1){partial[i]=str(i,value)||'null';}
v=partial.length===0?'[]':gap?'[\n'+gap+
partial.join(',\n'+gap)+'\n'+
mind+']':'['+partial.join(',')+']';gap=mind;return v;}
if(rep&&typeof rep==='object'){length=rep.length;for(i=0;i<length;i+=1){k=rep[i];if(typeof k==='string'){v=str(k,value);if(v){partial.push(quote(k)+(gap?': ':':')+v);}}}}else{for(k in value){if(Object.hasOwnProperty.call(value,k)){v=str(k,value);if(v){partial.push(quote(k)+(gap?': ':':')+v);}}}}
v=partial.length===0?'{}':gap?'{\n'+gap+partial.join(',\n'+gap)+'\n'+
mind+'}':'{'+partial.join(',')+'}';gap=mind;return v;}}
if(typeof JSON.stringify!=='function'){JSON.stringify=function(value,replacer,space){var i;gap='';indent='';if(typeof space==='number'){for(i=0;i<space;i+=1){indent+=' ';}}else if(typeof space==='string'){indent=space;}
rep=replacer;if(replacer&&typeof replacer!=='function'&&(typeof replacer!=='object'||typeof replacer.length!=='number')){throw new Error('JSON.stringify');}
return str('',{'':value});};}
if(typeof JSON.parse!=='function'){JSON.parse=function(text,reviver){var j;function walk(holder,key){var k,v,value=holder[key];if(value&&typeof value==='object'){for(k in value){if(Object.hasOwnProperty.call(value,k)){v=walk(value,k);if(v!==undefined){value[k]=v;}else{delete value[k];}}}}
return reviver.call(holder,key,value);}
cx.lastIndex=0;if(cx.test(text)){text=text.replace(cx,function(a){return'\\u'+
('0000'+a.charCodeAt(0).toString(16)).slice(-4);});}
if(/^[\],:{}\s]*$/.test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,'@').replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,']').replace(/(?:^|:|,)(?:\s*\[)+/g,''))){j=eval('('+text+')');return typeof reviver==='function'?walk({'':j},''):j;}
throw new SyntaxError('JSON.parse');};}}());
index.js
var $canvas, $signaturePad;

$(document).ready(function () {
    $canvas = $('canvas');

    $("#btnAddSignatureToPDF").click(resizeSignatureAndProceedWithAppendingToPDF);

    $signaturePad = $('.clsCanvasContainer').signaturePad({
        drawOnly: true,
        defaultAction: 'drawIt',
        validateFields: false,
        lineWidth: 0,
        output: null,
        sigNav: null,
        name: null,
        typed: null,
        clear: '#btnClear',
        typeIt: null,
        drawIt: null,
        typeItDesc: null,
        drawItDesc: null
    });
});

function resizeSignatureAndProceedWithAppendingToPDF() {

    const ctx = $canvas[0].getContext("2d");
    ctx.clearRect(0, 0, $canvas.width, $canvas.height);

    const originalImageFromCanvas = $canvas[0].toDataURL();

    const callbackFn = function (respImage) {
        addSignatureToPDF(respImage);
    }

    resizeCanvasImage(originalImageFromCanvas, 0.28, callbackFn);
}

function addSignatureToPDF(signatureImage) {

    $("#btnAddSignatureToPDF").prop("disabled", true).text("Please Wait...");

    var myHeaders = new Headers();
    myHeaders.append("Content-Type", "application/json");
    myHeaders.append("x-api-key", $("#txtPDFcoAPIKey").val());

    // You can also upload your own file into PDF.co and use it as url. Check "Upload File" samples for code snippets: https://github.com/bytescout/pdf-co-api-samples/tree/master/File%20Upload/    
    var raw = JSON.stringify({
        "url": $("#txtInputPDFUrl").val(),
        "searchString": $("#txtTextToReplaceWith").val(),
        "caseSensitive": false,
        "replaceImage": signatureImage,//"https://bytescout-com.s3-us-west-2.amazonaws.com/files/demo-files/cloud-api/pdf-edit/logo.png",
        "async": false,
        "profiles": "{ 'AutoCropImages': true }"
    });

    var requestOptions = {
        method: 'POST',
        headers: myHeaders,
        body: raw,
        redirect: 'follow'
    };

    fetch("https://api.pdf.co/v1/pdf/edit/replace-text-with-image", requestOptions)
        .then(response => response.text())
        .then(result => handleSuccessResponse(result))
        .catch(error => {
            $("#btnAddSignatureToPDF").prop("disabled", false).text("Add to PDF");
            console.log('error', error);
        });
}

function handleSuccessResponse(result) {
    $("#btnAddSignatureToPDF").prop("disabled", true).text("Add to PDF");
    var oResult = JSON.parse(result);
    if (oResult.error) {
        $("#divError").show();
        $("#divError").html(oResult.message);
    }
    else {
        $("#aViewResult").show();
        $("#aViewResult").attr("href", oResult.url);
    }
    $("#btnAddSignatureToPDF").prop("disabled", false);
}

function resizeCanvasImage(sourceImage, resizePercentage, callbackFn) {

    // const canvas = document.getElementById("canvas");
    // const ctx = canvas.getContext("2d");
    const img = new Image();
    img.src = sourceImage;

    img.onload = function () {
        const oc = document.createElement('canvas');
        const octx = oc.getContext('2d');
        octx.clearRect(0, 0, oc.width, oc.height);

        // Set the width & height to 75% of image
        oc.width = img.width * resizePercentage;
        oc.height = img.height * resizePercentage;
        // step 2, resize to temporary size
        octx.drawImage(img, 0, 0, oc.width, oc.height);

        // Get canvas image
        const resizedImageFromCanvas = oc.toDataURL();

        callbackFn(resizedImageFromCanvas);
    }
}
LICENSE.txt
Copyright (c) 2014, Thomas J Bradley
All rights reserved.

Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:

 - Redistributions of source code must retain the above copyright notice,
   this list of conditions and the following disclaimer.

 - Redistributions in binary form must reproduce the above copyright notice,
   this list of conditions and the following disclaimer in the documentation
   and/or other materials provided with the distribution.

 - Neither the name of Thomas J Bradley nor the names of its contributors may
   be used to endorse or promote products derived from this software without
   specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

jquery.signaturepad.js
/**
 * Usage for accepting signatures:
 *  $('.sigPad').signaturePad()
 *
 * Usage for displaying previous signatures:
 *  $('.sigPad').signaturePad({displayOnly:true}).regenerate(sig)
 *  or
 *  var api = $('.sigPad').signaturePad({displayOnly:true})
 *  api.regenerate(sig)
 */
 (function ($) {

    function SignaturePad (selector, options) {
      /**
       * Reference to the object for use in public methods
       *
       * @private
       *
       * @type {Object}
       */
      var self = this
    
      /**
       * Holds the merged default settings and user passed settings
       *
       * @private
       *
       * @type {Object}
       */
      , settings = $.extend({}, $.fn.signaturePad.defaults, options)
    
      /**
       * The current context, as passed by jQuery, of selected items
       *
       * @private
       *
       * @type {Object}
       */
      , context = $(selector)
    
      /**
       * jQuery reference to the canvas element inside the signature pad
       *
       * @private
       *
       * @type {Object}
       */
      , canvas = $(settings.canvas, context)
    
      /**
       * Dom reference to the canvas element inside the signature pad
       *
       * @private
       *
       * @type {Object}
       */
      , element = canvas.get(0)
    
      /**
       * The drawing context for the signature canvas
       *
       * @private
       *
       * @type {Object}
       */
      , canvasContext = null
    
      /**
       * Holds the previous point of drawing
       * Disallows drawing over the same location to make lines more delicate
       *
       * @private
       *
       * @type {Object}
       */
      , previous = {'x': null, 'y': null}
    
      /**
       * An array holding all the points and lines to generate the signature
       * Each item is an object like:
       * {
       *   mx: moveTo x coordinate
       *   my: moveTo y coordinate
       *   lx: lineTo x coordinate
       *   lx: lineTo y coordinate
       * }
       *
       * @private
       *
       * @type {Array}
       */
      , output = []
    
      /**
       * Stores a timeout for when the mouse leaves the canvas
       * If the mouse has left the canvas for a specific amount of time
       * Stops drawing on the canvas
       *
       * @private
       *
       * @type {Object}
       */
      , mouseLeaveTimeout = false
    
        /**
         * Whether the mouse button is currently pressed down or not
         *
         * @private
         *
         * @type {Boolean}
         */
      , mouseButtonDown = false
    
      /**
       * Whether the browser is a touch event browser or not
       *
       * @private
       *
       * @type {Boolean}
       */
      , touchable = false
    
      /**
       * Whether events have already been bound to the canvas or not
       *
       * @private
       *
       * @type {Boolean}
       */
      , eventsBound = false
    
      /**
       * Remembers the default font-size when typing, and will allow it to be scaled for bigger/smaller names
       *
       * @private
       *
       * @type {Number}
       */
      , typeItDefaultFontSize = 30
    
      /**
       * Remembers the current font-size when typing
       *
       * @private
       *
       * @type {Number}
       */
      , typeItCurrentFontSize = typeItDefaultFontSize
    
      /**
       * Remembers how many characters are in the name field, to help with the scaling feature
       *
       * @private
       *
       * @type {Number}
       */
      , typeItNumChars = 0
    
    
      /**
       * Clears the mouseLeaveTimeout
       * Resets some other variables that may be active
       *
       * @private
       */
      function clearMouseLeaveTimeout () {
        clearTimeout(mouseLeaveTimeout)
        mouseLeaveTimeout = false
        mouseButtonDown = false
      }
    
      /**
       * Draws a line on canvas using the mouse position
       * Checks previous position to not draw over top of previous drawing
       *  (makes the line really thick and poorly anti-aliased)
       *
       * @private
       *
       * @param {Object} e The event object
       * @param {Number} newYOffset A pixel value for drawing the newY, used for drawing a single dot on click
       */
      function drawLine (e, newYOffset) {
        var offset, newX, newY
    
        e.preventDefault()
    
        offset = $(e.target).offset()
    
        clearTimeout(mouseLeaveTimeout)
        mouseLeaveTimeout = false
    
        if (typeof e.targetTouches !== 'undefined') {
          newX = Math.floor(e.targetTouches[0].pageX - offset.left)
          newY = Math.floor(e.targetTouches[0].pageY - offset.top)
        } else {
          newX = Math.floor(e.pageX - offset.left)
          newY = Math.floor(e.pageY - offset.top)
        }
    
        if (previous.x === newX && previous.y === newY)
          return true
    
        if (previous.x === null)
          previous.x = newX
    
        if (previous.y === null)
          previous.y = newY
    
        if (newYOffset)
          newY += newYOffset
    
        canvasContext.beginPath()
        canvasContext.moveTo(previous.x, previous.y)
        canvasContext.lineTo(newX, newY)
        canvasContext.lineCap = settings.penCap
        canvasContext.stroke()
        canvasContext.closePath()
    
        output.push({
          'lx' : newX
          , 'ly' : newY
          , 'mx' : previous.x
          , 'my' : previous.y
        })
    
        previous.x = newX
        previous.y = newY
    
        if (settings.onDraw && typeof settings.onDraw === 'function')
          settings.onDraw.apply(self)
      }
    
      /**
       * Callback wrapper for executing stopDrawing without the event
       * Put up here so that it can be removed at a later time
       *
       * @private
       */
      function stopDrawingWrapper () {
        stopDrawing()
      }
    
      /**
       * Callback registered to mouse/touch events of the canvas
       * Stops the drawing abilities
       *
       * @private
       *
       * @param {Object} e The event object
       */
      function stopDrawing (e) {
        if (!!e) {
          drawLine(e, 1)
        } else {
          if (touchable) {
            canvas.each(function () {
              this.removeEventListener('touchmove', drawLine)
              // this.removeEventListener('MSPointerMove', drawLine)
            })
          } else {
            canvas.unbind('mousemove.signaturepad')
          }
    
          if (output.length > 0 && settings.onDrawEnd && typeof settings.onDrawEnd === 'function')
            settings.onDrawEnd.apply(self)
        }
    
        previous.x = null
        previous.y = null
    
        if (settings.output && output.length > 0)
          $(settings.output, context).val(JSON.stringify(output))
      }
    
      /**
       * Draws the signature line
       *
       * @private
       */
      function drawSigLine () {
        if (!settings.lineWidth)
          return false
    
        canvasContext.beginPath()
        canvasContext.lineWidth = settings.lineWidth
        canvasContext.strokeStyle = settings.lineColour
        canvasContext.moveTo(settings.lineMargin, settings.lineTop)
        canvasContext.lineTo(element.width - settings.lineMargin, settings.lineTop)
        canvasContext.stroke()
        canvasContext.closePath()
      }
    
      /**
       * Clears all drawings off the canvas and redraws the signature line
       *
       * @private
       */
      function clearCanvas () {
        canvasContext.clearRect(0, 0, element.width, element.height)
        canvasContext.fillStyle = settings.bgColour
        canvasContext.fillRect(0, 0, element.width, element.height)
    
        if (!settings.displayOnly)
          drawSigLine()
    
        canvasContext.lineWidth = settings.penWidth
        canvasContext.strokeStyle = settings.penColour
    
        $(settings.output, context).val('')
        output = []
    
        stopDrawing()
      }
    
      /**
       * Callback registered to mouse/touch events of the canvas
       * Draws a line at the mouse cursor location, starting a new line if necessary
       *
       * @private
       *
       * @param {Object} e The event object
       * @param {Object} o The object context registered to the event; canvas
       */
      function onMouseMove(e, o) {
        if (previous.x == null) {
          drawLine(e, 1)
        } else {
          drawLine(e, o)
        }
      }
    
      /**
       * Callback registered to mouse/touch events of canvas
       * Triggers the drawLine function
       *
       * @private
       *
       * @param {Object} e The event object
       * @param {Object} touchObject The object context registered to the event; canvas
       */
      function startDrawing (e, touchObject) {
        if (touchable) {
          touchObject.addEventListener('touchmove', onMouseMove, false)
          // touchObject.addEventListener('MSPointerMove', onMouseMove, false)
        } else {
          canvas.bind('mousemove.signaturepad', onMouseMove)
        }
    
        // Draws a single point on initial mouse down, for people with periods in their name
        drawLine(e, 1)
      }
    
      /**
       * Removes all the mouse events from the canvas
       *
       * @private
       */
      function disableCanvas () {
        eventsBound = false
    
        canvas.each(function () {
          if (this.removeEventListener) {
            this.removeEventListener('touchend', stopDrawingWrapper)
            this.removeEventListener('touchcancel', stopDrawingWrapper)
            this.removeEventListener('touchmove', drawLine)
            this.removeEventListener('touchstart', startTouchDrawing)
            // this.removeEventListener('MSPointerUp', stopDrawingWrapper)
            // this.removeEventListener('MSPointerCancel', stopDrawingWrapper)
            // this.removeEventListener('MSPointerMove', drawLine)
          }
           })
        $(document).unbind('mouseup.signaturepad')
        canvas.unbind('mousedown.signaturepad')
        canvas.unbind('mousemove.signaturepad')
        canvas.unbind('mouseleave.signaturepad')
    
        $(settings.clear, context).unbind('click.signaturepad')
      }
    
      /**
       * Lazy touch event detection
       * Uses the first press on the canvas to detect either touch or mouse reliably
       * Will then bind other events as needed
       *
       * @private
       *
       * @param {Object} e The event object
       */
      function initDrawEvents (e) {
        if (eventsBound)
          return false
    
        eventsBound = true
    
        // Closes open keyboards to free up space
        $('input').blur();
    
        if (typeof e.targetTouches !== 'undefined')
          touchable = true
    
        if (touchable) {
          canvas.each(function () {
            this.addEventListener('touchend', stopDrawingWrapper, false)
            this.addEventListener('touchcancel', stopDrawingWrapper, false)
            // this.addEventListener('MSPointerUp', stopDrawingWrapper, false)
            // this.addEventListener('MSPointerCancel', stopDrawingWrapper, false)
          })
    
          canvas.unbind('mousedown.signaturepad')
        } else {
          $(document).bind('mouseup.signaturepad', function () {
            if (mouseButtonDown) {
              stopDrawing()
              clearMouseLeaveTimeout()
            }
          })
          canvas.bind('mouseleave.signaturepad', function (e) {
            if (mouseButtonDown) stopDrawing(e)
    
            if (mouseButtonDown && !mouseLeaveTimeout) {
              mouseLeaveTimeout = setTimeout(function () {
                stopDrawing()
                clearMouseLeaveTimeout()
              }, 500)
            }
          })
    
          canvas.each(function () {
            this.removeEventListener('touchstart', startTouchDrawing)
          })
        }
      }
      function startTouchDrawing(e) {
          e.preventDefault()
          mouseButtonDown = true
          initDrawEvents(e)
          startDrawing(e, this)
    
      }
      /**
       * Triggers the abilities to draw on the canvas
       * Sets up mouse/touch events, hides and shows descriptions and sets current classes
       *
       * @private
       */
      function drawIt () {
        $(settings.typed, context).hide()
        clearCanvas()
    
        canvas.each(function () {
            this.addEventListener('touchstart', startTouchDrawing)
        })
    
        canvas.bind('mousedown.signaturepad', function (e) {
          e.preventDefault()
    
          // Only allow left mouse clicks to trigger signature drawing
          if (e.which > 1) return false
    
          mouseButtonDown = true
          initDrawEvents(e)
          startDrawing(e)
        })
    
        $(settings.clear, context).bind('click.signaturepad', function (e) { e.preventDefault(); clearCanvas() })
    
        $(settings.typeIt, context).bind('click.signaturepad', function (e) { e.preventDefault(); typeIt() })
        $(settings.drawIt, context).unbind('click.signaturepad')
        $(settings.drawIt, context).bind('click.signaturepad', function (e) { e.preventDefault() })
    
        $(settings.typeIt, context).removeClass(settings.currentClass)
        $(settings.drawIt, context).addClass(settings.currentClass)
        $(settings.sig, context).addClass(settings.currentClass)
    
        $(settings.typeItDesc, context).hide()
        $(settings.drawItDesc, context).show()
        $(settings.clear, context).show()
      }
    
      /**
       * Triggers the abilities to type in the input for generating a signature
       * Sets up mouse events, hides and shows descriptions and sets current classes
       *
       * @private
       */
      function typeIt () {
        clearCanvas()
        disableCanvas()
        $(settings.typed, context).show()
    
        $(settings.drawIt, context).bind('click.signaturepad', function (e) { e.preventDefault(); drawIt() })
        $(settings.typeIt, context).unbind('click.signaturepad')
        $(settings.typeIt, context).bind('click.signaturepad', function (e) { e.preventDefault() })
    
        $(settings.output, context).val('')
    
        $(settings.drawIt, context).removeClass(settings.currentClass)
        $(settings.typeIt, context).addClass(settings.currentClass)
        $(settings.sig, context).removeClass(settings.currentClass)
    
        $(settings.drawItDesc, context).hide()
        $(settings.clear, context).hide()
        $(settings.typeItDesc, context).show()
    
        typeItCurrentFontSize = typeItDefaultFontSize = $(settings.typed, context).css('font-size').replace(/px/, '')
      }
    
      /**
       * Callback registered on key up and blur events for input field
       * Writes the text fields value as Html into an element
       *
       * @private
       *
       * @param {String} val The value of the input field
       */
      function type (val) {
        var typed = $(settings.typed, context)
          , cleanedVal = $.trim(val.replace(/>/g, '&gt;').replace(/</g, '&lt;'))
          , oldLength = typeItNumChars
          , edgeOffset = typeItCurrentFontSize * 0.5
    
        typeItNumChars = cleanedVal.length
        typed.html(cleanedVal)
    
        if (!cleanedVal) {
          typed.css('font-size', typeItDefaultFontSize + 'px')
          return
        }
    
        if (typeItNumChars > oldLength && typed.outerWidth() > element.width) {
          while (typeItCurrentFontSize > 4 && typed.outerWidth() > element.width) {
            typeItCurrentFontSize--
            typed.css('font-size', typeItCurrentFontSize + 'px')
          }
        }
    
        if (typeItNumChars < oldLength && typed.outerWidth() + edgeOffset < element.width && typeItCurrentFontSize < typeItDefaultFontSize) {
          while (typeItCurrentFontSize < 512 && typed.outerWidth() + edgeOffset < element.width && typeItCurrentFontSize < typeItDefaultFontSize) {
            typeItCurrentFontSize++
            typed.css('font-size', typeItCurrentFontSize + 'px')
          }
        }
      }
    
      /**
       * Default onBeforeValidate function to clear errors
       *
       * @private
       *
       * @param {Object} context current context object
       * @param {Object} settings provided settings
       */
      function onBeforeValidate (context, settings) {
        $('p.' + settings.errorClass, context).remove()
        context.removeClass(settings.errorClass)
        $('input, label', context).removeClass(settings.errorClass)
      }
    
      /**
       * Default onFormError function to show errors
       *
       * @private
       *
       * @param {Object} errors object contains validation errors (e.g. nameInvalid=true)
       * @param {Object} context current context object
       * @param {Object} settings provided settings
       */
      function onFormError (errors, context, settings) {
        if (errors.nameInvalid) {
          context.prepend(['<p class="', settings.errorClass, '">', settings.errorMessage, '</p>'].join(''))
          $(settings.name, context).focus()
          $(settings.name, context).addClass(settings.errorClass)
          $('label[for=' + $(settings.name).attr('id') + ']', context).addClass(settings.errorClass)
        }
    
        if (errors.drawInvalid)
          context.prepend(['<p class="', settings.errorClass, '">', settings.errorMessageDraw, '</p>'].join(''))
      }
    
      /**
       * Validates the form to confirm a name was typed in the field
       * If drawOnly also confirms that the user drew a signature
       *
       * @private
       *
       * @return {Boolean}
       */
      function validateForm () {
        var valid = true
          , errors = {drawInvalid: false, nameInvalid: false}
          , onBeforeArguments = [context, settings]
          , onErrorArguments = [errors, context, settings]
    
        if (settings.onBeforeValidate && typeof settings.onBeforeValidate === 'function') {
          settings.onBeforeValidate.apply(self,onBeforeArguments)
        } else {
          onBeforeValidate.apply(self, onBeforeArguments)
        }
    
        if (settings.drawOnly && output.length < 1) {
          errors.drawInvalid = true
          valid = false
        }
    
        if ($(settings.name, context).val() === '') {
          errors.nameInvalid = true
          valid = false
        }
    
        if (settings.onFormError && typeof settings.onFormError === 'function') {
          settings.onFormError.apply(self,onErrorArguments)
        } else {
          onFormError.apply(self, onErrorArguments)
        }
    
        return valid
      }
    
      /**
       * Redraws the signature on a specific canvas
       *
       * @private
       *
       * @param {Array} paths the signature JSON
       * @param {Object} context the canvas context to draw on
       * @param {Boolean} saveOutput whether to write the path to the output array or not
       */
      function drawSignature (paths, context, saveOutput) {
        for(var i in paths) {
          if (typeof paths[i] === 'object') {
            context.beginPath()
            context.moveTo(paths[i].mx, paths[i].my)
            context.lineTo(paths[i].lx, paths[i].ly)
            context.lineCap = settings.penCap
            context.stroke()
            context.closePath()
    
            if (saveOutput && paths[i].lx) {
              output.push({
                'lx' : paths[i].lx
                , 'ly' : paths[i].ly
                , 'mx' : paths[i].mx
                , 'my' : paths[i].my
              })
            }
          }
        }
      }
    
      /**
       * Initialisation function, called immediately after all declarations
       * Technically public, but only should be used internally
       *
       * @private
       */
      function init () {
        // Fixes the jQuery.fn.offset() function for Mobile Safari Browsers i.e. iPod Touch, iPad and iPhone
        // https://gist.github.com/661844
        // http://bugs.jquery.com/ticket/6446
        if (parseFloat(((/CPU.+OS ([0-9_]{3}).*AppleWebkit.*Mobile/i.exec(navigator.userAgent)) || [0,'4_2'])[1].replace('_','.')) < 4.1) {
           $.fn.Oldoffset = $.fn.offset;
           $.fn.offset = function () {
              var result = $(this).Oldoffset()
              result.top -= window.scrollY
              result.left -= window.scrollX
    
              return result
           }
        }
    
        // Disable selection on the typed div and canvas
        $(settings.typed, context).bind('selectstart.signaturepad', function (e) { return $(e.target).is(':input') })
        canvas.bind('selectstart.signaturepad', function (e) { return $(e.target).is(':input') })
    
        if (!element.getContext && FlashCanvas)
          FlashCanvas.initElement(element)
    
        if (element.getContext) {
          canvasContext = element.getContext('2d')
    
          $(settings.sig, context).show()
    
          if (!settings.displayOnly) {
            if (!settings.drawOnly) {
              $(settings.name, context).bind('keyup.signaturepad', function () {
                type($(this).val())
              })
    
              $(settings.name, context).bind('blur.signaturepad', function () {
                type($(this).val())
              })
    
              $(settings.drawIt, context).bind('click.signaturepad', function (e) {
                e.preventDefault()
                drawIt()
              })
            }
    
            if (settings.drawOnly || settings.defaultAction === 'drawIt') {
              drawIt()
            } else {
              typeIt()
            }
    
            if (settings.validateFields) {
              if ($(selector).is('form')) {
                $(selector).bind('submit.signaturepad', function () { return validateForm() })
              } else {
                $(selector).parents('form').bind('submit.signaturepad', function () { return validateForm() })
              }
            }
    
            $(settings.sigNav, context).show()
          }
        }
      }
    
      $.extend(self, {
        /**
         * A property to store the current version of Signature Pad
         */
        signaturePad : '{{version}}'
    
        /**
         * Initializes SignaturePad
         */
        , init : function () { init() }
    
        /**
         * Allows options to be updated after initialization
         *
         * @param {Object} options An object containing the options to be changed
         */
        , updateOptions : function (options) {
          $.extend(settings, options)
        }
    
        /**
         * Regenerates a signature on the canvas using an array of objects
         * Follows same format as object property
         * @see var object
         *
         * @param {Array} paths An array of the lines and points
         */
        , regenerate : function (paths) {
          self.clearCanvas()
          $(settings.typed, context).hide()
    
          if (typeof paths === 'string')
            paths = JSON.parse(paths)
    
          drawSignature(paths, canvasContext, true)
    
          if (settings.output && $(settings.output, context).length > 0)
            $(settings.output, context).val(JSON.stringify(output))
        }
    
        /**
         * Clears the canvas
         * Redraws the background colour and the signature line
         */
        , clearCanvas : function () { clearCanvas() }
    
        /**
         * Returns the signature as a Js array
         *
         * @return {Array}
         */
        , getSignature : function () { return output }
    
        /**
         * Returns the signature as a Json string
         *
         * @return {String}
         */
        , getSignatureString : function () { return JSON.stringify(output) }
    
        /**
         * Returns the signature as an image
         * Re-draws the signature in a shadow canvas to create a clean version
         *
         * @return {String}
         */
        , getSignatureImage : function () {
          var tmpCanvas = document.createElement('canvas')
            , tmpContext = null
            , data = null
    
          tmpCanvas.style.position = 'absolute'
          tmpCanvas.style.top = '-999em'
          tmpCanvas.width = element.width
          tmpCanvas.height = element.height
          document.body.appendChild(tmpCanvas)
    
          if (!tmpCanvas.getContext && FlashCanvas)
            FlashCanvas.initElement(tmpCanvas)
    
          tmpContext = tmpCanvas.getContext('2d')
    
          tmpContext.fillStyle = settings.bgColour
          tmpContext.fillRect(0, 0, element.width, element.height)
          tmpContext.lineWidth = settings.penWidth
          tmpContext.strokeStyle = settings.penColour
    
          drawSignature(output, tmpContext)
          data = tmpCanvas.toDataURL.apply(tmpCanvas, arguments)
    
          document.body.removeChild(tmpCanvas)
          tmpCanvas = null
    
          return data
        }
    
        /**
         * The form validation function
         * Validates that the signature has been filled in properly
         * Allows it to be hooked into another validation function and called at a different time
         *
         * @return {Boolean}
         */
        , validateForm : function () { return validateForm() }
      })
    }
    
    /**
     * Create the plugin
     * Returns an Api which can be used to call specific methods
     *
     * @param {Object} options The options array
     *
     * @return {Object} The Api for controlling the instance
     */
    $.fn.signaturePad = function (options) {
      var api = null
    
      this.each(function () {
        if (!$.data(this, 'plugin-signaturePad')) {
          api = new SignaturePad(this, options)
          api.init()
          $.data(this, 'plugin-signaturePad', api)
        } else {
          api = $.data(this, 'plugin-signaturePad')
          api.updateOptions(options)
        }
      })
    
      return api
    }
    
    /**
     * Expose the defaults so they can be overwritten for multiple instances
     *
     * @type {Object}
     */
    $.fn.signaturePad.defaults = {
      defaultAction : 'typeIt' // What action should be highlighted first: typeIt or drawIt
      , displayOnly : false // Initialize canvas for signature display only; ignore buttons and inputs
      , drawOnly : false // Whether the to allow a typed signature or not
      , canvas : 'canvas' // Selector for selecting the canvas element
      , sig : '.sig' // Parts of the signature form that require Javascript (hidden by default)
      , sigNav : '.sigNav' // The TypeIt/DrawIt navigation (hidden by default)
      , bgColour : '#ffffff' // The colour fill for the background of the canvas; or transparent
      , penColour : '#145394' // Colour of the drawing ink
      , penWidth : 2 // Thickness of the pen
      , penCap : 'round' // Determines how the end points of each line are drawn (values: 'butt', 'round', 'square')
      , lineColour : '#ccc' // Colour of the signature line
      , lineWidth : 2 // Thickness of the signature line
      , lineMargin : 5 // Margin on right and left of signature line
      , lineTop : 35 // Distance to draw the line from the top
      , name : '.name' // The input field for typing a name
      , typed : '.typed' // The Html element to accept the printed name
      , clear : '.clearButton' // Button for clearing the canvas
      , typeIt : '.typeIt a' // Button to trigger name typing actions (current by default)
      , drawIt : '.drawIt a' // Button to trigger name drawing actions
      , typeItDesc : '.typeItDesc' // The description for TypeIt actions
      , drawItDesc : '.drawItDesc' // The description for DrawIt actions (hidden by default)
      , output : '.output' // The hidden input field for remembering line coordinates
      , currentClass : 'current' // The class used to mark items as being currently active
      , validateFields : true // Whether the name, draw fields should be validated
      , errorClass : 'error' // The class applied to the new error Html element
      , errorMessage : 'Please enter your name' // The error message displayed on invalid submission
      , errorMessageDraw : 'Please sign the document' // The error message displayed when drawOnly and no signature is drawn
      , onBeforeValidate : null // Pass a callback to be used instead of the built-in function
      , onFormError : null // Pass a callback to be used instead of the built-in function
      , onDraw : null // Pass a callback to be used to capture the drawing process
      , onDrawEnd : null // Pass a callback to be exectued after the drawing process
    }
    
    }(jQuery));

PDF.co Web API: the Web API with a set of tools for documents manipulation, data conversion, data extraction, splitting and merging of documents. Includes image recognition, built-in OCR, barcode generation and barcode decoders to decode bar codes from scans, pictures and pdf.

Get your PDF.co API key here!

Download Source Code (.zip)

return to the previous page explore PDF Search and Replace Text with Image endpoint