rework for API, folder changes, easter eggs and fun

This commit is contained in:
Anthony Stirling 2023-04-28 23:18:10 +01:00
parent 63a698b679
commit 91171727e4
35 changed files with 1345 additions and 1157 deletions

View file

@ -1,9 +1,9 @@
# Build jbig2enc in a separate stage # Build jbig2enc in a separate stage
FROM frooodle/stirling-pdf-base:beta FROM frooodle/stirling-pdf-base:beta2
# Create pythonScripts folder and copy local scripts # Create scripts folder and copy local scripts
RUN mkdir /pythonScripts RUN mkdir /scripts
COPY ./pythonScripts/* /pythonScripts/ COPY ./scripts/* /scripts/
# Copy the application JAR file # Copy the application JAR file
COPY build/libs/*.jar app.jar COPY build/libs/*.jar app.jar
@ -17,7 +17,8 @@ ENV APP_HOME_NAME="Stirling PDF"
#ENV APP_NAVBAR_NAME="Stirling PDF" #ENV APP_NAVBAR_NAME="Stirling PDF"
# Run the application # Run the application
ENTRYPOINT java -jar /app.jar ENTRYPOINT ["/scripts/init.sh"]
CMD ["java", "-jar", "/app.jar"]

View file

@ -11,8 +11,8 @@ RUN apt-get update && \
pkg-config \ pkg-config \
ca-certificates \ ca-certificates \
zlib1g-dev \ zlib1g-dev \
make \ make \
g++ g++
RUN git clone https://github.com/agl/jbig2enc && \ RUN git clone https://github.com/agl/jbig2enc && \
cd jbig2enc && \ cd jbig2enc && \
@ -21,10 +21,9 @@ RUN git clone https://github.com/agl/jbig2enc && \
make && \ make && \
make install make install
# Main stage
FROM openjdk:17-jdk-slim
# Install necessary dependencies # Main stage
FROM openjdk:17-jdk-slim AS base
RUN apt-get update && \ RUN apt-get update && \
apt-get install -y --no-install-recommends \ apt-get install -y --no-install-recommends \
libreoffice-core \ libreoffice-core \
@ -38,52 +37,27 @@ RUN apt-get update && \
pngquant \ pngquant \
unpaper \ unpaper \
ocrmypdf && \ ocrmypdf && \
pip install --user --upgrade ocrmypdf && \ rm -rf /var/lib/apt/lists/* && \
pip3 install opencv-python-headless mkdir /usr/share/tesseract-ocr-original && \
cp -r /usr/share/tesseract-ocr/* /usr/share/tesseract-ocr-original && \
# Copy the jbig2enc binary from the builder stage mv /usr/share/tesseract-ocr-original/4.00/tessdata/eng.traineddata /usr/share/tesseract-ocr-original/4.00/tessdata/english.traineddata && \
COPY --from=jbig2enc_builder /usr/local/bin/jbig2 /usr/local/bin/jbig2# Build jbig2enc in a separate stage rm -rf /usr/share/tesseract-ocr
FROM debian:bullseye-slim as jbig2enc_builder
# Python packages stage
FROM base AS python-packages
RUN apt-get update && \ RUN apt-get update && \
apt-get install -y --no-install-recommends \ apt-get install -y --no-install-recommends \
git \ build-essential \
automake \ libffi-dev \
autoconf \ libssl-dev \
libtool \
libleptonica-dev \
pkg-config \
ca-certificates \
zlib1g-dev \ zlib1g-dev \
make \ libjpeg-dev && \
g++ pip install --upgrade pip && \
pip install --no-cache-dir \
opencv-python-headless && \
rm -rf /var/lib/apt/lists/*
RUN git clone https://github.com/agl/jbig2enc && \ # Final stage: Copy necessary files from the previous stage
cd jbig2enc && \ FROM base
./autogen.sh && \ COPY --from=python-packages /usr/local /usr/local
./configure && \
make && \
make install
# Main stage
FROM openjdk:17-jdk-slim
# Install necessary dependencies
RUN apt-get update && \
apt-get install -y --no-install-recommends \
libreoffice-core \
libreoffice-common \
libreoffice-writer \
libreoffice-calc \
libreoffice-impress \
python3-uno \
python3-pip \
unoconv \
pngquant \
unpaper \
ocrmypdf && \
pip install --user --upgrade ocrmypdf && \
pip3 install opencv-python-headless
# Copy the jbig2enc binary from the builder stage
COPY --from=jbig2enc_builder /usr/local/bin/jbig2 /usr/local/bin/jbig2 COPY --from=jbig2enc_builder /usr/local/bin/jbig2 /usr/local/bin/jbig2

View file

@ -19,7 +19,9 @@ dependencies {
// https://mvnrepository.com/artifact/org.apache.pdfbox/jbig2-imageio // https://mvnrepository.com/artifact/org.apache.pdfbox/jbig2-imageio
implementation group: 'org.apache.pdfbox', name: 'jbig2-imageio', version: '3.0.4' implementation group: 'org.apache.pdfbox', name: 'jbig2-imageio', version: '3.0.4'
implementation 'commons-io:commons-io:2.11.0' implementation 'commons-io:commons-io:2.11.0'
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.1.0'
//general PDF //general PDF
implementation 'org.apache.pdfbox:pdfbox:2.0.28' implementation 'org.apache.pdfbox:pdfbox:2.0.28'

9
scripts/init.sh Normal file
View file

@ -0,0 +1,9 @@
#!/bin/bash
# Copy the original tesseract-ocr files to the volume directory without overwriting existing files
echo "Copying original files without overwriting existing files"
mkdir -p /usr/share/tesseract-ocr
cp -rn /usr/share/tesseract-ocr-original/* /usr/share/tesseract-ocr
# Run the main command
exec "$@"

View file

@ -85,7 +85,7 @@ def crop_borders(image, border_color, tolerance=30):
return image[y:y+h, x:x+w] return image[y:y+h, x:x+w]
def split_photos(input_file, output_directory, tolerance=30, min_area=10000, min_contour_area=500, angle_threshold=10, border_size=10): def split_photos(input_file, output_directory, tolerance=30, min_area=10000, min_contour_area=500, angle_threshold=10, border_size=0):
image = cv2.imread(input_file) image = cv2.imread(input_file)
background_color = estimate_background_color(image) background_color = estimate_background_color(image)
@ -121,7 +121,7 @@ if __name__ == "__main__":
print(" [min_area] - Optional. Sets the minimum area threshold for a photo (default: 10000).") print(" [min_area] - Optional. Sets the minimum area threshold for a photo (default: 10000).")
print(" [min_contour_area] - Optional. Sets the minimum contour area threshold for a photo (default: 500).") print(" [min_contour_area] - Optional. Sets the minimum contour area threshold for a photo (default: 500).")
print(" [angle_threshold] - Optional. Sets the minimum absolute angle required for the image to be rotated (default: 10).") print(" [angle_threshold] - Optional. Sets the minimum absolute angle required for the image to be rotated (default: 10).")
print(" [border_size] - Optional. Sets the size of the border added and removed to prevent white borders in the output (default: 10).") print(" [border_size] - Optional. Sets the size of the border added and removed to prevent white borders in the output (default: 0).")
sys.exit(1) sys.exit(1)
input_file = sys.argv[1] input_file = sys.argv[1]
@ -130,4 +130,5 @@ if __name__ == "__main__":
min_area = int(sys.argv[4]) if len(sys.argv) > 4 else 8000 min_area = int(sys.argv[4]) if len(sys.argv) > 4 else 8000
min_contour_area = int(sys.argv[5]) if len(sys.argv) > 5 else 500 min_contour_area = int(sys.argv[5]) if len(sys.argv) > 5 else 500
angle_threshold = int(sys.argv[6]) if len(sys.argv) > 6 else 60 angle_threshold = int(sys.argv[6]) if len(sys.argv) > 6 else 60
split_photos(input_file, output_directory, tolerance=tolerance, min_area=min_area, min_contour_area=min_contour_area, angle_threshold=angle_threshold) border_size = int(sys.argv[7]) if len(sys.argv) > 7 else 0
split_photos(input_file, output_directory, tolerance=tolerance, min_area=min_area, min_contour_area=min_contour_area, angle_threshold=angle_threshold, border_size=border_size)

View file

@ -0,0 +1,20 @@
package stirling.software.SPDF.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import io.swagger.v3.oas.models.Components;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.info.Info;
import io.swagger.v3.oas.models.info.License;
@Configuration
public class OpenApiConfig {
@Bean
public OpenAPI customOpenAPI() {
return new OpenAPI().components(new Components()).info(
new Info().title("Your API Title").version("1.0.0").description("Your API Description").license(new License().name("Your License Name").url("Your License URL")));
}
}

View file

@ -1,20 +0,0 @@
package stirling.software.SPDF.controller;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class MultiToolController {
private static final Logger logger = LoggerFactory.getLogger(MultiToolController.class);
@GetMapping("/multi-tool")
public String multiToolForm(Model model) {
model.addAttribute("currentPage", "multi-tool");
return "multi-tool";
}
}

View file

@ -1,25 +0,0 @@
package stirling.software.SPDF.controller;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class PdfController {
private static final Logger logger = LoggerFactory.getLogger(PdfController.class);
@GetMapping("/")
public String home(Model model) {
model.addAttribute("currentPage", "home");
return "home";
}
@GetMapping("/home")
public String root(Model model) {
return "redirect:/";
}
}

View file

@ -1,67 +1,62 @@
package stirling.software.SPDF.controller; package stirling.software.SPDF.controller.api;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage; import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDPageTree; import org.apache.pdfbox.pdmodel.PDPageTree;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller; import org.springframework.ui.Model;
import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestPart;
import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
import stirling.software.SPDF.utils.PdfUtils; import io.swagger.v3.oas.annotations.Hidden;
import stirling.software.SPDF.utils.PdfUtils;
@Controller
public class MergeController { @RestController
public class MergeController {
private static final Logger logger = LoggerFactory.getLogger(MergeController.class);
private static final Logger logger = LoggerFactory.getLogger(MergeController.class);
@GetMapping("/merge-pdfs")
public String hello(Model model) { private PDDocument mergeDocuments(List<PDDocument> documents) throws IOException {
model.addAttribute("currentPage", "merge-pdfs"); // Create a new empty document
return "merge-pdfs"; PDDocument mergedDoc = new PDDocument();
}
// Iterate over the list of documents and add their pages to the merged document
private PDDocument mergeDocuments(List<PDDocument> documents) throws IOException { for (PDDocument doc : documents) {
// Create a new empty document // Get all pages from the current document
PDDocument mergedDoc = new PDDocument(); PDPageTree pages = doc.getPages();
// Iterate over the pages and add them to the merged document
// Iterate over the list of documents and add their pages to the merged document for (PDPage page : pages) {
for (PDDocument doc : documents) { mergedDoc.addPage(page);
// Get all pages from the current document }
PDPageTree pages = doc.getPages(); }
// Iterate over the pages and add them to the merged document
for (PDPage page : pages) { // Return the merged document
mergedDoc.addPage(page); return mergedDoc;
} }
}
@PostMapping(consumes = "multipart/form-data", value = "/merge-pdfs")
// Return the merged document public ResponseEntity<byte[]> mergePdfs(@RequestPart(required = true, value = "fileInput") MultipartFile[] files) throws IOException {
return mergedDoc; // Read the input PDF files into PDDocument objects
} List<PDDocument> documents = new ArrayList<>();
@PostMapping("/merge-pdfs") // Loop through the files array and read each file into a PDDocument
public ResponseEntity<byte[]> mergePdfs(@RequestParam("fileInput") MultipartFile[] files) throws IOException { for (MultipartFile file : files) {
// Read the input PDF files into PDDocument objects documents.add(PDDocument.load(file.getInputStream()));
List<PDDocument> documents = new ArrayList<>(); }
// Loop through the files array and read each file into a PDDocument PDDocument mergedDoc = mergeDocuments(documents);
for (MultipartFile file : files) {
documents.add(PDDocument.load(file.getInputStream())); // Return the merged PDF as a response
} return PdfUtils.pdfDocToWebResponse(mergedDoc, files[0].getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_merged.pdf");
}
PDDocument mergedDoc = mergeDocuments(documents);
// Return the merged PDF as a response
return PdfUtils.pdfDocToWebResponse(mergedDoc, files[0].getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_merged.pdf");
}
} }

View file

@ -1,121 +1,112 @@
package stirling.software.SPDF.controller; package stirling.software.SPDF.controller.api;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage; import org.apache.pdfbox.pdmodel.PDPage;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller; import org.springframework.ui.Model;
import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RequestPart;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import stirling.software.SPDF.utils.PdfUtils;
import io.swagger.v3.oas.annotations.Hidden;
@Controller import stirling.software.SPDF.utils.PdfUtils;
public class RearrangePagesPDFController {
@RestController
private static final Logger logger = LoggerFactory.getLogger(RearrangePagesPDFController.class); public class RearrangePagesPDFController {
@PostMapping("/remove-pages") private static final Logger logger = LoggerFactory.getLogger(RearrangePagesPDFController.class);
public ResponseEntity<byte[]> deletePages(@RequestParam("fileInput") MultipartFile pdfFile, @RequestParam("pagesToDelete") String pagesToDelete) throws IOException {
@PostMapping(consumes = "multipart/form-data", value = "/remove-pages")
PDDocument document = PDDocument.load(pdfFile.getBytes()); public ResponseEntity<byte[]> deletePages(@RequestPart(required = true, value = "fileInput") MultipartFile pdfFile, @RequestParam("pagesToDelete") String pagesToDelete)
throws IOException {
// Split the page order string into an array of page numbers or range of numbers
String[] pageOrderArr = pagesToDelete.split(","); PDDocument document = PDDocument.load(pdfFile.getBytes());
List<Integer> pagesToRemove = pageOrderToString(pageOrderArr, document.getNumberOfPages()); // Split the page order string into an array of page numbers or range of numbers
String[] pageOrderArr = pagesToDelete.split(",");
for (int i = pagesToRemove.size() - 1; i >= 0; i--) {
int pageIndex = pagesToRemove.get(i); List<Integer> pagesToRemove = pageOrderToString(pageOrderArr, document.getNumberOfPages());
document.removePage(pageIndex);
} for (int i = pagesToRemove.size() - 1; i >= 0; i--) {
return PdfUtils.pdfDocToWebResponse(document, pdfFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_removed_pages.pdf"); int pageIndex = pagesToRemove.get(i);
document.removePage(pageIndex);
} }
return PdfUtils.pdfDocToWebResponse(document, pdfFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_removed_pages.pdf");
@GetMapping("/remove-pages")
public String pageDeleter(Model model) { }
model.addAttribute("currentPage", "remove-pages");
return "remove-pages"; private List<Integer> pageOrderToString(String[] pageOrderArr, int totalPages) {
} List<Integer> newPageOrder = new ArrayList<>();
// loop through the page order array
private List<Integer> pageOrderToString(String[] pageOrderArr, int totalPages) { for (String element : pageOrderArr) {
List<Integer> newPageOrder = new ArrayList<>(); // check if the element contains a range of pages
// loop through the page order array if (element.contains("-")) {
for (String element : pageOrderArr) { // split the range into start and end page
// check if the element contains a range of pages String[] range = element.split("-");
if (element.contains("-")) { int start = Integer.parseInt(range[0]);
// split the range into start and end page int end = Integer.parseInt(range[1]);
String[] range = element.split("-"); // check if the end page is greater than total pages
int start = Integer.parseInt(range[0]); if (end > totalPages) {
int end = Integer.parseInt(range[1]); end = totalPages;
// check if the end page is greater than total pages }
if (end > totalPages) { // loop through the range of pages
end = totalPages; for (int j = start; j <= end; j++) {
} // print the current index
// loop through the range of pages newPageOrder.add(j - 1);
for (int j = start; j <= end; j++) { }
// print the current index } else {
newPageOrder.add(j - 1); // if the element is a single page
} newPageOrder.add(Integer.parseInt(element) - 1);
} else { }
// if the element is a single page }
newPageOrder.add(Integer.parseInt(element) - 1);
} return newPageOrder;
} }
return newPageOrder; @PostMapping(consumes = "multipart/form-data", value = "/rearrange-pages")
} public ResponseEntity<byte[]> rearrangePages(@RequestPart(required = true, value = "fileInput") MultipartFile pdfFile, @RequestParam("pageOrder") String pageOrder) {
try {
@GetMapping("/pdf-organizer") // Load the input PDF
public String pageOrganizer(Model model) { PDDocument document = PDDocument.load(pdfFile.getInputStream());
model.addAttribute("currentPage", "pdf-organizer");
return "pdf-organizer"; // Split the page order string into an array of page numbers or range of numbers
} String[] pageOrderArr = pageOrder.split(",");
// int[] newPageOrder = new int[pageOrderArr.length];
@PostMapping("/rearrange-pages") int totalPages = document.getNumberOfPages();
public ResponseEntity<byte[]> rearrangePages(@RequestParam("fileInput") MultipartFile pdfFile, @RequestParam("pageOrder") String pageOrder) {
try { List<Integer> newPageOrder = pageOrderToString(pageOrderArr, totalPages);
// Load the input PDF
PDDocument document = PDDocument.load(pdfFile.getInputStream()); // Create a new list to hold the pages in the new order
List<PDPage> newPages = new ArrayList<>();
// Split the page order string into an array of page numbers or range of numbers for (int i = 0; i < newPageOrder.size(); i++) {
String[] pageOrderArr = pageOrder.split(","); newPages.add(document.getPage(newPageOrder.get(i)));
// int[] newPageOrder = new int[pageOrderArr.length]; }
int totalPages = document.getNumberOfPages();
// Remove all the pages from the original document
List<Integer> newPageOrder = pageOrderToString(pageOrderArr, totalPages); for (int i = document.getNumberOfPages() - 1; i >= 0; i--) {
document.removePage(i);
// 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++) { // Add the pages in the new order
newPages.add(document.getPage(newPageOrder.get(i))); for (PDPage page : newPages) {
} document.addPage(page);
}
// Remove all the pages from the original document
for (int i = document.getNumberOfPages() - 1; i >= 0; i--) { return PdfUtils.pdfDocToWebResponse(document, pdfFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_rearranged.pdf");
document.removePage(i); } catch (IOException e) {
}
logger.error("Failed rearranging documents", e);
// Add the pages in the new order return null;
for (PDPage page : newPages) { }
document.addPage(page); }
}
}
return PdfUtils.pdfDocToWebResponse(document, pdfFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_rearranged.pdf");
} catch (IOException e) {
logger.error("Failed rearranging documents", e);
return null;
}
}
}

View file

@ -1,48 +1,44 @@
package stirling.software.SPDF.controller; package stirling.software.SPDF.controller.api;
import java.io.IOException; import java.io.IOException;
import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage; import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDPageTree; import org.apache.pdfbox.pdmodel.PDPageTree;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller; import org.springframework.ui.Model;
import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RequestPart;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import stirling.software.SPDF.utils.PdfUtils;
import io.swagger.v3.oas.annotations.Hidden;
@Controller import stirling.software.SPDF.utils.PdfUtils;
public class RotationController {
@RestController
private static final Logger logger = LoggerFactory.getLogger(RotationController.class); public class RotationController {
@PostMapping("/rotate-pdf") private static final Logger logger = LoggerFactory.getLogger(RotationController.class);
public ResponseEntity<byte[]> rotatePDF(@RequestParam("fileInput") MultipartFile pdfFile, @RequestParam("angle") Integer angle) throws IOException {
@PostMapping(consumes = "multipart/form-data", value = "/rotate-pdf")
// Load the PDF document public ResponseEntity<byte[]> rotatePDF(@RequestPart(required = true, value = "fileInput") MultipartFile pdfFile, @RequestParam("angle") Integer angle) throws IOException {
PDDocument document = PDDocument.load(pdfFile.getBytes());
// Load the PDF document
// Get the list of pages in the document PDDocument document = PDDocument.load(pdfFile.getBytes());
PDPageTree pages = document.getPages();
// Get the list of pages in the document
for (PDPage page : pages) { PDPageTree pages = document.getPages();
page.setRotation(page.getRotation() + angle);
} for (PDPage page : pages) {
page.setRotation(page.getRotation() + angle);
return PdfUtils.pdfDocToWebResponse(document, pdfFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_rotated.pdf"); }
} return PdfUtils.pdfDocToWebResponse(document, pdfFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_rotated.pdf");
@GetMapping("/rotate-pdf") }
public String rotatePdfForm(Model model) {
model.addAttribute("currentPage", "rotate-pdf"); }
return "rotate-pdf";
}
}

View file

@ -1,135 +1,133 @@
package stirling.software.SPDF.controller; package stirling.software.SPDF.controller.api;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
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.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.zip.ZipEntry; import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream; import java.util.zip.ZipOutputStream;
import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage; import org.apache.pdfbox.pdmodel.PDPage;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.core.io.ByteArrayResource; import org.springframework.core.io.ByteArrayResource;
import org.springframework.core.io.Resource; import org.springframework.core.io.Resource;
import org.springframework.http.HttpHeaders; import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller; import org.springframework.ui.Model;
import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RequestPart;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
@Controller
public class SplitPDFController { import io.swagger.v3.oas.annotations.Hidden;
private static final Logger logger = LoggerFactory.getLogger(SplitPDFController.class); @RestController
public class SplitPDFController {
@PostMapping("/split-pages")
public ResponseEntity<Resource> splitPdf(@RequestParam("fileInput") MultipartFile file, @RequestParam("pages") String pages) throws IOException { private static final Logger logger = LoggerFactory.getLogger(SplitPDFController.class);
// parse user input
@PostMapping(consumes = "multipart/form-data", value = "/split-pages")
// open the pdf document public ResponseEntity<Resource> splitPdf(@RequestPart(required = true, value = "fileInput") MultipartFile file, @RequestParam("pages") String pages) throws IOException {
InputStream inputStream = file.getInputStream(); // parse user input
PDDocument document = PDDocument.load(inputStream);
// open the pdf document
List<Integer> pageNumbers = new ArrayList<>(); InputStream inputStream = file.getInputStream();
pages = pages.replaceAll("\\s+", ""); // remove whitespaces PDDocument document = PDDocument.load(inputStream);
if (pages.toLowerCase().equals("all")) {
for (int i = 0; i < document.getNumberOfPages(); i++) { List<Integer> pageNumbers = new ArrayList<>();
pageNumbers.add(i); pages = pages.replaceAll("\\s+", ""); // remove whitespaces
} if (pages.toLowerCase().equals("all")) {
} else { for (int i = 0; i < document.getNumberOfPages(); i++) {
List<String> pageNumbersStr = new ArrayList<>(Arrays.asList(pages.split(","))); pageNumbers.add(i);
if (!pageNumbersStr.contains(String.valueOf(document.getNumberOfPages()))) { }
String lastpage = String.valueOf(document.getNumberOfPages()); } else {
pageNumbersStr.add(lastpage); List<String> pageNumbersStr = new ArrayList<>(Arrays.asList(pages.split(",")));
} if (!pageNumbersStr.contains(String.valueOf(document.getNumberOfPages()))) {
for (String page : pageNumbersStr) { String lastpage = String.valueOf(document.getNumberOfPages());
if (page.contains("-")) { pageNumbersStr.add(lastpage);
String[] range = page.split("-"); }
int start = Integer.parseInt(range[0]); for (String page : pageNumbersStr) {
int end = Integer.parseInt(range[1]); if (page.contains("-")) {
for (int i = start; i <= end; i++) { String[] range = page.split("-");
pageNumbers.add(i); int start = Integer.parseInt(range[0]);
} int end = Integer.parseInt(range[1]);
} else { for (int i = start; i <= end; i++) {
pageNumbers.add(Integer.parseInt(page)); pageNumbers.add(i);
} }
} } else {
} pageNumbers.add(Integer.parseInt(page));
}
logger.info("Splitting PDF into pages: {}", pageNumbers.stream().map(String::valueOf).collect(Collectors.joining(","))); }
}
// split the document
List<ByteArrayOutputStream> splitDocumentsBoas = new ArrayList<>(); logger.info("Splitting PDF into pages: {}", pageNumbers.stream().map(String::valueOf).collect(Collectors.joining(",")));
int currentPage = 0;
for (int pageNumber : pageNumbers) { // split the document
try (PDDocument splitDocument = new PDDocument()) { List<ByteArrayOutputStream> splitDocumentsBoas = new ArrayList<>();
for (int i = currentPage; i < pageNumber; i++) { int currentPage = 0;
PDPage page = document.getPage(i); for (int pageNumber : pageNumbers) {
splitDocument.addPage(page); try (PDDocument splitDocument = new PDDocument()) {
logger.debug("Adding page {} to split document", i); for (int i = currentPage; i < pageNumber; i++) {
} PDPage page = document.getPage(i);
currentPage = pageNumber; splitDocument.addPage(page);
logger.debug("Setting current page to {}", currentPage); logger.debug("Adding page {} to split document", i);
}
ByteArrayOutputStream baos = new ByteArrayOutputStream(); currentPage = pageNumber;
splitDocument.save(baos); logger.debug("Setting current page to {}", currentPage);
splitDocumentsBoas.add(baos); ByteArrayOutputStream baos = new ByteArrayOutputStream();
} catch (Exception e) { splitDocument.save(baos);
logger.error("Failed splitting documents and saving them", e);
throw e; splitDocumentsBoas.add(baos);
} } catch (Exception e) {
} logger.error("Failed splitting documents and saving them", e);
throw e;
// closing the original document }
document.close(); }
Path zipFile = Files.createTempFile("split_documents", ".zip"); // closing the original document
document.close();
try (ZipOutputStream zipOut = new ZipOutputStream(Files.newOutputStream(zipFile))) {
// loop through the split documents and write them to the zip file Path zipFile = Files.createTempFile("split_documents", ".zip");
for (int i = 0; i < splitDocumentsBoas.size(); i++) {
String fileName = "split_document_" + (i + 1) + ".pdf"; try (ZipOutputStream zipOut = new ZipOutputStream(Files.newOutputStream(zipFile))) {
ByteArrayOutputStream baos = splitDocumentsBoas.get(i); // loop through the split documents and write them to the zip file
byte[] pdf = baos.toByteArray(); for (int i = 0; i < splitDocumentsBoas.size(); i++) {
String fileName = "split_document_" + (i + 1) + ".pdf";
// Add PDF file to the zip ByteArrayOutputStream baos = splitDocumentsBoas.get(i);
ZipEntry pdfEntry = new ZipEntry(fileName); byte[] pdf = baos.toByteArray();
zipOut.putNextEntry(pdfEntry);
zipOut.write(pdf); // Add PDF file to the zip
zipOut.closeEntry(); ZipEntry pdfEntry = new ZipEntry(fileName);
zipOut.putNextEntry(pdfEntry);
logger.info("Wrote split document {} to zip file", fileName); zipOut.write(pdf);
} zipOut.closeEntry();
} catch (Exception e) {
logger.error("Failed writing to zip", e); logger.info("Wrote split document {} to zip file", fileName);
throw e; }
} } catch (Exception e) {
logger.error("Failed writing to zip", e);
logger.info("Successfully created zip file with split documents: {}", zipFile.toString()); throw e;
byte[] data = Files.readAllBytes(zipFile); }
ByteArrayResource resource = new ByteArrayResource(data);
Files.delete(zipFile); logger.info("Successfully created zip file with split documents: {}", zipFile.toString());
byte[] data = Files.readAllBytes(zipFile);
// return the Resource in the response ByteArrayResource resource = new ByteArrayResource(data);
return ResponseEntity.ok().header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + file.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_split.zip") Files.delete(zipFile);
.contentType(MediaType.APPLICATION_OCTET_STREAM).contentLength(resource.contentLength()).body(resource);
} // return the Resource in the response
return ResponseEntity.ok().header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + file.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_split.zip")
@GetMapping("/split-pdfs") .contentType(MediaType.APPLICATION_OCTET_STREAM).contentLength(resource.contentLength()).body(resource);
public String splitPdfForm(Model model) { }
model.addAttribute("currentPage", "split-pdfs");
return "split-pdfs"; }
}
}

View file

@ -1,97 +1,89 @@
package stirling.software.SPDF.controller.converters; package stirling.software.SPDF.controller.api.converters;
import java.io.IOException; import java.io.IOException;
import org.apache.pdfbox.rendering.ImageType; import org.apache.pdfbox.rendering.ImageType;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.core.io.ByteArrayResource; import org.springframework.core.io.ByteArrayResource;
import org.springframework.core.io.Resource; import org.springframework.core.io.Resource;
import org.springframework.http.HttpHeaders; import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller; import org.springframework.ui.Model;
import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RequestPart;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import stirling.software.SPDF.utils.PdfUtils;
import io.swagger.v3.oas.annotations.Hidden;
@Controller import stirling.software.SPDF.utils.PdfUtils;
public class ConvertImgPDFController {
@RestController
private static final Logger logger = LoggerFactory.getLogger(ConvertImgPDFController.class); public class ConvertImgPDFController {
@PostMapping("/pdf-to-img") private static final Logger logger = LoggerFactory.getLogger(ConvertImgPDFController.class);
public ResponseEntity<Resource> convertToImage(@RequestParam("fileInput") MultipartFile file, @RequestParam("imageFormat") String imageFormat,
@RequestParam("singleOrMultiple") String singleOrMultiple, @RequestParam("colorType") String colorType, @RequestParam("dpi") String dpi) throws IOException { @PostMapping(consumes = "multipart/form-data", value = "/pdf-to-img")
public ResponseEntity<Resource> convertToImage(@RequestPart(required = true, value = "fileInput") MultipartFile file, @RequestParam("imageFormat") String imageFormat,
byte[] pdfBytes = file.getBytes(); @RequestParam("singleOrMultiple") String singleOrMultiple, @RequestParam("colorType") String colorType, @RequestParam("dpi") String dpi) throws IOException {
ImageType colorTypeResult = ImageType.RGB;
if ("greyscale".equals(colorType)) { byte[] pdfBytes = file.getBytes();
colorTypeResult = ImageType.GRAY; ImageType colorTypeResult = ImageType.RGB;
} else if ("blackwhite".equals(colorType)) { if ("greyscale".equals(colorType)) {
colorTypeResult = ImageType.BINARY; colorTypeResult = ImageType.GRAY;
} } else if ("blackwhite".equals(colorType)) {
// returns bytes for image colorTypeResult = ImageType.BINARY;
boolean singleImage = singleOrMultiple.equals("single"); }
byte[] result = null; // returns bytes for image
try { boolean singleImage = singleOrMultiple.equals("single");
result = PdfUtils.convertFromPdf(pdfBytes, imageFormat.toUpperCase(), colorTypeResult, singleImage, Integer.valueOf(dpi)); byte[] result = null;
} catch (IOException e) { try {
// TODO Auto-generated catch block result = PdfUtils.convertFromPdf(pdfBytes, imageFormat.toUpperCase(), colorTypeResult, singleImage, Integer.valueOf(dpi));
e.printStackTrace(); } catch (IOException e) {
} catch (Exception e) { // TODO Auto-generated catch block
// TODO Auto-generated catch block e.printStackTrace();
e.printStackTrace(); } catch (Exception e) {
} // TODO Auto-generated catch block
if (singleImage) { e.printStackTrace();
HttpHeaders headers = new HttpHeaders(); }
headers.setContentType(MediaType.parseMediaType(getMediaType(imageFormat))); if (singleImage) {
ResponseEntity<Resource> response = new ResponseEntity<>(new ByteArrayResource(result), headers, HttpStatus.OK); HttpHeaders headers = new HttpHeaders();
return response; headers.setContentType(MediaType.parseMediaType(getMediaType(imageFormat)));
} else { ResponseEntity<Resource> response = new ResponseEntity<>(new ByteArrayResource(result), headers, HttpStatus.OK);
ByteArrayResource resource = new ByteArrayResource(result); return response;
// return the Resource in the response } else {
return ResponseEntity.ok() ByteArrayResource resource = new ByteArrayResource(result);
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + file.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_convertedToImages.zip") // return the Resource in the response
.contentType(MediaType.APPLICATION_OCTET_STREAM).contentLength(resource.contentLength()).body(resource); return ResponseEntity.ok()
} .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + file.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_convertedToImages.zip")
} .contentType(MediaType.APPLICATION_OCTET_STREAM).contentLength(resource.contentLength()).body(resource);
}
@PostMapping("/img-to-pdf") }
public ResponseEntity<byte[]> convertToPdf(@RequestParam("fileInput") MultipartFile[] file, @RequestParam(defaultValue = "false", name = "stretchToFit") boolean stretchToFit,
@RequestParam(defaultValue = "true", name = "autoRotate") boolean autoRotate) throws IOException { @PostMapping(consumes = "multipart/form-data", value = "/img-to-pdf")
// Convert the file to PDF and get the resulting bytes public ResponseEntity<byte[]> convertToPdf(@RequestPart(required = true, value = "fileInput") MultipartFile[] file,
System.out.println(stretchToFit); @RequestParam(defaultValue = "false", name = "stretchToFit") boolean stretchToFit, @RequestParam(defaultValue = "true", name = "autoRotate") boolean autoRotate)
byte[] bytes = PdfUtils.imageToPdf(file, stretchToFit, autoRotate); throws IOException {
return PdfUtils.bytesToWebResponse(bytes, file[0].getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_coverted.pdf"); // Convert the file to PDF and get the resulting bytes
} System.out.println(stretchToFit);
byte[] bytes = PdfUtils.imageToPdf(file, stretchToFit, autoRotate);
@GetMapping("/img-to-pdf") return PdfUtils.bytesToWebResponse(bytes, file[0].getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_coverted.pdf");
public String convertToPdfForm(Model model) { }
model.addAttribute("currentPage", "img-to-pdf");
return "convert/img-to-pdf"; private String getMediaType(String imageFormat) {
} if (imageFormat.equalsIgnoreCase("PNG"))
return "image/png";
private String getMediaType(String imageFormat) { else if (imageFormat.equalsIgnoreCase("JPEG") || imageFormat.equalsIgnoreCase("JPG"))
if (imageFormat.equalsIgnoreCase("PNG")) return "image/jpeg";
return "image/png"; else if (imageFormat.equalsIgnoreCase("GIF"))
else if (imageFormat.equalsIgnoreCase("JPEG") || imageFormat.equalsIgnoreCase("JPG")) return "image/gif";
return "image/jpeg"; else
else if (imageFormat.equalsIgnoreCase("GIF")) return "application/octet-stream";
return "image/gif"; }
else
return "application/octet-stream";
} }
@GetMapping("/pdf-to-img")
public String pdfToimgForm(Model model) {
model.addAttribute("currentPage", "pdf-to-img");
return "convert/pdf-to-img";
}
}

View file

@ -1,4 +1,4 @@
package stirling.software.SPDF.controller.converters; package stirling.software.SPDF.controller.api.converters;
import java.io.IOException; import java.io.IOException;
import java.nio.file.Files; import java.nio.file.Files;
@ -10,17 +10,18 @@ import java.util.List;
import org.apache.commons.io.FilenameUtils; import org.apache.commons.io.FilenameUtils;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model; import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping; 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; import org.springframework.web.multipart.MultipartFile;
import io.swagger.v3.oas.annotations.Hidden;
import stirling.software.SPDF.utils.PdfUtils; import stirling.software.SPDF.utils.PdfUtils;
import stirling.software.SPDF.utils.ProcessExecutor; import stirling.software.SPDF.utils.ProcessExecutor;
@Controller @RestController
public class ConvertOfficeController { public class ConvertOfficeController {
public byte[] convertToPdf(MultipartFile inputFile) throws IOException, InterruptedException { public byte[] convertToPdf(MultipartFile inputFile) throws IOException, InterruptedException {
@ -50,20 +51,13 @@ public class ConvertOfficeController {
return pdfBytes; return pdfBytes;
} }
@GetMapping("/file-to-pdf")
public String convertToPdfForm(Model model) {
model.addAttribute("currentPage", "file-to-pdf");
return "convert/file-to-pdf";
}
private boolean isValidFileExtension(String fileExtension) { private boolean isValidFileExtension(String fileExtension) {
String extensionPattern = "^(?i)[a-z0-9]{2,4}$"; String extensionPattern = "^(?i)[a-z0-9]{2,4}$";
return fileExtension.matches(extensionPattern); return fileExtension.matches(extensionPattern);
} }
@PostMapping("/file-to-pdf") @PostMapping(consumes = "multipart/form-data", value = "/file-to-pdf")
public ResponseEntity<byte[]> processPdfWithOCR(@RequestParam("fileInput") MultipartFile inputFile) throws IOException, InterruptedException { public ResponseEntity<byte[]> processPdfWithOCR(@RequestPart(required = true, value = "fileInput") MultipartFile inputFile) throws IOException, InterruptedException {
// unused but can start server instance if startup time is to long // unused but can start server instance if startup time is to long
// LibreOfficeListener.getInstance().start(); // LibreOfficeListener.getInstance().start();

View file

@ -0,0 +1,55 @@
package stirling.software.SPDF.controller.api.converters;
import java.io.IOException;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
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;
import org.springframework.web.servlet.ModelAndView;
import io.swagger.v3.oas.annotations.Hidden;
import stirling.software.SPDF.utils.PDFToFile;
@RestController
public class ConvertPDFToOffice {
@PostMapping(consumes = "multipart/form-data", value = "/pdf-to-html")
public ResponseEntity<byte[]> processPdfToHTML(@RequestPart(required = true, value = "fileInput") MultipartFile inputFile) throws IOException, InterruptedException {
PDFToFile pdfToFile = new PDFToFile();
return pdfToFile.processPdfToOfficeFormat(inputFile, "html", "writer_pdf_import");
}
@PostMapping(consumes = "multipart/form-data", value = "/pdf-to-presentation")
public ResponseEntity<byte[]> processPdfToPresentation(@RequestPart(required = true, value = "fileInput") MultipartFile inputFile,
@RequestParam("outputFormat") String outputFormat) throws IOException, InterruptedException {
PDFToFile pdfToFile = new PDFToFile();
return pdfToFile.processPdfToOfficeFormat(inputFile, outputFormat, "impress_pdf_import");
}
@PostMapping(consumes = "multipart/form-data", value = "/pdf-to-text")
public ResponseEntity<byte[]> processPdfToRTForTXT(@RequestPart(required = true, value = "fileInput") MultipartFile inputFile,
@RequestParam("outputFormat") String outputFormat) throws IOException, InterruptedException {
PDFToFile pdfToFile = new PDFToFile();
return pdfToFile.processPdfToOfficeFormat(inputFile, outputFormat, "writer_pdf_import");
}
@PostMapping(consumes = "multipart/form-data", value = "/pdf-to-word")
public ResponseEntity<byte[]> processPdfToWord(@RequestPart(required = true, value = "fileInput") MultipartFile inputFile, @RequestParam("outputFormat") String outputFormat)
throws IOException, InterruptedException {
PDFToFile pdfToFile = new PDFToFile();
return pdfToFile.processPdfToOfficeFormat(inputFile, outputFormat, "writer_pdf_import");
}
@PostMapping(consumes = "multipart/form-data", value = "/pdf-to-xml")
public ResponseEntity<byte[]> processPdfToXML(@RequestPart(required = true, value = "fileInput") MultipartFile inputFile) throws IOException, InterruptedException {
PDFToFile pdfToFile = new PDFToFile();
return pdfToFile.processPdfToOfficeFormat(inputFile, "xml", "writer_pdf_import");
}
}

View file

@ -1,4 +1,4 @@
package stirling.software.SPDF.controller.converters; package stirling.software.SPDF.controller.api.converters;
import java.io.IOException; import java.io.IOException;
import java.nio.file.Files; import java.nio.file.Files;
@ -7,21 +7,22 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model; import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping; 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; import org.springframework.web.multipart.MultipartFile;
import io.swagger.v3.oas.annotations.Hidden;
import stirling.software.SPDF.utils.PdfUtils; import stirling.software.SPDF.utils.PdfUtils;
import stirling.software.SPDF.utils.ProcessExecutor; import stirling.software.SPDF.utils.ProcessExecutor;
@Controller @RestController
public class ConvertPDFToPDFA { public class ConvertPDFToPDFA {
@PostMapping("/pdf-to-pdfa") @PostMapping(consumes = "multipart/form-data", value = "/pdf-to-pdfa")
public ResponseEntity<byte[]> pdfToPdfA(@RequestParam("fileInput") MultipartFile inputFile) throws IOException, InterruptedException { public ResponseEntity<byte[]> pdfToPdfA(@RequestPart(required = true, value = "fileInput") MultipartFile inputFile) throws IOException, InterruptedException {
// Save the uploaded file to a temporary location // Save the uploaded file to a temporary location
Path tempInputFile = Files.createTempFile("input_", ".pdf"); Path tempInputFile = Files.createTempFile("input_", ".pdf");
@ -54,10 +55,4 @@ public class ConvertPDFToPDFA {
return PdfUtils.bytesToWebResponse(pdfBytes, outputFilename); return PdfUtils.bytesToWebResponse(pdfBytes, outputFilename);
} }
@GetMapping("/pdf-to-pdfa")
public String pdfToPdfAForm(Model model) {
model.addAttribute("currentPage", "pdf-to-pdfa");
return "convert/pdf-to-pdfa";
}
} }

View file

@ -1,83 +1,79 @@
package stirling.software.SPDF.controller.other; package stirling.software.SPDF.controller.api.other;
import java.io.IOException; 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.List; import java.util.List;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller; import org.springframework.ui.Model;
import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RequestPart;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import stirling.software.SPDF.utils.PdfUtils;
import stirling.software.SPDF.utils.ProcessExecutor; import io.swagger.v3.oas.annotations.Hidden;
import stirling.software.SPDF.utils.PdfUtils;
@Controller import stirling.software.SPDF.utils.ProcessExecutor;
public class CompressController {
@RestController
private static final Logger logger = LoggerFactory.getLogger(CompressController.class); public class CompressController {
@GetMapping("/compress-pdf") private static final Logger logger = LoggerFactory.getLogger(CompressController.class);
public String compressPdfForm(Model model) {
model.addAttribute("currentPage", "compress-pdf"); @PostMapping(consumes = "multipart/form-data", value = "/compress-pdf")
return "other/compress-pdf"; public ResponseEntity<byte[]> optimizePdf(@RequestPart(required = true, value = "fileInput") MultipartFile inputFile, @RequestParam("optimizeLevel") int optimizeLevel,
} @RequestParam(name = "fastWebView", required = false) Boolean fastWebView, @RequestParam(name = "jbig2Lossy", required = false) Boolean jbig2Lossy)
throws IOException, InterruptedException {
@PostMapping("/compress-pdf")
public ResponseEntity<byte[]> optimizePdf(@RequestParam("fileInput") MultipartFile inputFile, @RequestParam("optimizeLevel") int optimizeLevel, // Save the uploaded file to a temporary location
@RequestParam(name = "fastWebView", required = false) Boolean fastWebView, @RequestParam(name = "jbig2Lossy", required = false) Boolean jbig2Lossy) Path tempInputFile = Files.createTempFile("input_", ".pdf");
throws IOException, InterruptedException { inputFile.transferTo(tempInputFile.toFile());
// Save the uploaded file to a temporary location // Prepare the output file path
Path tempInputFile = Files.createTempFile("input_", ".pdf"); Path tempOutputFile = Files.createTempFile("output_", ".pdf");
inputFile.transferTo(tempInputFile.toFile());
// Prepare the OCRmyPDF command
// Prepare the output file path List<String> command = new ArrayList<>();
Path tempOutputFile = Files.createTempFile("output_", ".pdf"); command.add("ocrmypdf");
command.add("--skip-text");
// Prepare the OCRmyPDF command command.add("--tesseract-timeout=0");
List<String> command = new ArrayList<>(); command.add("--optimize");
command.add("ocrmypdf"); command.add(String.valueOf(optimizeLevel));
command.add("--skip-text"); command.add("--output-type");
command.add("--tesseract-timeout=0"); command.add("pdf");
command.add("--optimize");
command.add(String.valueOf(optimizeLevel)); if (fastWebView != null && fastWebView) {
command.add("--output-type"); long fileSize = inputFile.getSize();
command.add("pdf"); long fastWebViewSize = (long) (fileSize * 1.25); // 25% higher than file size
command.add("--fast-web-view");
if (fastWebView != null && fastWebView) { command.add(String.valueOf(fastWebViewSize));
long fileSize = inputFile.getSize(); }
long fastWebViewSize = (long) (fileSize * 1.25); // 25% higher than file size
command.add("--fast-web-view"); if (jbig2Lossy != null && jbig2Lossy) {
command.add(String.valueOf(fastWebViewSize)); command.add("--jbig2-lossy");
} }
if (jbig2Lossy != null && jbig2Lossy) { command.add(tempInputFile.toString());
command.add("--jbig2-lossy"); command.add(tempOutputFile.toString());
}
int returnCode = ProcessExecutor.getInstance(ProcessExecutor.Processes.OCR_MY_PDF).runCommandWithOutputHandling(command);
command.add(tempInputFile.toString());
command.add(tempOutputFile.toString()); // Read the optimized PDF file
byte[] pdfBytes = Files.readAllBytes(tempOutputFile);
int returnCode = ProcessExecutor.getInstance(ProcessExecutor.Processes.OCR_MY_PDF).runCommandWithOutputHandling(command);
// Clean up the temporary files
// Read the optimized PDF file Files.delete(tempInputFile);
byte[] pdfBytes = Files.readAllBytes(tempOutputFile); Files.delete(tempOutputFile);
// Clean up the temporary files // Return the optimized PDF as a response
Files.delete(tempInputFile); String outputFilename = inputFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_Optimized.pdf";
Files.delete(tempOutputFile); return PdfUtils.bytesToWebResponse(pdfBytes, outputFilename);
}
// Return the optimized PDF as a response
String outputFilename = inputFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_Optimized.pdf"; }
return PdfUtils.bytesToWebResponse(pdfBytes, outputFilename);
}
}

View file

@ -1,10 +1,9 @@
package stirling.software.SPDF.controller.other; package stirling.software.SPDF.controller.api.other;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.StandardCopyOption; import java.nio.file.StandardCopyOption;
@ -24,42 +23,35 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam; 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; import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.ModelAndView;
import io.swagger.v3.oas.annotations.Hidden;
import stirling.software.SPDF.utils.PdfUtils; import stirling.software.SPDF.utils.PdfUtils;
import stirling.software.SPDF.utils.ProcessExecutor; import stirling.software.SPDF.utils.ProcessExecutor;
@Controller @RestController
public class ExtractImageScansController { public class ExtractImageScansController {
private static final Logger logger = LoggerFactory.getLogger(ExtractImageScansController.class); private static final Logger logger = LoggerFactory.getLogger(ExtractImageScansController.class);
@PostMapping(consumes = "multipart/form-data", value = "/extract-image-scans")
@GetMapping("/extract-image-scans") public ResponseEntity<byte[]> extractImageScans(@RequestPart(required = true, value = "fileInput") MultipartFile inputFile,
public ModelAndView extractImageScansForm() { @RequestParam(name = "angle_threshold", defaultValue = "5") int angleThreshold, @RequestParam(name = "tolerance", defaultValue = "20") int tolerance,
ModelAndView modelAndView = new ModelAndView("other/extract-image-scans"); @RequestParam(name = "min_area", defaultValue = "8000") int minArea, @RequestParam(name = "min_contour_area", defaultValue = "500") int minContourArea,
modelAndView.addObject("currentPage", "extract-image-scans"); @RequestParam(name = "border_size", defaultValue = "1") int borderSize) throws IOException, InterruptedException {
return modelAndView;
}
@PostMapping("/extract-image-scans")
public ResponseEntity<byte[]> extractImageScans(@RequestParam("fileInput") MultipartFile inputFile,
@RequestParam(name = "angle_threshold", defaultValue = "5") int angleThreshold,
@RequestParam(name = "tolerance", defaultValue = "20") int tolerance,
@RequestParam(name = "min_area", defaultValue = "8000") int minArea,
@RequestParam(name = "min_contour_area", defaultValue = "500") int minContourArea) throws IOException, InterruptedException {
String fileName = inputFile.getOriginalFilename(); String fileName = inputFile.getOriginalFilename();
String extension = fileName.substring(fileName.lastIndexOf(".") + 1); String extension = fileName.substring(fileName.lastIndexOf(".") + 1);
List<String> images = new ArrayList<>(); List<String> images = new ArrayList<>();
// Check if input file is a PDF // Check if input file is a PDF
if (extension.equalsIgnoreCase("pdf")) { if (extension.equalsIgnoreCase("pdf")) {
// Load PDF document // Load PDF document
try (PDDocument document = PDDocument.load(new ByteArrayInputStream(inputFile.getBytes()))) { try (PDDocument document = PDDocument.load(new ByteArrayInputStream(inputFile.getBytes()))) {
@ -87,14 +79,14 @@ public class ExtractImageScansController {
images.add(tempInputFile.toString()); images.add(tempInputFile.toString());
} }
List<byte[]> processedImageBytes = new ArrayList<>(); List<byte[]> processedImageBytes = new ArrayList<>();
// Process each image // Process each image
for (int i = 0; i < images.size(); i++) { for (int i = 0; i < images.size(); i++) {
Path tempDir = Files.createTempDirectory("openCV_output"); Path tempDir = Files.createTempDirectory("openCV_output");
List<String> command = new ArrayList<>(Arrays.asList("python3", "/pythonScripts/split_photos.py", images.get(i), tempDir.toString(), String.valueOf(angleThreshold), String.valueOf(tolerance),String.valueOf(minArea),String.valueOf(minContourArea))); List<String> command = new ArrayList<>(Arrays.asList("python3", "/scripts/split_photos.py", images.get(i), tempDir.toString(), String.valueOf(angleThreshold),
String.valueOf(tolerance), String.valueOf(minArea), String.valueOf(minContourArea), String.valueOf(borderSize)));
// Run CLI command // Run CLI command
int returnCode = ProcessExecutor.getInstance(ProcessExecutor.Processes.PYTHON_OPENCV).runCommandWithOutputHandling(command); int returnCode = ProcessExecutor.getInstance(ProcessExecutor.Processes.PYTHON_OPENCV).runCommandWithOutputHandling(command);
@ -117,7 +109,7 @@ public class ExtractImageScansController {
try (ZipOutputStream zipOut = new ZipOutputStream(new FileOutputStream(tempZipFile.toFile()))) { try (ZipOutputStream zipOut = new ZipOutputStream(new FileOutputStream(tempZipFile.toFile()))) {
// Add processed images to the zip // Add processed images to the zip
for (int i = 0; i < processedImageBytes.size(); i++) { for (int i = 0; i < processedImageBytes.size(); i++) {
ZipEntry entry = new ZipEntry(fileName.replaceFirst("[.][^.]+$", "") + "_" + (i+1) + ".png"); ZipEntry entry = new ZipEntry(fileName.replaceFirst("[.][^.]+$", "") + "_" + (i + 1) + ".png");
zipOut.putNextEntry(entry); zipOut.putNextEntry(entry);
zipOut.write(processedImageBytes.get(i)); zipOut.write(processedImageBytes.get(i));
zipOut.closeEntry(); zipOut.closeEntry();
@ -135,11 +127,7 @@ public class ExtractImageScansController {
byte[] imageBytes = processedImageBytes.get(0); byte[] imageBytes = processedImageBytes.get(0);
return PdfUtils.bytesToWebResponse(imageBytes, fileName.replaceFirst("[.][^.]+$", "") + ".png", MediaType.IMAGE_PNG); return PdfUtils.bytesToWebResponse(imageBytes, fileName.replaceFirst("[.][^.]+$", "") + ".png", MediaType.IMAGE_PNG);
} }
} }
} }

View file

@ -1,4 +1,4 @@
package stirling.software.SPDF.controller.other; package stirling.software.SPDF.controller.api.other;
import java.awt.Graphics2D; import java.awt.Graphics2D;
import java.awt.Image; import java.awt.Image;
@ -20,22 +20,24 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model; import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam; 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; import org.springframework.web.multipart.MultipartFile;
import io.swagger.v3.oas.annotations.Hidden;
import stirling.software.SPDF.utils.PdfUtils; import stirling.software.SPDF.utils.PdfUtils;
@Controller @RestController
public class ExtractImagesController { public class ExtractImagesController {
private static final Logger logger = LoggerFactory.getLogger(ExtractImagesController.class); private static final Logger logger = LoggerFactory.getLogger(ExtractImagesController.class);
@PostMapping("/extract-images") @PostMapping(consumes = "multipart/form-data", value = "/extract-images")
public ResponseEntity<byte[]> extractImages(@RequestParam("fileInput") MultipartFile file, @RequestParam("format") String format) throws IOException { public ResponseEntity<byte[]> extractImages(@RequestPart(required = true, value = "fileInput") MultipartFile file, @RequestParam("format") String format) throws IOException {
System.out.println(System.currentTimeMillis() + "file=" + file.getName() + ", format=" + format); System.out.println(System.currentTimeMillis() + "file=" + file.getName() + ", format=" + format);
PDDocument document = PDDocument.load(file.getBytes()); PDDocument document = PDDocument.load(file.getBytes());
@ -96,14 +98,8 @@ public class ExtractImagesController {
// Create ByteArrayResource from byte array // Create ByteArrayResource from byte array
byte[] zipContents = baos.toByteArray(); byte[] zipContents = baos.toByteArray();
return PdfUtils.boasToWebResponse(baos, file.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_extracted-images.zip", MediaType.APPLICATION_OCTET_STREAM); return PdfUtils.boasToWebResponse(baos, file.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_extracted-images.zip", MediaType.APPLICATION_OCTET_STREAM);
} }
@GetMapping("/extract-images")
public String extractImagesForm(Model model) {
model.addAttribute("currentPage", "extract-images");
return "other/extract-images";
}
} }

View file

@ -1,4 +1,4 @@
package stirling.software.SPDF.controller.other; package stirling.software.SPDF.controller.api.other;
import java.io.IOException; import java.io.IOException;
import java.text.ParseException; import java.text.ParseException;
@ -11,23 +11,20 @@ import org.apache.pdfbox.cos.COSName;
import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDDocumentInformation; import org.apache.pdfbox.pdmodel.PDDocumentInformation;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model; import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam; 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; import org.springframework.web.multipart.MultipartFile;
import io.swagger.v3.oas.annotations.Hidden;
import stirling.software.SPDF.utils.PdfUtils; import stirling.software.SPDF.utils.PdfUtils;
@Controller @RestController
public class MetadataController { public class MetadataController {
@GetMapping("/change-metadata")
public String addWatermarkForm(Model model) {
model.addAttribute("currentPage", "change-metadata");
return "other/change-metadata";
}
private String checkUndefined(String entry) { private String checkUndefined(String entry) {
// Check if the string is "undefined" // Check if the string is "undefined"
@ -40,8 +37,8 @@ public class MetadataController {
} }
@PostMapping("/update-metadata") @PostMapping(consumes = "multipart/form-data", value = "/update-metadata")
public ResponseEntity<byte[]> metadata(@RequestParam("fileInput") MultipartFile pdfFile, public ResponseEntity<byte[]> metadata(@RequestPart(required = true, value = "fileInput") MultipartFile pdfFile,
@RequestParam(value = "deleteAll", required = false, defaultValue = "false") Boolean deleteAll, @RequestParam(value = "author", required = false) String author, @RequestParam(value = "deleteAll", required = false, defaultValue = "false") Boolean deleteAll, @RequestParam(value = "author", required = false) String author,
@RequestParam(value = "creationDate", required = false) String creationDate, @RequestParam(value = "creator", required = false) String creator, @RequestParam(value = "creationDate", required = false) String creationDate, @RequestParam(value = "creator", required = false) String creator,
@RequestParam(value = "keywords", required = false) String keywords, @RequestParam(value = "modificationDate", required = false) String modificationDate, @RequestParam(value = "keywords", required = false) String keywords, @RequestParam(value = "modificationDate", required = false) String modificationDate,

View file

@ -1,4 +1,4 @@
package stirling.software.SPDF.controller.other; package stirling.software.SPDF.controller.api.other;
import java.io.File; import java.io.File;
import java.io.FileOutputStream; import java.io.FileOutputStream;
@ -19,44 +19,30 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam; 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; import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.ModelAndView;
import io.swagger.v3.oas.annotations.Hidden;
import stirling.software.SPDF.utils.PdfUtils; import stirling.software.SPDF.utils.PdfUtils;
import stirling.software.SPDF.utils.ProcessExecutor; import stirling.software.SPDF.utils.ProcessExecutor;
@Controller @RestController
public class OCRController { public class OCRController {
private static final Logger logger = LoggerFactory.getLogger(OCRController.class); private static final Logger logger = LoggerFactory.getLogger(OCRController.class);
public List<String> getAvailableTesseractLanguages() {
String tessdataDir = "/usr/share/tesseract-ocr/4.00/tessdata";
File[] files = new File(tessdataDir).listFiles();
if (files == null) {
return Collections.emptyList();
}
return Arrays.stream(files).filter(file -> file.getName().endsWith(".traineddata")).map(file -> file.getName().replace(".traineddata", ""))
.filter(lang -> !lang.equalsIgnoreCase("osd")).collect(Collectors.toList());
}
@GetMapping("/ocr-pdf") @PostMapping(consumes = "multipart/form-data", value = "/ocr-pdf")
public ModelAndView ocrPdfPage() { public ResponseEntity<byte[]> processPdfWithOCR(@RequestPart(required = true, value = "fileInput") MultipartFile inputFile,
ModelAndView modelAndView = new ModelAndView("other/ocr-pdf"); @RequestParam("languages") List<String> selectedLanguages, @RequestParam(name = "sidecar", required = false) Boolean sidecar,
modelAndView.addObject("languages", getAvailableTesseractLanguages()); @RequestParam(name = "deskew", required = false) Boolean deskew, @RequestParam(name = "clean", required = false) Boolean clean,
modelAndView.addObject("currentPage", "ocr-pdf"); @RequestParam(name = "clean-final", required = false) Boolean cleanFinal, @RequestParam(name = "ocrType", required = false) String ocrType)
return modelAndView; throws IOException, InterruptedException {
}
@PostMapping("/ocr-pdf")
public ResponseEntity<byte[]> processPdfWithOCR(@RequestParam("fileInput") MultipartFile inputFile, @RequestParam("languages") List<String> selectedLanguages,
@RequestParam(name = "sidecar", required = false) Boolean sidecar, @RequestParam(name = "deskew", required = false) Boolean deskew,
@RequestParam(name = "clean", required = false) Boolean clean, @RequestParam(name = "clean-final", required = false) Boolean cleanFinal,
@RequestParam(name = "ocrType", required = false) String ocrType) throws IOException, InterruptedException {
// --output-type pdfa // --output-type pdfa
if (selectedLanguages == null || selectedLanguages.size() < 1) { if (selectedLanguages == null || selectedLanguages.size() < 1) {
@ -148,7 +134,7 @@ public class OCRController {
Files.delete(tempZipFile); Files.delete(tempZipFile);
Files.delete(tempOutputFile); Files.delete(tempOutputFile);
Files.delete(sidecarTextPath); Files.delete(sidecarTextPath);
// Return the zip file containing both the PDF and the text file // Return the zip file containing both the PDF and the text file
return PdfUtils.bytesToWebResponse(pdfBytes, outputZipFilename, MediaType.APPLICATION_OCTET_STREAM); return PdfUtils.bytesToWebResponse(pdfBytes, outputZipFilename, MediaType.APPLICATION_OCTET_STREAM);
} else { } else {

View file

@ -1,43 +1,39 @@
package stirling.software.SPDF.controller.other; package stirling.software.SPDF.controller.api.other;
import java.io.IOException; import java.io.IOException;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller; import org.springframework.ui.Model;
import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RequestPart;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import stirling.software.SPDF.utils.PdfUtils;
import io.swagger.v3.oas.annotations.Hidden;
@Controller import stirling.software.SPDF.utils.PdfUtils;
public class OverlayImageController {
@RestController
private static final Logger logger = LoggerFactory.getLogger(OverlayImageController.class); public class OverlayImageController {
@GetMapping("/add-image") private static final Logger logger = LoggerFactory.getLogger(OverlayImageController.class);
public String overlayImage(Model model) {
model.addAttribute("currentPage", "add-image"); @PostMapping(consumes = "multipart/form-data", value = "/add-image")
return "other/add-image"; public ResponseEntity<byte[]> overlayImage(@RequestPart(required = true, value = "fileInput") MultipartFile pdfFile, @RequestParam("fileInput2") MultipartFile imageFile,
} @RequestParam("x") float x, @RequestParam("y") float y, @RequestParam("everyPage") boolean everyPage) {
try {
@PostMapping("/add-image") byte[] pdfBytes = pdfFile.getBytes();
public ResponseEntity<byte[]> overlayImage(@RequestParam("fileInput") MultipartFile pdfFile, @RequestParam("fileInput2") MultipartFile imageFile, @RequestParam("x") float x, byte[] imageBytes = imageFile.getBytes();
@RequestParam("y") float y, @RequestParam("everyPage") boolean everyPage) { byte[] result = PdfUtils.overlayImage(pdfBytes, imageBytes, x, y, everyPage);
try {
byte[] pdfBytes = pdfFile.getBytes(); return PdfUtils.bytesToWebResponse(result, pdfFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_overlayed.pdf");
byte[] imageBytes = imageFile.getBytes(); } catch (IOException e) {
byte[] result = PdfUtils.overlayImage(pdfBytes, imageBytes, x, y, everyPage); logger.error("Failed to add image to PDF", e);
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
return PdfUtils.bytesToWebResponse(result, pdfFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_overlayed.pdf"); }
} catch (IOException e) { }
logger.error("Failed to add image to PDF", e); }
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
}
}
}

View file

@ -1,81 +1,69 @@
package stirling.software.SPDF.controller.security; package stirling.software.SPDF.controller.api.security;
import java.io.IOException; import java.io.IOException;
import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.encryption.AccessPermission; import org.apache.pdfbox.pdmodel.encryption.AccessPermission;
import org.apache.pdfbox.pdmodel.encryption.StandardProtectionPolicy; import org.apache.pdfbox.pdmodel.encryption.StandardProtectionPolicy;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller; import org.springframework.ui.Model;
import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RequestPart;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import stirling.software.SPDF.utils.PdfUtils;
import io.swagger.v3.oas.annotations.Hidden;
@Controller import stirling.software.SPDF.utils.PdfUtils;
public class PasswordController {
@RestController
private static final Logger logger = LoggerFactory.getLogger(PasswordController.class); public class PasswordController {
@GetMapping("/add-password") private static final Logger logger = LoggerFactory.getLogger(PasswordController.class);
public String addPasswordForm(Model model) {
model.addAttribute("currentPage", "add-password");
return "security/add-password"; @PostMapping(consumes = "multipart/form-data", value = "/remove-password")
} public ResponseEntity<byte[]> compressPDF(@RequestPart(required = true, value = "fileInput") MultipartFile fileInput, @RequestParam(name = "password") String password)
throws IOException {
@PostMapping("/remove-password") PDDocument document = PDDocument.load(fileInput.getBytes(), password);
public ResponseEntity<byte[]> compressPDF(@RequestParam("fileInput") MultipartFile fileInput, @RequestParam(name = "password") String password) throws IOException { document.setAllSecurityToBeRemoved(true);
PDDocument document = PDDocument.load(fileInput.getBytes(), password); return PdfUtils.pdfDocToWebResponse(document, fileInput.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_password_removed.pdf");
document.setAllSecurityToBeRemoved(true); }
return PdfUtils.pdfDocToWebResponse(document, fileInput.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_password_removed.pdf");
} @PostMapping(consumes = "multipart/form-data", value = "/add-password")
public ResponseEntity<byte[]> compressPDF(@RequestPart(required = true, value = "fileInput") MultipartFile fileInput,
@PostMapping("/add-password") @RequestParam(defaultValue = "", name = "password") String password, @RequestParam(defaultValue = "128", name = "keyLength") int keyLength,
public ResponseEntity<byte[]> compressPDF(@RequestParam("fileInput") MultipartFile fileInput, @RequestParam(defaultValue = "", name = "password") String password, @RequestParam(defaultValue = "false", name = "canAssembleDocument") boolean canAssembleDocument,
@RequestParam(defaultValue = "128", name = "keyLength") int keyLength, @RequestParam(defaultValue = "false", name = "canAssembleDocument") boolean canAssembleDocument, @RequestParam(defaultValue = "false", name = "canExtractContent") boolean canExtractContent,
@RequestParam(defaultValue = "false", name = "canExtractContent") boolean canExtractContent, @RequestParam(defaultValue = "false", name = "canExtractForAccessibility") boolean canExtractForAccessibility,
@RequestParam(defaultValue = "false", name = "canExtractForAccessibility") boolean canExtractForAccessibility, @RequestParam(defaultValue = "false", name = "canFillInForm") boolean canFillInForm, @RequestParam(defaultValue = "false", name = "canModify") boolean canModify,
@RequestParam(defaultValue = "false", name = "canFillInForm") boolean canFillInForm, @RequestParam(defaultValue = "false", name = "canModify") boolean canModify, @RequestParam(defaultValue = "false", name = "canModifyAnnotations") boolean canModifyAnnotations,
@RequestParam(defaultValue = "false", name = "canModifyAnnotations") boolean canModifyAnnotations, @RequestParam(defaultValue = "false", name = "canPrint") boolean canPrint, @RequestParam(defaultValue = "false", name = "canPrintFaithful") boolean canPrintFaithful)
@RequestParam(defaultValue = "false", name = "canPrint") boolean canPrint, @RequestParam(defaultValue = "false", name = "canPrintFaithful") boolean canPrintFaithful) throws IOException {
throws IOException {
PDDocument document = PDDocument.load(fileInput.getBytes());
PDDocument document = PDDocument.load(fileInput.getBytes()); AccessPermission ap = new AccessPermission();
AccessPermission ap = new AccessPermission();
ap.setCanAssembleDocument(!canAssembleDocument);
ap.setCanAssembleDocument(!canAssembleDocument); ap.setCanExtractContent(!canExtractContent);
ap.setCanExtractContent(!canExtractContent); ap.setCanExtractForAccessibility(!canExtractForAccessibility);
ap.setCanExtractForAccessibility(!canExtractForAccessibility); ap.setCanFillInForm(!canFillInForm);
ap.setCanFillInForm(!canFillInForm); ap.setCanModify(!canModify);
ap.setCanModify(!canModify); ap.setCanModifyAnnotations(!canModifyAnnotations);
ap.setCanModifyAnnotations(!canModifyAnnotations); ap.setCanPrint(!canPrint);
ap.setCanPrint(!canPrint); ap.setCanPrintFaithful(!canPrintFaithful);
ap.setCanPrintFaithful(!canPrintFaithful); StandardProtectionPolicy spp = new StandardProtectionPolicy(password, password, ap);
StandardProtectionPolicy spp = new StandardProtectionPolicy(password, password, ap); spp.setEncryptionKeyLength(keyLength);
spp.setEncryptionKeyLength(keyLength);
spp.setPermissions(ap);
spp.setPermissions(ap);
document.protect(spp);
document.protect(spp);
return PdfUtils.pdfDocToWebResponse(document, fileInput.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_passworded.pdf");
return PdfUtils.pdfDocToWebResponse(document, fileInput.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_passworded.pdf"); }
}
@GetMapping("/change-permissions") }
public String permissionsForm(Model model) {
model.addAttribute("currentPage", "change-permissions");
return "security/change-permissions";
}
@GetMapping("/remove-password")
public String removePasswordForm(Model model) {
model.addAttribute("currentPage", "remove-password");
return "security/remove-password";
}
}

View file

@ -1,150 +1,143 @@
package stirling.software.SPDF.controller.security; package stirling.software.SPDF.controller.api.security;
import java.awt.Color; import java.awt.Color;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDDocumentCatalog; import org.apache.pdfbox.pdmodel.PDDocumentCatalog;
import org.apache.pdfbox.pdmodel.PDPage; import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDPageContentStream; import org.apache.pdfbox.pdmodel.PDPageContentStream;
import org.apache.pdfbox.pdmodel.font.PDFont; import org.apache.pdfbox.pdmodel.font.PDFont;
import org.apache.pdfbox.pdmodel.font.PDType1Font; import org.apache.pdfbox.pdmodel.font.PDType1Font;
import org.apache.pdfbox.pdmodel.graphics.state.PDExtendedGraphicsState; import org.apache.pdfbox.pdmodel.graphics.state.PDExtendedGraphicsState;
import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotation; import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotation;
import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotationMarkup; import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotationMarkup;
import org.apache.pdfbox.pdmodel.interactive.form.PDAcroForm; import org.apache.pdfbox.pdmodel.interactive.form.PDAcroForm;
import org.apache.pdfbox.pdmodel.interactive.form.PDField; import org.apache.pdfbox.pdmodel.interactive.form.PDField;
import org.apache.pdfbox.util.Matrix; import org.apache.pdfbox.util.Matrix;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller; import org.springframework.ui.Model;
import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RequestPart;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import stirling.software.SPDF.utils.PdfUtils;
import stirling.software.SPDF.utils.WatermarkRemover; import io.swagger.v3.oas.annotations.Hidden;
import stirling.software.SPDF.utils.PdfUtils;
@Controller import stirling.software.SPDF.utils.WatermarkRemover;
public class WatermarkController {
@RestController
@PostMapping("/add-watermark") public class WatermarkController {
public ResponseEntity<byte[]> addWatermark(@RequestParam("fileInput") MultipartFile pdfFile, @RequestParam("watermarkText") String watermarkText,
@RequestParam(defaultValue = "30", name = "fontSize") float fontSize, @RequestParam(defaultValue = "0", name = "rotation") float rotation, @PostMapping(consumes = "multipart/form-data", value = "/add-watermark")
@RequestParam(defaultValue = "0.5", name = "opacity") float opacity, @RequestParam(defaultValue = "50", name = "widthSpacer") int widthSpacer, public ResponseEntity<byte[]> addWatermark(@RequestPart(required = true, value = "fileInput") MultipartFile pdfFile, @RequestParam("watermarkText") String watermarkText,
@RequestParam(defaultValue = "50", name = "heightSpacer") int heightSpacer) throws IOException { @RequestParam(defaultValue = "30", name = "fontSize") float fontSize, @RequestParam(defaultValue = "0", name = "rotation") float rotation,
@RequestParam(defaultValue = "0.5", name = "opacity") float opacity, @RequestParam(defaultValue = "50", name = "widthSpacer") int widthSpacer,
// Load the input PDF @RequestParam(defaultValue = "50", name = "heightSpacer") int heightSpacer) throws IOException {
PDDocument document = PDDocument.load(pdfFile.getInputStream());
// Load the input PDF
// Create a page in the document PDDocument document = PDDocument.load(pdfFile.getInputStream());
for (PDPage page : document.getPages()) {
// Create a page in the document
// Get the page's content stream for (PDPage page : document.getPages()) {
PDPageContentStream contentStream = new PDPageContentStream(document, page, PDPageContentStream.AppendMode.APPEND, true);
// Get the page's content stream
// Set transparency PDPageContentStream contentStream = new PDPageContentStream(document, page, PDPageContentStream.AppendMode.APPEND, true);
PDExtendedGraphicsState graphicsState = new PDExtendedGraphicsState();
graphicsState.setNonStrokingAlphaConstant(opacity); // Set transparency
contentStream.setGraphicsStateParameters(graphicsState); PDExtendedGraphicsState graphicsState = new PDExtendedGraphicsState();
graphicsState.setNonStrokingAlphaConstant(opacity);
// Set font of watermark contentStream.setGraphicsStateParameters(graphicsState);
PDFont font = PDType1Font.HELVETICA_BOLD;
contentStream.beginText(); // Set font of watermark
contentStream.setFont(font, fontSize); PDFont font = PDType1Font.HELVETICA_BOLD;
contentStream.setNonStrokingColor(Color.LIGHT_GRAY); contentStream.beginText();
contentStream.setFont(font, fontSize);
// Set size and location of watermark contentStream.setNonStrokingColor(Color.LIGHT_GRAY);
float pageWidth = page.getMediaBox().getWidth();
float pageHeight = page.getMediaBox().getHeight(); // Set size and location of watermark
float watermarkWidth = widthSpacer + font.getStringWidth(watermarkText) * fontSize / 1000; float pageWidth = page.getMediaBox().getWidth();
float watermarkHeight = heightSpacer + fontSize; float pageHeight = page.getMediaBox().getHeight();
int watermarkRows = (int) (pageHeight / watermarkHeight + 1); float watermarkWidth = widthSpacer + font.getStringWidth(watermarkText) * fontSize / 1000;
int watermarkCols = (int) (pageWidth / watermarkWidth + 1); float watermarkHeight = heightSpacer + fontSize;
int watermarkRows = (int) (pageHeight / watermarkHeight + 1);
// Add the watermark text int watermarkCols = (int) (pageWidth / watermarkWidth + 1);
for (int i = 0; i < watermarkRows; i++) {
for (int j = 0; j < watermarkCols; j++) { // Add the watermark text
contentStream.setTextMatrix(Matrix.getRotateInstance((float) Math.toRadians(rotation), j * watermarkWidth, i * watermarkHeight)); for (int i = 0; i < watermarkRows; i++) {
contentStream.showTextWithPositioning(new Object[] { watermarkText }); for (int j = 0; j < watermarkCols; j++) {
} contentStream.setTextMatrix(Matrix.getRotateInstance((float) Math.toRadians(rotation), j * watermarkWidth, i * watermarkHeight));
} contentStream.showTextWithPositioning(new Object[] { watermarkText });
}
contentStream.endText(); }
// Close the content stream contentStream.endText();
contentStream.close();
} // Close the content stream
return PdfUtils.pdfDocToWebResponse(document, pdfFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_watermarked.pdf"); contentStream.close();
} }
return PdfUtils.pdfDocToWebResponse(document, pdfFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_watermarked.pdf");
@GetMapping("/add-watermark") }
public String addWatermarkForm(Model model) {
model.addAttribute("currentPage", "add-watermark");
return "security/add-watermark"; @PostMapping(consumes = "multipart/form-data", value = "/remove-watermark")
} public ResponseEntity<byte[]> removeWatermark(@RequestPart(required = true, value = "fileInput") MultipartFile pdfFile, @RequestParam("watermarkText") String watermarkText)
throws Exception {
@PostMapping("/remove-watermark")
public ResponseEntity<byte[]> removeWatermark(@RequestParam("fileInput") MultipartFile pdfFile, @RequestParam("watermarkText") String watermarkText) throws Exception { // Load the input PDF
PDDocument document = PDDocument.load(pdfFile.getInputStream());
// Load the input PDF
PDDocument document = PDDocument.load(pdfFile.getInputStream()); // Create a new PDF document for the output
PDDocument outputDocument = new PDDocument();
// Create a new PDF document for the output
PDDocument outputDocument = new PDDocument(); // Loop through the pages
int numPages = document.getNumberOfPages();
// Loop through the pages for (int i = 0; i < numPages; i++) {
int numPages = document.getNumberOfPages(); PDPage page = document.getPage(i);
for (int i = 0; i < numPages; i++) {
PDPage page = document.getPage(i); // Process the content stream to remove the watermark text
WatermarkRemover editor = new WatermarkRemover(watermarkText) {
// Process the content stream to remove the watermark text };
WatermarkRemover editor = new WatermarkRemover(watermarkText) { editor.processPage(page);
}; editor.processPage(page);
editor.processPage(page); // Add the page to the output document
editor.processPage(page); outputDocument.addPage(page);
// Add the page to the output document }
outputDocument.addPage(page);
} for (PDPage page : outputDocument.getPages()) {
List<PDAnnotation> annotations = page.getAnnotations();
for (PDPage page : outputDocument.getPages()) { List<PDAnnotation> annotationsToRemove = new ArrayList<>();
List<PDAnnotation> annotations = page.getAnnotations();
List<PDAnnotation> annotationsToRemove = new ArrayList<>(); for (PDAnnotation annotation : annotations) {
if (annotation instanceof PDAnnotationMarkup) {
for (PDAnnotation annotation : annotations) { PDAnnotationMarkup markup = (PDAnnotationMarkup) annotation;
if (annotation instanceof PDAnnotationMarkup) { String contents = markup.getContents();
PDAnnotationMarkup markup = (PDAnnotationMarkup) annotation; if (contents != null && contents.contains(watermarkText)) {
String contents = markup.getContents(); annotationsToRemove.add(markup);
if (contents != null && contents.contains(watermarkText)) { }
annotationsToRemove.add(markup); }
} }
}
} annotations.removeAll(annotationsToRemove);
}
annotations.removeAll(annotationsToRemove); PDDocumentCatalog catalog = outputDocument.getDocumentCatalog();
} PDAcroForm acroForm = catalog.getAcroForm();
PDDocumentCatalog catalog = outputDocument.getDocumentCatalog(); if (acroForm != null) {
PDAcroForm acroForm = catalog.getAcroForm(); List<PDField> fields = acroForm.getFields();
if (acroForm != null) { for (PDField field : fields) {
List<PDField> fields = acroForm.getFields(); String fieldValue = field.getValueAsString();
for (PDField field : fields) { if (fieldValue.contains(watermarkText)) {
String fieldValue = field.getValueAsString(); field.setValue(fieldValue.replace(watermarkText, ""));
if (fieldValue.contains(watermarkText)) { }
field.setValue(fieldValue.replace(watermarkText, "")); }
} }
}
} return PdfUtils.pdfDocToWebResponse(outputDocument, "removed.pdf");
}
return PdfUtils.pdfDocToWebResponse(outputDocument, "removed.pdf");
}
}
@GetMapping("/remove-watermark")
public String removeWatermarkForm(Model model) {
model.addAttribute("currentPage", "remove-watermark");
return "security/remove-watermark";
}
}

View file

@ -1,86 +0,0 @@
package stirling.software.SPDF.controller.converters;
import java.io.IOException;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.ModelAndView;
import stirling.software.SPDF.utils.PDFToFile;
@Controller
public class ConvertPDFToOffice {
@GetMapping("/pdf-to-html")
public ModelAndView pdfToHTML() {
ModelAndView modelAndView = new ModelAndView("convert/pdf-to-html");
modelAndView.addObject("currentPage", "pdf-to-html");
return modelAndView;
}
@GetMapping("/pdf-to-presentation")
public ModelAndView pdfToPresentation() {
ModelAndView modelAndView = new ModelAndView("convert/pdf-to-presentation");
modelAndView.addObject("currentPage", "pdf-to-presentation");
return modelAndView;
}
@GetMapping("/pdf-to-text")
public ModelAndView pdfToText() {
ModelAndView modelAndView = new ModelAndView("convert/pdf-to-text");
modelAndView.addObject("currentPage", "pdf-to-text");
return modelAndView;
}
@GetMapping("/pdf-to-word")
public ModelAndView pdfToWord() {
ModelAndView modelAndView = new ModelAndView("convert/pdf-to-word");
modelAndView.addObject("currentPage", "pdf-to-word");
return modelAndView;
}
@GetMapping("/pdf-to-xml")
public ModelAndView pdfToXML() {
ModelAndView modelAndView = new ModelAndView("convert/pdf-to-xml");
modelAndView.addObject("currentPage", "pdf-to-xml");
return modelAndView;
}
@PostMapping("/pdf-to-html")
public ResponseEntity<byte[]> processPdfToHTML(@RequestParam("fileInput") MultipartFile inputFile) throws IOException, InterruptedException {
PDFToFile pdfToFile = new PDFToFile();
return pdfToFile.processPdfToOfficeFormat(inputFile, "html", "writer_pdf_import");
}
@PostMapping("/pdf-to-presentation")
public ResponseEntity<byte[]> processPdfToPresentation(@RequestParam("fileInput") MultipartFile inputFile, @RequestParam("outputFormat") String outputFormat)
throws IOException, InterruptedException {
PDFToFile pdfToFile = new PDFToFile();
return pdfToFile.processPdfToOfficeFormat(inputFile, outputFormat, "impress_pdf_import");
}
@PostMapping("/pdf-to-text")
public ResponseEntity<byte[]> processPdfToRTForTXT(@RequestParam("fileInput") MultipartFile inputFile, @RequestParam("outputFormat") String outputFormat)
throws IOException, InterruptedException {
PDFToFile pdfToFile = new PDFToFile();
return pdfToFile.processPdfToOfficeFormat(inputFile, outputFormat, "writer_pdf_import");
}
@PostMapping("/pdf-to-word")
public ResponseEntity<byte[]> processPdfToWord(@RequestParam("fileInput") MultipartFile inputFile, @RequestParam("outputFormat") String outputFormat)
throws IOException, InterruptedException {
PDFToFile pdfToFile = new PDFToFile();
return pdfToFile.processPdfToOfficeFormat(inputFile, outputFormat, "writer_pdf_import");
}
@PostMapping("/pdf-to-xml")
public ResponseEntity<byte[]> processPdfToXML(@RequestParam("fileInput") MultipartFile inputFile) throws IOException, InterruptedException {
PDFToFile pdfToFile = new PDFToFile();
return pdfToFile.processPdfToOfficeFormat(inputFile, "xml", "writer_pdf_import");
}
}

View file

@ -0,0 +1,86 @@
package stirling.software.SPDF.controller.web;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.servlet.ModelAndView;
import io.swagger.v3.oas.annotations.Hidden;
@Controller
public class ConverterWebController {
@GetMapping("/img-to-pdf")
@Hidden
public String convertImgToPdfForm(Model model) {
model.addAttribute("currentPage", "img-to-pdf");
return "convert/img-to-pdf";
}
@GetMapping("/pdf-to-img")
@Hidden
public String pdfToimgForm(Model model) {
model.addAttribute("currentPage", "pdf-to-img");
return "convert/pdf-to-img";
}
@GetMapping("/file-to-pdf")
@Hidden
public String convertToPdfForm(Model model) {
model.addAttribute("currentPage", "file-to-pdf");
return "convert/file-to-pdf";
}
//PDF TO......
@GetMapping("/pdf-to-html")
@Hidden
public ModelAndView pdfToHTML() {
ModelAndView modelAndView = new ModelAndView("convert/pdf-to-html");
modelAndView.addObject("currentPage", "pdf-to-html");
return modelAndView;
}
@GetMapping("/pdf-to-presentation")
@Hidden
public ModelAndView pdfToPresentation() {
ModelAndView modelAndView = new ModelAndView("convert/pdf-to-presentation");
modelAndView.addObject("currentPage", "pdf-to-presentation");
return modelAndView;
}
@GetMapping("/pdf-to-text")
@Hidden
public ModelAndView pdfToText() {
ModelAndView modelAndView = new ModelAndView("convert/pdf-to-text");
modelAndView.addObject("currentPage", "pdf-to-text");
return modelAndView;
}
@GetMapping("/pdf-to-word")
@Hidden
public ModelAndView pdfToWord() {
ModelAndView modelAndView = new ModelAndView("convert/pdf-to-word");
modelAndView.addObject("currentPage", "pdf-to-word");
return modelAndView;
}
@GetMapping("/pdf-to-xml")
@Hidden
public ModelAndView pdfToXML() {
ModelAndView modelAndView = new ModelAndView("convert/pdf-to-xml");
modelAndView.addObject("currentPage", "pdf-to-xml");
return modelAndView;
}
@GetMapping("/pdf-to-pdfa")
@Hidden
public String pdfToPdfAForm(Model model) {
model.addAttribute("currentPage", "pdf-to-pdfa");
return "convert/pdf-to-pdfa";
}
}

View file

@ -0,0 +1,62 @@
package stirling.software.SPDF.controller.web;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import io.swagger.v3.oas.annotations.Hidden;
@Controller
public class GeneralWebController {
@GetMapping("/merge-pdfs")
@Hidden
public String mergePdfForm(Model model) {
model.addAttribute("currentPage", "merge-pdfs");
return "merge-pdfs";
}
@GetMapping("/multi-tool")
@Hidden
public String multiToolForm(Model model) {
model.addAttribute("currentPage", "multi-tool");
return "multi-tool";
}
@GetMapping("/")
public String home(Model model) {
model.addAttribute("currentPage", "home");
return "home";
}
@GetMapping("/home")
public String root(Model model) {
return "redirect:/";
}
@GetMapping("/remove-pages")
@Hidden
public String pageDeleter(Model model) {
model.addAttribute("currentPage", "remove-pages");
return "remove-pages";
}
@GetMapping("/pdf-organizer")
@Hidden
public String pageOrganizer(Model model) {
model.addAttribute("currentPage", "pdf-organizer");
return "pdf-organizer";
}
@GetMapping("/rotate-pdf")
@Hidden
public String rotatePdfForm(Model model) {
model.addAttribute("currentPage", "rotate-pdf");
return "rotate-pdf";
}
@GetMapping("/split-pdfs")
@Hidden
public String splitPdfForm(Model model) {
model.addAttribute("currentPage", "split-pdfs");
return "split-pdfs";
}
}

View file

@ -0,0 +1,75 @@
package stirling.software.SPDF.controller.web;
import java.io.File;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.servlet.ModelAndView;
import io.swagger.v3.oas.annotations.Hidden;
@Controller
public class OtherWebController {
@GetMapping("/compress-pdf")
@Hidden
public String compressPdfForm(Model model) {
model.addAttribute("currentPage", "compress-pdf");
return "other/compress-pdf";
}
@GetMapping("/extract-image-scans")
@Hidden
public ModelAndView extractImageScansForm() {
ModelAndView modelAndView = new ModelAndView("other/extract-image-scans");
modelAndView.addObject("currentPage", "extract-image-scans");
return modelAndView;
}
@GetMapping("/extract-images")
@Hidden
public String extractImagesForm(Model model) {
model.addAttribute("currentPage", "extract-images");
return "other/extract-images";
}
@GetMapping("/change-metadata")
@Hidden
public String addWatermarkForm(Model model) {
model.addAttribute("currentPage", "change-metadata");
return "other/change-metadata";
}
public List<String> getAvailableTesseractLanguages() {
String tessdataDir = "/usr/share/tesseract-ocr/4.00/tessdata";
File[] files = new File(tessdataDir).listFiles();
if (files == null) {
return Collections.emptyList();
}
return Arrays.stream(files).filter(file -> file.getName().endsWith(".traineddata")).map(file -> file.getName().replace(".traineddata", ""))
.filter(lang -> !lang.equalsIgnoreCase("osd")).collect(Collectors.toList());
}
@GetMapping("/ocr-pdf")
@Hidden
public ModelAndView ocrPdfPage() {
ModelAndView modelAndView = new ModelAndView("other/ocr-pdf");
modelAndView.addObject("languages", getAvailableTesseractLanguages());
modelAndView.addObject("currentPage", "ocr-pdf");
return modelAndView;
}
@GetMapping("/add-image")
@Hidden
public String overlayImage(Model model) {
model.addAttribute("currentPage", "add-image");
return "other/add-image";
}
}

View file

@ -0,0 +1,46 @@
package stirling.software.SPDF.controller.web;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import io.swagger.v3.oas.annotations.Hidden;
@Controller
public class SecurityWebController {
@GetMapping("/add-password")
@Hidden
public String addPasswordForm(Model model) {
model.addAttribute("currentPage", "add-password");
return "security/add-password";
}
@GetMapping("/change-permissions")
@Hidden
public String permissionsForm(Model model) {
model.addAttribute("currentPage", "change-permissions");
return "security/change-permissions";
}
@GetMapping("/remove-password")
@Hidden
public String removePasswordForm(Model model) {
model.addAttribute("currentPage", "remove-password");
return "security/remove-password";
}
@GetMapping("/add-watermark")
@Hidden
public String addWatermarkForm(Model model) {
model.addAttribute("currentPage", "add-watermark");
return "security/add-watermark";
}
//WIP
@GetMapping("/remove-watermark")
@Hidden
public String removeWatermarkForm(Model model) {
model.addAttribute("currentPage", "remove-watermark");
return "security/remove-watermark";
}
}

View file

@ -58,7 +58,6 @@ public class PDFToFile {
// Get output files // Get output files
List<File> outputFiles = Arrays.asList(tempOutputDir.toFile().listFiles()); List<File> outputFiles = Arrays.asList(tempOutputDir.toFile().listFiles());
if (outputFiles.size() == 1) { if (outputFiles.size() == 1) {
// Return single output file // Return single output file
File outputFile = outputFiles.get(0); File outputFile = outputFiles.get(0);
@ -69,7 +68,7 @@ public class PDFToFile {
fileBytes = FileUtils.readFileToByteArray(outputFile); fileBytes = FileUtils.readFileToByteArray(outputFile);
} else { } else {
// Return output files in a ZIP archive // Return output files in a ZIP archive
fileName = pdfBaseName + "To" + outputFormat + ".zip"; fileName = pdfBaseName + "To" + outputFormat + ".zip";
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ZipOutputStream zipOutputStream = new ZipOutputStream(byteArrayOutputStream); ZipOutputStream zipOutputStream = new ZipOutputStream(byteArrayOutputStream);

View file

@ -48,10 +48,10 @@ public class PdfUtils {
} }
public static ResponseEntity<byte[]> boasToWebResponse(ByteArrayOutputStream baos, String docName, MediaType mediaType) throws IOException { public static ResponseEntity<byte[]> boasToWebResponse(ByteArrayOutputStream baos, String docName, MediaType mediaType) throws IOException {
return PdfUtils.bytesToWebResponse(baos.toByteArray(), docName, mediaType ); return PdfUtils.bytesToWebResponse(baos.toByteArray(), docName, mediaType);
} }
public static ResponseEntity<byte[]> bytesToWebResponse(byte[] bytes, String docName, MediaType mediaType ) throws IOException { public static ResponseEntity<byte[]> bytesToWebResponse(byte[] bytes, String docName, MediaType mediaType) throws IOException {
// Return the PDF as a response // Return the PDF as a response
HttpHeaders headers = new HttpHeaders(); HttpHeaders headers = new HttpHeaders();
@ -61,7 +61,7 @@ public class PdfUtils {
headers.setContentDispositionFormData("attachment", encodedDocName); headers.setContentDispositionFormData("attachment", encodedDocName);
return new ResponseEntity<>(bytes, headers, HttpStatus.OK); return new ResponseEntity<>(bytes, headers, HttpStatus.OK);
} }
public static ResponseEntity<byte[]> bytesToWebResponse(byte[] bytes, String docName) throws IOException { public static ResponseEntity<byte[]> bytesToWebResponse(byte[] bytes, String docName) throws IOException {
return bytesToWebResponse(bytes, docName, MediaType.APPLICATION_PDF); return bytesToWebResponse(bytes, docName, MediaType.APPLICATION_PDF);
} }

View file

@ -0,0 +1,35 @@
/* Rainbow Mode Styles */
body {
background: linear-gradient(90deg, rgba(255,0,0,1) 0%, rgba(255,154,0,1) 10%, rgba(208,222,33,1) 20%, rgba(79,220,74,1) 30%, rgba(63,218,216,1) 40%, rgba(47,201,226,1) 50%, rgba(28,127,238,1) 60%, rgba(95,21,242,1) 70%, rgba(186,12,248,1) 80%, rgba(251,7,217,1) 90%, rgba(255,0,0,1) 100%);
color: #fff !important;
}
.dark-card {
background: linear-gradient(90deg, rgba(255,0,0,1) 0%, rgba(255,154,0,1) 10%, rgba(208,222,33,1) 20%, rgba(79,220,74,1) 30%, rgba(63,218,216,1) 40%, rgba(47,201,226,1) 50%, rgba(28,127,238,1) 60%, rgba(95,21,242,1) 70%, rgba(186,12,248,1) 80%, rgba(251,7,217,1) 90%, rgba(255,0,0,1) 100%) !important;
color: white !important;
}
.jumbotron {
background: linear-gradient(90deg, rgba(255,0,0,1) 0%, rgba(255,154,0,1) 10%, rgba(208,222,33,1) 20%, rgba(79,220,74,1) 30%, rgba(63,218,216,1) 40%, rgba(47,201,226,1) 50%, rgba(28,127,238,1) 60%, rgba(95,21,242,1) 70%, rgba(186,12,248,1) 80%, rgba(251,7,217,1) 90%, rgba(255,0,0,1) 100%);
color: #fff !important;
}
.list-group {
background: linear-gradient(90deg, rgba(255,0,0,1) 0%, rgba(255,154,0,1) 10%, rgba(208,222,33,1) 20%, rgba(79,220,74,1) 30%, rgba(63,218,216,1) 40%, rgba(47,201,226,1) 50%, rgba(28,127,238,1) 60%, rgba(95,21,242,1) 70%, rgba(186,12,248,1) 80%, rgba(251,7,217,1) 90%, rgba(255,0,0,1) 100%) !important;
color: fff !important;
}
.list-group-item {
background: linear-gradient(90deg, rgba(255,0,0,1) 0%, rgba(255,154,0,1) 10%, rgba(208,222,33,1) 20%, rgba(79,220,74,1) 30%, rgba(63,218,216,1) 40%, rgba(47,201,226,1) 50%, rgba(28,127,238,1) 60%, rgba(95,21,242,1) 70%, rgba(186,12,248,1) 80%, rgba(251,7,217,1) 90%, rgba(255,0,0,1) 100%) !important;
color: fff !important;
}
#support-section {
background: linear-gradient(90deg, rgba(255,0,0,1) 0%, rgba(255,154,0,1) 10%, rgba(208,222,33,1) 20%, rgba(79,220,74,1) 30%, rgba(63,218,216,1) 40%, rgba(47,201,226,1) 50%, rgba(28,127,238,1) 60%, rgba(95,21,242,1) 70%, rgba(186,12,248,1) 80%, rgba(251,7,217,1) 90%, rgba(255,0,0,1) 100%) !important;
}
#pages-container-wrapper {
--background-color: rgba(255, 255, 255, 0.046) !important;
--scroll-bar-color: #4c4c4c !important;
--scroll-bar-thumb: #d3d3d3 !important;
--scroll-bar-thumb-hover: #ffffff !important;
}

