First commit: can open/save/sign/stamp PDFs

master
Skylar Ittner 3 years ago
commit 3f53f343fe

@ -0,0 +1,75 @@
/*
Copyright 2021 Netsyms Technologies.
This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
/*
Created on : Jun 27, 2021, 7:34:10 PM
Author : Skylar Ittner
*/
html, body {
height: 100%;
width: 100%;
}
#page-canvas-container {
height: 90vh;
max-height: 90vh;
max-width: 80vw;
overflow: scroll;
}
#page-canvas-container .page-canvas {
margin: 0.5em;
box-shadow: 6px 6px 7px -1px rgba(0,0,0,0.5);
height: 100%;
border: 1px solid gray;
}
#page-canvas-container .page-canvas.active {
margin: 0.5em;
box-shadow: 0px 0px 7px 0px rgba(0,255,0,0.8);
height: 100%;
border: 1px solid blue;
}
#page-canvas-container #placementguidebox {
opacity: 0.5;
position: absolute;
float: left;
margin-top: -40px;
padding: 0;
}
#signature_pad {
border: 1px solid black;
}
.signature-wrapper {
background-color: white;
border-radius: 5px;
position: relative;
width: 400px;
height: 200px;
margin: 0 auto;
border: 1px solid rgba(0,0,0,0.5);
/* fix bug on iOS where image sticks out right side and makes entire page scroll horiz. */
overflow: hidden;
}
.signature-wrapper img {
position: absolute;
bottom: 0;
left: 0;
}
.signature-wrapper canvas {
position: absolute;
left: 0;
top: 0;
width: 400px;
height: 200px;
}

@ -0,0 +1,73 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="400"
height="200"
viewBox="0 0 105.83333 52.916669"
version="1.1"
id="svg8"
inkscape:version="0.92.3 (2405546, 2018-03-11)"
sodipodi:docname="signature-line.svg">
<defs
id="defs2" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="0.7"
inkscape:cx="-82.70874"
inkscape:cy="65.096693"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
showgrid="false"
units="px"
fit-margin-top="50"
fit-margin-left="30"
fit-margin-right="30"
fit-margin-bottom="50"
inkscape:window-width="1920"
inkscape:window-height="1015"
inkscape:window-x="1920"
inkscape:window-y="0"
inkscape:window-maximized="1" />
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-25.421766,-74.750004)">
<g
aria-label="X"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.23333311px;line-height:100%;font-family:Ubuntu;-inkscape-font-specification:Ubuntu;letter-spacing:3.34962487px;word-spacing:0px;fill:#000000;fill-opacity:0.39303482;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
id="text817"
transform="matrix(4.2854051,0,0,4.2854051,-189.27268,-367.87464)">
<path
d="m 53.529892,112.54762 q -0.0635,-0.127 -0.1524,-0.2794 -0.0889,-0.15663 -0.194734,-0.32173 -0.105833,-0.16933 -0.220133,-0.33443 -0.1143,-0.16934 -0.220133,-0.3175 -0.105834,0.14816 -0.220134,0.3175 -0.1143,0.1651 -0.220133,0.33443 -0.1016,0.1651 -0.194733,0.32173 -0.0889,0.1524 -0.1524,0.2794 h -0.452967 q 0.1905,-0.37253 0.440267,-0.75776 0.254,-0.38524 0.537633,-0.79164 l -0.9398,-1.3843 h 0.474133 l 0.7239,1.0922 0.715434,-1.0922 h 0.4699 l -0.922867,1.36737 q 0.287867,0.41063 0.541867,0.8001 0.254,0.38947 0.448733,0.76623 z"
style="stroke-width:0.26458332px;fill:#000000;fill-opacity:0.39303482"
id="path819"
inkscape:connector-curvature="0" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.9 KiB

