Skip to main content

index.html

index.html

The frontpage for navigating to the various demo samples should be adjusted as such:

Firstly, the following adjustments are required for the src:

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<!--meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"-->
<title>Signature SDK Demo</title>
<link href="../common/style/wacom.css" rel="stylesheet" type="text/css">
<link href="/sigCaptDialog/style/sigCaptDialog.css" rel="stylesheet" type="text/css">
<link href="/node_modules/jquery-ui-dist/jquery-ui.min.css" rel="stylesheet" type="text/css">
<script src="/node_modules/jquery/dist/jquery.min.js"></script>
<script src="/node_modules/jquery-ui-dist/jquery-ui.min.js"></script>
<script src="/node_modules/jquery-ui-touch-punch/jquery.ui.touch-punch.min.js"></script>
<script src="/node_modules/clipper-lib/clipper.js"></script>
<script src="/node_modules/js-md5/build/md5.min.js"></script>
<script src="/node_modules/poly2tri/dist/poly2tri.min.js"></script>
<script src="/node_modules/long/dist/long.js"></script>
<script src="/node_modules/protobufjs/dist/protobuf.min.js"></script>
<script src="/node_modules/jszip/dist/jszip.min.js"></script>
<script src="/node_modules/gl-matrix/gl-matrix-min.js"></script>
<script src="/node_modules/rbush/rbush.min.js"></script>
<script src="/node_modules/js-ext/js-ext-min.js"></script>
<script src="/node_modules/digital-ink/digital-ink-min.js"></script>
<script src="../common/will/tools.js"></script>

<script src="/node_modules/sjcl/sjcl.js"></script>
<script src="/sigCaptDialog/libs/stu_capture/stu-sdk.min.js"></script> <!-- used to connect to STU devices -->
<script src="/sigCaptDialog/libs/stu_capture/stu_capture_encryption.js"></script> <!-- STU encryption functions -->

<script src="../common/libs/signature_sdk.js"></script> <!-- signature SDK -->
<script src="../common/libs/signature_sdk_helper.js"></script> <!-- signature SDK helper -->

<script src="/node_modules/node-forge/dist/forge.min.js"></script>

<script src="/sigCaptDialog/sigCaptDialog.js"></script>
<script src="/sigCaptDialog/stuCaptDialog.js"></script>

Immediately afterwards, adjust the code after the <script> tag on line 37 as such:

First, remove licence:


<script>
const licence = "<<<licence>>>";
var mSigObj;
var documentHash;
var backgroundImage;
var sigCaptDialog
var stuCapDialog;

After var encryptionKeys:

			// This var will store the public and private keys for encryption.
// Please note that this is only a demostration, but on a production application
// for security reasons the private key should not be stored in a global variable.
var encryptionKeys;

Module.onRuntimeInitialized = _ => {
document.getElementById("version_txt").innerHTML = Module.VERSION;
if (navigator.hid) {
document.getElementById("capture_stu_btn").disabled = false;
}

document.getElementById("capture_canvas_btn").disabled = false;
document.getElementById("document").disabled = false;

document.getElementById("load_signature").disabled = false;

documentHash = new Module.Hash(Module.HashType.None);
mSigObj = new Module.SigObj();
mSigObj.setLicence(licence);
}
...

The function generateConfig needs the following adjustments:


function generateConfig() {
const config = {};
config.useWill = document.getElementById("use_will").checked;
config.strokeSize = document.getElementById("ink_width").value;
config.strokeColor = document.getElementById("rendering_color_box").value;
config.width = document.getElementById("dialog_width").value;
config.height = document.getElementById("dialog_height").value;
config.left = document.getElementById("dialog_left").value + "px";
config.top = document.getElementById("dialog_top").value + "px";
config.centered = document.getElementById("is_centered").checked;
config.title = document.getElementById("title_text").value;
config.borderColor = document.getElementById("border_color_box").value;
config.borderWidth = document.getElementById("border_width_box").value;
config.hasTitle = document.getElementById("has_title_check").checked;

config.signatory = {visible:document.getElementById("show_signatory_check").checked,
fontFace:document.getElementById("signatory_font_type_text").value,
fontSize:parseInt(document.getElementById("signatory_font_size_text").value),
offsetX:parseInt(document.getElementById("signatory_offset_x_text").value),
offsetY:parseInt(document.getElementById("signatory_offset_y_text").value),
color:document.getElementById("signatory_color_box").value
};

config.reason = {visible:document.getElementById("show_reason_check").checked,
fontFace:document.getElementById("reason_font_type_text").value,
fontSize:parseInt(document.getElementById("reason_font_size_text").value),
offsetX:parseInt(document.getElementById("reason_offset_x_text").value),
offsetY:parseInt(document.getElementById("reason_offset_y_text").value),
color:document.getElementById("reason_color_box").value
};

config.date = {visible:document.getElementById("show_date_check").checked,
fontFace:document.getElementById("date_font_type_text").value,
fontSize:parseInt(document.getElementById("date_font_size_text").value),
offsetX:parseInt(document.getElementById("date_offset_x_text").value),
offsetY:parseInt(document.getElementById("date_offset_y_text").value),
color:document.getElementById("date_color_box").value
};

config.signingLine = {visible:document.getElementById("show_signing_line_check").checked,
left:parseInt(document.getElementById("signing_line_left_text").value),
right:parseInt(document.getElementById("signing_line_right_text").value),
width:parseInt(document.getElementById("signing_line_width_text").value),
offsetY:parseInt(document.getElementById("signing_line_offset_y_text").value),
color:document.getElementById("signing_line_color_box").value
};

config.buttonsFont = document.getElementById("button_font_type").value;
config.buttons = [];
const fields = document.getElementById("button_list_div").getElementsByTagName("fieldset");
for (var i=0; i<fields.length; i++) {
config.buttons.push({text:fields[i].elements.namedItem("button_text").value,
textColor:fields[i].elements.namedItem("button_text_color").value,
backgroundColor:fields[i].elements.namedItem("button_background_color").value,
borderColor:fields[i].elements.namedItem("button_border_color").value,
borderWidth:parseInt(fields[i].elements.namedItem("button_border_width").value),
onClick:eval(fields[i].elements.namedItem("button_action").value)});
}

if (!document.getElementById("shows_as_dialog").checked) {
config.attachTo = "captureDiv";
}

const comboSizeModes = document.getElementById("stu_fit_mode");
config.sizeMode = comboSizeModes.options[comboSizeModes.selectedIndex].value;

config.modal = document.getElementById("shows_modal").checked;
config.draggable = document.getElementById("is_draggable").checked;

const comboTools = document.getElementById("inking_tool");
const inkColor = document.getElementById("rendering_color_box").value;
if (document.getElementById("use_will").checked) {
config.will = {tool:tools[comboTools.options[comboTools.selectedIndex].value], color:inkColor};
}

const comboBackgroundMode = document.getElementById("background_image_mode");
config.background = {color:document.getElementById("background_color_box").value,
alpha:document.getElementById("background_opacity").value*0.01,
mode:comboBackgroundMode.options[comboBackgroundMode.selectedIndex].value};

if ((document.getElementById("put_background_image").checked) && (backgroundImage)) {
config.background.image = backgroundImage;
}

if (document.getElementById("enable_timeout").checked) {
config.timeOut = {enabled:true};
config.timeOut.time = parseInt(document.getElementById("timeOutValue").value);
config.timeOut.onTimeOut = timeOutCallback;
}

config.minTimeOnSurface = parseInt(document.getElementById("minTimeOnSurface").value);

return config;
}

As does the function captureFromCanvas:


function captureFromCanvas() {
stuCapDialog = null;
const config = generateConfig();
config.source = {mouse:document.getElementById("allow_mouse_check").checked,
touch:document.getElementById("allow_touch_check").checked,
pen:document.getElementById("allow_pen_check").checked}
sigCaptDialog = new SigCaptDialog(config);
sigCaptDialog.addEventListener("ok", function() {
encryptSignature();
renderSignature();
});
sigCaptDialog.open(mSigObj, document.getElementById("who").value, document.getElementById("why").value, generateExtraData(), Module.KeyType.SHA512, documentHash);
sigCaptDialog.startCapture();
}

The function captureFromSTU needs to be made asynchronous and given the following additional adjustments.


function captureFromSTU() {
sigCaptDialog = null;
const config = generateConfig();
config.encryption = {
sessionId: window.crypto.getRandomValues(new Uint32Array(1))[0], // 32 bits random value
encryptionHandler: new MyEncryptionHandler(), // only necessary if connecting to STU-300/500/520
encryptionHandler2: new MyEncryptionHandler2(), // only necessary if connection to STU-430/530/540
};
stuCapDialog = new StuCaptDialog(config);
stuCapDialog.addEventListener("ok", function() {
encryptSignature();
renderSignature();
});
stuCapDialog.open(mSigObj, document.getElementById("who").value, document.getElementById("why").value, generateExtraData(), Module.KeyType.SHA512, documentHash);
}

After this, add the following two functions: setDeviceName and removeDevice.


function setDeviceName() {
const stuDeviceStr = localStorage.getItem("stuDevice");
if (stuDeviceStr) {
document.getElementById("selectedStuDevice").innerHTML = JSON.parse(stuDeviceStr).productName;
} else {
document.getElementById("selectedStuDevice").innerHTML = "None";
}
}

function removeDevice() {
localStorage.removeItem("stuDevice");
setDeviceName();
}

Adjust the final several lines within renderSignatureImage.


...
const inkColor = document.getElementById("rendering_color_box").value;
const comboTools = document.getElementById("inking_tool");
const inkTool = document.getElementById("use_will").checked ? tools[comboTools.options[comboTools.selectedIndex].value] : parseInt(document.getElementById("ink_width").value);
const image = await mSigObj.renderBitmap(renderWidth, renderHeight, "image/png", inkTool, inkColor, backgroundColor, 0, 0, renderFlags);
return image;
}