View file

@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-rainbow" viewBox="0 0 16 16">
<path d="M8 4.5a7 7 0 0 0-7 7 .5.5 0 0 1-1 0 8 8 0 1 1 16 0 .5.5 0 0 1-1 0 7 7 0 0 0-7-7zm0 2a5 5 0 0 0-5 5 .5.5 0 0 1-1 0 6 6 0 1 1 12 0 .5.5 0 0 1-1 0 5 5 0 0 0-5-5zm0 2a3 3 0 0 0-3 3 .5.5 0 0 1-1 0 4 4 0 1 1 8 0 .5.5 0 0 1-1 0 3 3 0 0 0-3-3zm0 2a1 1 0 0 0-1 1 .5.5 0 0 1-1 0 2 2 0 1 1 4 0 .5.5 0 0 1-1 0 1 1 0 0 0-1-1z"/>
</svg>

After

Width:  |  Height:  |  Size: 459 B

View file

@ -28,53 +28,78 @@
<!-- Custom --> <!-- Custom -->
<link rel="stylesheet" href="css/general.css"> <link rel="stylesheet" href="css/general.css">
<link rel="stylesheet" th:href="@{css/dark-mode.css}" id="dark-mode-styles"> <link rel="stylesheet" th:href="@{css/dark-mode.css}" id="dark-mode-styles">
<link rel="stylesheet" th:href="@{css/rainbow-mode.css}" id="rainbow-mode-styles" disabled="true">
<script> <script>
function toggleDarkMode() { var toggleCount = 0;
var darkModeStyles = document.getElementById("dark-mode-styles"); var lastToggleTime = Date.now();
var darkModeIcon = document.getElementById("dark-mode-icon");
if (localStorage.getItem("dark-mode") == "on") { function toggleDarkMode() {
localStorage.setItem("dark-mode", "off"); var currentTime = Date.now();
darkModeStyles.disabled = true; if (currentTime - lastToggleTime < 1000) {
darkModeIcon.src = "sun.svg"; toggleCount++;
} else { } else {
localStorage.setItem("dark-mode", "on"); toggleCount = 1;
darkModeStyles.disabled = false; }
darkModeIcon.src = "moon.svg"; lastToggleTime = currentTime;
}
} var darkModeStyles = document.getElementById("dark-mode-styles");
var rainbowModeStyles = document.getElementById("rainbow-mode-styles");
var darkModeIcon = document.getElementById("dark-mode-icon");
if (toggleCount >= 18) {
localStorage.setItem("dark-mode", "rainbow");
darkModeStyles.disabled = true;
rainbowModeStyles.disabled = false;
darkModeIcon.src = "rainbow.svg";
} else if (localStorage.getItem("dark-mode") == "on") {
localStorage.setItem("dark-mode", "off");
darkModeStyles.disabled = true;
rainbowModeStyles.disabled = true;
darkModeIcon.src = "sun.svg";
} else {
localStorage.setItem("dark-mode", "on");
darkModeStyles.disabled = false;
rainbowModeStyles.disabled = true;
darkModeIcon.src = "moon.svg";
}
}
document.addEventListener("DOMContentLoaded", function () { document.addEventListener("DOMContentLoaded", function () {
var darkModeStyles = document.getElementById("dark-mode-styles"); var darkModeStyles = document.getElementById("dark-mode-styles");
var darkModeIcon = document.getElementById("dark-mode-icon"); var rainbowModeStyles = document.getElementById("rainbow-mode-styles");
var darkModeIcon = document.getElementById("dark-mode-icon");
// Check if the user has already set a preference if (localStorage.getItem("dark-mode") == "on") {
if (localStorage.getItem("dark-mode") == "on") { darkModeStyles.disabled = false;
darkModeStyles.disabled = false; rainbowModeStyles.disabled = true;
darkModeIcon.src = "moon.svg"; darkModeIcon.src = "moon.svg";
} else if (localStorage.getItem("dark-mode") == "off") { } else if (localStorage.getItem("dark-mode") == "off") {
darkModeStyles.disabled = true; darkModeStyles.disabled = true;
darkModeIcon.src = "sun.svg"; rainbowModeStyles.disabled = true;
} else { darkModeIcon.src = "sun.svg";
// Check the OS's default dark mode setting } else if (localStorage.getItem("dark-mode") == "rainbow") {
if (window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)").matches) { darkModeStyles.disabled = true;
darkModeStyles.disabled = false; rainbowModeStyles.disabled = false;
darkModeIcon.src = "moon.svg"; darkModeIcon.src = "rainbow.svg";
} else { } else {
darkModeStyles.disabled = true; if (window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)").matches) {
darkModeIcon.src = "sun.svg"; darkModeStyles.disabled = false;
} rainbowModeStyles.disabled = true;
} darkModeIcon.src = "moon.svg";
} else {
darkModeStyles.disabled = true;
rainbowModeStyles.disabled = true;
darkModeIcon.src = "sun.svg";
}
}
// Attach the toggleDarkMode function to the click event of the dark mode toggle document.getElementById("dark-mode-toggle").addEventListener("click", function (event) {
document.getElementById("dark-mode-toggle").addEventListener("click", function (event) { event.preventDefault();
event.preventDefault(); toggleDarkMode();
toggleDarkMode(); });
}); });
}); </script>
</script>
</head> </head>
<th:block th:fragment="fileSelector(name, multiple)" th:with="accept=${accept} ?: '*/*', inputText=${inputText} ?: #{pdfPrompt}"> <th:block th:fragment="fileSelector(name, multiple)" th:with="accept=${accept} ?: '*/*', inputText=${inputText} ?: #{pdfPrompt}">
<div class="custom-file-chooser"> <div class="custom-file-chooser">

