mirror of
https://github.com/Stirling-Tools/Stirling-PDF.git
synced 2024-11-04 15:00:14 +01:00
sign with text init
This commit is contained in:
parent
b75360bdb1
commit
3c47f21337
@ -108,6 +108,11 @@ home.PDFToXML.desc=Convert PDF to XML format
|
||||
home.ScannerImageSplit.title=Detect/Split Scanned photos
|
||||
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:
|
||||
|
41
src/main/resources/static/images/flatten.svg
Normal file
41
src/main/resources/static/images/flatten.svg
Normal 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 |
@ -10,6 +10,11 @@
|
||||
<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://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>
|
||||
#pdf-container {
|
||||
position: relative;
|
||||
@ -59,9 +64,11 @@
|
||||
|
||||
<div class = "btn-group">
|
||||
<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">
|
||||
<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 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>
|
||||
</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">
|
||||
<canvas id="pdf-canvas"></canvas>
|
||||
<canvas id="signature-canvas" hidden style="position: absolute;" data-scale="1"></canvas>
|
||||
@ -87,6 +106,15 @@
|
||||
</div>
|
||||
<script>
|
||||
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 signatureUpload = document.querySelector('input[name=signature-upload]');
|
||||
const pdfCanvas = document.getElementById('pdf-canvas');
|
||||
@ -96,9 +124,9 @@
|
||||
const signaturePadCanvas = document.getElementById('signature-pad-canvas');
|
||||
const clearSignatureBtn = document.getElementById('clear-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"
|
||||
|
||||
signatureTypeContainer.style.display = 'none';
|
||||
const signaturePad = new SignaturePad(signaturePadCanvas, {
|
||||
minWidth: 1,
|
||||
maxWidth: 2,
|
||||
@ -126,15 +154,44 @@
|
||||
|
||||
$("input[name=signature-type]").change(function() {
|
||||
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';
|
||||
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) {
|
||||
populateSignatureFromPad();
|
||||
} else {
|
||||
} else if (importImageInput.checked) {
|
||||
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) {
|
||||
const img = new Image();
|
||||
@ -178,6 +235,7 @@
|
||||
reader.readAsDataURL(file);
|
||||
}
|
||||
function populateSignatureFromPad() {
|
||||
if (!document.getElementById('draw-signature').checked) return;
|
||||
if (signaturePad.isEmpty()) return;
|
||||
|
||||
const dataURL = signaturePad.toDataURL();
|
||||
|
Loading…
Reference in New Issue
Block a user