@ -0,0 +1,120 @@
<!DOCTYPE html>
<!--
Copyright 2021 Netsyms Technologies.
This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at http://mozilla.org/MPL/2.0/.
-->
<title>IPENtool</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="node_modules/bootstrap/dist/css/bootstrap.min.css" />
<link rel="stylesheet" href="node_modules/@fortawesome/fontawesome-free/css/all.min.css" />
<link rel="stylesheet" href="css/main.css" />
<div class="modal fade" id="settingsModal" tabindex="-1" aria-labelledby="settingsModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="settingsModalLabel">Settings</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
Name: <input class="form-control" type="text" id="notary_name" />
Location (county/residing at): <input class="form-control" type="text" id="notary_location" />
Commission expiration: <input class="form-control" type="text" id="notary_expires" />
Commission number: <input class="form-control" type="text" id="notary_idnumber" />
State: <select class="form-control" id="notary_state">
<option selected></option>
<option value="mt">Montana</option>
</select>
<br>
Stamp Preview:<br>
<img src="" id="stamp-preview" />
<br>
Notary Signature:<br>
<img src="" id="signature-preview" />
<div class="btn btn-primary" onclick="activateNotarySignaturePad()"><i class="fas fa-signature"></i> Draw Signature</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
<button type="button" class="btn btn-primary" onclick="saveSettingsModal()">Save</button>
</div>
</div>
</div>
</div>
<div class="modal fade" id="signatureModal" tabindex="-1" aria-labelledby="signatureModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="signatureModalLabel">Signature Pad</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div class="btn btn-warning d-inline-block" onclick="resizeSignaturePadCanvas();signaturePad.clear();"><i class="fas fa-eraser"></i> Erase</div>
<div class="btn btn-primary d-inline-block" onclick="signaturePadUndo();"><i class="fas fa-undo"></i> Undo</div>
</div>
<div class="modal-body">
<div class="signature-wrapper">
<img src="img/signature-line.svg" />
<canvas id="signaturecanvas"></canvas>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-primary" data-bs-dismiss="modal" onclick="signaturePadCallback()">Apply</button>
</div>
</div>
</div>
</div>
<ul class="nav nav-pills">
<li class="nav-item">
<a class="nav-link" href="#" onclick="addPDF()"><i class="fas fa-file-pdf"></i> Add PDF</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#" onclick="savePDF()"><i class="fas fa-save"></i> Save</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#" onclick="openSettingsModal()"><i class="fas fa-cog"></i> Settings</a>
</li>
</ul>
<div class="card">
<div class="card-body">
<div class="btn btn-primary d-inline" onclick="pdfZoom('fitheight');"><i class="fas fa-arrows-alt-v"></i> Fit Height</div>
<div class="btn btn-primary d-inline" onclick="pdfZoom('fitwidth');"><i class="fas fa-arrows-alt-h"></i> Fit Width</div>
<div class="btn btn-primary d-inline" onclick="pdfZoom('out');"><i class="fas fa-search-minus"></i> Zoom Out</div>
<div class="btn btn-primary d-inline" onclick="pdfZoom('in');"><i class="fas fa-search-plus"></i> Zoom In</div>
<div class="btn btn-primary d-inline ms-4" onclick="activateStampDrawTool()"><i class="fas fa-stamp"></i> Stamp/Seal</div>
<div class="btn btn-primary d-inline" onclick="activateClientSignaturePad()"><i class="fas fa-file-signature"></i> Sign (Client)</div>
<div class="btn btn-primary d-inline" onclick="activateNotarySignatureTool()"><i class="fas fa-signature"></i> Sign (Notary)</div>
</div>
<div class="card-body" id="page-canvas-container">
<canvas id="placementguidebox" width="1000" height="1000"></canvas>
</div>
<div class="card-body">
Page count: <span id="page_count"></span>
</div>
</div>
<form style="display: none; height: 0; width: 0; margin: 0; padding: 0;">
<input type="file" id="open-file-input">
</form>
<div id="svgtrimbox"></div>
<script src="node_modules/jquery/dist/jquery.min.js"></script>
<script src="node_modules/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
<script src="node_modules/pdfjs-dist/build/pdf.min.js"></script>
<script src="node_modules/jspdf/dist/jspdf.umd.min.js"></script>
<script src="node_modules/signature_pad/dist/signature_pad.umd.min.js"></script>
<script src="js/svg-to-image.js"></script>
<script src="js/util.js"></script>
<script src="js/storage.js"></script>
<script src="js/filesystem.js"></script>
<script src="js/drawtools.js"></script>
<script src="js/pdf.js"></script>
<script src="js/main.js"></script>

@ -0,0 +1,164 @@
/*
* Copyright 2021 Netsyms Technologies.
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
var activeDrawImage;
var signaturePadCallback = function () {};
var clientSignatureSvg = "";
function getStampSvg(callback) {
$.get("templates/stamps/" + getStorage("notary_state") + ".svg", {}, function (data) {
data = data + "";
data = data.replaceAll("[[[NAME]]]", getStorage("notary_name"));
data = data.replaceAll("[[[LOCATION]]]", getStorage("notary_location"));
data = data.replaceAll("[[[EXPIRES]]]", getStorage("notary_expires"));
data = data.replaceAll("[[[IDNUMBER]]]", getStorage("notary_idnumber"));
callback(data);
}, "text");
}
function makeStampImage(callback) {
getStampSvg(function (data) {
svgToImage(data, function (err, image) {
if (err) {
callback(false);
return;
}
callback(image);
});
});
}
function activateStampDrawTool() {
makeStampImage(function (image) {
activeDrawImage = image;
enableGuideBox(image);
});
}
function activateNotarySignatureTool() {
if (!inStorage("notary_signature")) {
alert("Please set a notary signature in the settings.");
return;
}
svgToImage(getStorage("notary_signature"), function (err, image) {
if (err) {
callback(false);
return;
}
activeDrawImage = image;
enableGuideBox(image);
});
}
function activateClientSignaturePad() {
initSignaturePad();
signaturePadCallback = function () {
if (clientSignatureSvg != "" && signaturePad.isEmpty()) {
var signature = clientSignatureSvg;
} else {
var signature = signaturePad.toDataURL("image/svg+xml");
signature = signature.replace("data:image/svg+xml;base64,", "");
signature = atob(signature);
signature = trimAndShrinkSVG(signature);
clientSignatureSvg = signature;
}
svgToImage(signature, function (err, image) {
if (err) {
callback(false);
return;
}
activeDrawImage = image;
enableGuideBox(image);
});
};
}
function drawImageFromUrl(x, y, width, height, src, canvas) {
var ctx = canvas.getContext("2d");
const image = new Image();
image.src = src;
image.onload = () => {
ctx.drawImage(image, x, y, width, height);
}
}
function drawImage(x, y, width, height, image, canvas) {
var ctx = canvas.getContext("2d");
ctx.drawImage(image, x, y, width, height);
}
$("#page-canvas-container").on("click", ".page-canvas", function (evt) {
$("#page-canvas-container .page-canvas").removeClass("active");
$(this).addClass("active");
});
$("#page-canvas-container").on("click", ".page-canvas.active", function (evt) {
if (typeof activeDrawImage == "undefined") {
return;
}
var canvas = $(this)[0];
var coords = getMousePos(canvas, evt);
var imageWidth = (activeDrawImage.width / 96) * pdfAssumedDPI * pdfPageScale;
var imageHeight = (activeDrawImage.height / 96) * pdfAssumedDPI * pdfPageScale;
drawImage(coords.x, coords.y, imageWidth, imageHeight, activeDrawImage, canvas);
});
function enableGuideBox(image, scalecorrectionfactor) {
if (typeof scalecorrectionfactor == "undefined") {
scalecorrectionfactor = 1;
}
// disable first to clear contents
disableGuideBox();
$("#placementguidebox").css("display", "");
// calculate size of guide image
var pageWidthPx = $("#page-canvas-container .page-canvas")[0].getContext("2d").canvas.width;
var pageCanvasCurrentWidthPx = $("#page-canvas-container .page-canvas").css("width").replace("px", "") * 1;
var pageWidthInches = pageWidthPx / (pdfAssumedDPI * pdfPageScale);
var canvasCurrentDPI = pageCanvasCurrentWidthPx / pageWidthInches;
var imageWidth = (image.width / (96 * scalecorrectionfactor)) * canvasCurrentDPI;
var imageHeight = (image.height / (96 * scalecorrectionfactor)) * canvasCurrentDPI;
var canvas = $("#placementguidebox")[0];
var ctx = canvas.getContext("2d");
ctx.drawImage(image, 0, 0, imageWidth, imageHeight);
}
function disableGuideBox() {
$("#placementguidebox").css("display", "none");
var context = $("#placementguidebox")[0].getContext('2d');
context.clearRect(0, 0, $("#placementguidebox")[0].width, $("#placementguidebox")[0].height);
}
$("#page-canvas-container").on("mousemove", function (evt) {
$("#placementguidebox").css({
left: evt.pageX,
top: evt.pageY
});
});
/**
* https://stackoverflow.com/a/17130415
* @param {type} canvas
* @param {type} evt
* @returns {getMousePos.pdfAnonym$1}
*/
function getMousePos(canvas, evt) {
var rect = canvas.getBoundingClientRect(), // abs. size of element
scaleX = canvas.width / rect.width, // relationship bitmap vs. element for X
scaleY = canvas.height / rect.height; // relationship bitmap vs. element for Y
return {
x: (evt.clientX - rect.left) * scaleX, // scale mouse coordinates after they have
y: (evt.clientY - rect.top) * scaleY // been adjusted to be relative to element
}
}

