Add rotate and compress (#7)

* Add files via upload

* Update build.gradle

* Update general.css
This commit is contained in:
Anthony Stirling 2023-01-29 17:06:53 +00:00 committed by GitHub
parent b56e5a0e05
commit cd4bd2a796
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 496 additions and 106 deletions

View file

@ -5,11 +5,15 @@ plugins {
}
group = 'stirling.software'
version = '0.1.0'
version = '0.2.0'
sourceCompatibility = '17'
repositories {
mavenCentral()
maven {
url "https://repo.e-iceblue.com/nexus/content/groups/public/"
name "com.e-iceblue"
}
}
dependencies {
@ -18,6 +22,7 @@ dependencies {
testImplementation 'org.springframework.boot:spring-boot-starter-test'
implementation 'org.apache.pdfbox:pdfbox:2.0.27'
implementation 'org.apache.logging.log4j:log4j-core:2.19.0'
implementation 'e-iceblue:spire.pdf.free:5.1.0'
}
jar {
@ -27,6 +32,7 @@ jar {
tasks.named('test') {
useJUnitPlatform()
}
task printVersion {
println project.version
}

View file

@ -0,0 +1,87 @@
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<byte[]> 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);
}
}
// Save the rearranged PDF to a ByteArrayOutputStream
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
document.saveToStream(outputStream);
// Close the original document
document.close();
// Prepare the response headers
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_PDF);
headers.setContentDispositionFormData("attachment", "compressed.pdf");
headers.setContentLength(outputStream.size());
// Return the response with the PDF data and headers
return new ResponseEntity<>(outputStream.toByteArray(), headers, HttpStatus.OK);
}
}

View file

@ -0,0 +1,45 @@
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 ConvertPDFController {
private static final Logger logger = LoggerFactory.getLogger(ConvertPDFController.class);
@GetMapping("/convert-pdf")
public String convertToPdfForm(Model model) {
model.addAttribute("currentPage", "convert-pdf");
return "convert-pdf";
}
@PostMapping("/convert-to-pdf")
public ResponseEntity<byte[]> 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());
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_PDF);
String filename = "converted.pdf";
headers.setContentDispositionFormData(filename, filename);
headers.setCacheControl("must-revalidate, post-check=0, pre-check=0");
ResponseEntity<byte[]> response = new ResponseEntity<>(bytes, headers, HttpStatus.OK);
return response;
}
}

View file