Adjust the function decodePKCS12 like so:


function decodePKCS12(file, password) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = evt => {
try {
const binary = evt && evt.target ? evt.target.result : null
if (!binary) {
reject(new Error('No file data'))
}
const p12Asn1 = forge.asn1.fromDer(binary)
const p12 = forge.pkcs12.pkcs12FromAsn1(p12Asn1, password)

const bags = p12.getBags({bagType: forge.pki.oids.certBag})
const cert = bags[forge.pki.oids.certBag][0];
if (cert?.cert?.validity?.notAfter < new Date()) {
reject("Certificate has expired");
}

const pkeyBags = p12.getBags({bagType: forge.pki.oids.pkcs8ShroudedKeyBag})
const keybag = pkeyBags[forge.pki.oids.pkcs8ShroudedKeyBag][0];

const privateKey = forge.pki.privateKeyToPem(keybag.key)
const publicKey = forge.pki.publicKeyToPem(forge.pki.setRsaPublicKey(keybag.key.n, keybag.key.e));
resolve({publicKey, privateKey})
} catch (e) {
reject(e)
}
}
reader.onerror = reject
reader.readAsBinaryString(file)
})
}

The function useWillCheck should be removed.


function useWillCheck() {
document.getElementById("inking_tool").disabled = !document.getElementById("use_will").checked;
document.getElementById("ink_width").disabled = document.getElementById("use_will").checked;
}

The remaining adjustments for the HTML are:

  1. Adding a new div tag at the top of <body>:

<body>
<div class="wrapper">

The next adjustment required is for capturing data from an STU:


<label for="document">Document:</label><br>
<input type="file" id="document" name="document" onchange="addDocumentHash()" disabled="disabled"><br><br>

<button id="capture_stu_btn" onClick="capture('STU')" disabled="disabled">Capture signature from STU tablet</button><br><br>
<p>
The input data for the signature can be taken from different sources:
<fieldset style="width:400px;height:40px;padding:5px;margin:5px">
<legend>Input sources</legend>
<input type="checkbox" name="allow_mouse_check" id="allow_mouse_check" checked="checked">
<label for="allow_mouse_check">Allow mouse</label>
&nbsp;&nbsp;
<input type="checkbox" name="allow_touch_check" id="allow_touch_check" checked="checked">
<label for="allow_touch_check">Allow touch</label>
&nbsp;&nbsp;
<input type="checkbox" name="allow_pen_check" id="allow_pen_check" checked="checked">
<label for="allow_pen_check">Allow pen</label>
</fieldset>
</p>
<button id="capture_canvas_btn" onClick="capture('Canvas')">Capture signature from Generic device</button>
<br><br>
<fieldset style="width:0;padding:5px;margin:5px;">
<legend>Signature</legend>
<div id="captureDiv" style="width:400px;height:330px;"></div>
<img id="signatureImage" style="display:none">
</fieldset>

The final adjustment needed is within the Settings section.


<div id="settings_div" class="tabcontent">
<h2>Settings</h2>
<div>
<fieldset style="width:400px;padding:5px;margin:5px">
<legend>Ink</legend>
<input type="checkbox" name="use_will" id="use_will" checked="checked" onchange="useWillCheck()">
<label for="use_will">use WILL</label>
<br><br>
<label for="ink_width">Ink width:</label>
<input type="input" name="ink_width" id="ink_width" value="2" disabled="disabled">
<br><br>
<label for="inking_tool">Inking tool:</label>
<select name="inking_tool" id="inking_tool">
<option value="pen">pen</option>
<option value="felt">felt</option>
<option value="brush">brush</option>
<option value="marker">marker</option>
<option value="pencil">pencil</option>
<option value="waterBrush">waterBrush</option>
<option value="inkBrush">inkBrush</option>
<option value="rainbowBrush">rainbowBrush</option>
<option value="crayon">crayon</option>
</select>
</br></br>
<label for="rendering_color_box">Ink color:</label>
<input type="color" name="rendering_color_box" id="rendering_color_box" value="#000F55">
<br>
<label for="background_color_box">Background color</label>
<input type="color" name="background_color_box" id="background_color_box" value="#ffffff">
<br>
<input type="checkbox" name="put_background_image" id="put_background_image">
<label for="background_image">Use background image</label><br>
<input type="file" id="background_image" name="background_image" onChange="loadBackgroundImage()" accept="image/*">
<br>
<label for="background_image_mode">Background image mode</label><br>
<select name="background_image_mode" id="background_image_mode">
<option value="none">None</option>
<option value="fit" selected="selected">Fit</option>
<option value="center">Center</option>
<option value="pattern">Pattern</option>
</select>
<br>
<label for="background_opacity">Background opacity</label>
<input type="range" min="1" max="100" value="100" id="background_opacity">
</fieldset>
</div>