From dba6868b6de2aee2796135fd0563f6506b0ef77f Mon Sep 17 00:00:00 2001 From: Anthony Stirling <77850077+Frooodle@users.noreply.github.com> Date: Thu, 2 Feb 2023 21:08:51 +0000 Subject: [PATCH] Add files via upload --- build.gradle | 2 +- .../software/SPDF/SPdfApplication.java | 13 ++ .../SPDF/controller/CompressController.java | 74 ++++++++ .../SPDF/controller/MergeController.java | 77 ++++++++ .../controller/OverlayImageController.java | 46 +++++ .../SPDF/controller/PdfController.java | 43 +++++ .../RearrangePagesPDFController.java | 128 +++++++++++++ .../SPDF/controller/RotationController.java | 60 +++++++ .../SPDF/controller/SplitPDFController.java | 142 +++++++++++++++ .../converters/ConvertImgPDFController.java | 70 ++++++++ .../security/PasswordController.java | 92 ++++++++++ .../security/WatermarkController.java | 83 +++++++++ .../software/SPDF/utils/PdfUtils.java | 168 ++++++++++++++++++ src/main/resources/application.properties | 11 ++ src/main/resources/log4j.properties | 8 + src/main/resources/static/dark-mode.css | 23 +++ src/main/resources/static/favicon.png | Bin 0 -> 4356 bytes src/main/resources/static/favicon.svg | 1 + src/main/resources/static/general.css | 5 + src/main/resources/templates/add-image.html | 51 ++++++ .../resources/templates/compress-pdf.html | 44 +++++ .../templates/convert/img-to-pdf.html | 36 ++++ .../templates/convert/pdf-to-img.html | 41 +++++ .../resources/templates/delete-pages.html | 40 +++++ .../resources/templates/fragments/common.html | 97 ++++++++++ .../resources/templates/fragments/footer.html | 11 ++ .../resources/templates/fragments/navbar.html | 87 +++++++++ src/main/resources/templates/home.html | 106 +++++++++++ src/main/resources/templates/merge-pdfs.html | 133 ++++++++++++++ .../resources/templates/pdf-organizer.html | 42 +++++ src/main/resources/templates/rotate-pdf.html | 47 +++++ .../templates/security/add-password.html | 103 +++++++++++ .../templates/security/add-watermark.html | 56 ++++++ .../security/change-permissions.html | 92 ++++++++++ .../templates/security/remove-password.html | 38 ++++ src/main/resources/templates/split-pdfs.html | 49 +++++ 36 files changed, 2118 insertions(+), 1 deletion(-) create mode 100644 src/main/java/stirling/software/SPDF/SPdfApplication.java create mode 100644 src/main/java/stirling/software/SPDF/controller/CompressController.java create mode 100644 src/main/java/stirling/software/SPDF/controller/MergeController.java create mode 100644 src/main/java/stirling/software/SPDF/controller/OverlayImageController.java create mode 100644 src/main/java/stirling/software/SPDF/controller/PdfController.java create mode 100644 src/main/java/stirling/software/SPDF/controller/RearrangePagesPDFController.java create mode 100644 src/main/java/stirling/software/SPDF/controller/RotationController.java create mode 100644 src/main/java/stirling/software/SPDF/controller/SplitPDFController.java create mode 100644 src/main/java/stirling/software/SPDF/controller/converters/ConvertImgPDFController.java create mode 100644 src/main/java/stirling/software/SPDF/controller/security/PasswordController.java create mode 100644 src/main/java/stirling/software/SPDF/controller/security/WatermarkController.java create mode 100644 src/main/java/stirling/software/SPDF/utils/PdfUtils.java create mode 100644 src/main/resources/application.properties create mode 100644 src/main/resources/log4j.properties create mode 100644 src/main/resources/static/dark-mode.css create mode 100644 src/main/resources/static/favicon.png create mode 100644 src/main/resources/static/favicon.svg create mode 100644 src/main/resources/static/general.css create mode 100644 src/main/resources/templates/add-image.html create mode 100644 src/main/resources/templates/compress-pdf.html create mode 100644 src/main/resources/templates/convert/img-to-pdf.html create mode 100644 src/main/resources/templates/convert/pdf-to-img.html create mode 100644 src/main/resources/templates/delete-pages.html create mode 100644 src/main/resources/templates/fragments/common.html create mode 100644 src/main/resources/templates/fragments/footer.html create mode 100644 src/main/resources/templates/fragments/navbar.html create mode 100644 src/main/resources/templates/home.html create mode 100644 src/main/resources/templates/merge-pdfs.html create mode 100644 src/main/resources/templates/pdf-organizer.html create mode 100644 src/main/resources/templates/rotate-pdf.html create mode 100644 src/main/resources/templates/security/add-password.html create mode 100644 src/main/resources/templates/security/add-watermark.html create mode 100644 src/main/resources/templates/security/change-permissions.html create mode 100644 src/main/resources/templates/security/remove-password.html create mode 100644 src/main/resources/templates/split-pdfs.html diff --git a/build.gradle b/build.gradle index 1ad95df15..ba0b9acf9 100644 --- a/build.gradle +++ b/build.gradle @@ -5,7 +5,7 @@ plugins { } group = 'stirling.software' -version = '0.2.3' +version = '0.3.0' sourceCompatibility = '17' repositories { diff --git a/src/main/java/stirling/software/SPDF/SPdfApplication.java b/src/main/java/stirling/software/SPDF/SPdfApplication.java new file mode 100644 index 000000000..b0f260ad8 --- /dev/null +++ b/src/main/java/stirling/software/SPDF/SPdfApplication.java @@ -0,0 +1,13 @@ +package stirling.software.SPDF; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class SPdfApplication { + + public static void main(String[] args) { + SpringApplication.run(SPdfApplication.class, args); + } + +} diff --git a/src/main/java/stirling/software/SPDF/controller/CompressController.java b/src/main/java/stirling/software/SPDF/controller/CompressController.java new file mode 100644 index 000000000..6b06a5ef1 --- /dev/null +++ b/src/main/java/stirling/software/SPDF/controller/CompressController.java @@ -0,0 +1,74 @@ +package stirling.software.SPDF.controller; + +import java.io.ByteArrayOutputStream; +import java.io.FileOutputStream; +import java.io.IOException; + +import org.apache.pdfbox.pdmodel.PDDocument; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +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 com.spire.pdf.PdfCompressionLevel; +import com.spire.pdf.PdfDocument; +import com.spire.pdf.PdfPageBase; +import com.spire.pdf.exporting.PdfImageInfo; +import com.spire.pdf.graphics.PdfBitmap; + +import stirling.software.SPDF.utils.PdfUtils; + +//import com.spire.pdf.*; +@Controller +public class CompressController { + + private static final Logger logger = LoggerFactory.getLogger(CompressController.class); + + @GetMapping("/compress-pdf") + public String compressPdfForm(Model model) { + model.addAttribute("currentPage", "compress-pdf"); + return "compress-pdf"; + } + + @PostMapping("/compress-pdf") + public ResponseEntity compressPDF(@RequestParam("fileInput") MultipartFile pdfFile, + @RequestParam("imageCompressionLevel") String imageCompressionLevel) throws IOException { + + // Load a sample PDF document + PdfDocument document = new PdfDocument(); + document.loadFromBytes(pdfFile.getBytes()); + + // Compress PDF + document.getFileInfo().setIncrementalUpdate(false); + document.setCompressionLevel(PdfCompressionLevel.Best); + + // compress PDF Images + for (int i = 0; i < document.getPages().getCount(); i++) { + + PdfPageBase page = document.getPages().get(i); + PdfImageInfo[] images = page.getImagesInfo(); + if (images != null && images.length > 0) + for (int j = 0; j < images.length; j++) { + PdfImageInfo image = images[j]; + PdfBitmap bp = new PdfBitmap(image.getImage()); + // bp.setPngDirectToJpeg(true); + bp.setQuality(Integer.valueOf(imageCompressionLevel)); + + page.replaceImage(j, bp); + + } + } + + return PdfUtils.pdfDocToWebResponse(document, pdfFile.getName() + "_compressed.pdf"); + + } + +} diff --git a/src/main/java/stirling/software/SPDF/controller/MergeController.java b/src/main/java/stirling/software/SPDF/controller/MergeController.java new file mode 100644 index 000000000..1c0a2ec54 --- /dev/null +++ b/src/main/java/stirling/software/SPDF/controller/MergeController.java @@ -0,0 +1,77 @@ +package stirling.software.SPDF.controller; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.pdmodel.PDPage; +import org.apache.pdfbox.pdmodel.PDPageTree; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.core.io.InputStreamResource; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +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; + +@Controller +public class MergeController { + + private static final Logger logger = LoggerFactory.getLogger(MergeController.class); + + @GetMapping("/merge-pdfs") + public String hello(Model model) { + model.addAttribute("currentPage", "merge-pdfs"); + return "merge-pdfs"; + } + + @PostMapping("/merge-pdfs") + public ResponseEntity mergePdfs(@RequestParam("fileInput") MultipartFile[] files) + throws IOException { + // Read the input PDF files into PDDocument objects + List documents = new ArrayList<>(); + + // Loop through the files array and read each file into a PDDocument + for (MultipartFile file : files) { + documents.add(PDDocument.load(file.getInputStream())); + } + + PDDocument mergedDoc = mergeDocuments(documents); + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + mergedDoc.save(byteArrayOutputStream); + mergedDoc.close(); + + // Create an InputStreamResource from the merged PDF + InputStreamResource resource = new InputStreamResource( + new ByteArrayInputStream(byteArrayOutputStream.toByteArray())); + + // Return the merged PDF as a response + return ResponseEntity.ok().contentType(MediaType.APPLICATION_PDF).body(resource); + } + + private PDDocument mergeDocuments(List documents) throws IOException { + // Create a new empty document + PDDocument mergedDoc = new PDDocument(); + + // Iterate over the list of documents and add their pages to the merged document + for (PDDocument doc : documents) { + // 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) { + mergedDoc.addPage(page); + } + } + + // Return the merged document + return mergedDoc; + } + +} \ No newline at end of file diff --git a/src/main/java/stirling/software/SPDF/controller/OverlayImageController.java b/src/main/java/stirling/software/SPDF/controller/OverlayImageController.java new file mode 100644 index 000000000..281b9112f --- /dev/null +++ b/src/main/java/stirling/software/SPDF/controller/OverlayImageController.java @@ -0,0 +1,46 @@ +package stirling.software.SPDF.controller; + +import java.io.IOException; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +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 stirling.software.SPDF.utils.PdfUtils; + +@Controller +public class OverlayImageController { + + private static final Logger logger = LoggerFactory.getLogger(OverlayImageController.class); + + @GetMapping("/add-image") + public String overlayImage(Model model) { + model.addAttribute("currentPage", "add-image"); + return "add-image"; + } + + @PostMapping("/add-image") + public ResponseEntity overlayImage(@RequestParam("fileInput") MultipartFile pdfFile, + @RequestParam("fileInput2") MultipartFile imageFile, @RequestParam("x") float x, + @RequestParam("y") float y) { + try { + byte[] pdfBytes = pdfFile.getBytes(); + byte[] imageBytes = imageFile.getBytes(); + byte[] result = PdfUtils.overlayImage(pdfBytes, imageBytes, x, y); + + return PdfUtils.bytesToWebResponse(result, pdfFile.getName() + "_overlayed.pdf"); + } catch (IOException e) { + logger.error("Failed to add image to PDF", e); + return new ResponseEntity<>(HttpStatus.BAD_REQUEST); + } + } +} diff --git a/src/main/java/stirling/software/SPDF/controller/PdfController.java b/src/main/java/stirling/software/SPDF/controller/PdfController.java new file mode 100644 index 000000000..cff7df24a --- /dev/null +++ b/src/main/java/stirling/software/SPDF/controller/PdfController.java @@ -0,0 +1,43 @@ +package stirling.software.SPDF.controller; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.pdmodel.PDPage; +import org.apache.pdfbox.pdmodel.PDPageTree; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.core.io.InputStreamResource; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +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; + +@Controller +public class PdfController { + + private static final Logger logger = LoggerFactory.getLogger(PdfController.class); + + @GetMapping("/home") + public String root(Model model) { + return "redirect:/"; + } + + + @GetMapping("/") + public String home(Model model) { + model.addAttribute("currentPage", "home"); + return "home"; + } + + + +} \ No newline at end of file diff --git a/src/main/java/stirling/software/SPDF/controller/RearrangePagesPDFController.java b/src/main/java/stirling/software/SPDF/controller/RearrangePagesPDFController.java new file mode 100644 index 000000000..7750f6865 --- /dev/null +++ b/src/main/java/stirling/software/SPDF/controller/RearrangePagesPDFController.java @@ -0,0 +1,128 @@ +package stirling.software.SPDF.controller; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.pdmodel.PDPage; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +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 stirling.software.SPDF.utils.PdfUtils; + +@Controller +public class RearrangePagesPDFController { + + private static final Logger logger = LoggerFactory.getLogger(RearrangePagesPDFController.class); + + @GetMapping("/pdf-organizer") + public String pageOrganizer(Model model) { + model.addAttribute("currentPage", "pdf-organizer"); + return "pdf-organizer"; + } + + @GetMapping("/delete-pages") + public String pageDeleter(Model model) { + model.addAttribute("currentPage", "delete-pages"); + return "delete-pages"; + } + + @PostMapping("/delete-pages") + public ResponseEntity deletePages(@RequestParam("fileInput") MultipartFile pdfFile, + @RequestParam("pagesToDelete") String pagesToDelete) throws IOException { + + PDDocument document = PDDocument.load(pdfFile.getBytes()); + + // Split the page order string into an array of page numbers or range of numbers + String[] pageOrderArr = pagesToDelete.split(","); + + List pagesToRemove = pageOrderToString(pageOrderArr, document.getNumberOfPages()); + + for (int i = pagesToRemove.size() - 1; i >= 0; i--) { + int pageIndex = pagesToRemove.get(i); + document.removePage(pageIndex); + } + + return PdfUtils.pdfDocToWebResponse(document, pdfFile.getName() + "_removed_pages.pdf"); + + } + + private List pageOrderToString(String[] pageOrderArr, int totalPages) { + List newPageOrder = new ArrayList(); + // loop through the page order array + for (String element : pageOrderArr) { + // check if the element contains a range of pages + if (element.contains("-")) { + // split the range into start and end page + String[] range = element.split("-"); + int start = Integer.parseInt(range[0]); + int end = Integer.parseInt(range[1]); + // check if the end page is greater than total pages + if (end > totalPages) { + end = totalPages; + } + // loop through the range of pages + for (int j = start; j <= end; j++) { + // print the current index + newPageOrder.add(j - 1); + } + } else { + // if the element is a single page + newPageOrder.add(Integer.parseInt(element) - 1); + } + } + + return newPageOrder; + } + + @PostMapping("/rearrange-pages") + public ResponseEntity rearrangePages(@RequestParam("fileInput") MultipartFile pdfFile, + @RequestParam("pageOrder") String pageOrder) { + try { + // Load the input PDF + PDDocument document = PDDocument.load(pdfFile.getInputStream()); + + // Split the page order string into an array of page numbers or range of numbers + String[] pageOrderArr = pageOrder.split(","); + // int[] newPageOrder = new int[pageOrderArr.length]; + int totalPages = document.getNumberOfPages(); + + List newPageOrder = pageOrderToString(pageOrderArr, totalPages); + + // Create a new list to hold the pages in the new order + List newPages = new ArrayList<>(); + for (int i = 0; i < newPageOrder.size(); i++) { + newPages.add(document.getPage(newPageOrder.get(i))); + } + + // Remove all the pages from the original document + for (int i = document.getNumberOfPages() - 1; i >= 0; i--) { + document.removePage(i); + } + + // Add the pages in the new order + for (PDPage page : newPages) { + document.addPage(page); + } + + return PdfUtils.pdfDocToWebResponse(document, pdfFile.getName() + "_rearranged.pdf"); + } catch (IOException e) { + + logger.error("Failed rearranging documents", e); + return null; + } + } + +} diff --git a/src/main/java/stirling/software/SPDF/controller/RotationController.java b/src/main/java/stirling/software/SPDF/controller/RotationController.java new file mode 100644 index 000000000..42676f6dc --- /dev/null +++ b/src/main/java/stirling/software/SPDF/controller/RotationController.java @@ -0,0 +1,60 @@ +package stirling.software.SPDF.controller; + +import java.io.ByteArrayOutputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.Iterator; +import java.util.ListIterator; + +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.pdmodel.PDPage; +import org.apache.pdfbox.pdmodel.PDPageTree; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +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 stirling.software.SPDF.utils.PdfUtils; + +@Controller +public class RotationController { + + private static final Logger logger = LoggerFactory.getLogger(RotationController.class); + + @GetMapping("/rotate-pdf") + public String rotatePdfForm(Model model) { + model.addAttribute("currentPage", "rotate-pdf"); + return "rotate-pdf"; + } + + @PostMapping("/rotate-pdf") + public ResponseEntity rotatePDF(@RequestParam("fileInput") MultipartFile pdfFile, + @RequestParam("angle") String angle) throws IOException { + + // Load the PDF document + PDDocument document = PDDocument.load(pdfFile.getBytes()); + + // Get the list of pages in the document + PDPageTree pages = document.getPages(); + + // Rotate all pages by the specified angle + Iterator iterPage = pages.iterator(); + + while (iterPage.hasNext()) { + PDPage page = iterPage.next(); + page.setRotation(Integer.valueOf(angle)); + } + + return PdfUtils.pdfDocToWebResponse(document, pdfFile.getName() + "_rotated.pdf"); + + } + +} diff --git a/src/main/java/stirling/software/SPDF/controller/SplitPDFController.java b/src/main/java/stirling/software/SPDF/controller/SplitPDFController.java new file mode 100644 index 000000000..c283e5b52 --- /dev/null +++ b/src/main/java/stirling/software/SPDF/controller/SplitPDFController.java @@ -0,0 +1,142 @@ +package stirling.software.SPDF.controller; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.URI; +import java.nio.file.FileSystem; +import java.nio.file.FileSystems; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.pdmodel.PDPage; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.core.io.ByteArrayResource; +import org.springframework.core.io.Resource; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +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; + +@Controller +public class SplitPDFController { + + private static final Logger logger = LoggerFactory.getLogger(SplitPDFController.class); + + @GetMapping("/split-pdfs") + public String splitPdfForm(Model model) { + model.addAttribute("currentPage", "split-pdfs"); + return "split-pdfs"; + } + + @PostMapping("/split-pages") + public ResponseEntity splitPdf(@RequestParam("fileInput") MultipartFile file, + @RequestParam("pages") String pages) throws IOException { + // parse user input + + // open the pdf document + InputStream inputStream = file.getInputStream(); + PDDocument document = PDDocument.load(inputStream); + + List pageNumbers = new ArrayList<>(); + pages = pages.replaceAll("\\s+", ""); // remove whitespaces + if (pages.toLowerCase().equals("all")) { + for (int i = 0; i < document.getNumberOfPages(); i++) { + pageNumbers.add(i); + } + } else { + List pageNumbersStr = new ArrayList<>(Arrays.asList(pages.split(","))); + if (!pageNumbersStr.contains(String.valueOf(document.getNumberOfPages()))) { + String lastpage = String.valueOf(document.getNumberOfPages()); + pageNumbersStr.add(lastpage); + } + for (String page : pageNumbersStr) { + if (page.contains("-")) { + String[] range = page.split("-"); + int start = Integer.parseInt(range[0]); + int end = Integer.parseInt(range[1]); + for (int i = start; i <= end; i++) { + 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 splitDocumentsBoas = new ArrayList<>(); + int currentPage = 0; + for (int pageNumber : pageNumbers) { + try (PDDocument splitDocument = new PDDocument()) { + for (int i = currentPage; i < pageNumber; i++) { + PDPage page = document.getPage(i); + splitDocument.addPage(page); + logger.debug("Adding page {} to split document", i); + } + currentPage = pageNumber; + logger.debug("Setting current page to {}", currentPage); + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + splitDocument.save(baos); + + splitDocumentsBoas.add(baos); + } catch (Exception e) { + logger.error("Failed splitting documents and saving them", e); + throw e; + } + } + + // closing the original document + document.close(); + + // create the zip file + Path zipFile = Paths.get("split_documents.zip"); + URI uri = URI.create("jar:file:" + zipFile.toUri().getPath()); + Map env = new HashMap<>(); + env.put("create", "true"); + FileSystem zipfs = FileSystems.newFileSystem(uri, env); + + // loop through the split documents and write them to the zip file + for (int i = 0; i < splitDocumentsBoas.size(); i++) { + String fileName = "split_document_" + (i + 1) + ".pdf"; + ByteArrayOutputStream baos = splitDocumentsBoas.get(i); + byte[] pdf = baos.toByteArray(); + Path pathInZipfile = zipfs.getPath(fileName); + try (OutputStream os = Files.newOutputStream(pathInZipfile)) { + os.write(pdf); + logger.info("Wrote split document {} to zip file", fileName); + } catch (Exception e) { + logger.error("Failed writing to zip", e); + throw e; + } + } + zipfs.close(); + logger.info("Successfully created zip file with split documents: {}", zipFile.toString()); + byte[] data = Files.readAllBytes(zipFile); + ByteArrayResource resource = new ByteArrayResource(data); + new File("split_documents.zip").delete(); + // return the Resource in the response + return ResponseEntity.ok().header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=split_documents.zip") + .contentType(MediaType.APPLICATION_OCTET_STREAM).contentLength(resource.contentLength()).body(resource); + } +} diff --git a/src/main/java/stirling/software/SPDF/controller/converters/ConvertImgPDFController.java b/src/main/java/stirling/software/SPDF/controller/converters/ConvertImgPDFController.java new file mode 100644 index 000000000..5a943183c --- /dev/null +++ b/src/main/java/stirling/software/SPDF/controller/converters/ConvertImgPDFController.java @@ -0,0 +1,70 @@ +package stirling.software.SPDF.controller.converters; + +import java.io.IOException; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +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 stirling.software.SPDF.utils.PdfUtils; + +@Controller +public class ConvertImgPDFController { + + private static final Logger logger = LoggerFactory.getLogger(ConvertImgPDFController.class); + + @GetMapping("/img-to-pdf") + public String convertToPdfForm(Model model) { + model.addAttribute("currentPage", "img-to-pdf"); + return "convert/img-to-pdf"; + } + + @GetMapping("/pdf-to-img") + public String pdfToimgForm(Model model) { + model.addAttribute("currentPage", "pdf-to-img"); + return "convert/pdf-to-img"; + } + + @PostMapping("/img-to-pdf") + public ResponseEntity convertToPdf(@RequestParam("fileInput") MultipartFile file) throws IOException { + // Convert the file to PDF and get the resulting bytes + byte[] bytes = PdfUtils.convertToPdf(file.getInputStream()); + logger.info("File {} successfully converted to pdf", file.getOriginalFilename()); + + return PdfUtils.bytesToWebResponse(bytes, file.getName() + "_coverted.pdf"); + } + + @PostMapping("/pdf-to-img") + public ResponseEntity convertToImage(@RequestParam("fileInput") MultipartFile file, + @RequestParam("imageFormat") String imageFormat) throws IOException { + byte[] pdfBytes = file.getBytes(); + // returns bytes for image + byte[] result = PdfUtils.convertFromPdf(pdfBytes, imageFormat.toLowerCase()); + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.parseMediaType(getMediaType(imageFormat))); + headers.setCacheControl("must-revalidate, post-check=0, pre-check=0"); + ResponseEntity response = new ResponseEntity<>(result, headers, HttpStatus.OK); + return response; + } + + private String getMediaType(String imageFormat) { + if (imageFormat.equalsIgnoreCase("PNG")) + return "image/png"; + else if (imageFormat.equalsIgnoreCase("JPEG") || imageFormat.equalsIgnoreCase("JPG")) + return "image/jpeg"; + else if (imageFormat.equalsIgnoreCase("GIF")) + return "image/gif"; + else + return "application/octet-stream"; + } + +} diff --git a/src/main/java/stirling/software/SPDF/controller/security/PasswordController.java b/src/main/java/stirling/software/SPDF/controller/security/PasswordController.java new file mode 100644 index 000000000..1d7a79c51 --- /dev/null +++ b/src/main/java/stirling/software/SPDF/controller/security/PasswordController.java @@ -0,0 +1,92 @@ +package stirling.software.SPDF.controller.security; + +import java.io.ByteArrayOutputStream; +import java.io.FileOutputStream; +import java.io.IOException; + +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.pdmodel.encryption.AccessPermission; +import org.apache.pdfbox.pdmodel.encryption.StandardDecryptionMaterial; +import org.apache.pdfbox.pdmodel.encryption.StandardProtectionPolicy; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +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 stirling.software.SPDF.utils.PdfUtils; + +@Controller +public class PasswordController { + + private static final Logger logger = LoggerFactory.getLogger(PasswordController.class); + + @GetMapping("/add-password") + public String addPasswordForm(Model model) { + model.addAttribute("currentPage", "add-password"); + return "security/add-password"; + } + + @GetMapping("/remove-password") + public String removePasswordForm(Model model) { + model.addAttribute("currentPage", "remove-password"); + return "security/remove-password"; + } + + @GetMapping("/change-permissions") + public String permissionsForm(Model model) { + model.addAttribute("currentPage", "change-permissions"); + return "security/change-permissions"; + } + + @PostMapping("/remove-password") + public ResponseEntity compressPDF(@RequestParam("fileInput") MultipartFile fileInput, + @RequestParam(name = "password") String password) throws IOException { + PDDocument document = PDDocument.load(fileInput.getBytes(), password); + document.setAllSecurityToBeRemoved(true); + return PdfUtils.pdfDocToWebResponse(document, fileInput.getName() + "_password_removed.pdf"); + } + + @PostMapping("/add-password") + public ResponseEntity compressPDF(@RequestParam("fileInput") MultipartFile fileInput, + @RequestParam(defaultValue = "", name = "password") String password, + @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 = "canExtractForAccessibility") boolean canExtractForAccessibility, + @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 = "canPrint") boolean canPrint, + @RequestParam(defaultValue = "false", name = "canPrintFaithful") boolean canPrintFaithful) + throws IOException { + + PDDocument document = PDDocument.load(fileInput.getBytes()); + AccessPermission ap = new AccessPermission(); + + ap.setCanAssembleDocument(!canAssembleDocument); + ap.setCanExtractContent(!canExtractContent); + ap.setCanExtractForAccessibility(!canExtractForAccessibility); + ap.setCanFillInForm(!canFillInForm); + ap.setCanModify(!canModify); + ap.setCanModifyAnnotations(!canModifyAnnotations); + ap.setCanPrint(!canPrint); + ap.setCanPrintFaithful(!canPrintFaithful); + StandardProtectionPolicy spp = new StandardProtectionPolicy(password, password, ap); + spp.setEncryptionKeyLength(keyLength); + + spp.setPermissions(ap); + + document.protect(spp); + + return PdfUtils.pdfDocToWebResponse(document, fileInput.getName() + "_passworded.pdf"); + } + +} diff --git a/src/main/java/stirling/software/SPDF/controller/security/WatermarkController.java b/src/main/java/stirling/software/SPDF/controller/security/WatermarkController.java new file mode 100644 index 000000000..ba42a1d78 --- /dev/null +++ b/src/main/java/stirling/software/SPDF/controller/security/WatermarkController.java @@ -0,0 +1,83 @@ +package stirling.software.SPDF.controller.security; + +import java.awt.Color; +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.pdmodel.PDPage; +import org.apache.pdfbox.pdmodel.PDPageContentStream; +import org.apache.pdfbox.pdmodel.font.PDFont; +import org.apache.pdfbox.pdmodel.font.PDType1Font; +import org.apache.pdfbox.pdmodel.graphics.state.PDExtendedGraphicsState; +import org.apache.pdfbox.util.Matrix; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +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 stirling.software.SPDF.utils.PdfUtils; + +@Controller +public class WatermarkController { + + @GetMapping("/add-watermark") + public String addWatermarkForm(Model model) { + model.addAttribute("currentPage", "add-watermark"); + return "security/add-watermark"; + } + + @PostMapping("/add-watermark") + public ResponseEntity addWatermark(@RequestParam("pdfFile") MultipartFile pdfFile, + @RequestParam("watermarkText") String watermarkText, + @RequestParam(defaultValue = "30", name = "fontSize") float fontSize, + @RequestParam(defaultValue = "0", name = "rotation") float rotation, + @RequestParam(defaultValue = "50", name = "widthSpacer") int widthSpacer, + @RequestParam(defaultValue = "50", name = "heightSpacer") int heightSpacer) throws IOException { + + // Load the input PDF + PDDocument document = PDDocument.load(pdfFile.getInputStream()); + + // Create a page in the document + for (PDPage page : document.getPages()) { + // Get the page's content stream + PDPageContentStream contentStream = new PDPageContentStream(document, page, + PDPageContentStream.AppendMode.APPEND, true); + + // Set font of watermark + PDFont font = PDType1Font.HELVETICA_BOLD; + contentStream.beginText(); + contentStream.setFont(font, fontSize); + contentStream.setNonStrokingColor(Color.LIGHT_GRAY); + + // Set size and location of watermark + float pageWidth = page.getMediaBox().getWidth(); + float pageHeight = page.getMediaBox().getHeight(); + float watermarkWidth = widthSpacer + font.getStringWidth(watermarkText) * fontSize / 1000; + float watermarkHeight = heightSpacer + fontSize; + int watermarkRows = (int) (pageHeight / watermarkHeight + 1); + int watermarkCols = (int) (pageWidth / watermarkWidth + 1); + + // Add the watermark text + for (int i = 0; i < watermarkRows; i++) { + 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.close(); + } + return PdfUtils.pdfDocToWebResponse(document, pdfFile.getName() + "_watermarked.pdf"); + } +} diff --git a/src/main/java/stirling/software/SPDF/utils/PdfUtils.java b/src/main/java/stirling/software/SPDF/utils/PdfUtils.java new file mode 100644 index 000000000..43c235d62 --- /dev/null +++ b/src/main/java/stirling/software/SPDF/utils/PdfUtils.java @@ -0,0 +1,168 @@ +package stirling.software.SPDF.utils; + +import java.awt.image.BufferedImage; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.Iterator; + +import javax.imageio.IIOImage; +import javax.imageio.ImageIO; +import javax.imageio.ImageWriter; +import javax.imageio.stream.ImageOutputStream; + +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.pdmodel.PDPage; +import org.apache.pdfbox.pdmodel.PDPageContentStream; +import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject; +import org.apache.pdfbox.rendering.ImageType; +import org.apache.pdfbox.rendering.PDFRenderer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; + +import com.spire.pdf.PdfDocument; + +public class PdfUtils { + + private static final Logger logger = LoggerFactory.getLogger(PdfUtils.class); + + public static byte[] convertToPdf(InputStream imageStream) throws IOException { + + // Create a File object for the image + File imageFile = new File("image.jpg"); + + try (FileOutputStream fos = new FileOutputStream(imageFile); InputStream input = imageStream) { + byte[] buffer = new byte[1024]; + int len; + // Read from the input stream and write to the file + while ((len = input.read(buffer)) != -1) { + fos.write(buffer, 0, len); + } + logger.info("Image successfully written to file: {}", imageFile.getAbsolutePath()); + } catch (IOException e) { + logger.error("Error writing image to file: {}", imageFile.getAbsolutePath(), e); + throw e; + } + + try (PDDocument doc = new PDDocument()) { + // Create a new PDF page + PDPage page = new PDPage(); + doc.addPage(page); + + // Create an image object from the image file + PDImageXObject image = PDImageXObject.createFromFileByContent(imageFile, doc); + + try (PDPageContentStream contentStream = new PDPageContentStream(doc, page)) { + // Draw the image onto the page + contentStream.drawImage(image, 0, 0); + logger.info("Image successfully added to PDF"); + } catch (IOException e) { + logger.error("Error adding image to PDF", e); + throw e; + } + + // Create a ByteArrayOutputStream to save the PDF to + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + doc.save(byteArrayOutputStream); + logger.info("PDF successfully saved to byte array"); + return byteArrayOutputStream.toByteArray(); + } + } + + public static byte[] convertFromPdf(byte[] inputStream, String imageType) throws IOException { + try (PDDocument document = PDDocument.load(new ByteArrayInputStream(inputStream))) { + // Create a PDFRenderer to convert the PDF to an image + PDFRenderer pdfRenderer = new PDFRenderer(document); + BufferedImage bim = pdfRenderer.renderImageWithDPI(0, 300, ImageType.RGB); + + // Get an ImageWriter for the specified image type + Iterator writers = ImageIO.getImageWritersByFormatName(imageType); + ImageWriter writer = writers.next(); + + // Create a ByteArrayOutputStream to save the image to + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + try (ImageOutputStream ios = ImageIO.createImageOutputStream(baos)) { + writer.setOutput(ios); + // Write the image to the output stream + writer.write(new IIOImage(bim, null, null)); + // Log that the image was successfully written to the byte array + logger.info("Image successfully written to byte array"); + } + return baos.toByteArray(); + } catch (IOException e) { + // Log an error message if there is an issue converting the PDF to an image + logger.error("Error converting PDF to image", e); + throw e; + } + } + + public static byte[] overlayImage(byte[] pdfBytes, byte[] imageBytes, float x, float y) throws IOException { + + try (PDDocument document = PDDocument.load(new ByteArrayInputStream(pdfBytes))) { + // Get the first page of the PDF + PDPage page = document.getPage(0); + try (PDPageContentStream contentStream = new PDPageContentStream(document, page, + PDPageContentStream.AppendMode.APPEND, true)) { + // Create an image object from the image bytes + PDImageXObject image = PDImageXObject.createFromByteArray(document, imageBytes, ""); + // Draw the image onto the page at the specified x and y coordinates + contentStream.drawImage(image, x, y); + logger.info("Image successfully overlayed onto PDF"); + } + // Create a ByteArrayOutputStream to save the PDF to + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + document.save(baos); + logger.info("PDF successfully saved to byte array"); + return baos.toByteArray(); + } catch (IOException e) { + // Log an error message if there is an issue overlaying the image onto the PDF + logger.error("Error overlaying image onto PDF", e); + throw e; + } + } + + public static ResponseEntity pdfDocToWebResponse(PdfDocument document, String docName) throws IOException { + + // Open Byte Array and save document to it + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + document.saveToStream(baos); + // Close the document + document.close(); + + return PdfUtils.boasToWebResponse(baos, docName); + } + + public static ResponseEntity pdfDocToWebResponse(PDDocument document, String docName) throws IOException { + + // Open Byte Array and save document to it + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + document.save(baos); + // Close the document + document.close(); + + return PdfUtils.boasToWebResponse(baos, docName); + } + + public static ResponseEntity boasToWebResponse(ByteArrayOutputStream baos, String docName) + throws IOException { + return PdfUtils.bytesToWebResponse(baos.toByteArray(), docName); + + } + + public static ResponseEntity bytesToWebResponse(byte[] bytes, String docName) throws IOException { + + // Return the PDF as a response + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_PDF); + headers.setContentLength(bytes.length); + headers.setContentDispositionFormData("attachment", docName); + return new ResponseEntity<>(bytes, headers, HttpStatus.OK); + } +} diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties new file mode 100644 index 000000000..d49c5abdc --- /dev/null +++ b/src/main/resources/application.properties @@ -0,0 +1,11 @@ +spring.http.multipart.max-file-size=1GB +spring.http.multipart.max-request-size=1GB + +multipart.enabled=true +multipart.max-file-size=1000MB +multipart.max-request-size=1000MB + +spring.servlet.multipart.max-file-size=1000MB +spring.servlet.multipart.max-request-size=1000MB + +server.forward-headers-strategy=NATIVE diff --git a/src/main/resources/log4j.properties b/src/main/resources/log4j.properties new file mode 100644 index 000000000..d6a58a9f7 --- /dev/null +++ b/src/main/resources/log4j.properties @@ -0,0 +1,8 @@ +log4j.rootLogger=ERROR,stdout +log4j.logger.com.endeca=INFO +# Logger for crawl metrics +log4j.logger.com.endeca.itl.web.metrics=INFO + +log4j.appender.stdout=org.apache.log4j.ConsoleAppender +log4j.appender.stdout.layout=org.apache.log4j.PatternLayout +log4j.appender.stdout.layout.ConversionPattern=%p\t%d{ISO8601}\t%r\t%c\t[%t]\t%m%n \ No newline at end of file diff --git a/src/main/resources/static/dark-mode.css b/src/main/resources/static/dark-mode.css new file mode 100644 index 000000000..6c528702b --- /dev/null +++ b/src/main/resources/static/dark-mode.css @@ -0,0 +1,23 @@ +/* Dark Mode Styles */ +body { + background-color: #333; + color: #fff; +} + +.dark-card { + background-color: #333 !important; + color: white; +} +.jumbotron { + background-color: #222; /* or any other dark color */ + color: #fff; /* or any other light color */ +} + +.list-group { + background-color: #222 !important; + color: fff !important; +} +.list-group-item { + background-color: #222 !important; + color: fff !important; +} \ No newline at end of file diff --git a/src/main/resources/static/favicon.png b/src/main/resources/static/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..b17ecf108c49c8245de410f7d8355dca8b8612bf GIT binary patch literal 4356 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%0AS^TJYo@G4R3bP ze$X6%-gN*Zp-KQ~?C(dJw^-n)0jV_rC()vQzwn&`K*Px&&qq1zahuI4>Y)Ij_}qB` zFAD(mPT*MnZS~8UV*uW5cyj1Z!wi#ETNSLq7CE<|XdieKSV0RsTG;&{Ge zdCZF){evqw-sV}wcHA>!P)QR2o{I!vxQYw+;Q|)2m_=O9sq7Wl2TbM-oXJ13#PWqs zW1ZkO{x^TXfl3yrH1tKxnA^~WLueF-4sLXAc%I`9bw=;ud znZxPKV;-k6hxaj^eO;Ui9(o@~GA#54D2@xi4of{GrHK&eib z#1Q*+#H?OwwviME^8W8CD9}PFWk@{_O7ty!CB+P(58*;6Qnr*cgdPuNN|4vXq4WCK zx_^yz1uCSdA@Voq1;xsfvWCbrpj=5PmcoX_JCOtrG*D&>hotx4A0FvRDQ^h;I6PA# z)=Pmk_a=9fKkTYI{v}1$*hkR=9!us3=*B~7}>lrD#Mt%amD-mm?@S6CKYTx|XRf>wU#>6rE1GGgz>WIHd z00H~&=!^i>2jvn%pl$!&2w2~fS;K+(l=D^pUMN$#gn{@B?tvznAV*F3?pjQOMw%*- z`0PianI4r;eDrK+sAnY@pPUa(B{PTp@YB#(Q=~cQanmmsnrkdhOHiJ5{IU@OWujWb z^1xO4^!uCBU>uRa6uTTl5h7hF$4)4I<(WEU(H({=0l6|v+@wz~B+ z9(t)K-j>kM^F~~aI8y?$aY&*E@fETVr)rCQoQ(6O9WSWc(m9;E5-a02{)tFJ9S0{B?n72Qb@!aA1;!Z+!A^`ZeVk8?Zo*pB z8Ww&LM$6Z}6MY*m8}iZ832dnV4R?#2F>zItL5{`dL#xTs+ zV9a0_3NRGO5=cPoo$wAOjsG0A17?H{mfXMTv*|7M?^2euu{}7_Fq> z`YH*1kcqiifh{-~bLa=L1t@IR0000 \ No newline at end of file diff --git a/src/main/resources/static/general.css b/src/main/resources/static/general.css new file mode 100644 index 000000000..20e97c598 --- /dev/null +++ b/src/main/resources/static/general.css @@ -0,0 +1,5 @@ +#footer { + position: absolute; + bottom: 0; + width: 100%; +} diff --git a/src/main/resources/templates/add-image.html b/src/main/resources/templates/add-image.html new file mode 100644 index 000000000..41c61da7d --- /dev/null +++ b/src/main/resources/templates/add-image.html @@ -0,0 +1,51 @@ + + + + + +S-PDF Add-Image + + + +
+
+
+
+
+
+

Add image to PDF (Work in progress)

+ + + +
+ +
+ +
+
+ +
+
+ +
+
+ +
+ +
+ + +
+
+
+
+ + + \ No newline at end of file diff --git a/src/main/resources/templates/compress-pdf.html b/src/main/resources/templates/compress-pdf.html new file mode 100644 index 000000000..deff6254d --- /dev/null +++ b/src/main/resources/templates/compress-pdf.html @@ -0,0 +1,44 @@ + + + + + +S-PDF Add-Image + + + +
+
+
+
+
+
+

Compress PDF

+
+

Warning: This process can take up to a minute depending on + file-size

+
+ +
+
+ +
+ + +
+ + +
+
+
+
+ + + \ No newline at end of file diff --git a/src/main/resources/templates/convert/img-to-pdf.html b/src/main/resources/templates/convert/img-to-pdf.html new file mode 100644 index 000000000..c76cebb2b --- /dev/null +++ b/src/main/resources/templates/convert/img-to-pdf.html @@ -0,0 +1,36 @@ + + + + +S-PDF ConvertToPDF + + + +
+
+
+
+
+
+

Image to PDF

+ +
+
+ +
+
+
+ + +
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/src/main/resources/templates/convert/pdf-to-img.html b/src/main/resources/templates/convert/pdf-to-img.html new file mode 100644 index 000000000..b1fc0e10c --- /dev/null +++ b/src/main/resources/templates/convert/pdf-to-img.html @@ -0,0 +1,41 @@ + + + + +S-PDF ConvertToPDF + + + +
+ +
+
+
+
+
+

PDF to Image

+
+
+ +
+
+ +
+ +
+ + +
+
+
+
+ + \ No newline at end of file diff --git a/src/main/resources/templates/delete-pages.html b/src/main/resources/templates/delete-pages.html new file mode 100644 index 000000000..a8722fc60 --- /dev/null +++ b/src/main/resources/templates/delete-pages.html @@ -0,0 +1,40 @@ + + + + +S-PDF Page Remover + + + +
+
+
+
+
+
+

PDF Page remover

+ +
+
+ +
+
+ +
+ +
+ + +
+
+
+
+ + \ No newline at end of file diff --git a/src/main/resources/templates/fragments/common.html b/src/main/resources/templates/fragments/common.html new file mode 100644 index 000000000..576f9f8c6 --- /dev/null +++ b/src/main/resources/templates/fragments/common.html @@ -0,0 +1,97 @@ + + + + + + + + + + + + + +
+
+ + + + + +
\ No newline at end of file diff --git a/src/main/resources/templates/fragments/footer.html b/src/main/resources/templates/fragments/footer.html new file mode 100644 index 000000000..641035f57 --- /dev/null +++ b/src/main/resources/templates/fragments/footer.html @@ -0,0 +1,11 @@ +
+ + +
\ No newline at end of file diff --git a/src/main/resources/templates/fragments/navbar.html b/src/main/resources/templates/fragments/navbar.html new file mode 100644 index 000000000..159c7a5b0 --- /dev/null +++ b/src/main/resources/templates/fragments/navbar.html @@ -0,0 +1,87 @@ + \ No newline at end of file diff --git a/src/main/resources/templates/home.html b/src/main/resources/templates/home.html new file mode 100644 index 000000000..58115d6ce --- /dev/null +++ b/src/main/resources/templates/home.html @@ -0,0 +1,106 @@ + + + + + +S-PDF + + + +
+ +
+
+

Stirling PDF

+

Your locally hosted one-stop-shop for all your + PDF needs. (Made 100% in ChatGPT in 1 day as a experiment)

+
+
+ + +
+
+
+
+
+
Merge PDFs
+

Easily merge multiple PDFs into one.

+ Go +
+
+
+
+
+
+
Split PDFs
+

Split PDFs into multiple documents

+ Go +
+
+
+
+
+
+
Add image to PDF
+

Adds image/watermark to a PDF

+ Go +
+
+
+
+
+
+
+
+
+
Convert to/from PDF
+

Convert images to/from PDF.

+ Go +
+
+
+ +
+
+
+
PDF Organizer
+

Remove/Rearrange pages in any order

+ Go +
+
+
+ +
+
+
+
Rotate PDFs
+

Easily rotate your PDFs.

+ Go +
+
+
+ +
+
+
+ + + + + +
+
+
+
Compress PDFs
+

Compress PDFs to reduce their file size.

+ Go +
+
+
+ +
+
+
+ + + \ No newline at end of file diff --git a/src/main/resources/templates/merge-pdfs.html b/src/main/resources/templates/merge-pdfs.html new file mode 100644 index 000000000..5e377dda5 --- /dev/null +++ b/src/main/resources/templates/merge-pdfs.html @@ -0,0 +1,133 @@ + + + + +S-PDF MergePDFs + + + + + +
+
+
+
+
+
+

Merge multiple PDFs (2+)

+
+
+ +
+ +
+
+
+
    +
    +
    + +
    +
    + + + + + + + + + + +
    +
    +
    +
    + + \ No newline at end of file diff --git a/src/main/resources/templates/pdf-organizer.html b/src/main/resources/templates/pdf-organizer.html new file mode 100644 index 000000000..0b869ca9e --- /dev/null +++ b/src/main/resources/templates/pdf-organizer.html @@ -0,0 +1,42 @@ + + + + +S-PDF Organizer + + + +
    +
    +
    +
    +
    +
    +

    PDF Page Organizer

    + + + +
    +
    + +
    +
    + +
    + +
    + + +
    +
    +
    +
    + + \ No newline at end of file diff --git a/src/main/resources/templates/rotate-pdf.html b/src/main/resources/templates/rotate-pdf.html new file mode 100644 index 000000000..7c97db630 --- /dev/null +++ b/src/main/resources/templates/rotate-pdf.html @@ -0,0 +1,47 @@ + + + + + +S-PDF Add-Image + + + +
    +
    +
    +
    +
    +
    +

    Rotate PDF

    + + + +
    +
    + +
    + +
    + +
    + + +
    +
    +
    +
    + + + \ No newline at end of file diff --git a/src/main/resources/templates/security/add-password.html b/src/main/resources/templates/security/add-password.html new file mode 100644 index 000000000..1f50d58cf --- /dev/null +++ b/src/main/resources/templates/security/add-password.html @@ -0,0 +1,103 @@ + + + + + +S-PDF Add-Image + + + +
    +
    +
    +
    +
    +
    +

    Add password (Encrypt)

    + +
    +
    + +
    + +
    +
    +
    + +
    +
    + Higher values are + stronger, but lower values have better compatibility. +
    +
    + +
    + +
    +
    + +
    +
    + +
    +
    + +
    +
    + +
    +
    + +
    +
    + +
    +
    + +
    + +
    +
    +
    + +
    + +
    +
    +
    +
    +
    + + diff --git a/src/main/resources/templates/security/add-watermark.html b/src/main/resources/templates/security/add-watermark.html new file mode 100644 index 000000000..cf32b3cfe --- /dev/null +++ b/src/main/resources/templates/security/add-watermark.html @@ -0,0 +1,56 @@ + + + + + +S-PDF Add Watermark + + + +
    +
    +
    +
    +
    +
    +

    Add Watermark

    + +
    +
    + +
    + +
    +
    +
    + + +
    +
    + + +
    +
    + + +
    +
    + + +
    +
    + + +
    +
    + +
    +
    +
    +
    +
    +
    + + diff --git a/src/main/resources/templates/security/change-permissions.html b/src/main/resources/templates/security/change-permissions.html new file mode 100644 index 000000000..384539508 --- /dev/null +++ b/src/main/resources/templates/security/change-permissions.html @@ -0,0 +1,92 @@ + + + + + +S-PDF Add-Image + + + +
    +
    +
    +
    +
    +
    +

    Change permissions (Warning to have these permissions be + unchangeable it is recommended to set them with a password via the + add-password page)

    + +
    +
    + +
    + +
    +
    +
    + +
    + +
    +
    + +
    +
    + +
    +
    + +
    +
    + +
    +
    + +
    +
    + +
    +
    + +
    + +
    +
    +
    + +
    + +
    +
    +
    +
    +
    + + diff --git a/src/main/resources/templates/security/remove-password.html b/src/main/resources/templates/security/remove-password.html new file mode 100644 index 000000000..8c3740e3c --- /dev/null +++ b/src/main/resources/templates/security/remove-password.html @@ -0,0 +1,38 @@ + + + + + +S-PDF Add-Image + + + +
    +
    +
    +
    +
    +
    +

    Remove password (Decrypt)

    + +
    +
    + +
    + +
    +
    +
    + +
    +
    +
    +
    +
    +
    + + diff --git a/src/main/resources/templates/split-pdfs.html b/src/main/resources/templates/split-pdfs.html new file mode 100644 index 000000000..f6378aabf --- /dev/null +++ b/src/main/resources/templates/split-pdfs.html @@ -0,0 +1,49 @@ + + + + +S-PDF Split PDFs + + + +
    +
    +
    +
    +
    +
    +

    Split PDF

    +

    The numbers you select are the page number you wish to do a + split on

    +

    As such selecting 1,3,7-8 would split a 10 page document into + 6 separate PDFS with:

    +

    Document #1: Page 1

    +

    Document #2: Page 2 and 3

    +

    Document #3: Page 4, 5 and 6

    +

    Document #4: Page 7

    +

    Document #5: Page 8

    +

    Document #6: Page 9 and 10

    + +
    +
    + +
    + +
    + +
    +
    + +
    + +
    +
    +
    +
    + + \ No newline at end of file