mirror of
https://github.com/Stirling-Tools/Stirling-PDF.git
synced 2025-06-06 18:30:57 +00:00
Fix displayed identical fonts in sign PDF (#2751)
# Description of Changes ### Changes: - Add a new custom select to display fonts correctly and to allow more styling flexibility in Sign PDF feature by hiding the original `<select>` element (`display: none;`) and wrap it with a `<div>` and then create a custom selection menu using `<div>`s and CSS to achieve the required results due to the limitations of `<select>` and `<option>` while still preserving the hidden `<select>` for form submission. ### Why was the change made? 1. A bug that caused font families to not be displayed in Firefox. 2. Select/Option element are not flexible when it comes to styling (compared to DIVs for example) but bullet point `1.` is of higher priority. ### UI Changes: - Dark Mode: - Before:  - After:  - Light Mode: - Before:  - After:  Note: - Changes in `sign.js` are between the lines 95-228, as it seems the file was auto-formatted affecting whitespaces and single_quotes/double_quotes. #### Useful quotes from MDN: > [Styling with CSS](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/option#styling_with_css) Styling the <option> element is highly limited. Options don't inherit the font set on the parent. In Firefox, only [color](https://developer.mozilla.org/en-US/docs/Web/CSS/color) and [background-color](https://developer.mozilla.org/en-US/docs/Web/CSS/background-color) can be set, however in Chrome and Safari it's not possible to set any properties. You can find more details about styling in [our guide to advanced form styling](https://developer.mozilla.org/en-US/docs/Learn_web_development/Extensions/Forms/Advanced_form_styling). #### Useful references: - [Option Element](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/option#styling_with_css) - [Select Element](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/select) - [Advanced Form Styling](https://developer.mozilla.org/en-US/docs/Learn_web_development/Extensions/Forms/Advanced_form_styling) Closes #1575 --- ## Checklist ### General - [x] I have read the [Contribution Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md) - [x] I have read the [Stirling-PDF Developer Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md) (if applicable) - [ ] I have read the [How to add new languages to Stirling-PDF](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/HowToAddNewLanguage.md) (if applicable) - [x] I have performed a self-review of my own code - [x] My changes generate no new warnings ### Documentation - [ ] I have updated relevant docs on [Stirling-PDF's doc repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/) (if functionality has heavily changed) - [ ] I have read the section [Add New Translation Tags](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/HowToAddNewLanguage.md#add-new-translation-tags) (for new translation tags only) ### UI Changes (if applicable) - [x] Screenshots or videos demonstrating the UI changes are attached (e.g., as comments or direct attachments in the PR) ### Testing (if applicable) - [x] I have tested my changes locally. Refer to the [Testing Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md#6-testing) for more details.
This commit is contained in:
parent
8353c399d2
commit
b4451da2f4
@ -40,7 +40,6 @@ select#font-select option {
|
|||||||
max-width: 4rem;
|
max-width: 4rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.rotation-handle {
|
.rotation-handle {
|
||||||
width: 20px;
|
width: 20px;
|
||||||
height: 20px;
|
height: 20px;
|
||||||
@ -163,3 +162,76 @@ select#font-select option {
|
|||||||
.small-file-container-saved:hover .drag-icon {
|
.small-file-container-saved:hover .drag-icon {
|
||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* The container must be positioned relative: */
|
||||||
|
.custom-select {
|
||||||
|
position: relative;
|
||||||
|
font-family: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-select select {
|
||||||
|
display: none; /*hide original SELECT element: */
|
||||||
|
}
|
||||||
|
|
||||||
|
.select-selected {
|
||||||
|
background-color: inherit;
|
||||||
|
line-height: 30px;
|
||||||
|
font-size: 30px;
|
||||||
|
border-radius: 3rem !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Style the arrow inside the select element: */
|
||||||
|
.select-selected:after {
|
||||||
|
position: absolute;
|
||||||
|
content: "";
|
||||||
|
top: 50%;
|
||||||
|
right: 10px;
|
||||||
|
translate: 0 -50%;
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
border: 6px solid transparent;
|
||||||
|
border-color: #fff transparent transparent transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Point the arrow upwards when the select box is open (active): */
|
||||||
|
.select-selected.select-arrow-active:after {
|
||||||
|
border-color: transparent transparent #fff transparent;
|
||||||
|
translate: 0 -75%;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* style the items (options), including the selected item: */
|
||||||
|
.select-items div,
|
||||||
|
.select-selected {
|
||||||
|
color: inherit;
|
||||||
|
padding: 8px 16px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select-items div {
|
||||||
|
border: 1px solid transparent;
|
||||||
|
border-color: transparent transparent transparent transparent;
|
||||||
|
|
||||||
|
line-height: 30px;
|
||||||
|
font-size: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Style items (options): */
|
||||||
|
.select-items {
|
||||||
|
position: absolute;
|
||||||
|
background-color: inherit;
|
||||||
|
top: 100%;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
z-index: 101;
|
||||||
|
border: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Hide the items when the select box is closed: */
|
||||||
|
.select-hide {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select-items div:hover,
|
||||||
|
.same-as-selected {
|
||||||
|
background-color: rgba(54, 54, 54, 0.1);
|
||||||
|
}
|
||||||
|
@ -8,21 +8,21 @@ window.goToFirstOrLastPage = goToFirstOrLastPage;
|
|||||||
let currentPreviewSrc = null;
|
let currentPreviewSrc = null;
|
||||||
|
|
||||||
function toggleSignatureView() {
|
function toggleSignatureView() {
|
||||||
const gridView = document.getElementById('gridView');
|
const gridView = document.getElementById("gridView");
|
||||||
const listView = document.getElementById('listView');
|
const listView = document.getElementById("listView");
|
||||||
const gridText = document.querySelector('.grid-view-text');
|
const gridText = document.querySelector(".grid-view-text");
|
||||||
const listText = document.querySelector('.list-view-text');
|
const listText = document.querySelector(".list-view-text");
|
||||||
|
|
||||||
if (gridView.style.display !== 'none') {
|
if (gridView.style.display !== "none") {
|
||||||
gridView.style.display = 'none';
|
gridView.style.display = "none";
|
||||||
listView.style.display = 'block';
|
listView.style.display = "block";
|
||||||
gridText.style.display = 'none';
|
gridText.style.display = "none";
|
||||||
listText.style.display = 'inline';
|
listText.style.display = "inline";
|
||||||
} else {
|
} else {
|
||||||
gridView.style.display = 'block';
|
gridView.style.display = "block";
|
||||||
listView.style.display = 'none';
|
listView.style.display = "none";
|
||||||
gridText.style.display = 'inline';
|
gridText.style.display = "inline";
|
||||||
listText.style.display = 'none';
|
listText.style.display = "none";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -30,63 +30,204 @@ function previewSignature(element) {
|
|||||||
const src = element.dataset.src;
|
const src = element.dataset.src;
|
||||||
currentPreviewSrc = src;
|
currentPreviewSrc = src;
|
||||||
|
|
||||||
const filename = element.querySelector('.signature-list-name').textContent;
|
const filename = element.querySelector(".signature-list-name").textContent;
|
||||||
|
|
||||||
const previewImage = document.getElementById('previewImage');
|
const previewImage = document.getElementById("previewImage");
|
||||||
const previewFileName = document.getElementById('previewFileName');
|
const previewFileName = document.getElementById("previewFileName");
|
||||||
|
|
||||||
previewImage.src = src;
|
previewImage.src = src;
|
||||||
previewFileName.textContent = filename;
|
previewFileName.textContent = filename;
|
||||||
|
|
||||||
const modal = new bootstrap.Modal(document.getElementById('signaturePreview'));
|
const modal = new bootstrap.Modal(
|
||||||
|
document.getElementById("signaturePreview")
|
||||||
|
);
|
||||||
modal.show();
|
modal.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
function addSignatureFromPreview() {
|
function addSignatureFromPreview() {
|
||||||
if (currentPreviewSrc) {
|
if (currentPreviewSrc) {
|
||||||
DraggableUtils.createDraggableCanvasFromUrl(currentPreviewSrc);
|
DraggableUtils.createDraggableCanvasFromUrl(currentPreviewSrc);
|
||||||
bootstrap.Modal.getInstance(document.getElementById('signaturePreview')).hide();
|
bootstrap.Modal.getInstance(
|
||||||
|
document.getElementById("signaturePreview")
|
||||||
|
).hide();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let originalFileName = '';
|
let originalFileName = "";
|
||||||
document.querySelector('input[name=pdf-upload]').addEventListener('change', async (event) => {
|
document
|
||||||
|
.querySelector("input[name=pdf-upload]")
|
||||||
|
.addEventListener("change", async (event) => {
|
||||||
const fileInput = event.target;
|
const fileInput = event.target;
|
||||||
fileInput.addEventListener('file-input-change', async (e) => {
|
fileInput.addEventListener("file-input-change", async (e) => {
|
||||||
const { allFiles } = e.detail;
|
const { allFiles } = e.detail;
|
||||||
if (allFiles && allFiles.length > 0) {
|
if (allFiles && allFiles.length > 0) {
|
||||||
const file = allFiles[0];
|
const file = allFiles[0];
|
||||||
originalFileName = file.name.replace(/\.[^/.]+$/, '');
|
originalFileName = file.name.replace(/\.[^/.]+$/, "");
|
||||||
const pdfData = await file.arrayBuffer();
|
const pdfData = await file.arrayBuffer();
|
||||||
pdfjsLib.GlobalWorkerOptions.workerSrc = './pdfjs-legacy/pdf.worker.mjs';
|
pdfjsLib.GlobalWorkerOptions.workerSrc =
|
||||||
|
"./pdfjs-legacy/pdf.worker.mjs";
|
||||||
const pdfDoc = await pdfjsLib.getDocument({ data: pdfData }).promise;
|
const pdfDoc = await pdfjsLib.getDocument({ data: pdfData }).promise;
|
||||||
await DraggableUtils.renderPage(pdfDoc, 0);
|
await DraggableUtils.renderPage(pdfDoc, 0);
|
||||||
|
|
||||||
document.querySelectorAll('.show-on-file-selected').forEach((el) => {
|
document.querySelectorAll(".show-on-file-selected").forEach((el) => {
|
||||||
el.style.cssText = '';
|
el.style.cssText = "";
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
document.addEventListener('DOMContentLoaded', () => {
|
document.addEventListener("DOMContentLoaded", () => {
|
||||||
document.querySelectorAll('.show-on-file-selected').forEach((el) => {
|
document.querySelectorAll(".show-on-file-selected").forEach((el) => {
|
||||||
el.style.cssText = 'display:none !important';
|
el.style.cssText = "display:none !important";
|
||||||
});
|
});
|
||||||
document.querySelectorAll('.small-file-container-saved img ').forEach((img) => {
|
document
|
||||||
img.addEventListener('dragstart', (e) => {
|
.querySelectorAll(".small-file-container-saved img ")
|
||||||
e.dataTransfer.setData('fileUrl', img.src);
|
.forEach((img) => {
|
||||||
|
img.addEventListener("dragstart", (e) => {
|
||||||
|
e.dataTransfer.setData("fileUrl", img.src);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
document.addEventListener('keydown', (e) => {
|
document.addEventListener("keydown", (e) => {
|
||||||
if (e.key === 'Delete') {
|
if (e.key === "Delete") {
|
||||||
DraggableUtils.deleteDraggableCanvas(DraggableUtils.getLastInteracted());
|
DraggableUtils.deleteDraggableCanvas(DraggableUtils.getLastInteracted());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
addCustomSelect();
|
||||||
});
|
});
|
||||||
|
|
||||||
const imageUpload = document.querySelector('input[name=image-upload]');
|
function addCustomSelect() {
|
||||||
imageUpload.addEventListener('change', (e) => {
|
let customSelectElementContainer =
|
||||||
|
document.getElementById("signFontSelection");
|
||||||
|
let originalSelectElement =
|
||||||
|
customSelectElementContainer.querySelector("select");
|
||||||
|
|
||||||
|
let optionsCount = originalSelectElement.length;
|
||||||
|
|
||||||
|
let selectedItem = createAndStyleSelectedItem();
|
||||||
|
|
||||||
|
customSelectElementContainer.appendChild(selectedItem);
|
||||||
|
|
||||||
|
let customSelectionsOptionsContainer = createCustomOptionsContainer();
|
||||||
|
createAndAddCustomOptions();
|
||||||
|
customSelectElementContainer.appendChild(customSelectionsOptionsContainer);
|
||||||
|
|
||||||
|
selectedItem.addEventListener("click", function (e) {
|
||||||
|
/* When the select box is clicked, close any other select boxes,
|
||||||
|
and open/close the current select box: */
|
||||||
|
e.stopPropagation();
|
||||||
|
closeAllSelect(this);
|
||||||
|
this.nextSibling.classList.toggle("select-hide");
|
||||||
|
this.classList.toggle("select-arrow-active");
|
||||||
|
});
|
||||||
|
|
||||||
|
function createAndAddCustomOptions() {
|
||||||
|
for (let j = 0; j < optionsCount; j++) {
|
||||||
|
/* For each option in the original select element,
|
||||||
|
create a new DIV that will act as an option item: */
|
||||||
|
let customOptionItem = createAndStyleCustomOption(j);
|
||||||
|
|
||||||
|
customOptionItem.addEventListener("click", onCustomOptionClick);
|
||||||
|
customSelectionsOptionsContainer.appendChild(customOptionItem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function createCustomOptionsContainer() {
|
||||||
|
let customSelectionsOptionsContainer = document.createElement("DIV");
|
||||||
|
customSelectionsOptionsContainer.setAttribute(
|
||||||
|
"class",
|
||||||
|
"select-items select-hide"
|
||||||
|
);
|
||||||
|
return customSelectionsOptionsContainer;
|
||||||
|
}
|
||||||
|
|
||||||
|
function createAndStyleSelectedItem() {
|
||||||
|
let selectedItem = document.createElement("DIV");
|
||||||
|
selectedItem.setAttribute("class", "select-selected");
|
||||||
|
selectedItem.innerHTML =
|
||||||
|
originalSelectElement.options[
|
||||||
|
originalSelectElement.selectedIndex
|
||||||
|
].innerHTML;
|
||||||
|
|
||||||
|
selectedItem.style.fontFamily = window.getComputedStyle(
|
||||||
|
originalSelectElement.options[originalSelectElement.selectedIndex]
|
||||||
|
).fontFamily;
|
||||||
|
return selectedItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
function onCustomOptionClick(e) {
|
||||||
|
/* When an item is clicked, update the original select box,
|
||||||
|
and the selected item: */
|
||||||
|
let selectElement =
|
||||||
|
this.parentNode.parentNode.getElementsByTagName("select")[0];
|
||||||
|
let optionsCount = selectElement.length;
|
||||||
|
let currentlySelectedCustomOption = this.parentNode.previousSibling;
|
||||||
|
for (let i = 0; i < optionsCount; i++) {
|
||||||
|
if (selectElement.options[i].innerHTML == this.innerHTML) {
|
||||||
|
selectElement.selectedIndex = i;
|
||||||
|
currentlySelectedCustomOption.innerHTML = this.innerHTML;
|
||||||
|
currentlySelectedCustomOption.style.fontFamily = this.style.fontFamily;
|
||||||
|
|
||||||
|
let previouslySelectedOption =
|
||||||
|
this.parentNode.getElementsByClassName("same-as-selected");
|
||||||
|
|
||||||
|
if (previouslySelectedOption && previouslySelectedOption.length > 0)
|
||||||
|
previouslySelectedOption[0].classList.remove("same-as-selected");
|
||||||
|
|
||||||
|
this.classList.add("same-as-selected");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
currentlySelectedCustomOption.click();
|
||||||
|
}
|
||||||
|
|
||||||
|
function createAndStyleCustomOption(j) {
|
||||||
|
let customOptionItem = document.createElement("DIV");
|
||||||
|
customOptionItem.innerHTML = originalSelectElement.options[j].innerHTML;
|
||||||
|
customOptionItem.classList.add(originalSelectElement.options[j].className);
|
||||||
|
customOptionItem.style.fontFamily = window.getComputedStyle(
|
||||||
|
originalSelectElement.options[j]
|
||||||
|
).fontFamily;
|
||||||
|
|
||||||
|
if (j == originalSelectElement.selectedIndex)
|
||||||
|
customOptionItem.classList.add("same-as-selected");
|
||||||
|
return customOptionItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeAllSelect(element) {
|
||||||
|
/* A function that will close all select boxes in the document,
|
||||||
|
except the current select box: */
|
||||||
|
let allSelectedOptions = document.getElementsByClassName("select-selected");
|
||||||
|
let allSelectedOptionsCount = allSelectedOptions.length;
|
||||||
|
let indicesOfContainersToHide = [];
|
||||||
|
for (let i = 0; i < allSelectedOptionsCount; i++) {
|
||||||
|
if (element == allSelectedOptions[i]) {
|
||||||
|
indicesOfContainersToHide.push(i);
|
||||||
|
} else {
|
||||||
|
allSelectedOptions[i].classList.remove("select-arrow-active");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
hideOptionsContainers(indicesOfContainersToHide);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If the user clicks anywhere outside the select box,
|
||||||
|
then close all select boxes: */
|
||||||
|
document.addEventListener("click", closeAllSelect);
|
||||||
|
|
||||||
|
function hideOptionsContainers(containersIndices) {
|
||||||
|
let allOptionsContainers = document.getElementsByClassName("select-items");
|
||||||
|
let allSelectionListsContainerCount = allOptionsContainers.length;
|
||||||
|
for (let i = 0; i < allSelectionListsContainerCount; i++) {
|
||||||
|
if (containersIndices.indexOf(i)) {
|
||||||
|
allOptionsContainers[i].classList.add("select-hide");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const imageUpload = document.querySelector("input[name=image-upload]");
|
||||||
|
imageUpload.addEventListener("change", (e) => {
|
||||||
if (!e.target.files) return;
|
if (!e.target.files) return;
|
||||||
for (const imageFile of e.target.files) {
|
for (const imageFile of e.target.files) {
|
||||||
var reader = new FileReader();
|
var reader = new FileReader();
|
||||||
@ -97,11 +238,11 @@ imageUpload.addEventListener('change', (e) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const signaturePadCanvas = document.getElementById('drawing-pad-canvas');
|
const signaturePadCanvas = document.getElementById("drawing-pad-canvas");
|
||||||
const signaturePad = new SignaturePad(signaturePadCanvas, {
|
const signaturePad = new SignaturePad(signaturePadCanvas, {
|
||||||
minWidth: 1,
|
minWidth: 1,
|
||||||
maxWidth: 2,
|
maxWidth: 2,
|
||||||
penColor: 'black',
|
penColor: "black",
|
||||||
});
|
});
|
||||||
|
|
||||||
function addDraggableFromPad() {
|
function addDraggableFromPad() {
|
||||||
@ -113,7 +254,7 @@ function addDraggableFromPad() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getCroppedCanvasDataUrl(canvas) {
|
function getCroppedCanvasDataUrl(canvas) {
|
||||||
let originalCtx = canvas.getContext('2d');
|
let originalCtx = canvas.getContext("2d");
|
||||||
let originalWidth = canvas.width;
|
let originalWidth = canvas.width;
|
||||||
let originalHeight = canvas.height;
|
let originalHeight = canvas.height;
|
||||||
let imageData = originalCtx.getImageData(0, 0, originalWidth, originalHeight);
|
let imageData = originalCtx.getImageData(0, 0, originalWidth, originalHeight);
|
||||||
@ -129,7 +270,8 @@ function getCroppedCanvasDataUrl(canvas) {
|
|||||||
for (y = 0; y < originalHeight; y++) {
|
for (y = 0; y < originalHeight; y++) {
|
||||||
for (x = 0; x < originalWidth; x++) {
|
for (x = 0; x < originalWidth; x++) {
|
||||||
currentPixelColorValueIndex = (y * originalWidth + x) * 4;
|
currentPixelColorValueIndex = (y * originalWidth + x) * 4;
|
||||||
let currentPixelAlphaValue = imageData.data[currentPixelColorValueIndex + 3];
|
let currentPixelAlphaValue =
|
||||||
|
imageData.data[currentPixelColorValueIndex + 3];
|
||||||
if (currentPixelAlphaValue > 0) {
|
if (currentPixelAlphaValue > 0) {
|
||||||
if (minX > x) minX = x;
|
if (minX > x) minX = x;
|
||||||
if (maxX < x) maxX = x;
|
if (maxX < x) maxX = x;
|
||||||
@ -142,10 +284,15 @@ function getCroppedCanvasDataUrl(canvas) {
|
|||||||
let croppedWidth = maxX - minX;
|
let croppedWidth = maxX - minX;
|
||||||
let croppedHeight = maxY - minY;
|
let croppedHeight = maxY - minY;
|
||||||
if (croppedWidth < 0 || croppedHeight < 0) return null;
|
if (croppedWidth < 0 || croppedHeight < 0) return null;
|
||||||
let cuttedImageData = originalCtx.getImageData(minX, minY, croppedWidth, croppedHeight);
|
let cuttedImageData = originalCtx.getImageData(
|
||||||
|
minX,
|
||||||
|
minY,
|
||||||
|
croppedWidth,
|
||||||
|
croppedHeight
|
||||||
|
);
|
||||||
|
|
||||||
let croppedCanvas = document.createElement('canvas'),
|
let croppedCanvas = document.createElement("canvas"),
|
||||||
croppedCtx = croppedCanvas.getContext('2d');
|
croppedCtx = croppedCanvas.getContext("2d");
|
||||||
|
|
||||||
croppedCanvas.width = croppedWidth;
|
croppedCanvas.width = croppedWidth;
|
||||||
croppedCanvas.height = croppedHeight;
|
croppedCanvas.height = croppedHeight;
|
||||||
@ -158,9 +305,13 @@ function resizeCanvas() {
|
|||||||
var ratio = Math.max(window.devicePixelRatio || 1, 1);
|
var ratio = Math.max(window.devicePixelRatio || 1, 1);
|
||||||
var additionalFactor = 10;
|
var additionalFactor = 10;
|
||||||
|
|
||||||
signaturePadCanvas.width = signaturePadCanvas.offsetWidth * ratio * additionalFactor;
|
signaturePadCanvas.width =
|
||||||
signaturePadCanvas.height = signaturePadCanvas.offsetHeight * ratio * additionalFactor;
|
signaturePadCanvas.offsetWidth * ratio * additionalFactor;
|
||||||
signaturePadCanvas.getContext('2d').scale(ratio * additionalFactor, ratio * additionalFactor);
|
signaturePadCanvas.height =
|
||||||
|
signaturePadCanvas.offsetHeight * ratio * additionalFactor;
|
||||||
|
signaturePadCanvas
|
||||||
|
.getContext("2d")
|
||||||
|
.scale(ratio * additionalFactor, ratio * additionalFactor);
|
||||||
|
|
||||||
signaturePad.clear();
|
signaturePad.clear();
|
||||||
}
|
}
|
||||||
@ -174,12 +325,12 @@ new IntersectionObserver((entries, observer) => {
|
|||||||
new ResizeObserver(resizeCanvas).observe(signaturePadCanvas);
|
new ResizeObserver(resizeCanvas).observe(signaturePadCanvas);
|
||||||
|
|
||||||
function addDraggableFromText() {
|
function addDraggableFromText() {
|
||||||
const sigText = document.getElementById('sigText').value;
|
const sigText = document.getElementById("sigText").value;
|
||||||
const font = document.querySelector('select[name=font]').value;
|
const font = document.querySelector("select[name=font]").value;
|
||||||
const fontSize = 100;
|
const fontSize = 100;
|
||||||
|
|
||||||
const canvas = document.createElement('canvas');
|
const canvas = document.createElement("canvas");
|
||||||
const ctx = canvas.getContext('2d');
|
const ctx = canvas.getContext("2d");
|
||||||
ctx.font = `${fontSize}px ${font}`;
|
ctx.font = `${fontSize}px ${font}`;
|
||||||
const textWidth = ctx.measureText(sigText).width;
|
const textWidth = ctx.measureText(sigText).width;
|
||||||
const textHeight = fontSize;
|
const textHeight = fontSize;
|
||||||
@ -190,7 +341,7 @@ function addDraggableFromText() {
|
|||||||
canvas.height = paragraphs.length * textHeight * 1.35; // for tails
|
canvas.height = paragraphs.length * textHeight * 1.35; // for tails
|
||||||
ctx.font = `${fontSize}px ${font}`;
|
ctx.font = `${fontSize}px ${font}`;
|
||||||
|
|
||||||
ctx.textBaseline = 'top';
|
ctx.textBaseline = "top";
|
||||||
|
|
||||||
let y = 0;
|
let y = 0;
|
||||||
|
|
||||||
@ -212,8 +363,8 @@ async function goToFirstOrLastPage(page) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
document.getElementById('download-pdf').addEventListener('click', async () => {
|
document.getElementById("download-pdf").addEventListener("click", async () => {
|
||||||
const downloadButton = document.getElementById('download-pdf');
|
const downloadButton = document.getElementById("download-pdf");
|
||||||
const originalContent = downloadButton.innerHTML;
|
const originalContent = downloadButton.innerHTML;
|
||||||
|
|
||||||
downloadButton.disabled = true;
|
downloadButton.disabled = true;
|
||||||
@ -224,13 +375,13 @@ document.getElementById('download-pdf').addEventListener('click', async () => {
|
|||||||
try {
|
try {
|
||||||
const modifiedPdf = await DraggableUtils.getOverlayedPdfDocument();
|
const modifiedPdf = await DraggableUtils.getOverlayedPdfDocument();
|
||||||
const modifiedPdfBytes = await modifiedPdf.save();
|
const modifiedPdfBytes = await modifiedPdf.save();
|
||||||
const blob = new Blob([modifiedPdfBytes], {type: 'application/pdf'});
|
const blob = new Blob([modifiedPdfBytes], { type: "application/pdf" });
|
||||||
const link = document.createElement('a');
|
const link = document.createElement("a");
|
||||||
link.href = URL.createObjectURL(blob);
|
link.href = URL.createObjectURL(blob);
|
||||||
link.download = originalFileName + '_signed.pdf';
|
link.download = originalFileName + "_signed.pdf";
|
||||||
link.click();
|
link.click();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error downloading PDF:', error);
|
console.error("Error downloading PDF:", error);
|
||||||
} finally {
|
} finally {
|
||||||
downloadButton.disabled = false;
|
downloadButton.disabled = false;
|
||||||
downloadButton.innerHTML = originalContent;
|
downloadButton.innerHTML = originalContent;
|
||||||
|
@ -15,7 +15,8 @@
|
|||||||
|
|
||||||
#font-select option[value="[[${font.name}]]"] {
|
#font-select option[value="[[${font.name}]]"] {
|
||||||
font-family: "[[${font.name}]]",
|
font-family: "[[${font.name}]]",
|
||||||
cursive;
|
cursive
|
||||||
|
!important;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
</th:block>
|
</th:block>
|
||||||
@ -133,10 +134,12 @@
|
|||||||
<label class="form-check-label" for="sigText" th:text="#{text}"></label>
|
<label class="form-check-label" for="sigText" th:text="#{text}"></label>
|
||||||
<textarea class="form-control" id="sigText" name="sigText" rows="3"></textarea>
|
<textarea class="form-control" id="sigText" name="sigText" rows="3"></textarea>
|
||||||
<label th:text="#{font}"></label>
|
<label th:text="#{font}"></label>
|
||||||
|
<div id="signFontSelection" class="custom-select form-control">
|
||||||
<select class="form-control" name="font" id="font-select">
|
<select class="form-control" name="font" id="font-select">
|
||||||
<option th:each="font : ${fonts}" th:value="${font.name}" th:text="${font.name}"
|
<option th:each="font : ${fonts}" th:value="${font.name}" th:text="${font.name}"
|
||||||
th:class="${font.name.toLowerCase()+'-font'}"></option>
|
th:class="${font.name.toLowerCase()+'-font'}"></option>
|
||||||
</select>
|
</select>
|
||||||
|
</div>
|
||||||
<div class="margin-auto-parent">
|
<div class="margin-auto-parent">
|
||||||
<button id="save-text-signature" class="btn btn-outline-success mt-2 margin-center"
|
<button id="save-text-signature" class="btn btn-outline-success mt-2 margin-center"
|
||||||
onclick="addDraggableFromText()" th:text="#{sign.add}"></button>
|
onclick="addDraggableFromText()" th:text="#{sign.add}"></button>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user