mirror of
https://github.com/Frooodle/Stirling-PDF.git
synced 2025-09-03 17:52:30 +02:00
opacity
This commit is contained in:
parent
0890073bd4
commit
4b99093f6b
@ -5,7 +5,7 @@ plugins {
|
||||
}
|
||||
|
||||
group = 'stirling.software'
|
||||
version = '0.4.1'
|
||||
version = '0.4.2'
|
||||
sourceCompatibility = '17'
|
||||
|
||||
repositories {
|
||||
|
@ -1,66 +0,0 @@
|
||||
package stirling.software.SPDF.controller;
|
||||
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
|
||||
import org.apache.pdfbox.cos.COSName;
|
||||
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||
import org.apache.pdfbox.pdmodel.PDPage;
|
||||
import org.apache.pdfbox.pdmodel.PDResources;
|
||||
import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import com.itextpdf.text.DocumentException;
|
||||
import com.itextpdf.text.pdf.PdfReader;
|
||||
import com.itextpdf.text.pdf.PdfStamper;
|
||||
|
||||
import stirling.software.SPDF.utils.PdfUtils;
|
||||
import stirling.software.SPDF.utils.ProcessExecutor;
|
||||
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
@Controller
|
||||
public class CropController {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(CropController.class);
|
||||
|
||||
@GetMapping("/crop-pdf")
|
||||
public String compressPdfForm(Model model) {
|
||||
model.addAttribute("currentPage", "crop-pdf");
|
||||
return "crop-pdf";
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -1,66 +0,0 @@
|
||||
package stirling.software.SPDF.controller;
|
||||
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
|
||||
import org.apache.pdfbox.cos.COSName;
|
||||
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||
import org.apache.pdfbox.pdmodel.PDPage;
|
||||
import org.apache.pdfbox.pdmodel.PDResources;
|
||||
import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import com.itextpdf.text.DocumentException;
|
||||
import com.itextpdf.text.pdf.PdfReader;
|
||||
import com.itextpdf.text.pdf.PdfStamper;
|
||||
|
||||
import stirling.software.SPDF.utils.PdfUtils;
|
||||
import stirling.software.SPDF.utils.ProcessExecutor;
|
||||
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
@Controller
|
||||
public class SignController {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(SignController.class);
|
||||
|
||||
@GetMapping("/sign-pdf")
|
||||
public String compressPdfForm(Model model) {
|
||||
model.addAttribute("currentPage", "sign-pdf");
|
||||
return "sign-pdf";
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -26,6 +26,7 @@ import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import stirling.software.SPDF.utils.PdfUtils;
|
||||
import stirling.software.SPDF.utils.WatermarkRemover;
|
||||
import org.apache.pdfbox.pdmodel.graphics.state.PDExtendedGraphicsState;
|
||||
|
||||
@Controller
|
||||
public class WatermarkController {
|
||||
@ -45,7 +46,8 @@ public class WatermarkController {
|
||||
@PostMapping("/add-watermark")
|
||||
public ResponseEntity<byte[]> addWatermark(@RequestParam("fileInput") MultipartFile pdfFile, @RequestParam("watermarkText") String watermarkText,
|
||||
@RequestParam(defaultValue = "30", name = "fontSize") float fontSize, @RequestParam(defaultValue = "0", name = "rotation") float rotation,
|
||||
@RequestParam(defaultValue = "50", name = "widthSpacer") int widthSpacer, @RequestParam(defaultValue = "50", name = "heightSpacer") int heightSpacer)
|
||||
@RequestParam(defaultValue = "0.5", name = "opacity") float opacity,
|
||||
@RequestParam(defaultValue = "50", name = "widthSpacer") int widthSpacer, @RequestParam(defaultValue = "50", name = "heightSpacer") int heightSpacer)
|
||||
throws IOException {
|
||||
|
||||
// Load the input PDF
|
||||
@ -53,9 +55,18 @@ public class WatermarkController {
|
||||
|
||||
// Create a page in the document
|
||||
for (PDPage page : document.getPages()) {
|
||||
|
||||
|
||||
|
||||
|
||||
// Get the page's content stream
|
||||
PDPageContentStream contentStream = new PDPageContentStream(document, page, PDPageContentStream.AppendMode.APPEND, true);
|
||||
|
||||
// Set transparency
|
||||
PDExtendedGraphicsState graphicsState = new PDExtendedGraphicsState();
|
||||
graphicsState.setNonStrokingAlphaConstant(opacity);
|
||||
contentStream.setGraphicsStateParameters(graphicsState);
|
||||
|
||||
// Set font of watermark
|
||||
PDFont font = PDType1Font.HELVETICA_BOLD;
|
||||
contentStream.beginText();
|
||||
|
@ -225,6 +225,7 @@ watermark.selectText.3=حجم الخط:
|
||||
watermark.selectText.4=دوران (0-360):
|
||||
watermark.selectText.5=widthSpacer (مسافة بين كل علامة مائية أفقيًا):
|
||||
watermark.selectText.6=heightSpacer (مسافة بين كل علامة مائية عموديًا):
|
||||
watermark.selectText.7=\u0627\u0644\u062A\u0639\u062A\u064A\u0645 (0\u066A - 100\u066A):
|
||||
watermark.submit=إضافة علامة مائية
|
||||
|
||||
#remove-watermark
|
||||
|
@ -224,6 +224,7 @@ watermark.selectText.3=Schriftgröße:
|
||||
watermark.selectText.4=Drehung (0-360):
|
||||
watermark.selectText.5=breiteSpacer (horizontaler Abstand zwischen den einzelnen Wasserzeichen):
|
||||
watermark.selectText.6=höheSpacer (vertikaler Abstand zwischen den einzelnen Wasserzeichen):
|
||||
watermark.selectText.7=Deckkraft (0% - 100 %):
|
||||
watermark.submit=Wasserzeichen hinzufügen
|
||||
|
||||
#remove-watermark
|
||||
|
@ -222,6 +222,7 @@ watermark.selectText.3=Font Size:
|
||||
watermark.selectText.4=Rotation (0-360):
|
||||
watermark.selectText.5=widthSpacer (Space between each watermark horizontally):
|
||||
watermark.selectText.6=heightSpacer (Space between each watermark vertically):
|
||||
watermark.selectText.7=Opacity (0% - 100%):
|
||||
watermark.submit=Add Watermark
|
||||
|
||||
#remove-watermark
|
||||
|
@ -222,6 +222,7 @@ watermark.selectText.3=Font Size:
|
||||
watermark.selectText.4=Rotation (0-360):
|
||||
watermark.selectText.5=widthSpacer (Space between each watermark horizontally):
|
||||
watermark.selectText.6=heightSpacer (Space between each watermark vertically):
|
||||
watermark.selectText.7=Opacity (0% - 100%):
|
||||
watermark.submit=Add Watermark
|
||||
|
||||
#remove-watermark
|
||||
|
@ -230,6 +230,7 @@ watermark.selectText.3=Taille de la police
|
||||
watermark.selectText.4=Rotation (0-360)Â:
|
||||
watermark.selectText.5=widthSpacer (Espace entre chaque filigrane horizontalement)Â:
|
||||
watermark.selectText.6=heightSpacer (Espace entre chaque filigrane verticalement)Â:
|
||||
watermark.selectText.7=Opacité (0 % - 100 %):
|
||||
watermark.submit=Ajouter un filigrane
|
||||
|
||||
#remove-watermark
|
||||
|
@ -1,162 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html th:lang="${#locale.language}" th:lang-direction="#{language.direction}" xmlns:th="http://www.thymeleaf.org">
|
||||
|
||||
<th:block th:insert="~{fragments/common :: head(title=#{extractImages.title})}"></th:block>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.12.313/pdf.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/pdf-lib@1.17.1/dist/pdf-lib.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/cropperjs/1.5.12/cropper.min.js"></script>
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/cropperjs/1.5.12/cropper.min.css" />
|
||||
|
||||
|
||||
<body>
|
||||
<div id="page-container">
|
||||
<div id="content-wrap">
|
||||
<div th:insert="~{fragments/navbar.html :: navbar}"></div>
|
||||
<br> <br>
|
||||
<div class="container">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-6">
|
||||
<h2 th:text="#{extractImages.header}"></h2>
|
||||
|
||||
<input type="file" id="pdfFile" accept=".pdf" />
|
||||
<canvas id="pdfCanvas" style="border:1px solid black;"></canvas>
|
||||
<input type="number" id="pageNumber" min="1" />
|
||||
<button id="goToPage">Go to Page</button>
|
||||
<button id="cropPdf">Crop PDF</button>
|
||||
<button id="downloadPdf">Download PDF</button>
|
||||
|
||||
<Script>
|
||||
const pdfCanvas = document.getElementById('pdfCanvas');
|
||||
const ctx = pdfCanvas.getContext('2d');
|
||||
let pdfDoc = null;
|
||||
let currentPage = 1;
|
||||
let scaleFactor = 1.0;
|
||||
let cropper = null;
|
||||
|
||||
async function renderPage(pageNumber) {
|
||||
const page = await pdfDoc.getPage(pageNumber);
|
||||
const viewport = page.getViewport({ scale: scaleFactor });
|
||||
pdfCanvas.width = viewport.width;
|
||||
pdfCanvas.height = viewport.height;
|
||||
const renderContext = {
|
||||
canvasContext: ctx,
|
||||
viewport: viewport,
|
||||
};
|
||||
await page.render(renderContext);
|
||||
}
|
||||
|
||||
document.getElementById('pdfFile').addEventListener('change', async (event) => {
|
||||
const file = event.target.files[0];
|
||||
if (!file) return;
|
||||
const fileReader = new FileReader();
|
||||
fileReader.onload = async (e) => {
|
||||
const typedArray = new Uint8Array(e.target.result);
|
||||
pdfDoc = await pdfjsLib.getDocument(typedArray).promise;
|
||||
await renderPage(currentPage);
|
||||
|
||||
if (cropper) cropper.destroy();
|
||||
cropper = new Cropper(pdfCanvas, {
|
||||
viewMode: 1,
|
||||
autoCropArea: 1,
|
||||
background: false, // Disable the default checkered background
|
||||
modal: false, // Disable the default shading of the cropped area
|
||||
dragMode: "crop",
|
||||
cropBoxResizable: true,
|
||||
guides: false, // Disable the default dashed crop box guides
|
||||
// Set a custom background color with transparency
|
||||
cropBox: {
|
||||
background: "rgba(0, 0, 0, 0.2)",
|
||||
},
|
||||
});
|
||||
|
||||
};
|
||||
fileReader.readAsArrayBuffer(file);
|
||||
});
|
||||
|
||||
document.getElementById('goToPage').addEventListener('click', async () => {
|
||||
currentPage = parseInt(document.getElementById('pageNumber').value);
|
||||
await renderPage(currentPage);
|
||||
if (cropper) cropper.destroy();
|
||||
cropper = new Cropper(pdfCanvas, {
|
||||
viewMode: 1,
|
||||
autoCropArea: 1,
|
||||
background: false, // Disable the default checkered background
|
||||
modal: false, // Disable the default shading of the cropped area
|
||||
dragMode: "crop",
|
||||
cropBoxResizable: true,
|
||||
guides: false, // Disable the default dashed crop box guides
|
||||
// Set a custom background color with transparency
|
||||
cropBox: {
|
||||
background: "rgba(0, 0, 0, 0.2)",
|
||||
},
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
document.getElementById('cropPdf').addEventListener('click', async () => {
|
||||
if (!cropper) return;
|
||||
|
||||
const cropBoxData = cropper.getCropBoxData();
|
||||
const [x, y, width, height] = [cropBoxData.left, cropBoxData.top, cropBoxData.width, cropBoxData.height];
|
||||
|
||||
const page = await pdfDoc.getPage(currentPage);
|
||||
const croppedPdf = await PDFLib.PDFDocument.create();
|
||||
const [croppedPage] = await croppedPdf.copyPages(pdfDoc, [currentPage - 1]);
|
||||
const cropBox = {
|
||||
left: x / scaleFactor,
|
||||
bottom: (pdfCanvas.height - y - height) / scaleFactor,
|
||||
right: (x + width) / scaleFactor,
|
||||
top: (pdfCanvas.height - y) / scaleFactor,
|
||||
};
|
||||
croppedPage.setCropBox(cropBox);
|
||||
croppedPdf.addPage(croppedPage);
|
||||
|
||||
const croppedPdfBytes = await croppedPdf.save();
|
||||
const croppedPdfBlob = new Blob([croppedPdfBytes], { type: 'application/pdf' });
|
||||
|
||||
const downloadLink = document.getElementById('downloadPdf');
|
||||
downloadLink.href = URL.createObjectURL(croppedPdfBlob);
|
||||
downloadLink.download = 'cropped.pdf';
|
||||
});
|
||||
|
||||
document.getElementById('downloadPdf').addEventListener('click', async () => {
|
||||
if (!pdfDoc || !cropper) {
|
||||
alert('No PDF available. Please load a PDF first.');
|
||||
return;
|
||||
}
|
||||
|
||||
const cropBoxData = cropper.getCropBoxData();
|
||||
const [x, y, width, height] = [cropBoxData.left, cropBoxData.top, cropBoxData.width, cropBoxData.height];
|
||||
|
||||
const page = await pdfDoc.getPage(currentPage);
|
||||
const croppedPdf = await PDFLib.PDFDocument.create();
|
||||
if (!isNaN(currentPage)) {
|
||||
const [croppedPage] = await croppedPdf.copyPages(pdfDoc, [currentPage - 1]);
|
||||
const cropBox = {
|
||||
left: x / scaleFactor,
|
||||
bottom: (pdfCanvas.height - y - height) / scaleFactor,
|
||||
right: (x + width) / scaleFactor,
|
||||
top: (pdfCanvas.height - y) / scaleFactor,
|
||||
};
|
||||
croppedPage.setCropBox(cropBox);
|
||||
croppedPdf.addPage(croppedPage);
|
||||
}
|
||||
|
||||
const croppedPdfBytes = await croppedPdf.save();
|
||||
const croppedPdfBlob = new Blob([croppedPdfBytes], { type: 'application/pdf' });
|
||||
|
||||
const downloadLink = document.getElementById('downloadPdf');
|
||||
downloadLink.href = URL.createObjectURL(croppedPdfBlob);
|
||||
downloadLink.download = 'cropped.pdf';
|
||||
});
|
||||
|
||||
</Script>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div th:insert="~{fragments/footer.html :: footer}"></div>
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
@ -26,6 +26,45 @@
|
||||
<label for="fontSize" th:text="#{watermark.selectText.3}"></label>
|
||||
<input type="text" id="fontSize" name="fontSize" class="form-control" value="30" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="opacity" th:text="#{watermark.selectText.7}"></label>
|
||||
<input type="text" id="opacity" name="opacityText" class="form-control" value="50" onblur="updateopacityValue()" />
|
||||
<input type="hidden" id="opacityReal" name="opacity" value="0.5">
|
||||
</div>
|
||||
|
||||
|
||||
<script>
|
||||
const opacityInput = document.getElementById('opacity');
|
||||
const opacityRealInput = document.getElementById('opacityReal');
|
||||
|
||||
const updateopacityValue = () => {
|
||||
let percentageValue = parseFloat(opacityInput.value.replace('%', ''));
|
||||
if (isNaN(percentageValue)) {
|
||||
percentageValue = 0;
|
||||
}
|
||||
percentageValue = Math.min(Math.max(percentageValue, 0), 100);
|
||||
opacityInput.value = `${percentageValue}`;
|
||||
opacityRealInput.value = (percentageValue / 100).toFixed(2);
|
||||
};
|
||||
|
||||
const appendPercentageSymbol = () => {
|
||||
if (!opacityInput.value.endsWith('%')) {
|
||||
opacityInput.value += '%';
|
||||
}
|
||||
};
|
||||
|
||||
opacityInput.addEventListener('focus', () => {
|
||||
opacityInput.value = opacityInput.value.replace('%', '');
|
||||
});
|
||||
opacityInput.addEventListener('blur', () => {
|
||||
updateopacityValue();
|
||||
appendPercentageSymbol();
|
||||
});
|
||||
|
||||
// Set initial values
|
||||
updateopacityValue();
|
||||
appendPercentageSymbol();
|
||||
</script>
|
||||
<div class="form-group">
|
||||
<label for="rotation" th:text="#{watermark.selectText.4}"></label>
|
||||
<input type="text" id="rotation" name="rotation" class="form-control" value="45" />
|
||||
|
@ -1,173 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html th:lang="${#locale.language}" th:lang-direction="#{language.direction}" xmlns:th="http://www.thymeleaf.org">
|
||||
|
||||
<th:block th:insert="~{fragments/common :: head(title=#{extractImages.title})}"></th:block>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.12.313/pdf.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/pdf-lib@1.17.1/dist/pdf-lib.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/cropperjs/1.5.12/cropper.min.js"></script>
|
||||
|
||||
|
||||
<body>
|
||||
<div id="page-container">
|
||||
<div id="content-wrap">
|
||||
<div th:insert="~{fragments/navbar.html :: navbar}"></div>
|
||||
<br> <br>
|
||||
<div class="container">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-6">
|
||||
<h2 th:text="#{extractImages.header}"></h2>
|
||||
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>PDF Signature App</title>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.12.313/pdf.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>
|
||||
<style>
|
||||
canvas {
|
||||
border: 1px solid black;
|
||||
}
|
||||
</style>
|
||||
|
||||
<input type="file" id="pdf-upload" accept=".pdf" />
|
||||
<div>
|
||||
<canvas id="pdf-canvas"></canvas>
|
||||
<canvas id="signature-pad" width="400" height="200"></canvas>
|
||||
</div>
|
||||
<button id="save-signature">Save Signature</button>
|
||||
<button id="download-pdf">Download PDF</button>
|
||||
<script>
|
||||
|
||||
const pdfUpload = document.getElementById('pdf-upload');
|
||||
|
||||
// app.js
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const pdfCanvas = document.getElementById('pdf-canvas');
|
||||
const signatureCanvas = document.getElementById('signature-pad');
|
||||
const saveSignatureBtn = document.getElementById('save-signature');
|
||||
const downloadPdfBtn = document.getElementById('download-pdf');
|
||||
|
||||
const pdfCtx = pdfCanvas.getContext('2d');
|
||||
const signaturePad = new SignaturePad(signatureCanvas);
|
||||
|
||||
let pdfDoc = null;
|
||||
let signatureImage = null;
|
||||
|
||||
pdfjsLib.GlobalWorkerOptions.workerSrc = 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.12.313/pdf.worker.min.js';
|
||||
|
||||
async function loadPdf(pdfData) {
|
||||
pdfDoc = await pdfjsLib.getDocument({ data: pdfData }).promise;
|
||||
renderPage(1);
|
||||
}
|
||||
|
||||
pdfUpload.addEventListener('change', async (event) => {
|
||||
const file = event.target.files[0];
|
||||
if (file) {
|
||||
const pdfData = await file.arrayBuffer();
|
||||
loadPdf(pdfData);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
function renderPage(pageNum) {
|
||||
pdfDoc.getPage(pageNum).then((page) => {
|
||||
const viewport = page.getViewport({ scale: 1 });
|
||||
pdfCanvas.width = viewport.width;
|
||||
pdfCanvas.height = viewport.height;
|
||||
|
||||
const renderCtx = {
|
||||
canvasContext: pdfCtx,
|
||||
viewport: viewport,
|
||||
};
|
||||
|
||||
page.render(renderCtx);
|
||||
});
|
||||
}
|
||||
|
||||
saveSignatureBtn.addEventListener('click', () => {
|
||||
if (signaturePad.isEmpty()) {
|
||||
alert('Please provide a signature.');
|
||||
} else {
|
||||
signatureImage = signaturePad.toDataURL();
|
||||
signaturePad.clear();
|
||||
}
|
||||
});
|
||||
|
||||
downloadPdfBtn.addEventListener('click', async () => {
|
||||
if (!signatureImage) {
|
||||
alert('Please save a signature first.');
|
||||
return;
|
||||
}
|
||||
|
||||
// Load the original PDF
|
||||
const pdfUrl = 'path/to/your/pdf-file.pdf';
|
||||
const pdfBytes = await fetch(pdfUrl).then((res) => res.arrayBuffer());
|
||||
const pdfDoc = await PDFLib.PDFDocument.load(pdfBytes);
|
||||
|
||||
// Embed the signature image
|
||||
const signaturePng = await fetch(signatureImage).then((res) => res.arrayBuffer());
|
||||
const signatureImageObject = await pdfDoc.embedPng(signaturePng);
|
||||
|
||||
// Get the page to insert the signature
|
||||
const [page] = pdfDoc.getPages();
|
||||
const { width, height } = page.getSize();
|
||||
|
||||
// Set the signature dimensions and position (customize this as needed)
|
||||
const signatureWidth = 200;
|
||||
const signatureHeight = 100;
|
||||
const signatureX = width / 2 - signatureWidth / 2;
|
||||
const signatureY = height / 2 - signatureHeight / 2;
|
||||
|
||||
// Add the signature image to the page
|
||||
page.drawImage(signatureImageObject, {
|
||||
x: signatureX,
|
||||
y: signatureY,
|
||||
width: signatureWidth,
|
||||
height: signatureHeight,
|
||||
});
|
||||
|
||||
// Serialize the PDF and create a download link
|
||||
const pdfBytesModified = await pdfDoc.save();
|
||||
const blob = new Blob([pdfBytesModified], { type: 'application/pdf' });
|
||||
const link = document.createElement('a');
|
||||
link.href = URL.createObjectURL(blob);
|
||||
link.download = 'signed-document.pdf';
|
||||
link.click();
|
||||
});
|
||||
|
||||
if (pdfUpload.files[0]) {
|
||||
(async () => {
|
||||
if (pdfUpload.files[0]) {
|
||||
await loadPdf(await pdfUpload.files[0].arrayBuffer());
|
||||
}
|
||||
})();
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div th:insert="~{fragments/footer.html :: footer}"></div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
Loading…
Reference in New Issue
Block a user