feat: implement backend support for selective page splitting in Split PDF by Sections

This commit is contained in:
Ping Lin 2025-08-03 23:47:13 +01:00
parent 07b3bfa1f3
commit 1fc40f16c2
2 changed files with 57 additions and 31 deletions

View File

@ -5,7 +5,9 @@ import java.io.IOException;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set;
import java.util.zip.ZipEntry; import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream; import java.util.zip.ZipOutputStream;
@ -33,6 +35,7 @@ import lombok.RequiredArgsConstructor;
import stirling.software.SPDF.model.api.SplitPdfBySectionsRequest; import stirling.software.SPDF.model.api.SplitPdfBySectionsRequest;
import stirling.software.common.service.CustomPDFDocumentFactory; import stirling.software.common.service.CustomPDFDocumentFactory;
import stirling.software.common.util.GeneralUtils;
import stirling.software.common.util.WebResponseUtils; import stirling.software.common.util.WebResponseUtils;
@RestController @RestController
@ -55,13 +58,20 @@ public class SplitPdfBySectionsController {
List<ByteArrayOutputStream> splitDocumentsBoas = new ArrayList<>(); List<ByteArrayOutputStream> splitDocumentsBoas = new ArrayList<>();
MultipartFile file = request.getFileInput(); MultipartFile file = request.getFileInput();
String pageNumbers = request.getPageNumbers();
PDDocument sourceDocument = pdfDocumentFactory.load(file); 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);
// Process the PDF based on split parameters // Process the PDF based on split parameters
int horiz = request.getHorizontalDivisions() + 1; int horiz = request.getHorizontalDivisions() + 1;
int verti = request.getVerticalDivisions() + 1; int verti = request.getVerticalDivisions() + 1;
boolean merge = Boolean.TRUE.equals(request.getMerge()); boolean merge = Boolean.TRUE.equals(request.getMerge());
List<PDDocument> splitDocuments = splitPdfPages(sourceDocument, verti, horiz); List<PDDocument> splitDocuments = splitPdfPages(sourceDocument, verti, horiz, pagesToSplit);
String filename = String filename =
Filenames.toSimpleFileName(file.getOriginalFilename()) Filenames.toSimpleFileName(file.getOriginalFilename())
@ -110,51 +120,61 @@ public class SplitPdfBySectionsController {
} }
public List<PDDocument> splitPdfPages( public List<PDDocument> splitPdfPages(
PDDocument document, int horizontalDivisions, int verticalDivisions) PDDocument document, int horizontalDivisions, int verticalDivisions, Set<Integer> pagesToSplit)
throws IOException { throws IOException {
List<PDDocument> splitDocuments = new ArrayList<>(); List<PDDocument> splitDocuments = new ArrayList<>();
int pageIndex = 0;
for (PDPage originalPage : document.getPages()) { for (PDPage originalPage : document.getPages()) {
PDRectangle originalMediaBox = originalPage.getMediaBox(); // If current page is not to split, add it to the splitDocuments directly.
float width = originalMediaBox.getWidth(); if (!pagesToSplit.contains(pageIndex)) {
float height = originalMediaBox.getHeight(); PDDocument newDoc = pdfDocumentFactory.createNewDocument();
float subPageWidth = width / horizontalDivisions; newDoc.addPage(originalPage);
float subPageHeight = height / verticalDivisions; splitDocuments.add(newDoc);
} else {
// Otherwise, split current page.
PDRectangle originalMediaBox = originalPage.getMediaBox();
float width = originalMediaBox.getWidth();
float height = originalMediaBox.getHeight();
float subPageWidth = width / horizontalDivisions;
float subPageHeight = height / verticalDivisions;
LayerUtility layerUtility = new LayerUtility(document); LayerUtility layerUtility = new LayerUtility(document);
for (int i = 0; i < horizontalDivisions; i++) { for (int i = 0; i < horizontalDivisions; i++) {
for (int j = 0; j < verticalDivisions; j++) { for (int j = 0; j < verticalDivisions; j++) {
PDDocument subDoc = new PDDocument(); PDDocument subDoc = new PDDocument();
PDPage subPage = new PDPage(new PDRectangle(subPageWidth, subPageHeight)); PDPage subPage = new PDPage(new PDRectangle(subPageWidth, subPageHeight));
subDoc.addPage(subPage); subDoc.addPage(subPage);
PDFormXObject form = PDFormXObject form =
layerUtility.importPageAsForm( layerUtility.importPageAsForm(
document, document.getPages().indexOf(originalPage)); document, document.getPages().indexOf(originalPage));
try (PDPageContentStream contentStream = try (PDPageContentStream contentStream =
new PDPageContentStream( new PDPageContentStream(
subDoc, subPage, AppendMode.APPEND, true, true)) { subDoc, subPage, AppendMode.APPEND, true, true)) {
// Set clipping area and position // Set clipping area and position
float translateX = -subPageWidth * i; float translateX = -subPageWidth * i;
// float translateY = height - subPageHeight * (verticalDivisions - j); // float translateY = height - subPageHeight * (verticalDivisions - j);
float translateY = -subPageHeight * (verticalDivisions - 1 - j); float translateY = -subPageHeight * (verticalDivisions - 1 - j);
contentStream.saveGraphicsState(); contentStream.saveGraphicsState();
contentStream.addRect(0, 0, subPageWidth, subPageHeight); contentStream.addRect(0, 0, subPageWidth, subPageHeight);
contentStream.clip(); contentStream.clip();
contentStream.transform(new Matrix(1, 0, 0, 1, translateX, translateY)); contentStream.transform(new Matrix(1, 0, 0, 1, translateX, translateY));
// Draw the form // Draw the form
contentStream.drawForm(form); contentStream.drawForm(form);
contentStream.restoreGraphicsState(); contentStream.restoreGraphicsState();
}
splitDocuments.add(subDoc);
} }
splitDocuments.add(subDoc);
} }
} }
pageIndex++;
} }
return splitDocuments; return splitDocuments;

View File

@ -10,6 +10,12 @@ import stirling.software.common.model.api.PDFFile;
@Data @Data
@EqualsAndHashCode(callSuper = true) @EqualsAndHashCode(callSuper = true)
public class SplitPdfBySectionsRequest extends PDFFile { public class SplitPdfBySectionsRequest extends PDFFile {
@Schema(
description = "Pages to be split by section",
defaultValue = "all",
requiredMode = Schema.RequiredMode.REQUIRED)
private String pageNumbers;
@Schema( @Schema(
description = "Number of horizontal divisions for each PDF page", description = "Number of horizontal divisions for each PDF page",
defaultValue = "0", defaultValue = "0",