sign with text init

This commit is contained in:
Anthony Stirling 2023-05-06 00:48:56 +01:00
parent b75360bdb1
commit 3c47f21337
3 changed files with 110 additions and 6 deletions

View file

@ -108,6 +108,11 @@ home.PDFToXML.desc=Convert PDF to XML format
home.ScannerImageSplit.title=Detect/Split Scanned photos home.ScannerImageSplit.title=Detect/Split Scanned photos
home.ScannerImageSplit.desc=Splits multiple photos from within a photo/PDF home.ScannerImageSplit.desc=Splits multiple photos from within a photo/PDF
home.sign.title=Sign
home.sign.desc=Adds signature to PDF by drawing, text or image
home.flatten.title=Flatten
home.flatten.desc=Remove all interactive elements and forms from a PDF
ScannerImageSplit.selectText.1=Angle Threshold: ScannerImageSplit.selectText.1=Angle Threshold:

View file

@ -0,0 +1,41 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="16"
height="16"
fill="currentColor"
class="bi bi-ui-checks"
viewBox="0 0 16 16"
version="1.1"
id="svg826"
sodipodi:docname="no-checklist-black.svg"
inkscape:version="1.1.1 (3bf5ae0d25, 2021-09-20)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs830" />
<sodipodi:namedview
id="namedview828"
pagecolor="#ffffff"
bordercolor="#999999"
borderopacity="1"
inkscape:pageshadow="0"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="0"
showgrid="false"
inkscape:zoom="55.8125"
inkscape:cx="12.452408"
inkscape:cy="11.950728"
inkscape:current-layer="svg826" />
<path
d="M 7,2.5 C 7,2.2238576 7.2238576,2 7.5,2 h 7 C 14.776142,2 15,2.2238576 15,2.5 v 1 C 15,3.7761424 14.776142,4 14.5,4 h -7 C 7.2238576,4 7,3.7761424 7,3.5 Z M 2,1 C 0.8954305,1 0,1.8954305 0,3 V 5 C 0,6.1045695 0.8954305,7 2,7 H 4 C 5.1045695,7 6,6.1045695 6,5 V 3 C 6,1.8954305 5.1045695,1 4,1 Z M 2,9 C 0.8954305,9 0,9.8954305 0,11 v 2 c 0,1.104569 0.8954305,2 2,2 h 2 c 1.1045695,0 2,-0.895431 2,-2 V 11 C 6,9.8954305 5.1045695,9 4,9 Z M 2.854,5.354 c -0.1953639,0.1958584 -0.5126361,0.1958584 -0.708,0 l -1,-1 C 0.6740002,3.8820002 1.3820002,3.1740002 1.854,3.646 L 2.5,4.293 4.146,2.646 C 4.6179998,2.1740002 5.3259998,2.8820002 4.854,3.354 Z m 0,8 c -0.1953639,0.195858 -0.5126361,0.195858 -0.708,0 l -1,-1 C 0.67400022,11.882 1.3820002,11.174 1.854,11.646 L 2.5,12.293 4.146,10.646 c 0.4719998,-0.472 1.1799998,0.236 0.708,0.708 z M 7,10.5 C 7,10.223858 7.2238576,10 7.5,10 h 7 c 0.276142,0 0.5,0.223858 0.5,0.5 v 1 c 0,0.276142 -0.223858,0.5 -0.5,0.5 h -7 C 7.2238576,12 7,11.776142 7,11.5 Z m 0,-5 C 7,5.2238576 7.2238576,5 7.5,5 h 5 c 0.666666,0 0.666666,1 0,1 h -5 C 7.2238576,6 7,5.7761424 7,5.5 Z m 0,8 C 7,13.223858 7.2238576,13 7.5,13 h 5 c 0.666666,0 0.666666,1 0,1 h -5 C 7.2238576,14 7,13.776142 7,13.5 Z"
id="path824"
sodipodi:nodetypes="sssssssssssssssssssssssssssccscccscccscccscsssssssssssssssssssss"
style="fill:#000000" />
<path
d="m 0.14896862,0.16984545 c 0.1879493,-0.2023109 0.51141627,-0.22131574 0.7066285,-0.026004 L 15.795251,15.091111 c 0.471284,0.471524 -0.209339,1.204159 -0.680625,0.732632 L 0.17497212,0.87647378 C -0.02024026,0.68116189 -0.03898081,0.37215613 0.14896862,0.16984545 Z"
id="path937"
sodipodi:nodetypes="ssssss"
style="fill:#000000" />
</svg>