View file

@ -16,6 +16,31 @@
<form id="multiPdfForm" th:action="@{extract-image-scans}" method="post" enctype="multipart/form-data"> <form id="multiPdfForm" th:action="@{extract-image-scans}" method="post" enctype="multipart/form-data">
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false, accept='image/*, application/pdf')}"></div> <div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false, accept='image/*, application/pdf')}"></div>
<div class="form-group">
<label for="angleThreshold">Angle Threshold:</label>
<input type="number" class="form-control" id="angleThreshold" name="angle_threshold" value="5">
<small id="angleThresholdHelp" class="form-text text-muted">Sets the minimum absolute angle required for the image to be rotated (default: 10).</small>
</div>
<div class="form-group">
<label for="tolerance">Tolerance:</label>
<input type="number" class="form-control" id="tolerance" name="tolerance" value="20">
<small id="toleranceHelp" class="form-text text-muted">Determines the range of color variation around the estimated background color (default: 30).</small>
</div>
<div class="form-group">
<label for="minArea">Minimum Area:</label>
<input type="number" class="form-control" id="minArea" name="min_area" value="8000">
<small id="minAreaHelp" class="form-text text-muted">Sets the minimum area threshold for a photo (default: 10000).</small>
</div>
<div class="form-group">
<label for="minContourArea">Minimum Contour Area:</label>
<input type="number" class="form-control" id="minContourArea" name="min_contour_area" value="500">
<small id="minContourAreaHelp" class="form-text text-muted">Sets the minimum contour area threshold for a photo (default: 500).</small>
</div>
<div class="form-group">
<label for="borderSize">Border Size:</label>
<input type="number" class="form-control" id="borderSize" name="border_size" value="0">
<small id="borderSizeHelp" class="form-text text-muted">Sets the size of the border added and removed to prevent white borders in the output (default: 0).</small>
</div>
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{extractImageScans.submit}"></button> <button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{extractImageScans.submit}"></button>
</form> </form>
</div> </div>