Added zip file feature to fork

This commit is contained in:
ryantang 2025-02-27 13:21:07 +08:00
parent 4c701b2e69
commit 2d68876be1
98 changed files with 2799 additions and 2364 deletions

View File

@ -1343,6 +1343,7 @@ fileChooser.dragAndDrop=قم بسحب الملفات وإفلاتها
fileChooser.dragAndDropPDF=Drag & Drop PDF file
fileChooser.dragAndDropImage=Drag & Drop Image file
fileChooser.hoveredDragAndDrop=قم بسحب المفات وإفلاتها هنا
fileChooser.extractPDF=جاري الاستخراج...
#release notes
releases.footer=Releases

View File

@ -1343,6 +1343,7 @@ fileChooser.dragAndDrop=Drag & Drop
fileChooser.dragAndDropPDF=Drag & Drop PDF file
fileChooser.dragAndDropImage=Drag & Drop Image file
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
fileChooser.extractPDF=Extracting...
#release notes
releases.footer=Buraxılışlar

View File

@ -1343,6 +1343,7 @@ fileChooser.dragAndDrop=Влачете и пуснете
fileChooser.dragAndDropPDF=Влачете и пуснете PDF файл
fileChooser.dragAndDropImage=Влачете и пуснете изображение
fileChooser.hoveredDragAndDrop=Влачете и пуснете файл(ове) тук
fileChooser.extractPDF=Извличане...
#release notes
releases.footer=Версии

View File

@ -1343,6 +1343,7 @@ fileChooser.dragAndDrop=Drag & Drop
fileChooser.dragAndDropPDF=Drag & Drop PDF file
fileChooser.dragAndDropImage=Drag & Drop Image file
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
fileChooser.extractPDF=Extracting...
#release notes
releases.footer=Releases

View File

@ -1343,6 +1343,7 @@ fileChooser.dragAndDrop=Přetáhnout
fileChooser.dragAndDropPDF=Přetáhnout PDF soubor
fileChooser.dragAndDropImage=Přetáhnout obrázek
fileChooser.hoveredDragAndDrop=Přetáhněte soubor(y) sem
fileChooser.extractPDF=Extrahování...
#release notes
releases.footer=Vydání

View File

@ -1343,6 +1343,7 @@ fileChooser.dragAndDrop=Drag & Drop
fileChooser.dragAndDropPDF=Drag & Drop PDF file
fileChooser.dragAndDropImage=Drag & Drop Image file
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
fileChooser.extractPDF=Extracting...
#release notes
releases.footer=Releases

View File

@ -1343,6 +1343,7 @@ fileChooser.dragAndDrop=Drag & Drop
fileChooser.dragAndDropPDF=Drag & Drop PDF-Datei
fileChooser.dragAndDropImage=Drag & Drop Bilddatei
fileChooser.hoveredDragAndDrop=Datei(en) hierhin Ziehen & Fallenlassen
fileChooser.extractPDF=Extrahiere...
#release notes
releases.footer=Veröffentlichungen

View File

@ -1343,6 +1343,7 @@ fileChooser.dragAndDrop=Σύρετε & αφήστε
fileChooser.dragAndDropPDF=Σύρετε & αφήστε αρχείο PDF
fileChooser.dragAndDropImage=Σύρετε & αφήστε αρχείο εικόνας
fileChooser.hoveredDragAndDrop=Σύρετε & αφήστε αρχείο(α) εδώ
fileChooser.extractPDF=Εξαγωγή...
#release notes
releases.footer=Εκδόσεις

View File

@ -1343,6 +1343,7 @@ fileChooser.dragAndDrop=Drag & Drop
fileChooser.dragAndDropPDF=Drag & Drop PDF file
fileChooser.dragAndDropImage=Drag & Drop Image file
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
fileChooser.extractPDF=Extracting...
#release notes
releases.footer=Releases

View File

@ -1343,6 +1343,7 @@ fileChooser.dragAndDrop=Drag & Drop
fileChooser.dragAndDropPDF=Drag & Drop PDF file
fileChooser.dragAndDropImage=Drag & Drop Image file
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
fileChooser.extractPDF=Extracting...
#release notes
releases.footer=Releases

View File

@ -1343,6 +1343,7 @@ fileChooser.dragAndDrop=Arrastrar & Soltar
fileChooser.dragAndDropPDF=Arrastrar & Soltar archivo PDF
fileChooser.dragAndDropImage=Arrastrar & Soltar archivo de Imagen
fileChooser.hoveredDragAndDrop=Arrastrar & Soltar archivos(s) aquí
fileChooser.extractPDF=Extrayendo...
#release notes
releases.footer=Versiones

View File

@ -1343,6 +1343,7 @@ fileChooser.dragAndDrop=Drag & Drop
fileChooser.dragAndDropPDF=Drag & Drop PDF file
fileChooser.dragAndDropImage=Drag & Drop Image file
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
fileChooser.extractPDF=Extracting...
#release notes
releases.footer=Releases

View File

@ -1343,6 +1343,7 @@ fileChooser.dragAndDrop=بکشید و رها کنید
fileChooser.dragAndDropPDF=Drag & Drop PDF file
fileChooser.dragAndDropImage=Drag & Drop Image file
fileChooser.hoveredDragAndDrop=فایل(های) خود را اینجا بکشید و رها کنید
fileChooser.extractPDF=در حال استخراج...
#release notes
releases.footer=نسخه‌ها

View File

@ -1343,6 +1343,7 @@ fileChooser.dragAndDrop=Glisser & Déposer
fileChooser.dragAndDropPDF=Drag & Drop PDF file
fileChooser.dragAndDropImage=Drag & Drop Image file
fileChooser.hoveredDragAndDrop=Glisser & Déposer le(s) fichier(s) ici
fileChooser.extractPDF=Extraction en cours...
#release notes
releases.footer=Versions

View File

@ -1343,6 +1343,7 @@ fileChooser.dragAndDrop=Tarraing & Scaoil
fileChooser.dragAndDropPDF=Tarraing & Scaoil comhad PDF
fileChooser.dragAndDropImage=Tarraing & Scaoil comhad Íomhá
fileChooser.hoveredDragAndDrop=Tarraing agus scaoil comhad(í) anseo
fileChooser.extractPDF=Ag Aistriú...
#release notes
releases.footer=Eisiúintí

View File

@ -1343,6 +1343,7 @@ fileChooser.dragAndDrop=खींचें और छोड़ें
fileChooser.dragAndDropPDF=PDF फ़ाइल खींचें और छोड़ें
fileChooser.dragAndDropImage=छवि फ़ाइल खींचें और छोड़ें
fileChooser.hoveredDragAndDrop=फ़ाइल(ें) यहाँ खींचें और छोड़ें
fileChooser.extractPDF=निकालना...
#release notes
releases.footer=रिलीज़

View File

@ -1343,6 +1343,7 @@ fileChooser.dragAndDrop=Drag & Drop
fileChooser.dragAndDropPDF=Drag & Drop PDF file
fileChooser.dragAndDropImage=Drag & Drop Image file
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
fileChooser.extractPDF=Extracting...
#release notes
releases.footer=Releases

View File

@ -1343,6 +1343,7 @@ fileChooser.dragAndDrop=Húzza ide
fileChooser.dragAndDropPDF=Húzza ide a PDF fájlt
fileChooser.dragAndDropImage=Húzza ide a képfájlt
fileChooser.hoveredDragAndDrop=Húzza ide a fájl(oka)t
fileChooser.extractPDF=Kinyerés...
#release notes
releases.footer=Kiadási jegyzék

View File

@ -1343,6 +1343,7 @@ fileChooser.dragAndDrop=Drag & Drop
fileChooser.dragAndDropPDF=Drag & Drop PDF file
fileChooser.dragAndDropImage=Drag & Drop Image file
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
fileChooser.extractPDF=Extracting...
#release notes
releases.footer=Releases

View File

@ -1343,6 +1343,7 @@ fileChooser.dragAndDrop=Trascina & Rilascia
fileChooser.dragAndDropPDF=Trascina & rilascia il file PDF
fileChooser.dragAndDropImage=Trascina & rilascia il file immagine
fileChooser.hoveredDragAndDrop=Trascina & rilascia i file qui
fileChooser.extractPDF=Estraendo...
#release notes
releases.footer=Rilasci

View File

@ -1343,6 +1343,7 @@ fileChooser.dragAndDrop=ドラッグ&ドロップ
fileChooser.dragAndDropPDF=PDFファイルをドラッグドロップ
fileChooser.dragAndDropImage=画像ファイルをドラッグ&ドロップ
fileChooser.hoveredDragAndDrop=ファイルをここにドラッグ&ドロップ
fileChooser.extractPDF=抽出中...
#release notes
releases.footer=リリース

View File

@ -1343,6 +1343,7 @@ fileChooser.dragAndDrop=드래그 앤 드롭
fileChooser.dragAndDropPDF=PDF 파일을 드래그 앤 드롭
fileChooser.dragAndDropImage=이미지 파일을 드래그 앤 드롭
fileChooser.hoveredDragAndDrop=여기에 파일을 드래그 앤 드롭하세요
fileChooser.extractPDF=추출 중...
#release notes
releases.footer=릴리스

View File

@ -1343,6 +1343,7 @@ fileChooser.dragAndDrop=Drag & Drop
fileChooser.dragAndDropPDF=Drag & Drop PDF file
fileChooser.dragAndDropImage=Drag & Drop Image file
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
fileChooser.hoveredDragAndDrop=Extracting...
#release notes
releases.footer=Releases

View File

@ -1343,6 +1343,7 @@ fileChooser.dragAndDrop=Drag & Drop
fileChooser.dragAndDropPDF=Drag & Drop PDF file
fileChooser.dragAndDropImage=Drag & Drop Image file
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
fileChooser.hoveredDragAndDrop=Extracting...
#release notes
releases.footer=Releases

View File

@ -1343,6 +1343,7 @@ fileChooser.dragAndDrop=Drag & Drop
fileChooser.dragAndDropPDF=Drag & Drop PDF file
fileChooser.dragAndDropImage=Drag & Drop Image file
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
fileChooser.hoveredDragAndDrop=Extracting...
#release notes
releases.footer=Releases

View File

@ -1343,6 +1343,7 @@ fileChooser.dragAndDrop=Arraste & Solte
fileChooser.dragAndDropPDF=Arraste & Solte PDF(s)
fileChooser.dragAndDropImage=Arraste & Solte Imagem(ns)
fileChooser.hoveredDragAndDrop=Arraste & Solte arquivo(s) aqui
fileChooser.extractPDF=Extraindo...
#release notes
releases.footer=Versões

View File

@ -1343,6 +1343,7 @@ fileChooser.dragAndDrop=Arrastar e Largar
fileChooser.dragAndDropPDF=Arrastar e Largar ficheiro PDF
fileChooser.dragAndDropImage=Arrastar e Largar ficheiro de Imagem
fileChooser.hoveredDragAndDrop=Arrastar e Largar ficheiro(s) aqui
fileChooser.extractPDF=Extraindo...
#release notes
releases.footer=Lançamentos

View File

@ -1343,6 +1343,7 @@ fileChooser.dragAndDrop=Drag & Drop
fileChooser.dragAndDropPDF=Drag & Drop PDF file
fileChooser.dragAndDropImage=Drag & Drop Image file
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
fileChooser.extractPDF=Extracting...
#release notes
releases.footer=Releases

View File

@ -1343,6 +1343,7 @@ fileChooser.dragAndDrop=Перетащите
fileChooser.dragAndDropPDF=Перетащите PDF-файл
fileChooser.dragAndDropImage=Перетащите файл изображения
fileChooser.hoveredDragAndDrop=Перетащите файл(ы) сюда
fileChooser.extractPDF=Извлечение...
#release notes
releases.footer=Релизы

View File

@ -1343,6 +1343,7 @@ fileChooser.dragAndDrop=Drag & Drop
fileChooser.dragAndDropPDF=Drag & Drop PDF file
fileChooser.dragAndDropImage=Drag & Drop Image file
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
fileChooser.extractPDF=Extracting...
#release notes
releases.footer=Releases

View File

@ -1343,6 +1343,7 @@ fileChooser.dragAndDrop=Povleci in spusti
fileChooser.dragAndDropPDF=Povleci in spusti datoteko PDF
fileChooser.dragAndDropImage=Povleci in spusti slikovno datoteko
fileChooser.hoveredDragAndDrop=Povleci in spusti datoteko(e) sem
fileChooser.extractPDF=Izvlečenje...
#release notes
releases.footer=Izdaje

View File

@ -1343,6 +1343,8 @@ fileChooser.dragAndDrop=Drag & Drop
fileChooser.dragAndDropPDF=Drag & Drop PDF file
fileChooser.dragAndDropImage=Drag & Drop Image file
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
fileChooser.extractPDF=Extracting...
#release notes
releases.footer=Releases

View File

@ -1343,6 +1343,7 @@ fileChooser.dragAndDrop=Dra & Släpp
fileChooser.dragAndDropPDF=Dra & Släpp PDF fil
fileChooser.dragAndDropImage=Dra & Släpp bildfil
fileChooser.hoveredDragAndDrop=Dra & Släpp fil(er) här
fileChooser.extractPDF=Extraherar...
#release notes
releases.footer=Utgåvor

View File

@ -1343,6 +1343,7 @@ fileChooser.dragAndDrop=Drag & Drop
fileChooser.dragAndDropPDF=Drag & Drop PDF file
fileChooser.dragAndDropImage=Drag & Drop Image file
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
fileChooser.extractPDF=Extracting...
#release notes
releases.footer=Releases

View File

@ -1343,6 +1343,7 @@ fileChooser.dragAndDrop=Drag & Drop
fileChooser.dragAndDropPDF=Drag & Drop PDF file
fileChooser.dragAndDropImage=Drag & Drop Image file
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
fileChooser.extractPDF=Extracting...
#release notes
releases.footer=Releases

View File

@ -1343,6 +1343,7 @@ fileChooser.dragAndDrop=Drag & Drop
fileChooser.dragAndDropPDF=Drag & Drop PDF file
fileChooser.dragAndDropImage=Drag & Drop Image file
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
fileChooser.extractPDF=Extracting...
#release notes
releases.footer=Releases

View File

@ -1343,6 +1343,7 @@ fileChooser.dragAndDrop=Drag & Drop
fileChooser.dragAndDropPDF=Drag & Drop PDF file
fileChooser.dragAndDropImage=Drag & Drop Image file
fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
fileChooser.extractPDF=Extracting...
#release notes
releases.footer=Releases

View File

@ -1343,6 +1343,7 @@ fileChooser.dragAndDrop=འཐེན་ནས་འཇོག་པ།
fileChooser.dragAndDropPDF=PDF ཡིག་ཆ་འཐེན་ནས་འཇོག་པ།
fileChooser.dragAndDropImage=པར་རིས་ཡིག་ཆ་འཐེན་ནས་འཇོག་པ།
fileChooser.hoveredDragAndDrop=ཡིག་ཆ་འདིར་འཐེན་ནས་འཇོག་པ།
fileChooser.extractPDF=འབྱུང་བ།
#release notes
releases.footer=པར་གཞི།

View File

@ -1343,6 +1343,7 @@ fileChooser.dragAndDrop=拖放文件
fileChooser.dragAndDropPDF=拖放PDF文件
fileChooser.dragAndDropImage=拖放图片文件
fileChooser.hoveredDragAndDrop=拖放文件到此处
fileChooser.extractPDF=处理中...
#release notes
releases.footer=版本

View File

@ -1343,6 +1343,7 @@ fileChooser.dragAndDrop=拖放檔案
fileChooser.dragAndDropPDF=拖放 PDF 檔案
fileChooser.dragAndDropImage=拖放圖片檔案
fileChooser.hoveredDragAndDrop=將檔案拖放至此
fileChooser.extractPDF=處理中...
#release notes
releases.footer=版本資訊

View File