@ -0,0 +1,46 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
function openFileDialog(callback) {
$("#open-file-input").off("change");
if (typeof callback != "undefined") {
$("#open-file-input").on("change", function () {
callback($("#open-file-input").val());
});
}
$("#open-file-input").click();
}
function getFileAsString(path) {
const fs = require("fs");
return fs.readFileSync(path, "utf8");
}
function getFileAsUint8Array(path) {
const fs = require("fs");
return fs.readFileSync(path, null);
}
function writeStringToFile(path, text) {
const fs = require("fs");
fs.writeFileSync(path, text);
}
function writeDataToFile(path, data) {
const fs = require("fs");
fs.writeFileSync(path, data);
}
function copyFile(source, dest) {
const fs = require("fs");
fs.copyFileSync(source, dest);
}
function getBasename(fullpath) {
var path = require("path");
return path.basename(fullpath);
}

@ -0,0 +1,124 @@
/*
* Copyright 2021 Netsyms Technologies.
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
var pdfjsLib = window['pdfjs-dist/build/pdf'];
pdfjsLib.GlobalWorkerOptions.workerSrc = 'node_modules/pdfjs-dist/build/pdf.worker.min.js';
var signaturePad;
function setupNotaryOptions(name, location, expires, idnumber, state) {
setStorage("notary_name", name);
setStorage("notary_location", location);
setStorage("notary_expires", expires);
setStorage("notary_idnumber", idnumber);
setStorage("notary_state", state);
}
function openSettingsModal() {
$("#settingsModal #notary_name").val(getStorage("notary_name"));
$("#settingsModal #notary_location").val(getStorage("notary_location"));
$("#settingsModal #notary_expires").val(getStorage("notary_expires"));
$("#settingsModal #notary_idnumber").val(getStorage("notary_idnumber"));
$("#settingsModal #notary_state").val(getStorage("notary_state"));
// show preview of stamp
if (inStorage("notary_state")) {
getStampSvg(function (svg) {
$("#settingsModal #stamp-preview").attr("src", "data:image/svg+xml;base64," + btoa(svg));
});
}
// show signature
if (inStorage("notary_signature")) {
$("#settingsModal #signature-preview").attr("src", "data:image/svg+xml;base64," + btoa(getStorage("notary_signature")));
}
new bootstrap.Modal(document.getElementById('settingsModal')).show();
}
function saveSettingsModal() {
setupNotaryOptions(
$("#settingsModal #notary_name").val(),
$("#settingsModal #notary_location").val(),
$("#settingsModal #notary_expires").val(),
$("#settingsModal #notary_idnumber").val(),
$("#settingsModal #notary_state option:selected").val());
// show preview of stamp
if (inStorage("notary_state")) {
getStampSvg(function (svg) {
$("#settingsModal #stamp-preview").attr("src", "data:image/svg+xml;base64," + btoa(svg));
});
}
// show signature
if (inStorage("notary_signature")) {
$("#settingsModal #signature-preview").attr("src", "data:image/svg+xml;base64," + btoa(getStorage("notary_signature")));
}
}
function initSignaturePad() {
var canvas = document.getElementById("signaturecanvas");
signaturePad = new SignaturePad(canvas, {
backgroundColor: 'rgba(255, 255, 255, 0.5)',
onBegin: function () {
// stop page from jumping around if user starts drawing signature while a text box is focused
$("input").blur();
}
});
new bootstrap.Modal(document.getElementById('signatureModal')).show();
$("#signatureModal").on("shown.bs.modal", resizeSignaturePadCanvas);
}
function resizeSignaturePadCanvas() {
var canvas = document.getElementById("signaturecanvas");
var ratio = Math.max(window.devicePixelRatio || 1, 1);
canvas.width = canvas.offsetWidth * ratio;
canvas.height = canvas.offsetHeight * ratio;
canvas.getContext("2d").scale(ratio, ratio);
if (signaturePad != null) {
signaturePad.clear(); // otherwise isEmpty() might return incorrect value
}
}
function signaturePadUndo() {
var data = signaturePad.toData();
resizeSignaturePadCanvas();
if (data) {
data.pop(); // remove the last dot or line
signaturePad.fromData(data);
}
}
function activateNotarySignaturePad() {
initSignaturePad();
signaturePadCallback = function () {
var signature = signaturePad.toDataURL("image/svg+xml");
signature = signature.replace("data:image/svg+xml;base64,", "");
signature = atob(signature);
setStorage("notary_signature", trimAndShrinkSVG(signature));
$("#settingsModal #signature-preview").attr("src", "data:image/svg+xml;base64," + btoa(getStorage("notary_signature")));
};
}
function trimAndShrinkSVG(svgstring) {
var div = document.getElementById('svgtrimbox');
div.innerHTML = svgstring;
var svg = div.firstChild;
var bbox = svg.getBBox();
var viewBox = [bbox.x, bbox.y, bbox.width, bbox.height].join(" ");
svg.setAttribute("viewBox", viewBox);
svg.setAttribute("width", 100);
svg.setAttribute("height", 50);
//console.log(svg.outerHTML);
div.innerHTML = "";
return svg.outerHTML;
}

@ -0,0 +1,122 @@
/*
* Copyright 2021 Netsyms Technologies.
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
const {jsPDF} = window.jspdf;
var pdfPageScale = 4;
var pdfAssumedDPI = 72;
var pdfDoc = null;
var pageNumber = 0;
function addPDF() {
openFileDialog(function (path) {
var filedata = getFileAsUint8Array(path);
/**
* Asynchronously downloads PDF.
*/
pdfjsLib.getDocument(filedata).promise.then(function (pdfDoc_) {
pdfDoc = pdfDoc_;
renderAllPages(pdfDoc);
pdfZoom("fitwidth");
// Initial/first page rendering
//renderPage(pageNum);
});
});
}
function savePDF() {
var canvases = $("#page-canvas-container .page-canvas");
const pdf = new jsPDF({
unit: "in",
compress: true
});
// creating a PDF creates a blank page that we don't want to use,
// as we haven't done the calculations yet
pdf.deletePage(1);
for (var i = 0; i < canvases.length; i++) {
var canvas = $("#page-canvas-container .page-canvas")[i];
var widthpx = canvas.getContext("2d").canvas.width;
var heightpx = canvas.getContext("2d").canvas.height;
var pageWidthInches = widthpx / (pdfAssumedDPI * pdfPageScale);
var pageHeightInches = heightpx / (pdfAssumedDPI * pdfPageScale);
console.log(pageWidthInches + " x " + pageHeightInches);
var pageFormat = [pageWidthInches, pageHeightInches];
var pageOrientation = (pageWidthInches > pageHeightInches ? "landscape" : "portrait");
pdf.addPage(pageFormat, pageOrientation);
pdf.addImage($("#page-canvas-container .page-canvas")[i].toDataURL(), 0, 0, pageWidthInches, pageHeightInches);
}
pdf.save("signed.pdf");
}
function pdfZoom(str) {
disableGuideBox();
var widthpx = $("#page-canvas-container .page-canvas").css("width").replace("px", "") * 1;
var zoomstep = 100;
console.log(widthpx);
switch (str) {
case "out":
$("#page-canvas-container .page-canvas").css("height", "auto");
widthpx -= zoomstep;
$("#page-canvas-container .page-canvas").css("width", widthpx + "px");
break;
case "in":
$("#page-canvas-container .page-canvas").css("height", "auto");
widthpx += zoomstep;
$("#page-canvas-container .page-canvas").css("width", widthpx + "px");
break;
case "fitwidth":
$("#page-canvas-container .page-canvas").css("width", "100%");
$("#page-canvas-container .page-canvas").css("height", "auto");
break;
case "fitheight":
$("#page-canvas-container .page-canvas").css("height", "100%");
$("#page-canvas-container .page-canvas").css("width", "auto");
break;
}
}
function getNewCanvas(pagenumber) {
var canvas = document.createElement('canvas');
canvas.id = "pdf-canvas-page-" + pagenumber;
canvas.className = "page-canvas";
return canvas;
}
function addPage() {
pageNumber++;
var canvas = getNewCanvas(pageNumber);
var prevPageCanvas = $("#page-canvas-container .page-canvas#pdf-canvas-page-" + (pageNumber - 1))[0];
canvas.width = prevPageCanvas.getContext("2d").canvas.width;
canvas.height = prevPageCanvas.getContext("2d").canvas.height;
$("#page-canvas-container").append(canvas);
}
function renderAllPages() {
var startingPageNumber = pageNumber;
var thisDocPageCount = pdfDoc.numPages;
for (var i = 1; i <= pdfDoc.numPages; i++) {
pdfDoc.getPage(i).then(function (page) {
var viewport = page.getViewport({scale: pdfPageScale});
var canvas = getNewCanvas(page.pageNumber + startingPageNumber);
canvas.height = viewport.height;
canvas.width = viewport.width;
$("#page-canvas-container").append(canvas);
// Render PDF page into canvas context
var renderContext = {
canvasContext: canvas.getContext("2d"),
viewport: viewport
};
page.render(renderContext);
});
}
pageNumber = pageNumber + thisDocPageCount;
document.getElementById('page_count').textContent = pageNumber;
}