@ -21,10 +21,6 @@ public class FromPDFController {
private static final Logger logger = LoggerFactory.getLogger(FromPDFController.class);
@GetMapping("/convert-from-pdf")
public String convertFromPdfForm() {
return "convert-from-pdf";
}
@PostMapping("/convert-from-pdf")
public ResponseEntity<byte[]> convertToImage(@RequestParam("fileInput") MultipartFile file,

View file

@ -9,6 +9,7 @@ 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;
@ -22,7 +23,8 @@ public class OverlayImageController {
private static final Logger logger = LoggerFactory.getLogger(OverlayImageController.class);
@GetMapping("/add-image")
public String overlayImage() {
public String overlayImage(Model model) {
model.addAttribute("currentPage", "add-image");
return "add-image";
}

View file

@ -33,13 +33,13 @@ public class PdfController {
@GetMapping("/merge-pdfs")
public String hello(Model model) {
model.addAttribute("message", "Hello, World!");
model.addAttribute("currentPage", "merge-pdfs");
return "merge-pdfs";
}
@GetMapping("/home")
public String home(Model model) {
model.addAttribute("message", "Hello, World!");
model.addAttribute("currentPage", "home");
return "home";
}

View file

@ -27,6 +27,7 @@ public class RearrangePagesPDFController {
@GetMapping("/pdf-organizer")
public String pageOrganizer(Model model) {
model.addAttribute("currentPage", "pdf-organizer");
return "pdf-organizer";
}

View file

@ -0,0 +1,74 @@
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<byte[]> 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<PDPage> iterPage = pages.iterator();
while (iterPage.hasNext()) {
PDPage page = iterPage.next();
page.setRotation(Integer.valueOf(angle));
}
// Save the rearranged PDF to a ByteArrayOutputStream
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
document.save(outputStream);
// Close the document
document.close();
// Prepare the response headers
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_PDF);
headers.setContentDispositionFormData("attachment", "output.pdf");
headers.setContentLength(outputStream.size());
// Return the response with the PDF data and headers
return new ResponseEntity<>(outputStream.toByteArray(), headers, HttpStatus.OK);
}
}

View file

@ -28,6 +28,7 @@ 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;
@ -39,7 +40,8 @@ public class SplitPDFController {
private static final Logger logger = LoggerFactory.getLogger(SplitPDFController.class);
@GetMapping("/split-pdfs")
public String splitPdfForm() {
public String splitPdfForm(Model model) {
model.addAttribute("currentPage", "split-pdfs");
return "split-pdfs";
}

View file

@ -0,0 +1,43 @@
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<th:block th:insert="~{common :: head}"></th:block>
<title>S-PDF Add-Image</title>
</head>
<body>
<div th:insert="~{navbar.html :: navbar}"></div>
<br>
<br>
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6">
<h2>Compress PDF</h2>
<form action="#" th:action="@{/compress-pdf}" th:object="${rotateForm}"
method="post" enctype="multipart/form-data">
<p>Warning: This process can take up to a minute depending on file-size</p>
<div class="custom-file">
<input type="file" class="custom-file-input" id="fileInput"
name="fileInput" required> <label
class="custom-file-label" for="fileInput">Choose PDF</label>
</div>
<div class="form-group">
<label for="imageCompressionLevel">Value between 1 and 100 (1 being most reduced)</label> <input type="number" class="form-control"
id="imageCompressionLevel" name="imageCompressionLevel" step="1" value="1" min="1" max="100" required>
</div>
<button type="submit" class="btn btn-primary">Compress</button>
</form>
<th:block th:insert="~{common :: filelist}"></th:block>
</div>
</div>
</div>
<div th:insert="~{footer.html :: footer}"></div>
</body>
</html>

View file

@ -0,0 +1,61 @@
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<th:block th:insert="~{common :: head}"></th:block>
<title>S-PDF ConvertToPDF</title>
</head>
<body>
<div th:insert="~{navbar.html :: navbar}"></div>
<br>
<br>
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6">
<h2>Image to PDF</h2>
<form method="post" enctype="multipart/form-data"
th:action="@{/convert-to-pdf}">
<div class="custom-file">
<input type="file" class="custom-file-input" id="fileInput"
name="fileInput" required> <label
class="custom-file-label" for="fileInput">Choose Image</label>
</div>
<br><br>
<button type="submit" class="btn btn-primary">Convert</button>
</form>
<th:block th:insert="~{common :: filelist}"></th:block>
</div>
</div>
</div>
<br><br>
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6">
<h2>PDF to img</h2>
<form method="post" enctype="multipart/form-data"
th:action="@{/convert-from-pdf}">
<div class="custom-file">
<input type="file" class="custom-file-input" id="fileInput"
name="fileInput" required> <label
class="custom-file-label" for="fileInput">Choose PDF</label>
</div>
<div class="form-group">
<label>Image Format</label> <select class="form-control"
name="imageFormat">
<option value="jpg">JPEG</option>
<option value="png">PNG</option>
<option value="gif">GIF</option>
</select>
</div>
<button type="submit" class="btn btn-primary">Convert</button>
</form>
<th:block th:insert="~{common :: filelist}"></th:block>
</div>
</div>
</div>
<div th:insert="~{footer.html :: footer}"></div>
</body>
</html>

View file

@ -1,5 +1,6 @@
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<th:block th:insert="~{common :: head}"></th:block>
<title>S-PDF</title>
@ -17,6 +18,8 @@
</div>
<!-- Features -->
<div class="container">
<div class="row h-100">
<div class="col-4 h-100">
@ -32,32 +35,11 @@
<div class="dark-card card">
<div class="card-body">
<h5 class="card-title">Split PDFs</h5>
<p class="card-text">Split your PDFs into multiple single-page
documents or at specific page numbers.</p>
<p class="card-text">Split PDFs into multiple documents</p>
<a href="#" class="btn btn-primary" th:href="@{/split-pdfs}">Go</a>
</div>
</div>
</div>
<div class="col-4 h-100">
<div class="dark-card card">
<div class="card-body">
<h5 class="card-title">Convert to PDF</h5>
<p class="card-text">Convert images to PDF.</p>
<a href="#" class="btn btn-primary" th:href="@{/convert-to-pdf}">Go</a>
</div>
</div>
</div>
</div>
<div class="row h-100">
<div class="col-4 h-100">
<div class="dark-card card">
<div class="card-body dark-card">
<h5 class="card-title">Convert from PDF</h5>
<p class="card-text">Convert PDF to Image.</p>
<a href="#" class="btn btn-primary" th:href="@{/convert-from-pdf}">Go</a>
</div>
</div>
</div>
<div class="col-4 h-100">
<div class="dark-card card">
<div class="card-body">
@ -67,18 +49,60 @@
</div>
</div>
</div>
</div>
<br>
<div class="row h-100">
<div class="col-4 h-100">
<div class="dark-card card">
<div class="card-body">
<h5 class="card-title">Convert to/from PDF</h5>
<p class="card-text">Convert images to/from PDF.</p>
<a href="#" class="btn btn-primary" th:href="@{/convert-pdf}">Go</a>
</div>
</div>
</div>
<div class="col-4 h-100">
<div class="dark-card card">
<div class="card-body">
<h5 class="card-title">PDF Organizer</h5>
<p class="card-text">Rearrange PDF pages into any order (or
remove)</p>
<p class="card-text">Remove/Rearrange pages in any order</p>
<a href="#" class="btn btn-primary" th:href="@{/pdf-organizer}">Go</a>
</div>
</div>
</div>
<div class="col-4 h-100">
<div class="dark-card card">
<div class="card-body">
<h5 class="card-title">Rotate PDFs</h5>
<p class="card-text">Easily rotate your PDFs.</p>
<a href="#" class="btn btn-primary" th:href="@{/rotate-pdf}">Go</a>
</div>
</div>
</div>
</div>
<br>
<div class="row h-100">
<div class="col-4 h-100">
<div class="dark-card card">
<div class="card-body">
<h5 class="card-title">Compress PDFs</h5>
<p class="card-text">Compress PDFs to reduce their file size.</p>
<a href="#" class="btn btn-primary" th:href="@{/compress-pdf}">Go</a>
</div>
</div>
</div>
</div>
</div>
<div th:insert="~{footer.html :: footer}"></div>
</body>
</html>

View file

@ -12,28 +12,30 @@
<li class="nav-item"><a class="nav-link" href="#"
th:href="@{/merge-pdfs}"
th:classappend="${currentPage}=='/merge-pdfs' ? 'active' : ''">Merge
th:classappend="${currentPage}=='merge-pdfs' ? 'active' : ''">Merge
PDFs</a></li>
<li class="nav-item"><a class="nav-link" href="#"
th:href="@{/split-pdfs}"
th:classappend="${currentPage}=='/split-pdfs' ? 'active' : ''">Split
th:classappend="${currentPage}=='split-pdfs' ? 'active' : ''">Split
PDFs</a></li>
<li class="nav-item"><a class="nav-link" href="#"
th:href="@{/convert-to-pdf}"
th:classappend="${currentPage}=='/convert-to-pdf' ? 'active' : ''">Convert
to PDF</a></li>
<li class="nav-item"><a class="nav-link" href="#"
th:href="@{/convert-from-pdf}"
th:classappend="${currentPage}=='/convert-from-pdf' ? 'active' : ''">Convert
from PDF</a></li>
th:href="@{/convert-pdf}"
th:classappend="${currentPage}=='convert-pdf' ? 'active' : ''">Convert
to/from PDF</a></li>
<li class="nav-item"><a class="nav-link" href="#"
th:href="@{/add-image}"
th:classappend="${currentPage}=='/add-image' ? 'active' : ''">Add
th:classappend="${currentPage}=='add-image' ? 'active' : ''">Add
image to PDF</a></li>
<li class="nav-item"><a class="nav-link" href="#"
th:href="@{/pdf-organizer}"
th:classappend="${currentPage}=='/pdf-organizer' ? 'active' : ''">PDF
th:classappend="${currentPage}=='pdf-organizer' ? 'active' : ''">PDF
Organizer</a></li>
<li class="nav-item"><a class="nav-link" href="#"
th:href="@{/rotate-pdf}"
th:classappend="${currentPage}=='rotate-pdf' ? 'active' : ''">Rotate PDF</a></li>
<li class="nav-item"><a class="nav-link" href="#"
th:href="@{/compress-pdf}"
th:classappend="${currentPage}=='compress-pdf' ? 'active' : ''">Compress PDF</a></li>
<input type="checkbox" id="toggle-dark-mode"
th:onclick="javascript:toggleDarkMode()">

View file

@ -0,0 +1,47 @@
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<th:block th:insert="~{common :: head}"></th:block>
<title>S-PDF Add-Image</title>
</head>
<body>
<div th:insert="~{navbar.html :: navbar}"></div>
<br>
<br>
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6">
<h2>Rotate PDF</h2>
<form action="#" th:action="@{/rotate-pdf}" th:object="${rotateForm}"
method="post" enctype="multipart/form-data">
<div class="custom-file">
<input type="file" class="custom-file-input" id="fileInput"
name="fileInput" required> <label
class="custom-file-label" for="fileInput">Choose PDF</label>
</div>
<label for="angle">Select rotation angle (in multiples of
90 degrees):</label> <select id="angle" class="form-control" name="angle">
<option value="90">90</option>
<option value="180">180</option>
<option value="270">270</option>
<option value="-90">-90</option>
<option value="-180">-180</option>
<option value="-270">-270</option>
</select> <br>
<button type="submit" class="btn btn-primary">Rotate</button>
</form>
<th:block th:insert="~{common :: filelist}"></th:block>
</div>
</div>
</div>
<div th:insert="~{footer.html :: footer}"></div>
</body>
</html>