zip and drag drop changes

This commit is contained in:
Anthony Stirling 2023-03-19 23:19:13 +00:00
parent cbfa70d851
commit 98df0bfdf9
16 changed files with 257 additions and 174 deletions

View File

@ -35,6 +35,7 @@ RUN apt-get update && \
python3-uno \ python3-uno \
python3-pip \ python3-pip \
unoconv \ unoconv \
pngquant \
ocrmypdf && \ ocrmypdf && \
pip install --user --upgrade ocrmypdf pip install --user --upgrade ocrmypdf
@ -52,3 +53,6 @@ ENV LOG_LEVEL=INFO
# Run the application # Run the application
ENTRYPOINT ["java","-jar","/app.jar","-Dlogging.level=${LOG_LEVEL}"] ENTRYPOINT ["java","-jar","/app.jar","-Dlogging.level=${LOG_LEVEL}"]

View File

@ -1,11 +1,11 @@
fileToPDF.fileTypesList=Microsoft Word: (DOC, DOCX, DOT, DOTX) \ fileToPDF.fileTypesList=Microsoft Word: (DOC, DOCX, DOT, DOTX) <br> \
Microsoft Excel: (CSV, XLS, XLSX, XLT, XLTX, SLK, DIF) \ Microsoft Excel: (CSV, XLS, XLSX, XLT, XLTX, SLK, DIF) <br> \
Microsoft PowerPoint: (PPT, PPTX) \ Microsoft PowerPoint: (PPT, PPTX) <br> \
OpenDocument Formats: (ODT, OTT, ODS, OTS, ODP, OTP, ODG, OTG) \ OpenDocument Formats: (ODT, OTT, ODS, OTS, ODP, OTP, ODG, OTG) <br> \
Plain Text: (TXT, TEXT, XML) \ Plain Text: (TXT, TEXT, XML) <br> \
Rich Text Format: (RTF) \ Rich Text Format: (RTF) <br> \
Images: (BMP, GIF, JPEG, PNG, TIF, PBM, PGM, PPM, RAS, XBM, XPM, SVG, SVM, WMF) \ Images: (BMP, GIF, JPEG, PNG, TIF, PBM, PGM, PPM, RAS, XBM, XPM, SVG, SVM, WMF) <br> \
HTML: (HTML) \ HTML: (HTML) <br> \
Lotus Word Pro: (LWP) \ Lotus Word Pro: (LWP) <br> \
StarOffice formats: (SDA, SDC, SDD, SDW, STC, STD, STI, STW, SXD, SXG, SXI, SXW) \ StarOffice formats: (SDA, SDC, SDD, SDW, STC, STD, STI, STW, SXD, SXG, SXI, SXW) <br> \
Other formats: (DBF, FODS, VSD, VOR, VOR3, VOR4, UOP, PCT, PS, PDF) Other formats: (DBF, FODS, VSD, VOR, VOR3, VOR4, UOP, PCT, PS, PDF)

View File