@ -0,0 +1,51 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
/**
* Save something to persistent storage.
* @param {string} key
* @param {string} value non-string values are converted to strings.
* @returns {undefined}
*/
function setStorage(key, value) {
localStorage.setItem(key, value);
}
/**
* Get an item from persistent storage.
* @param {type} key
* @returns {DOMString}
*/
function getStorage(key) {
return localStorage.getItem(key);
}
/**
* Check if an item is in the persistent storage.
* @param {string} key
* @returns {Boolean}
*/
function inStorage(key) {
return localStorage.getItem(key) != null;
}
/**
* Get all item from persistent storage.
* @returns {Array} [{key: "", value: ""},...]
*/
function getAllStorage() {
var all = [];
for (var key in localStorage) {
if (localStorage.hasOwnProperty(key)) {
all.push({
key: key,
value: getStorage(key)
});
}
}
return all;
}

@ -0,0 +1,103 @@
/*
* https://github.com/Jam3/svg-to-image
* https://github.com/mattdesl/load-img
*/
function loadImage(src, opt, callback) {
if (typeof opt === 'function') {
callback = opt;
opt = null;
}
var el = document.createElement('img');
var locked;
el.onload = function onLoaded() {
if (locked)
return;
locked = true;
if (callback)
callback(undefined, el);
};
el.onerror = function onError() {
if (locked)
return;
locked = true;
if (callback)
callback(new Error('Unable to load "' + src + '"'), el);
};
if (opt && opt.crossOrigin) {
el.crossOrigin = opt.crossOrigin;
}
el.src = src;
return el;
}
function svgToImage(svg, opt, cb) {
if (typeof opt === 'function') {
cb = opt
opt = {}
}
cb = cb || noop
opt = opt || {}
if (typeof window === 'undefined') {
return bail('window global is undefined; not in a browser')
}
var DOMURL = getURL()
if (!DOMURL ||
typeof DOMURL.createObjectURL !== 'function' ||
typeof DOMURL.revokeObjectURL !== 'function') {
return bail('browser does not support URL.createObjectURL')
}
if (typeof window.Blob === 'undefined') {
return bail('browser does not support Blob constructor')
}
if (!Array.isArray(svg)) {
svg = [svg]
}
var blob
try {
blob = new window.Blob(svg, {
type: 'image/svg+xml;charset=utf-8'
})
} catch (e) {
return bail(e)
}
var url = DOMURL.createObjectURL(blob)
loadImage(url, opt, function (err, img) {
DOMURL.revokeObjectURL(url)
if (err) {
// try again for Safari 8.0, using simple encodeURIComponent
// this will fail with DOM content but at least it works with SVG
var url2 = 'data:image/svg+xml,' + encodeURIComponent(svg.join(''))
return loadImage(url2, opt, cb)
}
cb(err, img)
})
function getURL() {
return window.URL ||
window.webkitURL ||
window.mozURL ||
window.msURL
}
function bail(msg) {
process.nextTick(function () {
cb(new Error(msg))
})
}
}

