rearrange support n numbers, downloader.js fixes
This commit is contained in:
parent
e2a787e519
commit
4594765cbd
4 changed files with 235 additions and 245 deletions
|
@ -6,6 +6,11 @@ import stirling.software.SPDF.utils.WebResponseUtils;
|
|||
|
||||
import java.util.ArrayList;
|
||||
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.PDPage;
|
||||
|
@ -26,17 +31,12 @@ public class RearrangePagesPDFController {
|
|||
|
||||
private static final Logger logger = LoggerFactory.getLogger(RearrangePagesPDFController.class);
|
||||
|
||||
|
||||
@PostMapping(consumes = "multipart/form-data", value = "/remove-pages")
|
||||
@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.")
|
||||
@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.")
|
||||
public ResponseEntity<byte[]> deletePages(
|
||||
@RequestPart(required = true, value = "fileInput")
|
||||
@Parameter(description = "The input PDF file from which pages will be removed")
|
||||
MultipartFile pdfFile,
|
||||
@RequestParam("pagesToDelete")
|
||||
@Parameter(description = "Comma-separated list of pages or page ranges to delete, e.g., '1,3,5-8'")
|
||||
String pagesToDelete) throws IOException {
|
||||
@RequestPart(required = true, value = "fileInput") @Parameter(description = "The input PDF file from which pages will be removed") MultipartFile pdfFile,
|
||||
@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());
|
||||
|
||||
|
@ -49,50 +49,18 @@ public class RearrangePagesPDFController {
|
|||
int pageIndex = pagesToRemove.get(i);
|
||||
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 {
|
||||
REVERSE_ORDER,
|
||||
DUPLEX_SORT,
|
||||
BOOKLET_SORT,
|
||||
ODD_EVEN_SPLIT,
|
||||
REMOVE_FIRST,
|
||||
REMOVE_LAST,
|
||||
REMOVE_FIRST_AND_LAST,
|
||||
REVERSE_ORDER, DUPLEX_SORT, BOOKLET_SORT, ODD_EVEN_SPLIT, REMOVE_FIRST, REMOVE_LAST, REMOVE_FIRST_AND_LAST,
|
||||
}
|
||||
|
||||
private List<Integer> removeFirst(int totalPages) {
|
||||
if (totalPages <= 1) return new ArrayList<>();
|
||||
if (totalPages <= 1)
|
||||
return new ArrayList<>();
|
||||
List<Integer> newPageOrder = new ArrayList<>();
|
||||
for (int i = 2; i <= totalPages; i++) {
|
||||
newPageOrder.add(i - 1);
|
||||
|
@ -101,7 +69,8 @@ public class RearrangePagesPDFController {
|
|||
}
|
||||
|
||||
private List<Integer> removeLast(int totalPages) {
|
||||
if (totalPages <= 1) return new ArrayList<>();
|
||||
if (totalPages <= 1)
|
||||
return new ArrayList<>();
|
||||
List<Integer> newPageOrder = new ArrayList<>();
|
||||
for (int i = 1; i < totalPages; i++) {
|
||||
newPageOrder.add(i - 1);
|
||||
|
@ -110,7 +79,8 @@ public class RearrangePagesPDFController {
|
|||
}
|
||||
|
||||
private List<Integer> removeFirstAndLast(int totalPages) {
|
||||
if (totalPages <= 2) return new ArrayList<>();
|
||||
if (totalPages <= 2)
|
||||
return new ArrayList<>();
|
||||
List<Integer> newPageOrder = new ArrayList<>();
|
||||
for (int i = 2; i < totalPages; i++) {
|
||||
newPageOrder.add(i - 1);
|
||||
|
@ -118,7 +88,6 @@ public class RearrangePagesPDFController {
|
|||
return newPageOrder;
|
||||
}
|
||||
|
||||
|
||||
private List<Integer> reverseOrder(int totalPages) {
|
||||
List<Integer> newPageOrder = new ArrayList<>();
|
||||
for (int i = totalPages; i >= 1; i--) {
|
||||
|
@ -139,7 +108,6 @@ public class RearrangePagesPDFController {
|
|||
return newPageOrder;
|
||||
}
|
||||
|
||||
|
||||
private List<Integer> bookletSort(int totalPages) {
|
||||
List<Integer> newPageOrder = new ArrayList<>();
|
||||
for (int i = 0; i < totalPages / 2; i++) {
|
||||
|
@ -188,26 +156,17 @@ public class RearrangePagesPDFController {
|
|||
}
|
||||
|
||||
@PostMapping(consumes = "multipart/form-data", value = "/rearrange-pages")
|
||||
@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.")
|
||||
@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.")
|
||||
public ResponseEntity<byte[]> rearrangePages(
|
||||
@RequestPart(required = true, value = "fileInput")
|
||||
@Parameter(description = "The input PDF file to rearrange pages")
|
||||
MultipartFile pdfFile,
|
||||
@RequestParam(required = false, value = "pageOrder")
|
||||
@Parameter(description = "The new page order as a comma-separated list of page numbers or page ranges (e.g., '1,3,5-7')")
|
||||
String pageOrder,
|
||||
@RequestParam(required = false, value = "customMode")
|
||||
@Parameter(schema = @Schema(implementation = CustomMode.class, description = "The custom mode for page rearrangement. " +
|
||||
"Valid values are:\n" +
|
||||
"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) {
|
||||
@RequestPart(required = true, value = "fileInput") @Parameter(description = "The input PDF file to rearrange pages") MultipartFile pdfFile,
|
||||
@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,
|
||||
@RequestParam(required = false, value = "customMode") @Parameter(schema = @Schema(implementation = CustomMode.class, description = "The custom mode for page rearrangement. "
|
||||
+ "Valid values are:\n" + "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 {
|
||||
// Load the input PDF
|
||||
PDDocument document = PDDocument.load(pdfFile.getInputStream());
|
||||
|
@ -240,12 +199,71 @@ public class RearrangePagesPDFController {
|
|||
document.addPage(page);
|
||||
}
|
||||
|
||||
return WebResponseUtils.pdfDocToWebResponse(document, pdfFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_rearranged.pdf");
|
||||
return WebResponseUtils.pdfDocToWebResponse(document,
|
||||
pdfFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_rearranged.pdf");
|
||||
} catch (IOException e) {
|
||||
logger.error("Failed rearranging documents", e);
|
||||
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)
|
||||
genericSubmit=Submit
|
||||
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
|
||||
true=True
|
||||
false=False
|
||||
|
|
|
@ -19,25 +19,9 @@ $(document).ready(function() {
|
|||
|
||||
try {
|
||||
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);
|
||||
} else {
|
||||
const downloadDetails = 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);
|
||||
|
||||
await handleSingleDownload(url, formData);
|
||||
}
|
||||
|
||||
$('#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 {
|
||||
const response = await fetch(url, { method: 'POST', body: formData });
|
||||
const contentType = response.headers.get('content-type');
|
||||
|
@ -90,7 +54,7 @@ async function handleSingleDownload(url, formData) {
|
|||
const blob = await response.blob();
|
||||
|
||||
if (contentType.includes('application/pdf') || contentType.includes('image/')) {
|
||||
return handleResponse(blob, filename, true);
|
||||
return handleResponse(blob, filename, !isMulti);
|
||||
} else {
|
||||
return handleResponse(blob, filename);
|
||||
}
|
||||
|
@ -118,7 +82,7 @@ function getFilenameFromContentDisposition(contentDisposition) {
|
|||
async function handleJsonResponse(response) {
|
||||
const json = await response.json();
|
||||
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}]]');
|
||||
} else {
|
||||
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 zipFiles = files.length > zipThreshold;
|
||||
let jszip = null;
|
||||
//let progressBar = $('#progressBar');
|
||||
//progressBar.css('width', '0%');
|
||||
//progressBar.attr('aria-valuenow', 0);
|
||||
//progressBar.attr('aria-valuemax', Array.from(files).length);
|
||||
// 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);
|
||||
|
||||
if (zipFiles) {
|
||||
jszip = new JSZip();
|
||||
}
|
||||
|
@ -202,20 +171,21 @@ async function submitMultiPdfForm(url, files) {
|
|||
}
|
||||
|
||||
try {
|
||||
const downloadDetails = await handleSingleDownload(url, fileFormData);
|
||||
const downloadDetails = await handleSingleDownload(url, fileFormData, true);
|
||||
console.log(downloadDetails);
|
||||
if (zipFiles) {
|
||||
jszip.file(downloadDetails.filename, downloadDetails.blob);
|
||||
} else {
|
||||
downloadFile(downloadDetails.blob, downloadDetails.filename);
|
||||
}
|
||||
//updateProgressBar(progressBar, Array.from(files).length);
|
||||
updateProgressBar(progressBar, Array.from(files).length);
|
||||
} catch (error) {
|
||||
handleDownloadError(error);
|
||||
console.error(error);
|
||||
}
|
||||
});
|
||||
await Promise.all(promises);
|
||||
|
||||
}
|
||||
|
||||
if (zipFiles) {
|
||||
|
@ -226,6 +196,8 @@ async function submitMultiPdfForm(url, files) {
|
|||
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 class="form-group">
|
||||
<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>
|
||||
|
||||
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{pdfOrganiser.submit}"></button>
|
||||
|
|
Loading…
Reference in a new issue