mirror of
https://github.com/Stirling-Tools/Stirling-PDF.git
synced 2025-06-23 16:05:09 +00:00
Compare commits
7 Commits
9f5f333f57
...
def0552f24
Author | SHA1 | Date | |
---|---|---|---|
![]() |
def0552f24 | ||
![]() |
de9e3edf5c | ||
![]() |
1e0e942d93 | ||
![]() |
6da84338dc | ||
![]() |
1c27944329 | ||
![]() |
5a0567cf6a | ||
![]() |
63a9e40aa9 |
@ -134,7 +134,7 @@ Stirling-PDF currently supports 39 languages!
|
|||||||
| Hungarian (Magyar) (hu_HU) |  |
|
| Hungarian (Magyar) (hu_HU) |  |
|
||||||
| Indonesian (Bahasa Indonesia) (id_ID) |  |
|
| Indonesian (Bahasa Indonesia) (id_ID) |  |
|
||||||
| Irish (Gaeilge) (ga_IE) |  |
|
| Irish (Gaeilge) (ga_IE) |  |
|
||||||
| Italian (Italiano) (it_IT) |  |
|
| Italian (Italiano) (it_IT) |  |
|
||||||
| Japanese (日本語) (ja_JP) |  |
|
| Japanese (日本語) (ja_JP) |  |
|
||||||
| Korean (한국어) (ko_KR) |  |
|
| Korean (한국어) (ko_KR) |  |
|
||||||
| Norwegian (Norsk) (no_NB) |  |
|
| Norwegian (Norsk) (no_NB) |  |
|
||||||
|
@ -29,7 +29,7 @@ ext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
group = "stirling.software"
|
group = "stirling.software"
|
||||||
version = "0.45.5"
|
version = "0.45.6"
|
||||||
|
|
||||||
java {
|
java {
|
||||||
// 17 is lowest but we support and recommend 21
|
// 17 is lowest but we support and recommend 21
|
||||||
@ -516,7 +516,7 @@ dependencies {
|
|||||||
implementation "org.bouncycastle:bcprov-jdk18on:$bouncycastleVersion"
|
implementation "org.bouncycastle:bcprov-jdk18on:$bouncycastleVersion"
|
||||||
implementation "org.bouncycastle:bcpkix-jdk18on:$bouncycastleVersion"
|
implementation "org.bouncycastle:bcpkix-jdk18on:$bouncycastleVersion"
|
||||||
implementation "org.springframework.boot:spring-boot-starter-actuator:$springBootVersion"
|
implementation "org.springframework.boot:spring-boot-starter-actuator:$springBootVersion"
|
||||||
implementation "io.micrometer:micrometer-core:1.14.5"
|
implementation "io.micrometer:micrometer-core:1.14.6"
|
||||||
implementation group: "com.google.zxing", name: "core", version: "3.5.3"
|
implementation group: "com.google.zxing", name: "core", version: "3.5.3"
|
||||||
// https://mvnrepository.com/artifact/org.commonmark/commonmark
|
// https://mvnrepository.com/artifact/org.commonmark/commonmark
|
||||||
implementation "org.commonmark:commonmark:0.24.0"
|
implementation "org.commonmark:commonmark:0.24.0"
|
||||||
|
@ -109,33 +109,6 @@ public class AppConfig {
|
|||||||
return (rateLimit != null) ? Boolean.valueOf(rateLimit) : false;
|
return (rateLimit != null) ? Boolean.valueOf(rateLimit) : false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean(name = "uploadLimit")
|
|
||||||
public long uploadLimit() {
|
|
||||||
String maxUploadSize =
|
|
||||||
applicationProperties.getSystem().getFileUploadLimit() != null
|
|
||||||
? applicationProperties.getSystem().getFileUploadLimit()
|
|
||||||
: "";
|
|
||||||
|
|
||||||
if (maxUploadSize.isEmpty()) {
|
|
||||||
return 0;
|
|
||||||
} else if (!new Regex("^[1-9][0-9]{0,2}[KMGkmg][Bb]$").matches(maxUploadSize)) {
|
|
||||||
log.error(
|
|
||||||
"Invalid maxUploadSize format. Expected format: [1-9][0-9]{0,2}[KMGkmg][Bb], but got: {}",
|
|
||||||
maxUploadSize);
|
|
||||||
return 0;
|
|
||||||
} else {
|
|
||||||
String unit = maxUploadSize.replaceAll("[1-9][0-9]{0,2}", "").toUpperCase();
|
|
||||||
String number = maxUploadSize.replaceAll("[KMGkmg][Bb]", "");
|
|
||||||
long size = Long.parseLong(number);
|
|
||||||
return switch (unit) {
|
|
||||||
case "KB" -> size * 1024;
|
|
||||||
case "MB" -> size * 1024 * 1024;
|
|
||||||
case "GB" -> size * 1024 * 1024 * 1024;
|
|
||||||
default -> 0;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean(name = "RunningInDocker")
|
@Bean(name = "RunningInDocker")
|
||||||
public boolean runningInDocker() {
|
public boolean runningInDocker() {
|
||||||
return Files.exists(Paths.get("/.dockerenv"));
|
return Files.exists(Paths.get("/.dockerenv"));
|
||||||
|
@ -1,30 +0,0 @@
|
|||||||
package stirling.software.SPDF.controller.web;
|
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.stereotype.Component;
|
|
||||||
import org.springframework.web.bind.annotation.ControllerAdvice;
|
|
||||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
|
||||||
|
|
||||||
@Component
|
|
||||||
@ControllerAdvice
|
|
||||||
public class GlobalUploadLimitWebController {
|
|
||||||
|
|
||||||
@Autowired() private long uploadLimit;
|
|
||||||
|
|
||||||
@ModelAttribute("uploadLimit")
|
|
||||||
public long populateUploadLimit() {
|
|
||||||
return uploadLimit;
|
|
||||||
}
|
|
||||||
|
|
||||||
@ModelAttribute("uploadLimitReadable")
|
|
||||||
public String populateReadableLimit() {
|
|
||||||
return humanReadableByteCount(uploadLimit);
|
|
||||||
}
|
|
||||||
|
|
||||||
private String humanReadableByteCount(long bytes) {
|
|
||||||
if (bytes < 1024) return bytes + " B";
|
|
||||||
int exp = (int) (Math.log(bytes) / Math.log(1024));
|
|
||||||
String pre = "KMGTPE".charAt(exp - 1) + "B";
|
|
||||||
return String.format("%.1f %s", bytes / Math.pow(1024, exp), pre);
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,55 @@
|
|||||||
|
package stirling.software.SPDF.controller.web;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import stirling.software.SPDF.model.ApplicationProperties;
|
||||||
|
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
@Slf4j
|
||||||
|
public class UploadLimitService {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ApplicationProperties applicationProperties;
|
||||||
|
|
||||||
|
public long getUploadLimit() {
|
||||||
|
String maxUploadSize =
|
||||||
|
applicationProperties.getSystem().getFileUploadLimit() != null
|
||||||
|
? applicationProperties.getSystem().getFileUploadLimit()
|
||||||
|
: "";
|
||||||
|
|
||||||
|
if (maxUploadSize.isEmpty()) {
|
||||||
|
return 0;
|
||||||
|
} else if (!Pattern.compile("^[1-9][0-9]{0,2}[KMGkmg][Bb]$").matcher(maxUploadSize).matches()) {
|
||||||
|
log.error(
|
||||||
|
"Invalid maxUploadSize format. Expected format: [1-9][0-9]{0,2}[KMGkmg][Bb], but got: {}",
|
||||||
|
maxUploadSize);
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
String unit = maxUploadSize.replaceAll("[1-9][0-9]{0,2}", "").toUpperCase();
|
||||||
|
String number = maxUploadSize.replaceAll("[KMGkmg][Bb]", "");
|
||||||
|
long size = Long.parseLong(number);
|
||||||
|
return switch (unit) {
|
||||||
|
case "KB" -> size * 1024;
|
||||||
|
case "MB" -> size * 1024 * 1024;
|
||||||
|
case "GB" -> size * 1024 * 1024 * 1024;
|
||||||
|
default -> 0;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO: why do this server side not client?
|
||||||
|
public String getReadableUploadLimit() {
|
||||||
|
return humanReadableByteCount(getUploadLimit());
|
||||||
|
}
|
||||||
|
|
||||||
|
private String humanReadableByteCount(long bytes) {
|
||||||
|
if (bytes < 1024) return bytes + " B";
|
||||||
|
int exp = (int) (Math.log(bytes) / Math.log(1024));
|
||||||
|
String pre = "KMGTPE".charAt(exp - 1) + "B";
|
||||||
|
return String.format("%.1f %s", bytes / Math.pow(1024, exp), pre);
|
||||||
|
}
|
||||||
|
}
|
@ -10,9 +10,9 @@ multiPdfPrompt=Scegli 2 o più PDF
|
|||||||
multiPdfDropPrompt=Scegli (o trascina e rilascia) uno o più PDF
|
multiPdfDropPrompt=Scegli (o trascina e rilascia) uno o più PDF
|
||||||
imgPrompt=Scegli immagine/i
|
imgPrompt=Scegli immagine/i
|
||||||
genericSubmit=Invia
|
genericSubmit=Invia
|
||||||
uploadLimit=Maximum file size:
|
uploadLimit=Dimensione massima del file:
|
||||||
uploadLimitExceededSingular=is too large. Maximum allowed size is
|
uploadLimitExceededSingular=è troppo grande. La dimensione massima consentita è
|
||||||
uploadLimitExceededPlural=are too large. Maximum allowed size is
|
uploadLimitExceededPlural=sono troppo grandi. La dimensione massima consentita è
|
||||||
processTimeWarning=Nota: Questo processo potrebbe richiedere fino a un minuto in base alla dimensione dei file
|
processTimeWarning=Nota: Questo processo potrebbe richiedere fino a un minuto in base alla dimensione dei file
|
||||||
pageOrderPrompt=Ordine delle pagine (inserisci una lista di numeri separati da virgola):
|
pageOrderPrompt=Ordine delle pagine (inserisci una lista di numeri separati da virgola):
|
||||||
pageSelectionPrompt=Selezione pagina personalizzata (inserisci un elenco separato da virgole di numeri di pagina 1,5,6 o funzioni come 2n+1) :
|
pageSelectionPrompt=Selezione pagina personalizzata (inserisci un elenco separato da virgole di numeri di pagina 1,5,6 o funzioni come 2n+1) :
|
||||||
@ -93,7 +93,7 @@ legal.terms=Termini e Condizioni
|
|||||||
legal.accessibility=Accessibilità
|
legal.accessibility=Accessibilità
|
||||||
legal.cookie=Informativa sui cookie
|
legal.cookie=Informativa sui cookie
|
||||||
legal.impressum=Informazioni legali
|
legal.impressum=Informazioni legali
|
||||||
legal.showCookieBanner=Cookie Preferences
|
legal.showCookieBanner=Preferenze sui cookie
|
||||||
|
|
||||||
###############
|
###############
|
||||||
# Pipeline #
|
# Pipeline #
|
||||||
|
@ -553,7 +553,7 @@
|
|||||||
{
|
{
|
||||||
"moduleName": "io.micrometer:micrometer-core",
|
"moduleName": "io.micrometer:micrometer-core",
|
||||||
"moduleUrl": "https://github.com/micrometer-metrics/micrometer",
|
"moduleUrl": "https://github.com/micrometer-metrics/micrometer",
|
||||||
"moduleVersion": "1.14.5",
|
"moduleVersion": "1.14.6",
|
||||||
"moduleLicense": "The Apache Software License, Version 2.0",
|
"moduleLicense": "The Apache Software License, Version 2.0",
|
||||||
"moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt"
|
"moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt"
|
||||||
},
|
},
|
||||||
|
@ -132,7 +132,9 @@
|
|||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
clearTimeout(timeoutId);
|
clearTimeout(timeoutId);
|
||||||
showGameBtn.style.display = 'none';
|
if(showGameBtn){
|
||||||
|
showGameBtn.style.display = 'none';
|
||||||
|
}
|
||||||
submitButton.textContent = originalButtonText;
|
submitButton.textContent = originalButtonText;
|
||||||
submitButton.disabled = false;
|
submitButton.disabled = false;
|
||||||
handleDownloadError(error);
|
handleDownloadError(error);
|
||||||
|
@ -170,7 +170,7 @@ function setupFileInput(chooser) {
|
|||||||
inputContainer.querySelector('#fileInputText').innerHTML = window.fileInput.loading;
|
inputContainer.querySelector('#fileInputText').innerHTML = window.fileInput.loading;
|
||||||
|
|
||||||
async function checkZipFile() {
|
async function checkZipFile() {
|
||||||
const hasZipFiles = allFiles.some(file => zipTypes.includes(file.type));
|
const hasZipFiles = allFiles.some(file => file.type && zipTypes.includes(file.type));
|
||||||
|
|
||||||
// Only change to extractPDF message if we actually have zip files
|
// Only change to extractPDF message if we actually have zip files
|
||||||
if (hasZipFiles) {
|
if (hasZipFiles) {
|
||||||
|
@ -255,5 +255,12 @@ document.addEventListener('DOMContentLoaded', function () {
|
|||||||
});
|
});
|
||||||
}, 500);
|
}, 500);
|
||||||
|
|
||||||
|
Array.from(document.querySelectorAll('.feature-group-header')).forEach((header) => {
|
||||||
|
const parent = header.parentNode;
|
||||||
|
header.onclick = () => {
|
||||||
|
expandCollapseToggle(parent);
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
showFavoritesOnly();
|
showFavoritesOnly();
|
||||||
});
|
});
|
||||||
|
@ -241,10 +241,5 @@ document.addEventListener('DOMContentLoaded', async function () {
|
|||||||
console.error('Material Symbols Rounded font failed to load.');
|
console.error('Material Symbols Rounded font failed to load.');
|
||||||
});
|
});
|
||||||
|
|
||||||
Array.from(document.querySelectorAll('.feature-group-header')).forEach((header) => {
|
|
||||||
const parent = header.parentNode;
|
|
||||||
header.onclick = () => {
|
|
||||||
expandCollapseToggle(parent);
|
|
||||||
};
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
@ -57,11 +57,15 @@ function initLanguageSettings() {
|
|||||||
|
|
||||||
function sortLanguageDropdown() {
|
function sortLanguageDropdown() {
|
||||||
document.addEventListener('DOMContentLoaded', function () {
|
document.addEventListener('DOMContentLoaded', function () {
|
||||||
const dropdownMenu = document.querySelector('.dropdown-menu .dropdown-item.lang_dropdown-item').parentElement;
|
const dropdownMenu = document.getElementById('languageSelection');
|
||||||
if (dropdownMenu) {
|
if (dropdownMenu) {
|
||||||
const items = Array.from(dropdownMenu.children).filter((child) => child.matches('a'));
|
const items = Array.from(dropdownMenu.children).filter((child) => child.querySelector('a'));
|
||||||
items
|
items
|
||||||
.sort((a, b) => a.dataset.bsLanguageCode.localeCompare(b.dataset.bsLanguageCode))
|
.sort((wrapperA, wrapperB) => {
|
||||||
|
const a = wrapperA.querySelector('a');
|
||||||
|
const b = wrapperB.querySelector('a');
|
||||||
|
return a.dataset.bsLanguageCode.localeCompare(b.dataset.bsLanguageCode);
|
||||||
|
})
|
||||||
.forEach((node) => dropdownMenu.appendChild(node));
|
.forEach((node) => dropdownMenu.appendChild(node));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -21,12 +21,10 @@ export class DeletePageCommand extends Command {
|
|||||||
this.pagesContainer.removeChild(this.element);
|
this.pagesContainer.removeChild(this.element);
|
||||||
if (this.pagesContainer.childElementCount === 0) {
|
if (this.pagesContainer.childElementCount === 0) {
|
||||||
const filenameInput = document.getElementById("filename-input");
|
const filenameInput = document.getElementById("filename-input");
|
||||||
const filenameParagraph = document.getElementById("filename");
|
|
||||||
const downloadBtn = document.getElementById("export-button");
|
const downloadBtn = document.getElementById("export-button");
|
||||||
|
|
||||||
filenameInput.disabled = true;
|
filenameInput.disabled = true;
|
||||||
filenameInput.value = "";
|
filenameInput.value = "";
|
||||||
filenameParagraph.innerText = "";
|
|
||||||
|
|
||||||
downloadBtn.disabled = true;
|
downloadBtn.disabled = true;
|
||||||
}
|
}
|
||||||
@ -43,13 +41,10 @@ export class DeletePageCommand extends Command {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const filenameInput = document.getElementById("filename-input");
|
const filenameInput = document.getElementById("filename-input");
|
||||||
const filenameParagraph = document.getElementById("filename");
|
|
||||||
const downloadBtn = document.getElementById("export-button");
|
const downloadBtn = document.getElementById("export-button");
|
||||||
|
|
||||||
filenameInput.disabled = false;
|
filenameInput.disabled = false;
|
||||||
filenameInput.value = this.filenameInputValue;
|
filenameInput.value = this.filenameInputValue;
|
||||||
if (this.filenameParagraph)
|
|
||||||
filenameParagraph.innerText = this.filenameParagraphText;
|
|
||||||
|
|
||||||
downloadBtn.disabled = false;
|
downloadBtn.disabled = false;
|
||||||
}
|
}
|
||||||
@ -63,12 +58,10 @@ export class DeletePageCommand extends Command {
|
|||||||
this.pagesContainer.removeChild(this.element);
|
this.pagesContainer.removeChild(this.element);
|
||||||
if (this.pagesContainer.childElementCount === 0) {
|
if (this.pagesContainer.childElementCount === 0) {
|
||||||
const filenameInput = document.getElementById("filename-input");
|
const filenameInput = document.getElementById("filename-input");
|
||||||
const filenameParagraph = document.getElementById("filename");
|
|
||||||
const downloadBtn = document.getElementById("export-button");
|
const downloadBtn = document.getElementById("export-button");
|
||||||
|
|
||||||
filenameInput.disabled = true;
|
filenameInput.disabled = true;
|
||||||
filenameInput.value = "";
|
filenameInput.value = "";
|
||||||
filenameParagraph.innerText = "";
|
|
||||||
|
|
||||||
downloadBtn.disabled = true;
|
downloadBtn.disabled = true;
|
||||||
}
|
}
|
||||||
|
@ -112,10 +112,10 @@ function setAsDefault(value) {
|
|||||||
|
|
||||||
function adjustVisibleElements() {
|
function adjustVisibleElements() {
|
||||||
const container = document.querySelector('.recent-features');
|
const container = document.querySelector('.recent-features');
|
||||||
|
if(!container) return;
|
||||||
const subElements = Array.from(container.children);
|
const subElements = Array.from(container.children);
|
||||||
|
|
||||||
let totalWidth = 0;
|
let totalWidth = 0;
|
||||||
const containerWidth = container.offsetWidth;
|
|
||||||
|
|
||||||
subElements.forEach((element) => {
|
subElements.forEach((element) => {
|
||||||
totalWidth += 12 * parseFloat(getComputedStyle(document.documentElement).fontSize);
|
totalWidth += 12 * parseFloat(getComputedStyle(document.documentElement).fontSize);
|
||||||
|
@ -26,7 +26,6 @@ window.addEventListener("keydown", (event) => {
|
|||||||
|
|
||||||
function undoDraw() {
|
function undoDraw() {
|
||||||
const data = signaturePad.toData();
|
const data = signaturePad.toData();
|
||||||
|
|
||||||
if (data && data.length > 0) {
|
if (data && data.length > 0) {
|
||||||
const removed = data.pop();
|
const removed = data.pop();
|
||||||
undoData.push(removed);
|
undoData.push(removed);
|
||||||
@ -35,7 +34,6 @@ function undoDraw() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function redoDraw() {
|
function redoDraw() {
|
||||||
|
|
||||||
if (undoData.length > 0) {
|
if (undoData.length > 0) {
|
||||||
const data = signaturePad.toData();
|
const data = signaturePad.toData();
|
||||||
data.push(undoData.pop());
|
data.push(undoData.pop());
|
||||||
@ -52,24 +50,18 @@ function addDraggableFromPad() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getCroppedCanvasDataUrl(canvas) {
|
function getCroppedCanvasDataUrl(canvas) {
|
||||||
let originalCtx = canvas.getContext('2d');
|
let originalCtx = canvas.getContext('2d', { willReadFrequently: true });
|
||||||
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);
|
||||||
|
|
||||||
let minX = originalWidth + 1,
|
let minX = originalWidth + 1, maxX = -1, minY = originalHeight + 1, maxY = -1;
|
||||||
maxX = -1,
|
|
||||||
minY = originalHeight + 1,
|
|
||||||
maxY = -1,
|
|
||||||
x = 0,
|
|
||||||
y = 0,
|
|
||||||
currentPixelColorValueIndex;
|
|
||||||
|
|
||||||
for (y = 0; y < originalHeight; y++) {
|
for (let y = 0; y < originalHeight; y++) {
|
||||||
for (x = 0; x < originalWidth; x++) {
|
for (let x = 0; x < originalWidth; x++) {
|
||||||
currentPixelColorValueIndex = (y * originalWidth + x) * 4;
|
let idx = (y * originalWidth + x) * 4;
|
||||||
let currentPixelAlphaValue = imageData.data[currentPixelColorValueIndex + 3];
|
let alpha = imageData.data[idx + 3];
|
||||||
if (currentPixelAlphaValue > 0) {
|
if (alpha > 0) {
|
||||||
if (minX > x) minX = x;
|
if (minX > x) minX = x;
|
||||||
if (maxX < x) maxX = x;
|
if (maxX < x) maxX = x;
|
||||||
if (minY > y) minY = y;
|
if (minY > y) minY = y;
|
||||||
@ -81,14 +73,14 @@ 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 cutImageData = originalCtx.getImageData(minX, minY, croppedWidth, croppedHeight);
|
||||||
|
|
||||||
let croppedCanvas = document.createElement('canvas'),
|
let croppedCanvas = document.createElement('canvas');
|
||||||
croppedCtx = croppedCanvas.getContext('2d');
|
let croppedCtx = croppedCanvas.getContext('2d');
|
||||||
|
|
||||||
croppedCanvas.width = croppedWidth;
|
croppedCanvas.width = croppedWidth;
|
||||||
croppedCanvas.height = croppedHeight;
|
croppedCanvas.height = croppedHeight;
|
||||||
croppedCtx.putImageData(cuttedImageData, 0, 0);
|
croppedCtx.putImageData(cutImageData, 0, 0);
|
||||||
|
|
||||||
return croppedCanvas.toDataURL();
|
return croppedCanvas.toDataURL();
|
||||||
}
|
}
|
||||||
@ -114,10 +106,20 @@ function resizeCanvas() {
|
|||||||
signaturePad.clear();
|
signaturePad.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
new IntersectionObserver((entries, observer) => {
|
const debounce = (fn, delay = 100) => {
|
||||||
|
let timer;
|
||||||
|
return (...args) => {
|
||||||
|
clearTimeout(timer);
|
||||||
|
timer = setTimeout(() => fn(...args), delay);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const debouncedResize = debounce(resizeCanvas, 200);
|
||||||
|
|
||||||
|
new IntersectionObserver((entries) => {
|
||||||
if (entries.some((entry) => entry.intersectionRatio > 0)) {
|
if (entries.some((entry) => entry.intersectionRatio > 0)) {
|
||||||
resizeCanvas();
|
debouncedResize();
|
||||||
}
|
}
|
||||||
}).observe(signaturePadCanvas);
|
}).observe(signaturePadCanvas);
|
||||||
|
|
||||||
new ResizeObserver(resizeCanvas).observe(signaturePadCanvas);
|
new ResizeObserver(debouncedResize).observe(signaturePadCanvas);
|
||||||
|
@ -240,8 +240,8 @@
|
|||||||
window.stirlingPDF.sessionExpired = /*[[#{session.expired}]]*/ '';
|
window.stirlingPDF.sessionExpired = /*[[#{session.expired}]]*/ '';
|
||||||
window.stirlingPDF.refreshPage = /*[[#{session.refreshPage}]]*/ 'Refresh Page';
|
window.stirlingPDF.refreshPage = /*[[#{session.refreshPage}]]*/ 'Refresh Page';
|
||||||
window.stirlingPDF.error = /*[[#{error}]]*/ "Error";
|
window.stirlingPDF.error = /*[[#{error}]]*/ "Error";
|
||||||
window.stirlingPDF.uploadLimit = /*[[${uploadLimit}]]*/ 0;
|
window.stirlingPDF.uploadLimitReadable = /*[[${@uploadLimitService.getReadableUploadLimit()}]]*/ 'Unlimited';
|
||||||
window.stirlingPDF.uploadLimitReadable = /*[[${uploadLimitReadable}]]*/ 'Unlimited';
|
window.stirlingPDF.uploadLimit = /*[[${@uploadLimitService.getUploadLimit()}]]*/ 0;
|
||||||
window.stirlingPDF.uploadLimitExceededSingular = /*[[#{uploadLimitExceededSingular}]]*/ 'is too large. Maximum allowed size is';
|
window.stirlingPDF.uploadLimitExceededSingular = /*[[#{uploadLimitExceededSingular}]]*/ 'is too large. Maximum allowed size is';
|
||||||
window.stirlingPDF.uploadLimitExceededPlural = /*[[#{uploadLimitExceededPlural}]]*/ 'are too large. Maximum allowed size is';
|
window.stirlingPDF.uploadLimitExceededPlural = /*[[#{uploadLimitExceededPlural}]]*/ 'are too large. Maximum allowed size is';
|
||||||
})();
|
})();
|
||||||
@ -292,10 +292,10 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="selected-files flex-wrap"></div>
|
<div class="selected-files flex-wrap"></div>
|
||||||
<div class="text-muted small mt-0 text-end w-100" th:if="${uploadLimit != 0}">
|
<div class="text-muted small mt-0 text-end w-100" th:if="${@uploadLimitService.getUploadLimit() != 0}">
|
||||||
<span th:text="#{uploadLimit}">Maximum file size: </span>
|
<span th:text="#{uploadLimit}">Maximum file size: </span>
|
||||||
<span th:text="${uploadLimitReadable}"></span>
|
<span th:text="${@uploadLimitService.getReadableUploadLimit()}"></span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="progressBarContainer" style="display: none; position: relative;">
|
<div class="progressBarContainer" style="display: none; position: relative;">
|
||||||
<div class="progress" style="height: 1rem;">
|
<div class="progress" style="height: 1rem;">
|
||||||
|
@ -143,7 +143,7 @@
|
|||||||
</a>
|
</a>
|
||||||
<div class="dropdown-menu dropdown-menu-tp" aria-labelledby="languageDropdown">
|
<div class="dropdown-menu dropdown-menu-tp" aria-labelledby="languageDropdown">
|
||||||
<div class="dropdown-menu-wrapper px-xl-2 px-2">
|
<div class="dropdown-menu-wrapper px-xl-2 px-2">
|
||||||
<div class="scrollable-y lang_dropdown-mw scalable-languages-container">
|
<div id="languageSelection" class="scrollable-y lang_dropdown-mw scalable-languages-container">
|
||||||
<th:block th:insert="~{fragments/languages :: langs}"></th:block>
|
<th:block th:insert="~{fragments/languages :: langs}"></th:block>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -143,7 +143,7 @@
|
|||||||
</button>
|
</button>
|
||||||
<div class="dropdown-menu" aria-labelledby="languageDropdown">
|
<div class="dropdown-menu" aria-labelledby="languageDropdown">
|
||||||
<!-- Here's where the fragment will be included -->
|
<!-- Here's where the fragment will be included -->
|
||||||
<div class="scrollable-y">
|
<div id="languageSelection" class="scrollable-y" >
|
||||||
<th:block th:replace="~{fragments/languages :: langs}"></th:block>
|
<th:block th:replace="~{fragments/languages :: langs}"></th:block>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -27,7 +27,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<script type="module" th:src="@{'/pdfjs-legacy/pdf.mjs'}"></script>
|
<script type="module" th:src="@{'/pdfjs-legacy/pdf.mjs'}"></script>
|
||||||
<script type="module" th:src="@{'/js/pages/add-image.js'}"></script>
|
<script type="module" th:src="@{'/js/pages/add-image.js'}"></script>
|
||||||
<div class="tab-group show-on-file-selected">
|
<div class="show-on-file-selected">
|
||||||
<div
|
<div
|
||||||
th:replace="~{fragments/common :: fileSelector(name='image-upload', disableMultipleFiles=false, multipleInputsForSingleRequest=true, accept='image/*', inputText=#{imgPrompt})}">
|
th:replace="~{fragments/common :: fileSelector(name='image-upload', disableMultipleFiles=false, multipleInputsForSingleRequest=true, accept='image/*', inputText=#{imgPrompt})}">
|
||||||
</div>
|
</div>
|
||||||
|
@ -43,13 +43,13 @@
|
|||||||
</div>
|
</div>
|
||||||
<script type="module" th:src="@{'/pdfjs-legacy/pdf.mjs'}"></script>
|
<script type="module" th:src="@{'/pdfjs-legacy/pdf.mjs'}"></script>
|
||||||
<div class="tab-group show-on-file-selected">
|
<div class="tab-group show-on-file-selected">
|
||||||
<div class="tab-container" th:title="#{sign.upload}" th:data-title="#{sign.upload}">
|
<div class="tab-container"th:data-title="#{sign.upload}">
|
||||||
<div
|
<div
|
||||||
th:replace="~{fragments/common :: fileSelector(name='image-upload', disableMultipleFiles=false, multipleInputsForSingleRequest=true, accept='image/*', inputText=#{imgPrompt})}">
|
th:replace="~{fragments/common :: fileSelector(name='image-upload', disableMultipleFiles=false, multipleInputsForSingleRequest=true, accept='image/*', inputText=#{imgPrompt})}">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="tab-container drawing-pad-container" th:title="#{sign.draw}" th:data-title="#{sign.draw}">
|
<div class="tab-container drawing-pad-container" th:data-title="#{sign.draw}">
|
||||||
<canvas id="drawing-pad-canvas"></canvas>
|
<canvas id="drawing-pad-canvas"></canvas>
|
||||||
<br>
|
<br>
|
||||||
<button id="clear-signature" class="btn btn-outline-danger mt-2" onclick="signaturePad.clear()"
|
<button id="clear-signature" class="btn btn-outline-danger mt-2" onclick="signaturePad.clear()"
|
||||||
@ -62,7 +62,7 @@
|
|||||||
onclick="redoDraw()"></button>
|
onclick="redoDraw()"></button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="tab-container" th:title="#{sign.saved}" th:data-title="#{sign.saved}">
|
<div class="tab-container" th:data-title="#{sign.saved}">
|
||||||
<div class="saved-signatures-section" th:if="${not #lists.isEmpty(signatures)}">
|
<div class="saved-signatures-section" th:if="${not #lists.isEmpty(signatures)}">
|
||||||
|
|
||||||
<!-- Preview Modal -->
|
<!-- Preview Modal -->
|
||||||
@ -134,7 +134,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="tab-container" th:title="#{sign.text}" th:data-title="#{sign.text}">
|
<div class="tab-container" th:data-title="#{sign.text}">
|
||||||
<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>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user