@ -0,0 +1,275 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
/**
* Generate a UUID.
* From https://stackoverflow.com/a/2117523
* @returns {String}
*/
function uuidv4() {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
}
/**
* Take a UNIX timestamp (seconds since Jan 1 1970) and format it.
* (Mostly) compatible with PHP's date() function.
* @param {String} date format string, see https://www.php.net/manual/en/function.date.php
* @param {Integer} timestamp UNIX timestamp
* @return {String}
*/
function formatTimestamp(format, timestamp) {
if (typeof timestamp == "undefined") {
timestamp = time();
}
var date = new Date(timestamp * 1000);
var out = "";
var months = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];
var days = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
for (var i = 0; i < format.length; i++) {
var c = format.charAt(i);
// Handle backslash-escaped characters
if (c == "\\" && i < format.length - 1) {
out += format.charAt(i + 1);
i++;
continue;
}
switch (c) {
case "d":
var d = date.getDate();
if (d < 10) {
out += "0";
}
out += d;
break;
case "D":
out += days[date.getDay()].substring(0, 3);
break;
case "j":
out += date.getDate();
break;
case "l":
out += days[date.getDay()];
break;
case "N":
// TODO
break;
case "S":
// TODO
break;
case "w":
out += date.getDay();
break;
case "z":
// TODO
break;
case "W":
// TODO
break;
case "F":
out += months[date.getMonth()];
break;
case "m":
var m = date.getMonth() + 1;
if (m < 10) {
out += "0";
}
out += m;
break;
case "M":
out += months[date.getMonth()].substring(0, 3);
break;
case "n":
out += date.getMonth() + 1;
break;
case "t":
// TODO
break;
case "L":
// TODO
break;
case "o":
// TODO
break;
case "Y":
out += date.getFullYear();
break;
case "y":
var y = (date.getFullYear() + "");
out += y.substring(y.length - 2);
break;
case "a":
if (date.getHours() < 12) {
out += "am";
} else {
out += "pm";
}
break;
case "A":
if (date.getHours() < 12) {
out += "AM";
} else {
out += "PM";
}
break;
case "B":
// TODO
break;
case "g":
var h = date.getHours() % 12;
if (h == 0) {
h = 12;
}
out += h;
break;
case "G":
out += date.getHours();
break;
case "h":
var h = date.getHours() % 12;
if (h == 0) {
h = 12;
}
if (h < 10) {
out += "0";
}
out += h;
break;
case "H":
var h = date.getHours();
if (h < 10) {
out += "0";
}
out += h;
break;
case "i":
var ii = date.getMinutes();
if (ii < 10) {
out += "0";
}
out += ii;
break;
case "s":
var s = date.getSeconds();
if (s < 10) {
out += "0";
}
out += s;
break;
case "u":
out += date.getMilliseconds() * 1000;
break;
case "v":
out += date.getMilliseconds();
break;
case "e":
// TODO
break;
case "I":
// TODO
break;
case "O":
var off = date.getTimezoneOffset();
var m = off % 60;
var h = (off - m) / 60;
if (off >= 0) {
out += "+";
} else {
out += "-";
}
if (h < 10) {
out += "0";
}
out += h;
if (m < 10) {
out += "0";
}
out += m;
break;
case "P":
var off = date.getTimezoneOffset();
var m = off % 60;
var h = (off - m) / 60;
if (off >= 0) {
out += "+";
} else {
out += "-";
}
if (h < 10) {
out += "0";
}
out += h;
out += ":";
if (m < 10) {
out += "0";
}
out += m;
break;
case "T":
// TODO
break;
case "Z":
out += date.getTimezoneOffset() * 60;
break;
case "c":
out += formatTimestamp(timestamp, "Y-m-d\\TH:i:sP");
break;
case "r":
out += formatTimestamp(timestamp, "D, j M Y G:i:s O");
break;
case "U":
out += Math.round(timestamp);
break;
default:
out += c;
}
}
return out;
}
function timestampToDateTimeString(timestamp) {
return timestampToDateString(timestamp) + " " + timestampToTimeString(timestamp);
}
function timestampToDateString(timestamp) {
var date = new Date(timestamp * 1000);
return date.toLocaleDateString();
}
function timestampToTimeString(timestamp) {
var date = new Date(timestamp * 1000);
var pm = date.getHours() >= 12;
var hours = date.getHours() > 12 ? date.getHours() - 12 : date.getHours();
hours = (hours == 0 ? 12 : hours);
var minutes = date.getMinutes();
var time = hours + ":" + (minutes < 10 ? "0" + minutes : minutes) + " " + (pm ? "PM" : "AM");
return time;
}
/**
* Get the current UNIX timestamp in seconds.
* @returns {Number}
*/
function time() {
return Date.now() / 1000;
}
/**
* Get the number of seconds between now and the given timestamp.
* @param {Number} compareto
* @returns {Number}
*/
function timeDiff(compareto) {
return time() - compareto;
}

