This commit is contained in:
Anthony Stirling 2023-05-26 23:53:11 +01:00
parent 8a2aa44de8
commit 8bbbdbd359
3 changed files with 157 additions and 11 deletions

View file

@ -1,6 +1,7 @@
package stirling.software.SPDF.controller.api; package stirling.software.SPDF.controller.api;
import java.io.IOException; import java.io.IOException;
import io.swagger.v3.oas.annotations.media.Schema;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -79,26 +80,148 @@ public class RearrangePagesPDFController {
return newPageOrder; return newPageOrder;
} }
private enum CustomMode {
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<>();
List<Integer> newPageOrder = new ArrayList<>();
for (int i = 2; i <= totalPages; i++) {
newPageOrder.add(i - 1);
}
return newPageOrder;
}
private List<Integer> removeLast(int totalPages) {
if (totalPages <= 1) return new ArrayList<>();
List<Integer> newPageOrder = new ArrayList<>();
for (int i = 1; i < totalPages; i++) {
newPageOrder.add(i - 1);
}
return newPageOrder;
}
private List<Integer> removeFirstAndLast(int totalPages) {
if (totalPages <= 2) return new ArrayList<>();
List<Integer> newPageOrder = new ArrayList<>();
for (int i = 2; i < totalPages; i++) {
newPageOrder.add(i - 1);
}
return newPageOrder;
}
private List<Integer> reverseOrder(int totalPages) {
List<Integer> newPageOrder = new ArrayList<>();
for (int i = totalPages; i >= 1; i--) {
newPageOrder.add(i - 1);
}
return newPageOrder;
}
private List<Integer> duplexSort(int totalPages) {
List<Integer> newPageOrder = new ArrayList<>();
int half = (totalPages + 1) / 2; // This ensures proper behavior with odd numbers of pages
for (int i = 1; i <= half; i++) {
newPageOrder.add(i - 1);
if (i <= totalPages - half) { // Avoid going out of bounds
newPageOrder.add(totalPages - i);
}
}
return newPageOrder;
}
private List<Integer> bookletSort(int totalPages) {
List<Integer> newPageOrder = new ArrayList<>();
for (int i = 0; i < totalPages / 2; i++) {
newPageOrder.add(i);
newPageOrder.add(totalPages - i - 1);
}
return newPageOrder;
}
private List<Integer> oddEvenSplit(int totalPages) {
List<Integer> newPageOrder = new ArrayList<>();
for (int i = 1; i <= totalPages; i += 2) {
newPageOrder.add(i - 1);
}
for (int i = 2; i <= totalPages; i += 2) {
newPageOrder.add(i - 1);
}
return newPageOrder;
}
private List<Integer> processCustomMode(String customMode, int totalPages) {
try {
CustomMode mode = CustomMode.valueOf(customMode.toUpperCase());
switch (mode) {
case REVERSE_ORDER:
return reverseOrder(totalPages);
case DUPLEX_SORT:
return duplexSort(totalPages);
case BOOKLET_SORT:
return bookletSort(totalPages);
case ODD_EVEN_SPLIT:
return oddEvenSplit(totalPages);
case REMOVE_FIRST:
return removeFirst(totalPages);
case REMOVE_LAST:
return removeLast(totalPages);
case REMOVE_FIRST_AND_LAST:
return removeFirstAndLast(totalPages);
default:
throw new IllegalArgumentException("Unsupported custom mode");
}
} catch (IllegalArgumentException e) {
logger.error("Unsupported custom mode", e);
return null;
}
}
@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. Users can provide a page order as a comma-separated list of page numbers or page ranges.") 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") @Parameter(description = "The input PDF file to rearrange pages")
MultipartFile pdfFile, MultipartFile pdfFile,
@RequestParam("pageOrder") @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')") @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) { 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 { try {
// Load the input PDF // Load the input PDF
PDDocument document = PDDocument.load(pdfFile.getInputStream()); PDDocument document = PDDocument.load(pdfFile.getInputStream());
// Split the page order string into an array of page numbers or range of numbers // Split the page order string into an array of page numbers or range of numbers
String[] pageOrderArr = pageOrder.split(","); String[] pageOrderArr = pageOrder != null ? pageOrder.split(",") : new String[0];
// int[] newPageOrder = new int[pageOrderArr.length];
int totalPages = document.getNumberOfPages(); int totalPages = document.getNumberOfPages();
System.out.println("pageOrder=" + pageOrder);
List<Integer> newPageOrder = pageOrderToString(pageOrderArr, totalPages); System.out.println("customMode length =" + customMode.length());
List<Integer> newPageOrder;
if(customMode != null && customMode.length() > 0) {
newPageOrder = processCustomMode(customMode, totalPages);
} else {
newPageOrder = pageOrderToString(pageOrderArr, totalPages);
}
// Create a new list to hold the pages in the new order // Create a new list to hold the pages in the new order
List<PDPage> newPages = new ArrayList<>(); List<PDPage> newPages = new ArrayList<>();
@ -118,10 +241,10 @@ public class RearrangePagesPDFController {
return PdfUtils.pdfDocToWebResponse(document, pdfFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_rearranged.pdf"); return PdfUtils.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;
} }
} }
} }

View file

@ -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=Page Order (Enter a comma-separated list of page numbers) : pageOrderPrompt=Custom Page Order (Enter a comma-separated list of page numbers) :
goToPage=Go goToPage=Go
true=True true=True
false=False false=False

View file

@ -17,12 +17,35 @@
<form th:action="@{rearrange-pages}" method="post" enctype="multipart/form-data"> <form th:action="@{rearrange-pages}" method="post" enctype="multipart/form-data">
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false, accept='application/pdf')}"></div> <div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false, accept='application/pdf')}"></div>
<div class="form-group"> <div class="form-group">
<label for="pageOrder" th:text="#{pageOrderPrompt}"></label> <label for="customMode">Mode</label>
<input type="text" class="form-control" id="fileInput" name="pageOrder" placeholder="(e.g. 1,3,2 or 4-8,2,10-12)" required> <select class="form-control" id="customMode" name="customMode">
<option value="">Custom Page Order</option>
<option value="REVERSE_ORDER">Reverse Order</option>
<option value="DUPLEX_SORT">Duplex Sort</option>
<option value="BOOKLET_SORT">Booklet Sort</option>
<option value="ODD_EVEN_SPLIT">Odd-Even Split</option>
<option value="REMOVE_FIRST">Remove First</option>
<option value="REMOVE_LAST">Remove Last</option>
<option value="REMOVE_FIRST_AND_LAST">Remove First and Last</option>
</select>
</div> </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>
</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>
</form> </form>
<script>
document.getElementById('customMode').addEventListener('change', function () {
var pageOrderInput = document.getElementById('pageOrder');
if (this.value === "") {
pageOrderInput.disabled = false;
} else {
pageOrderInput.disabled = true;
}
});
</script>
</div> </div>
</div> </div>
</div> </div>