@ -15,6 +15,8 @@ goToPage=Go
true=True true=True
false=False false=False
unknown=Unknown unknown=Unknown
save=Save
close=Close
############# #############
# HOME-PAGE # # HOME-PAGE #
############# #############
@ -70,20 +72,68 @@ home.compressPdfs.desc=Compress PDFs to reduce their file size.
home.changeMetadata.title=Change Metadata home.changeMetadata.title=Change Metadata
home.changeMetadata.desc=Change/Remove/Add metadata from a PDF document home.changeMetadata.desc=Change/Remove/Add metadata from a PDF document
home.xlsToPdf.title=Excel (Xls) to PDF home.fileToPDF.title=Convert file to PDF
home.xlsToPdf.desc=Convert a Excel document (xls, xlsx) to PDF. home.fileToPDF.desc=Convert nearly any file to PDF (DOCX, PNG, XLS, PPT, TXT and more)
home.ocr.title=Run OCR on PDF
home.ocr.desc=Scans and detects text from images within a PDF and re-adds it as text.
home.extractImages.title=Extract Images
home.extractImages.desc=Extracts all images from a PDF and saves them to zip
navbar.settings=Settings/About
settings.title=Settings
settings.update=Update available
settings.appVersion=App Version:
settings.downloadOption.title=Choose download option (For single file non zip downloads):
settings.downloadOption.1=Open in same window
settings.downloadOption.2=Open in new window
settings.downloadOption.3=Download file
settings.zip=Zip multi-download files
#OCR
ocr.title=OCR
ocr.header=OCR (Optical Character Recognition)
ocr.selectText.1=Select languages that are to be detected within the PDF (Ones listed are the ones currently detected):
ocr.selectText.2=Produce text file containing OCR text alongside the OCR'ed PDF
ocr.help=Please read this documentation on how to use this for other languages and/or use not in docker
ocr.credit=This service uses OCRmyPDF and Tesseract for OCR.
ocr.submit=Process PDF with OCR
extractImages.title=Extract Images
extractImages.header=Extract Images
extractImages.selectText=Select image format to convert extracted images to
extractImages.submit=Extract
#File to PDF
fileToPDF.title=File to PDF
fileToPDF.header=Convert any file to PDF
fileToPDF.credit=This service uses LibreOffice and Unoconv for file conversion.
fileToPDF.supportedFileTypes=Supported file types should include the below however for a full updated list of supported formats, please refer to the LibreOffice documentation
fileToPDF.submit=Convert to PDF
#compress
compress.title=Compress
compress.header=Compress PDF
compress.credit=This service uses OCRmyPDF for PDF Compress/Optimisation.
compress.selectText.1=Optimization level:
compress.selectText.2=0 (No optimization)
compress.selectText.3=1 (Default, lossless optimization)
compress.selectText.4=2 (Lossy optimization)
compress.selectText.5=3 (Lossy optimization, more aggressive)
compress.selectText.6=Enable fast web view (linearize PDF)
compress.selectText.7=Enable lossy JBIG2 encoding
compress.submit=Compress
#Add image #Add image
addImage.title=Add Image addImage.title=Add Image
addImage.header=Add image to PDF (Work in progress) addImage.header=Add image to PDF (Work in progress)
addImage.submit=Add image addImage.submit=Add image
#compress
compress.title=Compress
compress.header=Compress PDF
compress.compressLevel=Value between 1 and 100 (1 being most reduced)
compress.submit=Compress
#merge #merge
merge.title=Merge merge.title=Merge

File diff suppressed because one or more lines are too long

View File

@ -28,7 +28,6 @@
</div> </div>
<button type="submit" class="btn btn-primary" th:text="#{addImage.submit}"></button> <button type="submit" class="btn btn-primary" th:text="#{addImage.submit}"></button>
</form> </form>
<th:block th:insert="~{fragments/common :: filelist}"></th:block>
</div> </div>
</div> </div>
</div> </div>

View File

@ -15,31 +15,27 @@
<div class="col-md-6"> <div class="col-md-6">
<h2 th:text="#{compress.header}"></h2> <h2 th:text="#{compress.header}"></h2>
<form action="#" th:action="@{/compress-pdf}" method="post" enctype="multipart/form-data"> <form action="#" th:action="@{/compress-pdf}" method="post" enctype="multipart/form-data">
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false)}"></div>
<div> <div>
<label for="fileInput">Choose a PDF file</label> <label for="optimizeLevel" th:text="#{compress.selectText.1}"></label>
<input type="file" name="fileInput" id="fileInput" accept="application/pdf" required>
</div>
<div>
<label for="optimizeLevel">Optimization level:</label>
<select name="optimizeLevel" id="optimizeLevel"> <select name="optimizeLevel" id="optimizeLevel">
<option value="0">-O0 (No optimization)</option> <option value="0" th:text="#{compress.selectText.2}"></option>
<option value="1" selected>-O1 (Default, lossless optimization)</option> <option value="1" selected th:text="#{compress.selectText.3}"></option>
<option value="2">-O2 (Lossy optimization)</option> <option value="2" th:text="#{compress.selectText.4}"></option>
<option value="3">-O3 (Lossy optimization, more aggressive)</option> <option value="3" th:text="#{compress.selectText.5}"></option>
</select> </select>
</div> </div>
<div> <div>
<input type="checkbox" name="fastWebView" id="fastWebView" checked> <input type="checkbox" name="fastWebView" id="fastWebView">
<label for="fastWebView">Enable fast web view (linearize PDF)</label> <label for="fastWebView" th:text="#{compress.selectText.6}"></label>
</div> </div>
<div> <div>
<input type="checkbox" name="jbig2Lossy" id="jbig2Lossy"> <input type="checkbox" name="jbig2Lossy" id="jbig2Lossy">
<label for="jbig2Lossy">Enable lossy JBIG2 encoding</label> <label for="jbig2Lossy" th:text="#{compress.selectText.7}"></label>
</div> </div>
<button type="submit">Optimize PDF</button> <button type="submit" th:text="#{compress.submit}"></button>
</form> </form>
<p class="mt-3" th:text="#{compress.credit}"></p>
<th:block th:insert="~{fragments/common :: filelist}"></th:block>
</div> </div>
</div> </div>

