Add rotate and compress (#7)
* Add files via upload * Update build.gradle * Update general.css
This commit is contained in:
parent
b56e5a0e05
commit
cd4bd2a796
15 changed files with 496 additions and 106 deletions
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
|
@ -42,7 +38,7 @@ public class FromPDFController {
|
|||
private String getMediaType(String imageFormat) {
|
||||
if(imageFormat.equalsIgnoreCase("PNG"))
|
||||
return "image/png";
|
||||
else if(imageFormat.equalsIgnoreCase("JPEG") || imageFormat.equalsIgnoreCase("JPG") )
|
||||
else if(imageFormat.equalsIgnoreCase("JPEG") || imageFormat.equalsIgnoreCase("JPG"))
|
||||
return "image/jpeg";
|
||||
else if(imageFormat.equalsIgnoreCase("GIF"))
|
||||
return "image/gif";
|
||||
|
|
|
@ -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";
|
||||
}
|
||||
|
||||
|
|
|
@ -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";
|
||||
}
|
||||
|
||||
|
|
|
@ -27,6 +27,7 @@ public class RearrangePagesPDFController {
|
|||
|
||||
@GetMapping("/pdf-organizer")
|
||||
public String pageOrganizer(Model model) {
|
||||
model.addAttribute("currentPage", "pdf-organizer");
|
||||
return "pdf-organizer";
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -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";
|
||||
}
|
||||
|
||||
|
|
|
@ -2,4 +2,4 @@
|
|||
position: absolute;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
|
43
src/main/resources/templates/compress-pdf.html
Normal file
43
src/main/resources/templates/compress-pdf.html
Normal 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>
|
61
src/main/resources/templates/convert-pdf.html
Normal file
61
src/main/resources/templates/convert-pdf.html
Normal 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>
|
|
@ -1,84 +1,108 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en" xmlns:th="http://www.thymeleaf.org">
|
||||
<head>
|
||||
<th:block th:insert="~{common :: head}"></th:block>
|
||||
<title>S-PDF</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div th:insert="~{navbar.html :: navbar}"></div>
|
||||
<!-- Jumbotron -->
|
||||
<div class="jumbotron jumbotron-fluid" id="jumbotron">
|
||||
<div class="container">
|
||||
<h1 class="display-4">Stirling PDF</h1>
|
||||
<p class="lead">Your locally hosted one-stop-shop for all your
|
||||
PDF needs. (Made 100% in ChatGPT in 1 day as a experiment)</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Features -->
|
||||
<div class="container">
|
||||
<div class="row h-100">
|
||||
<div class="col-4 h-100">
|
||||
<div class="dark-card card">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">Merge PDFs</h5>
|
||||
<p class="card-text">Easily merge multiple PDFs into one.</p>
|
||||
<a href="#" class="btn btn-primary" th:href="@{/merge-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">Split PDFs</h5>
|
||||
<p class="card-text">Split your PDFs into multiple single-page
|
||||
documents or at specific page numbers.</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">
|
||||
<h5 class="card-title">Add image to PDF</h5>
|
||||
<p class="card-text">Adds image/watermark to a PDF</p>
|
||||
<a href="#" class="btn btn-primary" th:href="@{/add-image}">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>
|
||||
<a href="#" class="btn btn-primary" th:href="@{/pdf-organizer}">Go</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div th:insert="~{footer.html :: footer}"></div>
|
||||
</body>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" xmlns:th="http://www.thymeleaf.org">
|
||||
|
||||
<head>
|
||||
<th:block th:insert="~{common :: head}"></th:block>
|
||||
<title>S-PDF</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div th:insert="~{navbar.html :: navbar}"></div>
|
||||
<!-- Jumbotron -->
|
||||
<div class="jumbotron jumbotron-fluid" id="jumbotron">
|
||||
<div class="container">
|
||||
<h1 class="display-4">Stirling PDF</h1>
|
||||
<p class="lead">Your locally hosted one-stop-shop for all your
|
||||
PDF needs. (Made 100% in ChatGPT in 1 day as a experiment)</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Features -->
|
||||
|
||||
|
||||
<div class="container">
|
||||
<div class="row h-100">
|
||||
<div class="col-4 h-100">
|
||||
<div class="dark-card card">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">Merge PDFs</h5>
|
||||
<p class="card-text">Easily merge multiple PDFs into one.</p>
|
||||
<a href="#" class="btn btn-primary" th:href="@{/merge-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">Split PDFs</h5>
|
||||
<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">Add image to PDF</h5>
|
||||
<p class="card-text">Adds image/watermark to a PDF</p>
|
||||
<a href="#" class="btn btn-primary" th:href="@{/add-image}">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">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">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>
|
|
@ -12,29 +12,31 @@
|
|||
|
||||
<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()">
|
||||
<a class="nav-link" href="#" for="toggle-dark-mode">Dark Mode</a>
|
||||
|
|
47
src/main/resources/templates/rotate-pdf.html
Normal file
47
src/main/resources/templates/rotate-pdf.html
Normal 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>
|
Loading…
Reference in a new issue