After

Width:  |  Height:  |  Size: 2.6 KiB

View file

@ -10,6 +10,11 @@
<script src="https://unpkg.com/pdf-lib@1.18.0/dist/umd/pdf-lib.min.js"></script> <script src="https://unpkg.com/pdf-lib@1.18.0/dist/umd/pdf-lib.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/signature_pad@4.1.5/dist/signature_pad.umd.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/signature_pad@4.1.5/dist/signature_pad.umd.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/interact.js/1.10.11/interact.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/interact.js/1.10.11/interact.min.js"></script>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Estonia&family=Tangerine&family=Windsong&display=swap" rel="stylesheet">
<style> <style>
#pdf-container { #pdf-container {
position: relative; position: relative;
@ -59,9 +64,11 @@
<div class = "btn-group"> <div class = "btn-group">
<input type="radio" class="btn-check" name="signature-type" id="draw-signature" autocomplete="off" checked> <input type="radio" class="btn-check" name="signature-type" id="draw-signature" autocomplete="off" checked>
<label class="btn btn-outline-secondary" for="draw-signature">Draw signature</label> <label class="btn btn-outline-secondary" for="draw-signature">Draw</label>
<input type="radio" class="btn-check" name="signature-type" id="import-image" autocomplete="off"> <input type="radio" class="btn-check" name="signature-type" id="import-image" autocomplete="off">
<label class="btn btn-outline-secondary" for="import-image">Import image</label> <label class="btn btn-outline-secondary" for="import-image">Import</label>
<input type="radio" class="btn-check" name="signature-type" id="type-signature" autocomplete="off">
<label class="btn btn-outline-secondary" for="type-signature">Type</label>
</div> </div>
<div th:replace="~{fragments/common :: fileSelector(name='signature-upload', multiple=false, accept='image/*', inputText=#{imgPrompt})}"></div> <div th:replace="~{fragments/common :: fileSelector(name='signature-upload', multiple=false, accept='image/*', inputText=#{imgPrompt})}"></div>
@ -73,6 +80,18 @@
<button id="save-signature" class="btn btn-outline-success mt-2">Save</button> <button id="save-signature" class="btn btn-outline-success mt-2">Save</button>
</div> </div>
<div id="signature-type-container">
<label class="form-check-label" for="sigText">Signature</label>
<input type="text" class="form-control" id="sigText" name="sigText">
<label>Font:</label>
<select class="form-control" name="font" id="font-select">
<option value="Estonia" class="estonia-font">Estonia</option>
<option value="Tangerine" class="tangerine-font">Tangerine</option>
<option value="Windsong" class="windsong-font">Windsong</option>
</select>
<button id="save-text-signature" class="btn btn-outline-success mt-2">Save</button>
</div>
<div id="pdf-container"> <div id="pdf-container">
<canvas id="pdf-canvas"></canvas> <canvas id="pdf-canvas"></canvas>
<canvas id="signature-canvas" hidden style="position: absolute;" data-scale="1"></canvas> <canvas id="signature-canvas" hidden style="position: absolute;" data-scale="1"></canvas>
@ -87,6 +106,15 @@
</div> </div>
<script> <script>
document.addEventListener('DOMContentLoaded', () => { document.addEventListener('DOMContentLoaded', () => {
const fontSelect = document.getElementById('font-select');
fontSelect.addEventListener('change', (event) => {
const selectedFont = event.target.value;
fontSelect.style.fontFamily = selectedFont;
});
fontSelect.style.fontFamily = fontSelect.value; // Set the initial font family
const pdfUpload = document.querySelector('input[name=pdf-upload]'); const pdfUpload = document.querySelector('input[name=pdf-upload]');
const signatureUpload = document.querySelector('input[name=signature-upload]'); const signatureUpload = document.querySelector('input[name=signature-upload]');
const pdfCanvas = document.getElementById('pdf-canvas'); const pdfCanvas = document.getElementById('pdf-canvas');
@ -96,9 +124,9 @@
const signaturePadCanvas = document.getElementById('signature-pad-canvas'); const signaturePadCanvas = document.getElementById('signature-pad-canvas');
const clearSignatureBtn = document.getElementById('clear-signature'); const clearSignatureBtn = document.getElementById('clear-signature');
const saveSignatureBtn = document.getElementById('save-signature'); const saveSignatureBtn = document.getElementById('save-signature');
const signatureTypeContainer = document.getElementById('signature-type-container');
document.querySelector('input[name=signature-upload]').closest(".custom-file-chooser").style.display = "none" document.querySelector('input[name=signature-upload]').closest(".custom-file-chooser").style.display = "none"
signatureTypeContainer.style.display = 'none';
const signaturePad = new SignaturePad(signaturePadCanvas, { const signaturePad = new SignaturePad(signaturePadCanvas, {
minWidth: 1, minWidth: 1,
maxWidth: 2, maxWidth: 2,
@ -126,15 +154,44 @@
$("input[name=signature-type]").change(function() { $("input[name=signature-type]").change(function() {
const drawSignatureInput = document.getElementById('draw-signature'); const drawSignatureInput = document.getElementById('draw-signature');
const importImageInput = document.getElementById('import-image');
const typeSignatureInput = document.getElementById('type-signature');
signaturePadContainer.style.display = drawSignatureInput.checked ? 'block' : 'none'; signaturePadContainer.style.display = drawSignatureInput.checked ? 'block' : 'none';
document.querySelector('input[name=signature-upload]').closest(".custom-file-chooser").style.display = drawSignatureInput.checked ? 'none' : 'block'; signatureTypeContainer.style.display = typeSignatureInput.checked ? 'block' : 'none';
document.querySelector('input[name=signature-upload]').closest(".custom-file-chooser").style.display = importImageInput.checked ? 'block' : 'none';
if (drawSignatureInput.checked) { if (drawSignatureInput.checked) {
populateSignatureFromPad(); populateSignatureFromPad();
} else { } else if (importImageInput.checked) {
populateSignatureFromFileUpload(); populateSignatureFromFileUpload();
} }
}); });
const saveTextSignatureBtn = document.getElementById('save-text-signature');
saveTextSignatureBtn.addEventListener('click', () => {
if (!document.getElementById('type-signature').checked) return;
const sigText = document.getElementById('sigText').value;
const font = document.querySelector('select[name=font]').value;
const fontSize = 50;
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
ctx.font = `${fontSize}px ${font}`;
const textWidth = ctx.measureText(sigText).width;
const textHeight = fontSize;
canvas.width = textWidth;
canvas.height = textHeight*1.35; //for tails
ctx.font = `${fontSize}px ${font}`;
ctx.fillText(sigText, 0, fontSize);
const dataURL = canvas.toDataURL();
populateSignature(dataURL);
});
function populateSignature(imgUrl) { function populateSignature(imgUrl) {
const img = new Image(); const img = new Image();
@ -178,6 +235,7 @@
reader.readAsDataURL(file); reader.readAsDataURL(file);
} }
function populateSignatureFromPad() { function populateSignatureFromPad() {
if (!document.getElementById('draw-signature').checked) return;
if (signaturePad.isEmpty()) return; if (signaturePad.isEmpty()) return;
const dataURL = signaturePad.toDataURL(); const dataURL = signaturePad.toDataURL();