mirror of
https://github.com/Frooodle/Stirling-PDF.git
synced 2025-04-08 01:16:26 +02:00
rearrange support n numbers, downloader.js fixes
This commit is contained in:
parent
e2a787e519
commit
4594765cbd
@ -6,6 +6,11 @@ import stirling.software.SPDF.utils.WebResponseUtils;
|
|||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import javax.script.ScriptEngineManager;
|
||||||
|
import javax.script.ScriptEngine;
|
||||||
|
import javax.script.ScriptException;
|
||||||
|
|
||||||
|
import javax.script.ScriptEngine;
|
||||||
|
|
||||||
import org.apache.pdfbox.pdmodel.PDDocument;
|
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||||
import org.apache.pdfbox.pdmodel.PDPage;
|
import org.apache.pdfbox.pdmodel.PDPage;
|
||||||
@ -26,17 +31,12 @@ public class RearrangePagesPDFController {
|
|||||||
|
|
||||||
private static final Logger logger = LoggerFactory.getLogger(RearrangePagesPDFController.class);
|
private static final Logger logger = LoggerFactory.getLogger(RearrangePagesPDFController.class);
|
||||||
|
|
||||||
|
|
||||||
@PostMapping(consumes = "multipart/form-data", value = "/remove-pages")
|
@PostMapping(consumes = "multipart/form-data", value = "/remove-pages")
|
||||||
@Operation(summary = "Remove pages from a PDF file",
|
@Operation(summary = "Remove pages from a PDF file", description = "This endpoint removes specified pages from a given PDF file. Users can provide a comma-separated list of page numbers or ranges to delete.")
|
||||||
description = "This endpoint removes specified pages from a given PDF file. Users can provide a comma-separated list of page numbers or ranges to delete.")
|
|
||||||
public ResponseEntity<byte[]> deletePages(
|
public ResponseEntity<byte[]> deletePages(
|
||||||
@RequestPart(required = true, value = "fileInput")
|
@RequestPart(required = true, value = "fileInput") @Parameter(description = "The input PDF file from which pages will be removed") MultipartFile pdfFile,
|
||||||
@Parameter(description = "The input PDF file from which pages will be removed")
|
@RequestParam("pagesToDelete") @Parameter(description = "Comma-separated list of pages or page ranges to delete, e.g., '1,3,5-8'") String pagesToDelete)
|
||||||
MultipartFile pdfFile,
|
throws IOException {
|
||||||
@RequestParam("pagesToDelete")
|
|
||||||
@Parameter(description = "Comma-separated list of pages or page ranges to delete, e.g., '1,3,5-8'")
|
|
||||||
String pagesToDelete) throws IOException {
|
|
||||||
|
|
||||||
PDDocument document = PDDocument.load(pdfFile.getBytes());
|
PDDocument document = PDDocument.load(pdfFile.getBytes());
|
||||||
|
|
||||||
@ -49,50 +49,18 @@ public class RearrangePagesPDFController {
|
|||||||
int pageIndex = pagesToRemove.get(i);
|
int pageIndex = pagesToRemove.get(i);
|
||||||
document.removePage(pageIndex);
|
document.removePage(pageIndex);
|
||||||
}
|
}
|
||||||
return WebResponseUtils.pdfDocToWebResponse(document, pdfFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_removed_pages.pdf");
|
return WebResponseUtils.pdfDocToWebResponse(document,
|
||||||
|
pdfFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_removed_pages.pdf");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<Integer> pageOrderToString(String[] pageOrderArr, int totalPages) {
|
|
||||||
List<Integer> newPageOrder = new ArrayList<>();
|
|
||||||
// loop through the page order array
|
|
||||||
for (String element : pageOrderArr) {
|
|
||||||
// check if the element contains a range of pages
|
|
||||||
if (element.contains("-")) {
|
|
||||||
// split the range into start and end page
|
|
||||||
String[] range = element.split("-");
|
|
||||||
int start = Integer.parseInt(range[0]);
|
|
||||||
int end = Integer.parseInt(range[1]);
|
|
||||||
// check if the end page is greater than total pages
|
|
||||||
if (end > totalPages) {
|
|
||||||
end = totalPages;
|
|
||||||
}
|
|
||||||
// loop through the range of pages
|
|
||||||
for (int j = start; j <= end; j++) {
|
|
||||||
// print the current index
|
|
||||||
newPageOrder.add(j - 1);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// if the element is a single page
|
|
||||||
newPageOrder.add(Integer.parseInt(element) - 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return newPageOrder;
|
|
||||||
}
|
|
||||||
|
|
||||||
private enum CustomMode {
|
private enum CustomMode {
|
||||||
REVERSE_ORDER,
|
REVERSE_ORDER, DUPLEX_SORT, BOOKLET_SORT, ODD_EVEN_SPLIT, REMOVE_FIRST, REMOVE_LAST, REMOVE_FIRST_AND_LAST,
|
||||||
DUPLEX_SORT,
|
|
||||||
BOOKLET_SORT,
|
|
||||||
ODD_EVEN_SPLIT,
|
|
||||||
REMOVE_FIRST,
|
|
||||||
REMOVE_LAST,
|
|
||||||
REMOVE_FIRST_AND_LAST,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<Integer> removeFirst(int totalPages) {
|
private List<Integer> removeFirst(int totalPages) {
|
||||||
if (totalPages <= 1) return new ArrayList<>();
|
if (totalPages <= 1)
|
||||||
|
return new ArrayList<>();
|
||||||
List<Integer> newPageOrder = new ArrayList<>();
|
List<Integer> newPageOrder = new ArrayList<>();
|
||||||
for (int i = 2; i <= totalPages; i++) {
|
for (int i = 2; i <= totalPages; i++) {
|
||||||
newPageOrder.add(i - 1);
|
newPageOrder.add(i - 1);
|
||||||
@ -101,7 +69,8 @@ public class RearrangePagesPDFController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private List<Integer> removeLast(int totalPages) {
|
private List<Integer> removeLast(int totalPages) {
|
||||||
if (totalPages <= 1) return new ArrayList<>();
|
if (totalPages <= 1)
|
||||||
|
return new ArrayList<>();
|
||||||
List<Integer> newPageOrder = new ArrayList<>();
|
List<Integer> newPageOrder = new ArrayList<>();
|
||||||
for (int i = 1; i < totalPages; i++) {
|
for (int i = 1; i < totalPages; i++) {
|
||||||
newPageOrder.add(i - 1);
|
newPageOrder.add(i - 1);
|
||||||
@ -110,7 +79,8 @@ public class RearrangePagesPDFController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private List<Integer> removeFirstAndLast(int totalPages) {
|
private List<Integer> removeFirstAndLast(int totalPages) {
|
||||||
if (totalPages <= 2) return new ArrayList<>();
|
if (totalPages <= 2)
|
||||||
|
return new ArrayList<>();
|
||||||
List<Integer> newPageOrder = new ArrayList<>();
|
List<Integer> newPageOrder = new ArrayList<>();
|
||||||
for (int i = 2; i < totalPages; i++) {
|
for (int i = 2; i < totalPages; i++) {
|
||||||
newPageOrder.add(i - 1);
|
newPageOrder.add(i - 1);
|
||||||
@ -118,7 +88,6 @@ public class RearrangePagesPDFController {
|
|||||||
return newPageOrder;
|
return newPageOrder;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private List<Integer> reverseOrder(int totalPages) {
|
private List<Integer> reverseOrder(int totalPages) {
|
||||||
List<Integer> newPageOrder = new ArrayList<>();
|
List<Integer> newPageOrder = new ArrayList<>();
|
||||||
for (int i = totalPages; i >= 1; i--) {
|
for (int i = totalPages; i >= 1; i--) {
|
||||||
@ -139,7 +108,6 @@ public class RearrangePagesPDFController {
|
|||||||
return newPageOrder;
|
return newPageOrder;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private List<Integer> bookletSort(int totalPages) {
|
private List<Integer> bookletSort(int totalPages) {
|
||||||
List<Integer> newPageOrder = new ArrayList<>();
|
List<Integer> newPageOrder = new ArrayList<>();
|
||||||
for (int i = 0; i < totalPages / 2; i++) {
|
for (int i = 0; i < totalPages / 2; i++) {
|
||||||
@ -188,26 +156,17 @@ public class RearrangePagesPDFController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping(consumes = "multipart/form-data", value = "/rearrange-pages")
|
@PostMapping(consumes = "multipart/form-data", value = "/rearrange-pages")
|
||||||
@Operation(summary = "Rearrange pages in a PDF file",
|
@Operation(summary = "Rearrange pages in a PDF file", description = "This endpoint rearranges pages in a given PDF file based on the specified page order or custom mode. Users can provide a page order as a comma-separated list of page numbers or page ranges, or a custom mode.")
|
||||||
description = "This endpoint rearranges pages in a given PDF file based on the specified page order or custom mode. Users can provide a page order as a comma-separated list of page numbers or page ranges, or a custom mode.")
|
|
||||||
public ResponseEntity<byte[]> rearrangePages(
|
public ResponseEntity<byte[]> rearrangePages(
|
||||||
@RequestPart(required = true, value = "fileInput")
|
@RequestPart(required = true, value = "fileInput") @Parameter(description = "The input PDF file to rearrange pages") MultipartFile pdfFile,
|
||||||
@Parameter(description = "The input PDF file to rearrange pages")
|
@RequestParam(required = false, value = "pageOrder") @Parameter(description = "The new page order as a comma-separated list of page numbers, page ranges (e.g., '1,3,5-7'), or functions in the format 'an+b' where 'a' is the multiplier of the page number 'n', and 'b' is a constant (e.g., '2n+1', '3n', '6n-5')") String pageOrder,
|
||||||
MultipartFile pdfFile,
|
@RequestParam(required = false, value = "customMode") @Parameter(schema = @Schema(implementation = CustomMode.class, description = "The custom mode for page rearrangement. "
|
||||||
@RequestParam(required = false, value = "pageOrder")
|
+ "Valid values are:\n" + "REVERSE_ORDER: Reverses the order of all pages.\n"
|
||||||
@Parameter(description = "The new page order as a comma-separated list of page numbers or page ranges (e.g., '1,3,5-7')")
|
+ "DUPLEX_SORT: Sorts pages as if all fronts were scanned then all backs in reverse (1, n, 2, n-1, ...). "
|
||||||
String pageOrder,
|
+ "BOOKLET_SORT: Arranges pages for booklet printing (last, first, second, second last, ...).\n"
|
||||||
@RequestParam(required = false, value = "customMode")
|
+ "ODD_EVEN_SPLIT: Splits and arranges pages into odd and even numbered pages.\n"
|
||||||
@Parameter(schema = @Schema(implementation = CustomMode.class, description = "The custom mode for page rearrangement. " +
|
+ "REMOVE_FIRST: Removes the first page.\n" + "REMOVE_LAST: Removes the last page.\n"
|
||||||
"Valid values are:\n" +
|
+ "REMOVE_FIRST_AND_LAST: Removes both the first and the last pages.\n")) String customMode) {
|
||||||
"REVERSE_ORDER: Reverses the order of all pages.\n" +
|
|
||||||
"DUPLEX_SORT: Sorts pages as if all fronts were scanned then all backs in reverse (1, n, 2, n-1, ...). " +
|
|
||||||
"BOOKLET_SORT: Arranges pages for booklet printing (last, first, second, second last, ...).\n" +
|
|
||||||
"ODD_EVEN_SPLIT: Splits and arranges pages into odd and even numbered pages.\n" +
|
|
||||||
"REMOVE_FIRST: Removes the first page.\n" +
|
|
||||||
"REMOVE_LAST: Removes the last page.\n" +
|
|
||||||
"REMOVE_FIRST_AND_LAST: Removes both the first and the last pages.\n"))
|
|
||||||
String customMode) {
|
|
||||||
try {
|
try {
|
||||||
// Load the input PDF
|
// Load the input PDF
|
||||||
PDDocument document = PDDocument.load(pdfFile.getInputStream());
|
PDDocument document = PDDocument.load(pdfFile.getInputStream());
|
||||||
@ -240,12 +199,71 @@ public class RearrangePagesPDFController {
|
|||||||
document.addPage(page);
|
document.addPage(page);
|
||||||
}
|
}
|
||||||
|
|
||||||
return WebResponseUtils.pdfDocToWebResponse(document, pdfFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_rearranged.pdf");
|
return WebResponseUtils.pdfDocToWebResponse(document,
|
||||||
|
pdfFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_rearranged.pdf");
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
logger.error("Failed rearranging documents", e);
|
logger.error("Failed rearranging documents", e);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private List<Integer> pageOrderToString(String[] pageOrderArr, int totalPages) {
|
||||||
|
List<Integer> newPageOrder = new ArrayList<>();
|
||||||
|
|
||||||
|
// loop through the page order array
|
||||||
|
for (String element : pageOrderArr) {
|
||||||
|
// check if the element contains a range of pages
|
||||||
|
if (element.matches("\\d*n\\+?-?\\d*|\\d*\\+?n")) {
|
||||||
|
// Handle page order as a function
|
||||||
|
int coefficient = 0;
|
||||||
|
int constant = 0;
|
||||||
|
boolean coefficientExists = false;
|
||||||
|
boolean constantExists = false;
|
||||||
|
|
||||||
|
if (element.contains("n")) {
|
||||||
|
String[] parts = element.split("n");
|
||||||
|
if (!parts[0].equals("") && parts[0] != null) {
|
||||||
|
coefficient = Integer.parseInt(parts[0]);
|
||||||
|
coefficientExists = true;
|
||||||
|
}
|
||||||
|
if (parts.length > 1 && !parts[1].equals("") && parts[1] != null) {
|
||||||
|
constant = Integer.parseInt(parts[1]);
|
||||||
|
constantExists = true;
|
||||||
|
}
|
||||||
|
} else if (element.contains("+")) {
|
||||||
|
constant = Integer.parseInt(element.replace("+", ""));
|
||||||
|
constantExists = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 1; i <= totalPages; i++) {
|
||||||
|
int pageNum = coefficientExists ? coefficient * i : i;
|
||||||
|
pageNum += constantExists ? constant : 0;
|
||||||
|
|
||||||
|
if (pageNum <= totalPages && pageNum > 0) {
|
||||||
|
newPageOrder.add(pageNum - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (element.contains("-")) {
|
||||||
|
// split the range into start and end page
|
||||||
|
String[] range = element.split("-");
|
||||||
|
int start = Integer.parseInt(range[0]);
|
||||||
|
int end = Integer.parseInt(range[1]);
|
||||||
|
// check if the end page is greater than total pages
|
||||||
|
if (end > totalPages) {
|
||||||
|
end = totalPages;
|
||||||
|
}
|
||||||
|
// loop through the range of pages
|
||||||
|
for (int j = start; j <= end; j++) {
|
||||||
|
// print the current index
|
||||||
|
newPageOrder.add(j - 1);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// if the element is a single page
|
||||||
|
newPageOrder.add(Integer.parseInt(element) - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return newPageOrder;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,7 @@ multiPdfDropPrompt=Select (or drag & drop) all PDFs you require
|
|||||||
imgPrompt=Select Image(s)
|
imgPrompt=Select Image(s)
|
||||||
genericSubmit=Submit
|
genericSubmit=Submit
|
||||||
processTimeWarning=Warning: This process can take up to a minute depending on file-size
|
processTimeWarning=Warning: This process can take up to a minute depending on file-size
|
||||||
pageOrderPrompt=Custom Page Order (Enter a comma-separated list of page numbers) :
|
pageOrderPrompt=Custom Page Order (Enter a comma-separated list of page numbers or Functions like 2n+1) :
|
||||||
goToPage=Go
|
goToPage=Go
|
||||||
true=True
|
true=True
|
||||||
false=False
|
false=False
|
||||||
|
@ -19,25 +19,9 @@ $(document).ready(function() {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
if (override === 'multi' || files.length > 1 && override !== 'single') {
|
if (override === 'multi' || files.length > 1 && override !== 'single') {
|
||||||
// Show the progress bar
|
|
||||||
$('#progressBarContainer').show();
|
|
||||||
// Initialize the progress bar
|
|
||||||
//let progressBar = $('#progressBar');
|
|
||||||
//progressBar.css('width', '0%');
|
|
||||||
//progressBar.attr('aria-valuenow', 0);
|
|
||||||
//progressBar.attr('aria-valuemax', files.length);
|
|
||||||
|
|
||||||
await submitMultiPdfForm(url, files);
|
await submitMultiPdfForm(url, files);
|
||||||
} else {
|
} else {
|
||||||
const downloadDetails = await handleSingleDownload(url, formData);
|
await handleSingleDownload(url, formData);
|
||||||
const downloadOption = localStorage.getItem('downloadOption');
|
|
||||||
|
|
||||||
// Handle the download action according to the selected option
|
|
||||||
//handleDownloadAction(downloadOption, downloadDetails.blob, downloadDetails.filename);
|
|
||||||
|
|
||||||
// Update the progress bar
|
|
||||||
//updateProgressBar(progressBar, 1);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$('#submitBtn').text('Submit');
|
$('#submitBtn').text('Submit');
|
||||||
@ -49,29 +33,9 @@ $(document).ready(function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
function handleDownloadAction(downloadOption, blob, filename) {
|
|
||||||
const url = URL.createObjectURL(blob);
|
|
||||||
|
|
||||||
switch (downloadOption) {
|
|
||||||
case 'sameWindow':
|
|
||||||
// Open the file in the same window
|
|
||||||
window.location.href = url;
|
|
||||||
break;
|
|
||||||
case 'newWindow':
|
|
||||||
// Open the file in a new window
|
|
||||||
window.open(url, '_blank');
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
// Download the file
|
|
||||||
const link = document.createElement('a');
|
|
||||||
link.href = url;
|
|
||||||
link.download = filename;
|
|
||||||
link.click();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function handleSingleDownload(url, formData) {
|
async function handleSingleDownload(url, formData, isMulti = false) {
|
||||||
try {
|
try {
|
||||||
const response = await fetch(url, { method: 'POST', body: formData });
|
const response = await fetch(url, { method: 'POST', body: formData });
|
||||||
const contentType = response.headers.get('content-type');
|
const contentType = response.headers.get('content-type');
|
||||||
@ -90,7 +54,7 @@ async function handleSingleDownload(url, formData) {
|
|||||||
const blob = await response.blob();
|
const blob = await response.blob();
|
||||||
|
|
||||||
if (contentType.includes('application/pdf') || contentType.includes('image/')) {
|
if (contentType.includes('application/pdf') || contentType.includes('image/')) {
|
||||||
return handleResponse(blob, filename, true);
|
return handleResponse(blob, filename, !isMulti);
|
||||||
} else {
|
} else {
|
||||||
return handleResponse(blob, filename);
|
return handleResponse(blob, filename);
|
||||||
}
|
}
|
||||||
@ -118,7 +82,7 @@ function getFilenameFromContentDisposition(contentDisposition) {
|
|||||||
async function handleJsonResponse(response) {
|
async function handleJsonResponse(response) {
|
||||||
const json = await response.json();
|
const json = await response.json();
|
||||||
const errorMessage = JSON.stringify(json, null, 2);
|
const errorMessage = JSON.stringify(json, null, 2);
|
||||||
if (errorMessage.toLowerCase().includes('the password is incorrect') || errorMessage.toLowerCase().includes('Password is not provided')) {
|
if (errorMessage.toLowerCase().includes('the password is incorrect') || errorMessage.toLowerCase().includes('Password is not provided') || errorMessage.toLowerCase().includes('PDF contains an encryption dictionary')) {
|
||||||
alert('[[#{error.pdfPassword}]]');
|
alert('[[#{error.pdfPassword}]]');
|
||||||
} else {
|
} else {
|
||||||
showErrorBanner(json.error + ':' + json.message, json.trace);
|
showErrorBanner(json.error + ':' + json.message, json.trace);
|
||||||
@ -173,10 +137,15 @@ async function submitMultiPdfForm(url, files) {
|
|||||||
const zipThreshold = parseInt(localStorage.getItem('zipThreshold'), 10) || 4;
|
const zipThreshold = parseInt(localStorage.getItem('zipThreshold'), 10) || 4;
|
||||||
const zipFiles = files.length > zipThreshold;
|
const zipFiles = files.length > zipThreshold;
|
||||||
let jszip = null;
|
let jszip = null;
|
||||||
//let progressBar = $('#progressBar');
|
// Show the progress bar
|
||||||
//progressBar.css('width', '0%');
|
$('#progressBarContainer').show();
|
||||||
//progressBar.attr('aria-valuenow', 0);
|
// Initialize the progress bar
|
||||||
//progressBar.attr('aria-valuemax', Array.from(files).length);
|
|
||||||
|
let progressBar = $('#progressBar');
|
||||||
|
progressBar.css('width', '0%');
|
||||||
|
progressBar.attr('aria-valuenow', 0);
|
||||||
|
progressBar.attr('aria-valuemax', files.length);
|
||||||
|
|
||||||
if (zipFiles) {
|
if (zipFiles) {
|
||||||
jszip = new JSZip();
|
jszip = new JSZip();
|
||||||
}
|
}
|
||||||
@ -202,20 +171,21 @@ async function submitMultiPdfForm(url, files) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const downloadDetails = await handleSingleDownload(url, fileFormData);
|
const downloadDetails = await handleSingleDownload(url, fileFormData, true);
|
||||||
console.log(downloadDetails);
|
console.log(downloadDetails);
|
||||||
if (zipFiles) {
|
if (zipFiles) {
|
||||||
jszip.file(downloadDetails.filename, downloadDetails.blob);
|
jszip.file(downloadDetails.filename, downloadDetails.blob);
|
||||||
} else {
|
} else {
|
||||||
downloadFile(downloadDetails.blob, downloadDetails.filename);
|
downloadFile(downloadDetails.blob, downloadDetails.filename);
|
||||||
}
|
}
|
||||||
//updateProgressBar(progressBar, Array.from(files).length);
|
updateProgressBar(progressBar, Array.from(files).length);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
handleDownloadError(error);
|
handleDownloadError(error);
|
||||||
console.error(error);
|
console.error(error);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
await Promise.all(promises);
|
await Promise.all(promises);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (zipFiles) {
|
if (zipFiles) {
|
||||||
@ -226,6 +196,8 @@ async function submitMultiPdfForm(url, files) {
|
|||||||
console.error('Error generating ZIP file: ' + error);
|
console.error('Error generating ZIP file: ' + error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
progressBar.css('width', '100%');
|
||||||
|
progressBar.attr('aria-valuenow', Array.from(files).length);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -31,7 +31,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="pageOrder" th:text="#{pageOrderPrompt}"></label>
|
<label for="pageOrder" th:text="#{pageOrderPrompt}"></label>
|
||||||
<input type="text" class="form-control" id="pageOrder" name="pageOrder" placeholder="(e.g. 1,3,2 or 4-8,2,10-12)" required>
|
<input type="text" class="form-control" id="pageOrder" name="pageOrder" placeholder="(e.g. 1,3,2 or 4-8,2,10-12 or 2n-1)" required>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{pdfOrganiser.submit}"></button>
|
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{pdfOrganiser.submit}"></button>
|
||||||
|
Loading…
Reference in New Issue
Block a user