View File

@ -13,17 +13,13 @@
<div class="row justify-content-center"> <div class="row justify-content-center">
<div class="col-md-6"> <div class="col-md-6">
<h2 th:text="#{fileToPDF.header}"></h2> <h2 th:text="#{fileToPDF.header}"></h2>
<p th:text="#{processTimeWarning}">
<form method="post" enctype="multipart/form-data" th:action="@{file-to-pdf}"> <form method="post" enctype="multipart/form-data" th:action="@{file-to-pdf}">
<div class="custom-file"> <div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false)}"></div>
<input type="file" class="custom-file-input" id="fileInput" name="fileInput" required> <br>
<label class="custom-file-label" for="fileInput" th:text="#{filePrompt}"></label>
</div>
<br> <br>
<button type="submit" class="btn btn-primary" th:text="#{fileToPDF.submit}"></button> <button type="submit" class="btn btn-primary" th:text="#{fileToPDF.submit}"></button>
</form> </form>
<th:block th:insert="~{fragments/common :: filelist}"></th:block>
<p class="mt-3" th:text="#{fileToPDF.credit}"></p> <p class="mt-3" th:text="#{fileToPDF.credit}"></p>
<p class="mt-3" th:text="#{fileToPDF.supportedFileTypes}"></p> <p class="mt-3" th:text="#{fileToPDF.supportedFileTypes}"></p>
<p th:utext="#{fileToPDF.fileTypesList}"></p> <p th:utext="#{fileToPDF.fileTypesList}"></p>

View File

@ -23,7 +23,6 @@
<button type="submit" class="btn btn-primary" th:text="#{imageToPDF.submit}"></button> <button type="submit" class="btn btn-primary" th:text="#{imageToPDF.submit}"></button>
</form> </form>
<th:block th:insert="~{fragments/common :: filelist}"></th:block>
</div> </div>
</div> </div>
</div> </div>

View File

@ -46,7 +46,6 @@
</div> </div>
<button type="submit" class="btn btn-primary" th:text="#{pdfToImage.submit}"></button> <button type="submit" class="btn btn-primary" th:text="#{pdfToImage.submit}"></button>
</form> </form>
<th:block th:insert="~{fragments/common :: filelist}"></th:block>
</div> </div>
</div> </div>

View File

