Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

199 lines
8.2 KiB
Java
Raw Normal View History

2023-12-11 23:20:31 +00:00
package stirling.software.SPDF.controller.api;
2023-12-30 19:11:27 +00:00
2023-12-16 19:30:47 +00:00
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
2023-12-29 22:53:46 +00:00
import java.util.ArrayList;
2023-12-16 19:30:47 +00:00
import java.util.HashMap;
2023-12-29 22:53:46 +00:00
import java.util.List;
2023-12-16 19:30:47 +00:00
import java.util.Map;
2023-12-30 19:11:27 +00:00
2023-12-11 23:20:31 +00:00
import org.apache.pdfbox.multipdf.Overlay;
import org.apache.pdfbox.pdmodel.PDDocument;
2023-12-16 19:30:47 +00:00
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ModelAttribute;
2023-12-11 23:20:31 +00:00
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
2023-12-16 19:30:47 +00:00
2023-12-11 23:20:31 +00:00
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
2023-12-30 19:11:27 +00:00
2023-12-11 23:20:31 +00:00
import stirling.software.SPDF.model.api.general.OverlayPdfsRequest;
import stirling.software.SPDF.utils.GeneralUtils;
import stirling.software.SPDF.utils.WebResponseUtils;
2023-12-30 19:11:27 +00:00
2023-12-11 23:20:31 +00:00
@RestController
@RequestMapping("/api/v1/general")
@Tag(name = "General", description = "General APIs")
public class PdfOverlayController {
2023-12-29 22:53:46 +00:00
@PostMapping(value = "/overlay-pdfs", consumes = "multipart/form-data")
2023-12-11 23:20:31 +00:00
@Operation(
summary = "Overlay PDF files in various modes",
description =
"Overlay PDF files onto a base PDF with different modes: Sequential, Interleaved, or Fixed Repeat. Input:PDF Output:PDF Type:MIMO")
public ResponseEntity<byte[]> overlayPdfs(@ModelAttribute OverlayPdfsRequest request)
throws IOException {
MultipartFile baseFile = request.getFileInput();
int overlayPos = request.getOverlayPosition();
2023-12-30 19:11:27 +00:00
2023-12-11 23:20:31 +00:00
MultipartFile[] overlayFiles = request.getOverlayFiles();
File[] overlayPdfFiles = new File[overlayFiles.length];
2023-12-29 22:53:46 +00:00
List<File> tempFiles = new ArrayList<>(); // List to keep track of temporary files
try {
for (int i = 0; i < overlayFiles.length; i++) {
overlayPdfFiles[i] = GeneralUtils.multipartToFile(overlayFiles[i]);
}
2023-12-30 19:11:27 +00:00
2023-12-29 22:53:46 +00:00
String mode = request.getOverlayMode(); // "SequentialOverlay", "InterleavedOverlay",
// "FixedRepeatOverlay"
int[] counts = request.getCounts(); // Used for FixedRepeatOverlay mode
try (PDDocument basePdf = PDDocument.load(baseFile.getInputStream());
Overlay overlay = new Overlay()) {
Map<Integer, String> overlayGuide =
prepareOverlayGuide(
basePdf.getNumberOfPages(),
overlayPdfFiles,
mode,
counts,
tempFiles);
2023-12-30 19:11:27 +00:00
2023-12-29 22:53:46 +00:00
overlay.setInputPDF(basePdf);
if (overlayPos == 0) {
overlay.setOverlayPosition(Overlay.Position.FOREGROUND);
} else {
overlay.setOverlayPosition(Overlay.Position.BACKGROUND);
}
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
overlay.overlay(overlayGuide).save(outputStream);
byte[] data = outputStream.toByteArray();
String outputFilename =
baseFile.getOriginalFilename().replaceFirst("[.][^.]+$", "")
+ "_overlayed.pdf"; // Remove file extension and append .pdf
2023-12-30 19:11:27 +00:00
2023-12-29 22:53:46 +00:00
return WebResponseUtils.bytesToWebResponse(
data, outputFilename, MediaType.APPLICATION_PDF);
}
2023-12-11 23:20:31 +00:00
} finally {
for (File overlayPdfFile : overlayPdfFiles) {
2023-12-29 22:53:46 +00:00
if (overlayPdfFile != null) {
overlayPdfFile.delete();
}
}
for (File tempFile : tempFiles) { // Delete temporary files
if (tempFile != null) {
tempFile.delete();
}
2023-12-11 23:20:31 +00:00
}
}
}
2023-12-29 22:53:46 +00:00
private Map<Integer, String> prepareOverlayGuide(
int basePageCount, File[] overlayFiles, String mode, int[] counts, List<File> tempFiles)
throws IOException {
2023-12-11 23:20:31 +00:00
Map<Integer, String> overlayGuide = new HashMap<>();
switch (mode) {
case "SequentialOverlay":
2023-12-29 22:53:46 +00:00
sequentialOverlay(overlayGuide, overlayFiles, basePageCount, tempFiles);
2023-12-11 23:20:31 +00:00
break;
case "InterleavedOverlay":
interleavedOverlay(overlayGuide, overlayFiles, basePageCount);
break;
case "FixedRepeatOverlay":
fixedRepeatOverlay(overlayGuide, overlayFiles, counts, basePageCount);
break;
default:
throw new IllegalArgumentException("Invalid overlay mode");
}
return overlayGuide;
}
2023-12-29 22:53:46 +00:00
private void sequentialOverlay(
Map<Integer, String> overlayGuide,
File[] overlayFiles,
int basePageCount,
List<File> tempFiles)
throws IOException {
int overlayFileIndex = 0;
int pageCountInCurrentOverlay = 0;
for (int basePageIndex = 1; basePageIndex <= basePageCount; basePageIndex++) {
if (pageCountInCurrentOverlay == 0
|| pageCountInCurrentOverlay
>= getNumberOfPages(overlayFiles[overlayFileIndex])) {
pageCountInCurrentOverlay = 0;
overlayFileIndex = (overlayFileIndex + 1) % overlayFiles.length;
}
2023-12-17 12:35:50 +00:00
2023-12-29 22:53:46 +00:00
try (PDDocument overlayPdf = PDDocument.load(overlayFiles[overlayFileIndex])) {
PDDocument singlePageDocument = new PDDocument();
singlePageDocument.addPage(overlayPdf.getPage(pageCountInCurrentOverlay));
File tempFile = File.createTempFile("overlay-page-", ".pdf");
singlePageDocument.save(tempFile);
singlePageDocument.close();
overlayGuide.put(basePageIndex, tempFile.getAbsolutePath());
tempFiles.add(tempFile); // Keep track of the temporary file for cleanup
2023-12-11 23:20:31 +00:00
}
2023-12-29 22:53:46 +00:00
pageCountInCurrentOverlay++;
}
}
private int getNumberOfPages(File file) throws IOException {
try (PDDocument doc = PDDocument.load(file)) {
return doc.getNumberOfPages();
2023-12-11 23:20:31 +00:00
}
}
private void interleavedOverlay(
Map<Integer, String> overlayGuide, File[] overlayFiles, int basePageCount)
throws IOException {
2023-12-29 22:53:46 +00:00
for (int basePageIndex = 1; basePageIndex <= basePageCount; basePageIndex++) {
File overlayFile = overlayFiles[(basePageIndex - 1) % overlayFiles.length];
// Load the overlay document to check its page count
try (PDDocument overlayPdf = PDDocument.load(overlayFile)) {
int overlayPageCount = overlayPdf.getNumberOfPages();
if ((basePageIndex - 1) % overlayPageCount < overlayPageCount) {
overlayGuide.put(basePageIndex, overlayFile.getAbsolutePath());
}
}
2023-12-11 23:20:31 +00:00
}
}
private void fixedRepeatOverlay(
Map<Integer, String> overlayGuide, File[] overlayFiles, int[] counts, int basePageCount)
throws IOException {
if (overlayFiles.length != counts.length) {
throw new IllegalArgumentException(
"Counts array length must match the number of overlay files");
}
int currentPage = 1;
for (int i = 0; i < overlayFiles.length; i++) {
2023-12-29 22:53:46 +00:00
File overlayFile = overlayFiles[i];
2023-12-11 23:20:31 +00:00
int repeatCount = counts[i];
2023-12-29 22:53:46 +00:00
// Load the overlay document to check its page count
try (PDDocument overlayPdf = PDDocument.load(overlayFile)) {
int overlayPageCount = overlayPdf.getNumberOfPages();
for (int j = 0; j < repeatCount; j++) {
for (int page = 0; page < overlayPageCount; page++) {
if (currentPage > basePageCount) break;
overlayGuide.put(currentPage++, overlayFile.getAbsolutePath());
}
}
2023-12-11 23:20:31 +00:00
}
}
}
}
// Additional classes like OverlayPdfsRequest, WebResponseUtils, etc. are assumed to be defined
// elsewhere.