feat: implement split modes including presets and custom input

Added support for predefined split modes:
- Split all except first and last
- Split all except first
- Split all except last
- Split all

Also added a custom mode that enables users to specify exact pages to split via input field.
This commit is contained in:
Ping Lin 2025-08-04 11:28:12 +01:00
parent 1fc40f16c2
commit 07c5294acc
5 changed files with 101 additions and 8 deletions

View File

@ -33,6 +33,7 @@ import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import stirling.software.SPDF.model.SplitTypes;
import stirling.software.SPDF.model.api.SplitPdfBySectionsRequest;
import stirling.software.common.service.CustomPDFDocumentFactory;
import stirling.software.common.util.GeneralUtils;
@ -51,7 +52,8 @@ public class SplitPdfBySectionsController {
summary = "Split PDF pages into smaller sections",
description =
"Split each page of a PDF into smaller sections based on the user's choice"
+ " (halves, thirds, quarters, etc.), both vertically and horizontally."
+ " which page to split, and how to split"
+ " ( halves, thirds, quarters, etc.), both vertically and horizontally."
+ " Input:PDF Output:ZIP-PDF Type:SISO")
public ResponseEntity<byte[]> splitPdf(@ModelAttribute SplitPdfBySectionsRequest request)
throws Exception {
@ -59,13 +61,10 @@ public class SplitPdfBySectionsController {
MultipartFile file = request.getFileInput();
String pageNumbers = request.getPageNumbers();
String splitMode = request.getSplitMode();
PDDocument sourceDocument = pdfDocumentFactory.load(file);
// Split the page order string into an array of page numbers or range of numbers
String[] pageOrderArr = pageNumbers.split(",");
List<Integer> pageListToSplit =
GeneralUtils.parsePageList(pageOrderArr, sourceDocument.getNumberOfPages(), false);
Set<Integer> pagesToSplit = new HashSet<>(pageListToSplit);
Set<Integer> pagesToSplit = getPagesToSplit(pageNumbers, splitMode, sourceDocument.getNumberOfPages());
// Process the PDF based on split parameters
int horiz = request.getHorizontalDivisions() + 1;
@ -119,6 +118,52 @@ public class SplitPdfBySectionsController {
}
}
// Based on the mode, get the pages that need to be split and return the pages set
private Set<Integer> getPagesToSplit(String pageNumbers, String splitMode, int totalPages) {
Set<Integer> pagesToSplit = new HashSet<>();
switch (SplitTypes.valueOf(splitMode)) {
case CUSTOM:
if (pageNumbers == null || pageNumbers.isBlank()) {
throw new IllegalArgumentException("Custom mode requires page numbers input.");
}
String[] pageOrderArr = pageNumbers.split(",");
List<Integer> pageListToSplit = GeneralUtils.parsePageList(pageOrderArr, totalPages, false);
pagesToSplit.addAll(pageListToSplit);
break;
case SPLIT_ALL:
for (int i = 0; i < totalPages; i++) {
pagesToSplit.add(i);
}
break;
case SPLIT_ALL_EXCEPT_FIRST:
for (int i = 1; i < totalPages; i++) {
pagesToSplit.add(i);
}
break;
case SPLIT_ALL_EXCEPT_LAST:
for (int i = 0; i < totalPages - 1; i++) {
pagesToSplit.add(i);
}
break;
case SPLIT_ALL_EXCEPT_FIRST_AND_LAST:
for (int i = 1; i < totalPages - 1; i++) {
pagesToSplit.add(i);
}
break;
default:
throw new IllegalArgumentException("Unsupported split mode: " + splitMode);
}
return pagesToSplit;
}
public List<PDDocument> splitPdfPages(
PDDocument document, int horizontalDivisions, int verticalDivisions, Set<Integer> pagesToSplit)
throws IOException {

View File

@ -0,0 +1,9 @@
package stirling.software.SPDF.model;
public enum SplitTypes {
CUSTOM,
SPLIT_ALL_EXCEPT_FIRST_AND_LAST,
SPLIT_ALL_EXCEPT_FIRST,
SPLIT_ALL_EXCEPT_LAST,
SPLIT_ALL
}

View File

@ -5,6 +5,7 @@ import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import stirling.software.SPDF.model.SplitTypes;
import stirling.software.common.model.api.PDFFile;
@Data
@ -16,6 +17,17 @@ public class SplitPdfBySectionsRequest extends PDFFile {
requiredMode = Schema.RequiredMode.REQUIRED)
private String pageNumbers;
@Schema(
implementation = SplitTypes.class,
description =
"Modes for page split. Valid values are:\n"
+ "SPLIT_ALL_EXCEPT_FIRST_AND_LAST: Splits all except the first and the last pages.\n"
+ "SPLIT_ALL_EXCEPT_FIRST: Splits all except the first page.\n"
+ "SPLIT_ALL_EXCEPT_LAST: Splits all except the last page.\n"
+ "SPLIT_ALL: Splits all pages.\n"
+ "CUSTOM: Custom split.\n")
private String splitMode;
@Schema(
description = "Number of horizontal divisions for each PDF page",
defaultValue = "0",

View File

@ -1587,7 +1587,14 @@ split-by-sections.vertical.placeholder=Enter number of vertical divisions
split-by-sections.submit=Split PDF
split-by-sections.merge=Merge Into One PDF
split-by-sections.pageToSplit=Pages to split (Enter a comma-separated list of page numbers) :
split-by-sections.pageToSplit.placeholder=(e.g. 1,2,6 or 1-10,15-30)
split-by-sections.pageToSplit.placeholder=(e.g. 1,2,6)
split-by-sections.mode=Mode
split-by-sections.mode.1=Split all except first and last
split-by-sections.mode.2=Split all except first
split-by-sections.mode.3=Split all except last
split-by-sections.mode.4=Split all
split-by-sections.mode.5=Custom. Specify pages to split
#printFile

View File

@ -20,6 +20,16 @@
</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>
<div class="mb-3">
<label for="splitMode" th:text="#{split-by-sections.mode}">Mode</label>
<select class="form-control" id="splitMode" name="splitMode">
<option value="SPLIT_ALL_EXCEPT_FIRST_AND_LAST" th:text="#{split-by-sections.mode.1}">Split all except first and last</option>
<option value="SPLIT_ALL_EXCEPT_FIRST" th:text="#{split-by-sections.mode.2}">Split all except first</option>
<option value="SPLIT_ALL_EXCEPT_LAST" th:text="#{split-by-sections.mode.3}">Split all except last</option>
<option value="SPLIT_ALL" th:text="#{split-by-sections.mode.4}">Split all</option>
<option value="CUSTOM" th:text="#{split-by-sections.mode.5}">Specify pages to split</option>
</select>
</div>
<div class="mb-3">
<label for="pageToSplit" th:text="#{split-by-sections.pageToSplit}"></label>
<input type="text" class="form-control" id="pageToSplit" name="pageNumbers"
@ -84,7 +94,17 @@
document.getElementById('pageToSplit').addEventListener('input', function () {
this.value = this.value.replace(/\s+/g, '');;
});
// Only enable the page list input field when split mode is custom.
function togglePageInput() {
const mode = document.getElementById('splitMode').value;
const pageListInput = document.getElementById('pageToSplit');
pageListInput.disabled = mode !== "CUSTOM";
}
document.getElementById('splitMode').addEventListener('change', togglePageInput);
window.addEventListener('DOMContentLoaded', togglePageInput);
</script>
<br>
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{split-by-sections.submit}">Submit</button>