@ -0,0 +1,6 @@
file.reference.IPENtool-public_html=public_html
file.reference.IPENtool-test=test
file.reference.Sources-IPENtool=.
files.encoding=UTF-8
project.license=mpl
site.root.folder=${file.reference.Sources-IPENtool}

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://www.netbeans.org/ns/project/1">
<type>org.netbeans.modules.web.clientproject</type>
<configuration>
<data xmlns="http://www.netbeans.org/ns/clientside-project/1">
<name>IPENtool</name>
</data>
</configuration>
</project>

228
package-lock.json generated

@ -0,0 +1,228 @@
{
"name": "IPENtool",
"version": "1.0.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"@babel/runtime-corejs3": {
"version": "7.14.7",
"resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.14.7.tgz",
"integrity": "sha512-Wvzcw4mBYbTagyBVZpAJWI06auSIj033T/yNE0Zn1xcup83MieCddZA7ls3kme17L4NOGBrQ09Q+nKB41RLWBA==",
"optional": true,
"requires": {
"core-js-pure": "^3.15.0",
"regenerator-runtime": "^0.13.4"
}
},
"@fortawesome/fontawesome-free": {
"version": "5.15.3",
"resolved": "https://npm.fontawesome.com/@fortawesome/fontawesome-free/-/5.15.3/fontawesome-free-5.15.3.tgz",
"integrity": "sha512-rFnSUN/QOtnOAgqFRooTA3H57JLDm0QEG/jPdk+tLQNL/eWd+Aok8g3qCI+Q1xuDPWpGW/i9JySpJVsq8Q0s9w=="
},
"@pdf-lib/standard-fonts": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@pdf-lib/standard-fonts/-/standard-fonts-1.0.0.tgz",
"integrity": "sha512-hU30BK9IUN/su0Mn9VdlVKsWBS6GyhVfqjwl1FjZN4TxP6cCw0jP2w7V3Hf5uX7M0AZJ16vey9yE0ny7Sa59ZA==",
"requires": {
"pako": "^1.0.6"
}
},
"@pdf-lib/upng": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@pdf-lib/upng/-/upng-1.0.1.tgz",
"integrity": "sha512-dQK2FUMQtowVP00mtIksrlZhdFXQZPC+taih1q4CvPZ5vqdxR/LKBaFg0oAfzd1GlHZXXSPdQfzQnt+ViGvEIQ==",
"requires": {
"pako": "^1.0.10"
}
},
"@types/raf": {
"version": "3.4.0",
"resolved": "https://registry.npmjs.org/@types/raf/-/raf-3.4.0.tgz",
"integrity": "sha512-taW5/WYqo36N7V39oYyHP9Ipfd5pNFvGTIQsNGj86xV88YQ7GnI30/yMfKDF7Zgin0m3e+ikX88FvImnK4RjGw==",
"optional": true
},
"atob": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz",
"integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg=="
},
"base64-arraybuffer": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.2.0.tgz",
"integrity": "sha512-7emyCsu1/xiBXgQZrscw/8KPRT44I4Yq9Pe6EGs3aPRTsWuggML1/1DTuZUuIaJPIm1FTDUVXl4x/yW8s0kQDQ==",
"optional": true
},
"bootstrap": {
"version": "5.0.2",
"resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.0.2.tgz",
"integrity": "sha512-1Ge963tyEQWJJ+8qtXFU6wgmAVj9gweEjibUdbmcCEYsn38tVwRk8107rk2vzt6cfQcRr3SlZ8aQBqaD8aqf+Q=="
},
"btoa": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/btoa/-/btoa-1.2.1.tgz",
"integrity": "sha512-SB4/MIGlsiVkMcHmT+pSmIPoNDoHg+7cMzmt3Uxt628MTz2487DKSqK/fuhFBrkuqrYv5UCEnACpF4dTFNKc/g=="
},
"canvg": {
"version": "3.0.7",
"resolved": "https://registry.npmjs.org/canvg/-/canvg-3.0.7.tgz",
"integrity": "sha512-4sq6iL5Q4VOXS3PL1BapiXIZItpxYyANVzsAKpTPS5oq4u3SKbGfUcbZh2gdLCQ3jWpG/y5wRkMlBBAJhXeiZA==",
"optional": true,
"requires": {
"@babel/runtime-corejs3": "^7.9.6",
"@types/raf": "^3.4.0",
"raf": "^3.4.1",
"rgbcolor": "^1.0.1",
"stackblur-canvas": "^2.0.0",
"svg-pathdata": "^5.0.5"
}
},
"core-js": {
"version": "3.15.2",
"resolved": "https://registry.npmjs.org/core-js/-/core-js-3.15.2.tgz",
"integrity": "sha512-tKs41J7NJVuaya8DxIOCnl8QuPHx5/ZVbFo1oKgVl1qHFBBrDctzQGtuLjPpRdNTWmKPH6oEvgN/MUID+l485Q==",
"optional": true
},
"core-js-pure": {
"version": "3.15.2",
"resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.15.2.tgz",
"integrity": "sha512-D42L7RYh1J2grW8ttxoY1+17Y4wXZeKe7uyplAI3FkNQyI5OgBIAjUfFiTPfL1rs0qLpxaabITNbjKl1Sp82tA==",
"optional": true
},
"css-line-break": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/css-line-break/-/css-line-break-1.1.1.tgz",
"integrity": "sha512-1feNVaM4Fyzdj4mKPIQNL2n70MmuYzAXZ1aytlROFX1JsOo070OsugwGjj7nl6jnDJWHDM8zRZswkmeYVWZJQA==",
"optional": true,
"requires": {
"base64-arraybuffer": "^0.2.0"
}
},
"dompurify": {
"version": "2.2.9",
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.2.9.tgz",
"integrity": "sha512-+9MqacuigMIZ+1+EwoEltogyWGFTJZWU3258Rupxs+2CGs4H914G9er6pZbsme/bvb5L67o2rade9n21e4RW/w==",
"optional": true
},
"fflate": {
"version": "0.4.8",
"resolved": "https://registry.npmjs.org/fflate/-/fflate-0.4.8.tgz",
"integrity": "sha512-FJqqoDBR00Mdj9ppamLa/Y7vxm+PRmNWA67N846RvsoYVMKB4q3y/de5PA7gUmRMYK/8CMz2GDZQmCRN1wBcWA=="
},
"html2canvas": {
"version": "1.0.0-rc.7",
"resolved": "https://registry.npmjs.org/html2canvas/-/html2canvas-1.0.0-rc.7.tgz",
"integrity": "sha512-yvPNZGejB2KOyKleZspjK/NruXVQuowu8NnV2HYG7gW7ytzl+umffbtUI62v2dCHQLDdsK6HIDtyJZ0W3neerA==",
"optional": true,
"requires": {
"css-line-break": "1.1.1"
}
},
"jquery": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/jquery/-/jquery-3.6.0.tgz",
"integrity": "sha512-JVzAR/AjBvVt2BmYhxRCSYysDsPcssdmTFnzyLEts9qNwmjmu4JTAMYubEfwVOSwpQ1I1sKKFcxhZCI2buerfw=="
},
"jspdf": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/jspdf/-/jspdf-2.3.1.tgz",
"integrity": "sha512-1vp0USP1mQi1h7NKpwxjFgQkJ5ncZvtH858aLpycUc/M+r/RpWJT8PixAU7Cw/3fPd4fpC8eB/Bj42LnsR21YQ==",
"requires": {
"atob": "^2.1.2",
"btoa": "^1.2.1",
"canvg": "^3.0.6",
"core-js": "^3.6.0",
"dompurify": "^2.2.0",
"fflate": "^0.4.8",
"html2canvas": "^1.0.0-rc.5"
}
},
"konva": {
"version": "8.1.0",
"resolved": "https://registry.npmjs.org/konva/-/konva-8.1.0.tgz",
"integrity": "sha512-HkS5jB4oZlj+koSBmKWWWBOoaivnKDsEAZUkk+6xlOT+ryj5HFXkTfBZZKBS+IA0WRz3vDpko0y3LFAzuc86kA=="
},
"load-img": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/load-img/-/load-img-1.0.0.tgz",
"integrity": "sha1-CVN0SYk8MqhwkHRkVWbExfqprCY="
},
"pako": {
"version": "1.0.11",
"resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz",
"integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw=="
},
"pdf-lib": {
"version": "1.16.0",
"resolved": "https://registry.npmjs.org/pdf-lib/-/pdf-lib-1.16.0.tgz",
"integrity": "sha512-P/1SSmElOBKrPlbc+Sn7UxikRQbzVA64+4Dh6/uczPscvq/NatP9eryoOguyBTpTnzICNiG8EnMH4Ziqp2TnFA==",
"requires": {
"@pdf-lib/standard-fonts": "^1.0.0",
"@pdf-lib/upng": "^1.0.1",
"pako": "^1.0.11",
"tslib": "^1.11.1"
}
},
"pdfjs-dist": {
"version": "2.8.335",
"resolved": "https://registry.npmjs.org/pdfjs-dist/-/pdfjs-dist-2.8.335.tgz",
"integrity": "sha512-2IKw7wP1RnzzWJcpkeZwF+cKROFiQext+/WburB6cgKwt9zc8rOyDH7a3FepdcciSGs8SDs/AuWe8qVx+iI6pw=="
},
"performance-now": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
"integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=",
"optional": true
},
"raf": {
"version": "3.4.1",
"resolved": "https://registry.npmjs.org/raf/-/raf-3.4.1.tgz",
"integrity": "sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==",
"optional": true,
"requires": {
"performance-now": "^2.1.0"
}
},
"regenerator-runtime": {
"version": "0.13.7",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz",
"integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==",
"optional": true
},
"rgbcolor": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/rgbcolor/-/rgbcolor-1.0.1.tgz",
"integrity": "sha1-1lBezbMEplldom+ktDMHMGd1lF0=",
"optional": true
},
"signature_pad": {
"version": "3.0.0-beta.4",
"resolved": "https://registry.npmjs.org/signature_pad/-/signature_pad-3.0.0-beta.4.tgz",
"integrity": "sha512-cOf2NhVuTiuNqe2X/ycEmizvCDXk0DoemhsEpnkcGnA4kS5iJYTCqZ9As7tFBbsch45Q1EdX61833+6sjJ8rrw=="
},
"stackblur-canvas": {
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/stackblur-canvas/-/stackblur-canvas-2.5.0.tgz",
"integrity": "sha512-EeNzTVfj+1In7aSLPKDD03F/ly4RxEuF/EX0YcOG0cKoPXs+SLZxDawQbexQDBzwROs4VKLWTOaZQlZkGBFEIQ==",
"optional": true
},
"svg-pathdata": {
"version": "5.0.5",
"resolved": "https://registry.npmjs.org/svg-pathdata/-/svg-pathdata-5.0.5.tgz",
"integrity": "sha512-TAAvLNSE3fEhyl/Da19JWfMAdhSXTYeviXsLSoDT1UM76ADj5ndwAPX1FKQEgB/gFMPavOy6tOqfalXKUiXrow==",
"optional": true
},
"svg-to-image": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/svg-to-image/-/svg-to-image-1.1.3.tgz",
"integrity": "sha1-1v9NiDyo9+P3krQrIyixXL4vsPM=",
"requires": {
"load-img": "^1.0.0"
}
},
"tslib": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
}
}
}