@ -1,7 +1,8 @@
import FileIconFactory from './file-icon-factory.js';
import FileUtils from './file-utils.js';
import UUID from './uuid.js';
import {DecryptFile} from './DecryptFiles.js';
import { DecryptFile } from './DecryptFiles.js';
let isScriptExecuted = false;
if (!isScriptExecuted) {
isScriptExecuted = true;
@ -11,6 +12,24 @@ if (!isScriptExecuted) {
}
let hasDroppedImage = false;
const zipTypes = [
'application/zip',
'multipart/x-zip',
'application/zip-compressed',
'application/x-zip-compressed',
];
const mimeTypes = {
"png": "image/png",
"jpg": "image/jpeg",
"jpeg": "image/jpeg",
"gif": "image/gif",
"bmp": "image/bmp",
"svg": "image/svg+xml",
"pdf": "application/pdf",
"zip": "application/zip",
};
function setupFileInput(chooser) {
const elementId = chooser.getAttribute('data-bs-element-id');
const filesSelected = chooser.getAttribute('data-bs-files-selected');
@ -55,6 +74,7 @@ function setupFileInput(chooser) {
overlay = false;
}
const dropListener = function (e) {
e.preventDefault();
// Drag and Drop shall only affect the target file chooser
@ -83,7 +103,7 @@ function setupFileInput(chooser) {
dragCounter = 0;
fileInput.dispatchEvent(new CustomEvent('change', {bubbles: true, detail: {source: 'drag-drop'}}));
fileInput.dispatchEvent(new CustomEvent('change', { bubbles: true, detail: { source: 'drag-drop' } }));
};
function pushFileListTo(fileList, container) {
@ -114,12 +134,34 @@ function setupFileInput(chooser) {
} else {
allFiles = Array.from(isDragAndDrop ? allFiles : [element.files[0]]);
}
// iterate through entries to check for zip files, if there is encryption needed it will passed to next function
async function checkZipFile() {
const originalText = inputContainer.querySelector('#fileInputText').innerHTML;
inputContainer.querySelector('#fileInputText').innerHTML = window.fileInput.extractPDF;
const promises = allFiles.map(async (file, index) => {
if (zipTypes.includes(file.type)) {
await extractZipFiles(file, element.accept);
allFiles.splice(index, 1);
}
});
await Promise.all(promises);
inputContainer.querySelector('#fileInputText').innerHTML = originalText;
}
await checkZipFile();
allFiles = await Promise.all(
allFiles.map(async (file) => {
let decryptedFile = file;
try {
const decryptFile = new DecryptFile();
const {isEncrypted, requiresPassword} = await decryptFile.checkFileEncrypted(file);
const { isEncrypted, requiresPassword } = await decryptFile.checkFileEncrypted(file);
if (file.type === 'application/pdf' && isEncrypted) {
decryptedFile = await decryptFile.decryptFile(file, requiresPassword);
if (!decryptedFile) throw new Error('File decryption failed.');
@ -139,7 +181,7 @@ function setupFileInput(chooser) {
}
handleFileInputChange(this);
this.dispatchEvent(new CustomEvent('file-input-change', {bubbles: true, detail: {elementId, allFiles}}));
this.dispatchEvent(new CustomEvent('file-input-change', { bubbles: true, detail: { elementId, allFiles } }));
});
function toDataTransfer(files) {
@ -147,17 +189,76 @@ function setupFileInput(chooser) {
files.forEach((file) => dataTransfer.items.add(file));
return dataTransfer;
}
async function extractZipFiles(zipFile, acceptedFileType) {
const jszip = new JSZip();
return jszip.loadAsync(zipFile).then(function (zip) {
const extractionPromises = [];
var promise;
zip.forEach(function (relativePath, zipEntry) {
if (zipEntry.name.endsWith('.zip')) {
console.log("Found nested ZIP file: " + zipEntry.name);
promise = zipEntry.async('blob').then(function (content) {
return extractZipFiles(content, acceptedFileType);
});
} else {
promise = zipEntry.async('blob').then(function (content) {
// Assuming that folders has size of zero
if (content.size > 0) {
const extension = zipEntry.name.split('.').pop().toLowerCase();
const mimeType = mimeTypes[extension]
// check for file extension
if (mimeType && (mimeType.startsWith(acceptedFileType.split('/')[0]) || acceptedFileType === mimeType)) {
var file = new File([content], zipEntry.name, {
type: mimeType,
});
file.uniqueId = UUID.uuidv4();
allFiles.push(file);
} else {
console.log(`File ${zipEntry.name} skipped. MIME type (${mimeType}) does not match accepted type (${acceptedFileType})`);
}
}
});
}
extractionPromises.push(promise);
});
return Promise.all(extractionPromises);
}).catch(function (err) {
console.error("Error loading the ZIP file:", err);
throw err;
});
}
function handleFileInputChange(inputElement) {
const files = allFiles;
showOrHideSelectedFilesContainer(files);
const filesInfo = files.map((f) => ({
name: f.name,
size: f.size,
uniqueId: f.uniqueId,
type: f.type,
url: URL.createObjectURL(f),
}));
const filesInfo = files.map((f) => {
const url = URL.createObjectURL(f);
return {
name: f.name,
size: f.size,
uniqueId: f.uniqueId,
type: f.type,
url: url,
};
});
const selectedFilesContainer = $(inputContainer).siblings('.selected-files');
selectedFilesContainer.empty();
@ -171,6 +272,8 @@ function setupFileInput(chooser) {
let fileIconContainer = document.createElement('div');
const isDragAndDropEnabled =
window.location.pathname.includes('add-image') || window.location.pathname.includes('sign');
// add image thumbnail to it
if (info.type.startsWith('image/')) {
let imgPreview = document.createElement('img');
imgPreview.src = info.url;
@ -283,7 +386,7 @@ function setupFileInput(chooser) {
showOrHideSelectedFilesContainer(allFiles);
inputElement.dispatchEvent(new CustomEvent('file-input-change', {bubbles: true}));
inputElement.dispatchEvent(new CustomEvent('file-input-change', { bubbles: true }));
}
function removeFileById(fileId, inputElement) {

View File

@ -26,7 +26,7 @@
<form method="post" enctype="multipart/form-data" th:action="@{'/api/v1/misc/auto-split-pdf'}">
<p th:text="#{autoSplitPDF.formPrompt}"></p>
<div
th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf')}">
th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf , application/zip' )}">
</div>
<div class="form-check ms-3">
<input type="checkbox" name="duplexMode" id="duplexMode">

View File

@ -22,7 +22,7 @@
<form id="imageToPDFForm" method="post" enctype="multipart/form-data"
th:action="@{'/api/v1/convert/img/pdf'}">
<div
th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='image/*', inputText=#{imgPrompt})}">
th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='image/*, application/zip', inputText=#{imgPrompt})}">
</div>
<div class="mb-3">
<label for="fitOption" th:text="#{imageToPDF.selectLabel}">Fit Options</label>

View File

@ -1,45 +1,50 @@
<!DOCTYPE html>
<html th:lang="${#locale.language}" th:dir="#{language.direction}" th:data-language="${#locale.toString()}" xmlns:th="https://www.thymeleaf.org">
<head>
<th:block th:insert="~{fragments/common :: head(title=#{PDFToBook.title}, header=#{PDFToBook.header})}"></th:block>
</head>
<html th:lang="${#locale.language}" th:dir="#{language.direction}" th:data-language="${#locale.toString()}"
xmlns:th="https://www.thymeleaf.org">
<body>
<th:block th:insert="~{fragments/common :: game}"></th:block>
<div id="page-container">
<div id="content-wrap">
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
<br><br>
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6">
<h2 th:text="#{PDFToBook.header}"></h2>
<form method="post" enctype="multipart/form-data" th:action="@{'/api/v1/convert/pdf/book'}">
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf')}"></div>
<div class="mb-3">
<label th:text="#{PDFToBook.selectText.1}"></label>
<select class="form-control" name="outputFormat">
<option value="epub">EPUB</option>
<option value="mobi">MOBI</option>
<option value="azw3">AZW3</option>
<option value="docx">DOCX</option>
<option value="rtf">RTF</option>
<option value="txt">TXT</option>
<option value="html">HTML</option>
<option value="lit">LIT</option>
<option value="fb2">FB2</option>
<option value="pdb">PDB</option>
<option value="lrf">LRF</option>
</select>
</div>
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{PDFToBook.submit}"></button>
</form>
<p class="mt-3" th:text="#{PDFToBook.credit}"></p>
</div>
<head>
<th:block th:insert="~{fragments/common :: head(title=#{PDFToBook.title}, header=#{PDFToBook.header})}"></th:block>
</head>
<body>
<th:block th:insert="~{fragments/common :: game}"></th:block>
<div id="page-container">
<div id="content-wrap">
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
<br><br>
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6">
<h2 th:text="#{PDFToBook.header}"></h2>
<form method="post" enctype="multipart/form-data" th:action="@{'/api/v1/convert/pdf/book'}">
<div
th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf , application/zip' )}">
</div>
<div class="mb-3">
<label th:text="#{PDFToBook.selectText.1}"></label>
<select class="form-control" name="outputFormat">
<option value="epub">EPUB</option>
<option value="mobi">MOBI</option>
<option value="azw3">AZW3</option>
<option value="docx">DOCX</option>
<option value="rtf">RTF</option>
<option value="txt">TXT</option>
<option value="html">HTML</option>
<option value="lit">LIT</option>
<option value="fb2">FB2</option>
<option value="pdb">PDB</option>
<option value="lrf">LRF</option>
</select>
</div>
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{PDFToBook.submit}"></button>
</form>
<p class="mt-3" th:text="#{PDFToBook.credit}"></p>
</div>
</div>
</div>
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
</div>
</body>
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
</div>
</body>
</html>

View File

@ -1,46 +1,58 @@
<!DOCTYPE html>
<html th:lang="${#locale.language}" th:dir="#{language.direction}" th:data-language="${#locale.toString()}" xmlns:th="https://www.thymeleaf.org">
<head>
<html th:lang="${#locale.language}" th:dir="#{language.direction}" th:data-language="${#locale.toString()}"
xmlns:th="https://www.thymeleaf.org">
<head>
<th:block th:insert="~{fragments/common :: head(title=#{PDFToCSV.title}, header=#{PDFToCSV.header})}"></th:block>
</head>
</head>
<body>
<div id="page-container">
<div id="content-wrap">
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
<br><br>
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6 bg-card">
<div class="tool-header">
<span class="material-symbols-rounded tool-header-icon convert">csv</span>
<span class="tool-header-text" th:text="#{PDFToCSV.header}"></span>
</div>
<form id="PDFToCSVForm" th:action="@{'/api/v1/convert/pdf/csv'}" method="post" enctype="multipart/form-data">
<input id="pageNumbers" type="hidden" name="pageNumbers">
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf')}"></div>
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{PDFToCSV.submit}"></button>
</form>
<p id="instruction-text" style="margin: 0; display: none" th:text="#{PDFToCSV.prompt}"></p>
<div style="position: relative; width: auto;" id="canvasesContainer">
<div>
<div style="display:none ;margin: 3px;position: absolute;top: 0;width: 120px;justify-content:space-between;z-index: 10" id="pagination-button-container">
<button id='previous-page-btn' style='opacity: 80% ; width: 50px; height: 30px; display: flex;align-items: center;justify-content: center; background: grey; color: #ffffff; ;border: none;outline: none; border-radius: 4px;'> &lt; </button>
<button id='next-page-btn' style='opacity: 80% ; width: 50px; height: 30px; display: flex;align-items: center;justify-content: center; background: grey; color: #ffffff; ;border: none;outline: none; border-radius: 4px;'> &gt; </button>
</div>
<canvas id="cropPdfCanvas" style="width: 100%"></canvas>
</div>
<canvas id="overlayCanvas" style="position: absolute; top: 0; left: 0; z-index: 2; width: 100%"></canvas>
</div>
<script type="module" th:src="@{'/pdfjs-legacy/pdf.mjs'}"></script>
<script type="module" th:src="@{'/js/pages/pdf-to-csv.js'}">
</script>
<body>
<div id="page-container">
<div id="content-wrap">
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
<br><br>
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6 bg-card">
<div class="tool-header">
<span class="material-symbols-rounded tool-header-icon convert">csv</span>
<span class="tool-header-text" th:text="#{PDFToCSV.header}"></span>
</div>
<form id="PDFToCSVForm" th:action="@{'/api/v1/convert/pdf/csv'}" method="post"
enctype="multipart/form-data">
<input id="pageNumbers" type="hidden" name="pageNumbers">
<div
th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf , application/zip' )}">
</div>
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{PDFToCSV.submit}"></button>
</form>
<p id="instruction-text" style="margin: 0; display: none" th:text="#{PDFToCSV.prompt}"></p>
<div style="position: relative; width: auto;" id="canvasesContainer">
<div>
<div
style="display:none ;margin: 3px;position: absolute;top: 0;width: 120px;justify-content:space-between;z-index: 10"
id="pagination-button-container">
<button id='previous-page-btn'
style='opacity: 80% ; width: 50px; height: 30px; display: flex;align-items: center;justify-content: center; background: grey; color: #ffffff; ;border: none;outline: none; border-radius: 4px;'>
&lt; </button>
<button id='next-page-btn'
style='opacity: 80% ; width: 50px; height: 30px; display: flex;align-items: center;justify-content: center; background: grey; color: #ffffff; ;border: none;outline: none; border-radius: 4px;'>
&gt; </button>
</div>
<canvas id="cropPdfCanvas" style="width: 100%"></canvas>
</div>
<canvas id="overlayCanvas" style="position: absolute; top: 0; left: 0; z-index: 2; width: 100%"></canvas>
</div>
<script type="module" th:src="@{'/pdfjs-legacy/pdf.mjs'}"></script>
<script type="module" th:src="@{'/js/pages/pdf-to-csv.js'}">
</script>
</div>
</div>
</div>
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
</div>
</body>
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
</div>
</body>
</html>

View File

@ -1,33 +1,38 @@
<!DOCTYPE html>
<html th:lang="${#locale.language}" th:dir="#{language.direction}" th:data-language="${#locale.toString()}" xmlns:th="https://www.thymeleaf.org">
<head>
<th:block th:insert="~{fragments/common :: head(title=#{PDFToHTML.title}, header=#{PDFToHTML.header})}"></th:block>
</head>
<html th:lang="${#locale.language}" th:dir="#{language.direction}" th:data-language="${#locale.toString()}"
xmlns:th="https://www.thymeleaf.org">
<body>
<th:block th:insert="~{fragments/common :: game}"></th:block>
<div id="page-container">
<div id="content-wrap">
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
<br><br>
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6 bg-card">
<div class="tool-header">
<span class="material-symbols-rounded tool-header-icon convert">html</span>
<span class="tool-header-text" th:text="#{PDFToHTML.header}"></span>
</div>
<form method="post" enctype="multipart/form-data" th:action="@{'/api/v1/convert/pdf/html'}">
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf')}"></div>
<br>
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{PDFToHTML.submit}"></button>
</form>
<p class="mt-3" th:text="#{PDFToHTML.credit}"></p>
<head>
<th:block th:insert="~{fragments/common :: head(title=#{PDFToHTML.title}, header=#{PDFToHTML.header})}"></th:block>
</head>
<body>
<th:block th:insert="~{fragments/common :: game}"></th:block>
<div id="page-container">
<div id="content-wrap">
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
<br><br>
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6 bg-card">
<div class="tool-header">
<span class="material-symbols-rounded tool-header-icon convert">html</span>
<span class="tool-header-text" th:text="#{PDFToHTML.header}"></span>
</div>
<form method="post" enctype="multipart/form-data" th:action="@{'/api/v1/convert/pdf/html'}">
<div
th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf , application/zip' )}">
</div>
<br>
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{PDFToHTML.submit}"></button>
</form>
<p class="mt-3" th:text="#{PDFToHTML.credit}"></p>
</div>
</div>
</div>
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
</div>
</body>
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
</div>
</body>
</html>

View File

@ -24,7 +24,7 @@
</p>
<form method="post" enctype="multipart/form-data" th:action="@{'/api/v1/convert/pdf/img'}">
<div
th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf')}">
th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf , application/zip' )}">
</div>
<div class="mb-3">
<label th:text="#{pdfToImage.selectText}"></label>

View File

@ -1,78 +1,83 @@
<!DOCTYPE html>
<html th:lang="${#locale.language}" th:dir="#{language.direction}" th:data-language="${#locale.toString()}" xmlns:th="https://www.thymeleaf.org">
<head>
<html th:lang="${#locale.language}" th:dir="#{language.direction}" th:data-language="${#locale.toString()}"
xmlns:th="https://www.thymeleaf.org">
<head>
<th:block th:insert="~{fragments/common :: head(title=#{pdfToPDFA.title}, header=#{pdfToPDFA.header})}"></th:block>
</head>
</head>
<body>
<th:block th:insert="~{fragments/common :: game}"></th:block>
<div id="page-container">
<div id="content-wrap">
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
<br><br>
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6 bg-card">
<div class="tool-header">
<span class="material-symbols-rounded tool-header-icon convert">picture_as_pdf</span>
<span class="tool-header-text" th:text="#{pdfToPDFA.header}"></span>
</div>
<p th:text="#{pdfToPDFA.tip}"></p>
<form method="post" enctype="multipart/form-data" th:action="@{'/api/v1/convert/pdf/pdfa'}">
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf')}"></div>
<div class="mb-3">
<label for="outputFormat" th:text="#{pdfToPDFA.outputFormat}"></label>
<select class="form-control" name="outputFormat" id="outputFormat">
<option value="pdfa-1">PDF/A-1b</option>
<option value="pdfa">PDF/A-2b</option>
</select>
</div>
<div id="result" class="alert-warning"></div>
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{pdfToPDFA.submit}"></button>
</form>
<script type="module" th:src="@{'/pdfjs-legacy/pdf.mjs'}"></script>
<script th:inline="javascript">
document.getElementById('fileInput-input').addEventListener('change', async () => {
pdfjsLib.GlobalWorkerOptions.workerSrc = './pdfjs-legacy/pdf.worker.mjs';
const fileInput = document.getElementById('fileInput-input');
const resultDiv = document.getElementById('result');
const file = fileInput.files[0];
const arrayBuffer = await file.arrayBuffer();
try {
const pdf = await pdfjsLib.getDocument({ data: arrayBuffer }).promise;
let hasSignature = false;
for (let i = 1; i <= pdf.numPages; i++) {
const page = await pdf.getPage(i);
const annotations = await page.getAnnotations({ intent: 'display' });
annotations.forEach(annotation => {
console.log(annotation)
if (annotation.subtype === 'Widget' && annotation.fieldType === 'Sig') {
hasSignature = true;
}
});
}
if (hasSignature) {
/*<![CDATA[*/
resultDiv.textContent = /*[[#{pdfToPDFA.pdfWithDigitalSignature}]]*/ "The PDF contains a digital signature. This will be removed in the next step.";
/*]]>*/
}
} catch (error) {
resultDiv.textContent = 'Error reading the PDF: ' + error.message;
}
});
</script>
<p class="mt-3" th:text="#{pdfToPDFA.credit}"></p>
<body>
<th:block th:insert="~{fragments/common :: game}"></th:block>
<div id="page-container">
<div id="content-wrap">
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
<br><br>
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6 bg-card">
<div class="tool-header">
<span class="material-symbols-rounded tool-header-icon convert">picture_as_pdf</span>
<span class="tool-header-text" th:text="#{pdfToPDFA.header}"></span>
</div>
<p th:text="#{pdfToPDFA.tip}"></p>
<form method="post" enctype="multipart/form-data" th:action="@{'/api/v1/convert/pdf/pdfa'}">
<div
th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf , application/zip' )}">
</div>
<div class="mb-3">
<label for="outputFormat" th:text="#{pdfToPDFA.outputFormat}"></label>
<select class="form-control" name="outputFormat" id="outputFormat">
<option value="pdfa-1">PDF/A-1b</option>
<option value="pdfa">PDF/A-2b</option>
</select>
</div>
<div id="result" class="alert-warning"></div>
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{pdfToPDFA.submit}"></button>
</form>
<script type="module" th:src="@{'/pdfjs-legacy/pdf.mjs'}"></script>
<script th:inline="javascript">
document.getElementById('fileInput-input').addEventListener('change', async () => {
pdfjsLib.GlobalWorkerOptions.workerSrc = './pdfjs-legacy/pdf.worker.mjs';
const fileInput = document.getElementById('fileInput-input');
const resultDiv = document.getElementById('result');
const file = fileInput.files[0];
const arrayBuffer = await file.arrayBuffer();
try {
const pdf = await pdfjsLib.getDocument({ data: arrayBuffer }).promise;
let hasSignature = false;
for (let i = 1; i <= pdf.numPages; i++) {
const page = await pdf.getPage(i);
const annotations = await page.getAnnotations({ intent: 'display' });
annotations.forEach(annotation => {
console.log(annotation)
if (annotation.subtype === 'Widget' && annotation.fieldType === 'Sig') {
hasSignature = true;
}
});
}
if (hasSignature) {
/*<![CDATA[*/
resultDiv.textContent = /*[[#{pdfToPDFA.pdfWithDigitalSignature}]]*/ "The PDF contains a digital signature. This will be removed in the next step.";
/*]]>*/
}
} catch (error) {
resultDiv.textContent = 'Error reading the PDF: ' + error.message;
}
});
</script>
<p class="mt-3" th:text="#{pdfToPDFA.credit}"></p>
</div>
</div>
</div>
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
</div>
</body>
</html>
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
</div>
</body>
</html>

View File

@ -23,7 +23,7 @@
</div>
<form method="post" enctype="multipart/form-data" th:action="@{'/api/v1/convert/pdf/presentation'}">
<div
th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf')}">
th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf , application/zip' )}">
</div>
<div class="mb-3">
<label th:text="#{PDFToPresentation.selectText.1}"></label>

View File

@ -1,39 +1,45 @@
<!DOCTYPE html>
<html th:lang="${#locale.language}" th:dir="#{language.direction}" th:data-language="${#locale.toString()}" xmlns:th="https://www.thymeleaf.org">
<head>
<th:block th:insert="~{fragments/common :: head(title=#{PDFToText.title}, header=#{PDFToText.header})}"></th:block>
</head>
<html th:lang="${#locale.language}" th:dir="#{language.direction}" th:data-language="${#locale.toString()}"
xmlns:th="https://www.thymeleaf.org">
<body>
<th:block th:insert="~{fragments/common :: game}"></th:block>
<div id="page-container">
<div id="content-wrap">
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
<br><br>
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6 bg-card">
<div class="tool-header">
<span class="material-symbols-rounded tool-header-icon convert">text_fields</span>
<span class="tool-header-text" th:text="#{PDFToText.header}"></span>
</div>
<form method="post" enctype="multipart/form-data" th:action="@{'/api/v1/convert/pdf/text'}">
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf')}"></div>
<div class="mb-3">
<label th:text="#{PDFToText.selectText.1}"></label>
<select class="form-control" name="outputFormat">
<option th:if="${@endpointConfiguration.isEndpointEnabled('pdf-to-rtf')}" value="rtf">RTF</option>
<option value="txt">TXT</option>
</select>
</div>
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{PDFToText.submit}"></button>
</form>
<p th:if="${@endpointConfiguration.isEndpointEnabled('pdf-to-rtf')}" class="mt-3" th:text="#{PDFToText.credit}"></p>
<head>
<th:block th:insert="~{fragments/common :: head(title=#{PDFToText.title}, header=#{PDFToText.header})}"></th:block>
</head>
<body>
<th:block th:insert="~{fragments/common :: game}"></th:block>
<div id="page-container">
<div id="content-wrap">
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
<br><br>
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6 bg-card">
<div class="tool-header">
<span class="material-symbols-rounded tool-header-icon convert">text_fields</span>
<span class="tool-header-text" th:text="#{PDFToText.header}"></span>
</div>
<form method="post" enctype="multipart/form-data" th:action="@{'/api/v1/convert/pdf/text'}">
<div
th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf , application/zip' )}">
</div>
<div class="mb-3">
<label th:text="#{PDFToText.selectText.1}"></label>
<select class="form-control" name="outputFormat">
<option th:if="${@endpointConfiguration.isEndpointEnabled('pdf-to-rtf')}" value="rtf">RTF</option>
<option value="txt">TXT</option>
</select>
</div>
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{PDFToText.submit}"></button>
</form>
<p th:if="${@endpointConfiguration.isEndpointEnabled('pdf-to-rtf')}" class="mt-3"
th:text="#{PDFToText.credit}"></p>
</div>
</div>
</div>
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
</div>
</body>
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
</div>
</body>
</html>

View File

@ -21,7 +21,7 @@
</div>
<form method="post" enctype="multipart/form-data" th:action="@{'/api/v1/convert/pdf/word'}">
<div
th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf')}">
th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf , application/zip' )}">
</div>
<div class="mb-3">
<label th:text="#{PDFToWord.selectText.1}"></label>

View File

@ -1,33 +1,38 @@
<!DOCTYPE html>
<html th:lang="${#locale.language}" th:dir="#{language.direction}" th:data-language="${#locale.toString()}" xmlns:th="https://www.thymeleaf.org">
<head>
<th:block th:insert="~{fragments/common :: head(title=#{PDFToXML.title}, header=#{PDFToXML.header})}"></th:block>
</head>
<html th:lang="${#locale.language}" th:dir="#{language.direction}" th:data-language="${#locale.toString()}"
xmlns:th="https://www.thymeleaf.org">
<body>
<th:block th:insert="~{fragments/common :: game}"></th:block>
<div id="page-container">
<div id="content-wrap">
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
<br><br>
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6 bg-card">
<div class="tool-header">
<span class="material-symbols-rounded tool-header-icon convert">code</span>
<span class="tool-header-text" th:text="#{PDFToXML.header}"></span>
</div>
<form method="post" enctype="multipart/form-data" th:action="@{'/api/v1/convert/pdf/xml'}">
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf')}"></div>
<br>
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{PDFToXML.submit}"></button>
</form>
<p class="mt-3" th:text="#{PDFToXML.credit}"></p>
<head>
<th:block th:insert="~{fragments/common :: head(title=#{PDFToXML.title}, header=#{PDFToXML.header})}"></th:block>
</head>
<body>
<th:block th:insert="~{fragments/common :: game}"></th:block>
<div id="page-container">
<div id="content-wrap">
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
<br><br>
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6 bg-card">
<div class="tool-header">
<span class="material-symbols-rounded tool-header-icon convert">code</span>
<span class="tool-header-text" th:text="#{PDFToXML.header}"></span>
</div>
<form method="post" enctype="multipart/form-data" th:action="@{'/api/v1/convert/pdf/xml'}">
<div
th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf , application/zip' )}">
</div>
<br>
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{PDFToXML.submit}"></button>
</form>
<p class="mt-3" th:text="#{PDFToXML.credit}"></p>
</div>
</div>
</div>
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
</div>
</body>
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
</div>
</body>
</html>

View File

@ -1,41 +1,46 @@
<!DOCTYPE html>
<html th:lang="${#locale.language}" th:dir="#{language.direction}" th:data-language="${#locale.toString()}" xmlns:th="https://www.thymeleaf.org">
<head>
<th:block th:insert="~{fragments/common :: head(title=#{crop.title}, header=#{crop.header})}"></th:block>
</head>
<html th:lang="${#locale.language}" th:dir="#{language.direction}" th:data-language="${#locale.toString()}"
xmlns:th="https://www.thymeleaf.org">
<body>
<div id="page-container">
<div id="content-wrap">
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
<br><br>
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6 bg-card">
<div class="tool-header">
<span class="material-symbols-rounded tool-header-icon organize">crop</span>
<span class="tool-header-text" th:text="#{crop.header}"></span>
</div>
<form id="cropForm" th:action="@{'/api/v1/general/crop'}" method="post" enctype="multipart/form-data">
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf')}"></div>
<input id="x" type="hidden" name="x">
<input id="y" type="hidden" name="y">
<input id="width" type="hidden" name="width">
<input id="height" type="hidden" name="height">
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{crop.submit}"></button>
</form>
<div id="canvasesContainer" style="position: relative; margin: 20px 0; width: auto;">
<canvas id="cropPdfCanvas" style="width: 100%"></canvas>
<canvas id="overlayCanvas" style="position: absolute; top: 0; left: 0; z-index: 2; width: 100%"></canvas>
</div>
<head>
<th:block th:insert="~{fragments/common :: head(title=#{crop.title}, header=#{crop.header})}"></th:block>
</head>
<body>
<div id="page-container">
<div id="content-wrap">
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
<br><br>
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6 bg-card">
<div class="tool-header">
<span class="material-symbols-rounded tool-header-icon organize">crop</span>
<span class="tool-header-text" th:text="#{crop.header}"></span>
</div>
<form id="cropForm" th:action="@{'/api/v1/general/crop'}" method="post" enctype="multipart/form-data">
<div
th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf , application/zip' )}">
</div>
<input id="x" type="hidden" name="x">
<input id="y" type="hidden" name="y">
<input id="width" type="hidden" name="width">
<input id="height" type="hidden" name="height">
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{crop.submit}"></button>
</form>
<div id="canvasesContainer" style="position: relative; margin: 20px 0; width: auto;">
<canvas id="cropPdfCanvas" style="width: 100%"></canvas>
<canvas id="overlayCanvas" style="position: absolute; top: 0; left: 0; z-index: 2; width: 100%"></canvas>
</div>
</div>
<script type="module" th:src="@{'/pdfjs-legacy/pdf.mjs'}"></script>
<script type="module" th:src="@{'/js/pages/crop.js'}"></script>
</div>
<script type="module" th:src="@{'/pdfjs-legacy/pdf.mjs'}"></script>
<script type="module" th:src="@{'/js/pages/crop.js'}"></script>
</div>
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
</div>
</body>
</html>
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
</div>
</body>
</html>

View File

@ -22,7 +22,7 @@
</div>
<form th:action="@{'/api/v1/general/rearrange-pages'}" method="post" enctype="multipart/form-data">
<div
th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf')}">
th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf , application/zip' )}">
</div>
<input type="hidden" id="customMode" name="customMode" value="">
<div class="mb-3">

View File

@ -219,7 +219,8 @@
};
window.fileInput = {
dragAndDropPDF: '[[#{fileChooser.dragAndDropPDF}]]',
dragAndDropImage: '[[#{fileChooser.dragAndDropImage}]]'
dragAndDropImage: '[[#{fileChooser.dragAndDropImage}]]',
extractPDF: '[[#{fileChooser.extractPDF}]]'
};</script>
<div class="custom-file-chooser mb-3"
th:attr="data-bs-unique-id=${name}, data-bs-element-id=${name+'-input'}, data-bs-element-container-id=${name+'-input-container'}, data-bs-files-selected=#{filesSelected}, data-bs-pdf-prompt=#{pdfPrompt}">
@ -230,9 +231,11 @@
th:attr="multiple=${!disableMultipleFiles}" th:required="${notRequired} ? null : 'required'">
Browse
</label>
<div th:text="#{fileChooser.click}"></div>
<div th:text="#{fileChooser.or}"></div>
<div th:text="#{fileChooser.dragAndDrop}" id="dragAndDrop"></div>
<div class="d-flex justify-content-start align-items-center" id="fileInputText">
<div th:text="#{fileChooser.click}" style="margin-right: 5px"></div>
<div th:text="#{fileChooser.or}" style="margin-right: 5px"></div>
<div th:text="#{fileChooser.dragAndDrop}" id="dragAndDrop"></div>
</div>
</div>
<div class="selected-files flex-wrap"></div>
</div>

View File

@ -24,7 +24,7 @@
<div class="mb-3">
<label th:text="#{multiPdfDropPrompt}" for="fileInput-input"></label>
<div
th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=true, accept='application/pdf')}">
th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=true, accept='application/pdf , application/zip')}">
</div>
</div>
<div class="mb-3">

View File

@ -23,13 +23,13 @@
<!-- pdf selector -->
<div
th:replace="~{fragments/common :: fileSelector(name='pdf-upload', disableMultipleFiles=true, multipleInputsForSingleRequest=false, accept='application/pdf')}">
th:replace="~{fragments/common :: fileSelector(name='pdf-upload', disableMultipleFiles=true, multipleInputsForSingleRequest=false, accept='application/pdf , application/zip')}">
</div>
<script type="module" th:src="@{'/pdfjs-legacy/pdf.mjs'}"></script>
<script type="module" th:src="@{'/js/pages/add-image.js'}"></script>
<div class="tab-group show-on-file-selected">
<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/*, application/zip', inputText=#{imgPrompt})}">
</div>
</div>

View File

@ -1,7 +1,10 @@
<!DOCTYPE html>
<html th:lang="${#locale.language}" th:dir="#{language.direction}" th:data-language="${#locale.toString()}" xmlns:th="https://www.thymeleaf.org">
<html th:lang="${#locale.language}" th:dir="#{language.direction}" th:data-language="${#locale.toString()}"
xmlns:th="https://www.thymeleaf.org">
<head>
<th:block th:insert="~{fragments/common :: head(title=#{addPageNumbers.title}, header=#{addPageNumbers.header})}"></th:block>
<th:block th:insert="~{fragments/common :: head(title=#{addPageNumbers.title}, header=#{addPageNumbers.header})}">
</th:block>
<style>
.a4container {
position: relative;
@ -48,105 +51,111 @@
</head>
<body>
<th:block th:insert="~{fragments/common :: game}"></th:block>
<div id="page-container">
<div id="content-wrap">
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
<br><br>
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6 bg-card">
<div class="tool-header">
<span class="material-symbols-rounded tool-header-icon other">123</span>
<span class="tool-header-text" th:text="#{addPageNumbers.header}"></span>
</div>
<form method="post" enctype="multipart/form-data" th:action="@{'/api/v1/misc/add-page-numbers'}">
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf')}"></div>
<br>
<div class="mb-3">
<label for="customMargin" th:text="#{addPageNumbers.selectText.2}"></label>
<select class="form-control" id="customMargin" name="customMargin">
<option value="small" th:text="#{sizes.small}"></option>
<option value="medium" selected th:text="#{sizes.medium}"></option>
<option value="large" th:text="#{sizes.large}"></option>
<option value="x-large" th:text="#{sizes.x-large}"></option>
</select>
<th:block th:insert="~{fragments/common :: game}"></th:block>
<div id="page-container">
<div id="content-wrap">
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
<br><br>
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6 bg-card">
<div class="tool-header">
<span class="material-symbols-rounded tool-header-icon other">123</span>
<span class="tool-header-text" th:text="#{addPageNumbers.header}"></span>
</div>
<div class="mb-3">
<label th:text="#{addPageNumbers.selectText.3}"></label>
<div class="a4container">
<div class="pageNumber" id="1" style="top: 10%; left: 10%;">1</div>
<div class="pageNumber" id="2" style="top: 10%; left: 50%;">2</div>
<div class="pageNumber" id="3" style="top: 10%; left: 90%;">3</div>
<div class="pageNumber" id="4" style="top: 50%; left: 10%;">4</div>
<div class="pageNumber" id="5" style="top: 50%; left: 50%;">5</div>
<div class="pageNumber" id="6" style="top: 50%; left: 90%;">6</div>
<div class="pageNumber" id="7" style="top: 90%; left: 10%;">7</div>
<div class="pageNumber selectedPosition" id="8" style="top: 90%; left: 50%;">8</div>
<div class="pageNumber" id="9" style="top: 90%; left: 90%;">9</div>
<form method="post" enctype="multipart/form-data" th:action="@{'/api/v1/misc/add-page-numbers'}">
<div
th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf , application/zip' )}">
</div>
</div>
<input type="hidden" id="numberInput" name="position" value="8">
<div class="mb-3">
<label for="fontSize" th:text="#{addPageNumbers.fontSize}"></label>
<input type="number" class="form-control" id="fontSize" name="fontSize" min="1" value="12" required>
</div>
<div class="mb-3">
<label for="fontType" th:text="#{addPageNumbers.fontName}"></label>
<select class="form-control" id="fontType" name="fontType">
<option value="Times">Times Roman</option>
<option value="Helvetica">Helvetica</option>
<option value="Courier">Courier New</option>
</select>
</div>
<div class="mb-3">
<label for="startingNumber" th:text="#{addPageNumbers.selectText.4}"></label>
<input type="number" class="form-control" id="startingNumber" name="startingNumber" min="1" required value="1">
</div>
<div class="mb-3">
<label for="pagesToNumber" th:text="#{addPageNumbers.selectText.5}"></label>
<input type="text" class="form-control" id="pagesToNumber" name="pagesToNumber" th:placeholder="#{addPageNumbers.numberPagesDesc}">
</div>
<div class="mb-3">
<label for="customText" th:text="#{addPageNumbers.selectText.6}"></label>
<input type="text" class="form-control" id="customText" name="customText" th:placeholder="#{addPageNumbers.customNumberDesc}">
</div>
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{addPageNumbers.submit}"></button>
</form>
<br>
<div class="mb-3">
<label for="customMargin" th:text="#{addPageNumbers.selectText.2}"></label>
<select class="form-control" id="customMargin" name="customMargin">
<option value="small" th:text="#{sizes.small}"></option>
<option value="medium" selected th:text="#{sizes.medium}"></option>
<option value="large" th:text="#{sizes.large}"></option>
<option value="x-large" th:text="#{sizes.x-large}"></option>
</select>
</div>
<div class="mb-3">
<label th:text="#{addPageNumbers.selectText.3}"></label>
<div class="a4container">
<div class="pageNumber" id="1" style="top: 10%; left: 10%;">1</div>
<div class="pageNumber" id="2" style="top: 10%; left: 50%;">2</div>
<div class="pageNumber" id="3" style="top: 10%; left: 90%;">3</div>
<div class="pageNumber" id="4" style="top: 50%; left: 10%;">4</div>
<div class="pageNumber" id="5" style="top: 50%; left: 50%;">5</div>
<div class="pageNumber" id="6" style="top: 50%; left: 90%;">6</div>
<div class="pageNumber" id="7" style="top: 90%; left: 10%;">7</div>
<div class="pageNumber selectedPosition" id="8" style="top: 90%; left: 50%;">8</div>
<div class="pageNumber" id="9" style="top: 90%; left: 90%;">9</div>
</div>
</div>
<input type="hidden" id="numberInput" name="position" value="8">
<div class="mb-3">
<label for="fontSize" th:text="#{addPageNumbers.fontSize}"></label>
<input type="number" class="form-control" id="fontSize" name="fontSize" min="1" value="12" required>
</div>
<div class="mb-3">
<label for="fontType" th:text="#{addPageNumbers.fontName}"></label>
<select class="form-control" id="fontType" name="fontType">
<option value="Times">Times Roman</option>
<option value="Helvetica">Helvetica</option>
<option value="Courier">Courier New</option>
</select>
</div>
<div class="mb-3">
<label for="startingNumber" th:text="#{addPageNumbers.selectText.4}"></label>
<input type="number" class="form-control" id="startingNumber" name="startingNumber" min="1" required
value="1">
</div>
<div class="mb-3">
<label for="pagesToNumber" th:text="#{addPageNumbers.selectText.5}"></label>
<input type="text" class="form-control" id="pagesToNumber" name="pagesToNumber"
th:placeholder="#{addPageNumbers.numberPagesDesc}">
</div>
<div class="mb-3">
<label for="customText" th:text="#{addPageNumbers.selectText.6}"></label>
<input type="text" class="form-control" id="customText" name="customText"
th:placeholder="#{addPageNumbers.customNumberDesc}">
</div>
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{addPageNumbers.submit}"></button>
</form>
</div>
</div>
</div>
</div>
<script>
let cells = document.querySelectorAll('.pageNumber');
let inputField = document.getElementById('numberInput');
<script>
let cells = document.querySelectorAll('.pageNumber');
let inputField = document.getElementById('numberInput');
cells.forEach(cell => {
cell.addEventListener('click', function(e) {
cells.forEach(cell => {
cell.classList.remove('selectedPosition'); // Remove selected class from all cells
cell.classList.remove('selectedHovered'); // Also remove selectedHovered class
cells.forEach(cell => {
cell.addEventListener('click', function (e) {
cells.forEach(cell => {
cell.classList.remove('selectedPosition'); // Remove selected class from all cells
cell.classList.remove('selectedHovered'); // Also remove selectedHovered class
});
let selectedLocation = e.target.id;
inputField.value = selectedLocation;
e.target.classList.add('selectedPosition'); // Add selected class to clicked cell
e.target.classList.add('selectedHovered'); // Add selectedHovered class
});
let selectedLocation = e.target.id;
inputField.value = selectedLocation;
e.target.classList.add('selectedPosition'); // Add selected class to clicked cell
e.target.classList.add('selectedHovered'); // Add selectedHovered class
});
cell.addEventListener('mouseenter', function(e) {
if(e.target.classList.contains('selectedPosition')) {
e.target.classList.add('selectedHovered');
}
});
cell.addEventListener('mouseenter', function (e) {
if (e.target.classList.contains('selectedPosition')) {
e.target.classList.add('selectedHovered');
}
});
cell.addEventListener('mouseleave', function(e) {
if(e.target.classList.contains('selectedPosition')) {
e.target.classList.remove('selectedHovered');
}
cell.addEventListener('mouseleave', function (e) {
if (e.target.classList.contains('selectedPosition')) {
e.target.classList.remove('selectedHovered');
}
});
});
});
</script>
</script>
</div>
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
</div>
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
</div>
</body>
</html>
</html>

View File

@ -1,27 +1,32 @@
<!DOCTYPE html>
<html th:lang="${#locale.language}" th:dir="#{language.direction}" th:data-language="${#locale.toString()}" xmlns:th="https://www.thymeleaf.org">
<head>
<th:block th:insert="~{fragments/common :: head(title=#{adjustContrast.title}, header=#{adjustContrast.header})}"></th:block>
<style>
#flex-container {
display: flex;
align-items: center;
}
#sliders-container {
padding: 0 20px; /* Add some padding to separate sliders from canvas */
}
</style>
</head>
<html th:lang="${#locale.language}" th:dir="#{language.direction}" th:data-language="${#locale.toString()}"
xmlns:th="https://www.thymeleaf.org">
<body>
<div id="page-container">
<div id="content-wrap">
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
<br><br>
<div class="container">
<div class="row justify-content-center">
<div class="col-md-12 bg-card">
<form th:action="@{''}">
<head>
<th:block th:insert="~{fragments/common :: head(title=#{adjustContrast.title}, header=#{adjustContrast.header})}">
</th:block>
<style>
#flex-container {
display: flex;
align-items: center;
}
#sliders-container {
padding: 0 20px;
/* Add some padding to separate sliders from canvas */
}
</style>
</head>
<body>
<div id="page-container">
<div id="content-wrap">
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
<br><br>
<div class="container">
<div class="row justify-content-center">
<div class="col-md-12 bg-card">
<form th:action="@{''}">
<div class="row justify-content-center">
<div class="col-md-3">
@ -42,24 +47,27 @@
<span class="tool-header-text" th:text="#{adjustContrast.header}"></span>
</div>
<div class="col-md-8">
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf', remoteCall='false')}"></div>
<div
th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf , application/zip', remoteCall='false')}">
</div>
</div>
<br>
<canvas id="contrast-pdf-canvas"></canvas>
<div class="mb-3 text-left">
<button id="download-button" class="btn btn-primary" th:text="#{adjustContrast.download}"></button>
<button id="download-button" class="btn btn-primary" th:text="#{adjustContrast.download}"></button>
</div>
</div>
</div>
</form>
<script type="module" th:src="@{'/pdfjs-legacy/pdf.mjs'}"></script>
<script type="module" th:src="@{'/js/pages/adjust-contrast.js'}"></script>
</div>
</form>
<script type="module" th:src="@{'/pdfjs-legacy/pdf.mjs'}"></script>
<script type="module" th:src="@{'/js/pages/adjust-contrast.js'}"></script>
</div>
</div>
</div>
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
</div>
</body>
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
</div>
</body>
</html>

View File

@ -1,33 +1,38 @@
<!DOCTYPE html>
<html th:lang="${#locale.language}" th:dir="#{language.direction}" th:data-language="${#locale.toString()}" xmlns:th="https://www.thymeleaf.org">
<head>
<th:block th:insert="~{fragments/common :: head(title=#{autoCrop.title}, header=#{autoCrop.header})}"></th:block>
</head>
<html th:lang="${#locale.language}" th:dir="#{language.direction}" th:data-language="${#locale.toString()}"
xmlns:th="https://www.thymeleaf.org">
<body>
<th:block th:insert="~{fragments/common :: game}"></th:block>
<div id="page-container">
<div id="content-wrap">
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
<br><br>
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6 bg-card">
<div class="tool-header">
<span class="material-symbols-rounded tool-header-icon advance">crop</span>
<span class="tool-header-text" th:text="#{autoCrop.header}"></span>
</div>
<form method="post" enctype="multipart/form-data" th:action="@{'/api/v1/misc/auto-crop'}">
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf')}"></div>
<br>
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{autoCrop.submit}"></button>
</form>
<p class="mt-3" th:text="#{autoCrop.credit}"></p>
<head>
<th:block th:insert="~{fragments/common :: head(title=#{autoCrop.title}, header=#{autoCrop.header})}"></th:block>
</head>
<body>
<th:block th:insert="~{fragments/common :: game}"></th:block>
<div id="page-container">
<div id="content-wrap">
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
<br><br>
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6 bg-card">
<div class="tool-header">
<span class="material-symbols-rounded tool-header-icon advance">crop</span>
<span class="tool-header-text" th:text="#{autoCrop.header}"></span>
</div>
<form method="post" enctype="multipart/form-data" th:action="@{'/api/v1/misc/auto-crop'}">
<div
th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf , application/zip' )}">
</div>
<br>
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{autoCrop.submit}"></button>
</form>
<p class="mt-3" th:text="#{autoCrop.credit}"></p>
</div>
</div>
</div>
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
</div>
</body>
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
</div>
</body>
</html>

View File

@ -1,35 +1,42 @@
<!DOCTYPE html>
<html th:lang="${#locale.language}" th:dir="#{language.direction}" th:data-language="${#locale.toString()}" xmlns:th="https://www.thymeleaf.org">
<head>
<th:block th:insert="~{fragments/common :: head(title=#{auto-rename.title}, header=#{auto-rename.header})}"></th:block>
</head>
<body>
<th:block th:insert="~{fragments/common :: game}"></th:block>
<div id="page-container">
<div id="content-wrap">
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
<br><br>
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6 bg-card">
<div class="tool-header">
<span >
<svg class="tool-header-icon advance">
<use xlink:href="/images/rename.svg#icon-rename"></use>
</svg>
</span>
<span class="tool-header-text" th:text="#{auto-rename.header}"></span>
</div>
<form method="post" enctype="multipart/form-data" th:action="@{'/api/v1/misc/auto-rename'}">
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf')}"></div>
<br>
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{auto-rename.submit}"></button>
</form>
<html th:lang="${#locale.language}" th:dir="#{language.direction}" th:data-language="${#locale.toString()}"
xmlns:th="https://www.thymeleaf.org">
<head>
<th:block th:insert="~{fragments/common :: head(title=#{auto-rename.title}, header=#{auto-rename.header})}">
</th:block>
</head>
<body>
<th:block th:insert="~{fragments/common :: game}"></th:block>
<div id="page-container">
<div id="content-wrap">
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
<br><br>
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6 bg-card">
<div class="tool-header">
<span>
<svg class="tool-header-icon advance">
<use xlink:href="/images/rename.svg#icon-rename"></use>
</svg>
</span>
<span class="tool-header-text" th:text="#{auto-rename.header}"></span>
</div>
<form method="post" enctype="multipart/form-data" th:action="@{'/api/v1/misc/auto-rename'}">
<div
th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf , application/zip' )}">
</div>
<br>
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{auto-rename.submit}"></button>
</form>
</div>
</div>
</div>
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
</div>
</body>
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
</div>
</body>
</html>

View File

@ -1,98 +1,107 @@
<!DOCTYPE html>
<html th:lang="${#locale.language}" th:dir="#{language.direction}" th:data-language="${#locale.toString()}" xmlns:th="https://www.thymeleaf.org">
<head>
<th:block th:insert="~{fragments/common :: head(title=#{changeMetadata.title}, header=#{changeMetadata.header})}"></th:block>
</head>
<html th:lang="${#locale.language}" th:dir="#{language.direction}" th:data-language="${#locale.toString()}"
xmlns:th="https://www.thymeleaf.org">
<body>
<div id="page-container">
<div id="content-wrap">
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
<br><br>
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6 bg-card">
<div class="tool-header">
<span class="material-symbols-rounded tool-header-icon other">assignment</span>
<span class="tool-header-text" th:text="#{changeMetadata.header}"></span>
</div>
<form method="post" id="form1" enctype="multipart/form-data" th:action="@{'/api/v1/misc/update-metadata'}">
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf')}"></div>
<p class="text-muted" th:text="#{changeMetadata.selectText.1}"></p>
<div class="form-check mb-3-inline ms-3">
<input type="checkbox" id="deleteAll" name="deleteAll">
<label for="deleteAll" th:text="#{changeMetadata.selectText.2}" ></label>
</div>
<div class="form-check mb-3-inline ms-3">
<input type="checkbox" id="customModeCheckbox">
<label for="customModeCheckbox" th:text="#{changeMetadata.selectText.3}"></label>
</div>
<div class="mb-3">
<label class="form-check-label" for="author" th:text="#{changeMetadata.author}"></label>
<input type="text" class="form-control" id="author" name="author">
</div>
<head>
<th:block th:insert="~{fragments/common :: head(title=#{changeMetadata.title}, header=#{changeMetadata.header})}">
</th:block>
</head>
<div class="mb-3">
<label class="form-check-label" for="creationDate" th:text="#{changeMetadata.creationDate}"></label>
<input type="text" class="form-control" id="creationDate" name="creationDate" placeholder="2020/12/25 18:30:59">
</div>
<div class="mb-3">
<label class="form-check-label" for="creator" th:text="#{changeMetadata.creator}"></label>
<input type="text" class="form-control" id="creator" name="creator">
</div>
<div class="mb-3">
<label class="form-check-label" for="keywords" th:text="#{changeMetadata.keywords}"></label>
<input type="text" class="form-control" id="keywords" name="keywords">
</div>
<div class="mb-3">
<label class="form-check-label" for="modificationDate" th:text="#{changeMetadata.modDate}"></label>
<input type="text" class="form-control" id="modificationDate" name="modificationDate" placeholder="2020/12/25 18:30:59">
</div>
<div class="mb-3">
<label class="form-check-label" for="producer" th:text="#{changeMetadata.producer}"></label>
<input type="text" class="form-control" id="producer" name="producer">
</div>
<div class="mb-3">
<label class="form-check-label" for="subject" th:text="#{changeMetadata.subject}"></label>
<input type="text" class="form-control" id="subject" name="subject">
</div>
<div class="mb-3">
<label class="form-check-label" for="title" th:text="#{changeMetadata.title}"></label>
<input type="text" class="form-control" id="title" name="title">
</div>
<div class="mb-3">
<label class="form-check-label" for="trapped" th:text="#{changeMetadata.trapped}"></label>
<select class="form-control" id="trapped" name="trapped">
<option value="True" th:text="#{true}"></option>
<option value="False" th:text="#{false}" selected></option>
<option value="Unknown" th:text="#{unknown}"></option>
</select>
</div>
<div id="customMetadata" style="display: none;">
<h3 th:text="#{changeMetadata.selectText.4}"></h3>
<div class="mb-3" id="otherMetadataEntries"></div>
</div>
<div id="customMetadataEntries"></div>
<button type="button" class="btn btn-secondary" id="addMetadataBtn" th:text="#{changeMetadata.selectText.5}"></button>
<br>
<br>
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{changeMetadata.submit}"></button>
<script type="module" th:src="@{'/pdfjs-legacy/pdf.mjs'}"></script>
<script type="module" th:src="@{'/js/pages/change-metadata.js'}">
</script>
</form>
<body>
<div id="page-container">
<div id="content-wrap">
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
<br><br>
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6 bg-card">
<div class="tool-header">
<span class="material-symbols-rounded tool-header-icon other">assignment</span>
<span class="tool-header-text" th:text="#{changeMetadata.header}"></span>
</div>
<form method="post" id="form1" enctype="multipart/form-data" th:action="@{'/api/v1/misc/update-metadata'}">
<div
th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf , application/zip' )}">
</div>
<p class="text-muted" th:text="#{changeMetadata.selectText.1}"></p>
<div class="form-check mb-3-inline ms-3">
<input type="checkbox" id="deleteAll" name="deleteAll">
<label for="deleteAll" th:text="#{changeMetadata.selectText.2}"></label>
</div>
<div class="form-check mb-3-inline ms-3">
<input type="checkbox" id="customModeCheckbox">
<label for="customModeCheckbox" th:text="#{changeMetadata.selectText.3}"></label>
</div>
<div class="mb-3">
<label class="form-check-label" for="author" th:text="#{changeMetadata.author}"></label>
<input type="text" class="form-control" id="author" name="author">
</div>
<div class="mb-3">
<label class="form-check-label" for="creationDate" th:text="#{changeMetadata.creationDate}"></label>
<input type="text" class="form-control" id="creationDate" name="creationDate"
placeholder="2020/12/25 18:30:59">
</div>
<div class="mb-3">
<label class="form-check-label" for="creator" th:text="#{changeMetadata.creator}"></label>
<input type="text" class="form-control" id="creator" name="creator">
</div>
<div class="mb-3">
<label class="form-check-label" for="keywords" th:text="#{changeMetadata.keywords}"></label>
<input type="text" class="form-control" id="keywords" name="keywords">
</div>
<div class="mb-3">
<label class="form-check-label" for="modificationDate" th:text="#{changeMetadata.modDate}"></label>
<input type="text" class="form-control" id="modificationDate" name="modificationDate"
placeholder="2020/12/25 18:30:59">
</div>
<div class="mb-3">
<label class="form-check-label" for="producer" th:text="#{changeMetadata.producer}"></label>
<input type="text" class="form-control" id="producer" name="producer">
</div>
<div class="mb-3">
<label class="form-check-label" for="subject" th:text="#{changeMetadata.subject}"></label>
<input type="text" class="form-control" id="subject" name="subject">
</div>
<div class="mb-3">
<label class="form-check-label" for="title" th:text="#{changeMetadata.title}"></label>
<input type="text" class="form-control" id="title" name="title">
</div>
<div class="mb-3">
<label class="form-check-label" for="trapped" th:text="#{changeMetadata.trapped}"></label>
<select class="form-control" id="trapped" name="trapped">
<option value="True" th:text="#{true}"></option>
<option value="False" th:text="#{false}" selected></option>
<option value="Unknown" th:text="#{unknown}"></option>
</select>
</div>
<div id="customMetadata" style="display: none;">
<h3 th:text="#{changeMetadata.selectText.4}"></h3>
<div class="mb-3" id="otherMetadataEntries"></div>
</div>
<div id="customMetadataEntries"></div>
<button type="button" class="btn btn-secondary" id="addMetadataBtn"
th:text="#{changeMetadata.selectText.5}"></button>
<br>
<br>
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{changeMetadata.submit}"></button>
<script type="module" th:src="@{'/pdfjs-legacy/pdf.mjs'}"></script>
<script type="module" th:src="@{'/js/pages/change-metadata.js'}">
</script>
</form>
</div>
</div>
</div>
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
</div>
</body>
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
</div>
</body>
</html>

View File

@ -60,10 +60,10 @@
<span class="tool-header-text" th:text="#{compare.header}"></span>
</div>
<div
th:replace="~{fragments/common :: fileSelector(name='fileInput', disableMultipleFiles=true, multipleInputsForSingleRequest=false, accept='application/pdf', remoteCall='false')}">
th:replace="~{fragments/common :: fileSelector(name='fileInput', disableMultipleFiles=true, multipleInputsForSingleRequest=false, accept='application/pdf , application/zip', remoteCall='false')}">
</div>
<div
th:replace="~{fragments/common :: fileSelector(name='fileInput2', disableMultipleFiles=true, multipleInputsForSingleRequest=false, accept='application/pdf', remoteCall='false')}">
th:replace="~{fragments/common :: fileSelector(name='fileInput2', disableMultipleFiles=true, multipleInputsForSingleRequest=false, accept='application/pdf , application/zip', remoteCall='false')}">
</div>
<div class="row">

View File

@ -21,7 +21,7 @@
</div>
<form action="#" th:action="@{'/api/v1/misc/compress-pdf'}" method="post" enctype="multipart/form-data">
<div
th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf')}">
th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf , application/zip' )}">
</div>
<div class="card mb-3">
<div class="card-body">
@ -43,9 +43,9 @@
<div class="form-check mt-3">
<input class="form-check-input" type="checkbox" name="grayscale" id="grayscaleCheck" value="true">
<label class="form-check-label" for="grayscaleCheck" th:text="#{compress.grayscale.label}">
Convert images to grayscale for better compression
Convert images to grayscale for better compression
</label>
</div>
</div>
</div>
</div>
<div class="card mb-3">
@ -65,4 +65,4 @@
</div>
</body>
</html>
</html>

View File

@ -1,43 +1,50 @@
<!DOCTYPE html>
<html th:lang="${#locale.language}" th:dir="#{language.direction}" th:data-language="${#locale.toString()}" xmlns:th="https://www.thymeleaf.org">
<head>
<th:block th:insert="~{fragments/common :: head(title=#{extractImages.title}, header=#{extractImages.header})}"></th:block>
</head>
<html th:lang="${#locale.language}" th:dir="#{language.direction}" th:data-language="${#locale.toString()}"
xmlns:th="https://www.thymeleaf.org">
<body>
<th:block th:insert="~{fragments/common :: game}"></th:block>
<div id="page-container">
<div id="content-wrap">
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
<br><br>
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6 bg-card">
<div class="tool-header">
<span class="material-symbols-rounded tool-header-icon other">photo_library</span>
<span class="tool-header-text" th:text="#{extractImages.header}"></span>
</div>
<form id="multiPdfForm" th:action="@{'/api/v1/misc/extract-images'}" method="post" enctype="multipart/form-data">
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf')}"></div>
<div class="mb-3">
<label th:text="#{extractImages.selectText}"></label>
<select class="form-control" name="format">
<option value="png">PNG</option>
<option value="jpg">JPG</option>
<option value="gif">GIF</option>
</select>
</div>
<div class="mb-3">
<input type="checkbox" name="allowDuplicates" id="allowDuplicates">
<label for="allowDuplicates" th:text="#{extractImages.allowDuplicates}"></label>
</div>
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{extractImages.submit}"></button>
</form>
<head>
<th:block th:insert="~{fragments/common :: head(title=#{extractImages.title}, header=#{extractImages.header})}">
</th:block>
</head>
<body>
<th:block th:insert="~{fragments/common :: game}"></th:block>
<div id="page-container">
<div id="content-wrap">
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
<br><br>
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6 bg-card">
<div class="tool-header">
<span class="material-symbols-rounded tool-header-icon other">photo_library</span>
<span class="tool-header-text" th:text="#{extractImages.header}"></span>
</div>
<form id="multiPdfForm" th:action="@{'/api/v1/misc/extract-images'}" method="post"
enctype="multipart/form-data">
<div
th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf , application/zip' )}">
</div>
<div class="mb-3">
<label th:text="#{extractImages.selectText}"></label>
<select class="form-control" name="format">
<option value="png">PNG</option>
<option value="jpg">JPG</option>
<option value="gif">GIF</option>
</select>
</div>
<div class="mb-3">
<input type="checkbox" name="allowDuplicates" id="allowDuplicates">
<label for="allowDuplicates" th:text="#{extractImages.allowDuplicates}"></label>
</div>
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{extractImages.submit}"></button>
</form>
</div>
</div>
</div>
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
</div>
</body>
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
</div>
</body>
</html>

View File

@ -1,29 +1,34 @@
<!DOCTYPE html>
<html th:lang="${#locale.language}" th:dir="#{language.direction}" th:data-language="${#locale.toString()}" xmlns:th="https://www.thymeleaf.org">
<head>
<th:block th:insert="~{fragments/common :: head(title=#{fakeScan.title}, header=#{fakeScan.header})}"></th:block>
</head>
<html th:lang="${#locale.language}" th:dir="#{language.direction}" th:data-language="${#locale.toString()}"
xmlns:th="https://www.thymeleaf.org">
<body>
<th:block th:insert="~{fragments/common :: game}"></th:block>
<div id="page-container">
<div id="content-wrap">
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
<br><br>
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6">
<h2 th:text="#{fakeScan.header}"></h2>
<form method="post" enctype="multipart/form-data" th:action="@{'/api/v1/misc/fake-scan'}">
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf')}"></div>
<br>
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{fakeScan.submit}"></button>
</form>
</div>
<head>
<th:block th:insert="~{fragments/common :: head(title=#{fakeScan.title}, header=#{fakeScan.header})}"></th:block>
</head>
<body>
<th:block th:insert="~{fragments/common :: game}"></th:block>
<div id="page-container">
<div id="content-wrap">
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
<br><br>
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6">
<h2 th:text="#{fakeScan.header}"></h2>
<form method="post" enctype="multipart/form-data" th:action="@{'/api/v1/misc/fake-scan'}">
<div
th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf , application/zip' )}">
</div>
<br>
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{fakeScan.submit}"></button>
</form>
</div>
</div>
</div>
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
</div>
</body>
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
</div>
</body>
</html>

View File

@ -1,38 +1,44 @@
<!DOCTYPE html>
<html th:lang="${#locale.language}" th:dir="#{language.direction}" th:data-language="${#locale.toString()}" xmlns:th="https://www.thymeleaf.org">
<head>
<html th:lang="${#locale.language}" th:dir="#{language.direction}" th:data-language="${#locale.toString()}"
xmlns:th="https://www.thymeleaf.org">
<head>
<th:block th:insert="~{fragments/common :: head(title=#{flatten.title}, header=#{flatten.header})}"></th:block>
</head>
</head>
<body>
<div id="page-container">
<div id="content-wrap">
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
<br><br>
<div class="container">
<div class="row justify-content-center">
<body>
<div id="page-container">
<div id="content-wrap">
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
<br><br>
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6 bg-card">
<div class="tool-header">
<span class="material-symbols-rounded tool-header-icon other">layers_clear</span>
<span class="tool-header-text" th:text="#{flatten.header}"></span>
</div>
<form method="post" enctype="multipart/form-data" th:action="@{'/api/v1/misc/flatten'}" id="pdfForm" class="mb-3">
<div class="custom-file">
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf')}"></div>
</div>
<div class="form-check ms-3">
<input type="checkbox" id="flattenOnlyForms" name="flattenOnlyForms">
<label for="flattenOnlyForms" th:text="#{flatten.flattenOnlyForms}" ></label>
</div>
<br>
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{flatten.submit}"></button>
</form>
<div class="col-md-6 bg-card">
<div class="tool-header">
<span class="material-symbols-rounded tool-header-icon other">layers_clear</span>
<span class="tool-header-text" th:text="#{flatten.header}"></span>
</div>
<form method="post" enctype="multipart/form-data" th:action="@{'/api/v1/misc/flatten'}" id="pdfForm"
class="mb-3">
<div class="custom-file">
<div
th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf , application/zip' )}">
</div>
</div>
<div class="form-check ms-3">
<input type="checkbox" id="flattenOnlyForms" name="flattenOnlyForms">
<label for="flattenOnlyForms" th:text="#{flatten.flattenOnlyForms}"></label>
</div>
<br>
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{flatten.submit}"></button>
</form>
</div>
</div>
</div>
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
</div>
</body>
</html>
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
</div>
</body>
</html>

View File

@ -1,231 +1,239 @@
<!DOCTYPE html>
<html th:lang="${#locale.language}" th:dir="#{language.direction}" th:data-language="${#locale.toString()}" xmlns:th="https://www.thymeleaf.org">
<head>
<html th:lang="${#locale.language}" th:dir="#{language.direction}" th:data-language="${#locale.toString()}"
xmlns:th="https://www.thymeleaf.org">
<head>
<th:block th:insert="~{fragments/common :: head(title=#{ocr.title}, header=#{ocr.header})}"></th:block>
<script>
function handleLangSelection() {
let checkboxes = document.getElementsByName("languages");
let selected = false;
for (let i = 0; i < checkboxes.length; i++) {
if (checkboxes[i].checked) {
selected = true;
checkboxes[i].setAttribute('required', 'false');
}
}
if (selected) {
for (let i = 0; i < checkboxes.length; i++) {
checkboxes[i].removeAttribute('required');
}
}
else {
for (let i = 0; i < checkboxes.length; i++) {
checkboxes[i].setAttribute('required', 'true');
}
<script>
function handleLangSelection() {
let checkboxes = document.getElementsByName("languages");
let selected = false;
for (let i = 0; i < checkboxes.length; i++) {
if (checkboxes[i].checked) {
selected = true;
checkboxes[i].setAttribute('required', 'false');
}
}
</script>
</head>
if (selected) {
for (let i = 0; i < checkboxes.length; i++) {
checkboxes[i].removeAttribute('required');
}
}
else {
for (let i = 0; i < checkboxes.length; i++) {
checkboxes[i].setAttribute('required', 'true');
}
}
}
</script>
</head>
<body>
<th:block th:insert="~{fragments/common :: game}"></th:block>
<div id="page-container">
<div id="content-wrap">
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
<br><br>
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6 bg-card">
<div class="tool-header">
<span class="material-symbols-rounded tool-header-icon other">quick_reference_all</span>
<span class="tool-header-text" th:text="#{ocr.header}"></span>
</div>
<form th:if="${#lists.size(languages) > 0}" action="#" th:action="@{'/api/v1/misc/ocr-pdf'}" method="post" enctype="multipart/form-data" class="mb-3">
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf')}"></div>
<div class="mb-3">
<label for="languages" class="form-label" th:text="#{ocr.selectText.1}"></label>
<hr>
<div id="languages">
<div class="form-check" th:each="language, iterStat : ${languages}">
<input type="checkbox" th:name="languages" th:value="${language}" required th:id="${'language-' + language}" onchange="handleLangSelection()" />
<label th:for="${'language-' + language}" th:text="${language}"></label>
</div>
</div>
<hr>
</div>
<div class="mb-3">
<label th:text="#{ocr.selectText.10}"></label>
<select class="form-control" name="ocrType">
<option value="skip-text" th:text="#{ocr.selectText.6}"></option>
<option value="force-ocr" th:text="#{ocr.selectText.7}"></option>
<option value="Normal" th:text="#{ocr.selectText.8}"></option>
</select>
</div>
<br>
<label for="languages" class="form-label" th:text="#{ocr.selectText.9}"></label>
<div class="mb-3">
<label th:text="#{ocr.selectText.12}"></label>
<select class="form-control" name="ocrRenderType">
<option value="hocr">HOCR (Latin/Roman alphabet only)</option>
<option value="sandwich">Sandwich</option>
</select>
</div>
<br>
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{ocr.submit}"></button>
</form>
<script>
const languageMap = {
'afr': 'Afrikaans',
'amh': 'Amharic',
'ara': 'Arabic',
'asm': 'Assamese',
'aze': 'Azerbaijani',
'aze_cyrl': 'Azerbaijani (Cyrillic)',
'bel': 'Belarusian',
'ben': 'Bengali',
'bod': 'Tibetan',
'bos': 'Bosnian',
'bre': 'Breton',
'bul': 'Bulgarian',
'cat': 'Catalan',
'ceb': 'Cebuano',
'ces': 'Czech',
'chi_sim': 'Chinese (Simplified)',
'chi_sim_vert': 'Chinese (Simplified, Vertical)',
'chi_tra': 'Chinese (Traditional)',
'chi_tra_vert': 'Chinese (Traditional, Vertical)',
'chr': 'Cherokee',
'cos': 'Corsican',
'cym': 'Welsh',
'dan': 'Danish',
'dan_frak': 'Danish (Fraktur)',
'deu': 'German',
'deu_frak': 'German (Fraktur)',
'div': 'Divehi',
'dzo': 'Dzongkha',
'ell': 'Greek',
'eng': 'English',
'enm': 'English, Middle (1100-1500)',
'epo': 'Esperanto',
'equ': 'Math / equation detection module',
'est': 'Estonian',
'eus': 'Basque',
'fao': 'Faroese',
'fas': 'Persian',
'fil': 'Filipino',
'fin': 'Finnish',
'fra': 'French',
'frk': 'Frankish',
'frm': 'French, Middle (ca.1400-1600)',
'fry': 'Western Frisian',
'gla': 'Scottish Gaelic',
'gle': 'Irish',
'glg': 'Galician',
'grc': 'Ancient Greek',
'guj': 'Gujarati',
'hat': 'Haitian, Haitian Creole',
'heb': 'Hebrew',
'hin': 'Hindi',
'hrv': 'Croatian',
'hun': 'Hungarian',
'hye': 'Armenian',
'iku': 'Inuktitut',
'ind': 'Indonesian',
'isl': 'Icelandic',
'ita': 'Italian',
'ita_old': 'Italian (Old)',
'jav': 'Javanese',
'jpn': 'Japanese',
'jpn_vert': 'Japanese (Vertical)',
'kan': 'Kannada',
'kat': 'Georgian',
'kat_old': 'Georgian (Old)',
'kaz': 'Kazakh',
'khm': 'Central Khmer',
'kir': 'Kirghiz, Kyrgyz',
'kmr': 'Northern Kurdish',
'kor': 'Korean',
'kor_vert': 'Korean (Vertical)',
'lao': 'Lao',
'lat': 'Latin',
'lav': 'Latvian',
'lit': 'Lithuanian',
'ltz': 'Luxembourgish',
'mal': 'Malayalam',
'mar': 'Marathi',
'mkd': 'Macedonian',
'mlt': 'Maltese',
'mon': 'Mongolian',
'mri': 'Maori',
'msa': 'Malay',
'mya': 'Burmese',
'nep': 'Nepali',
'nld': 'Dutch; Flemish',
'nor': 'Norwegian',
'oci': 'Occitan (post 1500)',
'ori': 'Oriya',
'osd': 'Orientation and script detection module',
'pan': 'Panjabi, Punjabi',
'pol': 'Polish',
'por': 'Portuguese',
'pus': 'Pushto, Pashto',
'que': 'Quechua',
'ron': 'Romanian, Moldavian, Moldovan',
'rus': 'Russian',
'san': 'Sanskrit',
'sin': 'Sinhala, Sinhalese',
'slk': 'Slovak',
'slk_frak': 'Slovak (Fraktur)',
'slv': 'Slovenian',
'snd': 'Sindhi',
'spa': 'Spanish',
'spa_old': 'Spanish (Old)',
'sqi': 'Albanian',
'srp': 'Serbian',
'srp_latn': 'Serbian (Latin)',
'sun': 'Sundanese',
'swa': 'Swahili',
'swe': 'Swedish',
'syr': 'Syriac',
'tam': 'Tamil',
'tat': 'Tatar',
'tel': 'Telugu',
'tgk': 'Tajik',
'tgl': 'Tagalog',
'tha': 'Thai',
'tir': 'Tigrinya',
'ton': 'Tonga (Tonga Islands)',
'tur': 'Turkish',
'uig': 'Uighur, Uyghur',
'ukr': 'Ukrainian',
'urd': 'Urdu',
'uzb': 'Uzbek',
'uzb_cyrl': 'Uzbek (Cyrillic)',
'vie': 'Vietnamese',
'yid': 'Yiddish',
'yor': 'Yoruba'
};
// Step 2: Function to get the full language name
function getFullLanguageName(shortCode) {
return languageMap[shortCode] || shortCode;
}
// Step 3: Apply the function to your labels
document.addEventListener('DOMContentLoaded', () => {
const labels = document.querySelectorAll('#languages label');
labels.forEach(label => {
const languageCode = label.getAttribute('for').split('-')[1];
label.textContent = getFullLanguageName(languageCode);
});
});
</script>
<p th:text="#{ocr.help}"></p>
<a href="https://docs.stirlingpdf.com/Advanced%20Configuration/OCR">https://docs.stirlingpdf.com/Advanced%20Configuration/OCR</a>
<body>
<th:block th:insert="~{fragments/common :: game}"></th:block>
<div id="page-container">
<div id="content-wrap">
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
<br><br>
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6 bg-card">
<div class="tool-header">
<span class="material-symbols-rounded tool-header-icon other">quick_reference_all</span>
<span class="tool-header-text" th:text="#{ocr.header}"></span>
</div>
<form th:if="${#lists.size(languages) > 0}" action="#" th:action="@{'/api/v1/misc/ocr-pdf'}" method="post"
enctype="multipart/form-data" class="mb-3">
<div
th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf , application/zip' )}">
</div>
<div class="mb-3">
<label for="languages" class="form-label" th:text="#{ocr.selectText.1}"></label>
<hr>
<div id="languages">
<div class="form-check" th:each="language, iterStat : ${languages}">
<input type="checkbox" th:name="languages" th:value="${language}" required
th:id="${'language-' + language}" onchange="handleLangSelection()" />
<label th:for="${'language-' + language}" th:text="${language}"></label>
</div>
</div>
<hr>
</div>
<div class="mb-3">
<label th:text="#{ocr.selectText.10}"></label>
<select class="form-control" name="ocrType">
<option value="skip-text" th:text="#{ocr.selectText.6}"></option>
<option value="force-ocr" th:text="#{ocr.selectText.7}"></option>
<option value="Normal" th:text="#{ocr.selectText.8}"></option>
</select>
</div>
<br>
<label for="languages" class="form-label" th:text="#{ocr.selectText.9}"></label>
<div class="mb-3">
<label th:text="#{ocr.selectText.12}"></label>
<select class="form-control" name="ocrRenderType">
<option value="hocr">HOCR (Latin/Roman alphabet only)</option>
<option value="sandwich">Sandwich</option>
</select>
</div>
<br>
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{ocr.submit}"></button>
</form>
<script>
const languageMap = {
'afr': 'Afrikaans',
'amh': 'Amharic',
'ara': 'Arabic',
'asm': 'Assamese',
'aze': 'Azerbaijani',
'aze_cyrl': 'Azerbaijani (Cyrillic)',
'bel': 'Belarusian',
'ben': 'Bengali',
'bod': 'Tibetan',
'bos': 'Bosnian',
'bre': 'Breton',
'bul': 'Bulgarian',
'cat': 'Catalan',
'ceb': 'Cebuano',
'ces': 'Czech',
'chi_sim': 'Chinese (Simplified)',
'chi_sim_vert': 'Chinese (Simplified, Vertical)',
'chi_tra': 'Chinese (Traditional)',
'chi_tra_vert': 'Chinese (Traditional, Vertical)',
'chr': 'Cherokee',
'cos': 'Corsican',
'cym': 'Welsh',
'dan': 'Danish',
'dan_frak': 'Danish (Fraktur)',
'deu': 'German',
'deu_frak': 'German (Fraktur)',
'div': 'Divehi',
'dzo': 'Dzongkha',
'ell': 'Greek',
'eng': 'English',
'enm': 'English, Middle (1100-1500)',
'epo': 'Esperanto',
'equ': 'Math / equation detection module',
'est': 'Estonian',
'eus': 'Basque',
'fao': 'Faroese',
'fas': 'Persian',
'fil': 'Filipino',
'fin': 'Finnish',
'fra': 'French',
'frk': 'Frankish',
'frm': 'French, Middle (ca.1400-1600)',
'fry': 'Western Frisian',
'gla': 'Scottish Gaelic',
'gle': 'Irish',
'glg': 'Galician',
'grc': 'Ancient Greek',
'guj': 'Gujarati',
'hat': 'Haitian, Haitian Creole',
'heb': 'Hebrew',
'hin': 'Hindi',
'hrv': 'Croatian',
'hun': 'Hungarian',
'hye': 'Armenian',
'iku': 'Inuktitut',
'ind': 'Indonesian',
'isl': 'Icelandic',
'ita': 'Italian',
'ita_old': 'Italian (Old)',
'jav': 'Javanese',
'jpn': 'Japanese',
'jpn_vert': 'Japanese (Vertical)',
'kan': 'Kannada',
'kat': 'Georgian',
'kat_old': 'Georgian (Old)',
'kaz': 'Kazakh',
'khm': 'Central Khmer',
'kir': 'Kirghiz, Kyrgyz',
'kmr': 'Northern Kurdish',
'kor': 'Korean',
'kor_vert': 'Korean (Vertical)',
'lao': 'Lao',
'lat': 'Latin',
'lav': 'Latvian',
'lit': 'Lithuanian',
'ltz': 'Luxembourgish',
'mal': 'Malayalam',
'mar': 'Marathi',
'mkd': 'Macedonian',
'mlt': 'Maltese',
'mon': 'Mongolian',
'mri': 'Maori',
'msa': 'Malay',
'mya': 'Burmese',
'nep': 'Nepali',
'nld': 'Dutch; Flemish',
'nor': 'Norwegian',
'oci': 'Occitan (post 1500)',
'ori': 'Oriya',
'osd': 'Orientation and script detection module',
'pan': 'Panjabi, Punjabi',
'pol': 'Polish',
'por': 'Portuguese',
'pus': 'Pushto, Pashto',
'que': 'Quechua',
'ron': 'Romanian, Moldavian, Moldovan',
'rus': 'Russian',
'san': 'Sanskrit',
'sin': 'Sinhala, Sinhalese',
'slk': 'Slovak',
'slk_frak': 'Slovak (Fraktur)',
'slv': 'Slovenian',
'snd': 'Sindhi',
'spa': 'Spanish',
'spa_old': 'Spanish (Old)',
'sqi': 'Albanian',
'srp': 'Serbian',
'srp_latn': 'Serbian (Latin)',
'sun': 'Sundanese',
'swa': 'Swahili',
'swe': 'Swedish',
'syr': 'Syriac',
'tam': 'Tamil',
'tat': 'Tatar',
'tel': 'Telugu',
'tgk': 'Tajik',
'tgl': 'Tagalog',
'tha': 'Thai',
'tir': 'Tigrinya',
'ton': 'Tonga (Tonga Islands)',
'tur': 'Turkish',
'uig': 'Uighur, Uyghur',
'ukr': 'Ukrainian',
'urd': 'Urdu',
'uzb': 'Uzbek',
'uzb_cyrl': 'Uzbek (Cyrillic)',
'vie': 'Vietnamese',
'yid': 'Yiddish',
'yor': 'Yoruba'
};
// Step 2: Function to get the full language name
function getFullLanguageName(shortCode) {
return languageMap[shortCode] || shortCode;
}
// Step 3: Apply the function to your labels
document.addEventListener('DOMContentLoaded', () => {
const labels = document.querySelectorAll('#languages label');
labels.forEach(label => {
const languageCode = label.getAttribute('for').split('-')[1];
label.textContent = getFullLanguageName(languageCode);
});
});
</script>
<p th:text="#{ocr.help}"></p>
<a
href="https://docs.stirlingpdf.com/Advanced%20Configuration/OCR">https://docs.stirlingpdf.com/Advanced%20Configuration/OCR</a>
</div>
</div>
</div>
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
</div>
</body>
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
</div>
</body>
</html>

View File

@ -1,68 +1,76 @@
<!DOCTYPE html>
<html th:lang="${#locale.language}" th:dir="#{language.direction}" th:data-language="${#locale.toString()}" xmlns:th="https://www.thymeleaf.org">
<head>
<th:block th:insert="~{fragments/common :: head(title=#{removeAnnotations.title}, header=#{removeAnnotations.header})}"></th:block>
</head>
<html th:lang="${#locale.language}" th:dir="#{language.direction}" th:data-language="${#locale.toString()}"
xmlns:th="https://www.thymeleaf.org">
<body>
<div id="page-container">
<div id="content-wrap">
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
<br><br>
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6 bg-card">
<div class="tool-header">
<span class="material-symbols-rounded tool-header-icon other">thread_unread</span>
<span class="tool-header-text" th:text="#{removeAnnotations.header}"></span>
</div>
<form id="pdfForm" th:action="@{''}" class="mb-3">
<div class="custom-file">
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf', remoteCall='false')}"></div>
</div>
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{removeAnnotations.submit}"></button>
</form>
<head>
<th:block
th:insert="~{fragments/common :: head(title=#{removeAnnotations.title}, header=#{removeAnnotations.header})}">
</th:block>
</head>
<body>
<div id="page-container">
<div id="content-wrap">
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
<br><br>
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6 bg-card">
<div class="tool-header">
<span class="material-symbols-rounded tool-header-icon other">thread_unread</span>
<span class="tool-header-text" th:text="#{removeAnnotations.header}"></span>
</div>
<form id="pdfForm" th:action="@{''}" class="mb-3">
<div class="custom-file">
<div
th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf , application/zip', remoteCall='false')}">
</div>
</div>
<button type="submit" id="submitBtn" class="btn btn-primary"
th:text="#{removeAnnotations.submit}"></button>
</form>
</div>
</div>
</div>
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
</div>
<script th:src="@{'/js/local-pdf-input-download.js'}"></script>
<script>
document.getElementById('pdfForm').addEventListener('submit', async (e) => {
e.preventDefault();
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
</div>
<script th:src="@{'/js/local-pdf-input-download.js'}"></script>
<script>
document.getElementById('pdfForm').addEventListener('submit', async (e) => {
e.preventDefault();
const { PDFDocument } = PDFLib;
const { PDFDocument } = PDFLib;
const processFile = async (file) => {
const origFileUrl = URL.createObjectURL(file);
const formPdfBytes = await window.fetchWithCsrf(origFileUrl).then(res => res.arrayBuffer());
const pdfDoc = await PDFDocument.load(formPdfBytes, { ignoreEncryption: true });
const processFile = async (file) => {
const origFileUrl = URL.createObjectURL(file);
const formPdfBytes = await window.fetchWithCsrf(origFileUrl).then(res => res.arrayBuffer());
const pdfDoc = await PDFDocument.load(formPdfBytes, { ignoreEncryption: true });
const pages = pdfDoc.getPages();
const pages = pdfDoc.getPages();
for (let i = 0; i < pages.length; ++i) {
const page = pages[i];
const annotations = page.node.Annots();
if (!annotations) continue;
const ctx = annotations.context;
for (let i = 0; i < pages.length; ++i) {
const page = pages[i];
const annotations = page.node.Annots();
if (!annotations) continue;
const ctx = annotations.context;
for (let j = 0; j < annotations.size(); ++j) {
const annotation = annotations.get(j);
ctx.delete(annotation);
}
for (let j = 0; j < annotations.size(); ++j) {
const annotation = annotations.get(j);
ctx.delete(annotation);
}
}
const pdfBytes = await pdfDoc.save();
const pdfBlob = new Blob([pdfBytes], { type: 'application/pdf' });
const fileName = (file.name ? file.name.replace('.pdf', '') : 'pdf') + '_removed_annotations.pdf';
const pdfBytes = await pdfDoc.save();
const pdfBlob = new Blob([pdfBytes], { type: 'application/pdf' });
const fileName = (file.name ? file.name.replace('.pdf', '') : 'pdf') + '_removed_annotations.pdf';
return { processedData: pdfBlob, fileName };
};
return { processedData: pdfBlob, fileName };
};
await downloadFilesWithCallback(processFile);
});
</script>
</body>
await downloadFilesWithCallback(processFile);
});
</script>
</body>
</html>

View File

@ -1,41 +1,49 @@
<!DOCTYPE html>
<html th:lang="${#locale.language}" th:dir="#{language.direction}" th:data-language="${#locale.toString()}" xmlns:th="https://www.thymeleaf.org">
<head>
<th:block th:insert="~{fragments/common :: head(title=#{removeBlanks.title}, header=#{removeBlanks.header})}"></th:block>
</head>
<html th:lang="${#locale.language}" th:dir="#{language.direction}" th:data-language="${#locale.toString()}"
xmlns:th="https://www.thymeleaf.org">
<body>
<th:block th:insert="~{fragments/common :: game}"></th:block>
<div id="page-container">
<div id="content-wrap">
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
<br><br>
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6 bg-card">
<div class="tool-header">
<span class="material-symbols-rounded tool-header-icon other">scan_delete</span>
<span class="tool-header-text" th:text="#{removeBlanks.header}"></span>
</div>
<form id="multiPdfForm" th:action="@{'/api/v1/misc/remove-blanks'}" method="post" enctype="multipart/form-data">
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf')}"></div>
<div class="mb-3">
<label for="threshold" th:text="#{removeBlanks.threshold}"></label>
<input type="number" class="form-control" id="threshold" name="threshold" value="10">
<small id="thresholdHelp" class="form-text text-muted" th:text="#{removeBlanks.thresholdDesc}"></small>
</div>
<div class="mb-3">
<label for="whitePercent" th:text="#{removeBlanks.whitePercent}"></label>
<input type="number" class="form-control" id="whitePercent" name="whitePercent" value="99.9" step="0.1">
<small id="whitePercentHelp" class="form-text text-muted" th:text="#{removeBlanks.whitePercentDesc}"></small>
</div>
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{removeBlanks.submit}"></button>
</form>
<head>
<th:block th:insert="~{fragments/common :: head(title=#{removeBlanks.title}, header=#{removeBlanks.header})}">
</th:block>
</head>
<body>
<th:block th:insert="~{fragments/common :: game}"></th:block>
<div id="page-container">
<div id="content-wrap">
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
<br><br>
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6 bg-card">
<div class="tool-header">
<span class="material-symbols-rounded tool-header-icon other">scan_delete</span>
<span class="tool-header-text" th:text="#{removeBlanks.header}"></span>
</div>
<form id="multiPdfForm" th:action="@{'/api/v1/misc/remove-blanks'}" method="post"
enctype="multipart/form-data">
<div
th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf , application/zip' )}">
</div>
<div class="mb-3">
<label for="threshold" th:text="#{removeBlanks.threshold}"></label>
<input type="number" class="form-control" id="threshold" name="threshold" value="10">
<small id="thresholdHelp" class="form-text text-muted" th:text="#{removeBlanks.thresholdDesc}"></small>
</div>
<div class="mb-3">
<label for="whitePercent" th:text="#{removeBlanks.whitePercent}"></label>
<input type="number" class="form-control" id="whitePercent" name="whitePercent" value="99.9" step="0.1">
<small id="whitePercentHelp" class="form-text text-muted"
th:text="#{removeBlanks.whitePercentDesc}"></small>
</div>
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{removeBlanks.submit}"></button>
</form>
</div>
</div>
</div>
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
</div>
</body>
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
</div>
</body>
</html>

View File

@ -1,30 +1,35 @@
<!DOCTYPE html>
<html th:lang="${#locale.language}" th:dir="#{language.direction}" th:data-language="${#locale.toString()}" xmlns:th="https://www.thymeleaf.org">
<head>
<th:block th:insert="~{fragments/common :: head(title=#{repair.title}, header=#{repair.header})}"></th:block>
</head>
<html th:lang="${#locale.language}" th:dir="#{language.direction}" th:data-language="${#locale.toString()}"
xmlns:th="https://www.thymeleaf.org">
<body>
<div id="page-container">
<div id="content-wrap">
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
<br><br>
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6 bg-card">
<div class="tool-header">
<span class="material-symbols-rounded tool-header-icon advance">build</span>
<span class="tool-header-text" th:text="#{repair.header}"></span>
</div>
<form id="multiPdfForm" th:action="@{'/api/v1/misc/repair'}" method="post" enctype="multipart/form-data">
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf')}"></div>
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{repair.submit}"></button>
</form>
<head>
<th:block th:insert="~{fragments/common :: head(title=#{repair.title}, header=#{repair.header})}"></th:block>
</head>
<body>
<div id="page-container">
<div id="content-wrap">
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
<br><br>
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6 bg-card">
<div class="tool-header">
<span class="material-symbols-rounded tool-header-icon advance">build</span>
<span class="tool-header-text" th:text="#{repair.header}"></span>
</div>
<form id="multiPdfForm" th:action="@{'/api/v1/misc/repair'}" method="post" enctype="multipart/form-data">
<div
th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf , application/zip' )}">
</div>
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{repair.submit}"></button>
</form>
</div>
</div>
</div>
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
</div>
</body>
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
</div>
</body>
</html>

View File

@ -1,89 +1,98 @@
<!DOCTYPE html>
<html th:lang="${#locale.language}" th:dir="#{language.direction}" th:data-language="${#locale.toString()}"
xmlns:th="https://www.thymeleaf.org">
xmlns:th="https://www.thymeleaf.org">
<head>
<th:block th:insert="~{fragments/common :: head(title=#{replace-color.title}, header=#{replace-color.header})}"></th:block>
<th:block th:insert="~{fragments/common :: head(title=#{replace-color.title}, header=#{replace-color.header})}">
</th:block>
</head>
<body>
<th:block th:insert="~{fragments/common :: game}"></th:block>
<div id="page-container">
<div id="content-wrap">
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
<br><br>
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6 bg-card">
<div class="tool-header">
<span class="material-symbols-rounded tool-header-icon advance">zoom_in_map</span>
<span class="tool-header-text" th:text="#{replace-color.header}"></span>
</div>
<form action="#" th:action="@{'/api/v1/misc/replace-invert-pdf'}" method="post" enctype="multipart/form-data">
<div
th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf')}">
<th:block th:insert="~{fragments/common :: game}"></th:block>
<div id="page-container">
<div id="content-wrap">
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
<br><br>
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6 bg-card">
<div class="tool-header">
<span class="material-symbols-rounded tool-header-icon advance">zoom_in_map</span>
<span class="tool-header-text" th:text="#{replace-color.header}"></span>
</div>
<div class="card mb-3">
<div class="card-body">
<h4 th:text="#{replace-color.selectText.1}"></h4>
<select name="replaceAndInvertOption" id="replace-invert" class="form-control">
<option value="HIGH_CONTRAST_COLOR" th:text="#{replace-color.selectText.2}" ></option>
<option value="CUSTOM_COLOR" th:text="#{replace-color.selectText.3}"></option>
<option value="FULL_INVERSION" th:text="#{replace-color.selectText.4}" selected></option>
</select>
<form action="#" th:action="@{'/api/v1/misc/replace-invert-pdf'}" method="post"
enctype="multipart/form-data">
<div
th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf , application/zip' )}">
</div>
</div>
<div class="card mb-3" id="high-contrast-options" style="display: none">
<div class="card-body">
<h4 th:text="#{replace-color.selectText.5}"></h4>
<select name="highContrastColorCombination" id="high-contrast" class="form-control">
<option value="WHITE_TEXT_ON_BLACK" th:text="#{replace-color.selectText.6}" selected></option>
<option value="BLACK_TEXT_ON_WHITE" th:text="#{replace-color.selectText.7}"></option>
<option value="YELLOW_TEXT_ON_BLACK" th:text="#{replace-color.selectText.8}"></option>
<option value="GREEN_TEXT_ON_BLACK" th:text="#{replace-color.selectText.9}"></option>
</select>
<div class="card mb-3">
<div class="card-body">
<h4 th:text="#{replace-color.selectText.1}"></h4>
<select name="replaceAndInvertOption" id="replace-invert" class="form-control">
<option value="HIGH_CONTRAST_COLOR" th:text="#{replace-color.selectText.2}">
</option>
<option value="CUSTOM_COLOR" th:text="#{replace-color.selectText.3}"></option>
<option value="FULL_INVERSION" th:text="#{replace-color.selectText.4}" selected>
</option>
</select>
</div>
</div>
</div>
<div class="card mb-3" id = "custom-color-1" style="display: none">
<div class="card-body">
<h4 th:text="#{replace-color.selectText.10}"></h4>
<input type="color" name="textColor" id="text-color" class="form-control">
<div class="card mb-3" id="high-contrast-options" style="display: none">
<div class="card-body">
<h4 th:text="#{replace-color.selectText.5}"></h4>
<select name="highContrastColorCombination" id="high-contrast" class="form-control">
<option value="WHITE_TEXT_ON_BLACK" th:text="#{replace-color.selectText.6}"
selected></option>
<option value="BLACK_TEXT_ON_WHITE" th:text="#{replace-color.selectText.7}">
</option>
<option value="YELLOW_TEXT_ON_BLACK" th:text="#{replace-color.selectText.8}">
</option>
<option value="GREEN_TEXT_ON_BLACK" th:text="#{replace-color.selectText.9}">
</option>
</select>
</div>
</div>
</div>
<div class="card mb-3" id = "custom-color-2" style="display: none">
<div class="card-body">
<h4 th:text="#{replace-color.selectText.11}"></h4>
<input type="color" name="backGroundColor" id="bg-color" class="form-control">
<div class="card mb-3" id="custom-color-1" style="display: none">
<div class="card-body">
<h4 th:text="#{replace-color.selectText.10}"></h4>
<input type="color" name="textColor" id="text-color" class="form-control">
</div>
</div>
<div class="card mb-3" id="custom-color-2" style="display: none">
<div class="card-body">
<h4 th:text="#{replace-color.selectText.11}"></h4>
<input type="color" name="backGroundColor" id="bg-color" class="form-control">
</div>
</div>
</div>
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{replace-color.submit}"></button>
</form>
<button type="submit" id="submitBtn" class="btn btn-primary"
th:text="#{replace-color.submit}"></button>
</form>
</div>
</div>
</div>
</div>
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
</div>
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
</div>
<script>
$(document).ready(function(){
$('#replace-invert').change(function() {
var selectedOption = $(this).val();
<script>
$(document).ready(function () {
$('#replace-invert').change(function () {
var selectedOption = $(this).val();
// Hide all conditional fields by default
$('#high-contrast-options').hide();
$('#custom-color-1').hide();
$('#custom-color-2').hide();
// Hide all conditional fields by default
$('#high-contrast-options').hide();
$('#custom-color-1').hide();
$('#custom-color-2').hide();
if (selectedOption === "HIGH_CONTRAST_COLOR") {
$('#high-contrast-options').show();
} else if (selectedOption === "CUSTOM_COLOR") {
$('#custom-color-1').show();
$('#custom-color-2').show();
}
if (selectedOption === "HIGH_CONTRAST_COLOR") {
$('#high-contrast-options').show();
} else if (selectedOption === "CUSTOM_COLOR") {
$('#custom-color-1').show();
$('#custom-color-2').show();
}
});
});
});
</script>
</script>
</body>
</html>

View File

@ -1,56 +1,62 @@
<!DOCTYPE html>
<html th:lang="${#locale.language}" th:dir="#{language.direction}" th:data-language="${#locale.toString()}" xmlns:th="https://www.thymeleaf.org">
<head>
<html th:lang="${#locale.language}" th:dir="#{language.direction}" th:data-language="${#locale.toString()}"
xmlns:th="https://www.thymeleaf.org">
<head>
<th:block th:insert="~{fragments/common :: head(title=#{showJS.title}, header=#{showJS.header})}"></th:block>
<link th:href="@{'/css/prism.css'}" rel="stylesheet">
<script th:src="@{'/js/thirdParty/prism.js'}"></script>
<style>
/* Add a max-height and make it scrollable */
#script-content {
max-height: 1000px; /* Adjust this to your preferred maximum height */
overflow-y: auto;
}
</style>
</head>
<link th:href="@{'/css/prism.css'}" rel="stylesheet">
<script th:src="@{'/js/thirdParty/prism.js'}"></script>
<style>
/* Add a max-height and make it scrollable */
#script-content {
max-height: 1000px;
/* Adjust this to your preferred maximum height */
overflow-y: auto;
}
</style>
</head>
<body>
<div id="page-container">
<div id="content-wrap">
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
<br><br>
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6 bg-card">
<div class="tool-header">
<span class="material-symbols-rounded tool-header-icon advance">javascript</span>
<span class="tool-header-text" th:text="#{showJS.header}"></span>
<body>
<div id="page-container">
<div id="content-wrap">
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
<br><br>
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6 bg-card">
<div class="tool-header">
<span class="material-symbols-rounded tool-header-icon advance">javascript</span>
<span class="tool-header-text" th:text="#{showJS.header}"></span>
</div>
<form id="pdfInfoForm" method="post" enctype="multipart/form-data" th:action="@{'/show-javascript'}">
<div
th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, remoteCall='false', accept='application/pdf , application/zip')}">
</div>
<form id="pdfInfoForm" method="post" enctype="multipart/form-data" th:action="@{'/show-javascript'}">
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, remoteCall='false', accept='application/pdf')}"></div>
<br>
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{showJS.submit}"></button>
</form>
<div class="container mt-4">
<!-- Iterate over each main section in the JSON -->
<div id="script-content">
<!-- JavaScript will populate this section -->
</div>
<!-- Button to download the JSON -->
<a href="#" id="downloadJS" class="btn btn-primary mt-3" style="display: none;" th:text="#{showJS.downloadJS}">Download JSON</a>
<br>
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{showJS.submit}"></button>
</form>
<div class="container mt-4">
<!-- Iterate over each main section in the JSON -->
<div id="script-content">
<!-- JavaScript will populate this section -->
</div>
<script th:src="@{'/js/fetch-utils.js'}"></script>
<script>
document.querySelector('#pdfInfoForm').addEventListener('submit', function(event){
event.preventDefault();
// Fetch the formData
const formData = new FormData(event.target);
<!-- Button to download the JSON -->
<a href="#" id="downloadJS" class="btn btn-primary mt-3" style="display: none;"
th:text="#{showJS.downloadJS}">Download JSON</a>
</div>
<script th:src="@{'/js/fetch-utils.js'}"></script>
<script>
document.querySelector('#pdfInfoForm').addEventListener('submit', function (event) {
event.preventDefault();
fetchWithCsrf('api/v1/misc/show-javascript', {
method: 'POST',
body: formData
}).then(response => response.text())
// Fetch the formData
const formData = new FormData(event.target);
fetchWithCsrf('api/v1/misc/show-javascript', {
method: 'POST',
body: formData
}).then(response => response.text())
.then(data => {
// Escape < and > characters
let escapedData = data.replace(/</g, '&lt;').replace(/>/g, '&gt;');
@ -73,7 +79,7 @@
Prism.highlightAll();
// Create a blob object from the data and create a URL for it
let blob = new Blob([data], {type: 'application/javascript'});
let blob = new Blob([data], { type: 'application/javascript' });
let url = URL.createObjectURL(blob);
// Set the URL as the href of the download button and provide a download name
@ -85,13 +91,14 @@
.catch(error => {
console.error('Error:', error);
});
});
</script>
</div>
});
</script>
</div>
</div>
</div>
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
</div>
</body>
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
</div>
</body>
</html>

View File

@ -1,153 +1,161 @@
<!DOCTYPE html>
<html th:lang="${#locale.language}" th:dir="#{language.direction}" th:data-language="${#locale.toString()}" xmlns:th="https://www.thymeleaf.org">
<head>
<th:block th:insert="~{fragments/common :: head(title=#{AddStampRequest.title}, header=#{AddStampRequest.header})}"></th:block>
<style>
.a4container {
position: relative;
width: 50%;
aspect-ratio: 0.707/1;
border: 1px solid #ddd;
box-sizing: border-box;
background-color: white;
}
</style>
</head>
<html th:lang="${#locale.language}" th:dir="#{language.direction}" th:data-language="${#locale.toString()}"
xmlns:th="https://www.thymeleaf.org">
<body>
<th:block th:insert="~{fragments/common :: game}"></th:block>
<div id="page-container">
<div id="content-wrap">
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
<br><br>
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6 bg-card">
<div class="tool-header">
<span class="material-symbols-rounded tool-header-icon security">approval</span>
<span class="tool-header-text" th:text="#{AddStampRequest.header}"></span>
<head>
<th:block th:insert="~{fragments/common :: head(title=#{AddStampRequest.title}, header=#{AddStampRequest.header})}">
</th:block>
<style>
.a4container {
position: relative;
width: 50%;
aspect-ratio: 0.707/1;
border: 1px solid #ddd;
box-sizing: border-box;
background-color: white;
}
</style>
</head>
<body>
<th:block th:insert="~{fragments/common :: game}"></th:block>
<div id="page-container">
<div id="content-wrap">
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
<br><br>
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6 bg-card">
<div class="tool-header">
<span class="material-symbols-rounded tool-header-icon security">approval</span>
<span class="tool-header-text" th:text="#{AddStampRequest.header}"></span>
</div>
<form method="post" enctype="multipart/form-data" th:action="@{'/api/v1/misc/add-stamp'}">
<div
th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf , application/zip' )}">
</div>
<form method="post" enctype="multipart/form-data" th:action="@{'/api/v1/misc/add-stamp'}">
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf')}"></div>
<br>
<div class="mb-3">
<label for="pageOrder" th:text="#{pageSelectionPrompt}"></label>
<input type="text" class="form-control" id="pageOrder" name="pageNumbers" value="1" placeholder="(e.g. 1,3,2 or 4-8,2,10-12 or 2n-1)" required>
<br>
<div class="mb-3">
<label for="pageOrder" th:text="#{pageSelectionPrompt}"></label>
<input type="text" class="form-control" id="pageOrder" name="pageNumbers" value="1"
placeholder="(e.g. 1,3,2 or 4-8,2,10-12 or 2n-1)" required>
</div>
<div class="mb-3">
<label for="customMargin" class="form-label" th:text="#{AddStampRequest.customMargin}">Custom
Margin</label>
<select class="form-select" id="customMargin" name="customMargin">
<option value="small" th:text="#{sizes.small}"></option>
<option value="medium" selected th:text="#{sizes.medium}"></option>
<option value="large" th:text="#{sizes.large}"></option>
<option value="x-large" th:text="#{sizes.x-large}"></option>
</select>
</div>
<div class="mb-3">
<label th:text="#{AddStampRequest.position}"></label>
<div class="a4container">
<div class="pageNumber" id="1" style="top: 10%; left: 10%;">1</div>
<div class="pageNumber" id="2" style="top: 10%; left: 50%;">2</div>
<div class="pageNumber" id="3" style="top: 10%; left: 90%;">3</div>
<div class="pageNumber" id="4" style="top: 50%; left: 10%;">4</div>
<div class="pageNumber" id="5" style="top: 50%; left: 50%;">5</div>
<div class="pageNumber" id="6" style="top: 50%; left: 90%;">6</div>
<div class="pageNumber" id="7" style="top: 90%; left: 10%;">7</div>
<div class="pageNumber selectedPosition" id="8" style="top: 90%; left: 50%;">8</div>
<div class="pageNumber" id="9" style="top: 90%; left: 90%;">9</div>
</div>
</div>
<div class="mb-3">
<label for="customMargin" class="form-label" th:text="#{AddStampRequest.customMargin}">Custom Margin</label>
<select class="form-select" id="customMargin" name="customMargin">
<option value="small" th:text="#{sizes.small}"></option>
<option value="medium" selected th:text="#{sizes.medium}"></option>
<option value="large" th:text="#{sizes.large}"></option>
<option value="x-large" th:text="#{sizes.x-large}"></option>
</select>
<input type="hidden" id="numberInput" name="position" value="8">
<div class="mb-3">
<label for="stampType" class="form-label" th:text="#{AddStampRequest.stampType}">Stamp Type</label>
<select class="form-select" id="stampType" name="stampType" onchange="toggleFileOption()">
<option value="text" th:text="#{watermark.type.1}">Text</option>
<option value="image" th:text="#{watermark.type.2}">Image</option>
</select>
</div>
<div id="stampTextGroup" class="mb-3">
<label for="stampText" class="form-label" th:text="#{AddStampRequest.stampText}">Stamp Text</label>
<input type="text" class="form-control" id="stampText" name="stampText">
</div>
<div id="stampImageGroup" class="mb-3" style="display: none;">
<label for="stampImage" class="form-label" th:text="#{AddStampRequest.stampImage}">Stamp Image</label>
<input type="file" class="form-control" id="stampImage" name="stampImage" accept="image/*">
</div>
<div id="alphabetGroup" class="mb-3">
<label for="alphabet" class="form-label" th:text="#{AddStampRequest.alphabet}">Alphabet</label>
<select class="form-select" id="alphabet" name="alphabet">
<option value="roman">Roman</option>
<option value="arabic">العربية</option>
<option value="japanese">日本語</option>
<option value="korean">한국어</option>
<option value="chinese">简体中文</option>
</select>
</div>
<div class="mb-3">
<label for="fontSize" class="form-label" th:text="#{AddStampRequest.fontSize}">Font Size</label>
<input type="number" class="form-control" id="fontSize" name="fontSize" value="30">
</div>
<div class="mb-3">
<label for="rotation" class="form-label" th:text="#{AddStampRequest.rotation}">Rotation</label>
<input type="number" class="form-control" id="rotation" name="rotation" value="0">
</div>
<div class="mb-3">
<label for="opacity" class="form-label" th:text="#{AddStampRequest.opacity}">Opacity</label>
<input type="number" class="form-control" id="opacity" name="opacity" step="0.1" value="0.5">
</div>
<div class="mb-3">
<label for="overrideX" class="form-label" th:text="#{AddStampRequest.overrideX}">Override X</label>
<input type="number" class="form-control" id="overrideX" name="overrideX" value="-1">
</div>
<div class="mb-3">
<label for="overrideY" class="form-label" th:text="#{AddStampRequest.overrideY}">Override Y</label>
<input type="number" class="form-control" id="overrideY" name="overrideY" value="-1">
</div>
<div class="mb-3">
<label for="customColor" class="form-label" th:text="#{AddStampRequest.customColor}">Custom
Color</label>
<div class="form-control form-control-color" style="background-color: #d3d3d3;">
<input type="color" id="customColor" name="customColor" value="#d3d3d3">
</div>
<div class="mb-3">
<label th:text="#{AddStampRequest.position}"></label>
<div class="a4container">
<div class="pageNumber" id="1" style="top: 10%; left: 10%;">1</div>
<div class="pageNumber" id="2" style="top: 10%; left: 50%;">2</div>
<div class="pageNumber" id="3" style="top: 10%; left: 90%;">3</div>
<div class="pageNumber" id="4" style="top: 50%; left: 10%;">4</div>
<div class="pageNumber" id="5" style="top: 50%; left: 50%;">5</div>
<div class="pageNumber" id="6" style="top: 50%; left: 90%;">6</div>
<div class="pageNumber" id="7" style="top: 90%; left: 10%;">7</div>
<div class="pageNumber selectedPosition" id="8" style="top: 90%; left: 50%;">8</div>
<div class="pageNumber" id="9" style="top: 90%; left: 90%;">9</div>
</div>
</div>
<input type="hidden" id="numberInput" name="position" value="8">
<div class="mb-3">
<label for="stampType" class="form-label" th:text="#{AddStampRequest.stampType}">Stamp Type</label>
<select class="form-select" id="stampType" name="stampType" onchange="toggleFileOption()">
<option value="text" th:text="#{watermark.type.1}">Text</option>
<option value="image" th:text="#{watermark.type.2}">Image</option>
</select>
</div>
<div id="stampTextGroup" class="mb-3">
<label for="stampText" class="form-label" th:text="#{AddStampRequest.stampText}">Stamp Text</label>
<input type="text" class="form-control" id="stampText" name="stampText">
</div>
<div id="stampImageGroup" class="mb-3" style="display: none;">
<label for="stampImage" class="form-label" th:text="#{AddStampRequest.stampImage}">Stamp Image</label>
<input type="file" class="form-control" id="stampImage" name="stampImage" accept="image/*" >
</div>
<div id="alphabetGroup" class="mb-3">
<label for="alphabet" class="form-label" th:text="#{AddStampRequest.alphabet}">Alphabet</label>
<select class="form-select" id="alphabet" name="alphabet">
<option value="roman">Roman</option>
<option value="arabic">العربية</option>
<option value="japanese">日本語</option>
<option value="korean">한국어</option>
<option value="chinese">简体中文</option>
</select>
</div>
<div class="mb-3">
<label for="fontSize" class="form-label" th:text="#{AddStampRequest.fontSize}">Font Size</label>
<input type="number" class="form-control" id="fontSize" name="fontSize" value="30">
</div>
<div class="mb-3">
<label for="rotation" class="form-label" th:text="#{AddStampRequest.rotation}">Rotation</label>
<input type="number" class="form-control" id="rotation" name="rotation" value="0">
</div>
<div class="mb-3">
<label for="opacity" class="form-label" th:text="#{AddStampRequest.opacity}">Opacity</label>
<input type="number" class="form-control" id="opacity" name="opacity" step="0.1" value="0.5">
</div>
<div class="mb-3">
<label for="overrideX" class="form-label" th:text="#{AddStampRequest.overrideX}">Override X</label>
<input type="number" class="form-control" id="overrideX" name="overrideX" value="-1">
</div>
<div class="mb-3">
<label for="overrideY" class="form-label" th:text="#{AddStampRequest.overrideY}">Override Y</label>
<input type="number" class="form-control" id="overrideY" name="overrideY" value="-1">
</div>
<div class="mb-3">
<label for="customColor" class="form-label" th:text="#{AddStampRequest.customColor}">Custom Color</label>
<div class="form-control form-control-color" style="background-color: #d3d3d3;">
<input type="color" id="customColor" name="customColor" value="#d3d3d3">
</div>
<script>
let colorInput = document.getElementById("customColor");
if (colorInput) {
let colorInputContainer = colorInput.parentElement;
if (colorInputContainer) {
colorInput.onchange = function() {
colorInputContainer.style.backgroundColor = colorInput.value;
}
<script>
let colorInput = document.getElementById("customColor");
if (colorInput) {
let colorInputContainer = colorInput.parentElement;
if (colorInputContainer) {
colorInput.onchange = function () {
colorInputContainer.style.backgroundColor = colorInput.value;
}
colorInputContainer.style.backgroundColor = colorInput.value;
}
}
</script>
</div>
</script>
</div>
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{AddStampRequest.submit}"></button>
</form>
</div>
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{AddStampRequest.submit}"></button>
</form>
</div>
</div>
<script>
</div>
<script>
let cells = document.querySelectorAll('.pageNumber');
let inputField = document.getElementById('numberInput');
cells.forEach(cell => {
cell.addEventListener('click', function(e) {
cell.addEventListener('click', function (e) {
cells.forEach(cell => {
cell.classList.remove('selectedPosition'); // Remove selected class from all cells
cell.classList.remove('selectedHovered'); // Also remove selectedHovered class
@ -158,14 +166,14 @@
e.target.classList.add('selectedHovered'); // Add selectedHovered class
});
cell.addEventListener('mouseenter', function(e) {
if(e.target.classList.contains('selectedPosition')) {
cell.addEventListener('mouseenter', function (e) {
if (e.target.classList.contains('selectedPosition')) {
e.target.classList.add('selectedHovered');
}
});
cell.addEventListener('mouseleave', function(e) {
if(e.target.classList.contains('selectedPosition')) {
cell.addEventListener('mouseleave', function (e) {
if (e.target.classList.contains('selectedPosition')) {
e.target.classList.remove('selectedHovered');
}
});
@ -194,9 +202,10 @@
alphabetGroup.style.display = 'none';
}
}
</script>
</div>
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
</script>
</div>
</body>
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
</div>
</body>
</html>

View File

@ -1,44 +1,50 @@
<!DOCTYPE html>
<html th:lang="${#locale.language}" th:dir="#{language.direction}" th:data-language="${#locale.toString()}" xmlns:th="https://www.thymeleaf.org">
<head>
<th:block th:insert="~{fragments/common :: head(title=#{pageLayout.title}, header=#{pageLayout.header})}"></th:block>
</head>
<html th:lang="${#locale.language}" th:dir="#{language.direction}" th:data-language="${#locale.toString()}"
xmlns:th="https://www.thymeleaf.org">
<body>
<div id="page-container">
<div id="content-wrap">
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
<br><br>
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6 bg-card">
<div class="tool-header">
<span class="material-symbols-rounded tool-header-icon organize">dashboard</span>
<span class="tool-header-text" th:text="#{pageLayout.header}"></span>
</div>
<form id="multiPdfForm" th:action="@{'/api/v1/general/multi-page-layout'}" method="post" enctype="multipart/form-data">
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf')}"></div>
<div class="mb-3">
<label for="pagesPerSheet" th:text="#{pageLayout.pagesPerSheet}"></label>
<select class="form-control" id="pagesPerSheet" name="pagesPerSheet">
<option value="2">2</option>
<option value="3">3</option>
<option value="4">4</option>
<option value="9">9</option>
<option value="16">16</option>
</select>
</div>
<div class="form-check mb-3">
<input type="checkbox" id="addBorder" name="addBorder">
<label for="addBorder" th:text="#{pageLayout.addBorder}"></label>
</div>
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{pageLayout.submit}"></button>
</form>
<head>
<th:block th:insert="~{fragments/common :: head(title=#{pageLayout.title}, header=#{pageLayout.header})}"></th:block>
</head>
<body>
<div id="page-container">
<div id="content-wrap">
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
<br><br>
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6 bg-card">
<div class="tool-header">
<span class="material-symbols-rounded tool-header-icon organize">dashboard</span>
<span class="tool-header-text" th:text="#{pageLayout.header}"></span>
</div>
<form id="multiPdfForm" th:action="@{'/api/v1/general/multi-page-layout'}" method="post"
enctype="multipart/form-data">
<div
th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf , application/zip' )}">
</div>
<div class="mb-3">
<label for="pagesPerSheet" th:text="#{pageLayout.pagesPerSheet}"></label>
<select class="form-control" id="pagesPerSheet" name="pagesPerSheet">
<option value="2">2</option>
<option value="3">3</option>
<option value="4">4</option>
<option value="9">9</option>
<option value="16">16</option>
</select>
</div>
<div class="form-check mb-3">
<input type="checkbox" id="addBorder" name="addBorder">
<label for="addBorder" th:text="#{pageLayout.addBorder}"></label>
</div>
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{pageLayout.submit}"></button>
</form>
</div>
</div>
</div>
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
</div>
</body>
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
</div>
</body>
</html>

View File

@ -1,102 +1,115 @@
<!DOCTYPE html>
<html th:lang="${#locale.language}" th:dir="#{language.direction}" th:data-language="${#locale.toString()}" xmlns:th="https://www.thymeleaf.org">
<head>
<th:block th:insert="~{fragments/common :: head(title=#{split-by-size-or-count.title}, header=#{split-by-size-or-count.header})}"></th:block>
</head>
<html th:lang="${#locale.language}" th:dir="#{language.direction}" th:data-language="${#locale.toString()}"
xmlns:th="https://www.thymeleaf.org">
<body>
<th:block th:insert="~{fragments/common :: game}"></th:block>
<div id="page-container">
<div id="content-wrap">
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
<br><br>
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6 bg-card">
<div class="tool-header">
<span class="material-symbols-rounded tool-header-icon advance">layers</span>
<span class="tool-header-text" th:text="#{overlay-pdfs.header}"></span>
</div>
<form id="overlayForm" method="post" enctype="multipart/form-data" th:action="@{'/api/v1/general/overlay-pdfs'}">
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf')}"></div>
<div th:replace="~{fragments/common :: fileSelector(name='overlayFiles', multipleInputsForSingleRequest=true, accept='application/pdf')}"></div>
<head>
<th:block
th:insert="~{fragments/common :: head(title=#{split-by-size-or-count.title}, header=#{split-by-size-or-count.header})}">
</th:block>
</head>
<label for="overlayMode" th:text="#{overlay-pdfs.mode.label}">Overlay Mode</label>
<select id="overlayMode" name="overlayMode" class="form-control">
<option value="SequentialOverlay" th:text="#{overlay-pdfs.mode.sequential}">Sequential Overlay</option>
<option value="InterleavedOverlay" th:text="#{overlay-pdfs.mode.interleaved}">Interleaved Overlay</option>
<option value="FixedRepeatOverlay" th:text="#{overlay-pdfs.mode.fixedRepeat}">Fixed Repeat Overlay</option>
</select>
<br>
<label for="overlayPosition" th:text="#{overlay-pdfs.position.label}">Overlay Position</label>
<select id="overlayPosition" name="overlayPosition" class="form-control">
<option value="0" th:text="#{overlay-pdfs.position.foreground}">Foreground</option>
<option value="1" th:text="#{overlay-pdfs.position.background}">Background</option>
</select>
<div id="countsContainer" style="display: none;">
<label th:text="#{overlay-pdfs.counts.label}">Overlay Counts</label>
<!-- Inputs for counts will be dynamically added here -->
</div>
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{overlay-pdfs.submit}">Submit</button>
</form>
<script>
function updateCountsInputs() {
const mode = document.getElementById('overlayMode').value;
console.log("mode",mode);
const countsContainer = document.getElementById('countsContainer');
console.log("countsContainer",countsContainer);
countsContainer.innerHTML = ''; // Clear previous inputs
if (mode === 'FixedRepeatOverlay') {
const fileInput = document.getElementById('overlayFiles-input');
console.log("fileInput",fileInput);
if(fileInput){
const files = fileInput.files
console.log("files",files);
if(files) {
const fileCount = files.length;
countsContainer.appendChild(document.createElement("br"));
const label = document.createElement('label');
label.setAttribute('th:text', '#{overlay-pdfs.counts.label}');
label.textContent = "Overlay Counts";
countsContainer.appendChild(label);
for (let i = 0; i < fileCount; i++) {
const input = document.createElement('input');
input.type = 'text';
input.name = 'counts';
input.classList.add('form-control');
input.placeholder = 'Count for file ' + (i + 1);
countsContainer.appendChild(input);
}
countsContainer.style.display = 'block';
}
}
} else {
countsContainer.style.display = 'none';
}
}
document.addEventListener('DOMContentLoaded', (event) => {
var fileInput = document.getElementById('overlayFiles-input');
console.log("fileInput2",fileInput);
if (fileInput) {
fileInput.addEventListener('change', updateCountsInputs);
}
});
document.addEventListener('DOMContentLoaded', (event) => {
var overlay = document.getElementById('overlayMode');
console.log("overlay2",overlay);
if (overlay) {
overlay.addEventListener('change', updateCountsInputs);
}
});
</script>
<body>
<th:block th:insert="~{fragments/common :: game}"></th:block>
<div id="page-container">
<div id="content-wrap">
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
<br><br>
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6 bg-card">
<div class="tool-header">
<span class="material-symbols-rounded tool-header-icon advance">layers</span>
<span class="tool-header-text" th:text="#{overlay-pdfs.header}"></span>
</div>
<form id="overlayForm" method="post" enctype="multipart/form-data"
th:action="@{'/api/v1/general/overlay-pdfs'}">
<div
th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf , application/zip' )}">
</div>
<div
th:replace="~{fragments/common :: fileSelector(name='overlayFiles', multipleInputsForSingleRequest=true, accept='application/pdf , application/zip')}">
</div>
<label for="overlayMode" th:text="#{overlay-pdfs.mode.label}">Overlay Mode</label>
<select id="overlayMode" name="overlayMode" class="form-control">
<option value="SequentialOverlay" th:text="#{overlay-pdfs.mode.sequential}">Sequential Overlay</option>
<option value="InterleavedOverlay" th:text="#{overlay-pdfs.mode.interleaved}">Interleaved Overlay
</option>
<option value="FixedRepeatOverlay" th:text="#{overlay-pdfs.mode.fixedRepeat}">Fixed Repeat Overlay
</option>
</select>
<br>
<label for="overlayPosition" th:text="#{overlay-pdfs.position.label}">Overlay Position</label>
<select id="overlayPosition" name="overlayPosition" class="form-control">
<option value="0" th:text="#{overlay-pdfs.position.foreground}">Foreground</option>
<option value="1" th:text="#{overlay-pdfs.position.background}">Background</option>
</select>
<div id="countsContainer" style="display: none;">
<label th:text="#{overlay-pdfs.counts.label}">Overlay Counts</label>
<!-- Inputs for counts will be dynamically added here -->
</div>
<button type="submit" id="submitBtn" class="btn btn-primary"
th:text="#{overlay-pdfs.submit}">Submit</button>
</form>
<script>
function updateCountsInputs() {
const mode = document.getElementById('overlayMode').value;
console.log("mode", mode);
const countsContainer = document.getElementById('countsContainer');
console.log("countsContainer", countsContainer);
countsContainer.innerHTML = ''; // Clear previous inputs
if (mode === 'FixedRepeatOverlay') {
const fileInput = document.getElementById('overlayFiles-input');
console.log("fileInput", fileInput);
if (fileInput) {
const files = fileInput.files
console.log("files", files);
if (files) {
const fileCount = files.length;
countsContainer.appendChild(document.createElement("br"));
const label = document.createElement('label');
label.setAttribute('th:text', '#{overlay-pdfs.counts.label}');
label.textContent = "Overlay Counts";
countsContainer.appendChild(label);
for (let i = 0; i < fileCount; i++) {
const input = document.createElement('input');
input.type = 'text';
input.name = 'counts';
input.classList.add('form-control');
input.placeholder = 'Count for file ' + (i + 1);
countsContainer.appendChild(input);
}
countsContainer.style.display = 'block';
}
}
} else {
countsContainer.style.display = 'none';
}
}
document.addEventListener('DOMContentLoaded', (event) => {
var fileInput = document.getElementById('overlayFiles-input');
console.log("fileInput2", fileInput);
if (fileInput) {
fileInput.addEventListener('change', updateCountsInputs);
}
});
document.addEventListener('DOMContentLoaded', (event) => {
var overlay = document.getElementById('overlayMode');
console.log("overlay2", overlay);
if (overlay) {
overlay.addEventListener('change', updateCountsInputs);
}
});
</script>
</div>
</div>
</div>
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
</div>
</body>
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
</div>
</body>
</html>

View File

@ -23,7 +23,7 @@
<form th:action="@{'/api/v1/general/rearrange-pages'}" method="post" enctype="multipart/form-data">
<div
th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf')}">
th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf , application/zip' )}">
</div>
<div class="mb-3">
<label for="customMode" th:text="#{pdfOrganiser.mode}">Mode</label>

View File

@ -1,30 +1,36 @@
<!DOCTYPE html>
<html th:lang="${#locale.language}" th:dir="#{language.direction}" th:data-language="${#locale.toString()}" xmlns:th="https://www.thymeleaf.org">
<head>
<th:block th:insert="~{fragments/common :: head(title=#{pdfToSinglePage.title}, header=#{pdfToSinglePage.header})}"></th:block>
</head>
<html th:lang="${#locale.language}" th:dir="#{language.direction}" th:data-language="${#locale.toString()}"
xmlns:th="https://www.thymeleaf.org">
<body>
<div id="page-container">
<div id="content-wrap">
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
<br><br>
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6 bg-card">
<div class="tool-header">
<span class="material-symbols-rounded tool-header-icon organize">looks_one</span>
<span class="tool-header-text" th:text="#{pdfToSinglePage.header}"></span>
</div>
<form method="post" enctype="multipart/form-data" th:action="@{'/api/v1/general/pdf-to-single-page'}">
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf')}"></div>
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{pdfToSinglePage.submit}"></button>
</form>
<head>
<th:block th:insert="~{fragments/common :: head(title=#{pdfToSinglePage.title}, header=#{pdfToSinglePage.header})}">
</th:block>
</head>
<body>
<div id="page-container">
<div id="content-wrap">
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
<br><br>
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6 bg-card">
<div class="tool-header">
<span class="material-symbols-rounded tool-header-icon organize">looks_one</span>
<span class="tool-header-text" th:text="#{pdfToSinglePage.header}"></span>
</div>
<form method="post" enctype="multipart/form-data" th:action="@{'/api/v1/general/pdf-to-single-page'}">
<div
th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf , application/zip' )}">
</div>
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{pdfToSinglePage.submit}"></button>
</form>
</div>
</div>
</div>
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
</div>
</body>
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
</div>
</body>
</html>

View File

@ -1,35 +1,42 @@
<!DOCTYPE html>
<html th:lang="${#locale.language}" th:dir="#{language.direction}" th:data-language="${#locale.toString()}"
xmlns:th="https://www.thymeleaf.org">
xmlns:th="https://www.thymeleaf.org">
<head>
<th:block th:insert="~{fragments/common :: head(title=#{removeImage.title}, header=#{removeImage.header})}"></th:block>
<th:block th:insert="~{fragments/common :: head(title=#{removeImage.title}, header=#{removeImage.header})}">
</th:block>
<link rel="stylesheet" th:href="@{'/css/removeImage.css'}">
</head>
<body>
<th:block th:insert="~{fragments/common :: game}"></th:block>
<div id="page-container">
<div id="content-wrap">
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
<br><br>
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6 bg-card">
<div class="tool-header">
<span class="material-symbols-rounded tool-header-icon word">remove_selection</span>
<span class="tool-header-text" th:text="#{removeImage.header}"></span>
</div>
<form th:action="@{'api/v1/general/remove-image-pdf'}" method="post" enctype="multipart/form-data">
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf')}"></div>
<th:block th:insert="~{fragments/common :: game}"></th:block>
<div id="page-container">
<div id="content-wrap">
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
<br><br>
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6 bg-card">
<div class="tool-header">
<span class="material-symbols-rounded tool-header-icon word">remove_selection</span>
<span class="tool-header-text" th:text="#{removeImage.header}"></span>
</div>
<form th:action="@{'api/v1/general/remove-image-pdf'}" method="post"
enctype="multipart/form-data">
<div
th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf , application/zip' )}">
</div>
<br>
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{removeImage.submit}"></button>
</form>
<br>
<button type="submit" id="submitBtn" class="btn btn-primary"
th:text="#{removeImage.submit}"></button>
</form>
</div>
</div>
</div>
</div>
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
</div>
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
</div>
</body>
</html>
</html>

View File

@ -23,7 +23,7 @@
<form th:action="@{'/api/v1/general/remove-pages'}" method="post" enctype="multipart/form-data">
<div
th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf')}">
th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf , application/zip' )}">
</div>
<div class="mb-3">
<label for="fileInput" th:text="#{pageRemover.pagesToDelete}"></label>

View File

@ -23,7 +23,7 @@
<form action="#" th:action="@{'/api/v1/general/rotate-pdf'}" th:object="${rotateForm}" method="post"
enctype="multipart/form-data">
<div
th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf')}">
th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf , application/zip' )}">
</div>
<input type="hidden" id="angleInput" name="angle" value="0">

View File

@ -1,49 +1,56 @@
<!DOCTYPE html>
<html th:lang="${#locale.language}" th:dir="#{language.direction}" th:data-language="${#locale.toString()}" xmlns:th="https://www.thymeleaf.org">
<head>
<th:block th:insert="~{fragments/common :: head(title=#{scalePages.title}, header=#{scalePages.header})}"></th:block>
</head>
<html th:lang="${#locale.language}" th:dir="#{language.direction}" th:data-language="${#locale.toString()}"
xmlns:th="https://www.thymeleaf.org">
<body>
<div id="page-container">
<div id="content-wrap">
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
<br><br>
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6 bg-card">
<div class="tool-header">
<span class="material-symbols-rounded tool-header-icon organize">fullscreen</span>
<span class="tool-header-text" th:text="#{scalePages.header}"></span>
</div>
<form id="scalePagesFrom" th:action="@{'/api/v1/general/scale-pages'}" method="post" enctype="multipart/form-data">
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf')}"></div>
<div class="mb-3">
<label for="pageSize" th:text="#{scalePages.pageSize}"></label>
<select class="form-control" id="pageSize" name="pageSize">
<option value="KEEP" th:text="#{scalePages.keepPageSize}" selected></option>
<option value="A0">A0</option>
<option value="A1">A1</option>
<option value="A2">A2</option>
<option value="A3">A3</option>
<option value="A4">A4</option>
<option value="A5">A5</option>
<option value="A6">A6</option>
<option value="LETTER">Letter</option>
<option value="LEGAL">Legal</option>
</select>
</div>
<div class="mb-3">
<label for="scaleFactor" th:text="#{scalePages.scaleFactor}"></label>
<input class="form-control" type="number" id="scaleFactor" name="scaleFactor" step="any" min="0" value="1">
</div>
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{scalePages.submit}"></button>
</form>
<head>
<th:block th:insert="~{fragments/common :: head(title=#{scalePages.title}, header=#{scalePages.header})}"></th:block>
</head>
<body>
<div id="page-container">
<div id="content-wrap">
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
<br><br>
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6 bg-card">
<div class="tool-header">
<span class="material-symbols-rounded tool-header-icon organize">fullscreen</span>
<span class="tool-header-text" th:text="#{scalePages.header}"></span>
</div>
<form id="scalePagesFrom" th:action="@{'/api/v1/general/scale-pages'}" method="post"
enctype="multipart/form-data">
<div
th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf , application/zip' )}">
</div>
<div class="mb-3">
<label for="pageSize" th:text="#{scalePages.pageSize}"></label>
<select class="form-control" id="pageSize" name="pageSize">
<option value="KEEP" th:text="#{scalePages.keepPageSize}" selected></option>
<option value="A0">A0</option>
<option value="A1">A1</option>
<option value="A2">A2</option>
<option value="A3">A3</option>
<option value="A4">A4</option>
<option value="A5">A5</option>
<option value="A6">A6</option>
<option value="LETTER">Letter</option>
<option value="LEGAL">Legal</option>
</select>
</div>
<div class="mb-3">
<label for="scaleFactor" th:text="#{scalePages.scaleFactor}"></label>
<input class="form-control" type="number" id="scaleFactor" name="scaleFactor" step="any" min="0"
value="1">
</div>
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{scalePages.submit}"></button>
</form>
</div>
</div>
</div>
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
</div>
</body>
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
</div>
</body>
</html>

View File

@ -1,88 +1,97 @@
<!DOCTYPE html>
<html th:lang="${#locale.language}" th:dir="#{language.direction}" th:data-language="${#locale.toString()}" xmlns:th="https://www.thymeleaf.org">
<head>
<th:block th:insert="~{fragments/common :: head(title=#{addPassword.title}, header=#{addPassword.header})}"></th:block>
</head>
<html th:lang="${#locale.language}" th:dir="#{language.direction}" th:data-language="${#locale.toString()}"
xmlns:th="https://www.thymeleaf.org">
<body>
<div id="page-container">
<div id="content-wrap">
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
<br><br>
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6 bg-card">
<div class="tool-header">
<span class="material-symbols-rounded tool-header-icon security">lock</span>
<span class="tool-header-text" th:text="#{addPassword.header}"></span>
</div>
<form th:action="@{'api/v1/security/add-password'}" method="post" enctype="multipart/form-data">
<div class="mb-3">
<label th:text="#{addPassword.selectText.1}"></label>
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf')}"></div>
</div>
<div class="mb-3">
<label th:text="#{addPassword.selectText.14}"></label> <input type="password" class="form-control" id="ownerPassword" name="ownerPassword">
<small class="form-text text-muted" th:text="#{addPassword.selectText.15}"></small>
</div>
<div class="mb-3">
<label th:text="#{addPassword.selectText.2}"></label> <input type="password" class="form-control" id="password" name="password">
<small class="form-text text-muted" th:text="#{addPassword.selectText.16}"></small>
</div>
<head>
<th:block th:insert="~{fragments/common :: head(title=#{addPassword.title}, header=#{addPassword.header})}">
</th:block>
</head>
<div class="mb-3">
<label th:text="#{addPassword.selectText.3}"></label> <select class="form-control" id="keyLength" name="keyLength">
<option value="40">40</option>
<option value="128">128</option>
<option value="256">256</option>
</select>
<small class="form-text text-muted" th:text="#{addPassword.selectText.4}"></small>
</div>
<div class="mb-3">
<label class="mb-2" th:text="#{addPassword.selectText.5}"></label>
<div class="form-check ms-3">
<input type="checkbox" id="canAssembleDocument" name="canAssembleDocument">
<label for="canAssembleDocument" th:text="#{addPassword.selectText.6}"></label>
</div>
<div class="form-check ms-3">
<input type="checkbox" id="canExtractContent" name="canExtractContent">
<label for="canExtractContent" th:text="#{addPassword.selectText.7}"></label>
</div>
<div class="form-check ms-3">
<input type="checkbox" id="canExtractForAccessibility" name="canExtractForAccessibility">
<label for="canExtractForAccessibility" th:text="#{addPassword.selectText.8}"></label>
</div>
<div class="form-check ms-3">
<input type="checkbox" id="canFillInForm" name="canFillInForm">
<label for="canFillInForm" th:text="#{addPassword.selectText.9}"></label>
</div>
<div class="form-check ms-3">
<input type="checkbox" id="canModify" name="canModify">
<label for="canModify" th:text="#{addPassword.selectText.10}"></label>
</div>
<div class="form-check ms-3">
<input type="checkbox" id="canModifyAnnotations" name="canModifyAnnotations">
<label for="canModifyAnnotations" th:text="#{addPassword.selectText.11}"></label>
</div>
<div class="form-check ms-3">
<input type="checkbox" id="canPrint" name="canPrint">
<label for="canPrint" th:text="#{addPassword.selectText.12}"></label>
</div>
<div class="form-check ms-3">
<input type="checkbox" id="canPrintFaithful" name="canPrintFaithful">
<label for="canPrintFaithful" th:text="#{addPassword.selectText.13}"></label>
</div>
</div>
<br>
<div class="mb-3 text-left">
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{addPassword.submit}"></button>
</div>
</form>
<body>
<div id="page-container">
<div id="content-wrap">
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
<br><br>
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6 bg-card">
<div class="tool-header">
<span class="material-symbols-rounded tool-header-icon security">lock</span>
<span class="tool-header-text" th:text="#{addPassword.header}"></span>
</div>
<form th:action="@{'api/v1/security/add-password'}" method="post" enctype="multipart/form-data">
<div class="mb-3">
<label th:text="#{addPassword.selectText.1}"></label>
<div
th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf , application/zip' )}">
</div>
</div>
<div class="mb-3">
<label th:text="#{addPassword.selectText.14}"></label> <input type="password" class="form-control"
id="ownerPassword" name="ownerPassword">
<small class="form-text text-muted" th:text="#{addPassword.selectText.15}"></small>
</div>
<div class="mb-3">
<label th:text="#{addPassword.selectText.2}"></label> <input type="password" class="form-control"
id="password" name="password">
<small class="form-text text-muted" th:text="#{addPassword.selectText.16}"></small>
</div>
<div class="mb-3">
<label th:text="#{addPassword.selectText.3}"></label> <select class="form-control" id="keyLength"
name="keyLength">
<option value="40">40</option>
<option value="128">128</option>
<option value="256">256</option>
</select>
<small class="form-text text-muted" th:text="#{addPassword.selectText.4}"></small>
</div>
<div class="mb-3">
<label class="mb-2" th:text="#{addPassword.selectText.5}"></label>
<div class="form-check ms-3">
<input type="checkbox" id="canAssembleDocument" name="canAssembleDocument">
<label for="canAssembleDocument" th:text="#{addPassword.selectText.6}"></label>
</div>
<div class="form-check ms-3">
<input type="checkbox" id="canExtractContent" name="canExtractContent">
<label for="canExtractContent" th:text="#{addPassword.selectText.7}"></label>
</div>
<div class="form-check ms-3">
<input type="checkbox" id="canExtractForAccessibility" name="canExtractForAccessibility">
<label for="canExtractForAccessibility" th:text="#{addPassword.selectText.8}"></label>
</div>
<div class="form-check ms-3">
<input type="checkbox" id="canFillInForm" name="canFillInForm">
<label for="canFillInForm" th:text="#{addPassword.selectText.9}"></label>
</div>
<div class="form-check ms-3">
<input type="checkbox" id="canModify" name="canModify">
<label for="canModify" th:text="#{addPassword.selectText.10}"></label>
</div>
<div class="form-check ms-3">
<input type="checkbox" id="canModifyAnnotations" name="canModifyAnnotations">
<label for="canModifyAnnotations" th:text="#{addPassword.selectText.11}"></label>
</div>
<div class="form-check ms-3">
<input type="checkbox" id="canPrint" name="canPrint">
<label for="canPrint" th:text="#{addPassword.selectText.12}"></label>
</div>
<div class="form-check ms-3">
<input type="checkbox" id="canPrintFaithful" name="canPrintFaithful">
<label for="canPrintFaithful" th:text="#{addPassword.selectText.13}"></label>
</div>
</div>
<br>
<div class="mb-3 text-left">
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{addPassword.submit}"></button>
</div>
</form>
</div>
</div>
</div>
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
</div>
</body>
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
</div>
</body>
</html>

View File

@ -1,175 +1,182 @@
<!DOCTYPE html>
<html th:lang="${#locale.language}" th:dir="#{language.direction}" th:data-language="${#locale.toString()}" xmlns:th="https://www.thymeleaf.org">
<head>
<th:block th:insert="~{fragments/common :: head(title=#{watermark.title}, header=#{watermark.header})}"></th:block>
</head>
<html th:lang="${#locale.language}" th:dir="#{language.direction}" th:data-language="${#locale.toString()}"
xmlns:th="https://www.thymeleaf.org">
<body onload="toggleFileOption()">
<div id="page-container">
<div id="content-wrap">
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
<br><br>
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6 bg-card">
<div class="tool-header">
<span class="material-symbols-rounded tool-header-icon security">water_drop</span>
<span class="tool-header-text" th:text="#{watermark.header}"></span>
<head>
<th:block th:insert="~{fragments/common :: head(title=#{watermark.title}, header=#{watermark.header})}"></th:block>
</head>
<body onload="toggleFileOption()">
<div id="page-container">
<div id="content-wrap">
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
<br><br>
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6 bg-card">
<div class="tool-header">
<span class="material-symbols-rounded tool-header-icon security">water_drop</span>
<span class="tool-header-text" th:text="#{watermark.header}"></span>
</div>
<form method="post" enctype="multipart/form-data" th:action="@{'api/v1/security/add-watermark'}">
<div class="mb-3">
<label th:text="#{watermark.selectText.1}"></label>
<div
th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf , application/zip' )}">
</div>
</div>
<form method="post" enctype="multipart/form-data" th:action="@{'api/v1/security/add-watermark'}">
<div class="mb-3">
<label th:text="#{watermark.selectText.1}"></label>
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf')}"></div>
</div>
<div class="mb-3">
<label th:text="#{watermark.selectText.8}"></label>
<select class="form-control" id="watermarkType" name="watermarkType" onchange="toggleFileOption()">
<option value="text" th:text="#{watermark.type.1}"></option>
<option value="image" th:text="#{watermark.type.2}"></option>
</select>
</div>
<div id="alphabetGroup" class="mb-3">
<label for="fontSize" th:text="#{alphabet} + ':'"></label>
<select class="form-control" name="alphabet" id="alphabet-select">
<option value="roman">Roman</option>
<option value="arabic">العربية</option>
<option value="japanese">日本語</option>
<option value="korean">한국어</option>
<option value="chinese">简体中文</option>
</select>
</div>
<div id="watermarkTextGroup" class="mb-3">
<label for="watermarkText" th:text="#{watermark.selectText.2}"></label>
<input type="text" id="watermarkText" name="watermarkText" class="form-control"
placeholder="Stirling-PDF" required>
</div>
<div class="mb-3">
<label th:text="#{watermark.selectText.8}"></label>
<select class="form-control" id="watermarkType" name="watermarkType" onchange="toggleFileOption()">
<option value="text" th:text="#{watermark.type.1}"></option>
<option value="image" th:text="#{watermark.type.2}"></option>
</select>
</div>
<div id="alphabetGroup" class="mb-3">
<label for="fontSize" th:text="#{alphabet} + ':'"></label>
<select class="form-control" name="alphabet" id="alphabet-select">
<option value="roman">Roman</option>
<option value="arabic">العربية</option>
<option value="japanese">日本語</option>
<option value="korean">한국어</option>
<option value="chinese">简体中文</option>
</select>
</div>
<div id="watermarkTextGroup" class="mb-3">
<label for="watermarkText" th:text="#{watermark.selectText.2}"></label>
<input type="text" id="watermarkText" name="watermarkText" class="form-control" placeholder="Stirling-PDF" required>
</div>
<div id="watermarkImageGroup" class="mb-3" style="display: none;">
<label for="watermarkImage" th:text="#{watermark.selectText.9}"></label>
<input type="file" id="watermarkImage" name="watermarkImage" class="form-control-file" accept="image/*">
</div>
<div id="watermarkImageGroup" class="mb-3" style="display: none;">
<label for="watermarkImage" th:text="#{watermark.selectText.9}"></label>
<input type="file" id="watermarkImage" name="watermarkImage" class="form-control-file" accept="image/*">
</div>
<div class="mb-3">
<label for="fontSize" th:text="#{watermark.selectText.3}"></label>
<input type="text" id="fontSize" name="fontSize" class="form-control" value="30">
</div>
<div class="mb-3">
<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');
<div class="mb-3">
<label for="fontSize" th:text="#{watermark.selectText.3}"></label>
<input type="text" id="fontSize" name="fontSize" class="form-control" value="30">
</div>
<div class="mb-3">
<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 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 += '%';
}
};
const appendPercentageSymbol = () => {
if (!opacityInput.value.endsWith('%')) {
opacityInput.value += '%';
}
};
opacityInput.addEventListener('focus', () => {
opacityInput.value = opacityInput.value.replace('%', '');
});
opacityInput.addEventListener('blur', () => {
updateOpacityValue();
appendPercentageSymbol();
});
// Set initial values
opacityInput.addEventListener('focus', () => {
opacityInput.value = opacityInput.value.replace('%', '');
});
opacityInput.addEventListener('blur', () => {
updateOpacityValue();
appendPercentageSymbol();
</script>
});
<div class="mb-3">
<label for="rotation" th:text="#{watermark.selectText.4}"></label>
<input type="text" id="rotation" name="rotation" class="form-control" value="45">
</div>
<div class="mb-3">
<label for="widthSpacer" th:text="#{watermark.selectText.5}"></label>
<input type="text" id="widthSpacer" name="widthSpacer" class="form-control" value="50">
</div>
<div class="mb-3">
<label for="heightSpacer" th:text="#{watermark.selectText.6}"></label>
<input type="text" id="heightSpacer" name="heightSpacer" class="form-control" value="50">
</div>
<div class="mb-3">
<label for="customColor" class="form-label" th:text="#{watermark.customColor}">Custom
Color</label>
<div class="form-control form-control-color" style="background-color: #d3d3d3;">
<input type="color" id="customColor" name="customColor" value="#d3d3d3">
</div>
<script>
let colorInput = document.getElementById("customColor");
if (colorInput) {
let colorInputContainer = colorInput.parentElement;
if (colorInputContainer) {
colorInput.onchange = function() {
colorInputContainer.style.backgroundColor = colorInput.value;
}
colorInputContainer.style.backgroundColor = colorInput.value;
}
}
</script>
</div>
<div class="mb-3 form-check">
<input type="checkbox" id="convertPDFToImage" name="convertPDFToImage">
<label for="convertPDFToImage" th:text="#{watermark.selectText.10}"></label>
</div>
<div class="mb-3 text-left">
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{watermark.submit}"></button>
</div>
</form>
<script>
function toggleFileOption() {
const watermarkType = document.getElementById('watermarkType').value;
const watermarkTextGroup = document.getElementById('watermarkTextGroup');
const watermarkImageGroup = document.getElementById('watermarkImageGroup');
const alphabetGroup = document.getElementById('alphabetGroup'); // This is the new addition
const watermarkText = document.getElementById('watermarkText');
const watermarkImage = document.getElementById('watermarkImage');
if (watermarkType === 'text') {
if (watermarkImage.hasAttribute('required')) {
watermarkImage.removeAttribute('required');
}
watermarkTextGroup.style.display = 'block';
watermarkText.required = true;
watermarkImageGroup.style.display = 'none';
watermarkImage.required = false;
alphabetGroup.style.display = 'block';
} else if (watermarkType === 'image') {
if (watermarkImage.hasAttribute('required')) {
watermarkImage.removeAttribute('required');
}
watermarkTextGroup.style.display = 'none';
watermarkText.required = false;
watermarkImageGroup.style.display = 'block';
watermarkImage.required = true;
alphabetGroup.style.display = 'none';
}
}
// Set initial values
updateOpacityValue();
appendPercentageSymbol();
</script>
</div>
<div class="mb-3">
<label for="rotation" th:text="#{watermark.selectText.4}"></label>
<input type="text" id="rotation" name="rotation" class="form-control" value="45">
</div>
<div class="mb-3">
<label for="widthSpacer" th:text="#{watermark.selectText.5}"></label>
<input type="text" id="widthSpacer" name="widthSpacer" class="form-control" value="50">
</div>
<div class="mb-3">
<label for="heightSpacer" th:text="#{watermark.selectText.6}"></label>
<input type="text" id="heightSpacer" name="heightSpacer" class="form-control" value="50">
</div>
<div class="mb-3">
<label for="customColor" class="form-label" th:text="#{watermark.customColor}">Custom
Color</label>
<div class="form-control form-control-color" style="background-color: #d3d3d3;">
<input type="color" id="customColor" name="customColor" value="#d3d3d3">
</div>
<script>
let colorInput = document.getElementById("customColor");
if (colorInput) {
let colorInputContainer = colorInput.parentElement;
if (colorInputContainer) {
colorInput.onchange = function () {
colorInputContainer.style.backgroundColor = colorInput.value;
}
colorInputContainer.style.backgroundColor = colorInput.value;
}
}
</script>
</div>
<div class="mb-3 form-check">
<input type="checkbox" id="convertPDFToImage" name="convertPDFToImage">
<label for="convertPDFToImage" th:text="#{watermark.selectText.10}"></label>
</div>
<div class="mb-3 text-left">
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{watermark.submit}"></button>
</div>
</form>
<script>
function toggleFileOption() {
const watermarkType = document.getElementById('watermarkType').value;
const watermarkTextGroup = document.getElementById('watermarkTextGroup');
const watermarkImageGroup = document.getElementById('watermarkImageGroup');
const alphabetGroup = document.getElementById('alphabetGroup'); // This is the new addition
const watermarkText = document.getElementById('watermarkText');
const watermarkImage = document.getElementById('watermarkImage');
if (watermarkType === 'text') {
if (watermarkImage.hasAttribute('required')) {
watermarkImage.removeAttribute('required');
}
watermarkTextGroup.style.display = 'block';
watermarkText.required = true;
watermarkImageGroup.style.display = 'none';
watermarkImage.required = false;
alphabetGroup.style.display = 'block';
} else if (watermarkType === 'image') {
if (watermarkImage.hasAttribute('required')) {
watermarkImage.removeAttribute('required');
}
watermarkTextGroup.style.display = 'none';
watermarkText.required = false;
watermarkImageGroup.style.display = 'block';
watermarkImage.required = true;
alphabetGroup.style.display = 'none';
}
}
</script>
</div>
</div>
</div>
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
</div>
</body>
</html>
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
</div>
</body>
</html>

View File

@ -1,116 +1,133 @@
<!DOCTYPE html>
<html th:lang="${#locale.language}" th:dir="#{language.direction}" th:data-language="${#locale.toString()}" xmlns:th="https://www.thymeleaf.org">
<head>
<th:block th:insert="~{fragments/common :: head(title=#{certSign.title}, header=#{certSign.header})}"></th:block>
</head>
<html th:lang="${#locale.language}" th:dir="#{language.direction}" th:data-language="${#locale.toString()}"
xmlns:th="https://www.thymeleaf.org">
<body>
<div id="page-container">
<div id="content-wrap">
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
<br><br>
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6 bg-card">
<div class="tool-header">
<span class="material-symbols-rounded tool-header-icon security">workspace_premium</span>
<span class="tool-header-text" th:text="#{certSign.header}"></span>
<head>
<th:block th:insert="~{fragments/common :: head(title=#{certSign.title}, header=#{certSign.header})}"></th:block>
</head>
<body>
<div id="page-container">
<div id="content-wrap">
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
<br><br>
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6 bg-card">
<div class="tool-header">
<span class="material-symbols-rounded tool-header-icon security">workspace_premium</span>
<span class="tool-header-text" th:text="#{certSign.header}"></span>
</div>
<form th:action="@{'api/v1/security/cert-sign'}" method="post" enctype="multipart/form-data">
<div class="mb-3">
<label th:text="#{certSign.selectPDF}"></label>
<div
th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf , application/zip' )}">
</div>
</div>
<form th:action="@{'api/v1/security/cert-sign'}" method="post" enctype="multipart/form-data">
<!-- Tell users to use keytool to generate JKS for other formats -->
<div class="mb-3">
<label th:text="#{certSign.jksNote}"></label>
</div>
<div class="mb-3">
<label for="certType" th:text="#{certSign.certType}"></label>
<select class="form-control" id="certType" name="certType">
<option value="" th:text="#{selectFillter}"></option>
<option value="PEM">PEM</option>
<option value="PKCS12">PKCS12</option>
<option value="JKS">JKS</option>
</select>
</div>
<div id="pemGroup" style="display: none;">
<div class="mb-3">
<label th:text="#{certSign.selectPDF}"></label>
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf')}"></div>
</div>
<!-- Tell users to use keytool to generate JKS for other formats -->
<div class="mb-3">
<label th:text="#{certSign.jksNote}"></label>
</div>
<div class="mb-3">
<label for="certType" th:text="#{certSign.certType}"></label>
<select class="form-control" id="certType" name="certType">
<option value="" th:text="#{selectFillter}"></option>
<option value="PEM">PEM</option>
<option value="PKCS12">PKCS12</option>
<option value="JKS">JKS</option>
</select>
</div>
<div id="pemGroup" style="display: none;">
<div class="mb-3">
<label th:text="#{certSign.selectKey}"></label>
<div th:replace="~{fragments/common :: fileSelector(name='privateKeyFile', multipleInputsForSingleRequest=false, notRequired=true, accept='.pem,.der')}"></div>
</div>
<div class="mb-3">
<label th:text="#{certSign.selectCert}"></label>
<div th:replace="~{fragments/common :: fileSelector(name='certFile', multipleInputsForSingleRequest=false, notRequired=true, accept='.pem,.der')}"></div>
<label th:text="#{certSign.selectKey}"></label>
<div
th:replace="~{fragments/common :: fileSelector(name='privateKeyFile', multipleInputsForSingleRequest=false, notRequired=true, accept='.pem,.der')}">
</div>
</div>
<div class="mb-3" id="p12Group" style="display: none;">
<label th:text="#{certSign.selectP12}"></label>
<div th:replace="~{fragments/common :: fileSelector(name='p12File', notRequired=true, multipleInputsForSingleRequest=false, accept='.p12,.pfx')}"></div>
<div class="mb-3">
<label th:text="#{certSign.selectCert}"></label>
<div
th:replace="~{fragments/common :: fileSelector(name='certFile', multipleInputsForSingleRequest=false, notRequired=true, accept='.pem,.der')}">
</div>
</div>
<div class="mb-3" id="jksGroup" style="display: none;">
<label th:text="#{certSign.selectJKS}"></label>
<div th:replace="~{fragments/common :: fileSelector(name='jksFile', notRequired=true, multipleInputsForSingleRequest=false, accept='.jks,.keystore')}"></div>
</div>
<div class="mb-3" id="p12Group" style="display: none;">
<label th:text="#{certSign.selectP12}"></label>
<div
th:replace="~{fragments/common :: fileSelector(name='p12File', notRequired=true, multipleInputsForSingleRequest=false, accept='.p12,.pfx')}">
</div>
</div>
<div class="mb-3" id="jksGroup" style="display: none;">
<label th:text="#{certSign.selectJKS}"></label>
<div
th:replace="~{fragments/common :: fileSelector(name='jksFile', notRequired=true, multipleInputsForSingleRequest=false, accept='.jks,.keystore')}">
</div>
</div>
<div class="mb-3">
<label th:text="#{certSign.password}" for="password"></label>
<input type="password" class="form-control" id="password" name="password">
</div>
<div class="form-check mb-3">
<input type="checkbox" id="showSignature" name="showSignature">
<label th:text="#{certSign.showSig}" for="showSignature"></label>
</div>
<div id="signatureDetails" style="display: none;">
<div class="mb-3">
<label for="reason" th:text="#{certSign.reason}"></label> <input type="text" class="form-control"
id="reason" name="reason">
</div>
<div class="mb-3">
<label th:text="#{certSign.password}" for="password"></label>
<input type="password" class="form-control" id="password" name="password">
<label for="location" th:text="#{certSign.location}"></label> <input type="text" class="form-control"
id="location" name="location">
</div>
<div class="mb-3">
<label for="name" th:text="#{certSign.name}"></label> <input type="text" class="form-control"
id="name" name="name">
</div>
<div class="mb-3">
<label for="pageNumber" th:text="#{pageNum}"></label> <input type="number" class="form-control"
id="pageNumber" name="pageNumber" min="1" value="1">
</div>
<div class="form-check mb-3">
<input type="checkbox" id="showSignature" name="showSignature">
<label th:text="#{certSign.showSig}" for="showSignature"></label>
<input type="checkbox" id="showLogo" name="showLogo" checked />
<label th:text="#{certSign.showLogo}" for="showLogo"></label>
</div>
<div id="signatureDetails" style="display: none;">
<div class="mb-3">
<label for="reason" th:text="#{certSign.reason}"></label> <input type="text" class="form-control" id="reason" name="reason">
</div>
<div class="mb-3">
<label for="location" th:text="#{certSign.location}"></label> <input type="text" class="form-control" id="location" name="location">
</div>
<div class="mb-3">
<label for="name" th:text="#{certSign.name}"></label> <input type="text" class="form-control" id="name" name="name">
</div>
<div class="mb-3">
<label for="pageNumber" th:text="#{pageNum}"></label> <input type="number" class="form-control" id="pageNumber" name="pageNumber" min="1" value="1">
</div>
<div class="form-check mb-3">
<input type="checkbox" id="showLogo" name="showLogo" checked />
<label th:text="#{certSign.showLogo}" for="showLogo"></label>
</div>
</div>
<div class="mb-3 text-left">
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{certSign.submit}"></button>
</div>
</form>
</div>
</div>
<div class="mb-3 text-left">
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{certSign.submit}"></button>
</div>
</form>
</div>
</div>
</div>
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
</div>
<script>
document.getElementById('certType').addEventListener('change', function() {
var pemGroup = document.getElementById('pemGroup');
var p12Group = document.getElementById('p12Group');
var jksGroup = document.getElementById('jksGroup');
var valueToGroupMap = {
'PEM': pemGroup,
'PKCS12': p12Group,
'JKS': jksGroup
};
for (var key in valueToGroupMap) {
valueToGroupMap[key].style.display = (this.value === key) ? 'block' : 'none';
}
});
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
</div>
<script>
document.getElementById('certType').addEventListener('change', function () {
var pemGroup = document.getElementById('pemGroup');
var p12Group = document.getElementById('p12Group');
var jksGroup = document.getElementById('jksGroup');
var valueToGroupMap = {
'PEM': pemGroup,
'PKCS12': p12Group,
'JKS': jksGroup
};
for (var key in valueToGroupMap) {
valueToGroupMap[key].style.display = (this.value === key) ? 'block' : 'none';
}
});
document.getElementById('showSignature').addEventListener('change', function () {
var signatureDetails = document.getElementById('signatureDetails');
if (this.checked) {
signatureDetails.style.display = 'block';
} else {
signatureDetails.style.display = 'none';
}
});
</script>
</body>
document.getElementById('showSignature').addEventListener('change', function() {
var signatureDetails = document.getElementById('signatureDetails');
if (this.checked) {
signatureDetails.style.display = 'block';
} else {
signatureDetails.style.display = 'none';
}
});
</script>
</body>
</html>

View File

@ -1,72 +1,78 @@
<!DOCTYPE html>
<html th:lang="${#locale.language}" th:dir="#{language.direction}" th:data-language="${#locale.toString()}" xmlns:th="https://www.thymeleaf.org">
<head>
<th:block th:insert="~{fragments/common :: head(title=#{permissions.title}, header=#{permissions.header})}"></th:block>
</head>
<html th:lang="${#locale.language}" th:dir="#{language.direction}" th:data-language="${#locale.toString()}"
xmlns:th="https://www.thymeleaf.org">
<body>
<div id="page-container">
<div id="content-wrap">
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
<br><br>
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6 bg-card">
<div class="tool-header">
<span class="material-symbols-rounded tool-header-icon security">encrypted</span>
<span class="tool-header-text" th:text="#{permissions.header}"></span>
</div>
<p th:text="#{permissions.warning}"></p>
<form th:action="@{'api/v1/security/add-password'}" method="post" enctype="multipart/form-data">
<div class="mb-3">
<label th:text="#{permissions.selectText.1}"></label>
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf')}"></div>
</div>
<div class="mb-3">
<label class="mb-2" th:text="#{permissions.selectText.2}"></label>
<div class="form-check ms-3">
<input type="checkbox" id="canAssembleDocument" name="canAssembleDocument">
<label for="canAssembleDocument" th:text="#{permissions.selectText.3}"></label>
</div>
<div class="form-check ms-3">
<input type="checkbox" id="canExtractContent" name="canExtractContent">
<label for="canExtractContent" th:text="#{permissions.selectText.4}"></label>
</div>
<div class="form-check ms-3">
<input type="checkbox" id="canExtractForAccessibility" name="canExtractForAccessibility">
<label for="canExtractForAccessibility" th:text="#{permissions.selectText.5}"></label>
</div>
<div class="form-check ms-3">
<input type="checkbox" id="canFillInForm" name="canFillInForm">
<label for="canFillInForm" th:text="#{permissions.selectText.6}"></label>
</div>
<div class="form-check ms-3">
<input type="checkbox" id="canModify" name="canModify">
<label for="canModify" th:text="#{permissions.selectText.7}"></label>
</div>
<div class="form-check ms-3">
<input type="checkbox" id="canModifyAnnotations" name="canModifyAnnotations">
<label for="canModifyAnnotations" th:text="#{permissions.selectText.8}"></label>
</div>
<div class="form-check ms-3">
<input type="checkbox" id="canPrint" name="canPrint">
<label for="canPrint" th:text="#{permissions.selectText.9}"></label>
</div>
<div class="form-check ms-3">
<input type="checkbox" id="canPrintFaithful" name="canPrintFaithful">
<label for="canPrintFaithful" th:text="#{permissions.selectText.10}"></label>
</div>
</div>
<br>
<div class="mb-3 text-left">
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{permissions.submit}"></button>
</div>
</form>
<head>
<th:block th:insert="~{fragments/common :: head(title=#{permissions.title}, header=#{permissions.header})}">
</th:block>
</head>
<body>
<div id="page-container">
<div id="content-wrap">
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
<br><br>
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6 bg-card">
<div class="tool-header">
<span class="material-symbols-rounded tool-header-icon security">encrypted</span>
<span class="tool-header-text" th:text="#{permissions.header}"></span>
</div>
<p th:text="#{permissions.warning}"></p>
<form th:action="@{'api/v1/security/add-password'}" method="post" enctype="multipart/form-data">
<div class="mb-3">
<label th:text="#{permissions.selectText.1}"></label>
<div
th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf , application/zip' )}">
</div>
</div>
<div class="mb-3">
<label class="mb-2" th:text="#{permissions.selectText.2}"></label>
<div class="form-check ms-3">
<input type="checkbox" id="canAssembleDocument" name="canAssembleDocument">
<label for="canAssembleDocument" th:text="#{permissions.selectText.3}"></label>
</div>
<div class="form-check ms-3">
<input type="checkbox" id="canExtractContent" name="canExtractContent">
<label for="canExtractContent" th:text="#{permissions.selectText.4}"></label>
</div>
<div class="form-check ms-3">
<input type="checkbox" id="canExtractForAccessibility" name="canExtractForAccessibility">
<label for="canExtractForAccessibility" th:text="#{permissions.selectText.5}"></label>
</div>
<div class="form-check ms-3">
<input type="checkbox" id="canFillInForm" name="canFillInForm">
<label for="canFillInForm" th:text="#{permissions.selectText.6}"></label>
</div>
<div class="form-check ms-3">
<input type="checkbox" id="canModify" name="canModify">
<label for="canModify" th:text="#{permissions.selectText.7}"></label>
</div>
<div class="form-check ms-3">
<input type="checkbox" id="canModifyAnnotations" name="canModifyAnnotations">
<label for="canModifyAnnotations" th:text="#{permissions.selectText.8}"></label>
</div>
<div class="form-check ms-3">
<input type="checkbox" id="canPrint" name="canPrint">
<label for="canPrint" th:text="#{permissions.selectText.9}"></label>
</div>
<div class="form-check ms-3">
<input type="checkbox" id="canPrintFaithful" name="canPrintFaithful">
<label for="canPrintFaithful" th:text="#{permissions.selectText.10}"></label>
</div>
</div>
<br>
<div class="mb-3 text-left">
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{permissions.submit}"></button>
</div>
</form>
</div>
</div>
</div>
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
</div>
</body>
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
</div>
</body>
</html>

View File

@ -1,157 +1,164 @@
<!DOCTYPE html>
<html th:lang="${#locale.language}" th:dir="#{language.direction}" th:data-language="${#locale.toString()}" xmlns:th="https://www.thymeleaf.org">
<head>
<html th:lang="${#locale.language}" th:dir="#{language.direction}" th:data-language="${#locale.toString()}"
xmlns:th="https://www.thymeleaf.org">
<head>
<th:block th:insert="~{fragments/common :: head(title=#{getPdfInfo.title}, header=#{getPdfInfo.header})}"></th:block>
</head>
</head>
<body>
<div id="page-container">
<div id="content-wrap">
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
<br><br>
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6 bg-card">
<div class="tool-header">
<span class="material-symbols-rounded tool-header-icon other">info</span>
<span class="tool-header-text" th:text="#{getPdfInfo.header}"></span>
<body>
<div id="page-container">
<div id="content-wrap">
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
<br><br>
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6 bg-card">
<div class="tool-header">
<span class="material-symbols-rounded tool-header-icon other">info</span>
<span class="tool-header-text" th:text="#{getPdfInfo.header}"></span>
</div>
<form id="pdfInfoForm" method="post" enctype="multipart/form-data"
th:action="@{'/api/v1/security/get-info-on-pdf'}">
<div
th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, remoteCall='false', accept='application/pdf , application/zip')}">
</div>
<form id="pdfInfoForm" method="post" enctype="multipart/form-data" th:action="@{'/api/v1/security/get-info-on-pdf'}">
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, remoteCall='false', accept='application/pdf')}"></div>
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{getPdfInfo.submit}"></button>
</form>
<div class="container mt-0">
<!-- Iterate over each main section in the JSON -->
<div id="json-content">
<!-- JavaScript will populate this section -->
</div>
<!-- Button to download the JSON -->
<a href="#" id="downloadJson" class="btn btn-primary mt-3" style="display: none;" th:text="#{getPdfInfo.downloadJson}">Download JSON</a>
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{getPdfInfo.submit}"></button>
</form>
<div class="container mt-0">
<!-- Iterate over each main section in the JSON -->
<div id="json-content">
<!-- JavaScript will populate this section -->
</div>
<script th:src="@{'/js/fetch-utils.js'}"></script>
<script>
document.getElementById("pdfInfoForm").addEventListener("submit", function(event) {
event.preventDefault();
<!-- Button to download the JSON -->
<a href="#" id="downloadJson" class="btn btn-primary mt-3" style="display: none;"
th:text="#{getPdfInfo.downloadJson}">Download JSON</a>
</div>
<script th:src="@{'/js/fetch-utils.js'}"></script>
<script>
const formData = new FormData(event.target);
document.getElementById("pdfInfoForm").addEventListener("submit", function (event) {
event.preventDefault();
fetchWithCsrf('api/v1/security/get-info-on-pdf', {
method: 'POST',
body: formData
}).then(response => response.json()).then(data => {
displayJsonData(data);
setDownloadLink(data);
document.getElementById("downloadJson").style.display = "block";
}).catch(error => console.error('Error:', error));
});
const formData = new FormData(event.target);
function displayJsonData(jsonData) {
const jsonContent = document.getElementById('json-content');
fetchWithCsrf('api/v1/security/get-info-on-pdf', {
method: 'POST',
body: formData
}).then(response => response.json()).then(data => {
displayJsonData(data);
setDownloadLink(data);
document.getElementById("downloadJson").style.display = "block";
}).catch(error => console.error('Error:', error));
});
while (jsonContent.firstChild) {
jsonContent.removeChild(jsonContent.firstChild);
}
function displayJsonData(jsonData) {
const jsonContent = document.getElementById('json-content');
for (const key in jsonData) {
const sectionElem = createJsonSection(key, jsonData[key]);
jsonContent.appendChild(sectionElem);
}
while (jsonContent.firstChild) {
jsonContent.removeChild(jsonContent.firstChild);
}
function setDownloadLink(jsonData) {
const downloadLink = document.getElementById('downloadJson');
const dataStr = "data:text/json;charset=utf-8," + encodeURIComponent(JSON.stringify(jsonData, null, 4));
downloadLink.setAttribute("href", dataStr);
downloadLink.setAttribute("download", "data.json");
for (const key in jsonData) {
const sectionElem = createJsonSection(key, jsonData[key]);
jsonContent.appendChild(sectionElem);
}
}
function createJsonSection(key, value, depth = 0) {
let safeKey = (typeof key === "string") ? key.replace(/[^a-zA-Z0-9]/g, '_') : key;
const card = document.createElement('div');
card.className = 'card mb-3';
function setDownloadLink(jsonData) {
const downloadLink = document.getElementById('downloadJson');
const dataStr = "data:text/json;charset=utf-8," + encodeURIComponent(JSON.stringify(jsonData, null, 4));
downloadLink.setAttribute("href", dataStr);
downloadLink.setAttribute("download", "data.json");
}
const header = document.createElement('div');
header.className = 'card-header';
header.id = `${safeKey}-heading-${depth}`;
const h5Elem = document.createElement('h5');
h5Elem.className = 'mb-0';
function createJsonSection(key, value, depth = 0) {
let safeKey = (typeof key === "string") ? key.replace(/[^a-zA-Z0-9]/g, '_') : key;
const card = document.createElement('div');
card.className = 'card mb-3';
if (key === 'XMPMetadata' && typeof value === "string") {
const header = document.createElement('div');
header.className = 'card-header';
header.id = `${safeKey}-heading-${depth}`;
const h5Elem = document.createElement('h5');
h5Elem.className = 'mb-0';
if (key === 'XMPMetadata' && typeof value === "string") {
const buttonElem = createButtonElement(key, safeKey, depth);
h5Elem.appendChild(buttonElem);
} else if (value && typeof value === 'object') {
if (Array.isArray(value) && value.length === 0) {
h5Elem.textContent = `${key}: Empty array`;
} else if (!Array.isArray(value) && Object.keys(value).length === 0) {
h5Elem.textContent = `${key}: Empty object`;
} else {
const buttonElem = createButtonElement(key, safeKey, depth);
h5Elem.appendChild(buttonElem);
} else if (value && typeof value === 'object') {
if (Array.isArray(value) && value.length === 0) {
h5Elem.textContent = `${key}: Empty array`;
} else if (!Array.isArray(value) && Object.keys(value).length === 0) {
h5Elem.textContent = `${key}: Empty object`;
} else {
const buttonElem = createButtonElement(key, safeKey, depth);
h5Elem.appendChild(buttonElem);
}
} else {
h5Elem.textContent = `${key}: ${String(value)}`;
}
} else {
h5Elem.textContent = `${key}: ${String(value)}`;
}
header.appendChild(h5Elem);
card.appendChild(header);
header.appendChild(h5Elem);
card.appendChild(header);
const content = document.createElement('div');
content.id = `${safeKey}-content-${depth}`;
content.className = 'collapse';
content.setAttribute('aria-labelledby', `${safeKey}-heading-${depth}`);
const content = document.createElement('div');
content.id = `${safeKey}-content-${depth}`;
content.className = 'collapse';
content.setAttribute('aria-labelledby', `${safeKey}-heading-${depth}`);
if (key === 'XMPMetadata' && typeof value === "string") {
const body = document.createElement('div');
body.className = 'card-body';
const preElem = document.createElement('pre');
preElem.textContent = value; // Not escaping since we're using textContent
body.appendChild(preElem);
content.appendChild(body);
} else if (value && typeof value === 'object' && !Array.isArray(value)) {
const body = document.createElement('div');
body.className = 'card-body';
for (const subKey in value) {
const subElem = createJsonSection(subKey, value[subKey], depth + 1);
body.appendChild(subElem);
}
content.appendChild(body);
} else if (value && typeof value === 'object' && Array.isArray(value)) {
const body = document.createElement('div');
body.className = 'card-body';
value.forEach((val, index) => {
const subElem = createJsonSection(`${key}[${index}]`, val, depth + 1);
body.appendChild(subElem);
});
content.appendChild(body);
if (key === 'XMPMetadata' && typeof value === "string") {
const body = document.createElement('div');
body.className = 'card-body';
const preElem = document.createElement('pre');
preElem.textContent = value; // Not escaping since we're using textContent
body.appendChild(preElem);
content.appendChild(body);
} else if (value && typeof value === 'object' && !Array.isArray(value)) {
const body = document.createElement('div');
body.className = 'card-body';
for (const subKey in value) {
const subElem = createJsonSection(subKey, value[subKey], depth + 1);
body.appendChild(subElem);
}
content.appendChild(body);
} else if (value && typeof value === 'object' && Array.isArray(value)) {
const body = document.createElement('div');
body.className = 'card-body';
value.forEach((val, index) => {
const subElem = createJsonSection(`${key}[${index}]`, val, depth + 1);
body.appendChild(subElem);
});
content.appendChild(body);
}
card.appendChild(content);
return card;
}
function createButtonElement(key, safeKey, depth) {
const buttonElem = document.createElement('button');
buttonElem.className = 'btn btn-link';
buttonElem.type = 'button';
buttonElem.dataset.bsToggle = "collapse";
buttonElem.dataset.bsTarget = `#${safeKey}-content-${depth}`;
buttonElem.setAttribute('aria-expanded', 'true');
buttonElem.setAttribute('aria-controls', `${safeKey}-content-${depth}`);
buttonElem.textContent = key;
return buttonElem;
}
const collapsibleElems = document.querySelectorAll('[data-bs-toggle="collapse"]');
collapsibleElems.forEach(elem => {
new bootstrap.Collapse(elem);
});
</script>
</div>
card.appendChild(content);
return card;
}
function createButtonElement(key, safeKey, depth) {
const buttonElem = document.createElement('button');
buttonElem.className = 'btn btn-link';
buttonElem.type = 'button';
buttonElem.dataset.bsToggle = "collapse";
buttonElem.dataset.bsTarget = `#${safeKey}-content-${depth}`;
buttonElem.setAttribute('aria-expanded', 'true');
buttonElem.setAttribute('aria-controls', `${safeKey}-content-${depth}`);
buttonElem.textContent = key;
return buttonElem;
}
const collapsibleElems = document.querySelectorAll('[data-bs-toggle="collapse"]');
collapsibleElems.forEach(elem => {
new bootstrap.Collapse(elem);
});
</script>
</div>
</div>
</div>
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
</div>
</body>
</html>
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
</div>
</body>
</html>

View File

@ -38,7 +38,7 @@
</div>
<form th:action="@{'api/v1/security/redact'}" method="post" enctype="multipart/form-data">
<div
th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, disableMultipleFiles=true, accept='application/pdf')}">
th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, disableMultipleFiles=true, accept='application/pdf , application/zip')}">
</div>
<div class="mb-3 form-check d-none">
<input type="checkbox" id="convertPDFToImage" name="convertPDFToImage" checked>

View File

@ -1,35 +1,42 @@
<!DOCTYPE html>
<html th:lang="${#locale.language}" th:dir="#{language.direction}" th:data-language="${#locale.toString()}" xmlns:th="https://www.thymeleaf.org">
<head>
<th:block th:insert="~{fragments/common :: head(title=#{removeCertSign.title}, header=#{removeCertSign.header})}"></th:block>
</head>
<html th:lang="${#locale.language}" th:dir="#{language.direction}" th:data-language="${#locale.toString()}"
xmlns:th="https://www.thymeleaf.org">
<body>
<div id="page-container">
<div id="content-wrap">
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
<br><br>
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6 bg-card">
<div class="tool-header">
<span class="material-symbols-rounded tool-header-icon security">remove_moderator</span>
<span class="tool-header-text" th:text="#{removeCertSign.header}"></span>
</div>
<form th:action="@{'api/v1/security/remove-cert-sign'}" method="post" enctype="multipart/form-data">
<div class="mb-3">
<label th:text="#{removeCertSign.selectPDF}"></label>
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf')}"></div>
</div>
<div class="mb-3 text-left">
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{removeCertSign.submit}"></button>
</div>
</form>
<head>
<th:block th:insert="~{fragments/common :: head(title=#{removeCertSign.title}, header=#{removeCertSign.header})}">
</th:block>
</head>
<body>
<div id="page-container">
<div id="content-wrap">
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
<br><br>
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6 bg-card">
<div class="tool-header">
<span class="material-symbols-rounded tool-header-icon security">remove_moderator</span>
<span class="tool-header-text" th:text="#{removeCertSign.header}"></span>
</div>
<form th:action="@{'api/v1/security/remove-cert-sign'}" method="post" enctype="multipart/form-data">
<div class="mb-3">
<label th:text="#{removeCertSign.selectPDF}"></label>
<div
th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf , application/zip' )}">
</div>
</div>
<div class="mb-3 text-left">
<button type="submit" id="submitBtn" class="btn btn-primary"
th:text="#{removeCertSign.submit}"></button>
</div>
</form>
</div>
</div>
</div>
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
</div>
</body>
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
</div>
</body>
</html>

View File

@ -1,40 +1,47 @@
<!DOCTYPE html>
<html th:lang="${#locale.language}" th:dir="#{language.direction}" th:data-language="${#locale.toString()}" xmlns:th="https://www.thymeleaf.org">
<head>
<th:block th:insert="~{fragments/common :: head(title=#{removePassword.title}, header=#{removePassword.header})}"></th:block>
</head>
<html th:lang="${#locale.language}" th:dir="#{language.direction}" th:data-language="${#locale.toString()}"
xmlns:th="https://www.thymeleaf.org">
<body>
<div id="page-container">
<div id="content-wrap">
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
<br><br>
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6 bg-card">
<div class="tool-header">
<span class="material-symbols-rounded tool-header-icon security">lock_open_right</span>
<span class="tool-header-text" th:text="#{removePassword.header}"></span>
</div>
<form th:action="@{'api/v1/security/remove-password'}" method="post" enctype="multipart/form-data">
<div class="mb-3">
<label th:text="#{removePassword.selectText.1}"></label>
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf')}"></div>
</div>
<div class="mb-3">
<label th:text="#{removePassword.selectText.2}"></label>
<input type="password" class="form-control" id="password" name="password">
</div>
<br>
<div class="mb-3 text-left">
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{removePassword.submit}"></button>
</div>
</form>
<head>
<th:block th:insert="~{fragments/common :: head(title=#{removePassword.title}, header=#{removePassword.header})}">
</th:block>
</head>
<body>
<div id="page-container">
<div id="content-wrap">
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
<br><br>
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6 bg-card">
<div class="tool-header">
<span class="material-symbols-rounded tool-header-icon security">lock_open_right</span>
<span class="tool-header-text" th:text="#{removePassword.header}"></span>
</div>
<form th:action="@{'api/v1/security/remove-password'}" method="post" enctype="multipart/form-data">
<div class="mb-3">
<label th:text="#{removePassword.selectText.1}"></label>
<div
th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf , application/zip' )}">
</div>
</div>
<div class="mb-3">
<label th:text="#{removePassword.selectText.2}"></label>
<input type="password" class="form-control" id="password" name="password">
</div>
<br>
<div class="mb-3 text-left">
<button type="submit" id="submitBtn" class="btn btn-primary"
th:text="#{removePassword.submit}"></button>
</div>
</form>
</div>
</div>
</div>
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
</div>
</body>
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
</div>
</body>
</html>

View File

@ -1,39 +1,47 @@
<!DOCTYPE html>
<html th:lang="${#locale.language}" th:dir="#{language.direction}" th:data-language="${#locale.toString()}" xmlns:th="https://www.thymeleaf.org">
<head>
<th:block th:insert="~{fragments/common :: head(title=#{remove-watermark.title}, header=#{remove-watermark.header})}"></th:block>
</head>
<html th:lang="${#locale.language}" th:dir="#{language.direction}" th:data-language="${#locale.toString()}"
xmlns:th="https://www.thymeleaf.org">
<body>
<div id="page-container">
<div id="content-wrap">
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
<br><br>
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6 bg-card">
<div class="tool-header">
<span class="material-symbols-rounded tool-header-icon security">water_drop</span>
<span class="tool-header-text" th:text="#{remove-watermark.header}"></span>
</div>
<form method="post" enctype="multipart/form-data" th:action="@{'api/v1/security/remove-watermark'}">
<div class="mb-3">
<label th:text="#{remove-watermark.selectText.1}"></label>
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf')}"></div>
</div>
<div class="mb-3">
<label for="watermarkText" th:text="#{remove-watermark.selectText.2}"></label>
<input type="text" id="watermarkText" name="watermarkText" class="form-control" placeholder="Stirling-PDF" required />
</div>
<div class="mb-3 text-center">
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{remove-watermark.submit}"></button>
</div>
</form>
<head>
<th:block th:insert="~{fragments/common :: head(title=#{remove-watermark.title}, header=#{remove-watermark.header})}">
</th:block>
</head>
<body>
<div id="page-container">
<div id="content-wrap">
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
<br><br>
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6 bg-card">
<div class="tool-header">
<span class="material-symbols-rounded tool-header-icon security">water_drop</span>
<span class="tool-header-text" th:text="#{remove-watermark.header}"></span>
</div>
<form method="post" enctype="multipart/form-data" th:action="@{'api/v1/security/remove-watermark'}">
<div class="mb-3">
<label th:text="#{remove-watermark.selectText.1}"></label>
<div
th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf , application/zip' )}">
</div>
</div>
<div class="mb-3">
<label for="watermarkText" th:text="#{remove-watermark.selectText.2}"></label>
<input type="text" id="watermarkText" name="watermarkText" class="form-control"
placeholder="Stirling-PDF" required />
</div>
<div class="mb-3 text-center">
<button type="submit" id="submitBtn" class="btn btn-primary"
th:text="#{remove-watermark.submit}"></button>
</div>
</form>
</div>
</div>
</div>
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
</div>
</body>
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
</div>
</body>
</html>

View File

@ -1,55 +1,61 @@
<!DOCTYPE html>
<html th:lang="${#locale.language}" th:dir="#{language.direction}" th:data-language="${#locale.toString()}" xmlns:th="https://www.thymeleaf.org">
<head>
<th:block th:insert="~{fragments/common :: head(title=#{sanitizePDF.title}, header=#{sanitizePDF.header})}"></th:block>
</head>
<html th:lang="${#locale.language}" th:dir="#{language.direction}" th:data-language="${#locale.toString()}"
xmlns:th="https://www.thymeleaf.org">
<body>
<div id="page-container">
<div id="content-wrap">
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
<br><br>
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6 bg-card">
<div class="tool-header">
<span class="material-symbols-rounded tool-header-icon security">sanitizer</span>
<span class="tool-header-text" th:text="#{sanitizePDF.header}"></span>
</div>
<form th:action="@{'api/v1/security/sanitize-pdf'}" method="post" enctype="multipart/form-data">
<div class="mb-3">
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf')}"></div>
</div>
<div class="form-check ms-3">
<input type="checkbox" id="removeJavaScript" name="removeJavaScript" checked>
<label for="removeJavaScript" th:text="#{sanitizePDF.selectText.1}"></label>
</div>
<div class="form-check ms-3">
<input type="checkbox" id="removeEmbeddedFiles" name="removeEmbeddedFiles" checked>
<label for="removeEmbeddedFiles" th:text="#{sanitizePDF.selectText.2}"></label>
</div>
<div class="form-check ms-3">
<input type="checkbox" id="removeMetadata" name="removeMetadata" checked>
<label for="removeMetadata" th:text="#{sanitizePDF.selectText.3}"></label>
</div>
<div class="form-check ms-3">
<input type="checkbox" id="removeLinks" name="removeLinks">
<label for="removeLinks" th:text="#{sanitizePDF.selectText.4}"></label>
</div>
<div class="form-check ms-3">
<input type="checkbox" id="removeFonts" name="removeFonts">
<label for="removeFonts" th:text="#{sanitizePDF.selectText.5}"></label>
</div>
<br>
<div class="mb-3 text-left">
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{sanitizePDF.submit}"></button>
</div>
</form>
<head>
<th:block th:insert="~{fragments/common :: head(title=#{sanitizePDF.title}, header=#{sanitizePDF.header})}">
</th:block>
</head>
<body>
<div id="page-container">
<div id="content-wrap">
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
<br><br>
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6 bg-card">
<div class="tool-header">
<span class="material-symbols-rounded tool-header-icon security">sanitizer</span>
<span class="tool-header-text" th:text="#{sanitizePDF.header}"></span>
</div>
<form th:action="@{'api/v1/security/sanitize-pdf'}" method="post" enctype="multipart/form-data">
<div class="mb-3">
<div
th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf , application/zip' )}">
</div>
</div>
<div class="form-check ms-3">
<input type="checkbox" id="removeJavaScript" name="removeJavaScript" checked>
<label for="removeJavaScript" th:text="#{sanitizePDF.selectText.1}"></label>
</div>
<div class="form-check ms-3">
<input type="checkbox" id="removeEmbeddedFiles" name="removeEmbeddedFiles" checked>
<label for="removeEmbeddedFiles" th:text="#{sanitizePDF.selectText.2}"></label>
</div>
<div class="form-check ms-3">
<input type="checkbox" id="removeMetadata" name="removeMetadata" checked>
<label for="removeMetadata" th:text="#{sanitizePDF.selectText.3}"></label>
</div>
<div class="form-check ms-3">
<input type="checkbox" id="removeLinks" name="removeLinks">
<label for="removeLinks" th:text="#{sanitizePDF.selectText.4}"></label>
</div>
<div class="form-check ms-3">
<input type="checkbox" id="removeFonts" name="removeFonts">
<label for="removeFonts" th:text="#{sanitizePDF.selectText.5}"></label>
</div>
<br>
<div class="mb-3 text-left">
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{sanitizePDF.submit}"></button>
</div>
</form>
</div>
</div>
</div>
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
</div>
</body>
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
</div>
</body>
</html>

View File

@ -1,188 +1,199 @@
<!DOCTYPE html>
<html th:lang="${#locale.language}" th:dir="#{language.direction}" th:data-language="${#locale.toString()}" xmlns:th="https://www.thymeleaf.org">
<head>
<th:block th:insert="~{fragments/common :: head(title=#{validateSignature.title}, header=#{validateSignature.header})}"></th:block>
</head>
<body>
<html th:lang="${#locale.language}" th:dir="#{language.direction}" th:data-language="${#locale.toString()}"
xmlns:th="https://www.thymeleaf.org">
<head>
<th:block
th:insert="~{fragments/common :: head(title=#{validateSignature.title}, header=#{validateSignature.header})}">
</th:block>
</head>
<body>
<div id="page-container">
<div id="content-wrap">
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
<br><br>
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6 bg-card">
<div class="tool-header">
<span class="material-symbols-rounded tool-header-icon security">verified</span>
<span class="tool-header-text" th:text="#{validateSignature.header}"></span>
</div>
<form id="pdfForm" th:action="@{'api/v1/security/validate-signature'}" method="post" enctype="multipart/form-data">
<div class="mb-3">
<label th:text="#{validateSignature.selectPDF}"></label>
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, remoteCall='false', accept='application/pdf')}"></div>
<div id="content-wrap">
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
<br><br>
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6 bg-card">
<div class="tool-header">
<span class="material-symbols-rounded tool-header-icon security">verified</span>
<span class="tool-header-text" th:text="#{validateSignature.header}"></span>
</div>
<form id="pdfForm" th:action="@{'api/v1/security/validate-signature'}" method="post"
enctype="multipart/form-data">
<div class="mb-3">
<label th:text="#{validateSignature.selectPDF}"></label>
<div
th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, remoteCall='false', accept='application/pdf , application/zip')}">
</div>
</div>
<div class="mb-3">
<label th:text="#{validateSignature.selectCustomCert}"></label>
<div
th:replace="~{fragments/common :: fileSelector(name='certFile', multipleInputsForSingleRequest=false, notRequired=true, remoteCall='false', accept='.cer,.crt,.pem')}">
</div>
</div>
<div class="mb-3 text-left">
<button type="submit" id="submitBtn" class="btn btn-primary"
th:text="#{validateSignature.submit}"></button>
</div>
</form>
<!-- Results section -->
<div id="results" style="display: none;">
<h4 th:text="#{validateSignature.results}"></h4>
<div id="signatures-list"></div>
</div>
</div>
</div>
<div class="mb-3">
<label th:text="#{validateSignature.selectCustomCert}" ></label>
<div th:replace="~{fragments/common :: fileSelector(name='certFile', multipleInputsForSingleRequest=false, notRequired=true, remoteCall='false', accept='.cer,.crt,.pem')}"></div>
</div>
<div class="mb-3 text-left">
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{validateSignature.submit}"></button>
</div>
</form>
<!-- Results section -->
<div id="results" style="display: none;">
<h4 th:text="#{validateSignature.results}"></h4>
<div id="signatures-list"></div>
</div>
</div>
</div>
</div>
</div>
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
</div>
<script th:inline="javascript">
const translations = {
signature: /*[[#{validateSignature.signature}]]*/,
signatureInfo: /*[[#{validateSignature.signature.info}]]*/,
certInfo: /*[[#{validateSignature.cert.info}]]*/,
signer: /*[[#{validateSignature.signer}]]*/,
date: /*[[#{validateSignature.date}]]*/,
reason: /*[[#{validateSignature.reason}]]*/,
location: /*[[#{validateSignature.location}]]*/,
noSignatures: /*[[#{validateSignature.noSignatures}]]*/,
statusValid: /*[[#{validateSignature.status.valid}]]*/,
statusInvalid: /*[[#{validateSignature.status.invalid}]]*/,
mathValid: /*[[#{validateSignature.signature.mathValid}]]*/,
chainInvalid: /*[[#{validateSignature.chain.invalid}]]*/,
trustInvalid: /*[[#{validateSignature.trust.invalid}]]*/,
certExpired: /*[[#{validateSignature.cert.expired}]]*/,
certRevoked: /*[[#{validateSignature.cert.revoked}]]*/,
certIssuer: /*[[#{validateSignature.cert.issuer}]]*/,
certSubject: /*[[#{validateSignature.cert.subject}]]*/,
certSerialNumber: /*[[#{validateSignature.cert.serialNumber}]]*/,
certValidFrom: /*[[#{validateSignature.cert.validFrom}]]*/,
certValidUntil: /*[[#{validateSignature.cert.validUntil}]]*/,
certAlgorithm: /*[[#{validateSignature.cert.algorithm}]]*/,
certKeySize: /*[[#{validateSignature.cert.keySize}]]*/,
certBits: /*[[#{validateSignature.cert.bits}]]*/,
certVersion: /*[[#{validateSignature.cert.version}]]*/,
certKeyUsage: /*[[#{validateSignature.cert.keyUsage}]]*/,
certSelfSigned: /*[[#{validateSignature.cert.selfSigned}]]*/,
yes: /*[[#{yes}]]*/,
no: /*[[#{no}]]*/,
selectPDF: /*[[#{validateSignature.selectPDF}]]*/
<script th:inline="javascript">
const translations = {
signature: /*[[#{validateSignature.signature}]]*/,
signatureInfo: /*[[#{validateSignature.signature.info}]]*/,
certInfo: /*[[#{validateSignature.cert.info}]]*/,
signer: /*[[#{validateSignature.signer}]]*/,
date: /*[[#{validateSignature.date}]]*/,
reason: /*[[#{validateSignature.reason}]]*/,
location: /*[[#{validateSignature.location}]]*/,
noSignatures: /*[[#{validateSignature.noSignatures}]]*/,
statusValid: /*[[#{validateSignature.status.valid}]]*/,
statusInvalid: /*[[#{validateSignature.status.invalid}]]*/,
mathValid: /*[[#{validateSignature.signature.mathValid}]]*/,
chainInvalid: /*[[#{validateSignature.chain.invalid}]]*/,
trustInvalid: /*[[#{validateSignature.trust.invalid}]]*/,
certExpired: /*[[#{validateSignature.cert.expired}]]*/,
certRevoked: /*[[#{validateSignature.cert.revoked}]]*/,
certIssuer: /*[[#{validateSignature.cert.issuer}]]*/,
certSubject: /*[[#{validateSignature.cert.subject}]]*/,
certSerialNumber: /*[[#{validateSignature.cert.serialNumber}]]*/,
certValidFrom: /*[[#{validateSignature.cert.validFrom}]]*/,
certValidUntil: /*[[#{validateSignature.cert.validUntil}]]*/,
certAlgorithm: /*[[#{validateSignature.cert.algorithm}]]*/,
certKeySize: /*[[#{validateSignature.cert.keySize}]]*/,
certBits: /*[[#{validateSignature.cert.bits}]]*/,
certVersion: /*[[#{validateSignature.cert.version}]]*/,
certKeyUsage: /*[[#{validateSignature.cert.keyUsage}]]*/,
certSelfSigned: /*[[#{validateSignature.cert.selfSigned}]]*/,
yes: /*[[#{yes}]]*/,
no: /*[[#{no}]]*/,
selectPDF: /*[[#{validateSignature.selectPDF}]]*/
};
function escapeHtml(unsafe) {
return unsafe
?.toString()
.replace(/&/g, "&amp;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;")
.replace(/'/g, "&#039;") || 'N/A';
}
document.querySelector('#pdfForm').addEventListener('submit', async (e) => {
e.preventDefault();
const fileInput = document.getElementById('fileInput-input');
const certInput = document.getElementById('certFile-input');
if (!fileInput.files.length) {
alert(escapeHtml(translations.selectPDF));
return;
}
const results = [];
for (const file of fileInput.files) {
const formData = new FormData();
formData.append('fileInput', file);
if (certInput.files.length > 0) {
formData.append('certFile', certInput.files[0]);
function escapeHtml(unsafe) {
return unsafe
?.toString()
.replace(/&/g, "&amp;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;")
.replace(/'/g, "&#039;") || 'N/A';
}
try {
const response = await window.fetchWithCsrf(e.target.action, {
method: 'POST',
body: formData
});
const fileResults = await response.json();
fileResults.forEach(result => {
result.fileName = file.name;
});
results.push(...fileResults);
} catch (error) {
results.push({
fileName: file.name,
valid: false,
errorMessage: `${escapeHtml(translations.statusInvalid)}: ${escapeHtml(error.message)}`
});
}
}
displayResults(results);
});
function displayResults(results) {
const resultDiv = document.getElementById('results');
const listDiv = document.getElementById('signatures-list');
listDiv.innerHTML = '';
resultDiv.style.display = 'block';
if (!results || results.length === 0) {
listDiv.innerHTML = `<div class="alert alert-warning">${escapeHtml(translations.noSignatures)}</div>`;
return;
}
results.forEach((result, index) => {
const signatureDiv = document.createElement('div');
signatureDiv.className = 'card mb-3';
let validationClass = 'alert-danger';
let validationIssues = [];
if (!result.valid) {
validationIssues.push(`${escapeHtml(translations.statusInvalid)}: ${escapeHtml(result.errorMessage || '')}`);
} else {
const isFullyValid = result.valid &&
result.chainValid &&
result.trustValid &&
result.notExpired &&
result.notRevoked;
document.querySelector('#pdfForm').addEventListener('submit', async (e) => {
e.preventDefault();
const fileInput = document.getElementById('fileInput-input');
const certInput = document.getElementById('certFile-input');
if (!fileInput.files.length) {
alert(escapeHtml(translations.selectPDF));
return;
}
if (isFullyValid) {
validationClass = 'alert-success';
validationIssues.push(escapeHtml(translations.statusValid));
} else {
validationClass = 'alert-warning';
validationIssues.push(escapeHtml(translations.mathValid));
if (!result.chainValid) {
validationIssues.push(escapeHtml(translations.chainInvalid));
const results = [];
for (const file of fileInput.files) {
const formData = new FormData();
formData.append('fileInput', file);
if (certInput.files.length > 0) {
formData.append('certFile', certInput.files[0]);
}
if (!result.trustValid) {
validationIssues.push(escapeHtml(translations.trustInvalid));
}
if (!result.notExpired) {
validationIssues.push(escapeHtml(translations.certExpired));
}
if (result.trustValid && result.chainValid && !result.notRevoked) {
validationIssues.push(escapeHtml(translations.certRevoked));
try {
const response = await window.fetchWithCsrf(e.target.action, {
method: 'POST',
body: formData
});
const fileResults = await response.json();
fileResults.forEach(result => {
result.fileName = file.name;
});
results.push(...fileResults);
} catch (error) {
results.push({
fileName: file.name,
valid: false,
errorMessage: `${escapeHtml(translations.statusInvalid)}: ${escapeHtml(error.message)}`
});
}
}
}
let statusMessage = validationIssues[0];
if (validationIssues.length > 1) {
statusMessage += '<ul class="mb-0 mt-2">';
for (let i = 1; i < validationIssues.length; i++) {
statusMessage += `<li>${validationIssues[i]}</li>`;
displayResults(results);
});
function displayResults(results) {
const resultDiv = document.getElementById('results');
const listDiv = document.getElementById('signatures-list');
listDiv.innerHTML = '';
resultDiv.style.display = 'block';
if (!results || results.length === 0) {
listDiv.innerHTML = `<div class="alert alert-warning">${escapeHtml(translations.noSignatures)}</div>`;
return;
}
statusMessage += '</ul>';
}
let content = `
results.forEach((result, index) => {
const signatureDiv = document.createElement('div');
signatureDiv.className = 'card mb-3';
let validationClass = 'alert-danger';
let validationIssues = [];
if (!result.valid) {
validationIssues.push(`${escapeHtml(translations.statusInvalid)}: ${escapeHtml(result.errorMessage || '')}`);
} else {
const isFullyValid = result.valid &&
result.chainValid &&
result.trustValid &&
result.notExpired &&
result.notRevoked;
if (isFullyValid) {
validationClass = 'alert-success';
validationIssues.push(escapeHtml(translations.statusValid));
} else {
validationClass = 'alert-warning';
validationIssues.push(escapeHtml(translations.mathValid));
if (!result.chainValid) {
validationIssues.push(escapeHtml(translations.chainInvalid));
}
if (!result.trustValid) {
validationIssues.push(escapeHtml(translations.trustInvalid));
}
if (!result.notExpired) {
validationIssues.push(escapeHtml(translations.certExpired));
}
if (result.trustValid && result.chainValid && !result.notRevoked) {
validationIssues.push(escapeHtml(translations.certRevoked));
}
}
}
let statusMessage = validationIssues[0];
if (validationIssues.length > 1) {
statusMessage += '<ul class="mb-0 mt-2">';
for (let i = 1; i < validationIssues.length; i++) {
statusMessage += `<li>${validationIssues[i]}</li>`;
}
statusMessage += '</ul>';
}
let content = `
<div class="card-body">
${results.length > 1 ? `<h4 class="mb-3">${escapeHtml(translations.signature)} ${index + 1}</h4>` : ''}
<div class="alert ${validationClass}">
@ -208,7 +219,7 @@ function displayResults(results) {
<td>${escapeHtml(result.location)}</td>
</tr>
</table>
<h5>${escapeHtml(translations.certInfo)}</h5>
<table class="table table-borderless">
<tr>
@ -255,11 +266,12 @@ function displayResults(results) {
</div>
</div>
`;
signatureDiv.innerHTML = content;
listDiv.appendChild(signatureDiv);
});
}
</script>
</body>
signatureDiv.innerHTML = content;
listDiv.appendChild(signatureDiv);
});
}
</script>
</body>
</html>

View File

@ -15,8 +15,7 @@
#font-select option[value="[[${font.name}]]"] {
font-family: "[[${font.name}]]",
cursive
!important;
cursive !important;
}
</style>
</th:block>
@ -39,13 +38,13 @@
<!-- pdf selector -->
<div
th:replace="~{fragments/common :: fileSelector(name='pdf-upload', multipleInputsForSingleRequest=false, disableMultipleFiles=true, accept='application/pdf')}">
th:replace="~{fragments/common :: fileSelector(name='pdf-upload', multipleInputsForSingleRequest=false, disableMultipleFiles=true, accept='application/pdf , application/zip')}">
</div>
<script type="module" th:src="@{'/pdfjs-legacy/pdf.mjs'}"></script>
<div class="tab-group show-on-file-selected">
<div class="tab-container" th:title="#{sign.upload}" th:data-title="#{sign.upload}">
<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/*, application/zip', inputText=#{imgPrompt})}">
</div>
</div>

View File

@ -25,7 +25,7 @@
</div>
<form method="post" enctype="multipart/form-data" th:action="@{'/api/v1/general/split-by-size-or-count'}">
<div
th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf')}">
th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf , application/zip' )}">
</div>
<label for="splitType" th:text="#{split-by-size-or-count.type.label}">Split Type</label>
<select id="splitType" name="splitType" class="form-control">

View File

@ -23,7 +23,7 @@
</div>
<form th:action="@{'/api/v1/general/split-pdf-by-chapters'}" method="post" enctype="multipart/form-data">
<div
th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf')}">
th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf , application/zip' )}">
</div>
<div class="mb-3">

View File

@ -1,88 +1,99 @@
<!DOCTYPE html>
<html th:lang="${#locale.language}" th:dir="#{language.direction}" th:data-language="${#locale.toString()}" xmlns:th="https://www.thymeleaf.org">
<head>
<th:block th:insert="~{fragments/common :: head(title=#{split-by-sections.title}, header=#{split-by-sections.header})}"></th:block>
<link rel="stylesheet" th:href="@{'/css/split-pdf-by-sections.css'}">
</head>
<html th:lang="${#locale.language}" th:dir="#{language.direction}" th:data-language="${#locale.toString()}"
xmlns:th="https://www.thymeleaf.org">
<body>
<th:block th:insert="~{fragments/common :: game}"></th:block>
<div id="page-container">
<div id="content-wrap">
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
<br><br>
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6 bg-card">
<div class="tool-header">
<span class="material-symbols-rounded tool-header-icon advance">grid_on</span>
<span class="tool-header-text" th:text="#{split-by-sections.header}"></span>
<head>
<th:block
th:insert="~{fragments/common :: head(title=#{split-by-sections.title}, header=#{split-by-sections.header})}">
</th:block>
<link rel="stylesheet" th:href="@{'/css/split-pdf-by-sections.css'}">
</head>
<body>
<th:block th:insert="~{fragments/common :: game}"></th:block>
<div id="page-container">
<div id="content-wrap">
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
<br><br>
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6 bg-card">
<div class="tool-header">
<span class="material-symbols-rounded tool-header-icon advance">grid_on</span>
<span class="tool-header-text" th:text="#{split-by-sections.header}"></span>
</div>
<form method="post" enctype="multipart/form-data" th:action="@{'/api/v1/general/split-pdf-by-sections'}">
<div
th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf , application/zip' )}">
</div>
<form method="post" enctype="multipart/form-data" th:action="@{'/api/v1/general/split-pdf-by-sections'}">
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf')}"></div>
<label for="horizontalDivisions" th:text="#{split-by-sections.horizontal.label}">Horizontal Divisions</label>
<input type="number" id="horizontalDivisions" name="horizontalDivisions" class="form-control" min="0" max="300" value="0" required th:placeholder="#{split-by-sections.horizontal.placeholder}">
<br>
<label for="verticalDivisions" th:text="#{split-by-sections.vertical.label}">Vertical Divisions</label>
<input type="number" id="verticalDivisions" name="verticalDivisions" class="form-control" min="0" max="300" required value="1" th:placeholder="#{split-by-sections.vertical.placeholder}">
<br>
<div class="mb-3 form-check">
<input type="checkbox" id="merge" name="merge">
<label for="merge" th:text="#{split-by-sections.merge}">merge PDFs into one</label>
</div>
<br>
<div id="pdfVisualAid" class="pdf-visual-aid"></div>
<script>
function updateVisualAid() {
const horizontalDivisions = document.getElementById('horizontalDivisions').value;
const verticalDivisions = document.getElementById('verticalDivisions').value;
const aid = document.getElementById('pdfVisualAid');
<label for="horizontalDivisions" th:text="#{split-by-sections.horizontal.label}">Horizontal
Divisions</label>
<input type="number" id="horizontalDivisions" name="horizontalDivisions" class="form-control" min="0"
max="300" value="0" required th:placeholder="#{split-by-sections.horizontal.placeholder}">
<br>
<label for="verticalDivisions" th:text="#{split-by-sections.vertical.label}">Vertical Divisions</label>
<input type="number" id="verticalDivisions" name="verticalDivisions" class="form-control" min="0"
max="300" required value="1" th:placeholder="#{split-by-sections.vertical.placeholder}">
<br>
<div class="mb-3 form-check">
<input type="checkbox" id="merge" name="merge">
<label for="merge" th:text="#{split-by-sections.merge}">merge PDFs into one</label>
</div>
<br>
<div id="pdfVisualAid" class="pdf-visual-aid"></div>
<script>
function updateVisualAid() {
const horizontalDivisions = document.getElementById('horizontalDivisions').value;
const verticalDivisions = document.getElementById('verticalDivisions').value;
const aid = document.getElementById('pdfVisualAid');
if(horizontalDivisions > 300) {
horizontalDivisions = 300
}
if(verticalDivisions > 300) {
verticalDivisions = 300
}
// Clear existing lines
aid.innerHTML = '';
if (horizontalDivisions > 300) {
horizontalDivisions = 300
}
if (verticalDivisions > 300) {
verticalDivisions = 300
}
// Clear existing lines
aid.innerHTML = '';
// Add horizontal lines
for (let i = 0; i < horizontalDivisions; i++) {
const line = document.createElement('div');
line.classList.add('line');
line.style.width = '100%';
line.style.height = '1px';
line.style.top = `${((i + 1) / (parseInt(horizontalDivisions) + 1)) * 100}%`;
aid.appendChild(line);
}
// Add vertical lines
for (let i = 0; i < verticalDivisions; i++) {
const line = document.createElement('div');
line.classList.add('line');
line.style.height = '100%';
line.style.width = '1px';
line.style.left = `${((i + 1) / (parseInt(verticalDivisions) + 1)) * 100}%`;
aid.appendChild(line);
}
// Add horizontal lines
for (let i = 0; i < horizontalDivisions; i++) {
const line = document.createElement('div');
line.classList.add('line');
line.style.width = '100%';
line.style.height = '1px';
line.style.top = `${((i + 1) / (parseInt(horizontalDivisions) + 1)) * 100}%`;
aid.appendChild(line);
}
// Event listeners
document.getElementById('horizontalDivisions').addEventListener('input', updateVisualAid);
document.getElementById('verticalDivisions').addEventListener('input', updateVisualAid);
// Add vertical lines
for (let i = 0; i < verticalDivisions; i++) {
const line = document.createElement('div');
line.classList.add('line');
line.style.height = '100%';
line.style.width = '1px';
line.style.left = `${((i + 1) / (parseInt(verticalDivisions) + 1)) * 100}%`;
aid.appendChild(line);
}
}
// Initial draw
updateVisualAid();
</script>
<br>
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{split-by-sections.submit}">Submit</button>
</form>
</div>
// Event listeners
document.getElementById('horizontalDivisions').addEventListener('input', updateVisualAid);
document.getElementById('verticalDivisions').addEventListener('input', updateVisualAid);
// Initial draw
updateVisualAid();
</script>
<br>
<button type="submit" id="submitBtn" class="btn btn-primary"
th:text="#{split-by-sections.submit}">Submit</button>
</form>
</div>
</div>
</div>
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
</div>
</body>
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
</div>
</body>
</html>

View File

@ -21,7 +21,7 @@
</div>
<form th:action="@{'/api/v1/general/split-pages'}" method="post" enctype="multipart/form-data">
<div
th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf')}">
th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf , application/zip' )}">
</div>
<div class="mb-3">