@ -9,6 +9,9 @@
<!-- jQuery --> <!-- jQuery -->
<script src="js/jquery.min.js"></script> <script src="js/jquery.min.js"></script>
<!-- jQuery -->
<script src="js/jszip.min.js"></script>
<!-- Bootstrap --> <!-- Bootstrap -->
<script src="js/popper.min.js"></script> <script src="js/popper.min.js"></script>
<script src="js/bootstrap.min.js"></script> <script src="js/bootstrap.min.js"></script>
@ -60,65 +63,6 @@ function toggleDarkMode() {
</script> </script>
</head> </head>
<th:block th:fragment="filelist">
<div id="fileList"></div>
<div id="fileList2"></div>
<script>
var input = document.getElementById("fileInput");
var output = document.getElementById("fileList");
input.addEventListener("change", function() {
var files = input.files;
var fileNames = "";
for (var i = 0; i < files.length; i++) {
fileNames += (i + 1) + ". " + files[i].name + "<br>";
}
output.innerHTML = fileNames;
});
</script>
<script>
var input2 = document.getElementById("fileInput2");
var output2 = document.getElementById("fileList2");
if (input2 != null && !input2) {
input2.addEventListener("change", function() {
var files = input2.files;
var fileNames = "";
for (var i = 0; i < files.length; i++) {
fileNames += (i + 1) + ". " + files[i].name + "<br>";
}
output2.innerHTML = fileNames;
});
}
</script>
<script>
if (dropContainer) {
dropContainer.ondragover = dropContainer.ondragenter = function(evt) {
evt.preventDefault();
};
dropContainer.ondrop = function(evt) {
if (fileInput) {
fileInput.files = evt.dataTransfer.files;
const dT = new DataTransfer();
dT.items.add(evt.dataTransfer.files[0]);
dT.items.add(evt.dataTransfer.files[3]);
fileInput.files = dT.files;
evt.preventDefault();
}
};
}
</script>
</th:block>
<th:block th:fragment="fileSelector(name, multiple)"> <th:block th:fragment="fileSelector(name, multiple)">
<div class="custom-file-chooser"> <div class="custom-file-chooser">
<div class="custom-file"> <div class="custom-file">
@ -263,7 +207,17 @@ function toggleDarkMode() {
progressBar.css('width', '0%'); progressBar.css('width', '0%');
progressBar.attr('aria-valuenow', 0); progressBar.attr('aria-valuenow', 0);
progressBar.attr('aria-valuemax', files.length); progressBar.attr('aria-valuemax', files.length);
// Check the flag in localStorage
const zipFiles = localStorage.getItem('zipParallelFiles') === 'true';
// Initialize JSZip instance if needed
let jszip = null;
if (zipFiles) {
jszip = new JSZip();
}
// Submit each PDF file in parallel // Submit each PDF file in parallel
var promises = []; var promises = [];
for (var i = 0; i < files.length; i++) { for (var i = 0; i < files.length; i++) {
@ -287,16 +241,24 @@ function toggleDarkMode() {
var contentDisposition = response.headers.get('content-disposition'); var contentDisposition = response.headers.get('content-disposition');
var fileName = contentDisposition.split('filename=')[1].replace(/"/g, ''); var fileName = contentDisposition.split('filename=')[1].replace(/"/g, '');
response.blob().then(function(blob) { response.blob().then(function (blob) {
var url = window.URL.createObjectURL(blob); if (zipFiles) {
var a = document.createElement('a'); // Add the file to the ZIP archive
a.href = url; jszip.file(fileName, blob);
a.download = fileName; resolve();
document.body.appendChild(a); } else {
a.click(); // Download the file directly
a.remove(); var url = window.URL.createObjectURL(blob);
resolve(); var a = document.createElement('a');
}); a.href = url;
a.download = fileName;
document.body.appendChild(a);
a.click();
a.remove();
resolve();
}
});
}).catch(function(error) { }).catch(function(error) {
console.error('Error submitting request for file ' + i + ': ' + error); console.error('Error submitting request for file ' + i + ': ' + error);
@ -329,10 +291,23 @@ function toggleDarkMode() {
} catch (error) { } catch (error) {
console.error('Error while uploading files: ' + error); console.error('Error while uploading files: ' + error);
} }
// Update the progress bar // Update the progress bar
progressBar.css('width', '100%'); progressBar.css('width', '100%');
progressBar.attr('aria-valuenow', files.length); progressBar.attr('aria-valuenow', files.length);
// After all requests are finished, download the ZIP file if needed
if (zipFiles) {
jszip.generateAsync({ type: "blob" }).then(function (content) {
var url = window.URL.createObjectURL(content);
var a = document.createElement('a');
a.href = url;
a.download = "files.zip";
document.body.appendChild(a);
a.click();
a.remove();
});
}
} }
@ -341,22 +316,54 @@ function toggleDarkMode() {
<script th:inline="javascript"> <script th:inline="javascript">
$([[${"#"+name+"-input"}]]).on("change", function() {
const files = $(this).get(0).files; document.addEventListener('DOMContentLoaded', function () {
const fileNames = Array.from(files).map(f => f.name); const fileInput = document.getElementById('fileInput-input');
const selectedFilesContainer = $(this).siblings(".selected-files");
selectedFilesContainer.empty(); // Prevent default behavior for drag events
fileNames.forEach(fileName => { ['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
selectedFilesContainer.append("<div>" + fileName + "</div>"); fileInput.addEventListener(eventName, preventDefaults, false);
}); });
if (fileNames.length === 1) {
$(this).siblings(".custom-file-label").addClass("selected").html(fileNames[0]); function preventDefaults(e) {
} else if (fileNames.length > 1) { e.preventDefault();
$(this).siblings(".custom-file-label").addClass("selected").html(fileNames.length + " files selected"); e.stopPropagation();
} else { }
$(this).siblings(".custom-file-label").addClass("selected").html([[#{pdfPrompt}]]);
} // Add drop event listener
fileInput.addEventListener('drop', handleDrop, false);
function handleDrop(e) {
const dt = e.dataTransfer;
const files = dt.files;
fileInput.files = files;
handleFileInputChange(fileInput)
}
}); });
$([[${"#"+name+"-input"}]]).on("change", function() {
handleFileInputChange(this);
});
function handleFileInputChange(inputElement) {
const files = $(inputElement).get(0).files;
const fileNames = Array.from(files).map(f => f.name);
const selectedFilesContainer = $(inputElement).siblings(".selected-files");
selectedFilesContainer.empty();
fileNames.forEach(fileName => {
selectedFilesContainer.append("<div>" + fileName + "</div>");
});
if (fileNames.length === 1) {
$(inputElement).siblings(".custom-file-label").addClass("selected").html(fileNames[0]);
} else if (fileNames.length > 1) {
$(inputElement).siblings(".custom-file-label").addClass("selected").html(fileNames.length + " files selected");
} else {
$(inputElement).siblings(".custom-file-label").addClass("selected").html([[#{pdfPrompt}]]);
}
}
</script> </script>

View File

@ -127,12 +127,12 @@ function compareVersions(version1, version2) {
<li class="nav-item"><a class="nav-link" href="#" th:href="@{rotate-pdf}" th:classappend="${currentPage}=='rotate-pdf' ? 'active' : ''" th:text="#{home.rotate.title}"></a></li> <li class="nav-item"><a class="nav-link" href="#" th:href="@{rotate-pdf}" th:classappend="${currentPage}=='rotate-pdf' ? 'active' : ''" th:text="#{home.rotate.title}"></a></li>
<li class="nav-item dropdown" th:classappend="${currentPage}=='pdf-to-img' OR ${currentPage}=='img-to-pdf' OR ${currentPage}=='xlsx-to-pdf' ? 'active' : ''"><a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-toggle="dropdown" <li class="nav-item dropdown" th:classappend="${currentPage}=='pdf-to-img' OR ${currentPage}=='img-to-pdf' OR ${currentPage}=='file-to-pdf' OR ${currentPage}=='xlsx-to-pdf' ? 'active' : ''"><a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-toggle="dropdown"
aria-haspopup="true" aria-expanded="false" th:text="#{navbar.convert}"></a> aria-haspopup="true" aria-expanded="false" th:text="#{navbar.convert}"></a>
<div class="dropdown-menu" aria-labelledby="navbarDropdown"> <div class="dropdown-menu" aria-labelledby="navbarDropdown">
<a class="dropdown-item" href="#" th:href="@{pdf-to-img}" th:classappend="${currentPage}=='pdf-to-img' ? 'active' : ''" th:text="#{home.pdfToImage.title}"></a> <a class="dropdown-item" href="#" th:href="@{pdf-to-img}" th:classappend="${currentPage}=='pdf-to-img' ? 'active' : ''" th:text="#{home.pdfToImage.title}"></a>
<a class="dropdown-item" href="#" th:href="@{img-to-pdf}" th:classappend="${currentPage}=='img-to-pdf' ? 'active' : ''" th:text="#{home.imageToPdf.title}"></a> <a class="dropdown-item" href="#" th:href="@{img-to-pdf}" th:classappend="${currentPage}=='img-to-pdf' ? 'active' : ''" th:text="#{home.imageToPdf.title}"></a>
<a class="dropdown-item" href="#" th:href="@{xlsx-to-pdf}" th:classappend="${currentPage}=='xlsx-to-pdf' ? 'active' : ''" th:text="#{home.xlsToPdf.title}"></a> <a class="dropdown-item" href="#" th:href="@{file-to-pdf}" th:classappend="${currentPage}=='file-to-pdf' ? 'active' : ''" th:text="#{home.fileToPDF.title}"></a>
</div> </div>
</li> </li>
@ -149,20 +149,22 @@ function compareVersions(version1, version2) {
</li> </li>
<li class="nav-item dropdown" th:classappend="${currentPage}=='remove-pages' OR ${currentPage}=='add-image' OR ${currentPage}=='compress-pdf' ? 'active' : ''"><a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-toggle="dropdown" <li class="nav-item dropdown" th:classappend="${currentPage}=='remove-pages' OR ${currentPage}=='add-image' OR ${currentPage}=='ocr-pdf' OR ${currentPage}=='extract-images' OR ${currentPage}=='compress-pdf' ? 'active' : ''"><a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-toggle="dropdown"
aria-haspopup="true" aria-expanded="false" th:text="#{navbar.other}"></a> aria-haspopup="true" aria-expanded="false" th:text="#{navbar.other}"></a>
<div class="dropdown-menu" aria-labelledby="navbarDropdown"> <div class="dropdown-menu" aria-labelledby="navbarDropdown">
<a class="dropdown-item" href="#" th:href="@{add-image}" th:classappend="${currentPage}=='add-image' ? 'active' : ''" th:text="#{home.addImage.title}"></a> <a class="dropdown-item" href="#" th:href="@{compress-pdf}" <a class="dropdown-item" href="#" th:href="@{ocr-pdf}" th:classappend="${currentPage}=='ocr-pdf' ? 'active' : ''" th:text="#{home.ocr.title}"></a>
th:classappend="${currentPage}=='compress-pdf' ? 'active' : ''" th:text="#{home.compressPdfs.title}"></a> <a class="dropdown-item" href="#" th:href="@{remove-pages}" th:classappend="${currentPage}=='remove-pages' ? 'active' : ''" th:text="#{home.removePages.title}"></a> <a class="dropdown-item" href="#" th:href="@{add-image}" th:classappend="${currentPage}=='add-image' ? 'active' : ''" th:text="#{home.addImage.title}"></a>
<a class="dropdown-item" href="#" th:href="@{compress-pdf}" th:classappend="${currentPage}=='compress-pdf' ? 'active' : ''" th:text="#{home.compressPdfs.title}"></a>
<a class="dropdown-item" href="#" th:href="@{remove-pages}" th:classappend="${currentPage}=='remove-pages' ? 'active' : ''" th:text="#{home.removePages.title}"></a>
<a class="dropdown-item" href="#" th:href="@{extract-images}" th:classappend="${currentPage}=='extract-images' ? 'active' : ''" th:text="#{home.extractImages.title}"></a>
</div></li> </div></li>
<input type="checkbox" id="toggle-dark-mode" checked="true" th:onclick="javascript:toggleDarkMode()"> <input type="checkbox" id="toggle-dark-mode" checked="true" th:onclick="javascript:toggleDarkMode()">
<a class="nav-link" href="#" for="toggle-dark-mode" th:text="#{navbar.darkmode}"></a> <a class="nav-link" href="#" for="toggle-dark-mode" th:text="#{navbar.darkmode}" ></a>
<li class="nav-item dropdown"> <li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" id="languageDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> <a class="nav-link dropdown-toggle" href="#" id="languageDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor" class="bi bi-globe2" viewBox="0 0 20 20"> <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor" class="bi bi-globe2" viewBox="0 0 20 20">
@ -178,7 +180,7 @@ function compareVersions(version1, version2) {
<li class="nav-item"> <li class="nav-item">
<!-- Settings Button --> <!-- Settings Button -->
<button type="button" class="btn btn-secondary float-right" data-toggle="modal" data-target="#settingsModal"> <button type="button" class="btn btn-secondary float-right" data-toggle="modal" data-target="#settingsModal">
<span class="glyphicon glyphicon-cog"></span> Settings/About <span class="glyphicon glyphicon-cog"></span><span th:text="#{navbar.settings}"></span>
</button> </button>
</li> </li>
</ul> </ul>
@ -191,31 +193,41 @@ function compareVersions(version1, version2) {
<div th:insert="~{fragments/errorBanner.html :: errorBanner}"></div> <div th:insert="~{fragments/errorBanner.html :: errorBanner}"></div>
<div class="modal fade" id="settingsModal" tabindex="-1" role="dialog" aria-labelledby="settingsModalLabel" aria-hidden="true"> <div class="modal fade" id="settingsModal" tabindex="-1" role="dialog" aria-labelledby="settingsModalLabel" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered" role="document"> <div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content dark-card"> <div class="modal-content dark-card">
<div class="modal-header"> <div class="modal-header">
<h5 class="modal-title" id="settingsModalLabel">Settings</h5> <h5 class="modal-title" id="settingsModalLabel" th:text="#{settings.title}"></h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close"> <button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span> <span aria-hidden="true">&times;</span>
</button> </button>
</div>
<div class="modal-body">
<div class="d-flex justify-content-between align-items-center mb-3">
<p class="mb-0" th:text="#{settings.appVersion} + ' ' + ${@appVersion}"></p>
<button type="button" class="btn btn-sm btn-outline-primary" id="update-btn" th:text="#{settings.update}"></button>
</div> </div>
<button type="button" class="btn btn-outline-primary" id="update-btn">Update available</button> <div class="form-group">
<div class="modal-body"> <label for="downloadOption" th:text="#{settings.downloadOption.title}"></label>
<p th:text="'App Version: ' + ${@appVersion}"></p> <select class="form-control" id="downloadOption">
<div class="form-group"> <option value="sameWindow" th:text="#{settings.downloadOption.1}"></option>
<label for="downloadOption">Choose download option (For non ZIP downloads):</label> <select class="form-control" id="downloadOption"> <option value="newWindow" th:text="#{settings.downloadOption.2}"></option>
<option value="sameWindow">Open in same window (Single file downloads only)</option> <option value="downloadFile" th:text="#{settings.downloadOption.3}"></option>
<option value="newWindow">Open in new window</option> </select>
<option value="downloadFile">Download file</option> </div>
</select> <div class="form-group">
<div class="custom-control custom-checkbox">
<input type="checkbox" class="custom-control-input" id="zipParallelFiles">
<label class="custom-control-label" for="zipParallelFiles" th:text="#{settings.zip}"></label>
</div> </div>
</div> </div>
<div class="modal-footer"> </div>
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button> <div class="modal-footer">
</div> <button type="button" class="btn btn-secondary" data-dismiss="modal" th:text="#{close}"></button>
</div> </div>
</div> </div>
</div> </div>
</div>
<script> <script>
// Get the download option from local storage, or set it to 'sameWindow' if it doesn't exist // Get the download option from local storage, or set it to 'sameWindow' if it doesn't exist
@ -233,6 +245,19 @@ function compareVersions(version1, version2) {
localStorage.setItem('downloadOption', localStorage.setItem('downloadOption',
downloadOption); downloadOption);
}); });
// Get the zipParallelFiles flag from local storage, or set it to false if it doesn't exist
var zipParallelFiles = localStorage.getItem('zipParallelFiles') === 'true';
// Set the checked state of the checkbox
document.getElementById('zipParallelFiles').checked = zipParallelFiles;
// Save the checked state of the checkbox to local storage when it changes
document.getElementById('zipParallelFiles').addEventListener('change', function () {
zipParallelFiles = this.checked;
localStorage.setItem('zipParallelFiles', zipParallelFiles);
});
</script> </script>

View File

@ -50,8 +50,8 @@
<div th:replace="~{fragments/card :: card(cardTitle=#{home.addImage.title}, cardText=#{home.addImage.desc}, cardLink='add-image')}"></div> <div th:replace="~{fragments/card :: card(cardTitle=#{home.addImage.title}, cardText=#{home.addImage.desc}, cardLink='add-image')}"></div>
<div th:replace="~{fragments/card :: card(cardTitle=#{home.watermark.title}, cardText=#{home.watermark.desc}, cardLink='add-watermark')}"></div> <div th:replace="~{fragments/card :: card(cardTitle=#{home.watermark.title}, cardText=#{home.watermark.desc}, cardLink='add-watermark')}"></div>
<div th:replace="~{fragments/card :: card(cardTitle=#{home.xlsToPdf.title}, cardText=#{home.xlsToPdf.desc}, cardLink='xlsx-to-pdf')}"></div> <div th:replace="~{fragments/card :: card(cardTitle=#{home.fileToPDF.title}, cardText=#{home.fileToPDF.desc}, cardLink='file-to-pdf')}"></div>
<div th:replace="~{fragments/card :: card(cardTitle=#{home.removePages.title}, cardText=#{home.removePages.desc}, cardLink='remove-pages')}"></div> <div th:replace="~{fragments/card :: card(cardTitle=#{home.removePages.title}, cardText=#{home.removePages.desc}, cardLink='remove-pages')}"></div>
<div th:replace="~{fragments/card :: card(cardTitle=#{home.addPassword.title}, cardText=#{home.addPassword.desc}, cardLink='add-password')}"></div> <div th:replace="~{fragments/card :: card(cardTitle=#{home.addPassword.title}, cardText=#{home.addPassword.desc}, cardLink='add-password')}"></div>
<div th:replace="~{fragments/card :: card(cardTitle=#{home.removePassword.title}, cardText=#{home.removePassword.desc}, cardLink='remove-password')}"></div> <div th:replace="~{fragments/card :: card(cardTitle=#{home.removePassword.title}, cardText=#{home.removePassword.desc}, cardLink='remove-password')}"></div>
@ -60,6 +60,10 @@
<div th:replace="~{fragments/card :: card(cardTitle=#{home.changeMetadata.title}, cardText=#{home.changeMetadata.desc}, cardLink='change-metadata')}"></div> <div th:replace="~{fragments/card :: card(cardTitle=#{home.changeMetadata.title}, cardText=#{home.changeMetadata.desc}, cardLink='change-metadata')}"></div>
<div th:replace="~{fragments/card :: card(cardTitle=#{home.permissions.title}, cardText=#{home.permissions.desc}, cardLink='change-permissions')}"></div> <div th:replace="~{fragments/card :: card(cardTitle=#{home.permissions.title}, cardText=#{home.permissions.desc}, cardLink='change-permissions')}"></div>
<div th:replace="~{fragments/card :: card(cardTitle=#{home.ocr.title}, cardText=#{home.ocr.desc}, cardLink='ocr-pdf')}"></div>
<div th:replace="~{fragments/card :: card(cardTitle=#{home.extractImages.title}, cardText=#{home.extractImages.desc}, cardLink='extract-images')}"></div>
</div> </div>
</div> </div>

View File

@ -2,7 +2,7 @@
<html th:lang="${#locale.language}" th:lang-direction="#{language.direction}" xmlns:th="http://www.thymeleaf.org"> <html th:lang="${#locale.language}" th:lang-direction="#{language.direction}" xmlns:th="http://www.thymeleaf.org">
<th:block th:insert="~{fragments/common :: head(title=#{addImage.title})}"></th:block> <th:block th:insert="~{fragments/common :: head(title=#{ocr.title})}"></th:block>
<body> <body>
@ -13,15 +13,12 @@
<div class="container"> <div class="container">
<div class="row justify-content-center"> <div class="row justify-content-center">
<div class="col-md-6"> <div class="col-md-6">
<h2 th:text="#{ocrPDF.header}"></h2> <h2 th:text="#{ocr.header}"></h2>
<form action="#" th:action="@{/ocr-pdf}" method="post" enctype="multipart/form-data" class="mb-3"> <form action="#" th:action="@{/ocr-pdf}" method="post" enctype="multipart/form-data" class="mb-3">
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false)}"></div>
<div class="mb-3"> <div class="mb-3">
<label for="fileInput" class="form-label">Choose a PDF file</label> <label for="languages" class="form-label" th:text="#{ocr.selectText.1}"></label>
<input type="file" name="fileInput" id="fileInput" accept="application/pdf" required class="form-control">
</div>
<div class="mb-3">
<label for="languages" class="form-label">Select languages that are to be detected within the PDF (Ones listed are the ones currently detected):</label>
<div id="languages"> <div id="languages">
<div th:each="language: ${languages}"> <div th:each="language: ${languages}">
<input type="checkbox" class="form-check-input" th:name="languages" th:value="${language}" th:id="${'language-' + language}" /> <input type="checkbox" class="form-check-input" th:name="languages" th:value="${language}" th:id="${'language-' + language}" />
@ -31,15 +28,13 @@
</div> </div>
<div class="mb-3"> <div class="mb-3">
<input type="checkbox" class="form-check-input" name="sidecar" id="sidecar" /> <input type="checkbox" class="form-check-input" name="sidecar" id="sidecar" />
<label class="form-check-label" for="sidecar">Produce text file containing OCR text alongside the OCR'ed PDF</label> <label class="form-check-label" for="sidecar" th:text="#{ocr.selectText.2}"></label>
</div> </div>
<button type="submit" class="btn btn-primary">Process PDF with OCR</button> <button type="submit" class="btn btn-primary" th:text="#{ocr.submit}"></button>
</form> </form>
<p> <p th:text="#{ocr.credit}"></p>
Please read this documentation on how to use this for other languages and/or not in docker <p th:text="#{ocr.help}"></p>
</p> <a href="https://github.com/Frooodle/Stirling-PDF/blob/main/HowToUseOCR.md"></a>
<th:block th:insert="~{fragments/common :: filelist}"></th:block>
</div> </div>
</div> </div>
</div> </div>

View File

@ -22,7 +22,6 @@
</div> </div>
<button type="submit" class="btn btn-primary" th:text="#{pdfOrganiser.submit}"></button> <button type="submit" class="btn btn-primary" th:text="#{pdfOrganiser.submit}"></button>
</form> </form>
<th:block th:insert="~{fragments/common :: filelist}"></th:block>
</div> </div>
</div> </div>

View File

@ -22,8 +22,6 @@
</div> </div>
<button type="submit" class="btn btn-primary" th:text="#{pageRemover.submit}"></button> <button type="submit" class="btn btn-primary" th:text="#{pageRemover.submit}"></button>
</form> </form>
<th:block th:insert="~{fragments/common :: filelist}"></th:block>
</div> </div>
</div> </div>
</div> </div>

View File

@ -33,7 +33,6 @@
<br> <br>
<button type="submit" class="btn btn-primary" th:text="#{split.submit}"></button> <button type="submit" class="btn btn-primary" th:text="#{split.submit}"></button>
</form> </form>
<th:block th:insert="~{fragments/common :: filelist}"></th:block>
</div> </div>
</div> </div>
</div> </div>