2023-04-28 23:18:10 +01:00
package stirling.software.SPDF.controller.api ;
import java.io.IOException ;
2023-05-26 23:53:11 +01:00
import io.swagger.v3.oas.annotations.media.Schema ;
2023-05-31 20:15:48 +01:00
import stirling.software.SPDF.utils.WebResponseUtils ;
2023-04-28 23:18:10 +01:00
import java.util.ArrayList ;
import java.util.List ;
2023-06-03 17:21:59 +01:00
import javax.script.ScriptEngineManager ;
import javax.script.ScriptEngine ;
import javax.script.ScriptException ;
import javax.script.ScriptEngine ;
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 ;
import org.springframework.web.bind.annotation.PostMapping ;
import org.springframework.web.bind.annotation.RequestParam ;
import org.springframework.web.bind.annotation.RequestPart ;
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 ;
import io.swagger.v3.oas.annotations.Parameter ;
2023-04-28 23:18:10 +01:00
@RestController
public class RearrangePagesPDFController {
2023-06-03 17:21:59 +01:00
private static final Logger logger = LoggerFactory . getLogger ( RearrangePagesPDFController . class ) ;
@PostMapping ( consumes = " multipart/form-data " , value = " /remove-pages " )
@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. " )
public ResponseEntity < byte [ ] > deletePages (
@RequestPart ( required = true , value = " fileInput " ) @Parameter ( description = " The input PDF file from which pages will be removed " ) MultipartFile pdfFile ,
@RequestParam ( " pagesToDelete " ) @Parameter ( description = " Comma-separated list of pages or page ranges to delete, e.g., '1,3,5-8' " ) String pagesToDelete )
throws IOException {
PDDocument document = PDDocument . load ( pdfFile . getBytes ( ) ) ;
// Split the page order string into an array of page numbers or range of numbers
String [ ] pageOrderArr = pagesToDelete . split ( " , " ) ;
List < Integer > pagesToRemove = pageOrderToString ( pageOrderArr , document . getNumberOfPages ( ) ) ;
for ( int i = pagesToRemove . size ( ) - 1 ; i > = 0 ; i - - ) {
int pageIndex = pagesToRemove . get ( i ) ;
document . removePage ( pageIndex ) ;
}
return WebResponseUtils . pdfDocToWebResponse ( document ,
pdfFile . getOriginalFilename ( ) . replaceFirst ( " [.][^.]+$ " , " " ) + " _removed_pages.pdf " ) ;
}
private enum CustomMode {
REVERSE_ORDER , DUPLEX_SORT , BOOKLET_SORT , ODD_EVEN_SPLIT , REMOVE_FIRST , REMOVE_LAST , REMOVE_FIRST_AND_LAST ,
}
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 ;
}
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 ;
}
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 ;
}
private List < Integer > reverseOrder ( int totalPages ) {
List < Integer > newPageOrder = new ArrayList < > ( ) ;
for ( int i = totalPages ; i > = 1 ; i - - ) {
newPageOrder . add ( i - 1 ) ;
}
return newPageOrder ;
}
private List < Integer > duplexSort ( int totalPages ) {
List < Integer > newPageOrder = new ArrayList < > ( ) ;
int half = ( totalPages + 1 ) / 2 ; // This ensures proper behavior with odd numbers of pages
for ( int i = 1 ; i < = half ; i + + ) {
newPageOrder . add ( i - 1 ) ;
if ( i < = totalPages - half ) { // Avoid going out of bounds
newPageOrder . add ( totalPages - i ) ;
}
}
return newPageOrder ;
}
private List < Integer > bookletSort ( int totalPages ) {
List < Integer > newPageOrder = new ArrayList < > ( ) ;
for ( int i = 0 ; i < totalPages / 2 ; i + + ) {
newPageOrder . add ( i ) ;
newPageOrder . add ( totalPages - i - 1 ) ;
}
return newPageOrder ;
}
private List < Integer > oddEvenSplit ( int totalPages ) {
List < Integer > newPageOrder = new ArrayList < > ( ) ;
for ( int i = 1 ; i < = totalPages ; i + = 2 ) {
newPageOrder . add ( i - 1 ) ;
}
for ( int i = 2 ; i < = totalPages ; i + = 2 ) {
newPageOrder . add ( i - 1 ) ;
}
return newPageOrder ;
}
private List < Integer > processCustomMode ( String customMode , int totalPages ) {
try {
CustomMode mode = CustomMode . valueOf ( customMode . toUpperCase ( ) ) ;
switch ( mode ) {
case REVERSE_ORDER :
return reverseOrder ( totalPages ) ;
case DUPLEX_SORT :
return duplexSort ( totalPages ) ;
case BOOKLET_SORT :
return bookletSort ( totalPages ) ;
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 ) ;
default :
throw new IllegalArgumentException ( " Unsupported custom mode " ) ;
}
} catch ( IllegalArgumentException e ) {
logger . error ( " Unsupported custom mode " , e ) ;
return null ;
}
}
@PostMapping ( consumes = " multipart/form-data " , value = " /rearrange-pages " )
@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. " )
public ResponseEntity < byte [ ] > rearrangePages (
@RequestPart ( required = true , value = " fileInput " ) @Parameter ( description = " The input PDF file to rearrange pages " ) MultipartFile pdfFile ,
@RequestParam ( required = false , value = " pageOrder " ) @Parameter ( description = " The new page order as a comma-separated list of page numbers, page ranges (e.g., '1,3,5-7'), or functions in the format 'an+b' where 'a' is the multiplier of the page number 'n', and 'b' is a constant (e.g., '2n+1', '3n', '6n-5') " ) String pageOrder ,
@RequestParam ( required = false , value = " customMode " ) @Parameter ( schema = @Schema ( implementation = CustomMode . class , description = " The custom mode for page rearrangement. "
+ " Valid values are: \ n " + " REVERSE_ORDER: Reverses the order of all pages. \ n "
+ " DUPLEX_SORT: Sorts pages as if all fronts were scanned then all backs in reverse (1, n, 2, n-1, ...). "
+ " BOOKLET_SORT: Arranges pages for booklet printing (last, first, second, second last, ...). \ n "
+ " ODD_EVEN_SPLIT: Splits and arranges pages into odd and even numbered pages. \ n "
+ " REMOVE_FIRST: Removes the first page. \ n " + " REMOVE_LAST: Removes the last page. \ n "
+ " REMOVE_FIRST_AND_LAST: Removes both the first and the last pages. \ n " ) ) String customMode ) {
try {
// Load the input PDF
PDDocument document = PDDocument . load ( pdfFile . getInputStream ( ) ) ;
// 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 ( ) ;
System . out . println ( " pageOrder= " + pageOrder ) ;
System . out . println ( " customMode length = " + customMode . length ( ) ) ;
List < Integer > newPageOrder ;
if ( customMode ! = null & & customMode . length ( ) > 0 ) {
newPageOrder = processCustomMode ( customMode , totalPages ) ;
} else {
newPageOrder = pageOrderToString ( pageOrderArr , totalPages ) ;
}
// 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 ) ) ) ;
}
// Remove all the pages from the original document
for ( int i = document . getNumberOfPages ( ) - 1 ; i > = 0 ; i - - ) {
document . removePage ( i ) ;
}
// Add the pages in the new order
for ( PDPage page : newPages ) {
document . addPage ( page ) ;
}
return WebResponseUtils . pdfDocToWebResponse ( document ,
pdfFile . getOriginalFilename ( ) . replaceFirst ( " [.][^.]+$ " , " " ) + " _rearranged.pdf " ) ;
} catch ( IOException e ) {
logger . error ( " Failed rearranging documents " , e ) ;
return null ;
}
}
private List < Integer > pageOrderToString ( String [ ] pageOrderArr , int totalPages ) {
List < Integer > newPageOrder = new ArrayList < > ( ) ;
// loop through the page order array
for ( String element : pageOrderArr ) {
// check if the element contains a range of pages
if ( element . matches ( " \\ d*n \\ +?-? \\ d*| \\ d* \\ +?n " ) ) {
// Handle page order as a function
int coefficient = 0 ;
int constant = 0 ;
boolean coefficientExists = false ;
boolean constantExists = false ;
if ( element . contains ( " n " ) ) {
String [ ] parts = element . split ( " n " ) ;
if ( ! parts [ 0 ] . equals ( " " ) & & parts [ 0 ] ! = null ) {
coefficient = Integer . parseInt ( parts [ 0 ] ) ;
coefficientExists = true ;
}
if ( parts . length > 1 & & ! parts [ 1 ] . equals ( " " ) & & parts [ 1 ] ! = null ) {
constant = Integer . parseInt ( parts [ 1 ] ) ;
constantExists = true ;
}
} else if ( element . contains ( " + " ) ) {
constant = Integer . parseInt ( element . replace ( " + " , " " ) ) ;
constantExists = true ;
}
for ( int i = 1 ; i < = totalPages ; i + + ) {
int pageNum = coefficientExists ? coefficient * i : i ;
pageNum + = constantExists ? constant : 0 ;
if ( pageNum < = totalPages & & pageNum > 0 ) {
newPageOrder . add ( pageNum - 1 ) ;
}
}
} else if ( element . contains ( " - " ) ) {
// split the range into start and end page
String [ ] range = element . split ( " - " ) ;
int start = Integer . parseInt ( range [ 0 ] ) ;
int end = Integer . parseInt ( range [ 1 ] ) ;
// check if the end page is greater than total pages
if ( end > totalPages ) {
end = totalPages ;
}
// loop through the range of pages
for ( int j = start ; j < = end ; j + + ) {
// print the current index
newPageOrder . add ( j - 1 ) ;
}
} else {
// if the element is a single page
newPageOrder . add ( Integer . parseInt ( element ) - 1 ) ;
}
}
return newPageOrder ;
}
2023-05-26 23:53:11 +01:00
2023-04-28 23:18:10 +01:00
}