@ -0,0 +1,25 @@
{
"name": "IPENtool",
"main": "index.html",
"version": "1.0.0",
"keywords": [
"util",
"functional",
"server",
"client",
"browser"
],
"author": "Skylar Ittner",
"contributors": [],
"dependencies": {
"@fortawesome/fontawesome-free": "^5.15.3",
"bootstrap": "^5.0.2",
"jquery": "^3.6.0",
"jspdf": "^2.3.1",
"konva": "^8.1.0",
"pdf-lib": "^1.16.0",
"pdfjs-dist": "^2.8.335",
"signature_pad": "^3.0.0-beta.4",
"svg-to-image": "^1.1.3"
}
}

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="2.5in" height="1in" version="1.1" viewBox="0 0 63.5 25.4" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><g transform="translate(0 -271.6)"><rect x=".24462" y="271.84" width="63.011" height="24.911" fill="none" stroke="#000" stroke-width=".48924"/><text x="44.025459" y="275.92871" fill="#000000" font-family="'Ubuntu Condensed'" font-size="3.5278px" letter-spacing=".052917px" stroke-width=".26458px" text-align="center" text-anchor="middle" word-spacing="0px" style="line-height:89.99999762%" xml:space="preserve"><tspan x="44.051918" y="275.92871" text-align="center" style="line-height:89.99999762%">[[[NAME]]]</tspan><tspan x="44.051918" y="279.1037" text-align="center" style="line-height:89.99999762%">Notary Public for the</tspan><tspan x="44.051918" y="282.27872" text-align="center" style="line-height:89.99999762%">State of Montana</tspan><tspan x="44.051918" y="285.4537" text-align="center" style="line-height:89.99999762%">Residing at</tspan><tspan x="44.051918" y="288.62872" text-align="center" style="line-height:89.99999762%">[[[LOCATION]]]</tspan><tspan x="44.051918" y="291.80371" text-align="center" style="line-height:89.99999762%">My Commission Expires</tspan><tspan x="44.051918" y="294.9787" text-align="center" style="line-height:89.99999762%">[[[EXPIRES]]]</tspan></text><g><g transform="translate(1.2654e-7 -.016449)" fill="#000000" font-family="Ubuntu" letter-spacing=".052917px" stroke-width=".26458px" text-anchor="middle" word-spacing="0px"><text x="13.02184" y="287.62936" font-size="3.5278px" text-align="center" style="line-height:100%" xml:space="preserve"><tspan x="13.048299" y="287.62936" font-size="3.5278px" letter-spacing=".052917px" stroke-width=".26458px" text-align="center" text-anchor="middle">SEAL</tspan></text><text x="12.974876" y="282.86688" font-size="2.4694px" text-align="center" style="line-height:100%" xml:space="preserve"><tspan x="13.001334" y="282.86688" font-size="2.4694px" letter-spacing=".052917px" stroke-width=".26458px" text-align="center" text-anchor="middle">Notarial</tspan></text></g><circle cx="13.051" cy="284.3" r="7.362" fill="none" stroke="#000" stroke-dasharray="0.254,0.254" stroke-width=".254"/></g><g fill="none" stroke="#000"><circle cx="13.051" cy="284.3" r="11.139" stroke-width=".254"/><circle cx="13.051" cy="284.3" r="11.301" stroke-dasharray="0.762,0.381" stroke-width=".381"/></g><path id="b" d="m4.3999 289.29a9.9893 9.9893 0 0 0 8.651 4.9947 9.9893 9.9893 0 0 0 8.651-4.9947" fill="none"/><text transform="rotate(-1.5305 13.012 288.47)" fill="#000000" font-family="'Ubuntu Condensed'" font-size="2.8222px" letter-spacing=".13229px" stroke-width=".26458px" text-align="center" text-anchor="middle" word-spacing="0px" style="line-height:100%" xml:space="preserve"><textPath startOffset="50%" xlink:href="#b"><tspan font-family="'Ubuntu Condensed'" font-size="2.8222px" letter-spacing=".13229px" stroke-width=".26458px" text-align="center" text-anchor="middle">State of Montana</tspan></textPath></text><text transform="rotate(1.0275 -16.556 287.48)" x="0.28497145" fill="#000000" font-family="'Ubuntu Condensed'" font-size="2.8222px" letter-spacing=".13229px" stroke-width=".26458px" text-align="center" text-anchor="middle" word-spacing="0px" style="line-height:100%" xml:space="preserve"><textPath startOffset="50%" xlink:href="#a"><tspan font-family="'Ubuntu Condensed'" font-size="2.8222px" letter-spacing=".13229px" stroke-width=".26458px" text-align="center" text-anchor="middle">[[[NAME]]]</tspan></textPath></text><path id="a" d="m4.9972 280.6a9.2996 10.003 0 0 1 8.0537-5.0017 9.2996 10.003 0 0 1 8.0537 5.0017" fill="none"/><g transform="translate(.064683 .27)" stroke="#000" stroke-width=".254"><path transform="matrix(.60047 -.042277 .042277 .60047 -3.9596 113.82)" d="m-5.6196 282.72-0.77787 0.6826 0.12132 1.0278-0.88957-0.52886-0.93997 0.43298 0.22809-1.0094-0.70226-0.76017 1.0305-0.095 0.50595-0.90279 0.40882 0.95073z"/><path transform="matrix(.60047 -.042277 .042277 .60047 14.561 113.82)" d="m-5.6196 282.72-0.77787 0.6826 0.12132 1.0278-0.88957-0.52886-0.93997 0.43298 0.22809-1.0094-0.70226-0.76017 1.0305-0.095 0.50595-0.90279 0.40882 0.95073z"/></g></g></svg>

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.
Loading…
Cancel
Save