2023-12-11 23:20:31 +00:00
package stirling.software.SPDF.controller.api ;
2023-12-16 19:30:47 +00:00
import java.io.ByteArrayOutputStream ;
import java.io.File ;
import java.io.IOException ;
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-29 22:53:46 +00:00
import java.util.ArrayList ;
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 ;
import stirling.software.SPDF.model.api.general.OverlayPdfsRequest ;
import stirling.software.SPDF.utils.GeneralUtils ;
import stirling.software.SPDF.utils.WebResponseUtils ;
@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 ( ) ;
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 ] ) ;
}
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 ) ;
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
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
}
}
2023-12-17 12:35:50 +00:00
2023-12-29 22:53:46 +00:00
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
}
}
2023-12-29 22:53:46 +00:00
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
}
}
}
2023-12-29 22:53:46 +00:00
2023-12-11 23:20:31 +00:00
}
// Additional classes like OverlayPdfsRequest, WebResponseUtils, etc. are assumed to be defined elsewhere.