2023-04-28 23:18:10 +01:00
package stirling.software.SPDF.controller.api ;
2024-02-01 23:48:27 +00:00
import io.github.pixee.security.Filenames ;
2023-04-28 23:18:10 +01:00
import java.io.IOException ;
import java.util.ArrayList ;
import java.util.List ;
2024-01-12 23:15:27 +00:00
import org.apache.pdfbox.Loader ;
2023-04-28 23:18:10 +01:00
import org.apache.pdfbox.pdmodel.PDDocument ;
import org.apache.pdfbox.pdmodel.PDPage ;
import org.slf4j.Logger ;
import org.slf4j.LoggerFactory ;
import org.springframework.http.ResponseEntity ;
2023-09-09 00:25:27 +01:00
import org.springframework.web.bind.annotation.ModelAttribute ;
2023-04-28 23:18:10 +01:00
import org.springframework.web.bind.annotation.PostMapping ;
2023-09-11 23:19:50 +01:00
import org.springframework.web.bind.annotation.RequestMapping ;
2023-04-28 23:18:10 +01:00
import org.springframework.web.bind.annotation.RestController ;
import org.springframework.web.multipart.MultipartFile ;
2023-05-08 15:20:04 +01:00
import io.swagger.v3.oas.annotations.Operation ;
2023-06-25 09:16:32 +01:00
import io.swagger.v3.oas.annotations.tags.Tag ;
2023-12-30 19:11:27 +00:00
2023-09-09 00:25:27 +01:00
import stirling.software.SPDF.model.SortTypes ;
2023-10-05 21:20:05 +01:00
import stirling.software.SPDF.model.api.PDFWithPageNums ;
2023-09-09 00:25:27 +01:00
import stirling.software.SPDF.model.api.general.RearrangePagesRequest ;
2023-09-10 09:24:20 +01:00
import stirling.software.SPDF.utils.GeneralUtils ;
import stirling.software.SPDF.utils.WebResponseUtils ;
2023-12-30 19:11:27 +00:00
2023-04-28 23:18:10 +01:00
@RestController
2023-09-11 23:19:50 +01:00
@RequestMapping ( " /api/v1/general " )
2023-06-25 09:16:32 +01:00
@Tag ( name = " General " , description = " General APIs " )
2023-04-28 23:18:10 +01:00
public class RearrangePagesPDFController {
2023-06-03 17:21:59 +01:00
private static final Logger logger = LoggerFactory . getLogger ( RearrangePagesPDFController . class ) ;
2023-12-30 19:11:27 +00:00
2023-06-03 17:21:59 +01:00
@PostMapping ( consumes = " multipart/form-data " , value = " /remove-pages " )
2023-06-23 23:29:53 +01:00
@Operation (
summary = " Remove pages from a PDF file " ,
description =
" This endpoint removes specified pages from a given PDF file. Users can provide a comma-separated list of page numbers or ranges to delete. Input:PDF Output:PDF Type:SISO " )
2023-10-05 21:20:05 +01:00
public ResponseEntity < byte [ ] > deletePages ( @ModelAttribute PDFWithPageNums request )
2023-06-03 17:21:59 +01:00
throws IOException {
2023-12-30 19:11:27 +00:00
2023-10-05 21:20:05 +01:00
MultipartFile pdfFile = request . getFileInput ( ) ;
String pagesToDelete = request . getPageNumbers ( ) ;
2023-12-30 19:11:27 +00:00
2024-01-12 23:15:27 +00:00
PDDocument document = Loader . loadPDF ( pdfFile . getBytes ( ) ) ;
2023-12-30 19:11:27 +00:00
2023-06-03 22:56:15 +01:00
// Split the page order string into an array of page numbers or range of numbers
2023-06-03 17:21:59 +01:00
String [ ] pageOrderArr = pagesToDelete . split ( " , " ) ;
2023-12-30 19:11:27 +00:00
2023-06-03 17:21:59 +01:00
List < Integer > pagesToRemove =
GeneralUtils . parsePageList ( pageOrderArr , document . getNumberOfPages ( ) ) ;
2023-12-30 19:11:27 +00:00
2023-06-03 17:21:59 +01:00
for ( int i = pagesToRemove . size ( ) - 1 ; i > = 0 ; i - - ) {
int pageIndex = pagesToRemove . get ( i ) ;
document . removePage ( pageIndex ) ;
2023-12-30 19:11:27 +00:00
}
2023-06-03 17:21:59 +01:00
return WebResponseUtils . pdfDocToWebResponse (
2023-12-30 19:11:27 +00:00
document ,
2024-02-01 23:48:27 +00:00
Filenames . toSimpleFileName ( pdfFile . getOriginalFilename ( ) ) . replaceFirst ( " [.][^.]+$ " , " " ) + " _removed_pages.pdf " ) ;
2023-06-03 17:21:59 +01:00
}
2023-12-30 19:11:27 +00:00
2023-06-03 17:21:59 +01:00
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 ;
}
2023-12-30 19:11:27 +00:00
2023-06-03 17:21:59 +01:00
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 ;
}
2023-12-30 19:11:27 +00:00
2023-06-03 17:21:59 +01:00
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 ;
}
2023-12-30 19:11:27 +00:00
2023-09-05 20:05:33 +02:00
private List < Integer > reverseOrder ( int totalPages ) {
List < Integer > newPageOrder = new ArrayList < > ( ) ;
for ( int i = totalPages ; i > = 1 ; i - - ) {
newPageOrder . add ( i - 1 ) ;
}
return newPageOrder ;
2023-09-05 19:48:16 +02:00
}
2023-06-03 17:21:59 +01:00
private List < Integer > duplexSort ( int totalPages ) {
List < Integer > newPageOrder = new ArrayList < > ( ) ;
2023-09-09 00:25:27 +01:00
int half = ( totalPages + 1 ) / 2 ; // This ensures proper behavior with odd numbers of pages
2023-06-03 17:21:59 +01:00
for ( int i = 1 ; i < = half ; i + + ) {
2023-06-21 21:19:52 +01:00
newPageOrder . add ( i - 1 ) ;
if ( i < = totalPages - half ) { // Avoid going out of bounds
2023-09-09 00:25:27 +01:00
newPageOrder . add ( totalPages - i ) ;
2023-06-03 17:21:59 +01:00
}
2023-12-30 19:11:27 +00:00
}
2023-06-03 17:21:59 +01:00
return newPageOrder ;
}
2023-05-26 23:53:11 +01:00
2023-06-03 17:21:59 +01:00
private List < Integer > bookletSort ( int totalPages ) {
List < Integer > newPageOrder = new ArrayList < > ( ) ;
for ( int i = 0 ; i < totalPages / 2 ; i + + ) {
newPageOrder . add ( i ) ;
2023-09-05 20:05:33 +02:00
newPageOrder . add ( totalPages - i - 1 ) ;
2023-12-30 19:11:27 +00:00
}
2023-06-03 17:21:59 +01:00
return newPageOrder ;
2023-12-30 19:11:27 +00:00
}
2023-06-03 17:21:59 +01:00
private List < Integer > sideStitchBooklet ( int totalPages ) {
List < Integer > newPageOrder = new ArrayList < > ( ) ;
for ( int i = 0 ; i < ( totalPages + 3 ) / 4 ; i + + ) {
2023-09-05 20:05:33 +02:00
int begin = i * 4 ;
newPageOrder . add ( Math . min ( begin + 3 , totalPages - 1 ) ) ;
newPageOrder . add ( Math . min ( begin , totalPages - 1 ) ) ;
newPageOrder . add ( Math . min ( begin + 1 , totalPages - 1 ) ) ;
newPageOrder . add ( Math . min ( begin + 2 , totalPages - 1 ) ) ;
2023-12-30 19:11:27 +00:00
}
2023-06-03 17:21:59 +01:00
return newPageOrder ;
2023-12-30 19:11:27 +00:00
}
2023-06-03 17:21:59 +01:00
private List < Integer > oddEvenSplit ( int totalPages ) {
List < Integer > newPageOrder = new ArrayList < > ( ) ;
for ( int i = 1 ; i < = totalPages ; i + = 2 ) {
newPageOrder . add ( i - 1 ) ;
2023-12-30 19:11:27 +00:00
}
2023-06-03 17:21:59 +01:00
for ( int i = 2 ; i < = totalPages ; i + = 2 ) {
newPageOrder . add ( i - 1 ) ;
2023-12-30 19:11:27 +00:00
}
2023-06-03 17:21:59 +01:00
return newPageOrder ;
2023-12-30 19:11:27 +00:00
}
2023-09-09 00:25:27 +01:00
private List < Integer > processSortTypes ( String sortTypes , int totalPages ) {
2023-12-30 19:11:27 +00:00
try {
2023-09-09 00:25:27 +01:00
SortTypes mode = SortTypes . valueOf ( sortTypes . toUpperCase ( ) ) ;
2023-06-03 17:21:59 +01:00
switch ( mode ) {
case REVERSE_ORDER :
return reverseOrder ( totalPages ) ;
case DUPLEX_SORT :
return duplexSort ( totalPages ) ;
case BOOKLET_SORT :
return bookletSort ( totalPages ) ;
2023-09-05 20:05:33 +02:00
case SIDE_STITCH_BOOKLET_SORT :
return sideStitchBooklet ( totalPages ) ;
2023-06-03 17:21:59 +01:00
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 ) ;
2023-12-30 19:11:27 +00:00
default :
2023-06-03 17:21:59 +01:00
throw new IllegalArgumentException ( " Unsupported custom mode " ) ;
2023-12-30 19:11:27 +00:00
}
2023-06-03 17:21:59 +01:00
} catch ( IllegalArgumentException e ) {
logger . error ( " Unsupported custom mode " , e ) ;
return null ;
2023-12-30 19:11:27 +00:00
}
}
2023-06-03 17:21:59 +01:00
@PostMapping ( consumes = " multipart/form-data " , value = " /rearrange-pages " )
2023-06-21 21:19:52 +01:00
@Operation (
summary = " Rearrange pages in a PDF file " ,
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. Input:PDF Output:PDF " )
2023-09-09 00:25:27 +01:00
public ResponseEntity < byte [ ] > rearrangePages ( @ModelAttribute RearrangePagesRequest request )
throws IOException {
MultipartFile pdfFile = request . getFileInput ( ) ;
String pageOrder = request . getPageNumbers ( ) ;
String sortType = request . getCustomMode ( ) ;
2023-12-30 19:11:27 +00:00
try {
2023-06-03 17:21:59 +01:00
// Load the input PDF
2024-01-12 23:15:27 +00:00
PDDocument document = Loader . loadPDF ( pdfFile . getBytes ( ) ) ;
2023-12-30 19:11:27 +00:00
2023-06-03 17:21:59 +01:00
// Split the page order string into an array of page numbers or range of numbers
String [ ] pageOrderArr = pageOrder ! = null ? pageOrder . split ( " , " ) : new String [ 0 ] ;
int totalPages = document . getNumberOfPages ( ) ;
List < Integer > newPageOrder ;
2023-09-09 00:25:27 +01:00
if ( sortType ! = null & & sortType . length ( ) > 0 ) {
newPageOrder = processSortTypes ( sortType , totalPages ) ;
2023-12-30 19:11:27 +00:00
} else {
2023-06-03 22:56:15 +01:00
newPageOrder = GeneralUtils . parsePageList ( pageOrderArr , totalPages ) ;
2023-12-30 19:11:27 +00:00
}
2023-09-13 00:46:30 +01:00
logger . info ( " newPageOrder = " + newPageOrder ) ;
logger . info ( " totalPages = " + totalPages ) ;
2023-06-03 17:21:59 +01:00
// Create a new list to hold the pages in the new order
List < PDPage > newPages = new ArrayList < > ( ) ;
for ( int i = 0 ; i < newPageOrder . size ( ) ; i + + ) {
newPages . add ( document . getPage ( newPageOrder . get ( i ) ) ) ;
2023-12-30 19:11:27 +00:00
}
2023-06-03 17:21:59 +01:00
// Remove all the pages from the original document
for ( int i = document . getNumberOfPages ( ) - 1 ; i > = 0 ; i - - ) {
document . removePage ( i ) ;
2023-12-30 19:11:27 +00:00
}
2023-06-03 17:21:59 +01:00
// Add the pages in the new order
for ( PDPage page : newPages ) {
document . addPage ( page ) ;
2023-12-30 19:11:27 +00:00
}
2023-06-03 17:21:59 +01:00
return WebResponseUtils . pdfDocToWebResponse (
2023-12-30 19:11:27 +00:00
document ,
2024-02-01 23:48:27 +00:00
Filenames . toSimpleFileName ( pdfFile . getOriginalFilename ( ) ) . replaceFirst ( " [.][^.]+$ " , " " )
2023-06-03 17:21:59 +01:00
+ " _rearranged.pdf " ) ;
} catch ( IOException e ) {
logger . error ( " Failed rearranging documents " , e ) ;
return null ;
2023-12-30 19:11:27 +00:00
}
}
2023-04-28 23:18:10 +01:00
}