From ca1aa75208fc4e87bd1680560455919763dec981 Mon Sep 17 00:00:00 2001 From: Anthony Stirling <77850077+Frooodle@users.noreply.github.com> Date: Fri, 17 Mar 2023 20:21:52 +0000 Subject: [PATCH] chnages testing new stuff --- build.gradle | 22 +++- .../software/SPDF/config/AppConfig.java | 2 +- .../SPDF/controller/CompressController.java | 111 +++++++++++++---- .../converters/ConvertCsvController.java | 79 ++++++++++++ .../converters/ConvertDocController.java | 43 +++++++ .../converters/ConvertHtmlController.java | 54 ++++++++ .../converters/ConvertPPTController.java | 79 ++++++++++++ .../converters/ConvertTextController.java | 63 ++++++++++ .../converters/ConvertXlsxController.java | 115 +++++++++++++++--- .../security/WatermarkController.java | 72 +++++++++++ .../software/SPDF/utils/PdfUtils.java | 12 +- .../software/SPDF/utils/WatermarkRemover.java | 69 +++++++++++ .../resources/templates/compress-pdf.html | 64 ++++++++-- .../templates/fragments/errorBanner.html | 79 +++++++----- .../resources/templates/fragments/navbar.html | 19 ++- .../templates/security/remove-watermark.html | 2 +- 16 files changed, 768 insertions(+), 117 deletions(-) create mode 100644 src/main/java/stirling/software/SPDF/controller/converters/ConvertCsvController.java create mode 100644 src/main/java/stirling/software/SPDF/controller/converters/ConvertDocController.java create mode 100644 src/main/java/stirling/software/SPDF/controller/converters/ConvertHtmlController.java create mode 100644 src/main/java/stirling/software/SPDF/controller/converters/ConvertPPTController.java create mode 100644 src/main/java/stirling/software/SPDF/controller/converters/ConvertTextController.java create mode 100644 src/main/java/stirling/software/SPDF/utils/WatermarkRemover.java diff --git a/build.gradle b/build.gradle index 5d46bf46d..155eb50cd 100644 --- a/build.gradle +++ b/build.gradle @@ -20,16 +20,26 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-web' implementation 'org.springframework.boot:spring-boot-starter-thymeleaf' testImplementation 'org.springframework.boot:spring-boot-starter-test' - implementation 'org.apache.pdfbox:pdfbox:2.0.27' - //implementation 'org.apache.pdfbox:preflight:2.0.27' - implementation 'org.apache.logging.log4j:log4j-core:2.20.0' - implementation 'e-iceblue:spire.pdf.free:5.1.0' - implementation 'org.apache.poi:poi:5.2.3' - implementation 'org.apache.poi:poi-ooxml:5.2.3' + implementation 'org.apache.xmlgraphics:batik-transcoder:1.14' + implementation 'org.apache.logging.log4j:log4j-core:2.20.0' + + //general PDF + implementation 'org.apache.pdfbox:pdfbox:2.0.27' implementation 'com.itextpdf:itextpdf:5.5.13.3' + //xml conversions and others + implementation 'org.apache.poi:poi:5.2.3' + implementation 'org.apache.poi:poi-scratchpad:5.2.3' + implementation 'org.apache.poi:poi-ooxml:5.2.3' + implementation 'com.itextpdf.tool:xmlworker:5.5.13.3' + //docx conversions + implementation('org.docx4j:docx4j:6.1.2') { + exclude group: 'org.slf4j', module: 'slf4j-reload4j' + } + implementation 'org.docx4j:docx4j-export-fo:11.2.9' + } jar { diff --git a/src/main/java/stirling/software/SPDF/config/AppConfig.java b/src/main/java/stirling/software/SPDF/config/AppConfig.java index 5b1242a28..bdea69045 100644 --- a/src/main/java/stirling/software/SPDF/config/AppConfig.java +++ b/src/main/java/stirling/software/SPDF/config/AppConfig.java @@ -9,6 +9,6 @@ public class AppConfig { @Bean(name = "appVersion") public String appVersion() { String version = getClass().getPackage().getImplementationVersion(); - return (version != null) ? version : "Develop"; + return (version != null) ? version : "0.3.3"; } } \ No newline at end of file diff --git a/src/main/java/stirling/software/SPDF/controller/CompressController.java b/src/main/java/stirling/software/SPDF/controller/CompressController.java index 47b659358..9a9d45b1b 100644 --- a/src/main/java/stirling/software/SPDF/controller/CompressController.java +++ b/src/main/java/stirling/software/SPDF/controller/CompressController.java @@ -1,9 +1,23 @@ package stirling.software.SPDF.controller; +import java.awt.Graphics2D; +import java.awt.image.BufferedImage; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; import java.io.IOException; +import java.io.InputStream; +import javax.imageio.ImageIO; + +import org.apache.pdfbox.cos.COSName; +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.pdmodel.PDPage; +import org.apache.pdfbox.pdmodel.PDResources; +import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; @@ -12,11 +26,9 @@ 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 com.itextpdf.text.DocumentException; +import com.itextpdf.text.pdf.PdfReader; +import com.itextpdf.text.pdf.PdfStamper; import stirling.software.SPDF.utils.PdfUtils; @@ -32,36 +44,81 @@ public class CompressController { 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()); + public ResponseEntity compressPDF( + @RequestParam("fileInput") MultipartFile pdfFile, + @RequestParam(value = "compressPDF", defaultValue = "false") boolean compressPDF, + @RequestParam(value = "compressImages", defaultValue = "false") boolean compressImages, + @RequestParam(value = "useLossyCompression", defaultValue = "false") boolean useLossyCompression, + @RequestParam(value = "resolutionPercentage", defaultValue = "50") int resolutionPercentage) { - // Compress PDF - document.getFileInfo().setIncrementalUpdate(false); - document.setCompressionLevel(PdfCompressionLevel.Best); + ByteArrayOutputStream baosPDFBox = new ByteArrayOutputStream(); + + try (InputStream is = pdfFile.getInputStream(); + PDDocument document = PDDocument.load(is)) { - // 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); + if (compressImages) { + for (PDPage page : document.getPages()) { + PDResources resources = page.getResources(); + for (COSName cosName : resources.getXObjectNames()) { + if (resources.isImageXObject(cosName)) { + PDImageXObject image = (PDImageXObject) resources.getXObject(cosName); + BufferedImage bufferedImage = image.getImage(); + BufferedImage resizedImage = resizeImage(bufferedImage, resolutionPercentage); + if (useLossyCompression) { + File tempFile = File.createTempFile("pdfbox", ".jpg"); + ImageIO.write(resizedImage, "jpg", tempFile); + PDImageXObject newImage = PDImageXObject.createFromFile(tempFile.getAbsolutePath(), document); + resources.put(cosName, newImage); + } else { + File tempFile = File.createTempFile("pdfbox", ".png"); + ImageIO.write(resizedImage, "png", tempFile); + PDImageXObject newImage = PDImageXObject.createFromFile(tempFile.getAbsolutePath(), document); + resources.put(cosName, newImage); + } + } + } } + } + + document.save(baosPDFBox); + + } catch (IOException e) { + e.printStackTrace(); + return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR); } - return PdfUtils.pdfDocToWebResponse(document, pdfFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_compressed.pdf"); + try (ByteArrayInputStream baisPDFBox = new ByteArrayInputStream(baosPDFBox.toByteArray()); + ByteArrayOutputStream baosFinal = new ByteArrayOutputStream()) { + PdfReader reader = new PdfReader(baisPDFBox); + PdfStamper stamper = new PdfStamper(reader, baosFinal); + + if (compressPDF) { + stamper.setFullCompression(); + } + + stamper.close(); + reader.close(); + + return PdfUtils.boasToWebResponse(baosFinal, pdfFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_compressed.pdf"); + } catch (IOException | DocumentException e) { + e.printStackTrace(); + return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR); + } } + + private BufferedImage resizeImage(BufferedImage originalImage, int resolutionPercentage) { + int newWidth = originalImage.getWidth() * resolutionPercentage / 100; + int newHeight = originalImage.getHeight() * resolutionPercentage / 100; + BufferedImage resizedImage = new BufferedImage(newWidth, newHeight, originalImage.getType()); + Graphics2D g = resizedImage.createGraphics(); + g.drawImage(originalImage, 0, 0, newWidth, newHeight, null); + g.dispose(); + return resizedImage; + } } diff --git a/src/main/java/stirling/software/SPDF/controller/converters/ConvertCsvController.java b/src/main/java/stirling/software/SPDF/controller/converters/ConvertCsvController.java new file mode 100644 index 000000000..d374c78b4 --- /dev/null +++ b/src/main/java/stirling/software/SPDF/controller/converters/ConvertCsvController.java @@ -0,0 +1,79 @@ +package stirling.software.SPDF.controller.converters; + +import java.io.BufferedReader; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; + +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.itextpdf.text.Document; +import com.itextpdf.text.DocumentException; +import com.itextpdf.text.Paragraph; +import com.itextpdf.text.pdf.PdfPCell; +import com.itextpdf.text.pdf.PdfPTable; +import com.itextpdf.text.pdf.PdfWriter; + +import stirling.software.SPDF.utils.PdfUtils; + +@Controller +public class ConvertCsvController { + + + @GetMapping("/csv-to-pdf") + public String cinvertToPDF(Model model) { + model.addAttribute("currentPage", "xlsx-to-pdf"); + return "convert/xlsx-to-pdf"; + } + + + + @PostMapping("/csv-to-pdf") + public ResponseEntity convertCsvToPdf(@RequestParam("fileInput") MultipartFile csvFile) throws IOException, DocumentException { + // Create PDF document + Document document = new Document(); + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + PdfWriter.getInstance(document, outputStream); + document.open(); + + // Read CSV file + InputStreamReader inputStreamReader = new InputStreamReader(csvFile.getInputStream(), StandardCharsets.UTF_8); + BufferedReader bufferedReader = new BufferedReader(inputStreamReader); + + // Create PDF table from CSV content + PdfPTable table = null; + String csvRow; + while ((csvRow = bufferedReader.readLine()) != null) { + String[] csvRowCells = csvRow.split(","); // Assuming comma as a delimiter + + if (table == null) { + table = new PdfPTable(csvRowCells.length); + } + + for (String cellValue : csvRowCells) { + PdfPCell pdfCell = new PdfPCell(new Paragraph(cellValue)); + table.addCell(pdfCell); + } + } + + if (table != null) { + document.add(table); + } + + // Close BufferedReader, document, and output stream + bufferedReader.close(); + document.close(); + outputStream.close(); + + return PdfUtils.boasToWebResponse(outputStream, csvFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_convertedToPDF.pdf"); + } + + +} diff --git a/src/main/java/stirling/software/SPDF/controller/converters/ConvertDocController.java b/src/main/java/stirling/software/SPDF/controller/converters/ConvertDocController.java new file mode 100644 index 000000000..10dd6c06e --- /dev/null +++ b/src/main/java/stirling/software/SPDF/controller/converters/ConvertDocController.java @@ -0,0 +1,43 @@ +package stirling.software.SPDF.controller.converters; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +import org.docx4j.Docx4J; +import org.docx4j.openpackaging.exceptions.Docx4JException; +import org.docx4j.openpackaging.packages.WordprocessingMLPackage; +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 ConvertDocController { + + + @GetMapping("/docx-to-pdf") + public String cinvertToPDF(Model model) { + model.addAttribute("currentPage", "xlsx-to-pdf"); + return "convert/xlsx-to-pdf"; + } + + @PostMapping("/docx-to-pdf") + public ResponseEntity convertDocxToPdf(@RequestParam("fileInput") MultipartFile docxFile) throws IOException, Docx4JException { + // Load WordprocessingMLPackage + WordprocessingMLPackage wordMLPackage = WordprocessingMLPackage.load(docxFile.getInputStream()); + + // Create PDF output stream + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + + // Convert DOCX to PDF + Docx4J.toPDF(wordMLPackage, outputStream); + + return PdfUtils.boasToWebResponse(outputStream, docxFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_convertedToPDF.pdf"); + } + +} diff --git a/src/main/java/stirling/software/SPDF/controller/converters/ConvertHtmlController.java b/src/main/java/stirling/software/SPDF/controller/converters/ConvertHtmlController.java new file mode 100644 index 000000000..fe5bc34ca --- /dev/null +++ b/src/main/java/stirling/software/SPDF/controller/converters/ConvertHtmlController.java @@ -0,0 +1,54 @@ +package stirling.software.SPDF.controller.converters; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; + +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.itextpdf.text.Document; +import com.itextpdf.text.DocumentException; +import com.itextpdf.text.pdf.PdfWriter; +import com.itextpdf.tool.xml.XMLWorkerHelper; + +import stirling.software.SPDF.utils.PdfUtils; + +@Controller +public class ConvertHtmlController { + + + @GetMapping("//html-to-pdf") + public String cinvertToPDF(Model model) { + model.addAttribute("currentPage", "xlsx-to-pdf"); + return "convert/xlsx-to-pdf"; + } + + @PostMapping("/html-to-pdf") + public ResponseEntity convertHtmlToPdf(@RequestParam("fileInput") MultipartFile htmlFile) throws IOException, DocumentException { + // Create PDF document + Document document = new Document(); + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + PdfWriter writer = PdfWriter.getInstance(document, outputStream); + document.open(); + + // Read HTML file + InputStream htmlInputStream = new ByteArrayInputStream(htmlFile.getBytes()); + + // Convert HTML content to PDF + XMLWorkerHelper.getInstance().parseXHtml(writer, document, htmlInputStream); + + // Close document and output stream + document.close(); + outputStream.close(); + + return PdfUtils.boasToWebResponse(outputStream, ""); + } + +} diff --git a/src/main/java/stirling/software/SPDF/controller/converters/ConvertPPTController.java b/src/main/java/stirling/software/SPDF/controller/converters/ConvertPPTController.java new file mode 100644 index 000000000..c5818219c --- /dev/null +++ b/src/main/java/stirling/software/SPDF/controller/converters/ConvertPPTController.java @@ -0,0 +1,79 @@ +package stirling.software.SPDF.controller.converters; + +import java.awt.Color; +import java.awt.Graphics2D; +import java.awt.RenderingHints; +import java.awt.geom.Rectangle2D; +import java.awt.image.BufferedImage; +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +import org.apache.poi.xslf.usermodel.XMLSlideShow; +import org.apache.poi.xslf.usermodel.XSLFSlide; +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.itextpdf.text.Document; +import com.itextpdf.text.DocumentException; +import com.itextpdf.text.Image; +import com.itextpdf.text.PageSize; +import com.itextpdf.text.pdf.PdfWriter; + +import stirling.software.SPDF.utils.PdfUtils; + +@Controller +public class ConvertPPTController { + + + @GetMapping("/pptx-to-pdf") + public String cinvertToPDF(Model model) { + model.addAttribute("currentPage", "xlsx-to-pdf"); + return "convert/xlsx-to-pdf"; + } + + @PostMapping("/pptx-to-pdf") + public ResponseEntity convertPptxToPdf(@RequestParam("fileInput") MultipartFile pptxFile) throws IOException, DocumentException { + // Read PowerPoint presentation + XMLSlideShow ppt = new XMLSlideShow(pptxFile.getInputStream()); + + // Create PDF document + Document pdfDocument = new Document(PageSize.A4.rotate()); + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + PdfWriter.getInstance(pdfDocument, outputStream); + pdfDocument.open(); + + // Convert PowerPoint slides to images, then add them to the PDF + for (XSLFSlide slide : ppt.getSlides()) { + BufferedImage slideImage = new BufferedImage((int) Math.ceil(ppt.getPageSize().getWidth()), (int) Math.ceil(ppt.getPageSize().getHeight()), BufferedImage.TYPE_INT_RGB); + Graphics2D graphics = slideImage.createGraphics(); + + // Set graphics rendering hints for better quality + graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + graphics.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); + graphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC); + + // Draw the slide on the graphics + graphics.setPaint(Color.white); + graphics.fill(new Rectangle2D.Float(0, 0, slideImage.getWidth(), slideImage.getHeight())); + slide.draw(graphics); + + // Add the slide image to the PDF document + Image image = Image.getInstance(slideImage, null); + image.scaleToFit(PageSize.A4.getWidth() - 72, PageSize.A4.getHeight() - 72); + pdfDocument.add(image); + } + + // Close PowerPoint and PDF documents + ppt.close(); + pdfDocument.close(); + outputStream.close(); + + return PdfUtils.boasToWebResponse(outputStream, pptxFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_convertedToPDF.pdf"); + } + +} diff --git a/src/main/java/stirling/software/SPDF/controller/converters/ConvertTextController.java b/src/main/java/stirling/software/SPDF/controller/converters/ConvertTextController.java new file mode 100644 index 000000000..29fe1ac9b --- /dev/null +++ b/src/main/java/stirling/software/SPDF/controller/converters/ConvertTextController.java @@ -0,0 +1,63 @@ +package stirling.software.SPDF.controller.converters; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.nio.charset.StandardCharsets; + +import org.apache.commons.io.FilenameUtils; +import org.apache.poi.hwpf.HWPFDocument; +import org.apache.poi.poifs.filesystem.POIFSFileSystem; +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.itextpdf.text.Document; +import com.itextpdf.text.DocumentException; +import com.itextpdf.text.Paragraph; +import com.itextpdf.text.pdf.PdfWriter; + +import stirling.software.SPDF.utils.PdfUtils; + +@Controller +public class ConvertTextController { + + + @GetMapping("/txt-rtf-to-pdf") + public String cinvertToPDF(Model model) { + model.addAttribute("currentPage", "xlsx-to-pdf"); + return "convert/xlsx-to-pdf"; + } + + @PostMapping("/txt-rtf-to-pdf") + public ResponseEntity convertTxtRtfToPdf(@RequestParam("fileInput") MultipartFile txtRtfFile) throws IOException, DocumentException { + // Create PDF document + Document document = new Document(); + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + PdfWriter.getInstance(document, outputStream); + document.open(); + + // Read TXT/RTF file content + String fileContent; + String fileExtension = FilenameUtils.getExtension(txtRtfFile.getOriginalFilename()); + if (fileExtension.equalsIgnoreCase("rtf")) { + HWPFDocument hwpfDocument = new HWPFDocument(new POIFSFileSystem(txtRtfFile.getInputStream())); + fileContent = hwpfDocument.getText().toString(); + } else { + fileContent = new String(txtRtfFile.getBytes(), StandardCharsets.UTF_8); + } + + // Add content to PDF + document.add(new Paragraph(fileContent)); + + // Close document and output stream + document.close(); + outputStream.close(); + + return PdfUtils.boasToWebResponse(outputStream, txtRtfFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_convertedToPDF.pdf"); + } + +} diff --git a/src/main/java/stirling/software/SPDF/controller/converters/ConvertXlsxController.java b/src/main/java/stirling/software/SPDF/controller/converters/ConvertXlsxController.java index e2b085428..b6731d848 100644 --- a/src/main/java/stirling/software/SPDF/controller/converters/ConvertXlsxController.java +++ b/src/main/java/stirling/software/SPDF/controller/converters/ConvertXlsxController.java @@ -3,8 +3,15 @@ package stirling.software.SPDF.controller.converters; import java.io.ByteArrayOutputStream; import java.io.IOException; +import org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.ss.usermodel.DataFormatter; +import org.apache.poi.ss.usermodel.Row; +import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.ss.usermodel.Workbook; import org.apache.poi.ss.usermodel.WorkbookFactory; +import org.apache.poi.xssf.usermodel.XSSFCellStyle; +import org.apache.poi.xssf.usermodel.XSSFColor; +import org.apache.poi.xssf.usermodel.XSSFFont; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; @@ -16,6 +23,8 @@ import org.springframework.web.multipart.MultipartFile; import com.itextpdf.text.BaseColor; import com.itextpdf.text.Document; import com.itextpdf.text.DocumentException; +import com.itextpdf.text.Font; +import com.itextpdf.text.Paragraph; import com.itextpdf.text.pdf.PdfPCell; import com.itextpdf.text.pdf.PdfPTable; import com.itextpdf.text.pdf.PdfWriter; @@ -33,11 +42,10 @@ public class ConvertXlsxController { } @PostMapping("/xlsx-to-pdf") - public ResponseEntity convertToPDF(@RequestParam("fileInput") MultipartFile xlsx) throws IOException, DocumentException{ - // Load Excel file - + public ResponseEntity convertToPDF(@RequestParam("fileInput") MultipartFile xlsx) throws IOException, DocumentException { + // Load Excel file Workbook workbook = WorkbookFactory.create(xlsx.getInputStream()); - + // Create PDF document Document document = new Document(); ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); @@ -46,32 +54,99 @@ public class ConvertXlsxController { // Convert each sheet in Excel to a separate page in PDF for (int i = 0; i < workbook.getNumberOfSheets(); i++) { - PdfPTable table = new PdfPTable(workbook.getSheetAt(i).getRow(0).getPhysicalNumberOfCells()); - for (int row = 0; row < workbook.getSheetAt(i).getPhysicalNumberOfRows(); row++) { - for (int cell = 0; cell < workbook.getSheetAt(i).getRow(row).getPhysicalNumberOfCells(); cell++) { - PdfPCell pdfCell = new PdfPCell(); - pdfCell.addElement(new com.itextpdf.text.Paragraph(workbook.getSheetAt(i).getRow(row).getCell(cell).toString())); + Sheet sheet = workbook.getSheetAt(i); + int numOfColumns = sheet.getRow(0).getPhysicalNumberOfCells(); + PdfPTable table = new PdfPTable(numOfColumns); - // Copy cell style, borders, and background color - pdfCell.setBorderColor(new BaseColor(workbook.getSheetAt(i).getRow(row).getCell(cell).getCellStyle().getBottomBorderColor())); - pdfCell.setBorderColor(new BaseColor(workbook.getSheetAt(i).getRow(row).getCell(cell).getCellStyle().getTopBorderColor())); - pdfCell.setBorderColor(new BaseColor(workbook.getSheetAt(i).getRow(row).getCell(cell).getCellStyle().getLeftBorderColor())); - pdfCell.setBorderColor(new BaseColor(workbook.getSheetAt(i).getRow(row).getCell(cell).getCellStyle().getRightBorderColor())); - Short bc = workbook.getSheetAt(i).getRow(row).getCell(cell).getCellStyle().getFillBackgroundColor(); - pdfCell.setBackgroundColor(new BaseColor(bc)); + for (int row = 0; row < sheet.getPhysicalNumberOfRows(); row++) { + Row excelRow = sheet.getRow(row); + if (excelRow == null) { + continue; // Skip this row if it's null + } + for (int cell = 0; cell < excelRow.getPhysicalNumberOfCells(); cell++) { + Cell excelCell = excelRow.getCell(cell); + + // Check if the cell is null + if (excelCell == null) { + table.addCell(""); // Add an empty cell to the PDF table + continue; + } + + // Convert cell to string + DataFormatter dataFormatter = new DataFormatter(); + String cellValue = dataFormatter.formatCellValue(excelCell); + System.out.println("Cell Value: " + cellValue); + // Get Excel cell font + Font cellFont = getFontFromExcelCell(workbook, excelCell); + + // Create PDF cell with Excel cell font + PdfPCell pdfCell = new PdfPCell(new Paragraph(cellValue, cellFont)); + + // Set cell height and width + float height = sheet.getRow(row).getHeightInPoints(); + System.out.print(height); + pdfCell.setFixedHeight(30f); + + + // Copy cell style, borders, and background color + XSSFCellStyle cellStyle = (XSSFCellStyle) excelCell.getCellStyle(); + if (cellStyle != null) { + XSSFColor bottomBorderColor = cellStyle.getBottomBorderXSSFColor(); + if (bottomBorderColor != null) { + pdfCell.setBorderColor(new BaseColor(bottomBorderColor.getRGB()[0] & 0xFF, bottomBorderColor.getRGB()[1] & 0xFF, bottomBorderColor.getRGB()[2] & 0xFF)); + } + + XSSFColor topBorderColor = cellStyle.getTopBorderXSSFColor(); + if (topBorderColor != null) { + pdfCell.setBorderColor(new BaseColor(topBorderColor.getRGB()[0] & 0xFF, topBorderColor.getRGB()[1] & 0xFF, topBorderColor.getRGB()[2] & 0xFF)); + } + + XSSFColor leftBorderColor = cellStyle.getLeftBorderXSSFColor(); + if (leftBorderColor != null) { + pdfCell.setBorderColor(new BaseColor(leftBorderColor.getRGB()[0] & 0xFF, leftBorderColor.getRGB()[1] & 0xFF, leftBorderColor.getRGB()[2] & 0xFF)); + } + + XSSFColor rightBorderColor = cellStyle.getRightBorderXSSFColor(); + if (rightBorderColor != null) { + pdfCell.setBorderColor(new BaseColor(rightBorderColor.getRGB()[0] & 0xFF, rightBorderColor.getRGB()[1] & 0xFF, rightBorderColor.getRGB()[2] & 0xFF)); + } + + XSSFColor fillForegroundColor = cellStyle.getFillForegroundXSSFColor(); + if (fillForegroundColor != null) { + pdfCell.setBackgroundColor(new BaseColor(fillForegroundColor.getRGB()[0] & 0xFF, fillForegroundColor.getRGB()[1] & 0xFF, fillForegroundColor.getRGB()[2] & 0xFF)); + } + + } table.addCell(pdfCell); } } + + // Add sheet to PDF document.add(table); + + // Add page break if there are more sheets + if (i < workbook.getNumberOfSheets() - 1) { + document.newPage(); + } } + // Close document and output stream document.close(); outputStream.flush(); - outputStream.close(); - return PdfUtils.boasToWebResponse(outputStream, xlsx.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_convertedToPDF.pdf"); - // Close document and input stream - + outputStream.close(); + // Return PDF as response + return PdfUtils.boasToWebResponse(outputStream, xlsx.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_convertedToPDF.pdf"); } + + private Font getFontFromExcelCell(Workbook workbook, Cell excelCell) { + XSSFFont excelFont = ((XSSFCellStyle) excelCell.getCellStyle()).getFont(); + Font.FontFamily fontFamily = Font.getFamily(excelFont.getFontName()); + float fontSize = excelFont.getFontHeightInPoints(); + int fontStyle = (excelFont.getBold() ? Font.BOLD : Font.NORMAL) | (excelFont.getItalic() ? Font.ITALIC : Font.NORMAL); + + return new Font(fontFamily, fontSize, fontStyle); + } + } diff --git a/src/main/java/stirling/software/SPDF/controller/security/WatermarkController.java b/src/main/java/stirling/software/SPDF/controller/security/WatermarkController.java index 36c23c9c3..ec37ebf18 100644 --- a/src/main/java/stirling/software/SPDF/controller/security/WatermarkController.java +++ b/src/main/java/stirling/software/SPDF/controller/security/WatermarkController.java @@ -2,12 +2,19 @@ package stirling.software.SPDF.controller.security; import java.awt.Color; import java.io.IOException; +import java.util.ArrayList; +import java.util.List; import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.pdmodel.PDDocumentCatalog; 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.interactive.annotation.PDAnnotation; +import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotationMarkup; +import org.apache.pdfbox.pdmodel.interactive.form.PDAcroForm; +import org.apache.pdfbox.pdmodel.interactive.form.PDField; import org.apache.pdfbox.util.Matrix; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; @@ -18,6 +25,7 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.multipart.MultipartFile; import stirling.software.SPDF.utils.PdfUtils; +import stirling.software.SPDF.utils.WatermarkRemover; @Controller public class WatermarkController { @@ -28,6 +36,12 @@ public class WatermarkController { return "security/add-watermark"; } + @GetMapping("/remove-watermark") + public String removeWatermarkForm(Model model) { + model.addAttribute("currentPage", "remove-watermark"); + return "security/remove-watermark"; + } + @PostMapping("/add-watermark") public ResponseEntity addWatermark(@RequestParam("fileInput") MultipartFile pdfFile, @RequestParam("watermarkText") String watermarkText, @RequestParam(defaultValue = "30", name = "fontSize") float fontSize, @RequestParam(defaultValue = "0", name = "rotation") float rotation, @@ -71,4 +85,62 @@ public class WatermarkController { } return PdfUtils.pdfDocToWebResponse(document, pdfFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_watermarked.pdf"); } + + + + + @PostMapping("/remove-watermark") + public ResponseEntity removeWatermark(@RequestParam("fileInput") MultipartFile pdfFile, @RequestParam("watermarkText") String watermarkText) throws Exception { + + // Load the input PDF + PDDocument document = PDDocument.load(pdfFile.getInputStream()); + + // Create a new PDF document for the output + PDDocument outputDocument = new PDDocument(); + + // Loop through the pages + int numPages = document.getNumberOfPages(); + 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) {}; + editor.processPage(page); + editor.processPage(page); + // Add the page to the output document + outputDocument.addPage(page); + } + + for (PDPage page : outputDocument.getPages()) { + List annotations = page.getAnnotations(); + List annotationsToRemove = new ArrayList<>(); + + for (PDAnnotation annotation : annotations) { + if (annotation instanceof PDAnnotationMarkup) { + PDAnnotationMarkup markup = (PDAnnotationMarkup) annotation; + String contents = markup.getContents(); + if (contents != null && contents.contains(watermarkText)) { + annotationsToRemove.add(markup); + } + } + } + + annotations.removeAll(annotationsToRemove); + } + PDDocumentCatalog catalog = outputDocument.getDocumentCatalog(); + PDAcroForm acroForm = catalog.getAcroForm(); + if (acroForm != null) { + List fields = acroForm.getFields(); + for (PDField field : fields) { + String fieldValue = field.getValueAsString(); + if (fieldValue.contains(watermarkText)) { + field.setValue(fieldValue.replace(watermarkText, "")); + } + } + } + + return PdfUtils.pdfDocToWebResponse(outputDocument, "removed.pdf"); + } + + } diff --git a/src/main/java/stirling/software/SPDF/utils/PdfUtils.java b/src/main/java/stirling/software/SPDF/utils/PdfUtils.java index 67629c0c9..18825a503 100644 --- a/src/main/java/stirling/software/SPDF/utils/PdfUtils.java +++ b/src/main/java/stirling/software/SPDF/utils/PdfUtils.java @@ -32,7 +32,6 @@ import org.springframework.http.ResponseEntity; import com.itextpdf.text.Document; import com.itextpdf.text.DocumentException; import com.itextpdf.text.pdf.PdfWriter; -import com.spire.pdf.PdfDocument; public class PdfUtils { @@ -172,16 +171,7 @@ public class PdfUtils { return PdfUtils.boasToWebResponse(baos, docName); } - 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 { diff --git a/src/main/java/stirling/software/SPDF/utils/WatermarkRemover.java b/src/main/java/stirling/software/SPDF/utils/WatermarkRemover.java new file mode 100644 index 000000000..8b4871c7d --- /dev/null +++ b/src/main/java/stirling/software/SPDF/utils/WatermarkRemover.java @@ -0,0 +1,69 @@ +package stirling.software.SPDF.utils; +import java.io.IOException; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.apache.pdfbox.contentstream.PDFStreamEngine; +import org.apache.pdfbox.contentstream.operator.Operator; +import org.apache.pdfbox.cos.COSArray; +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSString; + +public class WatermarkRemover extends PDFStreamEngine { + + private final String watermarkText; + private final Pattern pattern; + + public WatermarkRemover(String watermarkText) { + this.watermarkText = watermarkText; + this.pattern = Pattern.compile(Pattern.quote(watermarkText)); + } + + @Override + protected void processOperator(Operator operator, List operands) throws IOException { + String operation = operator.getName(); + + boolean processText = false; + if ("Tj".equals(operation) || "TJ".equals(operation) || "'".equals(operation) || "\"".equals(operation)) { + processText = true; + } + + if (processText) { + for(int j = 0 ; j < operands.size(); ++j) { + COSBase operand = operands.get(j); + if (operand instanceof COSString) { + COSString cosString = (COSString) operand; + String string = cosString.getString(); + Matcher matcher = pattern.matcher(string); + if (matcher.find()) { + string = matcher.replaceAll(""); + cosString.setValue(string.getBytes()); + } + } else if (operand instanceof COSArray) { + COSArray array = (COSArray) operand; + for (int i = 0; i < array.size(); i++) { + COSBase item = array.get(i); + if (item instanceof COSString) { + COSString cosString = (COSString) item; + String string = cosString.getString(); + Matcher matcher = pattern.matcher(string); + if (matcher.find()) { + System.out.println("operation =" + operation); + System.out.println("1 =" + string); + string = matcher.replaceAll(""); + cosString.setValue(string.getBytes()); + array.set(i, cosString); + operands.set(j, array); + } + + } + } + } + + + } + } + super.processOperator(operator, operands); + } +} diff --git a/src/main/resources/templates/compress-pdf.html b/src/main/resources/templates/compress-pdf.html index 2d746412a..79b67cee6 100644 --- a/src/main/resources/templates/compress-pdf.html +++ b/src/main/resources/templates/compress-pdf.html @@ -14,15 +14,61 @@

-
-

-
-
- - -
- -
+
+
+ + +
+
+
+ + +
+
+ + +
+
+
+
+ + +
+
+
+
+ + +
+
+
+ + +
+
+ +
+
+
diff --git a/src/main/resources/templates/fragments/errorBanner.html b/src/main/resources/templates/fragments/errorBanner.html index d843f4f7a..98fd9aeb0 100644 --- a/src/main/resources/templates/fragments/errorBanner.html +++ b/src/main/resources/templates/fragments/errorBanner.html @@ -1,4 +1,5 @@ + -
+
- - - - + \ No newline at end of file diff --git a/src/main/resources/templates/fragments/navbar.html b/src/main/resources/templates/fragments/navbar.html index 9b3a340bf..1a07236fb 100644 --- a/src/main/resources/templates/fragments/navbar.html +++ b/src/main/resources/templates/fragments/navbar.html @@ -93,10 +93,12 @@ function compareVersions(version1, version2) { console.log("latestVersion=" + latestVersion) console.log("currentVersion=" + currentVersion) console.log("compareVersions(latestVersion, currentVersion) > 0)=" + compareVersions(latestVersion, currentVersion)) - if (latestVersion != null && latestVersion != "" && compareVersions(currentVersion, latestVersion) > 0) { - document.getElementById("update-btn").style.display = "block"; + if (latestVersion != null && latestVersion != "" && compareVersions(latestVersion, currentVersion) > 0) { + document.getElementById("update-btn").style.visibility = "visible"; + console.log("visible") } else { - document.getElementById("update-btn").style.display = "none"; + document.getElementById("update-btn").style.visibility = "hidden"; + console.log("hidden") } } @@ -166,16 +168,12 @@ function compareVersions(version1, version2) { - - - + - -