mirror of
				https://github.com/Frooodle/Stirling-PDF.git
				synced 2025-10-25 11:17:28 +02: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.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: | ||||||
|  | |||||||
							
								
								
									
										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://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,16 +154,45 @@ | |||||||
| 
 | 
 | ||||||
|             $("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(); | ||||||
|                 img.onload = () => { |                 img.onload = () => { | ||||||
| @ -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(); | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user